深入浅出oracle_ebs之forms开发指南


Oracle ERP最佳技术实践 E-BUSINESS SUITE ORACLE 核心应用技术 Forms开发指南 Author: 黄建华Jianhua.Huang MSN: huajhua@hotmail.com Creation Date: October 16, 2006 Last Updated: April 5, 2007 Document Ref: Version: DRAFT 1A Approvals: Copy Number _____ Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Document Control ii Document Control Change Record 5 Date Author Version Change Reference 16-Oct-06 Jianhua.Huang Draft 1a No Previous Document Reviewers Name Position Distribution Copy No. Name Location 1 Library Master Project Library 2 Project Manager 3 4 Note To Holders: If you receive an electronic copy of this document and print it out, please write your name on the equivalent of the cover page, for document control purposes. If you receive a hard copy of this document, please write your name on the front cover, for document control purposes. Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Document Control iii Contents Document Control ..................................................................................................................ii 1. 开发背景与基础.........................................................................................................2 1.1. 读者基础要求......................................................................................................2 1.2. 用户和常用工具..................................................................................................2 1.3. AOL开发框架 .....................................................................................................2 1.4. 多组织支持..........................................................................................................6 1.5. 主要实例..............................................................................................................6 2. 基于EBS的Forms开发过程.......................................................................................8 2.1. Form文件类型 ....................................................................................................8 2.2. Forms Builder安装.............................................................................................8 2.3. 下载Template相关文件 ...................................................................................12 2.4. 一个简单的例子.创建数据库对象...................................................................14 2.5. 一个简单的例子.从模版开始设计...................................................................25 2.6. 一个简单的例子.编写数据操作触发器...........................................................32 2.7. 一个简单的例子.上传&编译............................................................................37 2.8. 一个简单的例子.在EBS中注册运行................................................................37 3. 参数、List、LOV、字段和记录控制、日历 ........................................................40 3.1. 例子:Parameter参数......................................................................................40 3.2. 例子:List值列表 .............................................................................................41 3.3. 例子:LOV窗口式值列表 ...............................................................................42 3.4. 例子:字段和记录控制....................................................................................44 3.5. 例子:日历........................................................................................................45 3.6. 上传&编译&运行..............................................................................................46 4. 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本............................47 4.1. 例子:销售订单行............................................................................................47 4.2. 例子:Master-Detail主从块............................................................................49 4.3. 例子:滚动条....................................................................................................51 4.4. 例子:Stacked画布 ..........................................................................................52 4.5. 画布小结............................................................................................................57 4.6. 例子:Tab画布 .................................................................................................59 4.7. 例子:控制Tab画布 .........................................................................................62 4.8. 例子:多行文本框............................................................................................63 5. Lov查询、块查询、Button ....................................................................................65 5.1. 查询原理............................................................................................................65 5.2. 例子:Lov查询.................................................................................................65 5.3. 例子:块查询....................................................................................................67 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Document Control iv 5.4. 例子:Button....................................................................................................69 6. 触发器层次关系、常用触发器编写规范 ...............................................................70 6.1. 理解层次关系....................................................................................................70 6.2. 触发器原理........................................................................................................70 6.3. 基于EBS模版开发的触发器.............................................................................71 6.4. 一些触发器的理解............................................................................................72 7. 描述性弹性域、Key弹性域、Key弹性域查询 .....................................................74 7.1. 描述性弹性域开发步骤....................................................................................74 7.2. Key弹性域开发步骤.........................................................................................77 8. Folder、JTF Grid ....................................................................................................80 8.1. Folder开发步骤 ................................................................................................80 8.2. JTF Grid开发步骤.............................................................................................85 9. 多语言开发...............................................................................................................90 9.1. 国际化支持........................................................................................................90 9.2. Form自身的多语言版本 ..................................................................................90 9.3. 数据多语言开发步骤........................................................................................90 9.4. EBS启用新语言时的考虑.................................................................................98 10. 附件开发.................................................................................................................100 10.1. 关于附件..........................................................................................................100 10.2. 标准附件设置..................................................................................................100 11. Javabean.................................................................................................................105 11.1. Form与Java.....................................................................................................105 11.2. 例子:Hello World........................................................................................107 11.3. 例子:执行PC本地命令.................................................................................110 11.4. 例子:读取PC文本文件.................................................................................113 12. Form个性化 ...........................................................................................................116 12.1. Form个性化原理 ............................................................................................116 12.2. 例子:修改字段Prompt.................................................................................117 12.3. 例子:有条件显示消息..................................................................................118 12.4. 例子:添加菜单..............................................................................................119 12.5. 例子:打开功能..............................................................................................120 12.6. 例子:执行查询..............................................................................................121 12.7. 例子:Instance间迁移 ...................................................................................122 13. 技巧、常用代码.....................................................................................................123 13.1. Form中的变量 ................................................................................................123 13.2. 初始值、格式掩码..........................................................................................123 13.3. 消息处理..........................................................................................................124 13.4. Special菜单 .....................................................................................................124 13.5. 库存组织访问..................................................................................................124 13.6. 常用内置过程..................................................................................................125 13.7. 待续..................................................................................................................125 附录:我开始学习Form时的笔记1,仅供参考,未必100%正确...................................126 附录:我开始学习Form时的笔记2,仅供参考,未必100%正确...................................129 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Document Control v 14. Open and Closed Issues for this Deliverable....................................................132 Open Issues...................................................................................................................132 Closed Issues ................................................................................................................132 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 开发背景与基础 2 of 136 1. 开发背景与基础 1.1. 读者基础要求 1、 有EBS的使用经验,尤其是Form的使用经验 2、 创建客户化应用,参考《深入浅出Oracle EBS之全模块设置详例》第一章节 3、 熟悉PL/SQL 4、 熟悉Telnet和FTP工具,熟悉Windows常规操作 5、 理解或开发过数据库应用系统 6、 有Form开发经验则更佳 1.2. 用户和常用工具 1.2.1. 区分3类用户 1、 OS用户:包括超级用户root,应用OS用户如applprod,数据库OS用户如 oraprod。后两个用户具体由dba安装环境时创建,名字不定。 2、 数据库用户:包括内置管理用户sys、system,EBS用户apps,EBS各模块用户 applsys、gl、inv、po、ar、ap等等,EBS网关用户applsyspub。 3、 EBS用户:也叫OA用户、应用用户、ERP用户,包括默认超级用户sysadmin,其 他内置用户(参见《深入浅出Oracle EBS之安全机制探索》),企业实施、使用过 程中创建的用户。 1.2.2. Form开发使用的用户和工具 Forms开发过程中需要具体使用如下3个用户。 1、 应用OS用户:用telnet工具如SecureCRT登录服务器,获得各$XXX_TOP的具体 路径、编译form和pll;用FTP如cuteftp连接服务器,下载必要文件、上传开发的 form。 2、 APPS:用PL/SQL Developer登录数据库,创建各类数据库对象。 3、 sysadmin或者拥有应用开发员和系统管理员职责的等价用户:注册form等各AOL 对象、测试form。 1.3. AOL开发框架 1.3.1. 再说Navigator Forms自身菜单其实和传统菜单一样: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 开发背景与基础 3 of 136 然而EBS中基本摒弃Forms自身的菜单功能,而是专门开发了一个Navigator界面,采 用树形结构显示菜单,每个菜单项对应一个Forms: 这里的菜单是可随意组织的,因此非常灵活,而不用如传统菜单那样要么写死要么用 代码控制。 实际上,该方式完成了EBS最主要的安全性控制——功能安全性,为什么这么说呢? 1.3.2. AOL开发框架:EBS功能安全性基本原理 这里仅说明Forms部分,其他的可参考《深入浅出Oracle EBS之安全机制探索》。 安全性最终都要落实到“用户”身上,即某一用户是否具有某一权限;功能安全性的 核心就是某一用户是否具有运行某一个Forms的权限。 为了方便管理,分类维护,EBS在“用户”和“Forms”之间加了几个层次。考察如下 过程: 1、 “用户”如sysadmin登录,系统验证其用户名/密码 2、 如果OK,系统列出其拥有的所有角色,在EBS中叫“职责”(Responsibility), 而每个职责,都对应一个定义好的“菜单” 3、 当用户选择相应的职责进入“Navigator”后,显示的就是此菜单的内容 4、 每个底层菜单项,还不是直接对应Forms,而是先对应一个“功能” (Function),由功能再去对应一个具体的“Forms”。这里的好处是,在功能上 可以定义参数比如查询条件、控制码等,然后传递给Forms,当然大部分情况是不 定义参数,所以功能和Forms基本上是一一对应关系 5、 用户点击菜单项,到定义Forms时指定的应用的TOP下,找到“fmx文件”执行之 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 开发背景与基础 4 of 136 所以,反过来,如果我们开发好一个Forms,要在EBS中跑起来,完整的过程就是为该 “Forms”定义“功能”,定义“菜单”调用该功能,定义“职责”使用该菜单,最 后把职责分配给“用户”等一系列无Coding的定义工作。 1.3.3. Template.fmb 专业的软件系统,其操作方式、界面风格总是非常统一,即便是后来收购集成进来的 模块,经过调整优化后,风格也基本一致。那么如何才能做到统一呢?一是依赖于规 范文档,大家老老实实照标准开发;二是采用更加直接有效的办法——模版。 Oracle EBS的Forms,基本上都是从Template.fmb开始,该模版预先定义了: 1、 各种界面元素的属性集——子类 2、 常用的控件——日历、进度条 3、 一系列Form级触发器,统一处理各种未被明确处理的事件 4、 丰富的PLL库函数,大大超越了Forms Builder内置的函数 所以,我们基于EBS的开发,当然也是从Template.fmb开始 1.3.4. EBS文件系统 EBS文件系统,指其以怎样的目录结构组织各种可执行文件、命令文件、配置文件 的。 从整个EBS的角度看,分DB、APP两部分、五个大目录: 其中COMN目录(对应环境变量$COMMON_TOP)存放服务启停脚本和基于HTML 的应用文件(Java类、JSP页等): Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 开发背景与基础 5 of 136 APPL(对应环境变量$APPL_TOP)则存放配置文件、各种管理脚本、各模块应用代 码: APPL下的各个应用模块目录,则是本次介绍的主角了: AU模块存放fmb、pll、plx文件、各应用模块存放fmx文件,具体是: $AU_TOP/resource: pll文件、plx文件 $AU_TOP/forms/US: 英文fmb文件 $AU_TOP/forms/<语言代码>: 特定语种(如ZHS)的fmb文件 $<应用简称>_TOP/forms/US: 各模块英文fmx文件录 $<应用简称>_TOP/forms/<语言代码>: 特定语种(如ZHS)fmb文件 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 开发背景与基础 6 of 136 上面<应用简称>,如INV、GL、AP、AR等等,在System Administrator职责下的 Application/Register中定义。 通常各个企业都会创建一个客户化应用来管理二次开发的所有代码和设置,比如 CUX、HAND等,下面以CUX(客户化的意思)为例。 总之我们需要的模版及相关文件在AU_TOP下;我们开发的fmb文件呢,也应根据上 述规则传到$AU_TOP/forms的相关语言路径下,不过为管理、备份方便,实际开发 中可能故意违反EBS的规则,与fmx一起放在$CUX_TOP/forms的相关语言路径下。 详情可参考:Arone的《11iConcepts.ppt》或Oracle的《Oracle Applications Concepts》PDF。 1.4. 多组织支持 1.4.1. 说明 Oracle的多组织数据屏蔽,设计要点如下: 1、 核心层次:业务组BG→账套SOB→法人实体LE→经营单位OU→库存组织INV, 这些层次统称为组织,可通过视图org_organization_definitions查看关系。 2、 数据级别:表中设计有组织ID来屏蔽;不同模块因为针对的层次不同,其组织ID 含义不同,比如HR的表用Business_Group_Id,GL的表用Set_Of_Book_Id, AR/AP/PO/OM等表用经营单位Org_Id,INV/MRP/WIP/BOM等模块用库存 组织Organization_Id。 3、 程序级别:用户登录、选择职责后,其所能操作的业务组、账套、法人实体、经 营单位就确定了,这个是通过相关的Profile来设置的;当进入制造和库存相关模 块,需要通过Change Organization菜单来获得可操作的库存组织。Oracle标准的 Package、Form、Java等程序,都是严格根据当前用户的参数来过滤各模块表数 据。 1.5. 主要实例 本文档主要围绕开发销售订单来介绍Form开发过程中涉及的关键技术点。 1.5.1. 销售订单 销售订单最核心的内容为:某客户,在某天,以何价格,购买多少数量的哪些商品。 一张销售订单,客户是一定的,销售员可能有多个,这里假定只记录主销售员,所以 这两个信息构成销售订单的“头信息”;一次订单,客户通常会同时购买多种商品, 并且未必是同一天要货,这样需求日期、商品、数量、价格构成销售订单的“行信 息”。 1.5.2. 开发需求分析 销售订单还需要记录其它重要的内容,这个可直接参照EBS的“Sales Order”,为学 习方便,这里仅加入如下不完整、不严谨的信息。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 开发背景与基础 7 of 136 头信息:订单编号、订单日期、内销还是外销、所采用的价目表、总价、币别、订单 状态;非“录入”的不能删除,“部分履行”或“完全履行”的不能修改。 订单状态:录入、确定、部分履行、完全履行。 行信息:发货日期、收款日期;如果已发货,商品和数量不能修改,记录不能删除; 如果已收款,整条记录都不能修改、不能删除。 全部行都已发货、已收款则订单状态为“完全履行”,部分发货或部分收款,则订单 状态为“部分履行”。 订单查询:需要提供按订单号、订单日期、客户、销售员、销售类型、商品、是否发 货、是否收款等条件进行组合查询,查询表现方式分为Folder形式和Grid形式。 1.5.3. 其它说明 本文档使用“SCF”客户化应用做开发,不过数据库对象仍然沿用“CUX”前缀;没 有建立专门的索引表空间。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 8 of 136 2. 基于EBS的Forms开发过程 2.1. Form文件类型 2.1.1. Form文件类型 .fmb:源文件,目前是二进制格式,也可以转成早期版本的ASCII格式 .fmx:可执行文件,类似VB的.exe文件,其也需要在Forms Runtime环境中运行 .pll:库函数源文件,类似所有开发语言的库函数,如VC的.cpp文件 .plx:库函数可执行文件 调用关系:fmb文件可以引用其他fmb文件、pll文件,pll文件可以进一步引用其他pll 文件,引用是可以嵌套的。所以要成功打开一个forms源文件,必须保证其直接引用、 间接引用的fmb、pll文件均存在。 怎样才叫“存在”呢?类似各种语言如C的Include Path或Java的Class Path,Forms也 有一个参数——注册表FORMS60_PATH来指示引用的路径,只要需要的文件在该路 径下即可。 2.2. Forms Builder安装 2.2.1. 版本 尽管Oracle的Developer工具已经升级到9i、10g,但EBS中使用的Forms Server和 Forms Builder版本还是6i,今后的开发将逐步转移到Java,Oracle将来也不会在EBS中 使用高于6i的Forms。 可以从http://edelivery.oracle.com/EPD/Search/get_form下载,其包含在EBS for Windows版本的下载列表中。 2.2.2. Oracle Home Oracle Home:Oracle产品的根目录及其名称;不同产品可以装到不同的目录,拥有 各自的Oracle Home;通过安装目录下的bin\oracle.key来指示使用哪个注册表项。 Default Home:指所有Oracle Home中,哪个是Default,其名字则未必叫Default。 Developer 6i的安装不够友好,必须安装到Default Home,为避免手工修改注册表的 烦恼,最好先安装6i,再安装其他Oracle产品。 2.2.3. 基本安装过程,请采用Custom 1、 运行Setup,选择安装目录和语言,建议采用English: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 9 of 136 2、 选择Oracle Forms Developer: Tips:待装完Forms Developer,需要重新运行Setup来安装Reports Developer,至于 两个Server,基于EBS的开发不需要,装了也用不了。 3、 选择安装类型,请采用Custom: 4、 选择安装Forms Builder,将自动分析并安装需要的相关组件和文件: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 10 of 136 如果系统足够“干净”,通常一路OK就能顺利安装;点击Exit退出。 5、 请从头开始,安装Reports Developer: 2.2.4. 打Patch,请采用Typical 上述安装的Developer6i的版本是6.0.8.11.3,在编辑比较复杂的Form时会报错退出, 需要打Patch,我打的是6.0.8.25。 1、 运行Setup,选择安装目录和语言,默认即可。 2、 选择安装类型,请采用Typical: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 11 of 136 通常一路OK就能顺利安装。 2.2.5. 配置TNSNAME 从DBA或他人索要SQLNET.ORA和TNSNAMES.ORA,直接覆盖到安装目录的 NET80\ADMIN下。 SQLNET.ORA是Oracle SQL*Net协议配置文件,样例如下: SQLNET.AUTHENTICATION_SERVICES= NONE NAMES.DIRECTORY_PATH= (TNSNAMES, ONAMES, HOSTNAME) SQLNET.EXPIRE_TIME=1 TNSNAMES.ORA是Oracle SQL*Net数据库服务解析文件,样例如下: PROD= (DESCRIPTION= (ADDRESS=(PROTOCOL=tcp)(HOST=HUAJHUA.leiko.com)(PORT=1521)) (CONNECT_DATA= (SERVICE_NAME=PROD) (INSTANCE_NAME=PROD) ) ) 2.2.6. 配置FORMS60_PATH 基于EBS的Forms开发,需要从服务器上下载必要的fmb和pll文件到本地,比如两类文 件都放在d:\oracle\resource,那么需要添加注册表的字符串值FORMS60_PATH,类 似C语言的Include Directory或者Java的Class Path: 2.2.7. 配置NLS_LANG 修改注册表:Local Machine/Software/Oracle/NLS_LANG 改为AMERICAN_AMERICA.ZHS16GBK,这样开发IDE使用英文,字符集可满足英 文、简体中文、繁体中文的需要。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 12 of 136 习惯上也同时修改其他子分支下的NLS_LANG。 2.3. 下载Template相关文件 用FTP以应用操作系统用户登录EBS服务器,进入到$AU_TOP目录下。 2.3.1. 下载TEMPLATE.fmb 从$AU_TOP/forms/US下载TEMPLATE.fmb到FORMS60_PATH对应的目录下。 2.3.2. 启动Form Builder 通过开始菜单启动: 去掉Display at startup,点击Cancel: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 13 of 136 2.3.3. 打开TEMPLATE.fmb及报错分析 N: 点击Open,打开TEMPLATE.fmb 本地仅有TEMPLATE.fmb,将报fmb文件找不到——Source Module后就是form文件 名: 点击OK,再报pll文件找不到——PL/SQL library后面就是就是pll文件名: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 14 of 136 注意只可关闭、不可保存TEMPLATE.fmb! 2.3.4. 下载必要的文件到FORMS60_PATH对应的目录 目标:不断测试、下载,直至打开TEMPLATE.fmb,没有任何错误为止。 从$AU_TOP/forms/US下载缺失的fmb文件。 从$AU_TOP/resource下载缺失的pll文件。 因为form和pll都可嵌套引用,所以有时候把提示的form或者pll下载下来,打开 TEMPLATE.fmb依然报错,那么需要直接打开提示缺失的fmb或pll文件,这个时候才 会看到真正缺失的文件,下载之。 为减少每个人的麻烦,现把需要的文件全部列出:【To Do】 2.4. 一个简单的例子.创建数据库对象 要求:表、序列、索引建在应用数据库用户下,表放在数据表空间中,索引放在索引 表空间中;视图、包建在APPS下,表和序列需要在APPS下创建别名。 本小节的脚本在PL/SQL Developer中,用APPS登录,在Command Window中运 行。 2.4.1. 创建数据库对象 这里仅创建“头信息”表,并遵循如下规范: 1、 1个表关键字ID,通常与表名一致,并用Sequence为每条记录获得一个唯一值。 2、 1个组织ID,根据不同的开发选用不同层次的组织ID,这里的销售订单跑在OU 层,所以基表命名为_ALL表,并创建一个过滤组织的View作为“基表”。 3、 5个Who字段,记录由谁在何时创建,并由谁在何时修改,登录ID是多少。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 15 of 136 4、 4个请求字段,处理的请求ID、请求日期、并发程序ID及其应用ID,如果有的话。 5、 16个描述性弹性域字段,1个上下文字段,15个弹性域字段。 6、 另外,根据以往开发经验,加个描述字段可解决不少难缠的问题。 7、 因为脚本中带有Schema前缀,可以在APPS直接运行创建,源文件为 cux_order_headers_all.sql。 -- Create table create table SCF.CUX_ORDER_HEADERS_ALL ( HEADER_ID NUMBER not null, ORG_ID NUMBER not null, ORDER_NUMBER NUMBER not null, ORDERED_DATE DATE not null, ORDER_TYPE VARCHAR2(1) not null, CUSTOMER_ID NUMBER not null, SALESREP_ID NUMBER(15), PRICE_LIST_ID NUMBER, CURRENCY_CODE VARCHAR2(15) not null, FLOW_STATUS_CODE VARCHAR2(30) not null, DESCRIPTION VARCHAR2(240), 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, PROGRAM_APPLICATION_ID NUMBER, PROGRAM_ID NUMBER, PROGRAM_UPDATE_DATE DATE, REQUEST_ID NUMBER, ATTRIBUTE_CATEGORY VARCHAR2(30), ATTRIBUTE1 VARCHAR2(240), ATTRIBUTE2 VARCHAR2(240), ATTRIBUTE3 VARCHAR2(240), ATTRIBUTE4 VARCHAR2(240), ATTRIBUTE5 VARCHAR2(240), ATTRIBUTE6 VARCHAR2(240), ATTRIBUTE7 VARCHAR2(240), ATTRIBUTE8 VARCHAR2(240), ATTRIBUTE9 VARCHAR2(240), ATTRIBUTE10 VARCHAR2(240), ATTRIBUTE11 VARCHAR2(240), ATTRIBUTE12 VARCHAR2(240), ATTRIBUTE13 VARCHAR2(240), ATTRIBUTE14 VARCHAR2(240), ATTRIBUTE15 VARCHAR2(240) ) tablespace SCF; -- Create/Recreate indexes create unique index SCF.CUX_ORDER_HEADERS_U1 on SCF.CUX_ORDER_HEADERS_ALL (HEADER_ID) tablespace SCF; create unique index SCF.CUX_ORDER_HEADERS_U2 on SCF.CUX_ORDER_HEADERS_ALL (ORG_ID, ORDER_NUMBER) tablespace SCF; create index SCF.CUX_ORDER_HEADERS_N1 on SCF.CUX_ORDER_HEADERS_ALL (CUSTOMER_ID) tablespace SCF; create index SCF.CUX_ORDER_HEADERS_N2 on SCF.CUX_ORDER_HEADERS_ALL (SALESREP_ID) tablespace SCF; create index SCF.CUX_ORDER_HEADERS_N3 on SCF.CUX_ORDER_HEADERS_ALL (FLOW_STATUS_CODE) tablespace SCF; -- Create/Recreate sequence CREATE SEQUENCE SCF.CUX_ORDER_HEADERS_S; -- Create/Recreate synonum CREATE SYNONYM CUX_ORDER_HEADERS_S FOR scf.CUX_ORDER_HEADERS_S; CREATE SYNONYM CUX_ORDER_HEADERS_ALL FOR scf.CUX_ORDER_HEADERS_ALL; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 16 of 136 -- Create/Recreate View CREATE OR REPLACE VIEW CUX_ORDER_HEADERS AS SELECT header_id, org_id, order_number, ordered_date, order_type, customer_id, salesrep_id, price_list_id, currency_code, flow_status_code, description, creation_date, created_by, last_updated_by, last_update_date, last_update_login, program_application_id, program_id, program_update_date, request_id, attribute_category, attribute1, attribute2, attribute3, attribute4, attribute5, attribute6, attribute7, attribute8, attribute9, attribute10, attribute11, attribute12, attribute13, attribute14, attribute15 FROM cux_order_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); 2.4.2. 注册表和字段 需要向EBS注册表和字段,这样以后就可通过标准功能设置弹性域、监控表操作。 EXECUTE AD_DD.REGISTER_TABLE('SCF','CUX_ORDER_HEADERS_ALL','T',2,10,40); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','HEADER_ID',1,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ORG_ID',2,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ORDER_NUMBER',3,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ORDERED_DATE',4,'DATE',9,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ORDER_TYPE',5,'VARCHAR2',1,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','CUSTOMER_ID',6,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','SALESREP_ID',7,'NUMBER',38,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','PRICE_LIST_ID',8,'NUMBER',38,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','CURRENCY_CODE',9,'VARCHAR2',15,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','FLOW_STATUS_CODE',10,'VARCHAR2',30,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','DESCRIPTION',11,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','CREATION_DATE',12,'DATE',9,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','CREATED_BY',13,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','LAST_UPDATED_BY',14,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','LAST_UPDATE_DATE',15,'DATE',9,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','LAST_UPDATE_LOGIN',16,'NUMBER',38,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','PROGRAM_APPLICATION_ID',17,'NUMBER',38,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','PROGRAM_ID',18,'NUMBER',38,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','PROGRAM_UPDATE_DATE',19,'DATE',9,'Y','N'); Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 17 of 136 EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','REQUEST_ID',20,'NUMBER',38,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE_CATEGORY',21,'VARCHAR2',30,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE1',22,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE2',23,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE3',24,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE4',25,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE5',26,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE6',27,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE7',28,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE8',29,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE9',30,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE10',31,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE11',32,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE12',33,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE13',34,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE14',35,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_ORDER_HEADERS_ALL','ATTRIBUTE15',36,'VARCHAR2',240,'Y','N'); 2.4.3. 创建用于Form的View Form可以直接基于基表,但对于复杂的表,则必需使用View,将各个ID转换为有意 义的编码或者描述。 CREATE OR REPLACE VIEW CUX_ORDER_HEADERS_V AS SELECT coh.ROWID row_id, coh.header_id, coh.org_id, coh.order_number, coh.ordered_date, coh.order_type, coh.customer_id, rac.customer_name, coh.salesrep_id, jrs.NAME salesrep_name, coh.price_list_id, qlh.NAME price_list_name, coh.currency_code, coh.flow_status_code, coh.description, coh.creation_date, coh.created_by, coh.last_updated_by, coh.last_update_date, coh.last_update_login, coh.program_application_id, coh.program_id, coh.program_update_date, coh.request_id, coh.attribute_category, coh.attribute1, coh.attribute2, coh.attribute3, coh.attribute4, coh.attribute5, coh.attribute6, coh.attribute7, coh.attribute8, coh.attribute9, coh.attribute10, coh.attribute11, coh.attribute12, coh.attribute13, coh.attribute14, coh.attribute15 FROM cux_order_headers coh, ra_customers rac, jtf_rs_salesreps jrs, qp_list_headers_vl qlh WHERE coh.customer_id = rac.customer_id AND coh.salesrep_id = jrs.salesrep_id(+) AND coh.price_list_id = qlh.list_header_id(+) Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 18 of 136 2.4.4. 创建表操作API 不管是基于基表还是基于视图的Block,都建议编写ON-UPDATE、ON-INSERT、 ON-DELETE、ON-LOCK触发器,并且,把具体的DML和锁记录代码放入数据库 Package中,然后在Form中调用,该Package以后还可以在其他地方调用。这些设计, 都是基于以往开发经验所归纳出来的,有利于模块化开发、有利于扩展。以下脚本在 APPS下运行,源文件为cux_order_headers_pkg.pck: CREATE OR REPLACE PACKAGE cux_order_headers_pkg AS /*===================================== ** PROCEDURE: insert_row() **=====================================*/ PROCEDURE insert_row(x_row_id IN OUT VARCHAR2, x_header_id IN OUT NUMBER, p_org_id IN NUMBER, p_order_number IN NUMBER, p_ordered_date IN DATE, p_order_type IN VARCHAR2, p_customer_id IN NUMBER, p_salesrep_id IN NUMBER DEFAULT NULL, p_price_list_id IN NUMBER DEFAULT NULL, p_currency_code IN VARCHAR2, p_flow_status_code IN VARCHAR2, p_description IN VARCHAR2 DEFAULT NULL, p_creation_date IN DATE, p_created_by IN NUMBER, p_last_updated_by IN NUMBER, p_last_update_date IN DATE, p_last_update_login IN NUMBER DEFAULT NULL, p_program_application_id IN NUMBER DEFAULT NULL, p_program_id IN NUMBER DEFAULT NULL, p_program_update_date IN DATE DEFAULT NULL, p_request_id IN NUMBER DEFAULT NULL, p_attribute_category IN VARCHAR2 DEFAULT NULL, p_attribute1 IN VARCHAR2 DEFAULT NULL, p_attribute2 IN VARCHAR2 DEFAULT NULL, p_attribute3 IN VARCHAR2 DEFAULT NULL, p_attribute4 IN VARCHAR2 DEFAULT NULL, p_attribute5 IN VARCHAR2 DEFAULT NULL, p_attribute6 IN VARCHAR2 DEFAULT NULL, p_attribute7 IN VARCHAR2 DEFAULT NULL, p_attribute8 IN VARCHAR2 DEFAULT NULL, p_attribute9 IN VARCHAR2 DEFAULT NULL, p_attribute10 IN VARCHAR2 DEFAULT NULL, p_attribute11 IN VARCHAR2 DEFAULT NULL, p_attribute12 IN VARCHAR2 DEFAULT NULL, p_attribute13 IN VARCHAR2 DEFAULT NULL, p_attribute14 IN VARCHAR2 DEFAULT NULL, p_attribute15 IN VARCHAR2 DEFAULT NULL); /*===================================== ** PROCEDURE: lock_row() **=====================================*/ PROCEDURE lock_row(p_header_id IN NUMBER, p_org_id IN NUMBER, p_order_number IN NUMBER, p_ordered_date IN DATE, p_order_type IN VARCHAR2, p_customer_id IN NUMBER, p_salesrep_id IN NUMBER, p_price_list_id IN NUMBER, p_currency_code IN VARCHAR2, p_flow_status_code IN VARCHAR2, p_description IN VARCHAR2, p_creation_date IN DATE, p_created_by IN NUMBER, p_last_updated_by IN NUMBER, p_last_update_date IN DATE, p_last_update_login IN NUMBER, p_program_application_id IN NUMBER, p_program_id IN NUMBER, p_program_update_date IN DATE, p_request_id IN NUMBER, Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 19 of 136 p_attribute_category IN VARCHAR2, p_attribute1 IN VARCHAR2, p_attribute2 IN VARCHAR2, p_attribute3 IN VARCHAR2, p_attribute4 IN VARCHAR2, p_attribute5 IN VARCHAR2, p_attribute6 IN VARCHAR2, p_attribute7 IN VARCHAR2, p_attribute8 IN VARCHAR2, p_attribute9 IN VARCHAR2, p_attribute10 IN VARCHAR2, p_attribute11 IN VARCHAR2, p_attribute12 IN VARCHAR2, p_attribute13 IN VARCHAR2, p_attribute14 IN VARCHAR2, p_attribute15 IN VARCHAR2); /*===================================== ** PROCEDURE: update_row() **=====================================*/ PROCEDURE update_row(p_header_id IN NUMBER, p_org_id IN NUMBER, p_order_number IN NUMBER, p_ordered_date IN DATE, p_order_type IN VARCHAR2, p_customer_id IN NUMBER, p_salesrep_id IN NUMBER DEFAULT NULL, p_price_list_id IN NUMBER DEFAULT NULL, p_currency_code IN VARCHAR2, p_flow_status_code IN VARCHAR2, p_description IN VARCHAR2 DEFAULT NULL, p_last_updated_by IN NUMBER, p_last_update_date IN DATE, p_last_update_login IN NUMBER DEFAULT NULL, p_program_application_id IN NUMBER DEFAULT NULL, p_program_id IN NUMBER DEFAULT NULL, p_program_update_date IN DATE DEFAULT NULL, p_request_id IN NUMBER DEFAULT NULL, p_attribute_category IN VARCHAR2 DEFAULT NULL, p_attribute1 IN VARCHAR2 DEFAULT NULL, p_attribute2 IN VARCHAR2 DEFAULT NULL, p_attribute3 IN VARCHAR2 DEFAULT NULL, p_attribute4 IN VARCHAR2 DEFAULT NULL, p_attribute5 IN VARCHAR2 DEFAULT NULL, p_attribute6 IN VARCHAR2 DEFAULT NULL, p_attribute7 IN VARCHAR2 DEFAULT NULL, p_attribute8 IN VARCHAR2 DEFAULT NULL, p_attribute9 IN VARCHAR2 DEFAULT NULL, p_attribute10 IN VARCHAR2 DEFAULT NULL, p_attribute11 IN VARCHAR2 DEFAULT NULL, p_attribute12 IN VARCHAR2 DEFAULT NULL, p_attribute13 IN VARCHAR2 DEFAULT NULL, p_attribute14 IN VARCHAR2 DEFAULT NULL, p_attribute15 IN VARCHAR2 DEFAULT NULL); /*===================================== ** PROCEDURE: delete_row() **=====================================*/ PROCEDURE delete_row(p_header_id IN NUMBER); END; / CREATE OR REPLACE PACKAGE BODY cux_order_headers_pkg AS /*===================================== ** PROCEDURE: insert_row() **=====================================*/ PROCEDURE insert_row(x_row_id IN OUT VARCHAR2, x_header_id IN OUT NUMBER, p_org_id IN NUMBER, p_order_number IN NUMBER, p_ordered_date IN DATE, p_order_type IN VARCHAR2, p_customer_id IN NUMBER, p_salesrep_id IN NUMBER DEFAULT NULL, p_price_list_id IN NUMBER DEFAULT NULL, Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 20 of 136 p_currency_code IN VARCHAR2, p_flow_status_code IN VARCHAR2, p_description IN VARCHAR2 DEFAULT NULL, p_creation_date IN DATE, p_created_by IN NUMBER, p_last_updated_by IN NUMBER, p_last_update_date IN DATE, p_last_update_login IN NUMBER DEFAULT NULL, p_program_application_id IN NUMBER DEFAULT NULL, p_program_id IN NUMBER DEFAULT NULL, p_program_update_date IN DATE DEFAULT NULL, p_request_id IN NUMBER DEFAULT NULL, p_attribute_category IN VARCHAR2 DEFAULT NULL, p_attribute1 IN VARCHAR2 DEFAULT NULL, p_attribute2 IN VARCHAR2 DEFAULT NULL, p_attribute3 IN VARCHAR2 DEFAULT NULL, p_attribute4 IN VARCHAR2 DEFAULT NULL, p_attribute5 IN VARCHAR2 DEFAULT NULL, p_attribute6 IN VARCHAR2 DEFAULT NULL, p_attribute7 IN VARCHAR2 DEFAULT NULL, p_attribute8 IN VARCHAR2 DEFAULT NULL, p_attribute9 IN VARCHAR2 DEFAULT NULL, p_attribute10 IN VARCHAR2 DEFAULT NULL, p_attribute11 IN VARCHAR2 DEFAULT NULL, p_attribute12 IN VARCHAR2 DEFAULT NULL, p_attribute13 IN VARCHAR2 DEFAULT NULL, p_attribute14 IN VARCHAR2 DEFAULT NULL, p_attribute15 IN VARCHAR2 DEFAULT NULL) IS CURSOR c IS SELECT ROWID FROM cux_order_headers_all WHERE header_id = x_header_id; BEGIN IF x_header_id IS NULL THEN SELECT cux_order_headers_s.NEXTVAL INTO x_header_id FROM dual; END IF; INSERT INTO cux_order_headers_all (header_id, org_id, order_number, ordered_date, order_type, customer_id, salesrep_id, price_list_id, currency_code, flow_status_code, description, creation_date, created_by, last_updated_by, last_update_date, last_update_login, program_application_id, program_id, program_update_date, request_id, attribute_category, attribute1, attribute2, attribute3, attribute4, attribute5, attribute6, attribute7, attribute8, attribute9, attribute10, attribute11, attribute12, attribute13, attribute14, attribute15) VALUES Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 21 of 136 (x_header_id, p_org_id, p_order_number, p_ordered_date, p_order_type, p_customer_id, p_salesrep_id, p_price_list_id, p_currency_code, p_flow_status_code, p_description, p_creation_date, p_created_by, p_last_updated_by, p_last_update_date, p_last_update_login, p_program_application_id, p_program_id, p_program_update_date, p_request_id, p_attribute_category, p_attribute1, p_attribute2, p_attribute3, p_attribute4, p_attribute5, p_attribute6, p_attribute7, p_attribute8, p_attribute9, p_attribute10, p_attribute11, p_attribute12, p_attribute13, p_attribute14, p_attribute15); OPEN c; FETCH c INTO x_row_id; IF (c%NOTFOUND) THEN CLOSE c; RAISE no_data_found; END IF; CLOSE c; END insert_row; /*===================================== ** PROCEDURE: lock_row() **=====================================*/ PROCEDURE lock_row(p_header_id IN NUMBER, p_org_id IN NUMBER, p_order_number IN NUMBER, p_ordered_date IN DATE, p_order_type IN VARCHAR2, p_customer_id IN NUMBER, p_salesrep_id IN NUMBER, p_price_list_id IN NUMBER, p_currency_code IN VARCHAR2, p_flow_status_code IN VARCHAR2, p_description IN VARCHAR2, p_creation_date IN DATE, p_created_by IN NUMBER, p_last_updated_by IN NUMBER, p_last_update_date IN DATE, p_last_update_login IN NUMBER, p_program_application_id IN NUMBER, p_program_id IN NUMBER, p_program_update_date IN DATE, p_request_id IN NUMBER, p_attribute_category IN VARCHAR2, p_attribute1 IN VARCHAR2, p_attribute2 IN VARCHAR2, p_attribute3 IN VARCHAR2, p_attribute4 IN VARCHAR2, p_attribute5 IN VARCHAR2, p_attribute6 IN VARCHAR2, p_attribute7 IN VARCHAR2, Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 22 of 136 p_attribute8 IN VARCHAR2, p_attribute9 IN VARCHAR2, p_attribute10 IN VARCHAR2, p_attribute11 IN VARCHAR2, p_attribute12 IN VARCHAR2, p_attribute13 IN VARCHAR2, p_attribute14 IN VARCHAR2, p_attribute15 IN VARCHAR2) IS CURSOR c IS SELECT header_id, org_id, order_number, ordered_date, order_type, customer_id, salesrep_id, price_list_id, currency_code, flow_status_code, description, creation_date, created_by, last_updated_by, last_update_date, last_update_login, program_application_id, program_id, program_update_date, request_id, attribute_category, attribute1, attribute2, attribute3, attribute4, attribute5, attribute6, attribute7, attribute8, attribute9, attribute10, attribute11, attribute12, attribute13, attribute14, attribute15 FROM cux_order_headers_all WHERE header_id = p_header_id FOR UPDATE OF header_id NOWAIT; rec c%ROWTYPE; BEGIN OPEN c; FETCH c INTO rec; IF (c%NOTFOUND) THEN CLOSE c; fnd_message.set_name('FND', 'FORM_RECORD_DELETED'); app_exception.raise_exception; END IF; CLOSE c; IF ((rec.header_id = p_header_id) AND ((rec.org_id = p_org_id) OR ((rec.org_id IS NULL) AND (p_org_id IS NULL))) AND ((rec.order_number = p_order_number) OR ((rec.order_number IS NULL) AND (p_order_number IS NULL))) AND ((rec.ordered_date = p_ordered_date) OR ((rec.ordered_date IS NULL) AND (p_ordered_date IS NULL))) AND ((rec.order_type = p_order_type) OR ((rec.order_type IS NULL) AND (p_order_type IS NULL))) AND ((rec.customer_id = p_customer_id) OR ((rec.customer_id IS NULL) AND (p_customer_id IS NULL))) AND ((rec.salesrep_id = p_salesrep_id) OR ((rec.salesrep_id IS NULL) AND (p_salesrep_id IS NULL))) AND ((rec.price_list_id = p_price_list_id) OR ((rec.price_list_id IS NULL) AND (p_price_list_id IS NULL))) AND Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 23 of 136 ((rec.currency_code = p_currency_code) OR ((rec.currency_code IS NULL) AND (p_currency_code IS NULL))) AND ((rec.flow_status_code = p_flow_status_code) OR ((rec.flow_status_code IS NULL) AND (p_flow_status_code IS NULL))) AND ((rec.description = p_description) OR ((rec.description IS NULL) AND (p_description IS NULL))) AND ((rec.creation_date = p_creation_date) OR ((rec.creation_date IS NULL) AND (p_creation_date IS NULL))) AND ((rec.created_by = p_created_by) OR ((rec.created_by IS NULL) AND (p_created_by IS NULL))) AND ((rec.last_updated_by = p_last_updated_by) OR ((rec.last_updated_by IS NULL) AND (p_last_updated_by IS NULL))) AND ((rec.last_update_date = p_last_update_date) OR ((rec.last_update_date IS NULL) AND (p_last_update_date IS NULL))) AND ((rec.last_update_login = p_last_update_login) OR ((rec.last_update_login IS NULL) AND (p_last_update_login IS NULL))) AND ((rec.program_application_id = p_program_application_id) OR ((rec.program_application_id IS NULL) AND (p_program_application_id IS NULL))) AND ((rec.program_id = p_program_id) OR ((rec.program_id IS NULL) AND (p_program_id IS NULL))) AND ((rec.program_update_date = p_program_update_date) OR ((rec.program_update_date IS NULL) AND (p_program_update_date IS NULL))) AND ((rec.request_id = p_request_id) OR ((rec.request_id IS NULL) AND (p_request_id IS NULL))) AND ((rec.attribute_category = p_attribute_category) OR ((rec.attribute_category IS NULL) AND (p_attribute_category IS NULL))) AND ((rec.attribute1 = p_attribute1) OR ((rec.attribute1 IS NULL) AND (p_attribute1 IS NULL))) AND ((rec.attribute2 = p_attribute2) OR ((rec.attribute2 IS NULL) AND (p_attribute2 IS NULL))) AND ((rec.attribute3 = p_attribute3) OR ((rec.attribute3 IS NULL) AND (p_attribute3 IS NULL))) AND ((rec.attribute4 = p_attribute4) OR ((rec.attribute4 IS NULL) AND (p_attribute4 IS NULL))) AND ((rec.attribute5 = p_attribute5) OR ((rec.attribute5 IS NULL) AND (p_attribute5 IS NULL))) AND ((rec.attribute6 = p_attribute6) OR ((rec.attribute6 IS NULL) AND (p_attribute6 IS NULL))) AND ((rec.attribute7 = p_attribute7) OR ((rec.attribute7 IS NULL) AND (p_attribute7 IS NULL))) AND ((rec.attribute8 = p_attribute8) OR ((rec.attribute8 IS NULL) AND (p_attribute8 IS NULL))) AND ((rec.attribute9 = p_attribute9) OR ((rec.attribute9 IS NULL) AND (p_attribute9 IS NULL))) AND ((rec.attribute10 = p_attribute10) OR ((rec.attribute10 IS NULL) AND (p_attribute10 IS NULL))) AND ((rec.attribute11 = p_attribute11) OR ((rec.attribute11 IS NULL) AND (p_attribute11 IS NULL))) AND ((rec.attribute12 = p_attribute12) OR ((rec.attribute12 IS NULL) AND (p_attribute12 IS NULL))) AND ((rec.attribute13 = p_attribute13) OR ((rec.attribute13 IS NULL) AND (p_attribute13 IS NULL))) AND ((rec.attribute14 = p_attribute14) OR ((rec.attribute14 IS NULL) AND (p_attribute14 IS NULL))) AND ((rec.attribute15 = p_attribute15) OR ((rec.attribute15 IS NULL) AND (p_attribute15 IS NULL)))) THEN NULL; ELSE fnd_message.set_name('FND', 'FORM_RECORD_CHANGED'); app_exception.raise_exception; END IF; END lock_row; /*===================================== ** PROCEDURE: update_row() **=====================================*/ PROCEDURE update_row(p_header_id IN NUMBER, p_org_id IN NUMBER, p_order_number IN NUMBER, p_ordered_date IN DATE, p_order_type IN VARCHAR2, p_customer_id IN NUMBER, p_salesrep_id IN NUMBER DEFAULT NULL, Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 24 of 136 p_price_list_id IN NUMBER DEFAULT NULL, p_currency_code IN VARCHAR2, p_flow_status_code IN VARCHAR2, p_description IN VARCHAR2 DEFAULT NULL, p_last_updated_by IN NUMBER, p_last_update_date IN DATE, p_last_update_login IN NUMBER DEFAULT NULL, p_program_application_id IN NUMBER DEFAULT NULL, p_program_id IN NUMBER DEFAULT NULL, p_program_update_date IN DATE DEFAULT NULL, p_request_id IN NUMBER DEFAULT NULL, p_attribute_category IN VARCHAR2 DEFAULT NULL, p_attribute1 IN VARCHAR2 DEFAULT NULL, p_attribute2 IN VARCHAR2 DEFAULT NULL, p_attribute3 IN VARCHAR2 DEFAULT NULL, p_attribute4 IN VARCHAR2 DEFAULT NULL, p_attribute5 IN VARCHAR2 DEFAULT NULL, p_attribute6 IN VARCHAR2 DEFAULT NULL, p_attribute7 IN VARCHAR2 DEFAULT NULL, p_attribute8 IN VARCHAR2 DEFAULT NULL, p_attribute9 IN VARCHAR2 DEFAULT NULL, p_attribute10 IN VARCHAR2 DEFAULT NULL, p_attribute11 IN VARCHAR2 DEFAULT NULL, p_attribute12 IN VARCHAR2 DEFAULT NULL, p_attribute13 IN VARCHAR2 DEFAULT NULL, p_attribute14 IN VARCHAR2 DEFAULT NULL, p_attribute15 IN VARCHAR2 DEFAULT NULL) IS BEGIN UPDATE cux_order_headers_all SET header_id = p_header_id, org_id = p_org_id, order_number = p_order_number, ordered_date = p_ordered_date, order_type = p_order_type, customer_id = p_customer_id, salesrep_id = p_salesrep_id, price_list_id = p_price_list_id, currency_code = p_currency_code, flow_status_code = p_flow_status_code, description = p_description, last_updated_by = p_last_updated_by, last_update_date = p_last_update_date, last_update_login = p_last_update_login, program_application_id = p_program_application_id, program_id = p_program_id, program_update_date = p_program_update_date, request_id = p_request_id, attribute_category = p_attribute_category, attribute1 = p_attribute1, attribute2 = p_attribute2, attribute3 = p_attribute3, attribute4 = p_attribute4, attribute5 = p_attribute5, attribute6 = p_attribute6, attribute7 = p_attribute7, attribute8 = p_attribute8, attribute9 = p_attribute9, attribute10 = p_attribute10, attribute11 = p_attribute11, attribute12 = p_attribute12, attribute13 = p_attribute13, attribute14 = p_attribute14, attribute15 = p_attribute15 WHERE header_id = p_header_id; IF (SQL%NOTFOUND) THEN RAISE no_data_found; END IF; END update_row; /*===================================== ** PROCEDURE: delete_row() **=====================================*/ PROCEDURE delete_row(p_header_id IN NUMBER) Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 25 of 136 IS BEGIN DELETE FROM cux_order_headers_all WHERE header_id = p_header_id; IF (SQL%NOTFOUND) THEN RAISE no_data_found; END IF; END delete_row; END; / 2.5. 一个简单的例子.从模版开始设计 2.5.1. 快速认识Forms Builder开发环境 1、 Object Navigator,分层次的对象管理,常用的有:Triggers触发器、Data Blocks 数据块(其下有Items字段、Triggers触发器)、Canvases画布、LOVs下拉列表、 Parameters参数、Program Units程序单元、Record Group记录组、Windows窗 体。 这些对象可以通过左边红框标记的按钮进行添加、删除。 2、 Property Palette,分类别的属性设置,主要属性后面用到再介绍。 3、 Canvases,设计各字段在画布上的布局。 快捷键F4:选中Object Navigator中的某个对象,F4可以调出Property Palette。 4、 Code Editor代码编辑器:编写PL/SQL代码的地方。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 26 of 136 2.5.2. 拷贝TEMPLATE.fmb 拷贝TEMPLATE.fmb,改名为CUXORDENT.fmb,打开之后把Form Name也改为 CUXORDENT,一定要保持一致性。 2.5.3. 删除多余对象 删除Data Blocks下的两个块——BLOCKNAME、DETAILBLOCK,它们是模版自带 的示例主从块。 删除Canvases下的一个画布——BLOCKNAME。 2.5.4. 修改Windows名称 修改Windows下的BLOCKNAME这个Window,在Property Palette中将Name改为 “SALES_ORDER”,将Title改为“Sales Order”。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 27 of 136 2.5.5. 修改2个触发器、1个程序单元 因为一个Forms有多个Window,所以需要明确指出哪个Window是主Window,这样 在关闭主Window的时候将退出整个Forms;此外,我们也须要在源代码中记录作者、 开发时间、版本等备注信息。这些是通过代码而非属性来控制的。 1、 修改Forms级触发器PRE-FORM 将其中的app_window.set_window_position('BLOCKNAME', 'FIRST_WINDOW');改为 app_window.set_window_position('SALES_ORDER', 'FIRST_WINDOW'); 同时修改上面几行代码中的作者、时间、版本、Form描述、应用简称,示例如下: FND_STANDARD.FORM_INFO('$Revision: 1.0.0 $', 'Sales Order Form', 'SCF', '$Date: 2007/02/28 11:02 $', '$Author: huajhua $'); 2、 修改Forms级触发器WHEN-NEW-FORM-INSTANCE 主要修改Form名字、版本和日期。 3、 修改Program Unit下app_custom中的close_window过程 将if (wnd = '') then app_window.close_first_window;改为 if (wnd = 'SALES_ORDER') then app_window.close_first_window; 2.5.6. 创建Block数据块 数据块,定义Form上的字段与数据库中的字段是如何对应的,同时定义块和字段的各 种操作特性——字段类型、长度、默认值、可否增删改查等等。 N: 在Data Blocks上右键,选择数据块创建向导: 之后第一步跳过,第二步保持默认的选择“Table or View” 第三步输入View名字CUX_ORDER_HEADERS_V,如果没有登录过,则会弹出登录 框: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 28 of 136 登录后Available Column将显示所有视图字段,点击>>全部选过来。 第四步跳过,第五步选择“Just create the data block”后Finish。 Forms Builder将自动将表字段的名称、类型、长度、是否必须等信息带到Block下的 Items中。 2.5.7. 设置Block属性及其Subclass 默认Block名字为视图名字,需要“精简”,此例改为“ORDER_HEADERS”,同时 需要设置块属性中的Subclass为Block: 此外,需要设置块的前后导航属性,本例目前仅有一个有意义的数据块,这里将 Previous和Next Navigation Data Block均设置为自身,这样在运行时,使用快捷键 Shift+Pageup和Shift+Pagedown时,均不会跳离本块。 子类:类似Java中的子类,用来继承Item类型和各种属性,在开发过程中,要严格设 置各种Item的子类,不得手工随意修改对象的属性,从而保持界面有一致的风格;设 置过子类的对象,图标上有个红色的箭头。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 29 of 136 2.5.8. 设置Item属性及其Subclass 设置需要显示Item和特殊Item的子类,同时设置部分Item的默认值: Item Subclass Required Initial Value 说明 ROW_ID ROW_ID 不显示 ORDER_NUMBER TEXT_ITEM Yes ORDERED_DATE TEXT_ITEM Yes $$DBDATE$$ 系统日期 ORDER_TYPE TEXT_ITEM Yes I CUSTOMER_NAME TEXT_ITEM Yes SALESREP_NAME TEXT_ITEM No PRICE_LIST_NAME TEXT_ITEM No CURRENCY_CODE TEXT_ITEM Yes FLOW_STATUS_CODE TEXT_ITEM Yes ENTERED DESCRIPTION TEXT_ITEM No 此外,为测试和学习方便,这里先把ORG_ID和CUSTOMER_ID的默认值设置为0。 以ORDERED_DATE为例,设置画面如下: 2.5.9. 创建Canvas画布 画布,用来设计各字段的布局,画布必需放置在Window上后才真正对用户“可 见”。最基本的画布为Content画布,一个Window必需有且仅有一个Content画布。 N: 在Data Blocks上右键,选择布局创建向导: 第一步选择New Canvas,Type选择Content: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 30 of 136 第二步选择要显示的字段: 第三步修改各字段的提示和显示长度: 后面两步跳过直至Finish。 在打开的画布设计界面,删除自动生成的Frame。 2.5.10. 设置画布属性和子类、调整布局 1、设置画布名字为SALES_ORDER,放置的Window为SALES_ORDER,子类为 Canvas: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 31 of 136 2、调整View和Canvas大小,Content画布这两者设置为一样大,即拉到重叠为止。 Canvas与View:Canvas是整个画布的大小,Item是放置在Canvas上的;View是这个 画布的可见部分,如果View小于Canvas,那么通常需要借助滚动条来查看整个 Canvas中的内容;可以通过菜单View中的Show View、Show Canvas来确定哪个框是 Canvas哪个框是View。 2.5.11. 调整布局 调整默认的布局,这里通过属性直接将Sales Person和Price List的宽度设置为1.8: 2.5.12. 调整Prompt提示 Oracle标准的Form中,列表形式的布局,提示放置在字段左边,居中对齐,并且距离 字段0.073;而表格形式的布局,提示放置在第一行字段上边,数字靠右,其他靠左, 并分别距离字段边缘0.05。以ORDER_NUMBER为例设置如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 32 of 136 其他字段可以按住Ctrl一起选中,然后一次性设置。 2.5.13. 设置Windows属性 设置Window“SALES_ORDER”的Primary Canvas为“SALES_ORDER”,该 Window的大小将自动调整为Content Canvas的大小: 2.5.14. 设置Form属性 设置Form的第一个导航块为ORDER_HEADERS,这样运行时,一进入Form,光标将 停在该块的第一个字段上: 2.6. 一个简单的例子.编写数据操作触发器 2.6.1. 编写数据操作Program Unit 该Program Unit主要用来调用之前步骤创建的数据库API。 1、 选中对象管理器中的Program Unit,点击左边的+,选择Package Spec,在Name 中输入ORDER_HEADERS_PRIVATE: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 33 of 136 2、 OK后,输入如下包头代码: PACKAGE ORDER_HEADERS_PRIVATE IS PROCEDURE insert_row; PROCEDURE lock_row; PROCEDURE update_row; PROCEDURE delete_row; END ORDER_HEADERS_PRIVATE; 3、 同1,选择Package Body,OK后输入如下包体代码,注意块字段的值的引用格式 “:块名.字段名”: PACKAGE BODY ORDER_HEADERS_PRIVATE IS /*===================================== ** PROCEDURE: insert_row() **=====================================*/ PROCEDURE insert_row IS BEGIN fnd_standard.set_who; IF :order_headers.header_id IS NULL THEN SELECT CUX_ORDER_HEADERS_S.NEXTVAL INTO :order_headers.header_id FROM DUAL; END IF; cux_order_headers_pkg.insert_row ( x_row_id => :order_headers.row_id ,x_header_id => :order_headers.header_id ,p_org_id => :order_headers.org_id ,p_order_number => :order_headers.order_number ,p_ordered_date => :order_headers.ordered_date ,p_order_type => :order_headers.order_type ,p_customer_id => :order_headers.customer_id ,p_salesrep_id => :order_headers.salesrep_id ,p_price_list_id => :order_headers.price_list_id ,p_currency_code => :order_headers.currency_code ,p_flow_status_code => :order_headers.flow_status_code ,p_description => :order_headers.description ,p_creation_date => :order_headers.creation_date ,p_created_by => :order_headers.created_by ,p_last_updated_by => :order_headers.last_updated_by ,p_last_update_date => :order_headers.last_update_date ,p_last_update_login => :order_headers.last_update_login Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 34 of 136 ,p_program_application_id => :order_headers.program_application_id ,p_program_id => :order_headers.program_id ,p_program_update_date => :order_headers.program_update_date ,p_request_id => :order_headers.request_id ,p_attribute_category => :order_headers.attribute_category ,p_attribute1 => :order_headers.attribute1 ,p_attribute2 => :order_headers.attribute2 ,p_attribute3 => :order_headers.attribute3 ,p_attribute4 => :order_headers.attribute4 ,p_attribute5 => :order_headers.attribute5 ,p_attribute6 => :order_headers.attribute6 ,p_attribute7 => :order_headers.attribute7 ,p_attribute8 => :order_headers.attribute8 ,p_attribute9 => :order_headers.attribute9 ,p_attribute10 => :order_headers.attribute10 ,p_attribute11 => :order_headers.attribute11 ,p_attribute12 => :order_headers.attribute12 ,p_attribute13 => :order_headers.attribute13 ,p_attribute14 => :order_headers.attribute14 ,p_attribute15 => :order_headers.attribute15 ); END insert_row; /*===================================== ** PROCEDURE: lock_row() **=====================================*/ PROCEDURE lock_row IS i NUMBER := 0; BEGIN LOOP BEGIN i := i + 1; cux_order_headers_pkg.lock_row( p_header_id => :order_headers.header_id ,p_org_id => :order_headers.org_id ,p_order_number => :order_headers.order_number ,p_ordered_date => :order_headers.ordered_date ,p_order_type => :order_headers.order_type ,p_customer_id => :order_headers.customer_id ,p_salesrep_id => :order_headers.salesrep_id ,p_price_list_id => :order_headers.price_list_id ,p_currency_code => :order_headers.currency_code ,p_flow_status_code => :order_headers.flow_status_code ,p_description => :order_headers.description ,p_creation_date => :order_headers.creation_date ,p_created_by => :order_headers.created_by ,p_last_updated_by => :order_headers.last_updated_by ,p_last_update_date => :order_headers.last_update_date ,p_last_update_login => :order_headers.last_update_login ,p_program_application_id => :order_headers.program_application_id ,p_program_id => :order_headers.program_id ,p_program_update_date => :order_headers.program_update_date ,p_request_id => :order_headers.request_id ,p_attribute_category => :order_headers.attribute_category ,p_attribute1 => :order_headers.attribute1 ,p_attribute2 => :order_headers.attribute2 ,p_attribute3 => :order_headers.attribute3 ,p_attribute4 => :order_headers.attribute4 ,p_attribute5 => :order_headers.attribute5 ,p_attribute6 => :order_headers.attribute6 ,p_attribute7 => :order_headers.attribute7 ,p_attribute8 => :order_headers.attribute8 ,p_attribute9 => :order_headers.attribute9 ,p_attribute10 => :order_headers.attribute10 ,p_attribute11 => :order_headers.attribute11 ,p_attribute12 => :order_headers.attribute12 ,p_attribute13 => :order_headers.attribute13 ,p_attribute14 => :order_headers.attribute14 ,p_attribute15 => :order_headers.attribute15 ); RETURN; EXCEPTION WHEN app_exception.record_lock_exception THEN app_exception.record_lock_error(i); END; END LOOP; END lock_row; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 35 of 136 /*===================================== ** PROCEDURE: update_row() **=====================================*/ PROCEDURE update_row IS BEGIN fnd_standard.set_who; cux_order_headers_pkg.update_row( p_header_id => :order_headers.header_id ,p_org_id => :order_headers.org_id ,p_order_number => :order_headers.order_number ,p_ordered_date => :order_headers.ordered_date ,p_order_type => :order_headers.order_type ,p_customer_id => :order_headers.customer_id ,p_salesrep_id => :order_headers.salesrep_id ,p_price_list_id => :order_headers.price_list_id ,p_currency_code => :order_headers.currency_code ,p_flow_status_code => :order_headers.flow_status_code ,p_description => :order_headers.description ,p_last_updated_by => :order_headers.last_updated_by ,p_last_update_date => :order_headers.last_update_date ,p_last_update_login => :order_headers.last_update_login ,p_program_application_id => :order_headers.program_application_id ,p_program_id => :order_headers.program_id ,p_program_update_date => :order_headers.program_update_date ,p_request_id => :order_headers.request_id ,p_attribute_category => :order_headers.attribute_category ,p_attribute1 => :order_headers.attribute1 ,p_attribute2 => :order_headers.attribute2 ,p_attribute3 => :order_headers.attribute3 ,p_attribute4 => :order_headers.attribute4 ,p_attribute5 => :order_headers.attribute5 ,p_attribute6 => :order_headers.attribute6 ,p_attribute7 => :order_headers.attribute7 ,p_attribute8 => :order_headers.attribute8 ,p_attribute9 => :order_headers.attribute9 ,p_attribute10 => :order_headers.attribute10 ,p_attribute11 => :order_headers.attribute11 ,p_attribute12 => :order_headers.attribute12 ,p_attribute13 => :order_headers.attribute13 ,p_attribute14 => :order_headers.attribute14 ,p_attribute15 => :order_headers.attribute15 ); END update_row; /*===================================== ** PROCEDURE: delete_row() **=====================================*/ PROCEDURE delete_row IS BEGIN cux_order_headers_pkg.delete_row( p_header_id => :order_headers.header_id ); END delete_row; END ORDER_HEADERS_PRIVATE; 4、 点击PL/SQL Editor窗口顶上的Compile按钮,必须保证没有错误: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 36 of 136 2.6.2. 编写块的On触发器 1、 选中Block“ORDER_HEADERS”下的Triggers,点击左边的+,选择ON- INSERT触发器: 2、 OK后输入如下代码: ORDER_HEADERS_PRIVATE.insert_row; 3、 同1,选择ON-LOCK后输入如下代码: ORDER_HEADERS_PRIVATE.lock_row; 4、 同1,选择ON-UPDATE后输入如下代码: ORDER_HEADERS_PRIVATE.update_row; 5、 同1,选择ON-DELETE后输入如下代码: ORDER_HEADERS_PRIVATE.delete_row; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 37 of 136 理解:对于基于非单表视图的Block来说,这里的4个触发器是必须要写的,具体代码 可以直接写在触发器内,但为了模块化管理和今后维护方便,这里分为三层调用,触 发器中调用Program Unit中的过程,Program Unit中调用数据库Package中的过程。 2.7. 一个简单的例子.上传&编译 2.7.1. 步骤 1、 以应用OS用户用telnet工具登录服务器,使用如下命令获得要上传的路径: echo $SCF_TOP/forms/US 假如得到/d1/oracle/prodappl/scf/1.0.0/forms/US 2、 以应用OS用户用FTP工具登录服务器,使用如下命令获得要上传的路径,将 CUXORDENT.fmb以二进制方式上传到/d1/oracle/prodappl/scf/1.0.0/forms。 3、 回到telnet工具,使用如下命令,必须进入Forms源文件目录: cd $AU_TOP/forms/US 4、 编译Form的命令如下: f60gen $SCF_TOP/forms/US/CUXORDENT apps/password output_file=$SCF_TOP/forms/US/CUXORDENT 成功编译的话,最后一行将显示: Created form file /d1/oracle/prodappl/scf/1.0.0/forms/US/CUXORDENT.fmx 另注:Window版本的EBS,直接在客户端Forms Builder中按CTRL+R,当然肯定无 法直接运行,需要我们不断按Esc键取消运行时错误信息(非编译错误!),不过可以 生成fmx文件,将其直接拷贝到服务器的 %SCF_TOP%/forms/US目录下。 2.8. 一个简单的例子.在EBS中注册运行 2.8.1. 登录EBS 用至少需要有Application Developer职责的用户登录,比如Sysadmin用户,然后选择 Application Developer职责。 2.8.2. 注册Form N: DEV/Application/Form 1、 这里的Form、源文件中Form名字、文件名三者要一致,这里是CUXORDENT。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 38 of 136 2、 选择合适的Application,通常用企业的客户化应用,这里是SCF Customized System。 3、 User Form Name可以输入一个友好的名字,建议直接用Form名字。 2.8.3. 定义Function N: DEV/Application/Function 1、输入Function名字和Form名字一致,输入一个友好的名字SCF Sales Order 2、切换到Form标签页,选择刚才定义的Form后按Ctrl+S保存。 2.8.4. 加入Menu N: DEV/Application/Menu 假定我们用SCF Customized System Super User这个职责来测试,该职责对应的菜单 为SCF_SUPER_USER。 查出菜单SCF_SUPER_USER,在下面添加一行: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 基于EBS的Forms开发过程 39 of 136 2.8.5. 运行Form 切换到SCF Customized System Super User职责,选择Sales Order菜单,可以看到做 好的Form: 因为2个隐藏的必需字段我们暂时没有赋值,而是给了无意义的默认值0,其他可见字 段也未做任何有效性控制,所以目前该Form还不能进行实际数据录入操作,不过还是 可以测试下打开、字段导航、录入记录、保存记录、关闭等正常操作;但查询记录、 修改记录则没法做,暂时只能用SQL直接从表cux_order_headers_all中查看。 字段导航:运行时用Tab或Shift+Tab键可以在字段间导航,默认情况下,导航的顺序 与我们开发时块中Item的排列顺序一致(并非和布局中的顺序一致);我们可以设置 Item的属性Keyboard Navigable为No来禁止键盘导航,也可以设置Previous/Next Navigation Item来强制改变前后导航顺序。 截止目前为止的Form源代码请参见CUXORDENT.01.fmb,这个例子最重要,必需保 证能够自己做出来;本文也没有面面俱到的详细介绍,还需读者耐心领会。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 参数、List、LOV、字段和记录控制、日历 40 of 136 3. 参数、List、LOV、字段和记录控制、日历 3.1. 例子:Parameter参数 3.1.1. 关于Parameter Parameter是Form级参数,外部程序在调用Form时,也可传递具体的参数值,从而达 到对Form的某种控制;如果不考虑外部程序传递,完全可以创建一个不基于数据库、 字段不显示的特殊块来替代Parameter,实际工作中也经常如此。不过这里仍然采用 Parameter。 3.1.2. 创建Parameter N: 选中对象浏览器中的Parameters,然后点击左边工具栏上的“+”新增Parameter, 并改名为ORG_ID,类型改为Number,子类不用设置: 3.1.3. 初始化Parameter 通常对Parameter的初始化需要在Form级触发器Pre-Form中完成。这里我们根据当前 用户的Profile来取得其对应的OU。追加的Pre-Form代码如下: :parameter.org_id := fnd_profile.value('ORG_ID'); 什么是Profile?Profile翻译为预制文件,它是特殊的系统参数,说它特殊,是因为其可 以设置在不同的层面上,优先级从高到低主要为:用户、职责、应用、系统。 3.1.4. 使用Parameter 本例用来给ORDER_HEADERS块的ORG_ID字段赋初始值: Tips:做完这一步,可以上传编译运行看看,尤其是初学者,一定要做一步就放到EBS 跑跑看,别太自信,否则你出的问题专家也解决不了的,下同。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 参数、List、LOV、字段和记录控制、日历 41 of 136 3.2. 例子:List值列表 3.2.1. 关于List List是一种特殊的Item,可限制字段的值在某一范围内,通常这个范围是固定的,而且 数量通常少于10个。 List值包含两部分:后台存储的Value + 前台显示的含义。 List字段会直接显示一个下来框,用户必须使用下来框来选择该字段的值,不能自己输 入。 3.2.2. 创建List N: 选中Item,这里是ORDER_HEADERS块的ORDER_TYPE,把子类改为List,点击 Elements in List输入值列表: 后台存储的Value 前台显示的含义 I Internal R External Mapping of Other Values这个属性要特别注意,它的意思是,如果从数据库中读出来 的数据,不在这个列表内(这里是I和E),那么要映射为I还是E,如果不映射,那么 该条记录将无法在Form中显示出来,表现出来的现象就是“数据库和View中明明 有,界面却看不到”。 对ORDER_HEADERS块的FLOW_STATUS_CODE字段,我们也用List,值列表为: 后台存储的Value 前台显示的含义 ENTERED Entered BOOKED Booked PARTIAL Partial COMPLETE Complete 3.2.3. 删除List条目 List中的条目,只要鼠标一点就自动生成,很垃圾,可以用shift+ctrl+<删除,因为这 个快捷键一般人不知道,所以只好重新Item。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 参数、List、LOV、字段和记录控制、日历 42 of 136 3.3. 例子:LOV窗口式值列表 3.3.1. 关于LOV LOV(List of Value)是窗口式值列表,也是用来限制字段的值在某一范围内,这个 范围在Form中用“记录组”来表示,而记录组通常来源于数据库的表;虽然也有类似 List的静态记录组,但很少使用。 LOV不是特殊的Item,Item本身通常还是Text Item,LOV仅是Item的一个属性,设有 LOV的Item,光标进入到该Item后才会显示一个“...”的按钮。 LOV的值与List不同了,其可以有多列,而List在用户看来只有1列。 List的值直接就是Item的值,而Lov不同,它的每一列都可以指定一个目标,也就是每 一列都可以返回给任何一个Item,未必要返回给设有LOV属性的Item。 LOV的验证采用“可见”的第一列,也就是用户可以自己输入,只要输入的值在第一 列中即可;如果用户输入的值可匹配到多个值,那么将自动弹出LOV供用户选择,如 果只匹配到一个,就不再弹出LOV,达到自动选择了。 LOV也可以不验证,用户可以随便输入,这个一般用在特殊用途比如日期、弹性域、 或者其他特殊业务需求。 不管是自动选择还是手工选择,只有“选择”了,才会触发LOV中各列的值返回给对 应的目标Item;而不验证的LOV就要注意了,因为其可能没有经过“选择”这一步。 LOV还有个麻烦的问题,如果清空了Item的值,因为空值不触发验证,这样之前返回 到各个目标Item上的值并没有自动被清空,需要写代码处理。 3.3.2. 创建LOV N: 在对象浏览器中(最好是在要设置LOV的Item上,这里是CUSTOMER_NAME) 右键,选择LOV Wizard。 第1步选择New Record Group based on a query。 第2步输入查询SQL: SELECT cust.customer_id, cust.customer_name, cust.customer_type FROM ra_customers cust 第3步选择所有列。 第4步设置显示的列名、列框、返回值: 数据库字段 列名 宽 Return Value customer_id 0 ORDER_HEADERS.CUSTOMER_ID customer_name Customer Name ORDER_HEADERS.CUSTOMER_NAME customer_number Customer Number customer_type Type ORDER_HEADERS.ORDER_TYPE Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 参数、List、LOV、字段和记录控制、日历 43 of 136 第5步输入整个LOV的窗口标题:Customers。 第6步跳过。 第7步的意思是,这个LOV要挂在哪个Item上,我们希望是Customer Name: 3.3.3. 改进LOV 向导创建的LOV没有子类,命名也是乱七八糟,需要修改: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 参数、List、LOV、字段和记录控制、日历 44 of 136 1、 把生成的记录组和LOV都改名为Customers 2、 设置LOV的子类为LOV 我们可以看看Assigned Item的LOV属性: 注意Validate from List,意思是用户输入的值必须在LOV第一可见列的值范围内。 3.3.4. 完善例子 本例还需要完成销售员、价目表、货币的LOV,请读者自行完成,这里列出关键点: SALESREP SELECT sal.salesrep_id, sal.NAME FROM ra_salesreps sal 数据库字段 列名 宽 Return Value salesrep_id 0 ORDER_HEADERS.SALESREP_ID NAME Name ORDER_HEADERS.SALESREP_NAME CURRENCY SELECT cur.currency_code FROM fnd_currencies_vl cur WHERE cur.enabled_flag = 'Y' AND cur.currency_flag = 'Y' 数据库字段 列名 宽 Return Value currency_code Currency ORDER_HEADERS.CURRENCY_CODE PRICE_LIST SELECT qlh.list_header_id, qlh.NAME FROM qp_list_headers_vl qlh WHERE qlh.start_date_active <= SYSDATE AND nvl(qlh.end_date_active, SYSDATE) >= SYSDATE 数据库字段 列名 宽 Return Value list_header_id Currency 0 ORDER_HEADERS.PRICE_LIST_ID NAME NAME ORDER_HEADERS.PRICE_LIST_NAME 3.4. 例子:字段和记录控制 3.4.1. 字段属性 字段的控制,最常见的就是默认值、是否必需、是否可以更新、是否可以F11查询。其 他的都可以顾名思义,可直接看各个属性。本例主要设置Order Number、Ordered Date、Order Type、Currency、Status字段的不可更新属性: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 参数、List、LOV、字段和记录控制、日历 45 of 136 对于Status字段,用户是不可干预的,输入时状态必须是Entered,所以还需要设置其 为不可插入: 3.4.2. 字段控制 Customer、Sales Person、Price List在状态为非“Entered”的情况下,才不可更新 的,这需要用代码实现。在Block的WHEN-NEW-RECORD-INSTANCE触发器中写入 下代码,最好写成program unit: if :ORDER_HEADERS.FLOW_STATUS_CODE = 'ENTERED' then app_item_property.set_property('ORDER_HEADERS.CUSTOMER_NAME',UPDATE_ALLOWED,PROPERTY_TRUE); app_item_property.set_property('ORDER_HEADERS.SALESREP_NAME',UPDATE_ALLOWED,PROPERTY_TRUE); app_item_property.set_property('ORDER_HEADERS.PRICE_LIST_NAME',UPDATE_ALLOWED,PROPERTY_TRUE); else app_item_property.set_property('ORDER_HEADERS.CUSTOMER_NAME',UPDATE_ALLOWED,PROPERTY_FALSE); app_item_property.set_property('ORDER_HEADERS.SALESREP_NAME',UPDATE_ALLOWED,PROPERTY_FALSE); app_item_property.set_property('ORDER_HEADERS.PRICE_LIST_NAME',UPDATE_ALLOWED,PROPERTY_FALSE); end if; 注:WHEN-NEW-RECORD-INSTANCE的执行层次要改为Before,为什么? 3.4.3. 记录控制 记录的控制,最常见就是显示条数、是否可增/删/改/查、是否可以F11查询、是否显 示滚动条。本例需要实现,在状态为非“Entered”的情况下,记录不可删除,这样需 要在Block的WHEN-NEW-RECORD-INSTANCE触发器中追加代码(红色部分),最 好写成program unit: if :ORDER_HEADERS.FLOW_STATUS_CODE = 'ENTERED' then app_item_property.set_property('ORDER_HEADERS.CUSTOMER_NAME',UPDATE_ALLOWED,PROPERTY_TRUE); app_item_property.set_property('ORDER_HEADERS.SALESREP_NAME',UPDATE_ALLOWED,PROPERTY_TRUE); app_item_property.set_property('ORDER_HEADERS.PRICE_LIST_NAME',UPDATE_ALLOWED,PROPERTY_TRUE); set_block_property('ORDER_HEADERS',DELETE_ALLOWED,PROPERTY_TRUE); else app_item_property.set_property('ORDER_HEADERS.CUSTOMER_NAME',UPDATE_ALLOWED,PROPERTY_FALSE); app_item_property.set_property('ORDER_HEADERS.SALESREP_NAME',UPDATE_ALLOWED,PROPERTY_FALSE); app_item_property.set_property('ORDER_HEADERS.PRICE_LIST_NAME',UPDATE_ALLOWED,PROPERTY_FALSE); set_block_property('ORDER_HEADERS',DELETE_ALLOWED,PROPERTY_FALSE); end if; Tips:如何通过代码控制字段和块的属性,可直接查这两个过程的在线帮助。 3.5. 例子:日历 3.5.1. 日历控件 Form中没有日历控件,日期的选择是通过一个特殊的Windowss实现的,因为系统封 装的比较好,我们需要按照如下三个步骤实现日期的选择: 1、 编写Item的KEY-LISTVAL触发器:calendar.show; 2、 设置Item的List of Values属性:ENABLE_LIST_LAMP 3、 设置Item的Validate from List属性:No Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 参数、List、LOV、字段和记录控制、日历 46 of 136 3.6. 上传&编译&运行 到目前为止,该Form可以录入、查询、修改、删除、导航: 截止目前为止的Form源代码请见CUXORDENT.02.fmb。 不过还相当粗糙,缺少查询界面,缺少订单行…… Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 47 of 136 4. 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 4.1. 例子:销售订单行 4.1.1. 创建数据库对象 与订单头类似,表、索引、序列、表注册、视图的代码参考cux_order_lines_all.sql。 表操作API代码参考cux_order_lines_pkg.pck。 4.1.2. 创建数据块ORDER_LINES 从CUX_ORDER_LINES_V中选择所有字段,并设置块子类。 4.1.3. 增加行指示Item 手工新建Item,名字和子类都是CURRENT_RECORD_INDICATOR。 4.1.4. 设置Item属性及其Subclass 设置需要显示Item和特殊Item的子类,同时设置部分Item的默认值: Item Subclass Required Initial Value 说明 ROW_ID ROW_ID 不显示 ORG_ID Yes :PARAMETER.ORG_ID NEED_BY_DATE TEXT_ITEM Yes $$DBDATE$$ 系统日期 ORGANIZATION _CODE TEXT_ITEM Yes ITEM_CODE TEXT_ITEM Yes 其他显示字段(见下面)的子类为TEXT_ITEM。 为使例子能够顺利运行,先设置HEADER_ID的初始值为0。 4.1.5. 创建Canvas画布 向导第一步选SALES_ORDER画布,字段为CURRENT_RECORD_INDICATOR、 ORGANIZATION_CODE、ITEM_CODE、NEED_BY_DATE、QUANTITY、 PRICE、SHIP_DATE、BILL_DATE、DESCRIPTION。 显示风格为Tabular,即二维表形式,行数为8行。 4.1.6. 调整布局、Prompt提示 调整Item大小和画布,结果如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 48 of 136 其中的Quantity和Price,因为是数字,通常右对齐,设置属性如下: 而且他们的Prompt也需要靠右了。 4.1.7. 设置头、行块互为前后导航块 ORDER_LINES属性: ORDER_HEADERS属性: 4.1.8. 创建LOV 发货组织和物料需要使用LOV: SHIP_ORG SELECT org.organization_id, org.organization_code FROM org_organization_definitions org WHERE org.operating_unit = :parameter.org_id 数据库字段 列名 宽 Return Value organization_id 0 ORDER_HEADERS.organization_id organization_code Organization ORDER_HEADERS.organization_code Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 49 of 136 ITEMS SELECT mst.inventory_item_id, mst.concatenated_segments item_code FROM mtl_system_items_vl mst WHERE mst.organization_id = :order_lines.organization_id ORDER BY mst.concatenated_segments 数据库字段 列名 宽 Return Value inventory_item_id 0 ORDER_HEADERS.item_id item_code Item Code ORDER_HEADERS.ITEM_CODE 4.1.9. 上传&编译&运行 运行结果如下: 现在头和行是不相干的两个块,可以独立进行增删改,我们至此学习了两种风格的数 据块。代码请见CUXORDENT.03.fmb。 4.2. 例子:Master-Detail主从块 4.2.1. 关于主从块 单据通常都可以划分为“头”与“行”的关系,在表设计中表现为“一对多”的主外 键关系,在Form中的表现形式就是“主从块”。主从块有以下一些特征: 1、 通常希望输入完主块才允许用户输入从块。 2、 删除主块记录前,必须先删除从块记录。 3、 保存的时候,系统则不一定先保存主块,而是根据块在对象浏览器中的顺序。 4、 查询出主块记录时,通常希望自动带出明细块记录。 5、 在滚动主块记录时,如果从块还未保存,则无法移动。 6、 主从块都是基于数据库的,并且至少有一个可导航的字段。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 50 of 136 4.2.2. 创建主从关系 主从块的创建异常简单。 N: 选中主块ORDER_HEADERS下的Relations,从左边工具栏点击“+” 选择Detail块为ORDER_LINES、选中Prevent Maserless Operations、输入Join Condition:ORDER_LINES.HEADER_ID = ORDER_HEADERS.HEADER_ID。 确定后,自动完成如下工作: 1、 创建一个名为“ORDER_HEADERS_ORDER_LINES”的Relation 2、 创建一个Form级触发器ON-CLEAR-DETAILS 3、 创建两个主块级触发器ON-POPULATE-DETAILS和ON-CHECK-DELETE- MASTER 4、 创建一个过程Query_Master_Details 5、 设置从块关联Item的Copy Value from Item属性 这个时候我们可以去掉HEADER_ID的初始值0了。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 51 of 136 Relation的属性和自动生成的3个触发器,读者请自行研究。 4.2.3. 上传&编译&运行 运行结果如下: 现在两个块是有关系了,必须创建完主块才能创建从块。不过从块中的字段和记录控 制还需要完善。截止目前代码请见CUXORDENT.04.fmb。 4.3. 例子:滚动条 4.3.1. 关于滚动条 当Item在一个指定大小的区域内无法完整显示时,需要借助滚动条;根据需要可以用 水平滚动条或垂直滚动条。Form中的滚动条有两个相互独立的来源: 1、 块的滚动条 可决定是否为该块显示滚动条,垂直或者水平只能任选其一,基本上都是垂直(水平 的我还不知道怎么用,汗),用来滚动记录。比如实际记录有10条,而块属性设置显 示8条记录,如果不借助滚动条就显得不直观,记录间的导航也不方便。 2、 画布的滚动条 画布有两个“区域”,一是Canvas——画布自身的大小,所有放在该画布上的Item, 不能超越Canvas的边界;二是View——画布在计算机屏幕上的固定的、可见区域,如 果View小于Canvas,那么需要借助滚动条来“挪动”Canvas,使其他Item也能有机 会显示在View中。 画布的滚动条可以有垂直或者水平,可以同时显示。内容画布没有滚动条,所以如果 Item放置在内容画布上,必须保证水平方向能够全部显示在View中。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 52 of 136 4.3.2. 设置块滚动条 N: 选中块(这里是ORDER_LINES),F4调出属性面板,设置显示Scrollbar属性为 Yes,Scrollbar所在Canvas为SALES_ORDER: 4.3.3. 上传&编译&运行 运行结果如下: 截止目前代码请见CUXORDENT.05.fmb。因为内容画布没有滚动条,如果我们要显示 的Item很多,水平方向放不下,那么则需要改用其他画布了,首选“堆叠画布”。 4.4. 例子:Stacked画布 4.4.1. 创建堆叠画布 在需要显示的Item比较多、内容画布显示不下的时候,我们需要使用一个或多个堆叠 画布来处理。通常我们需要判断,哪些Item保留在内容画布上,不随着水平滚动条滚 动,其他Item需要移到新的Stacked上,通常行指示符、弹性域是要保留的,其他字段 则看情况。创建画布有两种方式。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 53 of 136 N1: 启动画布创建向导,选择New Stacked,这个比较适用于调整布局前的首次创建: N2: 在对象浏览器中选中Canvases,点击左边工具栏的“+”,手工创建,然后修改名 字和子类如下图: 4.4.2. 设置Item到新画布 除了行指示符,我们希望发运组织和物料不随滚动条滚动。这样我们设置其他Item的 画布属性为新建的ORDER_LINES_STACKED: 4.4.3. 调整堆叠画布 堆叠画布的调整需要符合如下要求: 1、 View在Canvas的位置是X=0,Y=0 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 54 of 136 2、 Canvas左右都无空白区域,也就是全部被Item占满,第一个Item的X位置为0, Canvas的Width = 所有Item的Width之和 3、 字段顶部基本无空白,即Item的Y位置是标准高度0.25,用来放Prompt 4、 Canvas底部留出滚动条的位置,通常留0.2,这样如果有8行数据,总高是2.45。 5、 View的高度和Canvas一致,宽度则看实际情况 6、 设置画布的属性,使其仅显示水平滚动条 7、 设置画布的属性,使其和主画布置于同一Window Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 55 of 136 4.4.4. 调整堆叠画布在主画布的位置 1、 切换到主画布,选择菜单View/Stacked Views,选中堆叠画布: OK后堆叠画布显示在主画布的左上角: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 56 of 136 这个需要我们调整到合适位置,即调整到Item_Code位置后,可以手工拖动(麻 烦),也可通过设置属性。 2、 考察下当前Item Code的位置属性X、Y和宽度属性Width: 这样我们可以得出堆叠画布的位置X=.575 + 1 = 1.575,Y=3.138 – 0.25 = 2.888: 3、 继续调整主画布至“整洁美观”,把头块的部分字段调整为同一行,这样会空出 一大片位置,然后选中下面所有的内容,包括堆叠画布和滚动条,整体往上移: Tips:可以使用菜单Arrange来对齐、分布Item,要掌握Distribute和Stack: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 57 of 136 4.4.5. 上传&编译&运行 运行结果如下: 截止目前代码请见CUXORDENT.06.fmb,现在尚缺弹性域、符合业务需要的字段和记 录控制。 4.5. 画布小结 4.5.1. 子类与画布 EBS通过进一步“标准化”Form Builder自身的对象,提供了丰富的“子类”,这个和 Java等OO语言中的子类是相似的,其实际上是子对象! 对于画布来说,原始类型和子类对应如下: 原始类型 子类 说明 Content Canvas 内容画布,一个Windows上有且仅有一个Content画布 Stacked Canvss_Stacked 普通堆叠画布,属性Raise on Entry为No,意思是当导航到该画布的任一Item 时,仅当该Item无法全部展示在View中,那么整个View将自动移动到所有画布的 顶层,也就是全部显示 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 58 of 136 原始类型 子类 说明 Canvss_Stacked_Fixed 固定堆叠画布,属性Raise on Entry为Yes,意思是当导航到该画布的任一Item 时,那么整个View将自动移动到所有画布的顶层,也就是全部显示 Tab Tab_Canvas Tab画布,其本身不能放置Item,Item必须放在Tab Page上 Tab Page Tab_Page 标签页,如果Item直接放置在Tab画布上,那么点击不同的标签,将自动导航到该 标签的第一个可导航的Item,反之,如果导航到某标签的Item,那么也将自动切 换到该标签页;Tab Page使用起来类似内容画布,如果Item太多,需要借助 Stacked画布,这个时候,就没法“自动导航”、“自动切换”了,必须用代码去 响应Tab页的切换事件 V&H Toolbar 垂直和水平工具条,EBS开发中没用 4.5.2. 从UI角度看对象关系 刚开始做Form,通常搞不清楚Item、块、画布、Window、Form这些对象的关系,有 个简单的办法可以帮助大家了解。选择菜单View/Visual View,可以看的比较清楚: 可以看出,一个Form可以有任意个Window;一个Window上可以有任意个画布,反 过来一个画布只能放置在一个Window上,另外一个这里看不出的限制是一个 Windows上有且仅有一个Content画布;一个画布上可以放置任意多个Item,一个块 的Item,可以分步在不同的画布上,所以块和画布没有什么必然的关系。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 59 of 136 4.6. 例子:Tab画布 4.6.1. 创建Tab画布和标签页 创建Tab画布的步骤和其他画布一样,同样需要设置其Window属性,不过其子类为 Tab_Canvas;然后在其下创建标签页,我们创建了2个,子类为Tab_Page: 4.6.2. 设置Item到标签页并调整 为了练习,假定需求比较“变态”,要把原来Header区的描述字段放在Headers标签 页上,原来Line区的内容全部放到Lines标签页上,那么需要做如下设置: 1、 设置Header区描述字段的物理显示属性到Headers标签页: 2、 同理,把原先在内容画布上的行指示符等固定的Item也设置到Lines标签页。 3、 同时,把Order_Lines块的滚动条也设置到Lines标签页。 4、 单独调整Tab画布,大约如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 60 of 136 4.6.3. 调整主画布 1、 切换到主画布,先通过菜单View/Stacked Views,显示TAB_DEMO画布、隐藏 ORDER_LINES_STACKED画布,这样先调整TAB_DEMO画布到合适位置: Tips:在菜单View/Stacked Views中,按住Ctrl键来选中或取消画布。 2、 再通过菜单View/Stacked Views,把ORDER_LINES_STACKED画布也显示出 来,调整到合适位置: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 61 of 136 麻烦:Tab+Stacked,不是所见即所得,看起来是对齐的,实际运行起来不对齐,所以 需要耐心,不断运行、调整、再运行再调整….. Tips:如果希望在内容画布上选中堆叠画布或者Tab画布进行位置的微调,一般很难, 可以通过先在对象浏览器中选中,然后切换回来。 4.6.4. 上传&编译&运行 运行结果如下: 可以看到,如果不加控制,即使切换到Lines页,堆叠画布也不显示,必须用Tab键导 航,才会使其Raise出来。代码请见CUXORDENT.07.fmb。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 62 of 136 4.7. 例子:控制Tab画布 4.7.1. 控制思路 实际上Tab画布及其标签页和堆叠画布完全是独立的,哪个堆叠画布和哪个标签页对 应,必须通过代码来处理。 当标签页切换时,我们需要响应切换事件,决定该显示哪些堆叠画布、隐藏哪些堆叠 画布;反过来,当导航到堆叠画布上的Item时,因为会导致该画布Raise出来,我们需 要响应Item的导航事件,决定哪个标签页成为当前。 4.7.2. Tab控制代码 我们的例子比较简单,当切换到Headers标签页时,隐藏ORDER_LINES_STACKED 画布,切换到Lines标签页时,显示ORDER_LINES_STACKED画布;要注意Form初 始时,也需要默认一下当前标签页,也需要隐藏ORDER_LINES_STACKED画布。 在Form级WHEN-NEW-FORM-INSTANCE中追加: SET_CANVAS_PROPERTY('TAB_DEMO', TOPMOST_TAB_PAGE, 'HEADERS'); hide_view('ORDER_LINES_STACKED'); 新建Form级WHEN-TAB-PAGE-CHANGED触发器,该触发器是用鼠标点时才会触 发;另外注意几个Form标准过程的使用: IF :system.tab_previous_page = 'HEADERS' THEN validate(block_scope); IF :system.MODE = 'ENTER-QUERY' OR NOT form_success THEN --Message here set_canvas_property('TAB_DEMO', topmost_tab_page, :system.tab_previous_page); RETURN; END IF; ELSIF :system.tab_previous_page = 'LINES' THEN validate(block_scope); IF :system.MODE = 'ENTER-QUERY' OR NOT form_success THEN --Message here set_canvas_property('TAB_DEMO', topmost_tab_page, :system.tab_previous_page); RETURN; END IF; END IF; IF :system.tab_new_page = 'LINES' THEN show_view('ORDER_LINES_STACKED'); go_item('ORDER_LINES.ORGANIZATION_CODE'); ELSIF :system.tab_new_page = 'HEADERS' THEN hide_view('ORDER_LINES_STACKED'); go_item('ORDER_HEADERS.DESCRIPTION'); END IF; Tips:在Forms Builder中通过Help菜单,输入过程名,可查看具体的解释和Example。 本例中,如果在头块中使用Shift+Pagedown切换到行块,尽管已经切换到Lines标签 页了,但ORDER_LINES_STACKED还是没有显示。为解决此问题,需要响应行块的 第一个可导航的Item(这里是ORGANIZATION_CODE)的WHEN-NEW-ITEM- INSTANCE事件,写入代码: show_view('ORDER_LINES_STACKED'); Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 63 of 136 Tips:通过Jinitiator的Help/Keyboard Help,可以查看常用的快捷键。 4.7.3. 上传&编译&运行 运行结果如下: 可以正常使用了,怎么导航Item和块都没问题。代码请见CUXORDENT.08.fmb。 4.8. 例子:多行文本框 如果需要输入的文本比较多,单行输入可能不够又不直观,可以设置Item的Multi- Line属性为Yes,如本范例的头块Description字段。调整高度后运行结果如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 行指示符、主从块、滚动条、Stacked&Tab画布、多行文本 64 of 136 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Lov查询、块查询、Button 65 of 136 5. Lov查询、块查询、Button 5.1. 查询原理 Form标准功能支持F11查询,只要我们按下F11,输入条件值即可,再按Ctrl+F11就可 以执行查询。 5.1.1. F11查询原理 F11,这个时候的Block其实是处于Enter-query状态,输入的东西Form会自动拼成 Where语句(当然还要加上原来的default where,如果有Copy from item,也要加 上)。 对于每个Item上输入的值,一般是用 = ;如果有%,就解析为like;如果有#,则把后 边的表达式(比如between,甚至是子查询)直接作为条件。 5.1.2. 理解其他查询 下面我们将学习基于Lov的查询和基于块的查询,其原理和F11一样。 当form内部执行堆栈Navigate到Pre-query时,block也是处于Enter-query状态,和 F11一样,我们只管对Item赋值(相当于F11中人工输入),剩下的就交给Form去处理 了。 需要注意的是当处于enter-query状态的block,是使用query length属性来限制输入的 数据长度,而不是通常的maximum lengh,只不过query length默认是0,即等于 maximum lengh,所以会出现当用app_find.query_range时长度不够的情况。 5.2. 例子:Lov查询 5.2.1. 什么是Lov查询 当用户点击工具栏上的手电筒——查询按钮时,弹出的是一个简单的Lov,其内容通常 是可以唯一确定一条记录的“主码”,比如订单号。 5.2.2. 创建Lov查询 在基于Template的开发中,须遵循如下步骤: 1、 创建一个Parameter参数,用以保存lov的返回值,这里是Header_ID: 2、 根据实际需要创建LOV,并把条件值(通常是主键ID)返回给事先建好的 Parameter,我们用如下SQL创建LOV,并把LOV改名为“Q_ORDERS”: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Lov查询、块查询、Button 66 of 136 SELECT header_id, order_number, description FROM cux_order_headers 并把header_id返回给PARAMETER.HEADER_ID: 3、 添加块级QUERY_FIND触发器(该触发器不是Form标准的,所以需要手工输 入),代码如下: app_find.query_find('Q_ORDERS'); 4、 添加块级PRE-QUERY触发器,代码如下: if :parameter.g_query_find = 'TRUE' then :order_headers.header_id := :parameter.header_id; :parameter.g_query_find := 'FALSE'; end if; 5.2.3. 上传&编译&运行 运行结果如下: 代码请见CUXORDENT.09.fmb。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Lov查询、块查询、Button 67 of 136 5.3. 例子:块查询 5.3.1. 什么是块查询 当Form比较复杂,需要用比较多的条件才能方便查询,比如订单号范围、日期范围 等,或者希望查询条件用“选”而不是“输入”的方式时,需要专门做一个非数据库 块来完成。 5.3.2. 拷贝标准查询块 除了不基于数据库外,做查询块和普通的块没什么区别;不过我们可以采用比较快捷 的方法: 1、 打开APPSTAND.fmb,把其中的对象组“QUERY_FIND”拖到我们自己的Form 中: 并选择“Copy”而非“Subclass”,因为我们需要修改: 2、 这样会自动产生QUERY_FIND的块、画布、Window,然后我们就可以删除对象 组QUERY_FIND,因为用的是Copy,所以上述3个对象还保留着。 5.3.3. 修改标准查询块 1、 设置块、画布、Window的子类,修改Window的Title属性;这三个对象也可以改 名,本例只有一个查询块,不需要。 2、 设置块的下一导航块为目标块,上一导航块为自身。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Lov查询、块查询、Button 68 of 136 3、 仔细看下QUERY_FIND的块,有个块级触发器,还有三个标准Button,它们都有 一个WHEN-BUTTON-PRESSED触发器,分别处理清除查询块内容、新建目标块 记录、执行查询: 我们需要将其中的'your blockname here'全部改为目标块'ORDER_HEADERS'。 5.3.4. 创建查询条件Item 接下来,需要手工创建需要的查询条件Item,这里仅创建订单号和日期范围,其它字 段请大家自己练习: 5.3.5. 修改块触发器 1、 添加块级QUERY_FIND触发器(该触发器不是Form标准的,所以需要手工输 入),代码如下,具体参数含义请直接参考app_find包: app_find.query_find('SALES_ORDER','QUERY_FIND','QUERY_FIND'); 2、 添加块级PRE-QUERY触发器,代码如下,注意赋值用Copy,范围查询用 app_find.query_date_range: IF :parameter.g_query_find = 'TRUE' THEN copy(name_in('query_find.order_number'),'order_headers.order_number'); app_find.query_date_range(:query_find.ordered_date_from, :query_find.ordered_date_to, 'ORDER_HEADERS.ORDERED_DATE'); Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Lov查询、块查询、Button 69 of 136 :parameter.g_query_find := 'FALSE'; END IF; 5.3.6. 修改目标Item查询长度 如果是范围查询,通常Item默认的最大查询长度不够,需要设置得大点,比如200: 5.3.7. 上传&编译&运行 运行结果如下: 代码请见CUXORDENT.10.fmb。 5.4. 例子:Button 按钮最重要的触发器就是WHEN-BUTTON-PRESSED,可以参考上面查询的例子。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 触发器层次关系、常用触发器编写规范 70 of 136 6. 触发器层次关系、常用触发器编写规范 6.1. 理解层次关系 6.1.1. 说明 当一个事件发生的时候,Forms Engine可一并触发由低到高3个级别(Item、Block、 Form)的同名事件,如何触发,由定义低级别的事件时所设置的执行层次决定。 Execution Hierarchy: 1、 Override模式,默认模式,不再触发高级别事件 2、 Before模式,触发完本级别的事件后,再触发高级别事件 3、 After模式,先触发高级别事件,再回来触发本级别事件 当然了,如果某一层次我们没定义事件代码,Forms Engine就跳过该级别,直接进入 下一级别。 此外,Block中任何Item发生的事件,都可能引发该Block级事件,所以在Block级写本 属于Item级的事件,要特别性能问题;Form级事件更有类似问题。 6.1.2. WHEN-VALIDATE-ITEM例子 假设: 1、 Item XXX的WHEN-VALIDATE-ITEM的代码是“代码1”,模式是“After”; 2、 Item XXX所在BlockWHEN-VALIDATE-ITEM的代码是“代码2”,模式是 “Override”; 3、 Form级WHEN-VALIDATE-ITEM的代码是“代码3”。 事件:发生Item XXX的WHEN-VALIDATE-ITEM 那么:实际先执行“代码2”,然后执行“代码1”。 6.2. 触发器原理 Forms的触发器模型类似Windows系统的事件驱动模型,即当某个事件,比如鼠标 Click,发生的时候,Forms Engine会去找是否有相应的代码,如果有,就执行之,没 有的话就忽略该事件。 学习Oracle Forms触发器,除了理解同一事件的“层次关系”外,更要理解不同事件 的“先后关系”,这样才能在实际开发中选择准确合理的触发器,写出精要的代码。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 触发器层次关系、常用触发器编写规范 71 of 136 6.2.1. 触发器堆栈 当界面上某个动作发生的时候,Forms Engine会把一系列要发生的事件按照“先后关 系”压入执行堆栈,然后再从堆栈中一个个事件POP出来逐一执行,任一事件遇到 Raise Form-Trigger-Failure,Forms Engine就停止执行、清空堆栈、Rollback事务、 设置光标回合适的Item。 比如“光标从块A点入块B”,实际将发生如下事件: Event Trigger Validate the item When-Validate-Item Leave the item Post-Text-Item Validate the record When-Validate-Record Leave the record Post-Record Leave the block Post-Block Enter the block Pre-Block Enter the record Pre-Record Enter the Item Pre-Text-Item Ready block for input When-New-Block-Instance Ready record for input When-New-Record-Instance Ready item for input When-New-Item-Instance Forms Engine会把这些事件压入执行堆栈,然后逐一执行,如果在某一事件如Pre- Block中Raise Form-Trigger-Failure,Forms Engine会停止其下面所有事件的执行,并 认为“光标从块A点入块B”失败,把光标仍然留在块A中。 6.2.2. 常用触发器及其顺序 触发器及其触发顺序是最需要想象力和体会的东西,请参考附件《Trigger Execution Sequence in Oracle Forms.mht》。 6.3. 基于EBS模版开发的触发器 仔细观察Template.fmb,其Form级有很多事件,统一处理各种未被明确处理的事件, 统一设置界面的颜色等。其中有些是不能在低级别进行Override的,有些只能在低级 别中用Before或After。 比如有些人开发的Form,按F11进入查询模式,但记录颜色是白的而不是我们习惯的 浅蓝色,问题的症结在于其在Block级覆盖了when-new-record-instace。 6.3.1. ACCEPT 可以删除原代码,并代之以自行开发的代码;或者编写BLOCK 级的代码并将其执行方 式设置为Override。 6.3.2. KEY–DUPREC 默认代码禁止了复制记录;我们可用自己的代码取代它。 6.3.3. KEY–CLRFRM 客户化代码必须放置在原有代码的后面。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 触发器层次关系、常用触发器编写规范 72 of 136 6.3.4. KEY–LISTVAL 我们可编写BLOCK 级或ITEM 级的触发器来Override。 6.3.5. POST–FORM 客户化代码须放在原有代码的前面。 6.3.6. PRE–FORM 开发人员需要输入FORM的有关信息,并更改BLOCKNAME。 6.3.7. QUERY_FIND 我们可编写BLOCK 级的触发器来Override。 6.3.8. WHEN–NEW–FORM–INSTANCE 客户化代码须放置在原有代码的前面。 6.3.9. WHEN–NEW–BLOCK–INSTANCE 创建BLOCK 级的触发器,并设置执行方式为Before。 6.3.10. WHEN–NEW–RECORD–INSTANCE 创建BLOCK 级的触发器,并设置执行方式为Before,否则最常见的问题是F11呈白 色。 6.3.11. WHEN–NEW–ITEM–INSTANCE 创建BLOCK或Item 级的触发器,并设置执行方式为Before。 6.4. 一些触发器的理解 6.4.1. On-lock 对On-lock的理解,由于先入为主的缘故,开始一直很苦恼,为什么If里面只用了一个 Return,Form怎么知道要锁否?后来才知道On类型的数据库触发器是替换型的,On- lock也不例外,所以只要On-lock不Raise什么东西出来,Form就认为是锁成功了,至 于实际的锁,我们有Select……For Update来完成,至于If判断只是进行更加严格的判 定。 6.4.2. Pre-Form和When-New-Form-Instance Pre-Form可以理解为打开Form最先触发的事件,如果其失败,Form就退出。 所以变量比如Global和Parameter的初始化应该最好在Pre-Form里面。 When-New-Form-Instance则是Form初始化完毕,定位到第一个块的第一个可导航的 Item的时候触发的,通常用来定义弹性域和Folder、查询某个块。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 触发器层次关系、常用触发器编写规范 73 of 136 6.4.3. Post-Query和When-New-Record-Instance 假定数据库中有10条记录,块设置显示行数为8,那么当执行完查询,将连续触发8次 Post-Query,仅触发1次When-New-Record-Instance。这是因为前者是记录被Fetch到 Form中触发的,后者是光标进入行时触发的。 如果我们在记录间移动光标,包括移回原来的记录,每次都将触发When-New- Record-Instance;而只有移入第9、10条记录时才会触发Post-Query,并且移回“老” 记录,是不会再触发的。 如果想给某个字段赋值,通常在Post-Query中,而想根据字段的内容控制行或者某些 字段的属性,通常在When-New-Record-Instance中处理。 6.4.4. When-Validate-Item和When-Validate-Record 但字段被修改(包括改回原来的值)、清空,并在光标欲离开该字段,将触发When- Validate-Item,我们可以在里面编写验证代码,验证失败不允许光标离开。 当光标欲离开记录,比如滚动、保存时,将触发When-Validate-Record,我们可以在 这里对记录做总体验证。 待续…… Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 描述性弹性域、Key弹性域、Key弹性域查询 74 of 136 7. 描述性弹性域、Key弹性域、Key弹性域查询 7.1. 描述性弹性域开发步骤 7.1.1. 关于描述性弹性域 描述性弹性域的实质就是系统预留自定字段,不过Oracle做的很成功,提供一套完整 的“自定义”机制,可以用值集来验证字段、字段间可以设置依赖关系等等。 我们以基于基表的最简单例子来演示开发步骤。 7.1.2. 基表要求:基表中需含有1个结构字段和若干个自定义字段 结构字段通常起名ATTRIBUTE_CATEGORY,长度为30;自定义字段通常15个,起名 ATTRIBUTE1..n,长度240: -- Create table create table SCF.CUX_FLEXFIELD_DEMO ( FLEXFIELD_DEMO_ID NUMBER not null, CODE_COMBINATION_ID NUMBER not null, DESCRIPTION VARCHAR2(240), 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(240), ATTRIBUTE2 VARCHAR2(240), ATTRIBUTE3 VARCHAR2(240), ATTRIBUTE4 VARCHAR2(240), ATTRIBUTE5 VARCHAR2(240), ATTRIBUTE6 VARCHAR2(240), ATTRIBUTE7 VARCHAR2(240), ATTRIBUTE8 VARCHAR2(240), ATTRIBUTE9 VARCHAR2(240), ATTRIBUTE10 VARCHAR2(240), ATTRIBUTE11 VARCHAR2(240), ATTRIBUTE12 VARCHAR2(240), ATTRIBUTE13 VARCHAR2(240), ATTRIBUTE14 VARCHAR2(240), ATTRIBUTE15 VARCHAR2(240) ) tablespace SCF; -- Create/Recreate indexes create unique index SCF.CUX_FLEXFIELD_DEMO_U1 on SCF.CUX_FLEXFIELD_DEMO (FLEXFIELD_DEMO_ID) tablespace SCF; -- Create/Recreate sequence CREATE SEQUENCE SCF.CUX_FLEXFIELD_DEMO_S; -- Create/Recreate synonum CREATE SYNONYM CUX_FLEXFIELD_DEMO_S FOR scf.CUX_FLEXFIELD_DEMO_S; CREATE SYNONYM CUX_FLEXFIELD_DEMO FOR scf.CUX_FLEXFIELD_DEMO; 7.1.3. 注册要求:注册表和字段到EBS中 1、 首先需要调用EBS标准过程完成: EXECUTE AD_DD.REGISTER_TABLE('SCF','CUX_FLEXFIELD_DEMO','T',2,10,40); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','FLEXFIELD_DEMO_ID',1,'NUMBER',38,'N','N'); Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 描述性弹性域、Key弹性域、Key弹性域查询 75 of 136 EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','CODE_COMBINATION_ID',2,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','DESCRIPTION',3,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','CREATION_DATE',4,'DATE',9,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','CREATED_BY',5,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','LAST_UPDATED_BY',6,'NUMBER',38,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','LAST_UPDATE_DATE',7,'DATE',9,'N','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','LAST_UPDATE_LOGIN',8,'NUMBER',38,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE_CATEGORY',9,'VARCHAR2',30,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE1',10,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE2',11,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE3',12,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE4',13,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE5',14,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE6',15,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE7',16,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE8',17,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE9',18,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE10',19,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE11',20,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE12',21,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE13',22,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE14',23,'VARCHAR2',240,'Y','N'); EXECUTE AD_DD.REGISTER_COLUMN('SCF','CUX_FLEXFIELD_DEMO','ATTRIBUTE15',24,'VARCHAR2',240,'Y','N'); COMMIT; 2、 通过Application Developer职责/Flexfield/Descriptive/Register注册弹性域,通 常起名和表名一致: 3、 保存后,点击Columns,可以看到,系统自动选中了所有Attribute字段。 7.1.4. 字段要求:一个非数据库项 在块中手工创建一个字段,名字通常叫DESC_FLEX,子类为TEXT-ITEM-DESC- FLEX,Prompt为一对大括号,布局时通常放在最后,但不随滚动条滚动: 7.1.5. 触发器要求:Form级 WHEN-NEW-FORM-INSTANCE中追加: fnd_descr_flex.define(BLOCK => 'BLOCKNAME', field => 'DESC_FLEX', appl_short_name => 'SCF', desc_flex_name => 'CUX_FLEXFIELD_DEMO'); Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 描述性弹性域、Key弹性域、Key弹性域查询 76 of 136 7.1.6. 触发器要求:块级 PRE-INSERT中追加: fnd_flex.event('PRE-INSERT'); PRE-UPDATE中追加: fnd_flex.event('PRE-UPDATE'); PRE-QUERY中追加: fnd_flex.event('PRE-QUERY'); POST-QUERY中追加: fnd_flex.event('POST-QUERY'); WHEN-VALIDATE-RECORD中追加: fnd_flex.event('WHEN-VALIDATE-RECORD'); 注:可以把这些触发器写在Form级,这样不需要每个块都写,不过如果为了其它功能 在块级写了同名触发器,执行层次需要改为Before。 7.1.7. 触发器要求:Item级 WHEN-NEW-ITEM-INSTANCE中追加: fnd_flex.event('WHEN-NEW-ITEM-INSTANCE'); WHEN-VALIDATE-ITEM中追加: fnd_flex.event('WHEN-VALIDATE-ITEM'); 注:可以把这些触发器写在Form级,这样不需要每个Item都写,不过如果为了其它功 能在块级写了同名触发器,执行层次需要改为Before。 7.1.8. 启用弹性域 通过Application Developer职责/Flexfield/Descriptive/Segments启用弹性域: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 描述性弹性域、Key弹性域、Key弹性域查询 77 of 136 7.1.9. 上传&编译&运行 代码请见CUXFLXDEMO.01.fmb。 7.2. Key弹性域开发步骤 7.2.1. 关于Key弹性域 Key弹性域通常用来处理有层次结构的编码,比如账户、类别等,极少自行客户化开 发,一般都是使用系统标准的Key弹性域而已。 我们以基于基表的最简单例子来演示如何使用系统标准的Key弹性域。 7.2.2. 基表要求:基表中需含有1个ID字段 Key弹性域对应的表都有一个ID字段,如果我们在客户化开发中需要使用该表作为外 键,当然需要一个外键ID字段,名字根据需要起即可,比如我们的 CUX_FLEXFIELD_DEMO.CODE_COMBINATION_ID,实际上将用来保存账户ID。 7.2.3. 字段要求:一个Key代码组合字段+一个可选的Key描述组合字段 这两个字段可以是数据库项,也可以不是。 代码组合字段子类是Text_Item,描述组合字段子类通常是Text_Item_Display_Only; 注意它们的长度要足够,不然可能放不下组合。 对代码组合字段,需要设置其Lov属性,和日期字段一样: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 描述性弹性域、Key弹性域、Key弹性域查询 78 of 136 7.2.4. 触发器要求:Form级 WHEN-NEW-FORM-INSTANCE中追加: fnd_key_flex.define(BLOCK => 'BLOCKNAME', field => 'ACCOUNT_CODE', description => 'ACCOUNT_DESCRIPTION', appl_short_name => 'SQLGL', code => 'GL#', id => 'CODE_COMBINATION_ID', required => 'N', usedbflds => 'N', validate => 'FULL', vrule => '\\nSUMMARY_FLAG\\nI\\nAPPL=SQLGL;NAME=GL_NO_PARENT_SEGMENT_ALLOWED\\nN\\0GL_GLOBAL\\nDETAIL_POSTING_ALLO WED\\nE\\nAPPL=INV;NAME=INV_VRULE_POSTING\\nN', num => 101); 这个定义比较复杂,可以查阅115devg.pdf。 因为不同公司启用的Structure不同,实际开发中不要写死Num值了。 这里给出个SQL可以简单的查询不同Key弹性域的定义: SELECT app.application_short_name, app.application_name, flx.id_flex_code, flx.id_flex_name, str.id_flex_num, str.id_flex_structure_code, str.id_flex_structure_name FROM fnd_id_flexs flx, fnd_id_flex_structures_vl str, fnd_application_vl app WHERE flx.application_id = str.application_id AND flx.id_flex_code = str.id_flex_code AND flx.application_id = app.application_id ORDER BY 1, 3, 5 7.2.5. 触发器要求:块级 同描述性弹性域。 7.2.6. 触发器要求:Item级 同描述性弹性域。 7.2.7. 上传&编译&运行 运行结果如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 描述性弹性域、Key弹性域、Key弹性域查询 79 of 136 代码请见CUXFLXDEMO.02.fmb。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 80 of 136 8. Folder、JTF Grid 8.1. Folder开发步骤 本节标题说明:标准指做Folder都要做而且是一样的步骤,可以考虑做个模版了;普 通指和做普通Form一样;特殊指做Folder都要做但需要根据实际内容作修改。 8.1.1. 什么是Folder Folder不是Form的标准功能,而是Oracle自己在EBS开发中总结出来的“动态界 面”:不同用户可以根据自己的需要,设置块中哪些字段需要显示以及顺序;而开发 人员则免于被布局折腾的痛苦。 对于开发来说,要做的事情就是用“遵循Folder规范”换取“布局零工作量”。 8.1.2. 拷贝标准Folder对象 标准 打开APPSTAND.fmb,把对象组“STANDARD_FOLDER”拖到我们自己的Form 中,并选择“Subclass”而非“Copy”,这个和前面讲的查询块不同。 这样会自动产生一系列用于Folder的对象:块、画布、Lov/记录组、参数、Property Classes、Window,这些都不用修改。 8.1.3. 引用Folder的PLL库 标准 选中Attached Libraries,点击“+”,选择APPFLDR.pll,如果本地没有请先从服务 器下载。 点击Attach后选择“Yes”移除绝对路径。 8.1.4. 创建数据库对象 普通 创建数据库对象,没有任何特殊之处: -- Create table create table SCF.CUX_FLODER_DEMO ( FLODER_DEMO_ID NUMBER not null, NUMBER_FIELD1 NUMBER not null, NUMBER_FIELD2 NUMBER, NUMBER_FIELD3 NUMBER, NUMBER_FIELD4 NUMBER, DATE_FIELD1 DATE NOT NULL, DATE_FIELD2 DATE, Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 81 of 136 VARCHAR2_FIELD1 VARCHAR2(100) NOT NULL, VARCHAR2_FIELD2 VARCHAR2(100), VARCHAR2_FIELD3 VARCHAR2(100), VARCHAR2_FIELD4 VARCHAR2(100), VARCHAR2_FIELD5 VARCHAR2(100), VARCHAR2_FIELD6 VARCHAR2(100), 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(240), ATTRIBUTE2 VARCHAR2(240), ATTRIBUTE3 VARCHAR2(240), ATTRIBUTE4 VARCHAR2(240), ATTRIBUTE5 VARCHAR2(240), ATTRIBUTE6 VARCHAR2(240), ATTRIBUTE7 VARCHAR2(240), ATTRIBUTE8 VARCHAR2(240), ATTRIBUTE9 VARCHAR2(240), ATTRIBUTE10 VARCHAR2(240), ATTRIBUTE11 VARCHAR2(240), ATTRIBUTE12 VARCHAR2(240), ATTRIBUTE13 VARCHAR2(240), ATTRIBUTE14 VARCHAR2(240), ATTRIBUTE15 VARCHAR2(240) ) tablespace SCF; -- Create/Recreate indexes create unique index SCF.CUX_FLODER_DEMO_U1 on SCF.CUX_FLODER_DEMO (FLODER_DEMO_ID) tablespace SCF; -- Create/Recreate sequence CREATE SEQUENCE SCF.CUX_FLODER_DEMO_S; -- Create/Recreate synonum CREATE SYNONYM CUX_FLODER_DEMO_S FOR scf.CUX_FLODER_DEMO_S; CREATE SYNONYM CUX_FLODER_DEMO FOR scf.CUX_FLODER_DEMO; 8.1.5. 创建Folder块 普通 按照普通步骤创建数据块,包括块和字段的子类、LOV、On-XXX触发器、行指示符 等等,如果有弹性域,那么也需要DF字段和相关的触发器。 当然了,从Template开始的常规修改步骤也是要做的。 为规范起见,块名后加“FOLDER”,这里是“DEMO_FOLDER”,这种数据块我们 叫“Folder块”。 8.1.6. 修改Folder块 标准 1、 创建SWITCHER字段 手工添加字段,名字叫FOLDER_SWITCHER,子类为SWITCHER: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 82 of 136 并编写触发器WHEN-NEW-ITEM-INSTANCE: app_folder_move_cursor('1'); 2、 编写触发器 需要编写如下触发器: WHEN-NEW-RECORD-INSTANCE WHEN-NEW-BLOCK-INSTANCE PRE-QUERY POST-QUERY PRE-BLOCK POST-BLOCK KEY-ENTQRY KEY-EXEQRY KEY-PREV-ITEM KEY-NEXT-ITEM KEY-PRVREC KEY-NXTREC KEY-CLRREC KEY-CLRBLK 这些触发器的内容都是app_folder.event('触发器名称'); 8.1.7. 创建Prompt块 标准 手工创建非数据库块,子类仍为Block,为规范起见,块名后加“PROMPT”,这里是 “DEMO_PROMPT”,这种数据块我们叫“Prompt块”。 手工创建6个标准Item,名字和子类必须同下表: Name Subclass FOLDER_TITLE DYNAMIC_TITLE FOLDER_OPEN FOLDER_OPEN FOLDER_DUMMY FOLDER_DUMMY ORDER_BY1 FOLDER_ORDERBY ORDER_BY2 FOLDER_ORDERBY ORDER_BY3 FOLDER_ORDERBY 8.1.8. 修改Prompt块和Folder块 特殊 “Folder块”有多少字段要显示,就需要在“Prompt块”创建多少同名字段(除了 SWITCHER字段、行指示符、DF字段,前者是Folder的特殊字段,后两者通常需要固 定在内容画布上),并设置这些字段的关键属性: 属性 值 Subclass FOLDER_PROMPT_MULTIROW Initial Value 字段的Prompt Width 字段的宽度,根据实际需要调整 Prompt 注:清空 对“Folder块”的字段,也需要清空Prompt属性。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 83 of 136 8.1.9. 理解Folder自动布局原理 到这里,我们先理解下Folder是如何自动布局的: 1、 需要使用Folder功能的字段必须放在堆叠画布上,Folder功能仅自动布局堆叠画布 宽度和在其上的字段顺序。 2、 放在内容画布上的所有对象,包括堆叠画布自身在内容画布上的起始位置,需要 我们和以前一样手工调整布局;只要布局得当,一个Windows上可以有多个 Folder。 3、 自动布局的堆叠画布宽度 = 内容画布宽度 – 堆叠画布的X座标 – 0.26,这个0.26 啊,正好可以让我们放垂直滚动条!此外,堆叠画布高度也会被自动调整,调整 时系统自动回算上水平滚动条的位置! 4、 最终界面的字段顺序由Prompt块字段的顺序决定,那么Folder块的字段在界面的 排列顺序如何自动和Prompt对应起来呢?原来系统是根据字段名! 5、 最终界面Tab键导航的顺序仍然由Folder块字段的顺序决定,所以设计时注意两者 要一致。 6、 系统并不自动决定字段的Y轴位置!Y轴位置由字段自身属性决定,所以需要手工 设置,通常Prompt块的为0,Folder块的为0.25,即等于Prompt块的Item的高度。 7、 为操作方便,也为了标准化,通常需要放个文件夹按钮在内容画布的左上角,这 个就是Folder_Open字段。 8.1.10. 创建堆叠画布、内容画布、窗口 普通 Floder要求字段放置在堆叠画布上,为规范起见,画布名后加“FOLDER_STACK”, View和Canvas的宽度无所谓,运行时将自定根据窗口的大小调整,右边正好会留出滚 动条的位置。 另外,为规范起见,内容画布后也可加“FOLDER_CONTENT”。 设置这两个画布的子类,并设置它们的Windows属性相同。 8.1.11. 布局Item到画布 特殊 设置如下Item到画布: 1、 把FOLDER_OPEN、FOLDER_DUMMY、行指示符、Folder块的垂直滚动条,都 设到内容画布上,并设计它们的位置。Folder_Open按照其子类默认值即可:X为 0.1、Y为0。行指示符X我们改为0.1、Y改为0.5。垂直滚动条的Y为0.5,X则需要在 Window的Resize事件中设置,还记得那个0.26吗? 2、 这样,堆叠画布在内容画布上的位置,应该就是X为0.2,Y为0.25,想想为什么。 同时启用堆叠画布的水平滚动条。 3、 Folder块的SWITCHER字段设置到堆叠画布。 4、 Folder块的其他需要显示的字段都设置到堆叠画布,并且Y座标为0.25。 5、 Prompt块的其他字段全部设置到堆叠画布,并且Y座标为0。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 84 of 136 注:FOLDER_TITLE字段不知道是干什么的,其宽度设为0。 8.1.12. 追加Form级触发器 标准 在FOLDER_ACTION中追加: app_folder.event(:global.folder_action); 在KEY-CLRFRM中追加: app_folder.event('KEY-CLRFRM'); 8.1.13. 追加Form级触发器 特殊 1、 在WHEN-WINDOW-RESIZED中追加,注意BLOCKNAME,要改为你的Folder 所在的Window名字: declare l_x_position number; l_width number; begin if :system.event_window in ('DEMO_FOLDER') then app_folder.event('WHEN-WINDOW-RESIZED'); l_width:=GET_VIEW_PROPERTY('DEMO_FOLDER_CONTENT',WIDTH); SET_CANVAS_PROPERTY('DEMO_FOLDER_CONTENT',WIDTH,l_width); l_width:=GET_VIEW_PROPERTY('DEMO_FOLDER_STACK',WIDTH); l_x_position:=GET_VIEW_PROPERTY('DEMO_FOLDER_STACK',VIEWPORT_X_POS); l_x_position:=l_x_position + l_width + 0.015; SET_BLOCK_PROPERTY('DEMO_FOLDER',BLOCKSCROLLBAR_X_POS,l_x_position); end if; end; 注意必须用代码对内容画布进行调整,因为改变窗口大小时,Form不会自动调整。 2、 在WHEN-NEW-FORM-INSTANCE中追加: app_folder.define_folder_block(object_name => 'DEMO_FOLDER', folder_block_name => 'DEMO_FOLDER', prompt_block_name => 'DEMO_PROMPT', folder_canvas_name => 'DEMO_FOLDER_STACK', folder_window_name => 'DEMO_FOLDER', disabled_functions => '', tab_canvas_name => '', fixed_canvas_name => ''); app_folder.event('INSTANTIATE'); show_view('DEMO_FOLDER_CONTENT'); 第一句是Folder申明,根据参数名给出具体值即可,注意tab_canvas_name,我们不用 Tab页,所以为空。 最后一句是因为本例中内容画布上没有可导航的块,所以需要用带码使其显示。 8.1.14. 上传&编译&运行 运行结果如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 85 of 136 可以调整列宽度和顺序、隐藏或显示列,并可以保存布局;调整窗口大小,Folder会 自动调整适应。 代码请见CUXFLDDEMO.fmb。 注,上述触发器代码可以全部组织到一个Program Units中。 8.2. JTF Grid开发步骤 本节标题说明:标准指做JTF Grid都要做而且是一样的步骤,可以考虑做个模版了; 普通指和做普通Form一样;特殊指做JTF Grid都要做但需要根据实际内容作修改。 8.2.1. 什么是JTF Grid JTF Grid不是Form的标准功能,而是Oracle自己在EBS开发中总结出来的“可配置块 字段”:块中有多少字段可以通过专门的界面定义,。 对于开发来说,要做的事情就是用“遵循JTF Grid规范”换取“增删字段无需修改 Form代码”。 8.2.2. 拷贝标准JTF Grid对象 标准 1、 对象组 打开JTFSTAND.fmb,把对象组“JTF_GRID”拖到我们自己的Form中,并选择 “Subclass”而非“Copy”,这个和前面讲的Folder一样。 这样会自动产生一系列用于JTF_GRID的对象:块、画布、参数、Property Classes、 Window,尤其注意Form级触发器JTF_GRID_EVENT。这些都不用修改。 2、 过程 从JTFSTAND.fmb拷贝JTF_CUSTOM_GRID_EVENT过程到我们自己的Form中,然后 补上事件处理,暂时全部放null: PROCEDURE jtf_custom_grid_event(gridname IN VARCHAR2, eventtype IN VARCHAR2) IS Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 86 of 136 BEGIN IF p_event_type = jtf_grid_events.hyperlink_event THEN NULL; ELSIF p_event_type = jtf_grid_events.new_record_event THEN NULL; ELSIF p_event_type = jtf_grid_events.popup_event THEN NULL; ELSIF p_event_type = jtf_grid_events.row_selection_event THEN NULL; ELSIF p_event_type = jtf_grid_events.end_of_find_event THEN NULL; ELSIF p_event_type = jtf_grid_events.doubleclick_event THEN NULL; END IF; END; 8.2.3. 引用JTF Grid的PLL库 标准 选中Attached Libraries,点击“+”,选择JTF_GRID.pll,其将自动引用JTF_UTIL、 JTFDEBUG。如果本地没有请先从服务器下载。 8.2.4. 创建数据库对象 普通 创建数据库对象,没有任何特殊之处,可以使用现成的View和Table,比如我们使用 gl_je_headers_v。 8.2.5. 定义CRM电子表格 特殊 N: CRM Adminstrator/Spreadtable/Metadata Administraion 输入电子表格名称、源视图、字段定义: 8.2.6. 创建Grid块 普通 手工创建非数据库块,规范起见,块名后加“GRID”,这里是“DEMO_GRID”。 当然了,从Template开始的常规修改步骤也是要做的。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 87 of 136 8.2.7. 修改Grid块 特殊 手工创建非数据库项,并设置这些字段的关键属性: 字段名 Subclass 说明 READONLY_GRID JTF_GRID_ITEM 必须,名字随便 FIND BUTTON 可选 DETAIL BUTTON 可选 8.2.8. 布局Item到画布 普通 把DEMO_GRID布局到画布,什么画布都可以,我们需要设置其在画布的启示位置、 高度、宽度,因为设计时在画布上不容易看到,我们可以直接设置属性。 8.2.9. 追加Form级触发器 特殊 在WHEN-NEW-FORM-INSTANCE中追加: IF NOT jtf_grid.getbooleanproperty('DEMO_GRID.READONLY_GRID', jtf_grid_property.initialized) THEN jtf_grid.init(jtf_custom.grid_name, 'GL_JE_HEADERS_V'); jtf_grid.setbooleanproperty(jtf_custom.grid_name, jtf_grid_property.allow_multiple_row_selection, FALSE); END IF; 8.2.10. 编写Find Button触发器 特殊 用户点击Find,通常是弹出查询界面,输入完条件再执行查询。 我们这里省去查询条件界面,直接在FIND按钮的WHEN-BUTTON-PRESSED中编 写: jtf_grid.removeallbindvariables('DEMO_GRID.READONLY_GRID'); --jtf_grid.setbindvariable('DEMO_GRID.READONLY_GRID', 'CURRENCY_CODE', 'CNY'); jtf_grid.setcharproperty('DEMO_GRID.READONLY_GRID', jtf_grid_property.where_clause, 'CURRENCY_CODE=''CNY'''); IF jtf_grid.getbooleanproperty('DEMO_GRID.READONLY_GRID', jtf_grid_property.is_populated) THEN jtf_grid.refresh('DEMO_GRID.READONLY_GRID'); ELSE jtf_grid.populate('DEMO_GRID.READONLY_GRID'); END IF; 8.2.11. 处理选择事件 特殊 用户选中某行后,我们可以根据其选中的信息去打开一个普通块,这样首先需要在 FIND按钮的WHEN-BUTTON-PRESSED中编写: jtf_grid.RequestRowSelection('DEMO_GRID.READONLY_GRID'); 可以打开该包查看其具体作用。然后在过程jtf_custom_grid_event中响应选择事件: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 88 of 136 PROCEDURE jtf_custom_grid_event(gridname IN VARCHAR2, eventtype IN VARCHAR2) IS grid_selection JTF_GRID_PROPERTY.ROW_SELECTION_TYPE; l_startRow number; BEGIN IF eventtype = jtf_grid_events.hyperlink_event THEN NULL; ELSIF eventtype = jtf_grid_events.new_record_event THEN NULL; ELSIF eventtype = jtf_grid_events.popup_event THEN NULL; ELSIF eventtype = jtf_grid_events.row_selection_event THEN grid_selection := jtf_grid.GetRowSelection('DEMO_GRID.READONLY_GRID'); if grid_selection.COUNT > 0 then l_startRow := grid_selection(1).startRow; fnd_message.debug(jtf_grid.GetColumnCharValue('DEMO_GRID.READONLY_GRID', l_startRow, 'NAME')); --Do any thing here END IF; ELSIF eventtype = jtf_grid_events.end_of_find_event THEN NULL; ELSIF eventtype = jtf_grid_events.doubleclick_event THEN null; END IF; END; 问题:如何响应双击事件呢? 8.2.12. 上传&编译&运行 运行结果如下: 代码请见CUXJTFDEMO.fmb。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Folder、JTF Grid 89 of 136 注,上述触发器代码通常全部组织到一个名字为“JTF_CUSTOM”的Program Units 中,这样就可以定义一个变量GRID_NAME来保存字段名,免得每处代码重复写 'DEMO_GRID.READONLY_GRID'。 如果要使Window变化时Grid跟着变,那么需要参考Folder的做法,在WHEN- WINDOW-RESIZED触发器中调整画布大小、Grid的大小。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 90 of 136 9. 多语言开发 9.1. 国际化支持 9.1.1. 说明 EBS的国际化支持,也叫多语言支持,包含多个层面: 1、 数据库级别:字符集支持多国语言,如UTF8支持全球所有语言 2、 数据级别:采用_B表+_TL表+ENV(‘LANG’)环境变量+_VL表+“小地球”来实现 3、 消息级别:所有消息,通过分语种维护的消息字典获取 4、 文件级别:采用分语种目录的形式来实现Forms、Reports的国际化支持 9.2. Form自身的多语言版本 EBS时运行时“找fmx文件”,实际上是根据用户登录时选择的语言,首先到$<应用简 称>_TOP/forms/<语言代码>下找fmx文件,如果没有则继续在$<应用简称 >_TOP/forms/US下找。 也就是说,需要我们维护不同语言的Form编译到不同的目录。不过Oracle提供了工具 “Oracle Translation Builder (OTB)”,可以将多个语言的字符串保存入1个fmb文 件,这样在设计时根据NLS_LANG 自动显示该语言的内容,编译时、运行时也是同样 道理。 更多多语言方面的转换,请参考Note:372952.1“Customer Translations”。 9.3. 数据多语言开发步骤 要求熟练掌握基于Template、基于View的开发过程;要求熟悉EBS中“小地球”的操 作。 下面结合例子直接说明开发步骤和注意点,假定只有2个字段,1个需要维护多语言信 息,不考虑弹性域字段。 9.3.1. 数据库对象的要求:基表B 没有什么特殊之处: create table SCF.CUX_MULTILINGUAL_DEMO_B ( MULTILINGUAL_DEMO_ID NUMBER not null, MULTILINGUAL_DEMO_CODE VARCHAR2(30) not null, CREATED_BY NUMBER(15) not null, CREATION_DATE DATE not null, LAST_UPDATED_BY NUMBER(15) not null, LAST_UPDATE_DATE DATE not null, Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 91 of 136 LAST_UPDATE_LOGIN NUMBER(15) ); create unique index SCF.CUX_MULTILINGUAL_DEMO_B_U1 on SCF.CUX_MULTILINGUAL_DEMO_B (MULTILINGUAL_DEMO_ID); Create Sequence SCF.CUX_MULTILINGUAL_DEMO_B_S; Create Synonym CUX_MULTILINGUAL_DEMO_B For scf.CUX_MULTILINGUAL_DEMO_B; Create Synonym CUX_MULTILINGUAL_DEMO_B_S For scf.CUX_MULTILINGUAL_DEMO_B_S; 9.3.2. 数据库对象的要求:多语言表TL 主键字段:基表主键+LANGUAGE。 其他字段:需要维护多语言的字段+Who字段+SOURCE_LANG。 create table SCF.CUX_MULTILINGUAL_DEMO_TL ( MULTILINGUAL_DEMO_ID NUMBER not null, DESCRIPTION VARCHAR2(255), LANGUAGE VARCHAR2(4) not null, CREATED_BY NUMBER(15) not null, CREATION_DATE DATE not null, LAST_UPDATED_BY NUMBER(15) not null, LAST_UPDATE_DATE DATE not null, LAST_UPDATE_LOGIN NUMBER(15), SOURCE_LANG VARCHAR2(4) not null ); create unique index SCF.CUX_MULTILINGUAL_DEMO_TL_U1 on SCF.CUX_MULTILINGUAL_DEMO_TL(MULTILINGUAL_DEMO_ID, LANGUAGE); Create Synonym CUX_MULTILINGUAL_DEMO_TL For scf.CUX_MULTILINGUAL_DEMO_TL; 9.3.3. 数据库对象的要求:视图VL 该视图根据登录用户的语言过滤数据: Create Or Replace View CUX_MULTILINGUAL_DEMO_VL As SELECT b.ROWID row_id, b.multilingual_demo_id, b.multilingual_demo_code, t.description, b.created_by, b.creation_date, b.last_updated_by, b.last_update_date, b.last_update_login FROM scf.cux_multilingual_demo_b b, scf.cux_multilingual_demo_tl t WHERE b.multilingual_demo_id = t.multilingual_demo_id AND t.LANGUAGE = userenv('LANG'); 9.3.4. 数据库对象的要求:表操作API 需要同时操作TL表,同时提供add_language过程,供EBS启用新语言时使用。以下代 码仅关注“--Process TL table here”部分即可:: CREATE OR REPLACE PACKAGE cux_multilingual_demo_pkg AUTHID CURRENT_USER AS Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 92 of 136 /*===================================== ** PROCEDURE: insert_row() **=====================================*/ PROCEDURE insert_row(x_row_id IN OUT VARCHAR2, x_multilingual_demo_id IN OUT NUMBER, p_multilingual_demo_code IN VARCHAR2, p_description IN VARCHAR2, p_creation_date IN DATE, p_created_by IN NUMBER, p_last_update_date IN DATE, p_last_updated_by IN NUMBER, p_last_update_login IN NUMBER); /*===================================== ** PROCEDURE: lock_row() **=====================================*/ PROCEDURE lock_row(p_multilingual_demo_id IN NUMBER, p_multilingual_demo_code IN VARCHAR2, p_description IN VARCHAR2 DEFAULT NULL); /*===================================== ** PROCEDURE: update_row() **=====================================*/ PROCEDURE update_row(p_multilingual_demo_id IN NUMBER, p_multilingual_demo_code IN VARCHAR2, p_description IN VARCHAR2, p_last_update_date IN DATE, p_last_updated_by IN NUMBER, p_last_update_login IN NUMBER); /*===================================== ** PROCEDURE: delete_row() **=====================================*/ PROCEDURE delete_row(p_multilingual_demo_id IN NUMBER); /*===================================== ** PROCEDURE: add_language() **=====================================*/ PROCEDURE add_language; END cux_multilingual_demo_pkg; / CREATE OR REPLACE PACKAGE BODY cux_multilingual_demo_pkg AS /*===================================== ** PROCEDURE: insert_row() **=====================================*/ PROCEDURE insert_row(x_row_id IN OUT VARCHAR2, x_multilingual_demo_id IN OUT NUMBER, p_multilingual_demo_code IN VARCHAR2, p_description IN VARCHAR2, p_creation_date IN DATE, p_created_by IN NUMBER, p_last_update_date IN DATE, p_last_updated_by IN NUMBER, p_last_update_login IN NUMBER) IS Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 93 of 136 CURSOR c IS SELECT ROWID FROM cux_multilingual_demo_b WHERE multilingual_demo_id = x_multilingual_demo_id; BEGIN IF x_multilingual_demo_id IS NULL THEN SELECT cux_multilingual_demo_b_s.NEXTVAL INTO x_multilingual_demo_id FROM dual; END IF; INSERT INTO cux_multilingual_demo_b (multilingual_demo_id, multilingual_demo_code, created_by, creation_date, last_updated_by, last_update_date, last_update_login) VALUES (x_multilingual_demo_id, p_multilingual_demo_code, p_created_by, p_creation_date, p_last_updated_by, p_last_update_date, p_last_update_login); --Process TL table here INSERT INTO cux_multilingual_demo_tl (multilingual_demo_id, description, created_by, creation_date, last_updated_by, last_update_date, last_update_login, LANGUAGE, source_lang) SELECT x_multilingual_demo_id, p_description, p_created_by, p_creation_date, p_last_updated_by, p_last_update_date, p_last_update_login, l.language_code, userenv('LANG') FROM fnd_languages l WHERE l.installed_flag IN ('I', 'B') AND NOT EXISTS (SELECT NULL FROM cux_multilingual_demo_tl t WHERE t.multilingual_demo_id = x_multilingual_demo_id AND t.LANGUAGE = l.language_code); OPEN c; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 94 of 136 FETCH c INTO x_row_id; IF (c%NOTFOUND) THEN CLOSE c; RAISE no_data_found; END IF; CLOSE c; END insert_row; /*===================================== ** PROCEDURE: lock_row() **=====================================*/ PROCEDURE lock_row(p_multilingual_demo_id IN NUMBER, p_multilingual_demo_code IN VARCHAR2, p_description IN VARCHAR2 DEFAULT NULL) IS CURSOR c IS SELECT multilingual_demo_id, multilingual_demo_code FROM cux_multilingual_demo_b WHERE multilingual_demo_id = p_multilingual_demo_id FOR UPDATE OF multilingual_demo_id NOWAIT; rec c%ROWTYPE; --Process TL table here CURSOR c1 IS SELECT description FROM cux_multilingual_demo_tl WHERE multilingual_demo_id = p_multilingual_demo_id AND LANGUAGE = userenv('LANG') FOR UPDATE OF multilingual_demo_id NOWAIT; tlrec c1%ROWTYPE; BEGIN OPEN c; FETCH c INTO rec; IF (c%NOTFOUND) THEN CLOSE c; fnd_message.set_name('FND', 'FORM_RECORD_DELETED'); app_exception.raise_exception; END IF; CLOSE c; IF ((rec.multilingual_demo_id = p_multilingual_demo_id) AND ((rec.multilingual_demo_code = p_multilingual_demo_code) OR ((rec.multilingual_demo_code IS NULL) AND (p_multilingual_demo_code IS NULL)))) THEN NULL; ELSE fnd_message.set_name('FND', 'FORM_RECORD_CHANGED'); app_exception.raise_exception; END IF; --Process TL table here OPEN c1; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 95 of 136 FETCH c1 INTO tlrec; IF (c1%NOTFOUND) THEN CLOSE c1; fnd_message.set_name('FND', 'FORM_RECORD_DELETED'); app_exception.raise_exception; END IF; CLOSE c1; IF (((tlrec.description = p_description) OR ((tlrec.description IS NULL) AND (p_description IS NULL)))) THEN NULL; ELSE fnd_message.set_name('FND', 'FORM_RECORD_CHANGED'); app_exception.raise_exception; END IF; END lock_row; /*===================================== ** PROCEDURE: update_row() **=====================================*/ PROCEDURE update_row(p_multilingual_demo_id IN NUMBER, p_multilingual_demo_code IN VARCHAR2, p_description IN VARCHAR2, p_last_update_date IN DATE, p_last_updated_by IN NUMBER, p_last_update_login IN NUMBER) IS BEGIN UPDATE cux_multilingual_demo_b SET multilingual_demo_id = p_multilingual_demo_id, multilingual_demo_code = p_multilingual_demo_code, last_update_date = p_last_update_date, last_updated_by = p_last_updated_by, last_update_login = p_last_update_login WHERE multilingual_demo_id = p_multilingual_demo_id; IF (SQL%NOTFOUND) THEN RAISE no_data_found; END IF; --Process TL table here UPDATE cux_multilingual_demo_tl SET description = p_description, source_lang = userenv('LANG'), last_update_date = p_last_update_date, last_updated_by = p_last_updated_by, last_update_login = p_last_update_login WHERE multilingual_demo_id = p_multilingual_demo_id AND userenv('LANG') IN (LANGUAGE, source_lang); IF (SQL%NOTFOUND) THEN RAISE no_data_found; END IF; END update_row; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 96 of 136 /*===================================== ** PROCEDURE: delete_row() **=====================================*/ PROCEDURE delete_row(p_multilingual_demo_id IN NUMBER) IS BEGIN DELETE FROM cux_multilingual_demo_b WHERE multilingual_demo_id = p_multilingual_demo_id; IF (SQL%NOTFOUND) THEN RAISE no_data_found; END IF; --Process TL table here DELETE FROM cux_multilingual_demo_tl WHERE multilingual_demo_id = p_multilingual_demo_id; IF (SQL%NOTFOUND) THEN RAISE no_data_found; END IF; END delete_row; /*===================================== ** PROCEDURE: add_language() **=====================================*/ PROCEDURE add_language IS BEGIN INSERT INTO cux_multilingual_demo_tl (multilingual_demo_id, description, created_by, creation_date, last_updated_by, last_update_date, last_update_login, LANGUAGE, source_lang) SELECT b.multilingual_demo_id, b.description, b.created_by, b.creation_date, b.last_updated_by, b.last_update_date, b.last_update_login, l.language_code, b.source_lang FROM cux_multilingual_demo_tl b, fnd_languages l WHERE l.installed_flag IN ('I', 'B') AND b.LANGUAGE = userenv('LANG') AND NOT EXISTS (SELECT NULL FROM cux_multilingual_demo_tl t WHERE t.multilingual_demo_id = b.multilingual_demo_id AND t.LANGUAGE = l.language_code); Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 97 of 136 END add_language; END cux_multilingual_demo_pkg; 9.3.5. Form对象的要求:2个Form级触发器 1、 PRE-BLOCK,Form级灰掉“小地球”,块级根据需要启用: app_special.disable('TRANSLATE'); 2、 POST-FORMS-COMMIT。 FND_MULTILINGUAL.SAVE; 9.3.6. Form对象的要求:5个Block级触发器 1、 PRE-BLOCK,Form级灰掉“小地球”,块级根据需要启用: app_special.enable('TRANSLATE'); 2、 POST-INSERT,和具体的开发无关: FND_MULTILINGUAL.KEY; 3、 WHEN-CLEAR-BLOCK,和具体的开发无关: FND_MULTILINGUAL.FREE_BLOCK; 4、 WHEN-REMOVE-RECORD,和具体的开发无关: FND_MULTILINGUAL.FREE_RECORD; 5、 TRANSLATIONS,关键代码,和具体的开发相关,下面同时摘录过程参数说明: -- EDIT- called in WHEN-BUTTON-PRESSED -- block- name of translated block -- key_columns- names of primary key columns -- e.g. 'key1, key2' -- trans_columns- names of translated columns -- e.g. 'trans1, trans2' -- header_columns- the user-visible names of the -- translated columns. Prefixing -- with a '*APP:' indicates message -- dictionary translation, where -- APP is the application of the msg. -- e.g. '*FND:ML_KEY1, *FND:ML_KEY2' -- table_name - name of base table -- (defaults to ) -- tltable_name - name of translations table -- (defaults to _TL) -- validation_callback - optional stored procedure to validate -- translations. Callback API must be in the form: -- VALIDATE_CALLBACK( IN VARCHAR2, -- LANGUAGE IN VARCHAR2, -- IN VARCHAR2); -- --procedure EDIT(block IN VARCHAR2, Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 98 of 136 -- key_columns IN VARCHAR2, -- trans_columns IN VARCHAR2, -- header_columns IN VARCHAR2, -- table_name IN VARCHAR2 default null, -- tltable_name IN VARCHAR2 default null, -- validation_callback IN VARCHAR2 default null) FND_MULTILINGUAL.EDIT('MULTILINGUAL_DEMO', 'MULTILINGUAL_DEMO_ID', 'DESCRIPTION', '*FND:ML_DESCRIPTION', 'CUX_MULTILINGUAL_DEMO_B', 'CUX_MULTILINGUAL_DEMO_TL'); 9.3.7. Form对象的要求:多语言字段在画布的显示 多语言字段(本例的DESCRIPTION),需要与普通字段一样显示在画布上,理由很 简单: 1、 显示、维护当前Session语言对应的内容。 2、 在EBS只有一种语言时,点击“小地球”是无法弹出多语言界面的。 9.4. EBS启用新语言时的考虑 假如现在EBS只有US,如果哪天启用了新语言如ZHS,那么对新增的记录没有问题, 但对于已经在TL表的记录,将缺少一种语言的数据,这该如何处理呢? 首先当然可以通过写脚本升级,或直接调用开发时就写好的表操作API中的 add_language过程。 也可参照系统标准做法,不过该方法作为参考,不推荐使用。 9.4.1. EBS启用新语言的过程 1、 在OAM中通过Site Map/License Manager/Language启用新语言。 2、 用ADADMIN,在/Maintain Applications Database Entities/Maintain Multi- lingual Tables更新所有多语言表。 3、 打多语言Patch。 这里的关键步骤是2!下面结合mtl_system_items_tl的更新来说明。 9.4.2. Maintain Multi-lingual Tables核心过程 1、 分析$FND_TOP/admin/applpord.txt文件,里面有关于INV模块的定义 …… # ################################################## # Inventory # ################################################## # # application id, abbreviation, shortname, prefix 401 inv INV APP # multiple product installations for msob, "controlled release", shared only # optional fourth field is "stub product". default is No Yes No No No # multilingual, has NLADD.sql Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 多语言开发 99 of 136 Yes Yes …… 说明INV模块有多语言支持。 2、 调用$INV_TOP/sql/INVNLINS.sql来更新多语言表,其中有如下代码调用: curpkg := 'INV_ITEM_PVT'; INV_ITEM_PVT.add_language; commit; 9.4.3. 如何客户化 1、 编写客户化表的add_language。 2、 修改applprod.txt,加入客户化信息,例见附件中“SCF”相关部分;实际上我还 改了applUS.txt和topfile.txt。<用adsplice创建应用时,仅改了topfile.txt> 3、 编写客户化应用的NLINS.sql,例见SCFNLINS.sql。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附件开发 100 of 136 10. 附件开发 10.1. 关于附件 10.1.1. 关于附件 Oracle ERP二次开发中使用的附件方式有两种,一是通过标准功能,在系统管理员中 定义即可,不用写代码,就可以使任何Form,包括客户化Form,具有附件功能,这 个也是本章介绍的内容。 二是通过PL/SQL Gateway,需要我们便写代码完成;该方式其实和上述方式一的后 台实现是一样的;请参考ORACLE_UP_DOWN.PCK。 10.2. 标准附件设置 10.2.1. 表及其关系 fnd_attached_*****系列的表保存我们在开发员职责里面的附件定义。 fnd_documents_****系列的表保存最终用户的具体的附件业务数据,file类型的附件存 储在fnd_lobs表中。 fnd_documents_tl.media_id可以关联到fnd_documents_long_text.media_id、 fnd_lobs.file_id、fnd_documents_shot_text.media_id取得相应的附件内容。 10.2.2. 定义Entity实体 必须 N: Application Developer/Attachments/Document Entities 其实就是定义表,主要字段说明如下: 字段名 说明 Table 输入表名即可 Entity ID 输入表名即可,如果在同一个表定义多个实体,可以用“表名 _N”的形式 Entity Name 输入一个比较友好的名字 Prompt 没什么用 Application 如果是定义在Oracle标准表上,最好也用我们自己的应用名,否 则升级的时候会丢失 示例如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附件开发 101 of 136 实体起着联系各个Form中的附件的作用,比如组织层物料定义的附件可以在采购订单 行上看到,就是因为这个两个Form共同关联系Items这个实体。 10.2.3. 定义Categories类别 可选 N: Application Developer/Attachments/Document Categories 其实就是定义一个类别或者说一个标志,可以直接用系统的Miscellaneous。说明如 下: 字段名 说明 Category 输入任意一个名字即可 Default Datatype 最好选会应用这个Category最常用的类型,比如文件 Effective Date 默认,不填即可 Assinments按钮 查看关联到哪些Function或者Form 示例如下: 10.2.4. 定义Attachement Function 必须 N: Application Developer/Attachments/Attachment Functions 定义附件启用的功能或者Form。说明如下: 字段名 说明 Type 一个Form可能关联几个Function(进一步关联几个菜单),如果 附件在不同的Function下可能不同,比如Category不同(从而可 以过滤附件,这就是所谓的安全性),这里选Function;如果附 件不需要区分Function,这里就选Form Name Form或者Function的名字 User Name 自动出来 Session Context 示例如下: 10.2.5. 定义Function和Category关联 必须 N: Application Developer/Attachments/Attachment Functions/Categories Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附件开发 102 of 136 工作原理:一个Form(或Function)关联到一个或多个Category,最终用户把附件添 加到这个Form上的一条记录上时,需指定一个Category;到这里Category还没有显示 出什么作用,也就是如果仅仅一个地方会用到这个附件,Category就没什么用。如果 同一个实体的附件会在其他Form上出现,如一个Item的附件需要在Order Line上被显 示出来,那么,假如用户上传了一个图片作为附件,并分配Category为xxxxx,如果 Order Form的Category没有包含xxxxx,在Order Line上将看不到那个附件。 示例如下: 10.2.6. 定义启用附件的Block 必须 N: Application Developer/Attachments/Attachment Functions/Blocks 定义我们Form上包含附件的数据块,每个块都可以定义,主要字段说明如下: 字段名 说明 Block Name 输入Form中的块名 Method 一般Base Entity选Allow Change,如果是引用的选Query Only Secured By 这个可以进一步限制安全性,可以不定 示例如下: 10.2.7. 定义Block-Entity关系 必须 N: Application Developer/Attachments/Attachment Functions/Blocks/Entities 主要字段说明如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附件开发 103 of 136 字段名 说明 Entity 选择上面定义的实体,一行一个 Display Method 基础实体选择Main Window,引用实体选择Related Window Include in Indicator 基础实体打勾,引用实体不选;这个选项其实就是用来初始化工 具栏上的图标,选不选都不影响功能 操作许可 分别定义Query/Insert/Update/Delete,基础实体一般允许全部 操作,引用对象不能有Insert,其他的看需要 定义条件 根据条件更加灵活的定义“操作许可”范围 示例如下: 10.2.8. 定义关键字 必须 定义关键字段,一般是主键,这里指块上的Item而非表里面的,所以需要用“块 名.Item名”,按顺序定义。示例如下: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附件开发 104 of 136 基础实体和引用实体定义得关键字顺序要一致。 10.2.9. 使用过程 1、 打开我们的Form,查询记录或者输入新记录 2、 注意工具栏上的Attachment按钮时可用的 3、 点击会出来附件窗口,可以为不同的Category选择各种类型的附件: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 105 of 136 11. Javabean 11.1. Form与Java 11.1.1. Forms就是Java 虽然我们在设计Forms、编写PL/SQL代码的时候,感觉不到Java的影子,但在执行 时,其确确实实被转化为Java Applet在Jinitiator这个JVM中运行,我们在块中创建的 Item,其实际也对应一个个Java类,看下面的类列表,应该知道分别对应Forms中的哪 些Item: ORACLE FORMS JAVA UI CLASSES oracle.forms.ui.VButton oracle.forms.ui.Vcheckbox oracle.forms.ui.VComboBox oracle.forms.ui.VImage oracle.forms.ui.VPopList oracle.forms.ui.VRadioButton oracle.forms.ui.VRadioGroup oracle.forms.ui.VTextArea oracle.forms.ui.VTextField oracle.forms.ui.VTList *这些类在应用服务器的$ORACLE_HOME/FORMS60/java下面 11.1.2. 关于Implementation Class 在Forms Builder中,Item中有个属性叫“Implementation Class”,这个用来指定一 个Item到底继承自上表的哪个Java类。标准的Item,其Implementation Class都放空, 无须我们明确指定,因为Oracle内置了对应关系。 但如果要在Forms 中使用非标准的Class,比如我们自行扩展的,则必须明确指出Item 的“Implementation Class”,并且是带包名的全称,如cux.TextReader。 11.1.3. Forms中的Java类规范 一个类要在Forms中使用,其必须符合Oracle Forms的设计规范,简单说,就是要实 现oracle.forms.ui.IView接口。上述的VXXX类无一例外都implements了IView。 Oracle还提供了实现IView接口的Vbean类,如果我们欲创建的类不需要从其他类继 承,则可以直接extends VBean,省却麻烦的IView实现。 *如要加深理解或者遇到问题,可反编译VBean类来看个究竟。 11.1.4. Forms与Java类的交互 在Forms开发中,我们已经习惯于这样来“搞定”一个标准Item: 1、 通过属性面板设置属性。 2、 在代码中调用set_item_property或者get_item_property。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 106 of 136 3、 编写Item的Trigger。 4、 于是某个事件发生时会触发3的代码。 这些人性化的方式,背后是Oralce的辛苦转换: 1、 初始化Item时,调用setter函数。 2、 调用setter函数、调用getter函数。 3、 初始化Item时,调用addListener添加对应事件的侦听函数——我们写的代码。 4、 Java类Raise事件,并根据定义的Listener调用相应的代码。 但自定义的Class,则没有这么幸运,Oracle仅提供如下两种沟通方式: 1、 Forms中仅能调用set_custom_property、get_custom_property两个内置函数;背 后Oracle将其转换为调用Class的setter、getter函数。 2、 Java类中可以任意raise一个事件,包含事件名称和参数;Forms中统一通过when- custom-item-event来处理,事件名称保存在:system.custom_item_event中,参 数保存在:system.custom_item_event_parameters。 *参数是什么?是不是类的所有属性及其当前值? 11.1.5. Forms中使用自定义Java Bean 在Jinitiator中运行的Forms,基于Java的安全设计,“标准”功能无法操作客户端,如 果有此需求,可通过自定义Java类的方式来实现。 要在Forms中使用一个自定义的Javal类,那么按照上面的分析,结合Applet的安全 性,应该这样: 1、 根据需要,编写实现IView的类,或者简单的继承VBean,编写需要的代码。 2、 上传至Forms Server,并包含在CLASS_PATH中。 3、 如果需要操作客户端文件什么的,则需额外完成认证。 4、 Forms中创建Item,类型为BeanArea,且Implementation Class需明确设置。 仍然站在普通的Forms开发角度来理解,那么该如何“搞定”这个Item呢? 1、 编写when-custom-item-event,并根据:system.custom_item_event做出不同的处 理,如果需要参数,用Forms的两个内置函数get_parameter_list、 get_parameter_attr从:system.custom_item_event_parameters中取。当然编写者 需事先知道Class会Raise哪些事件、其参数分别是什么。 2、 任何时候,都可以调用set_custom_property、get_custom_property,至于Class 中的setter、getter,实际上可以实现任何逻辑,而不是通常所理解的设置属性、 获得属性,比如可以通过它们打开本地的文件、读取文件、执行本地命令、设置 可见Class的背景等等。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 107 of 136 11.2. 例子:Hello World 11.2.1. 功能 把一个字符串保存到Java Bean,再从中取出来。 11.2.2. 按规范编写Java类:BeanTemplate.java 该类实际上提供了一个最简单、但足够用的模版。 定义了2个属性并通过setProperty函数设置,但getProperty仅响应1个属性;Raise了1 个事件,事件名称可以随便定义,这里直接使用属性ID;事件参数,可以任意多个, 这里仅举例设置Event Description。请仔细理解如下代码: //import the minimum class import oracle.forms.properties.ID; import oracle.forms.ui.VBean; import oracle.forms.ui.CustomEvent; //Vbean class provides and empty implementation of the IView interface public class BeanTemplate extends VBean{ public static final String RCS_ID = "$Header: BeanTemplate.java 1.0 2007/01/25 20:00:00 pkm ship $"; static final ID PROPERTY1 = ID.registerProperty("PROPERTY1"); static final ID PROPERTY2 = ID.registerProperty("PROPERTY2"); static final ID EVENTDESCRIPTION = ID.registerProperty("EVENTDESCRIPTION"); private Object property1Value = ""; private Object property2Value = ""; //setter function //can be used in Forms: set_custom_property('BlockName.ItemName',ROW_NUM,'PropertyName','Value'); public boolean setProperty(ID id, Object value){ boolean result = true; try{ if(id == PROPERTY1){//Anything here, such as this.property1Value = value; //Raise any custom envet CustomEvent ce = new CustomEvent(this.getHandler(), PROPERTY1); this.getHandler().setProperty(EVENTDESCRIPTION,"Property PROPERTY1 is set."); //Any other parameter needed to be send to Forms dispatchCustomEvent(ce); } else{ if(id == PROPERTY2){//Anything here, such as this.property2Value = value; //Raise any custom envet } } } catch (Exception e){ e.printStackTrace(); result = false; } return result; } //getter function, available from Forms Server 6.06 //can be used in Forms: set_custom_property('BlockName.ItemName',ROW_NUM,'PropertyName'); public Object getProperty(ID id){ Object result = ""; try{ if(id == PROPERTY1){//Anything here, such as result = this.property1Value; } } catch (Exception e){ e.printStackTrace(); } return (Object)result; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 108 of 136 } //Just for test public static void main(String[] args) throws Exception { BeanTemplate bt = new BeanTemplate(); bt.setProperty(PROPERTY1,"Hello World"); System.out.println(bt.getProperty(PROPERTY1)); } } 11.2.3. 编译:BeanTemplate.class 方式一:上传至$OA_JAVA目录,直接编译javac BeanTemplate.java。 方式二:如果在本地JDK或者JDev下,需下载$ORACLE_HOME/FORMS60/java, 并设置到Class Path中。 11.2.4. 打包、上传、认证、配置 按照《深入浅出Oracle EBS之安全机制探索》的“Applet安全性”章节设置即可。 11.2.5. Forms中使用BeanTemplate 按照正常的步骤做Forms: BEAN_ITEM是手工创建的Item,需修改如下属性: 属性 值 说明 Item Type Bean Area 必须 Implementation Class BeanTemplate 必须,Java Bean的全称 Keyboard Navigable No 必须 Mouse Navigable No 必须 Cavas BLOCKNAME 必须 Foreground Color black 必须,黑色,设置白色测试报错 Background Color white 必须,白色,设置黑色测试报错 Width 0 可选,建议设置为0 Height 0 可选,建议设置为0 SET按钮的WHEN-BUTTON-PRESSED代码: set_custom_property('BLOCK_NAME.BEAN_ITEM',1,'PROPERTY1','Hello World'); GET按钮的WHEN-BUTTON-PRESSED代码: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 109 of 136 fnd_message.debug(get_custom_property('BLOCK_NAME.BEAN_ITEM',1,'PROPERTY1')); BEAN_ITEM的WHEN-CUSTOM-ITEM-EVENT代码: DECLARE l_parameter_list paramlist; l_parameter_type NUMBER; l_parameter_value VARCHAR2(2000); BEGIN fnd_message.debug('Event Name: ' || :system.custom_item_event); l_parameter_list := get_parameter_list(:system.custom_item_event_parameters); IF id_null(l_parameter_list) THEN fnd_message.debug('No Parameter.'); ELSE get_parameter_attr(l_parameter_list, 'EVENTDESCRIPTION', l_parameter_type, l_parameter_value); IF NOT form_success THEN fnd_message.debug('Can not get specific parameter.'); ELSE fnd_message.debug('Description: ' || l_parameter_value); END IF; END IF; END; 11.2.6. EBS中运行Form 在EBS中定义Form并运行,最终测试结果: 1、 11.5.10下,第一进入Form,就弹出认证,选“总是授权”: 2、 打开Form后,点击Set按钮,弹出两个消息框,一个显示“Event Name: PROPERTY1”,另外一个显示“Description: Property PROPERTY1 is set.”: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 110 of 136 内部过程:Form中调用的set_custom_property被转换为对类BeanTemplate中 setProperty的调用;setProperty除了保存传入的值外,产生了一个事件;该事件 被传递至Form,由WHEN-CUSTOM-ITEM-EVENT捕获并处理。 3、 点击Get按钮,弹出“Hello World”: 内部过程:Form中调用的get_custom_property被转换为对类BeanTemplate中 getProperty的调用。 11.3. 例子:执行PC本地命令 本例由石金兴提供并测试通过。。 11.3.1. 需求 执行PC本地命令:exe文件、bat文件、cmd文件、sh文件等等。比如在EBS Forms中 打开收银箱,可通过执行PC上一个类bat的文件来完成。 11.3.2. Java如何执行本地命令 Java通过Runtime.getRuntime().exec(String cmd)执行本地命令。 1、 直接调用:如cmd = “notepad”打开记事本,适用于系统目录下的命令 2、 打开文件:如“cmd /c c:\\1.doc”,打开C盘下的1.doc 注:“/c”表示命令执行后立即关闭Dos窗口;cmd NT/2000/XP/2003下用,Win98用 command 3、 用批处理:如“cmd /c c:\\1.bat”,执行C盘下的1.bat 4、 Ping:如 “ping www.google.com” 5、 打开网页:”cmd /c start www.google.com” 6、 等待结束:需要先获得执行的命令的句柄,然后调用wait。 Process p = Runtime.getRuntime().exec(String cmd); p.waitfor( ); Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 111 of 136 注:如果标准输出比较多,而运行窗口缓冲区不够大,会导致Java阻塞在waitfor。 7、 获得结果:需要先获得执行的命令的句柄,然后从标准输出缓冲区读取。 String s; Process p = Runtime.getRuntime().exec(“ping www.google.com”); BufferedReader bd = new BufferedReader(new InputStreamReader(p.getInputStream())); while ((s=bd.readLine()) != null){ System.out.println(s); System.out.flush(); } 11.3.3. 按规范编写Java类:CMDExecute.java 定义了2个ID,一是执行命令,一是执行结果,通过setProperty函数执行本地命令,并 把结果通过事件方式Raise给Form。核心代码其实只有一句,请仔细理解如下代码: //import the minimum class import oracle.forms.properties.ID; import oracle.forms.ui.VBean; import oracle.forms.ui.CustomEvent; //Vbean class provides and empty implementation of the IView interface public class CMDExecute extends VBean{ public static final String RCS_ID = "$Header: CMDExecute.java 1.0 2007/02/04 20:00:00 pkm ship $"; static final ID COMMANDEXECUTE = ID.registerProperty("COMMANDEXECUTE"); static final ID COMMANDRESULT = ID.registerProperty("COMMANDRESULT"); private Object commandValue = ""; private Object resultValue = ""; //setter function //can be used in Forms: set_custom_property('BlockName.ItemName',ROW_NUM,'PropertyName','Value'); public boolean setProperty(ID id, Object value){ boolean result = true; try{ if(id == COMMANDEXECUTE){//Anything here, sukch as this.commandValue = value; try{ Runtime.getRuntime().exec((String)this.commandValue); this.resultValue = "S"; } catch(Exception e){ e.printStackTrace(); this.resultValue = "E"; } //Raise any custom envet CustomEvent ce = new CustomEvent(this.getHandler(), COMMANDEXECUTE); this.getHandler().setProperty(COMMANDRESULT,this.resultValue); //Any other parameter needed to be send to Forms dispatchCustomEvent(ce); } } catch (Exception e){ e.printStackTrace(); result = false; } return result; } //getter function, available from Forms Server 6.06 //can be used in Forms: set_custom_property('BlockName.ItemName',ROW_NUM,'PropertyName'); public Object getProperty(ID id){ Object result = ""; try{ if(id == COMMANDEXECUTE){//Anything here, such as result = this.commandValue; } else if(id == COMMANDRESULT){//Anything here, such as result = this.resultValue; } } catch (Exception e){ Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 112 of 136 e.printStackTrace(); } return (Object)result; } //Just for test public static void main(String[] args) throws Exception { CMDExecute ce = new CMDExecute(); ce.setProperty(COMMANDEXECUTE,"notepad"); System.out.println(ce.getProperty(COMMANDRESULT)); } } 11.3.4. 编译:CMDExecute.class 参考“例子:Hello World”。 11.3.5. 打包、上传、认证、配置 按照《深入浅出Oracle EBS之安全机制探索》的“Applet安全性”章节设置即可。 11.3.6. Forms中使用CMDExecute 按照正常的步骤做Forms: BEAN_ITEM是手工创建的Item,需修改如下属性: 属性 值 说明 Implementation Class CMDExecute 必须,Java Bean的全称 其他属性 …… 参考“例子:Hello World” CMD文本框用来输入要执行的命令,默认值为notepad,可以根据Java要求修改。 RUN按钮的WHEN-BUTTON-PRESSED代码: set_custom_property('BLOCK_NAME.BEAN_ITEM',1,'COMMANDEXECUTE',:BLOCK_NAME.CMD); BEAN_ITEM的WHEN-CUSTOM-ITEM-EVENT代码: DECLARE l_parameter_list paramlist; l_parameter_type NUMBER; l_parameter_value VARCHAR2(2000); BEGIN fnd_message.debug('Event Name: ' || :system.custom_item_event); l_parameter_list := get_parameter_list(:system.custom_item_event_parameters); IF id_null(l_parameter_list) THEN fnd_message.debug('No Parameter.'); ELSE Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 113 of 136 get_parameter_attr(l_parameter_list, 'COMMANDRESULT', l_parameter_type, l_parameter_value); IF NOT form_success THEN fnd_message.debug('Can not get specific parameter.'); ELSE fnd_message.debug('Command Result: ' || l_parameter_value); END IF; END IF; END; 11.3.7. EBS中运行Form 在EBS中定义Form并运行,最终测试结果: 打开Form后,点击Run按钮,会打开一个空的记事本,然后立即弹出1个消息框 “Event Name: COMMANDEXECUTE”,OK后又显示“Command Result: S”: 可以设想,把要执行的命令用Profile或者做个Form来设置,就更加灵活了。 11.4. 例子:读取PC文本文件 本例来自公司实际的开发,不知道最先是哪位大侠使用,这里只能心怀感激了。 11.4.1. 需求 读取PC文本文件:txt文件、csv文件等等。比如从本地读取数据文件,验证、分析后 上传到某个表中。 11.4.2. 按规范编写Java类:TextReader.java setProperty中可以完成如下功能: 1、 选择本地一个文件,选择完Raise一个事件给Form 2、 设置初始路径 3、 设置需要读的行位置 getProperty中完成如下功能: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 114 of 136 1、 读取整个文件内容到内存向量中,并记录行数 2、 读取当前行 请仔细理解如下代码,其中main函数仅仅用来在Java中直接测试: //import the minimum class import oracle.forms.properties.ID; import oracle.forms.ui.VBean; import oracle.forms.ui.CustomEvent; //import specific class import java.awt.Dialog; import java.awt.FileDialog; import java.io.*; import java.util.Vector; import oracle.ewt.util.WindowUtils; import oracle.forms.api.IObject; public class TextReader extends VBean { public static final String RCS_ID = "$Header: TextReader.java 115.1 2002/06/19 12:00:00 pkm ship $"; static final ID OPENDIALOG = ID.registerProperty("OPENDIALOG"); static final ID LASTDIR = ID.registerProperty("LASTDIR"); static final ID FILTER = ID.registerProperty("FILTER"); static final ID READFILE = ID.registerProperty("READFILE"); static final ID LINENUM = ID.registerProperty("LINENUM"); static final ID READLINE = ID.registerProperty("READLINE"); private static String CRLF = "\r\n"; private static String NULLSTRING = ""; private static String filelastdir = ""; private static String filetoread = ""; private static int totalline = 0; private static int linenumber = 0; private static Vector filecontent = new Vector(); public boolean setProperty(ID id, Object obj) { boolean result = true; System.out.println("entering set_property ..." + id.toString()); try{ if(id == OPENDIALOG){ System.out.println("set_property OPENDIALOG...\n"); FileDialog filedialog = new FileDialog(WindowUtils.getSharedOwnerFrame()); if (obj.toString().equals(NULLSTRING)) filedialog.setDirectory(filelastdir); else filedialog.setDirectory(obj.toString()); filedialog.show(); try{ filetoread = filedialog.getFile(); filetoread = filetoread.equals("null") ? NULLSTRING : filedialog.getDirectory() + filetoread; System.out.println("filetoread is " + filetoread); filelastdir = filedialog.getDirectory(); CustomEvent customevent = new CustomEvent(this.getHandler(), OPENDIALOG); this.getHandler().setProperty(OPENDIALOG, filetoread); this.getHandler().setProperty(LASTDIR, filedialog.getDirectory()); dispatchCustomEvent(customevent); } catch(Exception e){ result = false; e.printStackTrace(); } } if(id == LASTDIR) filelastdir = (String)obj; if(id == LINENUM) linenumber = Integer.parseInt(obj.toString()); } catch (Exception e){ e.printStackTrace(); result = false; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Javabean 115 of 136 } return result; } public Object getProperty(ID id) { Object result = ""; try{ if(id == READFILE) result = read(); else if(id == READLINE){ if(filecontent.isEmpty() || linenumber > totalline) result = NULLSTRING; else result = filecontent.elementAt(linenumber - 1); } else if(id == OPENDIALOG) result = filetoread; else result = super.getProperty(id); } catch (Exception e){ e.printStackTrace(); } return (Object)result; } private static String read() { try{ BufferedReader br = new BufferedReader(new FileReader(filetoread)); filecontent.removeAllElements(); String s; System.out.println("Reading " + filetoread + "...\n"); while((s = br.readLine()) != null) filecontent.addElement(s); br.close(); System.out.println("Done.\n"); totalline = filecontent.size(); return Integer.toString(totalline); } catch(IOException ioexception){ System.err.println("IOException: " + ioexception.getMessage()); return null; } } //Just for test public static void main(String[] args) throws Exception { int totalline = 0; TextReader tr = new TextReader(); tr.setProperty(OPENDIALOG,""); System.out.println(tr.getProperty(OPENDIALOG)); totalline = Integer.parseInt(tr.getProperty(READFILE).toString()); for (int i=1; i<=totalline; i++) { tr.setProperty(LINENUM,String.valueOf(i)); System.out.println(tr.getProperty(READLINE)); } } } 11.4.3. 后续工作 如果能过领会Hello World的框架,接下来应该很容易参照TextReader的main函数, 在Form中使用该Java Bean!如果还不会,请撞豆腐哦! Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Form个性化 116 of 136 12. Form个性化 12.1. Form个性化原理 12.1.1. 个性化与客户化 要客户化一个标准Form,我们可以采取两种办法: 1、 修改fmb文件,适用于重大逻辑修改、布局调整比如新增Item。 2、 个性化,适用于简单的调整比如修改Prompt、执行PLL和数据库过程、显示消 息、添加Special菜单、触发触发器、提交请求、打开一个Function或者URL、 Forms_DDL、Do_Key、Go_Item、Go_Block。 个性化的好处在于,基本上不用担心升级问题,因为个性化信息是保存在表中的,独 立于源代码,但EBS版本升级或打Patch时,fmb可能被覆盖,但个性化信息仍然得以 保留。 个性化信息保留在下面表中: SELECT fcr.function_name, fcr.description, fcr.trigger_event, fcr.trigger_object, fcr.condition, fcr.enabled, fcr.fire_in_enter_query, --fcr.rule_type, --fcr.form_name, fcs.level_id, fcs.level_value, fca.* FROM applsys.fnd_form_custom_rules fcr, applsys.fnd_form_custom_scopes fcs, applsys.fnd_form_custom_actions fca WHERE fcr.id = fcs.rule_id(+) AND fcr.id = fca.rule_id(+) 12.1.2. 个性化原理 个性化的内容我们完全可以通过修改fmb文件来实现,道理很简单,Form个性化的本 质是触发器。 基于Template的开发中,几个触发器里回去判断是否有个性化信息,如果有,就动态 执行之。 主要触发器:When-New-XXX-Instance、When-Validate-Record、Special1..45。 附件《FormPersonalization_for_MetaLink_Rollup3.pdf》详细描述了Form个性化, 最新的官方PDF请查阅Metalink。 我们这里以举例的方式来学习。 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Form个性化 117 of 136 12.2. 例子:修改字段Prompt 12.2.1. 打开欲个性化的Form,调出个性化定义界面 先进入Sysadmin/Security/User/Define N: Help/Diagnostics/Custom Code/Personalize 12.2.2. 输入个性化条件、个性化内容 可以看到,个性化是在Function级别的(.10版本后可以设置在Form级别),可以同时 做多个个性化(按序号排列),每个个性化都可以设置Condition、适用的语言,并且 有多个Action(按序号排列): Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Form个性化 118 of 136 像修改字段Prompt这种简单的个性化,可以直接点击“Apply Now”查看效果。 12.3. 例子:有条件显示消息 12.3.1. 打开欲个性化的Form,调出个性化定义界面 先进入Sysadmin/Security/User/Define N: Help/Diagnostics/Custom Code/Personalize 12.3.2. 输入个性化条件 假如我们希望每次光标进入User块、用户为HQMRP01时就显示消息: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Form个性化 119 of 136 注意这里可以调用数据库包,这样可以做非常复杂的判断了。 可以点击Validate测试下条件。 12.3.3. 输入个性化Action 显示“Hello World”: 12.4. 例子:添加菜单 12.4.1. 打开欲个性化的Form,调出个性化定义界面 先进入Sysadmin/Security/User/Define N: Help/Diagnostics/Custom Code/Personalize 12.4.2. 输入个性化Action 假如我们希望每次光标进入User块、启用Special菜单“Responsibility”: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Form个性化 120 of 136 注11.5.10种,请使用Menu1..n,尽量不用Special,因为后者Form可能已经使用过了, 而前者则是新推出的菜单。 12.5. 例子:打开功能 12.5.1. 打开欲个性化的Form,调出个性化定义界面 先进入Sysadmin/Security/User/Define N: Help/Diagnostics/Custom Code/Personalize 12.5.2. 输入个性化条件 只有当职责ID不为空时,才执行: 12.5.3. 输入个性化Action 打开职责Form: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Form个性化 121 of 136 注意参数格式: ='RESPONSIBILITY_ID_QF='||:USER_RESP.RESPONSIBILITY_ID。 对于传递给Form的参数,只有Form自身创建的Parameter可以成功,对于从对象组继 承过来的,个性化无法识别! 更多引用方式包括对字段属性、Select语句等,请参考官方PDF文档。 可惜系统的职责Form自身不支持根据参数自动查询,不然就可以实现和User Form的 “集成”;我们只好自己来处理,请继续看例子。 12.6. 例子:执行查询 12.6.1. 打开欲个性化的Form,调出个性化定义界面 先进入Sysadmin/Security/Responsibility/Define N: Help/Diagnostics/Custom Code/Personalize 12.6.2. 输入个性化条件 当一进入Form并且RESPONSIBILITY_ID_QF有值时: Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Form个性化 122 of 136 12.6.3. 输入个性化Action 设置查询控制参数为True,这样在Pre-Query中才会考虑RESPONSIBILITY_ID_QF 执行查询(本例不需要go_block): 12.7. 例子:Instance间迁移 12.7.1. 使用FNDLOADER Download,function_name参数是必须的: FNDLOAD / 0 Y DOWNLOAD $FND_TOP/patch/115/import/affrmcus.lct FND_FORM_CUSTOM_RULES function_name= Upload: FNDLOAD / 0 Y UPLOAD $FND_TOP/patch/115/import/affrmcus.lct Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 技巧、常用代码 123 of 136 13. 技巧、常用代码 13.1. Form中的变量 Form中用到的变量,总结如下: 变量定义位置 作用域,由低到高 访问方法 引用方式 各层触发器中的变量 该触发器 Form PL/SQL 变量名 Program Units中的变量 该Form的Session Form PL/SQL 包名.变量名 DB存储过程中的变量 该Form的Session Form PL/SQL+DB PL/SQL 包名.变量名 Block中的Item 该Form的Session Form PL/SQL+界面录入、修改 :块名.变量名 Parameters中的Item 该Form的Session Form PL/SQL+EBS定义Function传初值 :parameter.变量名 SYSTEM变量 该Form的Session Form PL/SQL,只读,不能定义和修改值 :SYSTEM.变量名 GLOBAL变量 整个应用 Form PL/SQL,不用明确定义 :GLOBAL.变量名 13.2. 初始值、格式掩码 13.2.1. Item的初始值属性 当前日期:$$dbdate$$ 当前时间:$$dbdatetime$$ 下一序列::sequence..nextval 引用参数::parameter. 引用字段::. 13.2.2. 在OU层获得默认本位币 思路是根据财务选项视图(屏蔽OU)连接SOB表取: SELECT sob.currency_code INTO :parameter.g_default_currency FROM financials_system_parameters fsp, gl_sets_of_books sob WHERE fsp.set_of_books_id = sob.set_of_books_id 13.2.3. 货币格式 思路是根据字段长度、预制文件设置的货币格式智能设置掩码: set_item_property('.', format_mask, fnd_currency.get_format_mask(:parameter.g_default_currency, get_item_property('.', max_length))); Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 技巧、常用代码 124 of 136 13.3. 消息处理 13.3.1. 调式 fnd_message.debug('Message'); 13.3.2. 出错消息 fnd_message.set_name('','') ; fnd_message.error; 13.3.3. 提示消息 fnd_message.set_name('','') ; fnd_message.show; 13.3.4. 询问信息 fnd_message.set_name('', ''); if fnd_message.question('YES','NO',NULL,1,2) = 1 then --do something else --do something end if; 13.4. Special菜单 13.4.1. 触发器When-New-Form-Instance Form级app_special.instantiate('SPECIAL1',''); 13.4.2. 触发器Pre_Block app_special.enable('SPECIAL1',property_on); 13.4.3. 触发器SPECIAL1 Form级写实际代码。 13.5. 库存组织访问 13.5.1. 拷贝标准对象组 从INVSTANDARD.fmb中拖INV_PARAMS到我们的Form中,并用Subclass,这样会 生成4个参数。 13.5.2. Form触发器 Pre-Form中编写:Fnd_Org.Choose_Org; Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 技巧、常用代码 125 of 136 13.6. 常用内置过程 13.6.1. 显示、隐藏画布 hide_view并没有真正hide一个画布,只是放到最下层,所以如果上层的画布没有完全 覆盖下层画布,下层的画布很可能用户还看得到;show_view则是把画布放在最上 层。 hide_view(‘View_Name’); show_view(‘View_Name’); 13.6.2. 取得查询到的记录数 没有属性指明Block的记录数,但可以通过函数取得查询到的记录数: GET_BLOCK_PROPERTY(‘Block_Name’, QUERY_HITS) ; 13.7. 待续 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附录:我开始学习Form时的笔记1,仅供参考,未必100%正确 126 of 136 附录:我开始学习Form时的笔记1,仅供参考,未必100%正确 实在对象如表格、Sequence、索引等建在本应用对应的用户表空间中,其他对象如视图、别名创建在Apps下,常见错误是新手把 表建在APPS下,以后又来建别名,这个时候删除别名时会报对象不存在,而建别名的时候又报对象已存在 如果把脚本保存在文件里面,注意一个块比如一个创建视图的语句不要有空行,否则会出现如下情况:把语句拷贝到SQL Window 能正常运行,用@执行文件却报错 如果要执行execute_query,注意要go_block到适当的Block,但是go_block是个受限过程,并不一定都能成功 Master-detail关系 block both are database block each block has one item based on database displayed 在PL/SQL Develop中没有环境变量,所以如果要查询多组织的View,需要先执行设置环境变量函数(下面例子是设置为Site级设 置的OU) declare x_org_id number; begin Fnd_profile.GET('ORG_ID',x_org_id); fnd_client_info.set_org_context(x_org_id); end; GLOBAL变量对于所有form有效(可能是同一个应用,这个尚未验证),而不仅仅是你所开发的form 变量比如Global和Parameter的初始化应该在pre-form里面,在when-new-form-instance里面初始化不行,因为when-new-form- instance是在进入第一个导航块的第一个item之后才促发的 没有属性指明Block的记录数,不过可以通过GET_BLOCK_PROPERTY(QUERY_HITS) 取得查询到的记录数 hide_view并没有真正hide一个画布,只是放到最下层,所以如果上层的画布没有完全覆盖下层画布,下层的画布很可能用户还 看得到;show_view则是把画布放在最上层 lov验证的时候是验证第一个可见的列,并且会把其他的返回值返回给各个Item,而不是仅仅验证而已 Tip:lov的查询一般是针对第一列,但是如果我们把%放在最前面,则可以查询所有列 用Execute_query执行查询的时候,会把Copy Value From Item里面的那个Item的值自动作为查询条件。当创建记录的时候也会 直接用该值初始化,而且不改变记录的状态。在更新记录的时候不知道会不会Copy过来尚未验证。Get_Item_property的时候用 ENFORCE_KEY属性,但不能Set。该属性在Master-detail设置的时候自动创建,删除的时候自动删除。如果不希望Copy Value From Item影响查询结果,可以在Pre-Query里面把Item的值设为null。 app_query.reset('block_name');如果第一次调用,会把当前的DEFAULT_WHERE,然后什么都不做,以后再来调用的时候则会把 第一次设置的DEFAULT_WHERE用set_block_property('SAA_HEADERS',DEFAULT_WHERE,...)设置回来,具体请参考app_core库 When-create-record的时候给Item赋值不改变记录状态 Sequence,如果我们在Item的Initial Value里面赋值,那么假如用户Focus To新记录,又回到老记录,如此反复,Sequence会 不断变大的 SQL Order BY的时候null值排在最后,这个一般不符合实际要求,可以这样解决ORDER BY nvl(Geography_Code,chr(0))解决 Trigger顺序1 pre-commit 块1的pre-delete,on-delete,post-delete(一条一下) 块1的pre-insert,on-insert,post-insert(一条一下) 块1的pre-update,on-update,post-update(一条一下) Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附录:我开始学习Form时的笔记1,仅供参考,未必100%正确 127 of 136 块2的pre-delete,on-delete,post-delete(一条一下) 块2的pre-insert,on-insert,post-insert(一条一下) 块2的pre-update,on-update,post-update(一条一下) ... post-forms-commit(不管有没有操作,都要发生!而且只有一次) on-commit post-database-commit Trigger顺序2 when-list-changed在前,Validation item在后,因为Validation item是在要离开这个item的时候才促发的 Trigger顺序3 pre-form/when-create-record/pre-block/when-new-forms-instance/when-validate-record/on-insert/post-forms 当定位到主块的一个记录,会促发子块的when-clear-record事件和when-create-record事件,问题是如果主块的是新记录(未 保存),在子块的when-create-record里面取主块的任何东西,居然是主块的上一次获得焦点的记录的东西;连用取块的当前记 录也是上一次获得焦点的记录 Trigger顺序4 post-changed在when-validate-item之前 所有的when-validate事件是当forms自己验证通过之后才促发的 禁用Clear功能可以通过在Form的key-clrblk里面调用app_exception.disabled,其实只是用Bell覆盖默认的执行 直接放在TAB Page上的Item,和放在堆叠画布上的Item在设计时是无法“所见即所得”,所以建议把所有的Item根据需要放在不同 的堆叠画布上再堆到TAB Page上 伪列Rownum在排序之前就已经决定,如果想得到排序后的Rownum,应当在嵌套一个Select语句;另外Where语句中的rownum只能 用<或者<=,不能有>或者>= 在SQL中用Over的时候,如果整个语句没有Order by语句,最后的结果还是会排序的,规则是先按Over里面的Partition排序,再 按Over里面的Order by排序。原因可能和分析函数的处理顺序有关(8ifunctions.pdf有详细介绍):先查询到数据 (Join/Where/Group By/Having),再运算分析函数(先分区,然后排序,再算Slide Windows,最后计算),最后是Order By。另外,一个疑问:我测试到的一个结果Group By好像无法影响Partition,可是按照8ifunctions.pdf的说法,应该先执行 Group By的,是不是因为Group By只是在第一阶段的处理时作用在集合函数上,之后进入第二阶段的处理就没用了。 同事在装8i的时候,连安装界面都没出来,而我机器可以装,后来才知道原来他的机器是P4,无法正常安装,记得以前看过一篇 文章,讲如何在P4上安装;近期又看到Oracle彻底解决8iP4安装问题 实际执行的Where条件,是我们设置DEFAULT_WHERE,再加上通过赋过值的Item。注意APP_FIND.query_range已经重载过,我们调 用的时候可以不区分query_number_range或者query_date_range;观察其代码,发现也是通过给Item赋值来影响查询的,只不过 是赋值的时候,可能是加上 # between,# >=或# <=;这样导致的一个结果是:Date类型的Item长度默认是11,被query_range 这样一搞,长度根本不够,于是就导致诸如where REQUEST_DATE >= to_dat的错误,所以记得把字段长度加长,比如1000;总的 来说,碰到From to的要小心长度。 当修改子类的时候,会自动更改很多属性,特别是Required,一定要注意 当对块进行刷新时,会修改很多Item的属性,别以为你设置过了,Oracle就会记住。我碰到的情况是Insert Allowed等被自动改 掉了!即使我的子类设置为Text_Item_Display_Only 当拷贝Item的时候,也有些问题,比如日期和关键弹性域的Validate-from-list会变成Yes 两个变量,如果都为Null,判断还是不相等,所以必须用 a1 is null and a2 is null。所以在On-lock里面的if条件,我们可 以把所以不可以为空的字段都写成允许为空的形式。 一般来说,系统变量是很好用的。然而有时候并非如此,比如Current_Record, get_block_property('blockname',Current_Record)的结果并非总是一样的,后者更加保险!特别是刚打开Form的时候,在 WHEN-NEW-RECORD-INSTANCE里面,前者是0,后者是1 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附录:我开始学习Form时的笔记1,仅供参考,未必100%正确 128 of 136 ''''表示一个单引号,''''''表示两个单引号。应该是这样理解,一个单引号表示转义字符,首尾两个单引号里面的内容表示字 符串。 重启Application cd $APPLCSF cd scripts cd PROD ./adstpall.sh apps/apps ./adstrtal.sh apps/apps Trigger顺序5 post-query,只有在界面可见的记录才会促发,记录从不可见变为可见时促发,促发过的记录不再促发; 保存的时候会引发Post Item/Record/Block事件,因为要Navigate到Form 数据库org_id初始值to_number(decode(substrb(userenv('CLIENT_INFO'),1,1),' ',null,substrb(userenv('CLIENT_INFO'),1,10))) 给非数据库Item赋值, new记录会变成insert(所以就不能F11了) query/changed记录不变 new块会变成query query/changed块不变 对On-lock的理解,由于先入为主的缘故,开始一直很苦恼,为什么If里面只用了一个Return,Form怎么知道要锁否?后来才知 道On类型的数据库触发器是替换型的,On-lock也不例外,所以只要On-lock不Raise什么东西出来,Form就认为是锁成功了,至 于实际的锁,我们有Select……For Update来完成,至于If判断只是进行更加严格的判定。 对Find的理解,开始也很纳闷,为什么在Pre-query里面直接给Item赋值就可,不用自己拼语句,现在也逐渐发现里面大有文 章。回想F11,这个时候的block其实是处于Enter-query状态,输入的东西Form会自动拼成Where语句(当然还要加上原来的 default where,如果有Copy from item,也要加上),对于每个Item上输入的值,一般是用 = ,如果有,就解析为like,如果 有#,则把后边的表达式(比如between,甚至是子查询)直接作为条件;而当form内部执行堆栈Navigate到Pre-query时,block 也是处于Enter-query状态,道理和F11一样,我们只管按业务查询要求对Item赋值,剩下的就交给Form去处理了;需要注意的是 当处于enter-query状态的block,是使用query length属性来限制输入的数据长度,而不是通常的maximum lengh,只不过query length默认是0,即等于maximum lengh,所以会出现当用app_find.query_range时长度不够的情况。 二次开发中自定义菜单的使用需要在三个地方写代码form when-new-form- instance/app_special.instantiate('SPECIAL1','prompt'),form pre_block/app_special.enable('SPECIAL1',property_on),form SPECIAL1 trigger/菜单需要执行的代码 Trigger顺序6 系统按照form中块的前后触发保存,而不是按照主从关系;所以可以把子块拉大主块的上面,这样子块内容会先保存 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附录:我开始学习Form时的笔记2,仅供参考,未必100%正确 129 of 136 附录:我开始学习Form时的笔记2,仅供参考,未必100%正确 Profile:一个Profile其实就是一个参数,在开发员职责里面定义,注意这个时候选择的Application仅仅是方便管理,对以后 的使用没有限制;一个Profile的具体值需要在系统管理员职责里面定义,可以分别在四个层次上定义,即 Site/Application/Responsibility/User,具体来说可以给任意一个Site或任意一个Application或任意一个Resp或任意一个 User定义这个Profile的值,可以四个层次都定义,也可以定义某几个层次;对于一个Session来说,一个Profile到底取什么值 是和上面的定义有关系的,即后面的覆盖前面的,如果它定义了的话。我们可以通过fnd_profile.value('Profile_Name')来取 得当前会话的Profile值,于库存组织Profile是mfg_organization_id 在用fnd_request.submit_request的时候,第五个参数用false,不要被参数名称误导;这个函数有105个参数,前面五个 定义请求本身,后面100个是传递给请求的具体参数,都是Char类型,我们需要转换,默认值是chr(0),代表这个参数不用传递 给调用的请求;在Package里面调用只需要传递需要的参数个数,因为它有默认值指示结束;在form里面则不行,要写满105个, 而且我们参数结束之后要用一个chr(0)来表示结束 用submit_request的时候,其实最终还是向fnd_concurrent_requests(另外我们定义的request是保存在 Fnd_Concurrent_Programs里面的)里面插入一行数据,如果不提交的话,该请求是不会真正开始运行的,所以我们一般在 submit_request之后来个Commit,否则的话如果使用wait_for_request的话会无限期等待下去;在Form里面需要注意的是,必须 使块基于数据库,不然没得Commit。假如在一个请求内部使用了submit_request,特别注意的是需要检查Concurrent Manager的 Process数至少为2,否则上述两个请求会处于死锁状态 数据库表中的Org_Id或者Organization_Id对初学者来说往往搞不清楚是哪个层次的组织,一般来说前者是OU,后者是库存组 织,然而这也不一定,有一个简易的方法是,只要我们碰到Item,比如在订单中,那么一般是指库存组织 _all,基表,保存多组织数据,里面有一个Org_Id字段,一般不直接出现在我们的DML中 去掉_all的视图,根据用户环境过滤掉组织,相当于普通的基表,我们直接使用它,就当它是基表 _v,视图,给Form用 _kfv,启用关键性弹性域的时候动态生成的视图,包含Concact过的字段组合 _dfv,启用描述性弹性域的时候动态生成的视图,我们取弹性域子段的描述的时候,需要用用户出口函 数。。。。。。。。。。。 _tl,基表,有language字段,us肯定有,其他的看安装 _vl,视图,根据环境设置过滤语种,所以做报表用_vl _s,序列号 _API,Package,保证向后兼容 _PKG,Package,Program _SV,Package,供Form调用 rowid,伪列,指明记录物理位置,文件号+块号等,具体的我也不清楚 rownum,伪列,指明符合条件的记录的记录号,需要注意的是这个记录号在Order By之前就已经决定,如果想用Order By之后的 记录号,只能再套一层Select level,伪列,指明树状结构记录的层次,以下示例代码可作为Form Tree的数据源 select 1 state, level, CUSTOMER_NAME,null,CUSTOMER_ID from da_customers a start with PARENT_CUSTOMER_ID = -1 connect by prior CUSTOMER_ID = PARENT_CUSTOMER_ID who,五个记录创建及修改历史的字段,可以通过OA的Help/Record History菜单查看 attribute_xxx,描述性弹性域字段 文件系统File system的层次一般是$APPL_TOP/$MODULE_TOP such as $GL_TOP,$AU_TOP/Version/forms,reports,sql,.../EN,ZHS,...五个层次,在URL中有一个语言参数,它的值是根据用户当前的 NLS_LANG设置来的;假如指明是中文,那么Form Server会到ZHS下取文件,在任何其他语言对应的目录下找不到文件的话,系统 都会到EN目录下取,假如还取不到,这个时候才报错。AU指Application Utility,我们编写的Form源文件一般放在 $AU_TOP/Version/forms/LANG下,而PLL源文件一般放在$AU_TOP/Version/resource下,但编译的时候前者要放到对应应用的对 应语言的目录下,而后者还是在源目录;对于Report,则直接放在对应应用的对应语言下,不需要编译 一个用户对应一个或多个responsibility,一个responsibility对应一个或多个menu,一个底层menu对应一个function,一个 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附录:我开始学习Form时的笔记2,仅供参考,未必100%正确 130 of 136 function对应一个form或者一个报表或者一个程序;一个responsibility对应一个请求组,一个请求组对应一个或多个报表和程 序,这个应该放过来说,在定义报表的时候可以选择哪个请求组;一个responsibility对应一个Application,这个不要和该 responsibility拥有的菜单混淆,菜单没有限制 Attachements Oracle的附件给我们提供了一个方便的功能,只要定义定义,不需要任何代码就可以实现附件功能,比弹性域还方便(当然功能 没有弹性域强) 定义Attachements比较容易,遵循115devg中的Attachements章节即可完成。 fnd_attached_*****系列的表保存我们在开发员职责里面的附件定义 fnd_documents_****系列的表保存最终用户的具体的附件业务数据,file类型的附件存储在fnd_lobs表中 fnd_documents_tl.media_id可以关联到fnd_lobs.file_id、fnd_documents_long_text.media_id、 fnd_documents_shot_text.media_id取得相应的附件内容 定义过程如下 1、定义Entity实体,其实就是表,必须的 Table 输入表名即可 Entity ID 输入表名即可,如果在同一个标定义多个实体,可以用“表名_N”的形式,随便 Entity Name 输入一个比较友好的名字,这个名字要显示给用户看 Prompt 没什么用 Application 就是我们的应用;如果是定义在Oracle标准表上,最好也用我们自己的应用 名,否则升级的时候会丢失 2、定义Document Categories,其实就是定义一个类别或者说一个标志,可以直接用系统的Miscellaneous这个类别,可选的 Category 输入任意一个名字即可 Default Datatype 随便选一个,最好选会应用这个Category最常用的类型,比如文件 Effective Date 默认,不填即可 Assinments按钮 这里不用管 3、定义Attachement Function,必须的 Type 一个Form可能关联几个Function(进一步关联几个菜单),如果附件在不同的 Function下可能不同,比如Category不同(从而可以过滤附件,这就是所谓的安全性),这里选Function;如果附件不需要区分 Function,这里就选Form Name Form或者Function的名字 User Name 自动出来 Session Context 我没用它 Enabled 打勾 4、点击Category按钮,为上面定义的Attachement Function选择刚才定义的Category,或者选择Miscellaneous,可以选择任意 个,必须的 **Category的工作原理:Category本身仅仅是一个标志,就像我们部门字典表一样;一个Form(或者其Function)会关联到一个 或多个Category(就是在这里定义);最终用户在把一个附件添加到这个Form上的一条记录上时,必定会指定属于某个 Category,可选范围就是这里定义的;到这里Category还没有显示出什么作用,也就是如果仅仅一个地方会用到这个附件, Category就没什么用。如果同一个实体的附件会在其他Form上出现,就像115devg文档所说的,一个Product的附件可以在Order Line上被显示出来,假如用户上传了一个图片作为附件,并分配Category为xxxxx,如果Order Form的Category没有包含xxxxx, 在Order Line上将看不到那个附件。按我的理解,就这样。 5、定义Attachement Function Block,定义我们Form上包含附件的数据块,每个块都可以定义,必须的 Block Name 输入块名,不要告诉我你不知道! Method 一般Base Entity选Allow Change,如果是引用的选Query Only Secured By 这个可以进一步限制安全性,可以不定;这里我不多说,如果不清楚再找我 6、定义Block-Entity关系,必须的 Entity 选择上面定义的实体,一行一个 Display Method 基础实体选择Main Window,引用实体选择Related Window Include in Indicator 基础实体打勾,引用实体不选;这个选项其实就是用来初始化工具栏上的图标,选不选都 不影响功能 Indicator in View 我没选 操作许可 分别定义Query/Insert/Update/Delete,基础实体一般允许全部操作,引用对象不能有 Insert,其他的看需要 定义条件 根据条件更加灵活的定义“操作许可”范围;这里我不多说,如果不清楚再找我 7、定义关键字段,一般是主键,这里指块上的Item而非表里面的,所以需要用“块名.Item名”,必须的 按顺序定义,如果基础块的实体不定义主键,仍然可以工作,但在引用块将看不到,这是我碰到的问题 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 附录:我开始学习Form时的笔记2,仅供参考,未必100%正确 131 of 136 8、定义SQL Statement,附加的限制条件,就像我们在Form开发时定义的Where Clause一样,一般没用,可选的 使用过程如下 1、打开我们的Form,查询记录或者输入新记录 2、注意工具栏上的Attachment按钮时可用的,点击 3、出来附件窗口,想怎么玩都行 最好用IE浏览器打开Oracle Application,有些基于IE内核的浏览器可能无法打开附件上传窗口 Oracle ERP最佳技术实践 Forms开发指南 File Ref: 深入浅出Oracle EBS之Forms开发指南.doc (v. DRAFT 1A ) Company Confidential - For internal use only Doc Ref: April 5, 2007 Open and Closed Issues for this Deliverable 132 of 136 14. Open and Closed Issues for this Deliverable Add open issues that you identify while writing or reviewing this document to the open issues section. As you resolve issues, move them to the closed issues section and keep the issue ID the same. Include an explanation of the resolution. When this deliverable is complete, any open issues should be transferred to the project- or process-level Risk and Issue Log (PJM.CR.040) and managed using a project level Risk and Issue Form (PJM.CR.040). In addition, the open items should remain in the open issues section of this deliverable, but flagged in the resolution column as being transferred. Open Issues ID Issue Resolution Responsibility Target Date Impact Date Closed Issues ID Issue Resolution Responsibility Target Date Impact Date
还剩135页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

zhengrr

贡献于2017-06-12

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf