文章目录
前言
9.4~9.16学习情况总结
学习任务
- MVG的配置与学习
- View Mode和Visible的配置
- SQL基础略看
- EBC和VBC的学习
- Import Object学习
- eScript入门、编码训练
一、MVG的配置与学习
个人理解MVG就是1:M或者M:M的关系模型,MVG的配置就将这种关系可视化、数据化。
1. 1:M的MVG配置,以统计订单行数量功能为例
-
创建Link:Cascade Delete级联删除填写Delete,避免出现垃圾数据,父子BC填写对应字段即可
-
在父BC中创建MVL如下,DestinationBC选择子BC名,Link选刚才的Link即可。
Auto Primary&Primary ID Field: 对于没有Primary概念的子BC(即订单行
中,没有一个订单行是主要的) , 我们把该属性置为None, Primary ID
Field留空。
No Associate/Copy/Delete/Insert/Update: 配置订单行BC是为了统计订
单行的数据,而不能直接通过MVG对订单行进行编辑,因此我们把这些勾全
勾上。
- 创建MVF:如我们需要统计订单行订购的总量, 那么我们可以把订单行上的Quantity字段作为订单头的MVF。一个订单头上有多少条订单行,这个MVF就有多少个值。MVF也是FIeld的一种。
4. 创建一个Calculate字段:注意Type属性更改(默认是Text属性),使用Count函数对MVF进行统计,例子中有多少序列号就有多少MVF记录数,统计记录数就是统计订单行数量。注意, Count函数的参数一定是一个MVL的名字。
1. M:M的MVG配置
-
创建中间表,新建两个外键,分别指向父子BC
-
创建Link,与1:M区别较大,有中间表做级联了,所以Source Field和Destination Field不填
重要属性包括:
Inter Table:中间表。
Inter Parent Column: 中间表中父BC的表的外键。
Inter Child Column:中间表中子BC的表的外键。
Primary ID Field: 父BC中用于标识主要的字段。
Cascade Delete: 再次强调, 除非需求很特殊,否则M:M的Link一定要选
None!
Primary Id Field = Child Id:业务上区分主要非主要、提升系统性能。
-
创建MVF
-
创建MVG Applet
- 运行向导,BC字段填写子BC
- Pick Applet类型。选择Edit List模式,模板选择Popup List Mvg
- 选择需要的字段(MVF中创建的字段),其中SSA是主要字段,隐藏Field
- 调整布局
- 配置确定按钮:
在Control下新建一条记录,设置以下属性:
Name: CloseApplet
Caption – String Reference: SBL_OK-1004224735-0D4
HTML Type: MiniButton
Method Invoke: CloseApplet
并拖拽至界面。 - 设置Applet属性:为了让MVG Applet能够进行关联数据和删除关联,以及更改Primary记录等操作,保持No Insert\Delete\Update属性为N,勾上No Merge属性
- 新建BC FIeld ,将MVF的Column添加进去(此处是Name)
- 将FIeld添加Column至Applet,并选择List Column中的MVG Applet属性
- 创建Associate Applet
-
复制MVG Applet,把No Delete\Insert\Update\Merge属性勾上,把Type属性改为Association List。
-
修改模板,Applet Web Template下,把Associate Applet的Web Template属性改为 Popup List Assoc。
-
删除SSA Primary Field和确定按钮,Associate Applet上无Primary的概念,因此我们把该字段删除。并把确定按钮删除。
-
在MVG Applet上配置Associate Applet
二、View Mode & Visibility配置
1. Personal安全性
- 在需要做安全性的BC对象中,在BuscompView Mode下创建记录如下:
- 修改View的Applet Visibility Type=Personal
- View Web Template Items上的NGC Order Header List Applet的Applet Visibility Type设为Personal。
2. Position安全性
Position安全性是应用最广泛的一种安全性,因为它有两个最重要的特性是
Person安全性所不具备的:
继承性: 职位的数据可以被继承,如果一个用户离职或者换岗,那么新
顶替他的岗位用户可以继承该岗位下所有的历史数据,实现人走岗留,
使业务数据独立于人(用户)而存在。
层级性: 上级职位可以通过配置实现对他所有下级职位的数据的访问。
由于这两个特性与大多实际业务非常吻合,因此Position安全性是一种使用
很广泛,也很重要的安全性类型。
我们有标准的职位BC:Position,首先基于Position以及他的Table:S_PARTY,做一个订单头上关于职位的MVG
-
基于父表CX_ORDER_DWJ和子表S_PARTY建立中间表CX_POSITION_CON,创建外键字段
-
创建Link,填写父子BC以及中间表(不必填Source和Destination Field)
-
将Position加进BO,添加Link关系
-
创建MVLink,Destination BC写子BC
-
创建MVF,选择自己需要的字段
-
创建或者直接使用MVG Applet
-
添加MVF到订单头Applet的Column,并绑定MVG Applet
-
在父BC中配置View Mode,选择相应的MVLink
-
配置View的Visibility Applet Type属性,分别设置成Sales Rep和Manager
-
配置View Web Template Items分别设置Applet Visibility Type为对应的属性
-
在Screen中上挂上刚刚创建的View
-
创建职位:开发环境中,管理—组—职位
-
创建职责:开发环境中,管理—应用程序—职责
-
分配职责、职位:开发环境中,管理—用户—员工
-
在管理—应用程序中注册视图,将Manager和Sales视图分配给相应的经理账号,Sales分配给相应的销售人员账号
用户、职责、职位的关系如下:
View与职责的关系是在环境中手动配置关联的,将下属的View手动添加到父职位中
三、EBC和VBC的创建
EBC指的是通过sql视图,来获取单表或者多表数据的BC,不基于任何实体表,不能直接进行新建、删除、修改操作。
1. EBC的创建
- 根据需求创建View,并给View赋权
创建View,保存为.sql文件备用
CREATE VIEW detail AS
SELECT d.product_id,d.product_price,d.order_deadline,h.order_type,h.order_comments,d.order_comment
FROM CX_ORDER_DWJ h , CX_ORDER_ITEM d
WHERE d.parent_order_id = h.row_id
给View赋权
GRANT SELECT ON detail TO SSE_ROLE;
- 用向导创建External Table,如下图选择
-
表里面需要有一个可以作为id的唯一列,将唯一列的system field mapping设置为Id,如下图所示
-
新建BC,选择刚才建的Table字段即可
-
至此BC已可以正常使用,配置相应的Applet,View,Screen,BO即可,可以作数据展示或者数据处理结果展示等。
2. VBC的创建
- 不要用向导,直接在bc界面New。不多做说明。
- 在BC下面新建Field,不要填Column,PickList等可以正常进行配置。
- 正常新建Applet,注意所有字段都要要勾上runtime
四、Import Object配置
Import Object的作用就是通过CSV文件导入记录
-
把Import Object对象从Object Explorer中调出来,并创建一个Import Object
对象,BC填写你需要导入的BC
-
在Import Field中选择你可能需要导入的Field
-
创建导入源(建两个只是为了测试,一般用一个就行)
-
定义导入源的Field,即需要导入的字段
-
定义Field对应的CSV文件的列名
-
制作导入文件, 本次是用的导出文件的格式作的导入文件
-
导入设置如下
五、eScript入门
提交订单功能的实现
需求:新建完订单后,我们实现一个提交按钮。该按钮只有在订单状态为“新建”时可点击。
点击新建按钮后,判断是否存在订单行。如果没有订单行,那么报错提示录入订单行。
如果验证通过,则把订单头的状态改为“已提交”,并且把所有订单行的价格改为“999”。
-
在需要添加按钮的Applet上添加UP字段如下,注意冒号与“SubmitOrder”间需要一个空格,“ SubmitOrder”应该与 Control 的 Method Invoked 属性值对应。
注意: Value 的值控制按钮可用条件,为真(TRUE) 时按钮可用,为假时(FALSE)按钮不可用。这里可以设置相应的条件来控制按钮是否可用,例如这个 Applet 所在的 BC 有一个字段“Status”,是值列表字段,其中有一个值是“导入”,当将 Value 设
置成: [Status]=”导入” 表示在 Applet 上选中某一条记录,当这条记录的“Status”字段值不是“导入”时,按钮将不
可用,当“Status”字段值是“导入”时,按钮可用。
提示: 这里仅是方便说明问题,所以硬编码写了“导入”两个汉字,实际应用中应当用 LookupValue 形式,如下:
[Status]=LookupValue(“STATUS_TYPE”,”import”)
NGC_ORDER_TYPE 表示这个静态值列表的类型
Incomplete表示对应的独立源代码 -
添加Control,HTML Type与UP设置的值对应,然后把按钮添加到Layout上
-
创建BS,External Use: Y(能被代码或者工作流调用,一般建立的 BS 都应该勾上),
-
在Business Service Method下添加方法:SubmitOrder,Display Name: 提交订单。
-
在SubmitOrder方法下添加输入输出参数:OrderId
-
选择NGC Order Header Service,Edit Server Script,在function Service_PreInvokeMethod (MethodName, Inputs, Outputs)添加以下代码:
function Service_PreInvokeMethod (MethodName, Inputs, Outputs)
{
//为了保证代码的健壮性,function开始前应使用try{}catch(e){}finally{}来捕获并处理异常。
try
{
//每个客户化BS开头定义事务,以确保数据的完整性
//定义事务控制
var psTranIn = TheApplication().NewPropertySet();
var psTranOut = TheApplication().NewPropertySet();
var bsTran = TheApplication().GetService("EAI Transaction Service");
//开始事务
bsTran.InvokeMethod("BeginTransaction", psTranIn, psTranOut);
if (MethodName == "SubmitOrder")
{
SubmitOrder(Inputs, Outputs);
}
//如果逻辑正确结束,提交事务
//正常结束,提交事务
psTranIn.SetProperty("Abort", "false");
bsTran.InvokeMethod("EndTransaction", psTranIn, psTranOut);
}
catch(e)
{
//如果捕获到异常,回滚事务
//捕获异常,回滚事务
psTranIn.SetProperty("Abort", "true");
bsTran.InvokeMethod("EndTransaction", psTranIn, psTranOut);
TheApplication().RaiseErrorText(e.message);
}
finally
{
//程序结束时,必须释放所有新创建的对象型变量
//释放对象
psTranIn = null;
psTranOut = null;
bsTran = null;
}
//所有客户化的BS都要返回CancelOperaton
return (CancelOperation);
}
- 我们把提交订单的逻辑封装在BS的一个SubmitOrder的function里。在
BS里添加function:展开general,在declarations里添加以下代码:
//提交订单的逻辑
//输入参数:OrderId,需要提交的订单的Id
function SubmitOrder(Inputs, Outputs)
{
try
{
//获取订单BO,BC
var boOrder = TheApplication().GetBusObject("HAND Order");
var bcOrder = boOrder.GetBusComp("HAND Order");
var bcItem = boOrder.GetBusComp("HAND Order Item");
//获取参数
//GetProperty()是Property的一个最重要的方法之一,它的作用是获取 Property Set里的某个键的值。我们这里是获取输入参数OrderId的值。
var orderId = Inputs.GetProperty("OrderId");
//根据输入参数OrderId查询出需要提交的订单
with (bcOrder)
{
//从下面一句开始到ExecuteQuery,是最常用的脚本BC查询
操作步骤
//激活字段,查询前要激活所有你需要获取或者设置值的字段
ActivateField("Order Status");
//设置安全性,结合
安全性的知识理解。代码中新开的BC实例一般用AllView安全性,
确保能查到数据。但是界面上的实例不可更改安全性
SetViewMode(AllView);
//清空查询条件
ClearToQuery();
//设置查询条件,这里有两种设置查询条件的方法,SetSearchSpec和SetSearchExpr,我们建议使用SetSearchExpr,具体原因后续再分析
//SetSearchSpec("Id", orderId);
SetSearchExpr("[Id] = '" + orderId + "'");
//执行查询
ExecuteQuery(ForwardOnly);
//查询完成后,通过FirstRecord定位到查询结果的第一条记录,如果查询不到任何记录,则FirstRecord方法会返回false
if (!FirstRecord())
throw "找不到订单Id " + orderId + " !";
}
//查询订单下的订单行
with (bcItem)
{
ActivateField("Item Status");
//这里可以不需要对订单行设置头Id的查询条件,因为在获取BC时,订单行被BO里的Link限制为订单头的子,该条件会自动附加
ClearToQuery();
ExecuteQuery(ForwardOnly);
var bLineFound = FirstRecord();
//验证如果不存在至少一条订单行,报错
if (!bLineFound)
throw "请先录入订单行再提交!";
//以下是最典型的通过while循环查询出的记录做操作的例子
//循环所有订单行,更新订单行状态为待发运
var TO_BE_SHIPPED =
TheApplication().InvokeMethod("LookupValue","HAND_ORD_ITEM_STATUS", "To Be Shipped");
while (bLineFound)
{
//SetFieldVaue是设置BC Field的值的方法
SetFieldValue("Item Status", TO_BE_SHIPPED);
//WriteRecord是保存记录的方法
WriteRecord();
//NextRecord是把记录定位到查询结果的下一条记录,如果已经是最后一条(即不存在下一条),返回false,跳出while循环
bLineFound = NextRecord();
}
}
//更新为订单行后,更新订单头状态
with (bcOrder)
{
var PENDING =
TheApplication().InvokeMethod("LookupValue", "HAND_ORD_STATUS", "Pending");
SetFieldValue("Order Status", PENDING);
WriteRecord();
}
}
catch(e)
{
//捕获异常,报错
throw e;
}
finally
{
//释放对象变量
bcItem = null;
bcOrder = null;
boOrder = null;
}
}
- BS的调用方法1:同样在NGC Order Header List Applet的里修改脚本PreInvokeMethod,如下:
function WebApplet_PreInvokeMethod (MethodName)
{
//提交订单
if (MethodName == "SubmitOrder")
{
try
{
//获取Applet对应的BC实例
var bcOrder = this.BusComp();
//调用前先做一次界面的保存
bcOrder.WriteRecord();
//创建BS的输入参数和输出参数的Property Set
var psIn = TheApplication().NewPropertySet();
var psOut = TheApplication().NewPropertySet();
//设置输入参数OrderId的值为当前光标选中的订单头Id
psIn.SetProperty("OrderId", bcOrder.GetFieldValue("Id"));
//获取BS
var bsOrdSvc = TheApplication().GetService("NGC Order Header Service");
//调用BS的方法SubmitOrder
bsOrdSvc.InvokeMethod("SubmitOrder", psIn, psOut);
//注意,SNTO Order BC的Class必须是CSSBCBase或者其子类,否则不支持RefreshRecord方法
//此处为什么要对记录做刷新呢?因为BS里更新数据时基于一个全新的实例去更新的,
//并不影响当前界面的实例。为了使当前界面的实例能反馈给用户处理结果(订单状态字段改为“已提交”)
//因此需要刷新当前界面的实例
bcOrder.InvokeMethod("RefreshRecord");
//客户化方法,返回CancelOperation
return (CancelOperation);
}
catch(e)
{
TheApplication().RaiseErrorText(e.message);
}
finally
{
//释放对象
psIn = null;
psOut = null;
bsOrdSvc = null;
bcOrder = null;
}
}
//Hello World方法
else if (MethodName == "HelloWorld")
{
TheApplication().RaiseErrorText("Hello World");
return (CancelOperation);
}
return (ContinueOperation);
}
- BS的调用方法2:配置三个UP,Named Method
多选删除按钮的实现
在订单行的删除按钮上新增多选删除特性,在不改变按钮原有功能和外观的情况下,可以实现一次删除所有选中的记录。
实现过程以及代码略。
撤销按钮的实现
实现在订单头界面一键删除所有订单行,并且修改订单状态为未提交。
实现过程以及代码略。
同步按钮的实现
同步订单头的产品名称和Id到对应订单行,产品种类是PIcklist字段,自动为所有订单行Pick订单头的产品。
实现过程以及代码略。
MVG添加按钮的实现
实现MVG字段的添加,以及设置主键的功能。
实现过程以及代码略。
MVG删除按钮的实现
实现MVG字段删除关系的功能。
实现过程以及代码略。
代码实现计算字段
用BC Field的Calculate字段调用BS方法,实现不用配置UP也能实现计算的功能,既节约了性能成本又可以实现代码复用。
遇到问题及其解决方案
-
VIew 与Applet做关联,然后编辑布局,将添加的Applet拖进去
-
数据库sql语句报错
创建中间表时,默认字段会带空格,无法使用,需要修改。
检查Table是否正确check in和apply到服务器,修改前check out,修改后apply。 -
导入数据时显示文件无法使用或格式不匹配,无法找到与siebel的字段映射
检查Import Field字段和Applet上显示的字段是否一致
先导出,使用导出的文件格式填写数据再导入 -
VBC无法正常显示
注意自定义字段不要与系统字段重名 -
Import Object无法正常导入
文件格式问题,可以导出之后清空数据添加自己的数据
字段命名问题,字段一般要和BC上显示的名称一致,便于映射 -
eScript无法正常执行
检查使用的字段是否激活
检查属性名称是否对应 -
动态PickList只读,能看不能选
检查字段对应问题,一般要用唯一字段ID -
MVG按钮代码无法设置PRIMARY KEY
View Mode安全性问题,取消安全性设置即可 -
MVG按钮代码,新建记录可以指定PRIMARY KEY,原有记录的PRIMARY KEY无法被修改
级联删除的bug问题,实体字段没有被删除以及覆盖,获取管理员权限,手动设置字段为空。 -
BC Script 无限循环
BC的代码不能写this,否则会造成死循环
可以这样写:
SetSearchExpr("[Id] = '"+this.GetFieldValue("Id")+"'");
ExecuteQuery(ForwardOnly);
for ( var j = FirstRecord(); j; j = NextRecord() ){
var bcOrderMVG = GetMVGBusComp("Position Name");
}
- BC Script 引用的值无法正常显示到界面,代码执行正常但无法生效
return (CancelOperation);要写在标准方法里,否则自己的方法还会被覆盖。
function BusComp_PreGetFieldValue (FieldName, &FieldValue)
{
if(FieldName == "Position Name"){
//调用自定义方法,然后一定要return cancel
allPos(FieldName, FieldValue);
return (CancelOperation);
}
return (ContinueOperation);
}
- 写代码的时候注意写入数据要保存BC,否则无效
bcMVG.SetFieldValue("SSA Primary Field", "Y");
bcMVG.WriteRecord();
- DEBUG的时候经常不能在断点停顿
一般是由于程序走不到断点处,检查判断逻辑和流程。
系统bug,解决办法是关闭浏览器,关闭所有编程窗口重新编译,然后重新打断点 - 在浏览器debug的一个方法记录
- 开发环境选择 管理-业务服务(B) ——模拟器
- 服务名称选择自己的BS名,再选择要测试的方法
- 输入参数要与**Inputs.GetProperty(“OrderId”);**内容一致,点击物业名称进行配置
- 点击运行即可测试
心得总结
DEBUG是开发过程中至关重要的一步,但是TOOLS的编码和DEBUG工具并不算好用,所以在学习过程中浪费了一些时间。不过好在有经验之后,大体上也能定位到问题在哪里,所以总体体验下来,SIEBEL开发是需要一定经验积累的。一是比较小众,网上资料和帖子比较少,很难上网找到解决方案;二是开发工具需要一个熟悉的过程,对于它的特性以及BUG等都需要摸索一下。
多做,多做,多做。做过的东西当时是会了,但是过一段时间非常容易遗忘,特别是SIEBEL配置,过程细节比较多,不同功能配置都各不相同,漏了一个环节就直接跑不起来,报错信息也非常不好定位问题所在。所以还是要多实战多做几遍,也能发现一些细节问题,记录也是必要的,遗忘可以快速捡起来。
刚学eScript的时候,遇到什么问题都想看看有没有对应的API能用,就去查开发手册,结果未必能找到对应的API。后来发现通过配置就很简单能够实现这个功能。有时候目光不能太局限,SIEBEL是一个界面化的二次开发平台,要结合它的优点去做开发,配置和代码,哪个方便稳定性能好,就用哪个。我们的最终目的是实现功能,不管是通过什么方法。