9.4~9.16学习情况总结
个人理解MVG就是1:M或者M:M的关系模型,MVG的配置就将这种关系可视化、数据化。
在父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对订单行进行编辑,因此我们把这些勾全
勾上。
4. 创建一个Calculate字段:注意Type属性更改(默认是Text属性),使用Count函数对MVF进行统计,例子中有多少序列号就有多少MVF记录数,统计记录数就是统计订单行数量。注意, Count函数的参数一定是一个MVL的名字。
创建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:业务上区分主要非主要、提升系统性能。
创建MVG 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
Position安全性是应用最广泛的一种安全性,因为它有两个最重要的特性是
Person安全性所不具备的:
继承性: 职位的数据可以被继承,如果一个用户离职或者换岗,那么新
顶替他的岗位用户可以继承该岗位下所有的历史数据,实现人走岗留,
使业务数据独立于人(用户)而存在。
层级性: 上级职位可以通过配置实现对他所有下级职位的数据的访问。
由于这两个特性与大多实际业务非常吻合,因此Position安全性是一种使用
很广泛,也很重要的安全性类型。
我们有标准的职位BC:Position,首先基于Position以及他的Table:S_PARTY,做一个订单头上关于职位的MVG
在管理—应用程序中注册视图,将Manager和Sales视图分配给相应的经理账号,Sales分配给相应的销售人员账号
用户、职责、职位的关系如下:
View与职责的关系是在环境中手动配置关联的,将下属的View手动添加到父职位中
EBC指的是通过sql视图,来获取单表或者多表数据的BC,不基于任何实体表,不能直接进行新建、删除、修改操作。
创建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;
至此BC已可以正常使用,配置相应的Applet,View,Screen,BO即可,可以作数据展示或者数据处理结果展示等。
Import Object的作用就是通过CSV文件导入记录
需求:新建完订单后,我们实现一个提交按钮。该按钮只有在订单状态为“新建”时可点击。
点击新建按钮后,判断是否存在订单行。如果没有订单行,那么报错提示录入订单行。
如果验证通过,则把订单头的状态改为“已提交”,并且把所有订单行的价格改为“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表示对应的独立源代码
在Business Service Method下添加方法:SubmitOrder,Display Name: 提交订单。
选择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里添加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;
}
}
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);
}
在订单行的删除按钮上新增多选删除特性,在不改变按钮原有功能和外观的情况下,可以实现一次删除所有选中的记录。
实现过程以及代码略。
实现在订单头界面一键删除所有订单行,并且修改订单状态为未提交。
实现过程以及代码略。
同步订单头的产品名称和Id到对应订单行,产品种类是PIcklist字段,自动为所有订单行Pick订单头的产品。
实现过程以及代码略。
实现MVG字段的添加,以及设置主键的功能。
实现过程以及代码略。
实现MVG字段删除关系的功能。
实现过程以及代码略。
用BC Field的Calculate字段调用BS方法,实现不用配置UP也能实现计算的功能,既节约了性能成本又可以实现代码复用。
数据库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");
}
function BusComp_PreGetFieldValue (FieldName, &FieldValue)
{
if(FieldName == "Position Name"){
//调用自定义方法,然后一定要return cancel
allPos(FieldName, FieldValue);
return (CancelOperation);
}
return (ContinueOperation);
}
bcMVG.SetFieldValue("SSA Primary Field", "Y");
bcMVG.WriteRecord();
DEBUG是开发过程中至关重要的一步,但是TOOLS的编码和DEBUG工具并不算好用,所以在学习过程中浪费了一些时间。不过好在有经验之后,大体上也能定位到问题在哪里,所以总体体验下来,SIEBEL开发是需要一定经验积累的。一是比较小众,网上资料和帖子比较少,很难上网找到解决方案;二是开发工具需要一个熟悉的过程,对于它的特性以及BUG等都需要摸索一下。
多做,多做,多做。做过的东西当时是会了,但是过一段时间非常容易遗忘,特别是SIEBEL配置,过程细节比较多,不同功能配置都各不相同,漏了一个环节就直接跑不起来,报错信息也非常不好定位问题所在。所以还是要多实战多做几遍,也能发现一些细节问题,记录也是必要的,遗忘可以快速捡起来。
刚学eScript的时候,遇到什么问题都想看看有没有对应的API能用,就去查开发手册,结果未必能找到对应的API。后来发现通过配置就很简单能够实现这个功能。有时候目光不能太局限,SIEBEL是一个界面化的二次开发平台,要结合它的优点去做开发,配置和代码,哪个方便稳定性能好,就用哪个。我们的最终目的是实现功能,不管是通过什么方法。