常用功能
手电筒功能
在需要使用手电筒的数据块下,添加快级触发器:QUERY_FIND,调用过程app_find.query_find,具体逻辑如下所示:
PROCEDURE query_find(block_window VARCHAR2 --需要使用手电筒的数据块名称
,find_window VARCHAR2 --查询块所在窗口
,find_block VARCHAR2 --查询块名称
)
BEGIN
copy('Entering app_find.query_find.'
,'global.frd_debug');
app_window.set_window_position(find_window
,'CENTER_ONCE'
,block_window);
go_block(find_block);
copy('Completed app_find.query_find.'
,'global.frd_debug');
END query_find;
具体如下图所示:
头块添加手电筒功能,头所在窗口为 MAIN
,查询块所在窗口为 QUERY_FIND
,查询块名称为 QUERY_FIND
。
跳转界面
1.调用存储过程 fnd_function.execute 实现跳转功能:
PROCEDURE EXECUTE(function_name IN VARCHAR2 --功能代码
,open_flag IN VARCHAR2 DEFAULT 'Y' --调用不同的内置函数 Y: open_form, C: call_form, 其他值: new_form
,session_flag IN VARCHAR2 DEFAULT 'SESSION' --SESSION: 新建会话,界面提交互不影响,NOSESSION: 不新建会话,任意一个界面提交,两个界面都会生效
,other_params IN VARCHAR2 DEFAULT NULL --传参 格式:param1name="param 1 value" param2name="param 2 value",如果跳转web页面,各参数之间改成 & 作为分隔符
,activate_flag IN VARCHAR2 DEFAULT 'ACTIVATE' --ACTIVATE/NO_ACTIVATE,表示是否聚焦到新打开的FORM
,browser_target IN VARCHAR2 DEFAULT NULL);
这里以跳转到查看请求界面作为示例:
fnd_function.execute(function_name => 'FND_FNDRSRUN'
,open_flag => 'Y'
,session_flag => 'Y'
,other_params => 'CONCURRENT_PROGRAM_NAME="CUXXXXRPT" PROGRAM_APPL_SHORT_NAME="CUX" DODT_REQ_ID="' || to_char(666) || '"');
2.调用存储过程 app_navigate.execute 实现跳转功能:
PROCEDURE EXECUTE(function_name IN VARCHAR2 --同
,open_flag IN VARCHAR2 DEFAULT 'Y' --同fnd_function.execute
,session_flag IN VARCHAR2 DEFAULT 'SESSION' --同fnd_function.execute
,other_params IN VARCHAR2 DEFAULT NULL --同fnd_function.execute
,activate_flag IN VARCHAR2 DEFAULT 'ACTIVATE' --同fnd_function.execute
,pinned IN BOOLEAN DEFAULT FALSE --值为TURE时,与fnd_function.execute一致,为TRUE时,相同FORM会复用,只会打开同一个实例,不会新打开一个FORM实例
);
询问
fnd_message.question 定义:
-- QUESTION-
-- display the message in a dialog box, with user defined buttons
--
function QUESTION(BUTTON1 in varchar2 default 'YES',
BUTTON2 in varchar2 default 'NO',
BUTTON3 in varchar2 default 'CANCEL',
DEFAULT_BTN in number default 1,
CANCEL_BTN in number default 3,
ICON in varchar2 default 'question') return number;
示例:
PROCEDURE warning_test IS
l_result NUMBER;
l_warning_message VARCHAR2(2000) := '存在警告信息,是否继续?';
BEGIN
fnd_message.set_string(l_warning_message);
l_result := fnd_message.question(button1 => 'YES'
,button2 => 'NO'
,button3 => ''
,default_btn => 1
,cancel_btn => 2);
IF l_result <> 1 THEN
RAISE form_trigger_failure;
END IF;
END;
显示如下:选择是时,返回 1,选择否时,返回 2,选择关闭时,返回 NULL
批量操作
勾选记录行,点击按钮,实现批量功能,本例中,LINE
为需要全选的数据块,CONTROL
作为按钮所在的数据块,点击测试按钮 TEST_BTN
,提示勾选的行数
具体实现:
(1)在数据块中,添加非数据块项
CHECK_FLAG
,相关属性如下(物理属性略):
属性名称 | 属性值 |
---|---|
子类信息 | CHECKBOX |
复选时的值 | Y |
未复选时的值 | N |
其他值的复选框映射 | 未选中 |
数据库项 | 否 |
(2)全选框,一般还会配套加上全选功能,比如可以在数据块 CONTROL
中添加数据库项 SELECT_ALL
,相关属性参考上面字段 CHECK_FLAG
方式一:
CONTROL.SELECT_ALL
添加触发器 WHEN-CHECKBOX-CHANGED
,内容如下:
PROCEDURE select_all IS
l_top_record NUMBER;
l_current_record NUMBER;
l_current_record_old NUMBER;
l_current_record_new NUMBER;
BEGIN
go_block('LINE');
l_top_record := get_block_property('LINE'
,top_record);
l_current_record := get_block_property('LINE'
,current_record);
first_record;
LOOP
:line.check_flag := :control.select_all;
--do something
EXIT WHEN :system.last_record = 'TRUE';
/*
移动记录前,如果有修改过字段的值,会触发 WHEN-VALIDATE-ITEM,如果中间有报错导致移动失败,导致下次循环时,仍定位的是相同记录
也可以前面修改字段值后,主动调用 WHEN-VALIDATE-ITEM 触发器的代码,保证不会报错,那么即使后面 next_record 时,会再调用一次 WHEN-VALIDATE-ITEM 也无所谓
至于 CHECKBOK 类型的 WHEN-CHECKBOX-CHANGED 触发器是不会自动触发,所有如果有必要,需要手工调用
*/
l_current_record_old := get_block_property('LINE'
,current_record);
next_record;
l_current_record_new := get_block_property('LINE'
,current_record);
IF l_current_record_old = l_current_record_new THEN
RAISE form_trigger_failure;
END IF;
END LOOP;
go_record(l_top_record);
go_record(l_current_record);
END;
方式二:
除了手写 for loop 循环
,也可以使用封装好的方法,封装的代码逻辑有兴趣的话,也可以看看,会发现其实和上面手写逻辑一致的
1)首先给 LINE
添加自定义触发器,如 SELECT_ALL
DECLARE
BEGIN
:line.check_flag := :control.select_all;
--do something
END;
2)CONTROL.SELECT_ALL
添加触发器 WHEN-CHECKBOX-CHANGED
,内容如下:
DECLARE
l_current_record NUMBER;
BEGIN
app_record.for_all_records('LINE'
,'SELECT_ALL'); --会自动循环执行自定义的触发器
END;
(3)点击按钮,提示勾选的行数,CONTROL.TEST_BTN
添加触发器 WHEN-BUTTON-PRESSED
,我这里比较简单,就直接再次循环,记录勾选的记录数
PROCEDURE test_btn IS
l_top_record NUMBER;
l_current_record NUMBER;
l_current_record_old NUMBER;
l_current_record_new NUMBER;
l_count NUMBER;
BEGIN
go_block('LINE');
l_top_record := get_block_property('LINE'
,top_record);
l_current_record := get_block_property('LINE'
,current_record);
l_count := 0;
first_record;
LOOP
IF :line.check_flag = 'Y' THEN
l_count := l_count + 1;
END IF;
EXIT WHEN :system.last_record = 'TRUE';
/*
移动记录前,如果有修改过字段的值,会触发 WHEN-VALIDATE-ITEM,如果中间有报错导致移动失败,导致下次循环时,仍定位的是相同记录
也可以前面修改字段值后,主动调用 WHEN-VALIDATE-ITEM 触发器的代码,保证不会报错,那么即使后面 next_record 时,会再调用一次 WHEN-VALIDATE-ITEM 也无所谓
至于 CHECKBOK 类型的 WHEN-CHECKBOX-CHANGED 触发器是不会自动触发,所有如果有必要,需要手工调用
*/
l_current_record_old := get_block_property('LINE'
,current_record);
next_record;
l_current_record_new := get_block_property('LINE'
,current_record);
IF l_current_record_old = l_current_record_new THEN
RAISE form_trigger_failure;
END IF;
END LOOP;
go_record(l_top_record);
go_record(l_current_record);
fnd_message.set_string('已勾选 ' || l_count || ' 条记录');
fnd_message.show;
END;
拓展:
以上只是一个最简单的例子,实际情况下,一般做法是在按钮逻辑中,将勾选数据插入临时表中,再调用存储过程;如果需要提并发请求,那就需要用普通表。
权限控制
参考代码如下,然后在 WHEN-NEW-RECORD-INSTANCE
和 POST-QUERY
触发器中调用 set_items_property
,在触发器 POST-INSERT
和 POST-UPDATE
,也可以考虑加上
---重置块字段属性
PROCEDURE reset_block_property(p_block_name IN VARCHAR2
,p_value IN NUMBER) IS
l_item_name VARCHAR2(240);
l_last_item_name VARCHAR2(240);
BEGIN
--fetch the first block item
l_item_name := p_block_name || '.' ||
get_block_property(p_block_name
,first_item);
--fetch the last block item
l_last_item_name := p_block_name || '.' ||
get_block_property(p_block_name
,last_item);
LOOP
IF get_item_property(l_item_name
,displayed) = 'TRUE' THEN
set_item_property(l_item_name
,insert_allowed
,p_value);
set_item_property(l_item_name
,update_allowed
,p_value);
END IF;
EXIT WHEN l_item_name = l_last_item_name;
l_item_name := p_block_name || '.' ||
get_item_property(l_item_name
,nextitem);
END LOOP;
END;
--重置当前记录字段属性
PROCEDURE reset_record_property(p_block_name IN VARCHAR2
,p_value IN NUMBER) IS
l_item_name VARCHAR2(240);
l_last_item_name VARCHAR2(240);
BEGIN
--fetch the first block item
l_item_name := p_block_name || '.' ||
get_block_property(p_block_name
,first_item);
--fetch the last block item
l_last_item_name := p_block_name || '.' ||
get_block_property(p_block_name
,last_item);
LOOP
IF get_item_property(l_item_name
,displayed) = 'TRUE' THEN
set_item_instance_property(l_item_name
,current_record
,insert_allowed
,p_value);
set_item_instance_property(l_item_name
,current_record
,update_allowed
,p_value);
END IF;
EXIT WHEN l_item_name = l_last_item_name;
l_item_name := p_block_name || '.' ||
get_item_property(l_item_name
,nextitem);
END LOOP;
END;
--权限控制
PROCEDURE set_items_property IS
BEGIN
--重置
--block
set_block_property('LINE'
,insert_allowed
,property_true);
set_block_property('LINE'
,update_allowed
,property_true);
set_block_property('LINE'
,delete_allowed
,property_true);
--item
reset_block_property(p_block_name => 'LINE'
,p_value => property_true);
/*
如果允许更改的字段比较少,那可以默认全部不允许更新,然后再设置允许更新的字段;
如果允许更改的字段比较多,那可以默认全部允许更新,然后再设置不允许更新的字段;
*/
--record
reset_record_property(p_block_name => 'LINE'
,p_value => property_false);
--根据条件,将修改具体字段权限
set_item_instance_property('LINE.COLUMN1'
,current_record
,insert_allowed
,property_true);
set_item_instance_property('LINE.COLUMN1'
,current_record
,update_allowed
,property_true);
END;
客户化菜单栏
可定义45个表单层的触发器,SPECIAL1 ~ SPECIAL45:
工具:SPECIAL1 ~ SPECIAL15
报表:SPECIAL16 ~ SPECIAL 30
活动:SPECIAL31 ~ SPECIAL45
设置快码菜单
CUX:快速编码 功能定义示例:
功能:CUX_LOOKUP
用户功能名:CUX:快速编码
表单:定义代码
参数:APPL_SHORT_NAME=“CUX” VIEW_APPLICATION=“CUX”
只展示某个快码的菜单,如 CUX_TEST
功能:CUX_LOOKUP_TEST
用户功能名:CUX:快速编码-TEST
表单:定义代码
参数:APPL_SHORT_NAME=“CUX” VIEW_APPLICATION=“CUX” LOOKUP_TYPE=“CUX_TEST”