form开发笔记(一)

来源:互联网 发布:angularjs http json 编辑:程序博客网 时间:2024/05/08 13:10

 

1、开篇

    我在项目上的首个form开发是杂项费用定义界面的开发。下面是我的开发笔记。

2、开发步骤

    项目上有关于form的标准模板,这个模板是整合了所有与具体开发无关的项,比如添加folder函数库,对象组,以及folder特有的item等等,省却了我们很多的麻烦,而这些在黄老的folder开发指南中有详细的步骤,不详述。

总体的开发步骤如下:

1)根据基表写视图,注意命名,视图里用的是不带all的同义词,视图具有MOAC安全性。 

2)给form命名、修改form的PRE-FORM、WHEN-NEW-FORM-INSTANCE 触发器            

3)基于视图建立block;建立对应的prompt块                                            

4)建立画布、把第三步生成的item发到画布上                                 

5)修改PROMPT 名,拖拽生成                                                 

6)生成视图增删改 package cux_0_plsql_autocreate.form_view_iud          

7)编写lov、block trigger代码。                                           

  其中,block级trigger是对主从块添加四个on- 开头的trigger,要建立以块命令的private包,trigger调用包中的过程,实现视图的增删查改

 

3、预备役基础知识

    在我们做form开发的时候,需要弄明白的一点就是:基表,视图以及form三者的关系,要明白它们三者之间是如何进行联系的:数据库基表的数据是如何显示到前台界面,我们在form中录入的数据又是如何存储到后台数据库中的。

3.1、基表,视图和form的关系——从后台基表到form前台界面;

   

    首先,每一个form界面都必然对应一个相应的后台基表,这个基表是储存所有前台显示数据相关的字段,它储存的是对应的id和code。

    这些id和code如何展现到前台呢?

通过视图;我们新建的各个block块很多都是基于视图的;

Form是客户化的,后台存储的id和code可能开发人员都不清楚每一个具体代表什么意思,因此在创建视图的时候会根据这些字段关联到其他的表,从这些表中取出id、code相对应的中文名称。视图是做什么用的?实际开发中我们基于视图view创建数据块,我理解为:视图只是专门用来在form前台呈现数据给用户看的,没有其他的作用。我们在前台所做的增删查改四种操作,只有会用到视图,静态的视图,其他三种会引起数据库数据改变的动态操作是通过调用private包内的过程实现的,和视图没有任何的关系。没做form之前我还以为前台数据会通过视图进行反编译出对应的后台字段,我果然太天真了~~~现在明白:视图只是单纯的显示数据用~

    而我们在前台录入的数据,在点击保存的时候,是通过调用我们写的对应的private包(后面详述)实现的对数据的增删改~

3.2、lov列和list的区别

    Lov列和List都可以实现指定数据的范围进行选择。

它们的共同点:

a.都可以供用户选择指定范围的数据;

    b.Lov和list对数据的修改都是在form层中进行修改,对view是没有影响的,只有在点击保存的时候,会触发触发器调用private的包,对基表进行DML操作,从而将我们所做的修改保存到基本表中;

 

它们的区别:

区别一:

lov只是在用户修改数据的时候会用到,比如要修改一个物料编码,我点开lov,选择一个进行修改;在不进行修改操作的情况下,只是查询数据,那么lov本身是不起任何作用的; 而且我们在lov中显示的值一般都会在视图中查找出来;

而list在显示数据的时候,一直在生效;可以把form界面分为form层的前台和后台,前台显示数据,后台存放对应的值;同样的一个值列表:

 中文名:“按通知单行”对应基表中存储的字段: MEMOLINE

 

如果使用lov值列表实现,那么前台我们见到的数据项只有一个,但是后台却需要两个数据项来储存数据;  我们需要在form中建立两个item,其中一个存放中文名:用来在前台显示数据;另一个存放对应的code值:在增改操作的时候写入数据库时用到;

 

