本文介绍了如何使用纯插件在金蝶系统中创建并保存物料(以费用报销单为例)的详细步骤和代码实现。文中通过定义一系列方法和类,包括参数校验、创建单据视图、填写单据字段、触发插件事件及最终保存单据等功能,展示了动态保存单据的完整流程。通过`IBillView`接口创建单据视图,并利用`IDynamicFormViewService`接口进行字段赋值和单据保存,同时处理了参数校验和异常捕获,确保了操作的稳定性和健壮性。
参考帖子之一:http://club.kingdee.com/forum.ph ... ewsource=newservice (知识分享 - 如何使用纯插件创建物料并保存)
需求背景,实现动态保存单据(保存任何单据).
此例子采用费用报销单为例, 代码如下:
private JObject jObjOuter = null;
private Context context;
public JObject SaveBills(string parameter)
{
try
{
jObjOuter = paramenterVerify(parameter);//接口数据校验和用户登录验证校验
if (jObjOuter.IsNullOrEmptyOrWhiteSpace() == false)
{//如果校验参数出现错误则返回方法内封装的错误信息
return jObjOuter;
}
return GetBillsData(JObject.Parse(parameter));
}
catch (Exception e)
{//校验传入的参数是否合法,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "传入的参数非法,请检查传入的参数是否合法!");
return jObjOuter;
}
}
///
/// 请求传入的参数
///
///
///
private JObject GetBillsData(JObject jObject)
{
try
{
string formId = jObject["formid"] + "";
string userName = jObject["name"] + "";
//InstensBillsViews(formId, "");//实例化单据查看,具体的内容.
// 构建一个IBillView实例,通过此实例,可以方便的填写单据各字段
IBillView billView = this.CreateMaterialView(formId);
billView.CreateNewModelData();//新建单据,单据体为一行.
//billView.Model.BatchCreateNewEntryRow("FEntity", 1);//新增单据体 一下是新增一行.
//billView.Model.CreateNewEntryRow("FEntity");//新增单据体 一下是新增两行.
// 触发插件的OnLoad事件:
// 组织控制基类插件,在OnLoad事件中,对主业务组织改变是否提示选项进行初始化。
// 如果不触发OnLoad事件,会导致主业务组织赋值不成功
DynamicFormViewPlugInProxy eventProxy = billView.GetService
eventProxy.FireOnLoad();
// 填写单据各字段属性
this.FillFieldValue(billView, jObject, formId);
// 保存物料
OperateOption saveOption = OperateOption.Create();
return this.SaveMaterial(billView, saveOption);//调用单据保存服务并返回成功或者错误信息.
}
catch (Exception e)
{//校验传入的参数是否合法,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "传入的参数非法,请检查传入的参数是否合法!");
return jObjOuter;
}
}
///
/// 创建一个单据视图,后续将利用此视图的各种方法,设置物料字段值
///
///
/// 理论上,也可以直接修改物料的数据包达成修改数据的目的
/// 但是,利用单据视图更具有优势:
/// 1. 视图会自动触发插件,这样逻辑更加完整;
/// 2. 视图会自动利用单据元数据,填写字段默认值,不用担心字段值不符合逻辑;
/// 3. 字段改动,会触发实体服务规则;
///
/// 而手工修改数据包的方式,所有的字段值均需要自行填写,非常麻烦
///
private IBillView CreateMaterialView(string formId)
{
// 读取单据的元数据
FormMetadata meta = MetaDataServiceHelper.Load(context, formId) as FormMetadata;
Form form = meta.BusinessInfo.GetForm();
// 创建用于引入数据的单据view
Type type = Type.GetType("Kingdee.BOS.Web.Import.ImportBillView,Kingdee.BOS.Web");
var billView = (IDynamicFormViewService)Activator.CreateInstance(type);
// 开始初始化billView:
// 创建视图加载参数对象,指定各种参数,如FormId, 视图(LayoutId)等
BillOpenParameter openParam = CreateOpenParameter(meta);
// 动态领域模型服务提供类,通过此类,构建MVC实例
var provider = form.GetFormServiceProvider();
billView.Initialize(openParam, provider);
return billView as IBillView;
}
///
/// 创建视图加载参数对象,指定各种初始化视图时,需要指定的属性
///
/// 元数据
///
private BillOpenParameter CreateOpenParameter(FormMetadata meta)
{
Form form = meta.BusinessInfo.GetForm();
// 指定FormId, LayoutId
BillOpenParameter openParam = new BillOpenParameter(form.Id, meta.GetLayoutInfo().Id);
// 数据库上下文
openParam.Context = context;
// 本单据模型使用的MVC框架
openParam.ServiceName = form.FormServiceName;
// 随机产生一个不重复的PageId,作为视图的标识
openParam.PageId = Guid.NewGuid().ToString();
// 元数据
openParam.FormMetaData = meta;
// 界面状态:新增 (修改、查看)
openParam.Status = OperationStatus.ADDNEW;
// 单据主键:本案例演示新建费用报销单
openParam.PkValue = null;
// 界面创建目的:普通无特殊目的 (为工作流、为下推、为复制等)
openParam.CreateFrom = CreateFrom.Default;
// 基础资料分组维度:基础资料允许添加多个分组字段,每个分组字段会有一个分组维度
// 具体分组维度Id,请参阅 form.FormGroups 属性
openParam.GroupId = "";
// 基础资料分组:如果需要为新建的基础资料指定所在分组,请设置此属性
openParam.ParentId = 0;
// 单据类型
openParam.DefaultBillTypeId = "";
// 业务流程
openParam.DefaultBusinessFlowId = "";
// 主业务组织改变时,不用弹出提示界面
openParam.SetCustomParameter("ShowConfirmDialogWhenChangeOrg", false);
// 插件
List
openParam.SetCustomParameter(FormConst.PlugIns, plugs);
//PreOpenFormEventArgs args = new PreOpenFormEventArgs(context, openParam);
//foreach (var plug in plugs)
//{// 触发插件PreOpenForm事件,供插件确认是否允许打开界面
// plug.PreOpenForm(args);
//}
//if (args.Cancel == true)
//{// 插件不允许打开界面
// // 本案例不理会插件的诉求,继续....
//}
// 返回
return openParam;
}
///
/// 把费用报销单的各属性,填写到IBillView当前单据中
///
///
private void FillFieldValue(IBillView billView, JObject jObject, string formId)
{
// 把billView转换为IDynamicFormViewService接口:
// 调用IDynamicFormViewService.UpdateValue: 会执行字段的值更新事件
// 调用 dynamicFormView.SetItemValueByNumber :不会执行值更新事件,需要继续调用
// ((IDynamicFormView)dynamicFormView).InvokeFieldUpdateService(key, rowIndex);
IDynamicFormViewService dynamicFormView = billView as IDynamicFormViewService;
XmlDocument xmlDocument = readBillAndFieldType(formId);//读取到单据字段类型模板
HeadFieldFillValue(xmlDocument, jObject, dynamicFormView);//单据头字段进行赋值
JArray entityFieldArray = JArray.Parse(jObject["BillEntity"] + "");//单据体标识数组
EntityFieldFillValue(xmlDocument, jObject, dynamicFormView);//单据体字段进行赋值
//dynamicFormView.SetItemValueByNumber("FOrgID", "101.1", 0);//申请组织-FOrgID
//dynamicFormView.SetItemValueByNumber("FExpenseOrgId", "101.1", 0);//费用承担组织-FExpenseOrgId
//dynamicFormView.UpdateValue("", 0, "obj value");//
}
///
/// 根据Cloud字段类型进行字段赋值
///
///
///
private void FillsFieldValueByFieldType(IDynamicFormViewService dynamicFormView, string cloudFieldType, string fieldKey, string fieldValue)
{
try
{
switch (cloudFieldType)
{
case "Value":
dynamicFormView.UpdateValue(fieldKey, 0, fieldValue);//进行字段赋值
return;
case "Number":
dynamicFormView.SetItemValueByNumber(fieldKey, fieldValue, 0);//申请组织-FOrgID
return;
default:
return;
}
}
catch (Exception e)
{
}
}
///
/// 单据头字段进行赋值
///
/// 读取xml对象
/// 传过来的数据
/// 使用dynamicFormView进行赋值
private void HeadFieldFillValue(XmlDocument xmlDocument, JObject jObject, IDynamicFormViewService dynamicFormView)
{
try
{
JArray jArrayHead = JArray.Parse(jObject["Head"] + "");//单据头标识数组
string headKey = jArrayHead[0] + "";//单据头标识
JArray headFieldArray = JArray.Parse(jObject[headKey] + "");//单据头字段数据数组
for (int i = 0; i < headFieldArray.Count; i++)
{
JObject headField = headFieldArray[i] as JObject;
string fieldKey = headField["fieldKey"] + "";//获取字段标识
string fieldValue = headField["fieldValue"] + "";//获取字段值
string fieldType = headField["fieldType"] + "";//获取字段类型
string cloudFieldType = GetFieldTypeByFieldId(headKey, fieldKey, xmlDocument);//根据字段ID获取字段类型
FillsFieldValueByFieldType(dynamicFormView, cloudFieldType, fieldKey, fieldValue);//根据Cloud字段类型进行字段赋值
}
return;
}
catch (Exception e)
{
jObjOuter = new JObject();//返回错误信息
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "单据头传入的参数非法,请检查单据头传入的参数是否合法!");
return;
}
}
///
/// 单据体字段进行赋值
///
/// 读取xml对象
/// 传过来的数据
/// 使用dynamicFormView进行赋值
private void EntityFieldFillValue(XmlDocument xmlDocument, JObject jObject, IDynamicFormViewService dynamicFormView)
{
try
{
JArray jArrayHead = JArray.Parse(jObject["BillEntity"] + "");//单据体标识数组
for (int i = 0; i < jArrayHead.Count; i++)
{//循环获取单据体唯一标识
string entityKey = jArrayHead[i] + "";//单据体唯一标识
JArray entityFieldArray = JArray.Parse(jObject[entityKey] + "");//单据体字段数据数组
foreach(JObject entityFieldIndex in entityFieldArray)
{//遍历获取单据体的每一行
foreach (var entityFieldObject in entityFieldIndex)
{//单据体的每一行都是一个JObject
string fieldKey = entityFieldObject.Key;//单据体每一行中每一个字段的key
JObject fieldJObject = entityFieldObject.Value as JObject;//单据体每一行中每一个字段的key对应的值是一个JObject,里面才有具体的值
string fieldType = fieldJObject["fieldType"] + "";//获取字段类型
string fieldValue = fieldJObject["fieldValue"] + "";//获取字段值
string cloudFieldType = GetEntityFieldTypeByFieldId(entityKey, fieldKey, xmlDocument);//根据字段ID获取字段类型
FillsFieldValueByFieldType(dynamicFormView, cloudFieldType, fieldKey, fieldValue);//根据Cloud字段类型进行字段赋值
}
}
}
return;
}
catch (Exception e)
{
return;
}
}
///
/// 保存物料,并显示保存结果
///
///
///
private JObject SaveMaterial(IBillView billView, OperateOption saveOption)
{
JObject jsonObject = null;
try
{
// 设置FormId
Form form = billView.BillBusinessInfo.GetForm();
if (form.FormIdDynamicProperty != null)
{
form.FormIdDynamicProperty.SetValue(billView.Model.DataObject, form.Id);
}
// 调用保存操作
IOperationResult saveResult = BusinessDataServiceHelper.Save(
context,
billView.BillBusinessInfo,
billView.Model.DataObject,
saveOption,
"Save");
// 显示处理结果
if (saveResult == null)
{
jsonObject = new JObject();
jsonObject.Add("Message", "未知原因导致保存费用报销单失败!");
jsonObject.Add("IsSuccess", "0");
return jsonObject;
}
else if (saveResult.IsSuccess == true)
{// 保存成功,直接显示
jsonObject = new JObject();
jsonObject.Add("Message", saveResult.OperateResult[0].Message);
jsonObject.Add("IsSuccess", "1");
return jsonObject;
}
else if (saveResult.InteractionContext != null)
{// 保存失败,需要用户确认问题 && saveResult.InteractionContext.Option.GetInteractionFlag().Count > 0
//InteractionUtil.DoInteraction(this.View, saveResult, saveOption,
// new Action
// formResult, opResult, option) =>
// {
// // 用户确认完毕,重新调用保存处理
// this.SaveMaterial(billView, option);
// }));
}
// 保存失败,显示错误信息
if (saveResult.IsShowMessage)
{
jsonObject = new JObject();
saveResult.MergeValidateErrors();
OperateResultCollection operateResultCollection = saveResult.OperateResult;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < operateResultCollection.Count;i++)
{
sb.Append(operateResultCollection[0].Message);
}
jsonObject.Add("Message", sb.ToString());
jsonObject.Add("IsSuccess", "0");
return jsonObject;
}
return null;
}
catch (Exception e)
{
return null;
}
}
///
/// 接口数据校验和用户登录验证校验
///
/// 接口传过来的参数
///
private JObject paramenterVerify(string parameter)
{
try
{
context = this.KDContext.Session.AppContext;
bool conBool = CHUtils.isCloudSSO(context);
if (conBool == false)
{//校验传入的context是否合法,如果为非法返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "先登陆验证!");
return jObjOuter;
}
if (parameter.IsNullOrEmptyOrWhiteSpace())
{//校验传入的参数是否为空,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "参数不能为空!");
return jObjOuter;
}
JObject jObject = JObject.Parse(parameter);
if ((jObject["formid"] + "").IsNullOrEmptyOrWhiteSpace())
{//校验传入的单据名称是否为空,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "当前用户为空,请检查传入的用户名是否正确!");
return jObjOuter;
}
if ((jObject["name"] + "").IsNullOrEmptyOrWhiteSpace())
{//校验传入的用户名是否为空,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "当前用户名为空,请检查传入的用户名是否正确!");
return jObjOuter;
}
else if ((jObject["name"] + "").IsNullOrEmptyOrWhiteSpace() == false)
{//校验传入的用户名是否为空,如果不为空则获取Cloud中此用户信息,校验Cloud中是否有此用户,如果有此用户则构造Context!
string contextUserName = context.UserName;//其实这里还需要获取登陆名,登陆名也需要校验用户名称.
if (contextUserName.Equals((jObject["name"] + "")) == false)
{//如果当前context用户名和传过来的用户名不相同,则需要校验用户名是否存在.
DynamicObjectCollection dynamicObjectCollection = CHUtils.GetUserByUserName(jObject["name"] + "", context);
if (dynamicObjectCollection != null && dynamicObjectCollection.Count > 0)
{
string fuserId = (dynamicObjectCollection[0])["FUSERID"] + "";//用户ID
string fphome = (dynamicObjectCollection[0])["FPHONE"] + "";//用户手机号
context.UserName = jObject["name"] + "";
context.UserId = long.Parse(fuserId);
context.LoginName = jObject["name"] + "";
context.UserPhone = fphome;
}
else
{
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "用户:" + jObject["name"] + " 不存在,请联系系统管理员!");
return jObjOuter;
}
}
//如果当前context用户名和传过来的用户名相同,则不需要校验用户名是否存在了.
}
if ((jObject["Head"] + "").IsNullOrEmptyOrWhiteSpace())
{//校验传入的单据头是否为空,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "当前单据头为空,请检查传入的单据头是否正确!");
return jObjOuter;
}
else if ((jObject["Head"] + "").IsNullOrEmptyOrWhiteSpace() == false)
{
if(JArray.Parse((jObject["Head"] + "")).Count <= 0)
{//校验传入的单据头是否为空,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "当前单据头里面的标识为空,请检查传入的单据头里面的标识是否正确!");
return jObjOuter;
}
}
if ((jObject["BillEntity"] + "").IsNullOrEmptyOrWhiteSpace())
{//校验传入的单据体是否为空,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "当前单据体为空,请检查传入的单据体是否正确!");
return jObjOuter;
}
else if ((jObject["BillEntity"] + "").IsNullOrEmptyOrWhiteSpace() == false)
{
if (JArray.Parse((jObject["BillEntity"] + "")).Count <= 0)
{//校验传入的单据体里面的标识是否为空,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "当前单据体里面的标识为空,请检查传入的单据体里面的标识是否正确!");
return jObjOuter;
}
}
return null;
}
catch (Exception e)
{//校验传入的参数是否合法,如果为空返回错误信息!
jObjOuter = new JObject();
jObjOuter.Add("IsSuccess", "0");
jObjOuter.Add("Message", "传入的参数非法,请检查传入的参数是否合法!");
return jObjOuter;
}
}
本地测试保存单据已成功,其中牵扯其他的文件无法上传, 如果想要请私聊。[/i][/i]
推荐阅读