Oracle Retail RMS 客户化开发文档
概述
本文是在学习了retail rms下开发form的一点点心得。主要涉及内容是Form和Reports的开发。本文是在商云方经理的retail blog上取得原始资料,吸取了项目组很多同事的经验,非常感谢他们。本文档所描述的内容,可能只是Retail客户化开发的冰山一角,形如管中窥豹,希望后面有机会开发rms 的同事能从这篇文档里获得一些帮助,并对rms客户化开发文档进一步完善。
商云方经理的blog地址:http://blog.retailsolution.cn/
Retail 为Form开发提供了一个模板 fm_temp1.fmb ,本项目在/d01/app/retail/rms/app/rms/forms/src目录下,该模板基本上已经将retail form涉及到的底层代码写好(比如实现多语言的调用等),我们在开发一个新的form的时候,只要在该模板的基础上实现自己的业务逻辑就行了。
另外,还有一个模板fm_date.fmb,用来做和日期相关的操作,最常见的是做LOV。比如,和EBS一样,我们需要选择日期,弹出一个选则日期的LOV,就要用到这个文件。
Retail的form界面风格和EBS有点不同,给我的总体感觉是布局比较灵活,比较接近我们客户化的风格。它的一个block在界面上显示出来的字段一般比较少,现在还没有发现使用folder的情况。很可惜,也没有类似EBS的弹性域,因为其数据库表结构没有弹性域字段,我认为这是Retail需要改进的一个地方。
注:有的地方叫法为Retek,实际上也是指的retail。
开发背景与基础
读者基础要求
1. 有EBS的使用经验,尤其是Form的使用经验
2. 熟悉PL/SQL
3. 熟悉Telnet和FTP工具,熟悉Windows常规操作
4. 理解或开发过数据库应用系统
5. 有Form(EBS Form)开发经验则更佳
准备工作
用户
1. OS用户:包括超级用户root,用于上传编译开发的form
2. 数据库开发用户,比如本项目中rms12dev,用于数据库的操作,比如使用pl/sql developer开发package等
3. Rms开发管理用户,比如本项目的rms12dev,用于在rms系统中调试
注: 关于Rms用户,将在“新增RMS”用户讲到
工具
1. telnet工具 :如SecureCRT登录服务器,编译form和pll;连接服务器,SecureFX下载必要文件、上传开发的Form
2. Form builder: 安装developer10gr2(安装好后包括Form builder/Reports builder/Sql*pls/Jdeveloper)
3. 配置Form编辑环境:
客户端:
在D盘建立 retail_resource 目录
从服务器下载 /d01/app/retail/rms/app/rms/forms 目录
从服务器下载 /d01/app/retail/rms/app/toolsets 目录
Regedit
hkey_local_machine/software/oracle/KEY_DevSuiteHome1
编辑item : forms_path
添加
D:\Retail_Resource\forms\src;D:\Retail_Resource\toolset\src;D:\Retail_Resource\toolset\bin;
D:\Retail_Resource\forms\bin
服务器端配置
建立一个环境变量文件:rmsformsvr.env (创建在/home/oracle/),其内容如下:(环境不同,有些变量名称、路径不同)
ORACLE_HOME=/d01/app/oracleas/OraHome_1
export ORACLE_HOME
DISPLAY=syfr12:1.0
INSTALL_DIR=/d01/app/retail/rms/app
export INSTALL_DIR
PATH=$ORACLE_HOME/bin:$ORACLE_HOME/opmn/bin:$ORACLE_HOME/dcm/bin:$INSTALL_DIR/
forms10gr2_scripts:$PATH
CLASSPATH=$ORACLE_HOME/jlib/importer:$ORACLE_HOME/jlib/debugger.jar:$ORACLE_HOME/
jlib/utj.jar:$ORACLE_HOME/jlib/ewt3.jar:$ORACLE_HOME/jlib/share.jar:$ORACLE_HOME/
jlib/dfc.jar:$ORACLE_HOME/jlib/help4.jar:$ORACLE_HOME/jlib/oracle_ice.jar:$ORACLE_HOME/
jlib/jewt4.jar
FORMS_BUILDER_CLASSPATH=$CLASSPATH
FORMS_PATH=$INSTALL_DIR/toolset/bin:$INSTALL_DIR/rms/forms/bin:$ORACLE_HOME/forms
REPORTS_PATH=$INSTALL_DIR/rms/reports/bin:$ORACLE_HOME/forms
TK_UNKNOWN==$ORACLE_HOME/guicommon/tk/admin
UP=rms12dev/rms12dev@RETL
LD_LIBRARY_PATH=$ORACLE_HOME/lib:$ORACLE_HOME/jdk/jre/lib/sparc:$ORACLE_HOME/jdk/jre/lib
/sparc/native_threads
export DISPLAY PATH CLASSPATH FORMS_BUILDER_CLASSPATH FORMS_PATH REPORTS_PATH TK_UNKNOWN UP LD_LIBRARY_PATH
注:本小节资料由商云方提供 http://blog.retailsolution.cn/blog/index.php/rms-form-compile
感性认识
下面用几张图片来感性认识下rms form界面风格
<图1-1> rms登录界面
<图1-2> 用rms12dev登录后的主界面(rms12dev为开发管理员)
<图1-3> 查询界面以及LOV,查询界面类似ebs中点击“手电筒”后的查询窗口,区别是这里是一个单独的Form。
<图1-4> 主要业务逻辑处理界面,布局比较灵活。
<图1-5> 打印报表,点击“打印”后会显示<图1-6>所示画面。
<图1-6> 在运行报表前,需要再登录一次
<图1-7> 报表参数输入
<图1-8> 报表运行结果
Form获取
如果要修改一个Form,怎么知道它的名称呢?从界面上可以看到form的名字。比如下图中的recutadj,然后在/d01/app/retail/rms/app/rms/forms/src目录下(不同项目此目录可能目录不同)找到recutadj.fmb,下载之。
<图1-9> 通过界面查看Form名称
基于rms form开发过程
按Retail的习惯,开发一个新的form,一般需要开发下面4个文件,但不是所有的都是必须的。
fm_XXX_find.fmb[可选]
查找form,在打开“真正”的业务处理form fm_XXX.fmb之前,需要打开这个查找form,该form的作用是选择本次操作的类型查询出数据,一般操作类型有三种,“查看”,“编辑”,“新建”。如果是“新建”,则直接打开form_XXX来新建业务;如果是“查看”,则通过该form查到到业务记录,调用fm_XXX查看业务细节,fm_XXX的项都是不可编辑的;如果是“编辑”,则调用fm_XXX查看业务细节,并可对业务细节数据进行编辑。 当在RMS主界面上双击rms菜单项时,一般是打开该form,再由该Form调用fm_XXX。
使用fm_XXX_find,是因为Retail form中没有EBS的 F11,Ctl+F11等快捷键,也没有EBS中“查找”窗体。我们也可以不使用这个form,只要在打开form fm_XXX.fmb的时候能够确定本次操作是查看,编辑或者新建就好了。比如,同一个form我们需要不同权限的人来打开,有的权限是查看,有的是编辑,有的是新建。这时候,我们只要在菜单上给每个权限挂不同的菜单项,并对每个菜单项设置打开form的默认模式即可。
<图2-1>
<图2-2> 在<图2-1>所示界面点击“搜索”后,显示Form的另一个Canvases。
fm_XXX.fmb
这是实现业务逻辑的主要form。该form由fm_XXX_find.fmb调用open_form打开,open_form会使用一些参数,比如打开新form的状态是view,edit或者是new;或者是业务自己规定的一些变量。
<图2-3>
Me_XXX.mmb[可选]
fm_XXX.fmb所对应的主菜单。一般情况下,新开发的form不需要新建菜单,直接使用系统默认的菜单。除非业务功能特别多,需要菜单来实现。
<图2-4>
XXX.pls[可选]
fm_XXX.fmb处理业务逻辑所调用的package。如果业务逻辑比较复杂,建议建立一个这样的package,来实现业务逻辑,这样可以简化form的复杂度。
基于RMS form开发简单例子
先看一个简单例子,来演示一下RMS form的开发过程。
gmlsupreconfind.fmb
建立gmlsupreconfind.fmb。实现
界面
<图3-1>
<图3-2>
实现过程
1. 打开fm_temp1,将Module(FM_TEMPL)改为FM_GMLSUPRECONFIND,另存为gmlsupreconfind.fmb。
2. 新建一个data block,名称为B_MAIN,非数据库block。
3. 在B_MAIN下建立如下图界面(红线圈内)所示的非数据库Item,和ebs下步骤一样,就不多说了。这里只是演示建立block,其Canvas后面建立。(红线内的按钮先不管,后面将LOV的时候会讲到)
<图3-3>
4. 新建一个data block,名称为B_ACTION,非数据库block。
5. 在B_ACTION下建立如下界面(红线圈内)所示的非数据库Item,都是push button类型的。
<图3-4>
6. 新建一个data block,名称为B_SUPRECON,数据库block,基于表GML_INVC_STM_HEAD。其上的Item都不能insert、update、delete。因为这里只是将数据显示出来。将实现的效果如下:
<图3-5>
<图3-6>
7. 新建一个data block,名称为B_COL_HEAD,在B_COL_HEAD下建立如下界面(红线圈内)所示的非数据库Item,主要是按钮,一方面用来做上一步中建立的Item的标题,另一个用途主要是排序(点击某列上的按钮,则按该列排序,实际上是加上order by重新查询一次)。
<图3-7>
8. 建立Canvas: C_MAIN,type 为content。
9. 建立Canvas: C_SUPRECON,type为stacked。
10. 将B_MAIN上的Item放在C_MAIN上(见图3),将B_SUPPECON上的Item放在C_SUPPECON上(见图6),B_ACTION上的Item放C_MAIN上(见图4)。如
11. 建立W_MAIN,将C_MAIN和C_SUPPECON的window属性都设成W_MAIN,注意调整C_SUPPECON的位置和大小。需调整位置的大小和原因见下一步。
12. 建立:B_ACTION.PB_SEARCH(那个Search按钮)的WHEN-BUTTON-PRESSED事件:
BEGIN
if not FORM_SUCCESS then
raise FORM_TRIGGER_FAILURE;
end if;
show_View(‘C_SUPRECON’);–C_SUPRECON
P_MULTIVIEW.GO_MULTIVIEW_BLOCK(‘C_SUPRECON’);–已有的form是这样写的,但我这样写的时候会报一大堆错。记住这里实现的就是显示C_SUPRECON,代码可以自己重写
RWIDGET.TURN_OFF(‘B_main.LI_action’);
RWIDGET.TURN_ON(‘B_action.PB_back’);
RWIDGET.TURN_ON(‘B_action.PB_ok’);
RWIDGET.TURN_ON(‘B_action.PB_REFRESH’);–retail标准的函数RWID.TRUN_OFF,RWID.TRUN_OFF,实现Item的enabled属性
EXCEPTION
when FORM_TRIGGER_FAILURE then
Raise;
when OTHERS then
emessage(SQLERRM);
Raise FORM_TRIGGER_FAILURE;
END;
13. 建立:B_ACTION.PB_OK(OK按钮)的WHEN-BUTTON-PRESSED事件:
该事件实现:在C_SUPPECON界面上,点击“OK”按钮,打开fm_SUPPECON。
DECLARE
L_action VARCHAR2(10) := :B_main.LI_action;
BEGIN
Issue_Savepoint(‘CALL_FORM’);
if L_action = ‘NEW’ then
P_CALL_FORM(‘NEW’);
elsif L_action = ‘VIEW’ then
P_CALL_FORM(‘VIEW’);
elsif L_action = ‘EDIT’ then
P_CALL_FORM(‘EDIT’);
end if;
EXCEPTION
when FORM_TRIGGER_FAILURE then
Issue_Rollback(‘CALL_FORM’);
raise;
when OTHERS then
emessage(SQLERRM);
raise FORM_TRIGGER_FAILURE;
END;
14. P_CALL_FORM
PROCEDURE P_CALL_FORM(p_action varchar2) IS
L_action VARCHAR2(10) := :B_main.LI_action;
PARAM_LIST PARAMLIST;
PL_id VARCHAR2(9) := ‘gml_supreconfind’;
L_exists VARCHAR2(1) := ‘N’;
L_settlement_id GML_INVC_STM_HEAD.SETTLEMENT_ID%TYPE;
BEGIN
— destroy the parm list if still present
P_DESTROY_PARAMETER_LIST(PL_id);
PARAM_LIST := Create_Parameter_List(PL_id);
— add parameters
add_parameter(PARAM_LIST,
‘PM_MODE’,
TEXT_PARAMETER,
L_action);
add_parameter(PARAM_LIST,
‘PM_settlement_id’,
TEXT_PARAMETER,
L_settlement_id);
— call the program
open_form(‘gmlsuprecom’,
ACTIVATE,
SESSION,
PARAM_LIST);
EXCEPTION
when FORM_TRIGGER_FAILURE then
Raise;
END P_CALL_FORM;
fm_SUPPECON建立完毕。
15. 现在开始写fm_XXX.fmb,其开发过程和fm_XXX_find一样,也是从fm_temp1来。主要要建立三个习惯上的Block:
B_ACTION:和fm_XXX.fmb一样,用于放下面的一排按钮。
B_XXX:基于表的一个block。和ebs下一样。当然,可能还有主从结构的。
<图3-8>
16. B_APPLYS:类似EBS下的“Control”block,控制块。
其他的可以自由发挥,只要便于后面的人维护就好。这里,不再详细叙述。
17. me_XXX.mmb菜单我们在后面专门来叙述。
18. 如果form fm_XXX.fmb要用到package,需要在数据库中建立package。
到此,开发一个form的基本流程基本演示完毕,用户在使用的时候,会一次看到以下的画面。
<图3-1>
<图3-2>
<图3-8>
基于rms form开发常用技巧
下面看一看retail开发的一些函数和技巧,这些多是retail标准的。
命名规则
Form:Retail没有统一规则,一般以客户名简写加通俗易懂的字母/单词(比如:gmlgmlsuprecom.fmb,gml为客户名称简写)下面我们自己取名将以XXX替代。
Module:一般命名为FM_XXX(比如:FM_GMLCONSMRGN);
Block:一般命名为 B_XXX(比如:B_ACTION);
Cavans::一般命名为C_XXX(比如:C_CONSMRGN);
Window:一般命名为W_XXX(比如:W_CONSMRGN);
Item:一般是Text Item命名为TI_XXX,List Item命名为LI_XXX,Check Box命名为CB_XXX,按钮命名为PB_XXX…按此规律命名即可;只要大家一看都能明白就好。
多语言的实现
Retail的多语言,和EBS不同。EBS是在不同的语言环境放置不同语言的form程序,retail则只有一个form程序,它将需要显示给用户看的项的label,存放在数据库的特定表里,如果一个可视项有多种语言,则一个项在表里就有多条记录,当form启动的时候,在Form级WHEN-NEW-FORM-INSTANCE 触发器中,通过P_FORM_STARTUP->P_INITIALIZE 将Form中的每个Item和Item对应Label项取出来(该逻辑在fm_temp1中已经写好了),我们要做的是将这些项和它们对应的语言手工取出来,放入数据库的特定表中。
Form的多语言:
Form的语言是放在表form_elements_langs和form_elements中的。
其获取语言的sql如下:
select fe.block_name,
fe.item_name,
fe.sub_item_name,
fe.item_type,
nvl(fel.lang_label_prompt, fe.default_label_prompt) label_prompt,
nvl(fel.lang_access_key,fe.default_access_key) access_key ,fel.lang
from form_elements_langs fel,
form_elements fe
where fe.fm_name = ‘FM_ORDHEAD’/*I_form*/–by Form
and upper(fe.fm_name) = upper(fel.fm_name(+))
and upper(fe.block_name) = upper(fel.block_name(+))
and upper(fe.item_name) = upper(fel.item_name(+))
and upper(fe.sub_item_name) = upper(fel.sub_item_name(+))
and 8/*L_user_lang*/ = fel.lang(+)–by user Language
and fe.default_label_prompt is not NULL;
所以,需要向表form_elements_langs和form_elements中插入每个需要显示的项的信息。比如,在供应商维护form(SUPVWEDT)中有一个供应商代码(SUPPLIER)字段,查询这两个表
select * from form_elements where fm_name=’FM_SUPVWEDT’ and item_name=’SUPPLIER’
FM_NAME BLOCK_NAME ITEM_NAME ITEM_TYPE SUB_ITEM_NAME DEFAULT_LABEL_PROMPT DEFAULT_ACCESS_KEY BASE_IND
FM_SUPVWEDT B_SUPS SUPPLIER Text Item NONE Supplier Y
字段描述:
FM_NAME: FM_||Form文件的名字
BLOCK_NAME: 该项(SUPPLIER)所在的block名字,如没有,就写”NONE”
ITEM_NAME: 该项(SUPPLIER)名
ITEM_TYPE: 项的类型,这里是 Text Item,还有Push Button,Window,LOV等,这里一定要写正确项的类型
SUB_ITEM_NAME: 子项,目前我写的还没用到
DEFAULT_LABEL_PROMPT: 如果表form_elements_langs中没有设置语言标签,则用这个
DEFAULT_ACCESS_KEY : 快捷键
BASE_IND: 目前还没搞清是什么意思,表里设置的都是’Y’
上面这个表里,是设置form拥有的元素,要实现多语言,还需要在表form_elements_langs中插入数据。这个表结构和form_elements差不多,只不过少了字段ITEM_TYPE,多了Lang字段,Lang字段就是语言的种类。目前取值有8(中文)和1(英文)。如果要设置两种语言,则要插入2笔数据。
这样,一个form可视项的多语言就设置成功了。
Retail这样设置多语言,感觉太麻烦,每一个项都这样设置一遍,一个form的工作量很大的。
Menu的多语言:
和form差不多,它的表示menu_elements和menu_elements_langs。
注:form的多语言是指form上的提示信息,如Item标题,message,Lov标题等,不能像ebs的表fnd_lookup一样,表里的数据是多语言的。
Form初始化
Form的初始化,写在Form的WHEN-NEW-FORM-INSTANCE触发器中。调用的主要过程有:
P_FORM_STARTUP->P_INITIALIZE、P_BOILERPLATE,这几个procedure都是fm_temp1中写好的,我们不用改变。我们需要添加的是自己的逻辑。
A. 初始化List Item
初始化List Item,要调用P_POPULATE_LIST(p_item_name varchar2,p_para varchar2),这个过程(该过程原型在stand45中),比如:P_POPULATE_LIST(‘B_apply.LI_cons_rev_freq’, ‘GCRF’)中,’B_apply.LI_cons_rev_freq’指List Item的名(一定要加上block的名),’GCRF’是表code_detail里的code_type字段。基本原理是该过程先定义一个cursor:
select code,code_desc from code_detail where code_type=’GCRF’
然后把code和code_desc赋给List Item,所以,List Item的动态值都来源于code_detail,做一个新的List Item之前,需要向这个表里插入数据。当然,也可以在程序里面写死
Show错误提示
显示错误提示,使用函数emessage(p_msg varchar2),如:
emessage(‘This_is_a_err_msg’);
raise form_trigger_failure;
错误提示也是分多语言的。所有的提示信息都放在rtk_errors表中,表里有一个三个重要字段
rtk_key: 比如这里的’This_is a err_msg’
rtk_lang: 语言
rtk_text: 提示信息;
比如在这个表里存在下面两条记录,则根据不同语言环境提示rtk_text的内容:
rtk_key rtk_lang rtk_text
This_is_a_err_msg 1 This is a err msg
This_is_a_err_msg 8 这个是错误信息
如果表里没有对应的提示信息,则直接显示emessage(‘This_is_a_err_msg’)中的参数,即’This_is_a_err_msg’
注:该过程原型:
PROCEDURE EMESSAGE (I_key varchar2,
I_txt_1 varchar2 := null,
I_txt_2 varchar2 := null,
I_txt_3 varchar2 := null) IS
l_ret_val BOOLEAN;
BEGIN
L_ret_val := RMESSAGE.SHOW (‘ALT_ERROR’, I_key, I_txt_1, I_txt_2, I_txt_3);
END;
Lov设计
Retail的Lov的展现一般不会像ebs中,ebs中,一般是鼠标落在某个text item的项上,弹出某个lov,retail是在这个项的后面有一个按钮,当点击这个按钮的时候,会弹出一个lov,选择lov中的东西,再把选择的东西赋值给该item。
创建一个Lov的过程:
Step1. 建立一个Lov,指定其返回给那些项。
Step2. 在需要Lov的Text Item的后面加一个按钮项(按钮项的图标:日期按钮的图标名称 calendsm,一般Lov 按钮的图标名称 listval,备注按钮的图标名称 smaltalk)
Step3. 在按钮下写如下代码:
Go_Item(‘B_ordhead.supplier’);–先把光标移到Text Item上
Do_Key(‘List_Values’);–显示Lov
特别,创建弹出日期的Lov过程:
Step1. 打开我们自己写的form的同时,打开fm_date.fmb(这个文件是标准程式)。
Step2. 将fm_date下Object Group下的OG_DATE,拖动到自己的Object Group下,将弹出提示,这时选择“复制”。
Step3. 将calend45.pll添加进来。
Step4. 添加显示日期的text Item的when-new-item-instance事件:
另外,有一种情况也需要注意:
当需要根据不同的情况,动态显示不同的LOV。比如一个Text Item项里,但参数Para1为’学生’时,调用’学生’Lov(LOV_STUDENT),当Para1为’教师’时候,显示’教师’LOV(LOV_TEACHER)。这种情况,需要在Text Item的KEY-LISTVAL触发器中添加如下代码:
if :Para1= ‘学生’ then
if F_SHOW_LOV(LOV_STUDENT’) then
Do_Key(‘Next_Item’);
end if;
elsif :Para1= ‘教师’ then
if F_SHOW_LOV(LOV_TEACHER’) then
Do_Key(‘Next_Item’);
end if;end if;
F_SHOW_LOV函数定义在标准package stand45中。
BEGIN
Validate(Item_Scope);
if not FORM_SUCCESS then
raise FORM_TRIGGER_FAILURE;
end if;
—
if (P_CALENDAR.LP_date_ok is not NULL) then
if (P_CALENDAR.LP_date_ok = ‘Y’) then
:B_MAIN.TI_SETTLEMENT_DATE := P_CALENDAR.LP_current_date;
P_CALENDAR.LP_date_ok := NULL;
Do_Key(‘Next_Item’);
else
P_CALENDAR.LP_date_ok := NULL;
end if;
end if;
EXCEPTION
when FORM_TRIGGER_FAILURE then
raise;
when OTHERS then
emessage(SQLERRM);
raise FORM_TRIGGER_FAILURE;
END;
Step5. 添加显示日期的text Item的key-listval事件:
DECLARE
L_return_to VARCHAR(256) := ‘B_main.TI_settlement_date’;
L_min_date DATE := NULL;
L_max_date DATE := INTERNAL_VARIABLES.GP_vdate;
L_default_date DATE := INTERNAL_VARIABLES.GP_vdate;
BEGIN
Validate(Item_Scope);
if not FORM_SUCCESS then
raise FORM_TRIGGER_FAILURE;
end if;
—
P_CALENDAR.SHOW_CALENDAR(L_return_to => L_return_to,
L_default_date => L_default_date,
L_min_date => L_min_date,
L_max_date => L_max_date);
EXCEPTION
when FORM_TRIGGER_FAILURE then
raise;
when OTHERS then
emessage(SQLERRM);
raise FORM_TRIGGER_FAILURE;
END;
Step6. 在按钮WHEN-BUTTON-PRESSED事件下写如下代码:
Go_Item (‘B_main.TI_settlement_date’); — B_main.TI_settlement_date是需要得到LOV值得Text Item
Do_Key (‘List_Values’);
菜单
Retail的菜单是通过菜单文件(.mmb)来实现的。其真正的事件是代码是在form中,菜单只调用form中的代码。下面以一个例子来说明:
这个例子演示的是增加 供应商维护–〉选项 菜单中,增加“联营保底”,其form文件是supvwedt.fmb,菜单文件是supplier.mmb.。
Step 1. 新建(修改)Menu (我们以supplier.mmb来说明,这个文件已经存在,我们来修改它既可) 在菜单OPTIONS_MENU下新增一个项:
name: GML_CONS_MARGIN
lable: &Consignment Attributes–其实这里的不准,因为我们后面还会改变它的lable
Menu Item Type: Plain
Step 2. 在该Menu下写事件
Execute_Trigger(‘T_cons_margin’);–T_cons_margin是在form中定义的
Step 3. 在Form中写事件 ,注意将form的menu Module属性设置为supplier
在 supplier.mmb 中增加form级trigger
name: T_CONS_MARGIN
execution Hierarchy:Override
其他默认
事件代码:
declare
…………
begin
………. –你自己的事件就写到这里就行了。
Open_Form(‘gmlconsmrgn’,
ACTIVATE,
SESSION,
L_pl_id);
………..
exception ….
…
end;
Step 4. 编译form和Menu
(编译方法参见下一节)
编译好后,将生成的.fmx和.mmx放到bin目录下去
注意:如果没有编译菜单文件生成.mmx并放入bin目录下,则菜单虽然会挂上去,但点击所有的菜单都没有任何事件响应。
Step 5. 支持多语言
首先,执行
select * from menu_elements where menu_filename=’supplier’ for update;
可以看到,菜单选项就是放这个表里的。
字段含义:
menu_filename 菜单文件名
menu_name 菜单名
menu_item_name 菜单选项名,这里我们一般使用英文,随便取一个名字,假设我们取 GML_CONS_MARGIN
default_lable 默认提示标签,如果语言设置里没有找到对应语言的标签,就会用这个
base_ind 目前我也不知道是什么含义,把它设为’Y’
所以,在上面的表里增加一行menu_name 取OPTIONS_MENU(因为我们要挂在“选项”下),menu_item_name 取 GML_CONS_MARGIN,default_lable 取&GML_CONS_MARGIN
其次,在执行
select * from form_menu_link where menu_filename=’supplier’ for update
可以看到,这个表里存放的是menu和form的关系。因为这form和menu的关系已经存在,我们不去修改它。
再次,执行
select * from menu_elements_langs where menu_filename=’supplier’ for update
这个表就是存放多菜单语言的关系表了。它和menu_elements的字段含义差不多,只不过多了一个语言
我们增加一行,语言设置成中文(字段值为8)
最后运行下边的sql检查多语言是否设置成功
select me.menu_name,
me.menu_item_name,
nvl(mel.lang_label, me.default_label) label
from menu_elements_langs mel,
menu_elements me,
form_menu_link fml
where upper(fml.fm_name) = upper(‘fm_supvwedt’)—form名
and upper(fml.menu_filename) = upper(me.menu_filename)
and upper(me.menu_filename) = upper(mel.menu_filename(+))
and upper(me.menu_name) = upper(mel.menu_name(+))
and upper(me.menu_item_name) = upper(mel.menu_item_name(+))
and 8/*L_user_lang*/ = mel.lang(+)–语言
and mel.menu_item_name = ‘GML_CONS_MARGIN’–菜单选项
and mel.menu_name = ‘OPTIONS_MENU’;–菜单名
如果能查到一条数据,就说明菜单多语言设置已经成功了。
编译Form/Menu
根据前面建立的环境变量,Form的编译语句如下:
假如要编译trv.fmb
oracle 用户登录
source rmsformsvr.env
cd $INSTALL_DIR/rms/forms/src
cp rtv.fmb rtv.fmb.bak
frmcmp.sh userid=$UP module=rtv module_type=form
cp $INSTALL_DIR/rms/forms/bin/rtv.fmx $INSTALL_DIR/rms/forms/bin/rtv.fmx.bak
cp -f $INSTALL_DIR/rms/forms/src/rtv.fmx $INSTALL_DIR/rms/forms/bin/
注:后面两句的意思是将原来的.fmx备份,再将编译后生成的文件放到bin目录下,这样才能运行。
编译menu,比如menu名称supplier.mmb,将上面的frmcmp.sh userid=$UP module=rtv module_type=form
换成frmcmp.sh userid=$UP module=supplier module_type=menu,即改变module=menu名,module_type=menu
注:本小节资料有商云方提供 http://blog.retailsolution.cn/blog/index.php/rms-form-compile
将客户化Form挂到rms上
和Form上菜单项有关的sql语句
一级菜单(folder)
SELECT * FROM NAV_FOLDER WHERE parent_folder IS NULL
二级菜单(folder)
SELECT * FROM NAV_FOLDER WHERE parent_folder =’ORDER_RELATED’ –比如 订购
菜单项(Form)
SELECT * FROM NAV_ELEMENT where element=’cux_jeffdemo’ –Form名是 FM_cux_jeffdemo
SELECT * FROM NAV_ELEMENT_MODE WHERE folder=’ORDER_RELATED’ –Form 挂哪个Menu(folder)下
Menu和 Form对应关系
(头上显示的菜单,不是菜单项)
SELECT * FROM FORM_MENU_LINK WHERE fm_name =’FM_’||upper(‘cux_jeffdemo’) for update
Form和角色的关系
即哪个角色可以看某个Form
SELECT * from NAV_ELEMENT_MODE_ROLE where element = ‘cux_jeffdemo’
用户和角色的关系
SELECT * FROM USER_ROLE_PRIVS WHERE USERNAME = ‘RMS12DEV’
所以,这里存在两种关系:
1. User->Role->Form(一个用户可以看见哪些Form)
User
|
Role1 – Role2
|
Form1-Form2-…FormN
2. Folder->Form (一个Form挂在哪个地方)
Folder
|
Folder-Folder…
|
.
.
.
Folder-Folder…
|
Form1-Form2…
实例挂Form
下面以一个实例说明:
将form FM_cux_jeffdemo2 挂在rms系统的“订购”–>“固定交易” 菜单下
Step 1.建立菜单
向 table NAV_FOLDER 中加入一行(“订购”和“固定交易”两个菜单都有,所以不用建立)
Step 2.
向 table NAV_ELEMENT 中加入一行
ELEMENT ELEMENT_TYPE COMPONENT
cux_jeffdemo2 F RMS
–ELEMENT Form 名
–ELEMENT_TYPE 类型 F表示Form
–COMPONENT 挂在RMS下
Step 3.
向 table FORM_MENU_LINK 中加入一行
FM_NAME MENU_FILENAME
FM_CUX_JEFFDEMO2 cux_jeffdemo2
–FM_NAME ‘FM_’ + Form名
–MENU_FILENAME Form名
Step 4.
向 table NAV_ELEMENT_MODE 中加入一行
ELEMENT NAV_MODE FOLDER ELEMENT_MODE_NAME
cux_jeffdemo2 –DEFAULT– FIX_DEAL 客户化Form Demo
–ELEMENT Form名
–NAV_MODE 打开Form的状态 查看(VIEW)-编辑(EDIT)-默认(–DEFAULT–)
–FOLDER Form放哪一个菜单(folder)下
–ELEMENT_MODE_NAME 菜单上显示的 prompt
Step 5.
向 table NAV_ELEMENT_MODE _ROLE中加入一行
ELEMENT NAV_MODE FOLDER ROLE
cux_jeffdemo2 –DEFAULT– FIX_DEAL DEVELOPER
–ELEMENT Form名
–NAV_MODE 打开Form状态,需与上一步一致,否则建立不了(与上一步的table设置了外键)
–FOLDER 同上一步
–Role 该Form分给哪个Role
可以查看表 USER_ROLE_PRIVS 知道用户和Role的对应关系
至此,就可以在rms的菜单项中,看到我们增加的这个菜单了(“客户化Form Demo”)。
Retail Report开发简要说明
Retail报表采用Reports builder开发。和6i下开发EBS报表基本没有区别。使用过6i的话,很快就能开发报表。需要提到的一点就是,根据不同输入参数,报表显示不同格式。
例如下面的报表,输入不同的参数,输出不容的布局。
<图4-1> 参数一
<图4-2> 布局一
<图4-3> 参数二
<图4-4>布局二
该类型的报表开发逻辑是:根据需要,用Reports builder建立两个不同的布局,根据参数显示不同的布局。
上面的这个例子在开发的时候,建立了两个循环框。分别在两个循环框里写如下代码如下:
function R_2FormatTrigger return boolean is
begin
if :P_layout=’1′ then
return (false);
else
return (true);
end if;
end;
function R_1FormatTrigger return boolean is
begin
if :P_layout=’1′ then
return (TRUE);
else
return (false);
end if;
end;
仅需这样,就可以实现上面的效果。以前6i里面直接这样写是不行的,还的在布局里建立一个过程。
将客户化Report挂到rms上
Rms下,报表管理做得比较粗劣,下面用一个实例来了解Rms挂报表的方法。Rms报表的运行结果是一个浏览器打开的pdf格式的文件,很遗憾,不能像ebs中的并发请求那样,将结果保存在服务器上,只能保存到本地。
一个例子
生成格式文件.rep文件
利用写好的报表CUX_GouBaoRep_Demo.rdf生成CUX_GouBaoRep_Demo.rep
(6i开发rdf报表公司资料库有很多资料介绍,10 report builder建立rdf文件和6i差不多,这里不详细介绍了)
1. 将本地编译通过的CUX_GouBaoRep_Demo.rdf文件,上传到 /d01/app/retail/rms/app/rms/reports/src
2. 运行下面的命令
cd /home/oracle
su oracle
source rmsformsvr.env
cd /d01/app/retail/rms/app/rms/reports/src
rwconverter.sh source=CUX_GouBaoRep_Demo.rdf userid=$UP dest=CUX_GouBaoRep_Demo.rep stype=RDFFILE dtype=REPFILE batch=yes
上面的命令会在 /d01/app/retail/rms/app/rms/reports/src 目录下生成 CUX_GouBaoRep_Demo.rep,再将 CUX_GouBaoRep_Demo.rep 剪贴到
/d01/app/retail/rms/app/rms/reports/bin下
注意:生成的.rep文件名如果含有字母,则字母必须是全小写,否则在运行的时候会报找不到报表
在rms中设置报表可用
在后台表RTK_REPORTS添加如下记录
MODULE REPORT_NAME REPORT_DESC PARAMETER_IND PRINTFRM_IND PRINT_MODE SELECT_IND
CUX_GouBaoRep_Demo CUX_GouBaoRep_Demo CUX_GouBaoRep_Description_Demo Y Y A
table RTK_REPORTS 字段说明:
1. MODULE 应该类似于EBS中的模组,但这里随便输入都可以
2. REPORT_NAME 报表名,这个须是报表文件名,比如CUX_GouBaoRep_Demo
3. REPORT_DESC 报表描述,随便取一个用户容易看懂的
4. PARAMETER_IND 只能选择’Y’/’N’,目前还不知道作用,从意义上来看应该和参数有关,但我测试输入’Y’或’N’好像没有什么区别
5. PRINTFRM_IND 只能选择’Y’/’N’,在form上可以打印的时候是否可以选择到该报表,选择’Y’,可在rms form中选择到,否则选不到
6. PRINT_MODE 只能选择’A’/’S’,同PARAMETER_IND,目前也不确定
7. SELECT_IND 不确定,目前系统里都为空
可以看到客户化的报表已经可以运行了。
效果图见:<图1-5>-<图1-8>
新增rms用户
RMS系统用户的用户为DB用户,创建非常繁琐,而且需要后台处理,这里总结步骤如下:
1. 在数据库后台创建DB用户。
2. 对创建的用户授予角色权限、系统权限。
/*==================================================
Copyright (C) HAND Enterprise Solutions Co.,Ltd.
AllRights Reserved
==================================================*/
/*==================================================
Program Name:
CREATE RMS USER
Description:
This program create Oracle rms db user and grant all privileges .
History:
1.00 2008-11-10 Kenneth.shao Creation
2.00 2008-11-10 Kenneth.shao Modified
修改角色授权,增加选择角色功能,可以支持授权多个角色,用“,”号分割
如:developer,select_catalog_role
Version:
2.00
==================================================*/
DECLARE
l_user_name VARCHAR2(30) := '&USER_NAME' ;
l_roles VARCHAR2(400) := '&ROLE' ;
l_crt_user varchar2(300) := 'create user '|| l_user_name ||'
identified by "' || l_user_name || '"
default tablespace RETEK_DATA
temporary tablespace TEMP
profile DEFAULT
quota unlimited on lob_data
quota unlimited on retek_data
quota unlimited on retek_index';
l_grant VARCHAR2(4000) ;
BEGIN
--Create user
EXECUTE IMMEDIATE l_crt_user ;
--Grant directory
l_grant := 'grant read on directory SYS.OUTPUT_WORKING to '||l_user_name;
EXECUTE IMMEDIATE l_grant ;
l_grant := 'grant read on directory SYS.UTL_FILE_DIR to '||l_user_name||'with grant option';
EXECUTE IMMEDIATE l_grant ;
--Grant role privileges
l_grant := 'grant ' || l_roles || ' to '||l_user_name ;
EXECUTE IMMEDIATE l_grant ;
--Grant system privileges
l_grant := 'grant alter session
, analyze any
, create any context
, create any procedure
, create any synonym
, create any table
, create any type
, create database link
, create library
, create materialized view
, create procedure
, create public database link
, create public synonym
, create sequence
, create session
, create synonym
, create table
, create trigger
, create view
, debug connect session
, delete any table
, drop any procedure
, drop any synonym
, drop any table
, execute any procedure
, execute any type
, insert any table
, query rewrite
, select any sequence
, select any table
, update any table to '||l_user_name ;
EXECUTE IMMEDIATE l_grant ;
END ;
3. 为新建DB用户创建RMS12DEV(现行环境下的用户)下对象的同义词
/*==================================================
Copyright (C) HAND Enterprise Solutions Co.,Ltd.
AllRights Reserved
==================================================*/
/*==================================================
Program Name:
CREATE RMS SYNONYM
Description:
This program create all synonym for rms12dev to rms new user .
History:
1.00 2008-11-10 Kenneth.shao Creation
Version:
1.00
==================================================*/
DECLARE
l_user VARCHAR2(30) := '&USER_NAME';
BEGIN
FOR syn_r IN (SELECT DISTINCT 'create or replace synonym '|| l_user || '.' ||object_name || ' for rms12dev.' || object_name syn
FROM dba_objects
WHERE owner = 'RMS12DEV'
AND object_name NOT LIKE 'BIN$%'
AND object_type IN ('SEQUENCE'
,'PROCEDURE'
,'PACKAGE'
,'LOB'
,'LIBRARY'
,'MATERIALIZED VIEW'
,'TABLE'
,'VIEW'
,'FUNCTION'
,'TYPE')
) LOOP
BEGIN
EXECUTE IMMEDIATE syn_r.syn ;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(syn_r.syn||'-----'||SQLERRM) ;
END ;
END LOOP;
END ;
4. 在RMS系统中设置用户属性,位置:启动->控制->设置->用户属性->新建
选择语言
注:如果没有创建用户,Step4中会提示用户无效。如果没有创建同义词,则用新建的用户进系统的时候会提示对表操作没有权限。
5. 设置用户组,位置:启动->控制->系统->地点/产品安全性->用户/组链接->编辑。(设置之前必须保证组已经建立并赋予一定的权限,这里不详细讲述)。添加一行新的。
到这里为止,用户已经成功创建,只要组已经设置并赋权,用户便可以使用。
可以看出,其实RMS用户是在DB层建立的用户。
注:本小节资料有邵志国提供 http://blog.retailsolution.cn/blog/index.php/retail-rms-user-create
报表服务器的一个特殊设置
在oracleias 10g中,已经没有report server,被整合到OC4J里,所以,要启动10AS的“Report Server”,只有启动OC4J就可以了。
通过下面命令:
opmnctl status
得到如下输出:
Processes in Instance: formreportsvr.syfr12
——————-+——————–+———+———
ias-component | process-type | pid | status
——————-+——————–+———+———
DSA | DSA | N/A | Down
LogLoader | logloaderd | 11895 | Alive
HTTP_Server | HTTP_Server | 5298 | Alive
dcm-daemon | dcm-daemon | N/A | Down
WebCache | WebCache | 5305 | Alive
WebCache | WebCacheAdmin | 5294 | Alive
OC4J | home | 13643 | Alive
OC4J | OC4J_BI_Forms | 13644 | Alive
可以看到OC4J已经起来了,但在运行报表的时候如下错误:
“http 404”的错误,或者找不到网页的错误
Case
Report Server的配置问题。
Solution
1. 修改rms.env(/d01/app/oracleas/OraHome_1/forms/server/目录下),在后面追加一下内容
具体请参考(Detail请参考 /d01/app/retail/rms/12.0.1下rms-1201-ig.pdf 【Configure Oracle Application Server 10 for RMS Reports】)
/*****************************************************
ORACLE_RMS_REPORTS_HOST=http://syfr12:7778/
ORACLE_RMS_REPORTS_SERVER=rep_syfr12_oracleas1+envid=rms12arep
ORACLE_RMS_RWSERVER=reports/rwservlet
*****************************************************/
注:ORACLE_RMS_REPORTS_HOST=http://<server>:<Listen port>
ORACLE_RMS_REPORTS_SERVER is the value of the reports configuration file without the .conf plus a standard envid (“envid” must be used) and the name of the header inside the configuration file that defines the variable REPORTS_PATH.
2. 修改rep_syfr12_oracleas1.conf(/d01/app/oracleas/OraHome_1/reports/conf目录下)
在标签</server>前添加以下内容(Detail请参考metalink 559039.1)
/*******************************************************
<environment id=”rms12arep”>
<envVariable name=”REPORTS_PATH”
value=”/appl/acc/ah01/rt12/rms/reports/bin:/appl/acc/ise/oras/aoasgm02/reports/reports/
templates:/appl/acc/ise/oras/aoasgm02/reports/reports/samples/demo:/appl/acc/ise/oras/
aoasgm02/reports/integ:/appl/acc/ise/oras/aoasgm02/reports/printers”/><envVariable name=”NLS_LANG” value=”AMERICAN_AMERICA.UTF8″/>
</environment>
*******************************************************/
Open Issues
从Retail标准Form来看,新增一个Form,需在SCREEN_SQL中增加一个函数,在Form的初始化过程P_INITIALIZE中会调用SCREEN_SQ.GET_SCREENTEXT,进而调用的新增的函数。一直没弄明白该函数的作用,我自己新建的一个Form,没有做这一个动作,仍然可以运行。
Closed Issues
———————————————————————————————————
作者 黄鸿
组别 广州技术一组
2006年正式参加工作,热衷ERP事业,曾在liteonit(广州)实施SFC系统和EBS的二次开发和流程优化工作。2008年6月进入Hand,先后在温州天正协同制造项目,上海购宝零售项目做技术支持工作。对EBS制造、分销方面有开发经验,熟悉的开发工具有form/report/pl sql/discover/xml report 这篇文章是在上海购宝项目期间,做客户化开发过程中所写下的。
要学习的还有很多,同志仍需努力
关于作者:
昵称:jeff.huang 档案信息: 联系方式:你可以通过hong.huang@hand-china.com联系作者 点击查看jeff.huang发表过的所有文章... 本文永久链接: http://blog.retailsolution.cn/archives/2043 |
对本文的评价:
关于RMS的用户安全性,上文已经把如何创建一个新用户讲完了;那么如何控制用户的的功能安全性和数据安全性呢? 请参考:merch-131-impg.pdf 在章节:RMS Users and Security 有详细描述;大概的意思:功能安全性通过插表的方式,数据安全性通过界面设置。
可否再更新一下这个开发文档,或者 你那里有更新的版本么?或者有标准的开发官方文档吗,有的话发我邮箱一份,谢谢!