如果使用list值列表,那么我们前台见到的数据项只有一个,后台需要的数据项也只要一个;list本身相当于是在前台和后台中间建立了一个转换器,我们在后台存储的code值经过list转换器会自动转换成对应的中文名,显示到前台界面;也就是说form后台存储的是code值,但是我们在前台看到的却是对应的中文;

 

可以参照list定义值的结构进行理解:

它就是一个翻译软件~


因此在建立视图的时候,我们没有必要把去关联N多张表,把code对应的中文名取出来,而是可以直接在form层使用list值列表定义其对应的中文名;

一般在快速编码中会用到这种方法;见知识点XX

注意,如果某个item属性是list,它的列表值是快速编码中的值,需要调用一个函数,难点:函数的作用就是将我们编写的类似lov的sql语句的records值指定给list列表值,同时,我们要给这个list指定一个初始值,不然编译时会报错。这时候,我们点击下拉列表,列表值就是我们定义的初始值和records值;

 

区别二:lov列表值可以指定返回多个item,而list列表值只能返回值给当前的item;

 

3.3  moac安全性控制与按ou屏蔽

 

我对moac还没有怎么深入的研究过:目前我对moac的理解是,在form中,会启用moac进行安全性控制;

我们所打开的form,都是定义在某一个具体的职责之下的,但并不是每一个职责都可以访问到所有的后台数据;比如,一个大公司,它在北京,上海都有分公司,它们共用一个ebs系统,数据库中储存了北京公司和上海公司的业务数据;

现在,系统中定义了两个职责:北京分公司办事处和上海分公司办事处;

 

进入北京分公司办事处的操作人员在对数据库中的数据进行修改时,是不允许对上海分公司的数据进行任何操作的,严格上来说,北京分公司的人在查询数据的时候,上海分公司的数据都不应该显示出来;那么,这个是如何实现的呢?

通过定义安全性配置文件,实现moac安全性控制;

Moac安全性配置文件中会定义该职责能够访问哪些ou,在我们进行查询数据的时候,从数据库中查询出来的数据,会经过配置文件的过滤,过滤掉所有没有定义的ou的数据,只把该职责下允许访问的数据显示出来;



按Ou屏蔽不是指在语句的条件中进行所谓的指定org_id=块.org_id,这等价于moac初始化;

举个例子:在多ou的情况下,比如:订单超级用户下有两个ou:江苏和浙江,后台表中江苏的ou下定义了费用类型,有四个值;而浙江下没有定义费用类型;

此时,在form中,即使你选定的ou是浙江,在点击lov值列表选择费用类型的时候,即使浙江并没有定义,但是依然可以选出四个费用类型;可以理解为,这个费用类型是该职责下所有的ou所公用的;唯一的区别是:要在配置文件中定义好该ou是否可以访问其他的ou;默认的是当前的ou只能访问当前ou的数据;

 

所谓的跨ou的业务:是指在同一个订单类型定义界面,在选择订单类型的时候,我可以选择多个不同的ou数据,就是说同一个页面,有多个ou的记录,当然,保存到数据库中的时候可以根据org_id进行指定; 就像下面的数据,多ou;

像我一开始的时候,指定lov值列表是使用的:

Where org_id=fnd_profile.value('org_id');

这样取的是当前的ou职责下,form级的默认org_id,对于多ou的情况下,那么该lov值只能选择到当前form级org_id对应的ou下表中定义的数据,当我们的行选择的是另一个ou的时候,因为form级的默认ou是不变的,块级的ou只对当前块的当前记录有效,所以如果上面那样指定的话,lov返回的是上一行的相同的结果;这样,就无法实现多ou的智能判断取值,应该使用:块名.org_id,这样取的就是数据块的值了;

 

