v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);}
<!--[if !supportLists]-->
原来的BOTP自定义公式解决方案只介绍了绑定目标实体的BOTP公式开发方法,现已加入面向所有实体(单据)的BOTP公式开发方法,应用场景如,要做一个根据BosType和ID获取值对象的公式,该公式很明显就是对所有实体通用的。开发方案详见下面正文,原解决方案见附件V1.0部分。
====================正文=============================1. <!--[endif]-->需求背景
运行时BOTP公式平台提供的函数较少,如果要实现比较复杂或个性化的逻辑,譬如通过物料编码从其他单据取出对应数量,运行时的公式平台就显得捉襟见肘了。对于客户化开发的单据,还可以通过写硬代码实现。但是对于标准产品的单据,如果反编译代码,则升级时相当麻烦。而自定义公式方案正是为了解决此难题而生。
<!--[if !supportLists]-->2. <!--[endif]-->实现原理
BOS框架利用Java的反射技术,通过创建自定义公式类(class实现指定的接口)实现公式逻辑,并在系统配置文件(xml)指定自定义公式类的全名,系统在运行时即会加载该类内容,从而实现BOTP公式自定义并且不会受升级影响。
<!--[if !supportLists]-->3. <!--[endif]-->环境说明
本方案在EAS60SP1上测试验证过,其他版本可参考。
<!--[if !supportLists]-->4. <!--[endif]-->实现方法
BOTP自定义公式目前分为两种:
<!--[if !supportLists]-->1. <!--[endif]-->绑定目标实体(目标单据)的自定义公式;
<!--[if !supportLists]-->2. <!--[endif]-->面向所有实体(所有单据)的自定义公式;
<!--[if !supportLists]-->4.1. <!--[endif]-->绑定目标实体的自定义公式实现方法
通过该方法创建的自定义公式,只能被指定目标单据的BOTP规则引用。
<!--[if !supportLists]-->1. <!--[endif]-->在BOS创建一个类,实现接口com.kingdee.bos.service.formula.api.IFormulaFunctions。本文以com.kingdee.eas.custom.gz.utils.botp.formula. BotpCustomFormula为例。可运行的示例代码可以参考本文的4.3章节;
<!--[if !supportLists]-->2. <!--[endif]-->将目标实体(目标单据对应的实体entity)的扩展属性billFormulaClass设置为第1步创建的类的全名(包名+类名)com.kingdee.eas.custom.gz.utils.botp.formula. BotpCustomFormula,如下图:
<!--[if !supportLists]-->3. <!--[endif]-->测试运行,打开目标单据是第2步指定的单据的BOTP单据转换规则,进入公式平台(脚本编辑),通过“函数”页签可以看到我们刚增加的自定义公式。如下图:
<!--[if !supportLists]-->4. <!--[endif]-->测试实际的单据转换。
至此,绑定目标实体的自定义公式的开发全过程已完成。
<!--[if !supportLists]-->4.2. <!--[endif]-->面向所有实体(所有单据)的自定义公式
通过该方法创建的自定义公式,可以被任何单据的BOTP规则引用。
<!--[if !supportLists]-->1. <!--[endif]-->在BOS创建一个类,继承标准产品的类com.kingdee.eas.base.dap.util. ExtendFormulaFunctions。可运行的示例代码可以参考本文的4.3章节;
<!--[if !supportLists]-->2. <!--[endif]-->修改客户端和服务端的ServiceProviderImpl.xml文件,该文件在BOS和Server的路径分别为:
BOS:
%Solution%\runtime\client\deploy\client\ServiceProviderImpl.xml
%Solution%\runtime\server\properties\ServiceProviderImpl.xml
EAS Server:
server\deploy\fileserver.ear\easWebClient\deploy\client\ServiceProviderImpl.xml
server\properties\ServiceProviderImpl.xml
用xml编辑器打开该文件,查找关键字“BOTP_EXTENDFUNCIONS”,将蓝色部分改成第1步创建的类名:
<!-- BOTP扩展公用函数-->
<configitem name="BOTP_EXTENDFUNCIONS">
<attribute key="providerName" value="BotpCustomFormula"/>
<attribute key="providerClassName" value="com.kingdee.eas.custom.gz.utils.botp.formula.BotpCustomFormula"/>
</configitem>
<!--[if !supportLists]-->3. <!--[endif]-->测试运行,进入BOTP规则的公式平台(脚本编辑),通过“函数”页签可以看到我们刚增加的自定义公式。
<!--[if !supportLists]-->4. <!--[endif]-->测试实际的单据转换。
<!--[if !supportLists]-->5. <!--[endif]-->至此,面向所有实体单据的自定义公式的开发全过程已完成。
<!--[if !supportLists]-->4.3. <!--[endif]-->自定义公式类代码
自定义公式类的可运行示例代码如下:
package com.kingdee.eas.custom.gz.utils.botp.formula;
import java.util.List;
import java.util.Vector;
import com.kingdee.bos.BOSException;
import com.kingdee.bos.ContextUtils;
import com.kingdee.bos.framework.DynamicObjectFactory;
import com.kingdee.bos.kscript.KScriptException;
import com.kingdee.bos.service.formula.api.IFormulaFunctions;
import com.kingdee.bos.util.BOSObjectType;
import com.kingdee.eas.base.dap.util.ExtendFormulaFunctions;
/**
* @author House
* @email yyh2001@gmail.com
* @createdate 2010-5-31
* @lastupdate 2010-5-31
*/
public class BotpCustomFormula extends ExtendFormulaFunctions {
//用于存放公式的变量
private static Vector funcInfos;
public static final String __BOTgetOVByBosType = "__BOTgetOVByBosType";
static
{
//加载类时定义自定义公式
funcInfos = new Vector();
//FuncInfo()的第一个参数是公式名,第二个参数是公式所在的分类(可随便取名),第三个参数是公式的描述信息(在BOTP公式平台显示)
funcInfos.add(new FuncInfo(__BOTgetOVByBosType, "自定义公式", "通过业务单元的BosType和编码获取对应的值对象(F7)\n__BOTgetOVByBosType(String number, String bosType),返回值为IObjectValue。 \nDeveloped by GZ Kingdee Development Dept.."));
}
/**
* 运行时BOTP公式平台加载自定义公式时,就是从该返回值里取公式的名称的
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
* @return 所有自定义公式的名称
*/
public String[] getAllFuncNames()
{
String as[] = new String[funcInfos.size()];
for(int i = 0; i < funcInfos.size(); i++)
as[i] = ((FuncInfo)funcInfos.get(i)).funcName;
return as;
}
/**
* 运行时BOTP公式平台加载自定义公式时,就是从该返回值里取公式的分类的(看运行时的界面就很好理解)
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
* @param s
* @return
*/
public String getFuncCategory(String s)
{
if(s == null)
return null;
for(int i = 0; i < funcInfos.size(); i++)
if(s.equals(((FuncInfo)funcInfos.get(i)).funcName))
return ((FuncInfo)funcInfos.get(i)).funcCategory;
return null;
}
/**
* 获取公式的描述
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
* @param s
* @return
*/
public String getFuncDesc(String s)
{
if(s == null)
return null;
for(int i = 0; i < funcInfos.size(); i++)
if(s.equals(((FuncInfo)funcInfos.get(i)).funcName))
return ((FuncInfo)funcInfos.get(i)).funcDesc;
return null;
}
public boolean existFunction(String s)
{
if(s == null)
return false;
for(int i = 0; i < funcInfos.size(); i++)
if(s.equals(((FuncInfo)funcInfos.get(i)).funcName))
return true;
return false;
}
/**
* 运行公式时调用此方法,公式的具体逻辑就写在这里
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
* @param func 公式名称
* @param paramList 公式的参数
* @return 公式结果
* @throws KScriptException
*/
public Object evalFunction(String func, List paramList) throws KScriptException
{
//判断用户是选了哪个公式
if(func != null && func.equals(__BOTgetOVByBosType))
{
//这里可以写满足各式各样需求的代码
if(paramList != null && paramList.size() == 3)
{
String number = (String) paramList.get(1);
String bosType = (String) paramList.get(2);
try
{
return DynamicObjectFactory.getLocalInstance(ContextUtils.getContextFromSession()).getValue(BOSObjectType.create(bosType), "where number='" + number + "'");
}
catch (BOSException e)
{
e.printStackTrace();
}
}
else
return null;
}
return null;
}
}
class FuncInfo {
public void setFuncName(String name)
{
funcName = name;
}
public void setFuncDesc(String desc)
{
funcDesc = desc;
}
public void setFuncCatetory(String category)
{
funcCategory = category;
}
String funcName;
String funcCategory;
String funcDesc;
public FuncInfo(String name, String category, String desc)
{
super();
funcName = name;
funcCategory = category;
funcDesc = desc;
}
public FuncInfo()
{
super();
}
}
<!--[if !supportLists]-->5. <!--[endif]-->鸣谢
感谢开发部兄弟们的支持!
===================V1.0============================
(以下内容引用自杨毅浩的“BOTP自定义公式解决方案.pdf”,加以补充完善)
一、业务场景:
运行时BOTP的公式平台提供的函数较少,如果要实现比较复杂的逻辑,譬如通过物料编码从其他单据取出对应数量,运行时的公式平台就显得捉襟见肘了。对于客户开发的单据,可以通过写代码实现。但是对于标准产品的单据,如果反编译代码,则升级时相当麻烦。而自定义公式方案正是为了解决此难题而生。它可以通过写代码在BOTP公式平台挂上自定义的公式,公式内容由程序员自由决定,而不会受升级影响。
注意:只适用于EASBOS530或以上版本
二、原理:(业务人员只需重点关注步骤3、4、5)
1.在BOS自定义一个类,实现com.kingdee.bos.service.formula.api.IFormulaFunctions接口。(该类必须位于com.kingdee.bos.service.formula.api此包下,因为要引用此包里的非公共类)
类的定义参考如下:
package com.kingdee.bos.service.formula.api;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import com.kingdee.bos.kscript.KScriptException;
/**
* BOTP自定义公式demo
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
*/
public class CustomBTPFunctionDemo implements IFormulaFunctions
{
//用于存放公式的变量
private static Vector funcInfos;
static
{
//加载类时定义自定义公式
funcInfos = new Vector();
//FuncInfo()的第一个参数是公式名,第二个参数是公式所在的分类(可随便取名),第三个参数是公式的描述信息(在BOTP公式平台显示)
funcInfos.add(new FuncInfo("helloLiuXun", "HelloWorld", "Wish the
Telecom project would complete soon."));
}
/**
* 运行时BOTP公式平台加载自定义公式时,就是从该返回值里取公式的名称的
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
* @return 所有自定义公式的名称
*/
public String[] getAllFuncNames()
{
String as[] = new String[funcInfos.size()];
for(int i = 0; i < funcInfos.size(); i++)
as[i] = ((FuncInfo)funcInfos.get(i)).funcName;
return as;
}
/**
* 运行时BOTP公式平台加载自定义公式时,就是从该返回值里取公式的分类的(看运
行时的界面就很好理解)
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
* @param s
* @return
*/
public String getFuncCategory(String s)
{
if(s == null)
return null;
for(int i = 0; i < funcInfos.size(); i++)
if(s.equals(((FuncInfo)funcInfos.get(i)).funcName))
return ((FuncInfo)funcInfos.get(i)).funcCategory;
return null;
}
/**
* 获取公式的描述
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
* @param s
* @return
*/
public String getFuncDesc(String s) {
if(s == null)
return null;
for(int i = 0; i < funcInfos.size(); i++)
if(s.equals(((FuncInfo)funcInfos.get(i)).funcName))
return ((FuncInfo)funcInfos.get(i)).funcDesc;
return null;
}
public boolean existFunction(String s)
{
if(s == null)
return false;
for(int i = 0; i < funcInfos.size(); i++)
if(s.equals(((FuncInfo)funcInfos.get(i)).funcName))
return true;
return false;
}
/**
* 运行公式时调用此方法,公式的具体逻辑就写在这里
* @author Yang Yihao. Just call me House.
* @date since 2007-9-6
* @Email: dejavu.house@gmail.com
* @param func 公式名称
* @param paramList 公式的参数
* @return 公式结果
* @throws KScriptException
*/
public Object evalFunction(String func, List paramList) throws
KScriptException
{
//判断用户是选了哪个公式
if(func != null && func.equals("helloLiuXun"))
{
//这里可以写满足各式各样需求的代码
if(paramList != null && paramList.size() > 0)
{
StringBuffer sb = new StringBuffer(10);
for(Iterator iter = paramList.iterator(); iter.hasNext();)
{
sb.append(iter.next()).append(",");
}
return sb.toString();
}
else
return "Congratulations";
}
return "TheEnd";
}
}
2.在BOTP目标单据的实体(entity)上加上扩展属性“billFormulaClass”,属性值设为第1步创建的类的全名(含包名)。如下图:
3.运行时,在BOTP规则设置的“公式平台”->“公式元素”->“函数”页签里可以看到自定义的函数:
4.设置BOTP公式的格式如下:
5.恭喜,令人兴奋的结果完全满足你个性化的需求!