一般一个职责下有多个ou,我们在进入和ou有关的form的时候,会弹出一个值列表,让我们选择是在哪个ou下,我们进入后,当前的form级org_id就是该org_id,如果当前form支持多ou,那么在选择ou的时候,我们可以选择其他的org_id,也就是块级的org_id;form级一般有一个默认的org_id,在配置文件中进行定义;

 

3.4、安全性配置文件的定义和分配

分配安全性配置文件

路径:系统管理员配置文件系统:指定责任、MO%配置文件,可进行分配,定义默认的业务实体;

如何查看、定义moac对应的配置文件内容?  B02_CB000_华润啤酒

职责:%HR%超级用户%--安全性配置文件   可查看和定义配置文件中定义的ou

我所在项目上职责划分很清晰,基本是的单OU

 

4、开发步骤详解

4.1、根据基表写视图

 

视图必须具有moac安全性,也就是说对基表的引用必须使用同义词,moac对ou数据的访问控制可以理解为:在form界面中我们使用Ctrl+F11,从all表中查询出所有数据后,因为moac安全性配置文件,会将所有不在配置文件中的ou的数据全部自动过滤掉,只留下符合条件的ou下的数据;

建表:

我们所做的form开发,首先判断是否需要新建数据库表?如果需要建表,判断是否有org_id字段,需要ou屏蔽?一般如果表中具有org_id即业务实体字段,都是受moac安全性控制,建表的时候需要建一个带_ALL的表(CUX_2_PX_MIXFEES_TYPES_ALL)和一个不带_ALL的同义词(CUX_2_PX_MIXFEES_TYPES),建立同义词的sql语句:

 

CREATESYNONYMAPPS.CUX_2_PX_MIXFEES_TYPESFORCUX.CUX_2_PX_MIXFEES_TYPES_ALL;

BEGIN

 dbms_rls.add_policy(object_name    =>'CUX_2_PX_MIXFEES_TYPES',

                      policy_name     => 'ORG_SEC',

                      policy_function => 'MO_GLOBAL.ORG_SECURITY',

                      policy_type     => dbms_rls.shared_context_sensitive);

END;

 

建视图:

当我们建好或找到我们所需要的基表以后,可以开始写视图了。

视图中所需要包含的字段,包括:

 

Rowid字段+基表中的所有原始字段+form前台显示字段

 

其中,如果需要ou屏蔽,基表需要使用同义词,从表不用;


所谓的moac安全性会在查询数据的时候自动过滤掉所有不在配置文件中定义的ou数据,只保留定义了的ou的视图数据;

 

注意:正式的视图中不能包含中文,否则程序迁移的时候会出现问题。如果需要有中文作为参考,可考虑使用两个版本

4.2、给form命名、修改form的PRE-FORM、WHEN-NEW-FORM-INSTANCE触发器

指定触发器中的form为我们自己编写的form文件名,其中:

在对某个form进行修改的时候,修改完成以后最好在PRE-FORM触发器中修改日期为当前日期,

'$Date: 2014/10/2 10:09 $'

作用:可以在form页面查看当前form的最后修改日期;当然,这不强制;属于编码习惯的问题;


 

4.3、基于视图建立block;

基于视图用向导建立block块,从模板中将block级trigger、两个item拖拽过来(FOLDER_SWITCHER,CURRENT_RECORD_INDICATOR)

需要注意的地方:

问题点:FRM-10757报错:No masterdata blocks are available.

在创建和其他块关联的时候,可能会报这个错误;

原因:勾选了auto-join data blocks,去掉后可以选择自己拟定关联条件;

 

4.4、建立画布、把第三步生成的item发到画布上

建立堆叠画布,将第三步创建的块的item放置到该画布上,属性设置参照其他form模板;

唯一麻烦的就是布局的设置,尤其是堆叠画布,调整相对位置;

知识点:活用画布布局的辅助线

 

4.5、建立对应的prompt块,拖拽生成;

建立对应的Prompt块,item宽度、顺序和block中对应的item一样~

从模板中将folder特有的6个item拖拽过来,并分配order by1/2/3到画布;

 

知识点:folder排序:prompt中的 order by

Order_by 按钮可实现folder记录的自动排序,无论放在哪个画布上都无所谓,但是必须放置在某个画布上,不能让Canvas属性为Null,否则报错:FRM-41014:不能设置空画布项;


可以排序的总是前三个基于table的列,Order by 1/2/3针对的form界面前3列数据项(对应prompt的前三个item);

 

4.6、生成视图增删改的package  cux_0_plsql_autocreate.form_view_iud

Form中,每一个数据块,我们在前台界面所做的修改,如果想要将数据保存到后台,需要调用一个private包,包里包含四个过程:

PACKAGEmixfees_header_privateIS

  PROCEDUREinsert_row;

  PROCEDURElock_row;

  PROCEDUREupdate_row;

  PROCEDUREdelete_row;

ENDmixfees_header_private;

这个包里的所有代码都是调用系统的一个包自动生成的(这个包是我们公司自己写的,其他项目上不一定叫做这个名字,以项目上的为准);

 

知识点1:package的开发规范

在form开发中,一般都不会直接在tirgger中写死函数,而是创建一个同名的包,里面放置各trigger对应的处理过程或函数,然后进行调用,比如:

包名规范如下:

 

知识点2:视图增删改过程函数的生成~

cux_0_plsql_autocreate.form_view_iud,右键test输入块名表名所有者CUX以及主键

自动生成包体注意直接生成的文件有小bug修改body中的用到的序列改成不带ALLsequence

执行后,在DBMS Output查看输出


将对应的包和包体部分代码直接复制过去就好~

 

4.7、编写lov、block trigger代码。

整个form开发的难点有两个:一个是布局:麻烦,另外一个就是trigger的编写;

知识点1:lov中隐藏的返回值;

在之前培训时候我做的form,基于表,而且也只是做form的前台界面,不管后台的实现;在实际的项目中,我们要做的不是form,而是一个功能,用form实现的功能,能够实现对后台数据的增删查改等操作的有意义的form;

基于视图和基于表创建block块的区别就在于:基于表,我们block中的每一个字段都能够和数据库中基表的字段相一致;而基于视图,block中会多出几项,这几项是我们根据基表中的数据关联到其他表中取出来,用来展示数据用的;

比如:费用类型的一个lov,我们在前台点击lov值列表,返回一个meaning值,而我们基表中储存的是该meaning值对应的code;我们如何将这个code值存储到后台中;方法:虽然在前台页面看lov值只有一个meaning,实际上,该值列表隐藏了一个code字段,它的显示长度为0;

前台界面:


对应的sql语句:

SELECT flv.lookup_code,flv.meaning

  FROMfnd_lookup_values_vl flv

 WHEREflv.lookup_type ='CRC_B02_PX_EXPENSE_TYPE'

   ANDSYSDATEBETWEENflv.start_date_activeANDnvl(flv.end_date_active,SYSDATE)

   ANDflv.enabled_flag='Y'

 

关于返回值lov中选定以后返回值不仅仅只是返回在界面上可见的item对于未在画布上显示的字段也需要返回值这部分未显示的字段是对应后台表中的字段在保存插入数据时用到。重要~

 

知识点2:头行结构中,行item与头item相关项的两种赋值方法

 

针对头行结构的行,我们一般需要在行表中储存一个头表的主键header_Id,以此将头表和行表相关联起来,同时如果头中涉及到了ou,那么行表很可能需要储存org_Id(按OU屏蔽);对于ou,头表我们可以通过lov值列表中选择好后,将它对应的org_id返回给头的org_id,方便储存的时候将对应ou的org_id储存到后台表中;但是,这个返回只能指定返回给一个项;那么,行表呢?行表中的org_id要如何指定?不止是org_id,还有其他和头表有关的数据应该如何获取?

 

方法一:通过触发器赋值 when-create-record

当光标移动移动到行新建记录的时候触发行block级触发器:when-create-record;在这里将行表中与头相关的项进行赋值;

 

方法二:在属性中直接设置:Copy Value from Item

 

知识点3:插入和更新时的重复性验证代码

这里的要点是要弄清楚表中的唯一性条件是什么,本开发中的行表唯一性条件是:

相同的业务实体(ou),相同的头(m_type_id)下,term_code必须唯一

这里的重复性验证代码主要是我们插入或更新的数据不能与表中其他数据重复;

插入和更新时的最大区别:

插入,pre-insert表示在插入之前进行重复性判断,行表的主键id是在插入时由序列自动生成,插入前主键id值为空~ NULL值和任何数值进行比较,结果依然为NULL,整个查询语句查不出结果,会抛出异常;因此,我们写的pre-insert触发器将无法生效;而且该条记录本身还没有存入到数据库中,所以数据库中所有的数据相对于该条记录来说都是其他数据;

 

更新,是修改现有数据,行表的主键id已存在,我们要判断其他记录和我们当前修改的记录的term_code是不是相同(相同ou,相同type_id下);如果有相同的term_code存在,那么l_count结果大于0,抛出错误;

 

 

知识点4:日历控件的编写

编写日历控件只要三步就可以设置好:

a.编写Item的KEY-LISTVAL触发器:calendar.show;

b.设置Item的List of Values属性:ENABLE_LIST_LAMP

c.设置Item的Validate from List属性:No

 

知识点5:日历控件的高级用法:指定可选的日期范围;

效果图:灰掉的区域鼠标无法选择,对应开始日期之前的日期不可选


代码如下:

Calendar Package中包含一下几个procedure:

1、Calendar.show([first_date])

显示日期选择列表,默认参数为空,即表示当前日期高亮,也可设置first_date指定任意日期高亮

2Calendar.setup(new_typevarchar2 --自定义类型,随便取

               ,low_date date --可选,为此日期之前

               ,high_date date –-可选,为此日期之后

               ,sql_string varchar2) --禁止显示的日期,可写sql语句

                --Setup必须必须在show的前面才能生效

Calendar.show;

 

a)calendar.setup(WEEKEND)禁止选择周末

注意WEEKEND参数是硬编码为星期六和星期天的,所以不适用于所有的国家

b)calendar.setup(‘title’,null,null,<SQL>);禁止选择SQL提取出来的日期清单

c)calendar.setup(‘title’,null,2014-01-01);只能选择2014-01-01后的日期

d)calendar.setup(‘title’,2014-01-01,null);  只能选择2014-01-01前的时期

 

要注意的一点:

如果使用了setup那么选出的日期是不会经过lov验证的也就是触发不了验证条件如果属性validate from list选择yes那么鼠标光标将无法移开

该属性的作用是:用户输入的值必须在LOV第一可见列的值范围内;

设为yes的话,选中日期会弹出如下后果;

 

但可是,如果是直接用日历控件去选择日期,因为灰掉的区域都是比开始日期小,处于不可选的状态,OK,我们选择的数据是肯定比开始日期大的,没有任何问题;

但是用户可能会作死,不用lov值,而是直接手动更改日期,因为数据是日期类型,如果输入错误的类型,系统下方会自动提示类型错误,那么用户可能输入错误的比开始日期小的日期,比如“2014”改成“2013”,怎么办?

方法:

在日期项下添加触发器,when-validate-item添加验证代码,验证日期值的有效性;

    IF event ='WHEN-VALIDATE-ITEM'THEN

      IF :mixfees_header.effective_end_date< :mixfees_header.effective_start_dateTHEN

        fnd_message.set_name('CUX','CUX_2_PX_MIXFEES_DATE_E');

        fnd_message.error;

        RAISE form_trigger_failure;

      ENDIFENDIF;

 

我们在日历控件中做了限制但与此同时也必须编写触发器进行限制

 

知识点6:快速编码的作用和定义

Form中用的较多的就是快速编码,快速编码就是定义一个类型,这个类型下面包含有数个我们自己定义的值,这些值可以自由的添加和删除以应对不同的业务需求;快速编码的应用很广泛;快码中还可以定义快码。

我们可以方便的在快码中添加和删除值,比如:我定义一个快速编码:

       CRC_B02_PX_EXPENSE_TYPE

这个快速编码的值我们可以在表fnd_lookup_values_vl中查看到

快码的定义路径:应用开发员代码公用

目前,该快码中只有四个费用类型,如果某一天因为业务需求变更,我们需要再添加一个类型,那么我们可以直接在这里修改快速编码的值,因为直接是图形界面,可以方便的进行定义和删除,可以适应随时的需求变更,而且用户也好操作,具有很大的灵活性;在form开发中被大量使用;

实例见知识点7~

 

 

知识点7:通过快速编码定义list值列表

需求:


倒三角选择快码值:即使用item的属性是list item,显示列表值;

 

方法:

声明一个记录组,定义一个block级过程define_list()在form初始化的调用该过程进行list值列表的初始化

 

代码: 其中enabled_flag ='Y'表示启用,同时要指定系统日期在有效范围内;

一般使用快码时都需要加上这两个限制条件;

1、声明一个记录组records:  ACTIVE_POINT,该记录组从快码中取出相应的值,其中,这里查询出来的结果集与list的对应关系:meaning->ListEliments; lookup_code->List Item Value

SELECT flv.meaning,flv.lookup_code

  FROMfnd_lookup_values_vl flv

 WHEREflv.lookup_type ='CRC_B02_PX_TRIGGER_POINT'

   ANDflv.enabled_flag ='Y'

   ANDSYSDATEBETWEENflv.start_date_activeANDnvl(.end_date_active,SYSDATE)

2、在block级package中定义一个过程:define_list()

(因为该item是在mixfees_header块下,所以该过程定义在该block下)

PROCEDUREdefine_list(p_record_group_nameINVARCHAR2,p_block_item_nameINVARCHAR2);

 

PROCEDUREdefine_list(p_record_group_nameINVARCHAR2,p_block_item_nameINVARCHAR2)IS

 record_group NUMBER := 0;

BEGIN

 record_group := populate_group(p_record_group_name);

 populate_list(p_block_item_name, p_record_group_name);

END define_list;

 

3、Form级triggerWHEN-NEW-FORM-INSTANCE中调用过程,form加载时初始化值列表

IF event ='WHEN-NEW-FORM-INSTANCE'THEN

 mixfees_header.define_list(p_record_group_name =>'ACTIVE_POINT',

          p_block_item_name   =>'MIXFEES_HEADER.ACTIVE_POINT_CODE');

 .. .. ..

 ELSIF event ='PRE-FORM'THEN

完成;

 

注意:我们如果是使用快码定义list值列表,需要在两处地方进行定义:

a. 指定Elements inList ,该项值至少有一个,绝不能为空,否则报错;

一般是从快速编码中选定一个已存在的值放在这里;

b. 指定Initial Value;否则编译时会报错:FRM-30188

一般,我们指定的初始值是快码中对应的某一个值~ 必须赋初值

 

 

 

知识点8:记录控制-指定块、项不可更新和删除

实际需求

a.不允许对记录进行删除;

b.一条记录保存后,除了“终止日期”外其他字段均不可修改;

 

在Block级的WHEN-NEW-RECORD-INSTANCE中追加代码,判断状态:

如果是插入的新值,所有块、项相应属性设置为true;如果是更新已存在的记录,则设置对应块、项属性为false;

判断的关键点:有无主键id

控制item的属性还可以使用:

app_item_property.set_property('MIXFEES_HEADER.END_DATE',UPDATE_ALLOWED, PROPERTY_TRUE); (这个更常用)

 

按照上面的代码,使用when-new-record-instance我们实现的实际上是要在光标选中记录的时候,当前记录才会变灰掉,成为不可更近状态;但是,实际上:

经常,我们会遇到查询出来的结果直接就是不可更新状态:

方法:使用post_query触发器

post_query是在查询出结果以后触发,每查询出一条结果触发一次;在这里设置不可更新,那么每一条查询出来的结果直接就是不可更新状态;

 

触发器的触发时点很重要,在实际开发过程中可以参考该篇博文,每一种操作后台对应的是哪些触发器: Oracle Form触发器执行顺序

http://www.cnblogs.com/quanweiru/archive/2012/12/28/2837148.html

 

 

知识点9:lov的item值清空时,清空相关联的数据;clear 与:=null

比如该列允许为空值,它还关联了一列code供后台对应数据用,我从lov中选择一项,返回一个值给该列,同时,返回一个code给另一个item;忽然,我又不想填数据了,于是我把它选为了空值,但是因为空值不触发验证,所以实际上另一个item依然存储的是前一个值对应的code;

这时候,如果我们点击保存,那么再度查询的时候,你会惊奇的发现,该列居然有值!!!明明我之前是置为空值了;

这种和当前列有关联的项,在该列置空的同时,应该将所有和它关联到的列存储的字段clear掉;

 

什么时候需要使用该clear代码?

一种:关联的项前台会显示;

当选中lov值,前台自动带出一溜儿值显示在界面上,比如我选择订单类型,后面自动带出类型的description;如果该lov被置空,因为空值不触发验证,所以,其他的值比如description不会自动发生改变,你会发现:lov值为空,但是却有对应的description。

此时:需要在when-validate-item中用代码直接手动将它关联的项置为null

        when xxisnullthen xx.xx := null

 

另一种:是只清空后台存储的code

使用clear清空数据,是指那些不在form前台显示的那些孤傲的,隐藏的,lov显示长度为0的字段值,可以使用clear函数帮助清空;

比如前台我显示的是类型的中文名,但是我后台储存的是对应的code值,在前台lov中选择值后除了返回一个中文名成给当前项以外,还会返回一个code值给code项,供插入数据的时候用;这时候,如果我们选中了一个类型,它对应的code是MIXFEE_CODE,此时,我们把类型值置空,对应的code值依然储存的是MIXFEE_CODE ,这不合理;

调用:app_field.clear_dependent_fields()

最好每一个存在关联的项都加上这个代码,即使该项是必输,这是为了养成良好的编码规范;

 

 

知识点10:头行结构,要求行:第一行必输?

在头中添加blocktrigger:pre-insert插入之前进行判断如果行的值为空则报错

ELSIF event ='PRE-INSERT'THEN

 IF:MIXFEES_LINES.TERMISNULLTHEN

 fnd_message.set_name('CUX','CUX_2_PX_MIXFEES_TERM_NULL_E');

 fnd_message.error;

 RAISEform_trigger_failure;

 ENDIF;

 

知识点11:消息定义,测试,弹出消息名而不是消息内容

 

消息定义完成后必须跑一个请求才能生效:应用开发员-工具栏查看-请求-提交新请求--选择生成信息--填写语言和应用产品(cus)

每次定义完消息,都要跑一次请求,中英文语言环境都要;退出form后再进,可生效;

 

 

知识点12:修改后台字段,对应三层改变:表,视图和form;

遇到需要修改后台字段,一旦修改,那么与之关联的需要修改视图,form前台:注意form中的增加的字段不要用向导创建,在已经定义布局好的form中,如果遇到因为后台字段的变化而修改对应item,使用向导会打乱已有的布局~直接修改item的name和后台对应的数据库字段:

form界面和数据库后台的字段的映射关系是在database属性中的ColumnName中建立的,修改了ColumnName对应的后台字段,那么该项与后台字段的映射关系也随之改变;所以我们无需做太多的改变,只需要修改item的属性即可:

实例: 数据库后台原本存的是MIXFEES_TYPE_ID ,现变更需求,储存MIXFEES_TYPE_CODE ,我需要做三层改变:

a.表    : 首先是修改数据库后台字段,我用的比较极端的方式,直接删表重建;也可以直接使用语句修改列属性;我是因为里面本身没有数据,所以简单粗暴干掉它;

b.视图层: 因为后台字段发生改变,对应的我需要修改我建立的视图;因为该列在视图中的作用有限;

c.form层: 在form层显示的只有meaning和enabled_flag,

其中meaning通过lov选择后会返回code值给该code项,以便在进行数据插入、更新时修改数据库表数据;要做的修改如下:

修改code对应的item名,修改属性对应数据库后台列,修改类型和字符长度;

修改lov对应的records的sql语句;指定lov中字段的返回值;

修改块级触发器pre-insert、pre-update中使用的原id字段改为code,重复性验证会用到;

修改private函数包,重新指定对应字段;

最快的方式查询你在哪些函数中调用了原有的id字段,使用该功能最方便快捷~

over!

 

知识点13:FORM级org_Id与块级org_id ——多ou下的org_id释义

Form级org_id:在多ou的情况下,一般会指定一个默认ou,我们通过

fnd_profile.value('org_id')  这是取的form级org_id,也就是对应的默认业务实体,多ou情况下无法由这段代码取到其他ou的值,因此在form中不常用;

在记录中,我们常常会取一些需要ou屏蔽的值,这时候要指定当前记录所在的ou一般是使用块级的org_id;

 

在对业务实体进行选择的时候,受moac安全性控制,判断条件:

SELECThou.organization_id, hou.name

  FROMhr_operating_units hou

 WHEREmo_global.check_access(p_org_id => hou.organization_id) ='Y'

其中,= 'Y'是检测ou值是否在该责任对应的moac配置文件中;是判断;

 

一开始,我在块级判断org_id也这样写:

SELECT aml.memo_line_id,aml.name, aml.description

  FROMar_memo_lines_all_tl aml

 WHEREaml.language =userenv('LANG')

   ANDmo_global.check_access(p_org_id => aml.org_id) ='Y'

因为没有明白mo_global.check_access()用法,简直作死;实际上针对多ou的请款下,它会取出所有符合条件的ou下的信息,而在块级,我们想取出来的实际上只是我们选定的ou下的信息,因此,数据明显多了;

实际上:

块级org_id调用:指定具体的org_Id,可直接引用form中的参数; :xx.org_id

SELECT aml.memo_line_id,aml.name, aml.description

  FROMar_memo_lines_all_tl aml

 WHEREaml.language =userenv('LANG')

   ANDaml.org_id =:mixfees_header.org_id

 

 

知识点14:item的属性限制和触发器起作用的先后关系

关于结束日期,我同时在属性和触发器做了双重的限制:

如下:

item中设置允许的最小值:


Trigger中设置最小值:

  IF:mixfees_header.effective_end_date < :mixfees_header.effective_start_dateTHEN

   fnd_message.set_name('CUX','CUX_2_PX_MIXFEES_DATE_E');

   fnd_message.error;

    RAISEform_trigger_failure;

  ENDIF;

它们的执行顺序:先根据属性验证值,然后根据trigger判断

 

此时,我在日历界面故意选择错误日期,数值返回到item中的时候,会自动根据设置的属性进行数值判断,如果在属性中定义了最小值,它会在trigger前触发,直接下方弹出错误提示;

如果属性值中未做验证,即属性是正确的,那么会触发when-validate-item的trigger,弹出错误提示框~;

 

这是两种设置的区别,相对来说,前者要简单些,直接在属性中设置即可;后者更客户化一些,可以定义消息提示框进行错误提示,不过要编写触发器代码;

0 0
原创粉丝点击