2022年5月

# -K3Cloud-
K3 Cloud是一款开放的ERP云平台.
一,产品的安装:
  1,SQL Server xxx
  2,K3Cloud产品安装包。(silverlight插件)
二,K3Cloud的开发前概述
  k3cloud支持两种语言以插件干预业务单据,实现业务逻辑的处理。(即C#(.net) , Python2.x)
  K3Cloud分层架构( Web - APP - DataBase)
  K3Cloud插件干预不同的层(Web:表单插件、列表插件、表单构建插件; APP:服务插件)
三,业务单据对象的开发
   实现步骤:
  1,业务对象建模设计
  2,配置元素对象逻辑(关联配置、值更新、实体服务规则、单据转换、唯一检验等)
  3,插件干预业务对象逻辑
  4,发布并部署
==================================================================================
四,思践行
   《申请调拨单》
   4.1 核心节点一:
       1,判断【单据体(以list).需求量】字段,当为0或null时,实现【保存】删除单据体当前记录行。
       2,如下插件实现:
          思路:继承AbstractBillPlugIn,引用Kingdee.BOS,Kingdee.BOS.Core,Kingdee.BOS.DataEntity组件,添加Kingdee.BOS.Core.Bill.PlugIn,Kingdee.BOS.Orm.DataEntity,Kingdee.BOS.Core.Metadata.EntityElement实体对象,重载BarItemClick方法;
          插件:

public override void BarItemClick(Kingdee.BOS.Core.DynamicForm.PlugIn.Args.BarItemClickEventArgs e)
                 {
                     base.BarItemClick(e);
                     Entity entitys = this.View.BillBusinessInfo.GetEntity("FEntity");//单据分录实体元数据
                     DynamicObjectCollection cons = entitys.DynamicProperty.GetValue(this.Model.DataObject) as DynamicObjectCollection;

if (e.BarItemKey == "tbSave" || e.BarItemKey == "tbSplitSave") //保存唯一标识
                     {
                         //注解: i--遍历删除记录行,而非 i++处理删除记录行,是为了避免DeleteEntryRow执行导致Cons--,循环条件不满足,终止了循                                     环,造成不能一次性完成【保存】删除记录行。
                         for (int i = cons.Count - 1; i >= 0; i--)
                         {
                             if (Convert.ToInt32(cons[i]["F_CMK_Qty3"]) == 0)
                             {
                               this.View.Model.DeleteEntryRow("FEntity", i);
                             }
                         }
                     }
                  }

4.2 核心节点二:
          1,判断【单据体.物料编码】,等于当前选中的单据头.仓库所在《即时库存(以list)》中该库存的物料编码一样时。则取当前《即时库存(list)》记录中的库存量字段值,并赋值给当前的《申请调拨单》的单据体(list)记录中。
          2,如下插件实现:
             思路:继承AbstractBillPlugIn,引用Kingdee.BOS,Kingdee.BOS.Core,Kingdee.BOS.DataEntity,Kingdee.BOS.ServiceHelper,添加Kingdee.BOS.Core.Bill.PlugIn,Kingdee.BOS.Orm.DataEntity,Kingdee.BOS.Core.Metadata.EntityElement,Kingdee.BOS.ServiceHelper,Kingdee.BOS.Core.Metadata实体对象,重载ButtonClick方法。
             插件:

public override void ButtonClick(Kingdee.BOS.Core.DynamicForm.PlugIn.Args.ButtonClickEventArgs e)
                 {
                     base.ButtonClick(e);
                     ListselectItems = new List(); //即时库存的字段片段列表
                     selectItems.Add(new SelectorItemInfo("FStockId"));
                     selectItems.Add(new SelectorItemInfo("FStockName"));
                     selectItems.Add(new SelectorItemInfo("FQty"));
                     selectItems.Add(new SelectorItemInfo("FMaterialId"));
                     selectItems.Add(new SelectorItemInfo("FMaterialName"));
                     selectItems.Add(new SelectorItemInfo("F_CMK_Text"));
                     OQLFilter filters = new OQLFilter(); //快捷过滤对象
                     filters.Add(new OQLFilterHeadEntityItem { FilterString = string.Format("FStockOrgId = {0}", this.Model.Context.CurrentOrganizationInfo.ID) });
                     //即时库存列表的集合数据
                     DynamicObject[] newLibraryDataModels = BusinessDataServiceHelper.Load(this.Context, "STK_Inventory", selectItems, filters);
                     Entity entitys = this.View.BillBusinessInfo.GetEntity("FEntity");//申请调拨单单据体的元数据模型
                     DynamicObjectCollection cons = entitys.DynamicProperty.GetValue(this.Model.DataObject) as DynamicObjectCollection;
                     DynamicObject dyObjects1 = this.View.Model.GetValue("F_CMK_WareHouseOne") as DynamicObject;//当前仓库列表
                     DynamicObject dyObjects2 = this.View.Model.GetValue("F_CMK_WareHouseTwo") as DynamicObject;
                     DynamicObject dyObjects3 = this.View.Model.GetValue("F_CMK_WareHouseThree") as DynamicObject;
                     String LibraryNumber01 = Convert.ToString(dyObjects1["Number"]); //申请调拨单的仓库一编码(此方式,要BOS上添加字段引用)
                     String LibraryNumber02 = Convert.ToString(dyObjects2["Number"]); //申请调拨单的仓库二编码  
                     String LibraryNumber03 = Convert.ToString(dyObjects3["Number"]); //申请调拨单的仓库三编码          
                     if (e.Key.ToUpper() == "F_CMK_BUTTON")
                     {
                        for (int i = 0; i < cons.Count; i++)
                        {
                            Decimal sum01 = 0; // 用于累加即时库存中多行同一物料的
                            Decimal sum02 = 0;
                            Decimal sum03 = 0;  
                            for (int j = 0; j < newLibraryDataModels.Count(); j++)
                            {
                               DynamicObject libraryDatas = newLibraryDataModels[j]["StockID"] as DynamicObject;
                               String LibraryNumber = Convert.ToString(libraryDatas["Number"]); //即时库存的仓库编码
                               DynamicObject materialDatas = cons[i]["F_CMK_Base3"] as DynamicObject;
                               String MaterialNumber = Convert.ToString(materialDatas["Number"]); //申请调拨单的物料编码
                               DynamicObject newLibaryDatas = newLibraryDataModels[j]["MaterialID"] as DynamicObject;
                               String MaterialNumber01 = Convert.ToString(newLibaryDatas["Number"]); //即时库存的物料编码
                               if(LibraryNumber01.Equals(LibraryNumber) && MaterialNumber.Equals(MaterialNumber01))
                               {
                                  sum01 += Convert.ToDecimal(newLibraryDataModels[j]["FQty"]);
                                  this.Model.SetValue("F_CMK_Qty", sum01, i);
                               }
                               if (LibraryNumber02.Equals(LibraryNumber) && MaterialNumber.Equals(MaterialNumber01))
                               {
                                  sum02 += Convert.ToDecimal(newLibraryDataModels[j]["FQty"]);
                                  this.Model.SetValue("F_CMK_Qty1", sum02, i);
                               }
                               if (LibraryNumber03.Equals(LibraryNumber) && MaterialNumber.Equals(MaterialNumber01))
                               {
                                  sum03 += Convert.ToDecimal(newLibraryDataModels[j]["FQty"]);
                                  this.Model.SetValue("F_CMK_Qty2", sum03, i);
                               }  
                          }    
                        }
                     }
                    this.View.UpdateView("FEntity");
                  }[/i][/i]


开发工具

•        Visual studio 2012


•        IE插件Silverlight5


•        SQLServer 2008R2 或 Oracle 11G R2


•        跟踪工具(HttpWatchPro6.0)


•        插件Building路径(K3Cloud\K3CloudServer\Bin\)


注意事项:


使用SQLServer2008排序规则为Chinese_PRC_CI_AS


使用Oracle时,数据库字符集必须是:AL32UTF8,国家字符集必须是:AL16UTF16


开环境搭建

公共环境:


1.    配置一台数据库服务器,安装SQLServer2008R2;


2.    配置一台web服务器,安装K/3Cloud产品,配置为管理中心站点;


个人环境:


1.    根据环境配置要求,安装visual studio,安装K/3Cloud产品(不需要配置管理中心)。


2.    检查并更改管理中心地址,


打开K/3Cloud产品安装目录K3Cloud\K3CloudServer\App_Data下Common.config文件,查找managementSiteUrl,把地址更改为公共环境下建立的管理中心ip。


<addkey="managementSiteUrl"value="http://192.168.73.40:8000/" />


开发插件的步骤

 插件开发的步骤


1.    定义插件类


打开Visual studio 2012,新建工程:


MyDev.K3.SCM.Stock.Business.PlugIn;


添加引用组件:


Kingdee.BOS


Kingdee.BOS.Core


新建类:


ReceiptEdit,继承自Kingdee.BOS.Core.Bill.PlugIn.AbstractBillPlugIn


2.    分析业务定义重载方法;


这里,我们先简单实现一个Hello World:


点击菜单HelloWorld,弹出一个Hello World对话框。


点击菜单要重载BarItemClick方法;


3.    引用相关组件(参照组件引用规则);


增加using:


C#


 


using Kingdee.BOS.Core.Bill.PlugIn;


using Kingdee.BOS.Core.DynamicForm;


using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;


 


4.    重载方法编码;


重载BarItemClick方法,输入以下代码:


C#


 


public override void BarItemClick(BarItemClickEventArgs e)


{


    base.BarItemClick(e);


    if (e.BarItemKey == "HelloWorld")


    {


        this.View.ShowMessage("Hello world!", MessageBoxType.Notice);


    }


}


保存;


5.    设置编译路径,编译组件;


编译路径:K3Cloud\K3CloudServer\Bin;


6.    打开IDE设计器,配置插件;


先找到单据属性窗口,编辑“采购收料单-_Bill”单据属性:


 


在插件列表界面,点击注册插件:


(注意该列表中可能已注册有其他插件,这些插件在运行时会动态加载,删除插件可能会导致业务数据错误)


选择插件界面点击浏览:


选择编译好的组件:


勾选插件,确定返回


确定并保存单据。


7.    运行测试;


 


2、动态表单插件

 2. 动态表单插件


        动态表单插件提供了丰富的接口,通过这些接口可以在插件中对表单编辑和列表界面样式、操作进行控制,也可以对显示数据进行各种处理。


再来回顾一下动态表单元数据结构和继承关系:


动态表单模型包含表单外观和表单业务逻辑,表单外观管理界面控件外观及样式,在模型中由视图(View)来控制,表单业务逻辑管理包括服务、校验器、操作和业务规则等,由模型(Model)来控制。


动态表单外观和逻辑都是在IDE中设置的,设置的数据保存在动态表单模型元数据中,具体由布局元数据(LayoutInfo)记录表单外观数据,由业务元数据(BusinessInfo)记录表单逻辑数据,这2个类分别由View和Model持有。


 


                                    (图 10 – 2 动态表单元模型)


 


为了方便使用和提高开发效率,我们将动态表单模型分解为各种表单领域模型,同时为各种模型提供了相应插件:


                                              (图 10 – 3 领域模型-动态表单模型关系)


 


        动态表单插件分为5大类:


 


1.      单据插件


 


2.      列表插件


 


3.      过滤条件插件


 


4.      账表插件


 


5.      动态表单插件


 


继承关系如下:


                                      (图 10 – 4 插件继承关系)


 


 


动态表单视图

 动态表单视图


        前面已经介绍,外观是由视图来管理,我们先看看动态表单视图模型。


        根据BOS架构图可以看到,客户端首先向服务发起HTTP请求,服务端由控制器服务接受请求并转送到动态表单模型控制器,再有动态表单控制器访问动态表单视图。动态表单视图加载外观模型,并从动态表单模型获取数据模型。


 


动态表单视图提供2个视图接口,IDynamicFormView和IDynamicFormViewService。


IDynamicFormView是视图接口,包含领域模型元数据、多视图模型接口、操作转发指令和通用属性方法。该接口可由插件直接访问。


IDynamicFormViewService是动态表单内部使用的接口定义,包含Controller消息路由方法,插件开发不需关注。


 


 


IDynamicFromView有2个重要属性,BusinessInfo和LayoutInfo,分别表示业务对象逻辑元数据和布局元数据。包含在IDE中设置的表单的所有信息。在运行时,客户端发出访问表单请求后,首先读取元数据初始化BusinessInfo和LayoutInfo,View和Model根据元数据定义的界面数据和布局信息展示出表单。


IDynamicFromView接口提供了访问BusinessInfo和LayoutInfo的一些方法,供插件调用以实现业务,例如:访问菜单,修改控件样式,设置标题,更新界面等。


IDynamicFromView接口同时提供操作控制和调用Model的方法,如:调用表单服务,执行操作,发送客户端指令,刷新界面,打开表单,动态注册插件等。


本章节通过一些示例做详细介绍。


先看看界面元素的访问。在实际业务中,经常需要对单据扩展,增加功能,那么就需要访问菜单、字段显示隐藏锁定等。


 


 


动态表单模型

 动态表单模型


动态表单模型接口:IDynamicFormModel和IDynamicFormModelService。


设计思想同动态表单视图一样,将逻辑和插件模型分开。


IDynamicFormModel是模型接口,包含领域模型元数据、数据操作方法。该接口可由插件直接访问。


IDynamicFormModelService是动态表单内部使用的接口定义,插件开发不需关注。


 


        IDynamicFormModel也有BusinessInfo,和IDynamicFromView一样,表示业务对象逻辑元数据。这里BusinessInfo的意义是根据元数据定义绑定数据。


   另外一个重要属性DataObject是当前表单的数据对象。该数据是个DynamicObject,包含单据头和单据体数据,其中单据体是集合对象DynamicObjectCollection,并且可以有多个.


K/3Cloud BOS动态实体类型,默认使用DynamicObject作为数据承载类,可以通过DynamicObjectType.ClrType属性指定自定义类。但我们要求指定的类型必须派生自DynamicObject。


 


 


 


   IDynamicFormModel提供的主要是针对数据进行操作的系列方法,包括:初始化、新增表单数据、复制数据、删除数据、定位当前分录数据行、设置值等方法。


 


动态表单插件

动态表单模型是通过插件代理实现业务逻辑,对外部的接口主要是插件,这些接口可以提供给二次开发使用。


命名空间

 命名空间


Kingdee.BOS.Core.DynamicForm.PlugIn


 


主要类及说明:


 


Class


Description


AbstractDynamicFormDataBinder


动态表单数据绑定器抽象类


AbstractDynamicFormPlugIn


动态表单插件抽象基类


AbstractDynamicWebFormBuilderPlugIn


动态表单页面元数据构建插件


AbstractOperationServicePlugIn


操作服务插件抽象类


 


主要接口:


 


 


Interface


Description


IDynamicFormModelPlugIn


动态表单Model层插件控制接口;实现本接口的插件,可以接收Model层的事件


IDynamicFormViewPlugIn


动态表单View层插件接口;实现本接口的插件可以接收动态表单View层事件


继承体系

 继承体系


动态表单插件分4类,单据、基础资料、动态表单和列表。


业务模型


类(插件、服务)


继承自抽象类


表单插件


单据插件


Kingdee.BOS.Core.Bill.PlugIn.AbstractBillPlugIn


基础资料插件


Kingdee.BOS.Core.Base.PlugIn.AbstractBasePlugIn


动态表单插件


Kingdee.BOS.Core.DynamicForm.PlugIn.AbstractDynamicFormPlugIn


列表插件


列表插件


Kingdee.BOS.Core.List.PlugIn.AbstractListPlugIn


单据插件

命名空间

Kingdee.BOS.Core.Bill.PlugIn


继承体系

 继承体系


System.Object


   Kingdee.BOS.Core.DynamicForm.PlugIn.AbstractDynamicFormPlugIn


      Kingdee.BOS.Core.Bill.PlugIn.AbstractBillPlugIn


         Kingdee.BOS.Core.Base.PlugIn.AbstractBasePlugIn


 


 


接口

视图访问接口

接口名:IdynamicFormViewPlugIn


动态表单View层插件接口;实现本接口的插件可以接收动态表单View层事件。


 


Name


Description


AfterBarItemClick


菜单单击事件完成后处理扩展接口  


AfterBindData


绑定数据后事件处理后扩展接口  


AfterButtonClick


按钮单击之后调用  


AfterCopyRow


分录行拷贝后调用  


AfterDoOperation


操作完成后调用  


AfterEntryBarItemClick


分录菜单单击事件处理扩展接口  


AfterF7Select


基础资料选择返回后调用  


AfterToolBarItemClick


工具栏单击事件处理扩展接口  


BarItemClick


主菜单单击事件处理扩展接口  


BeforeBindData


绑定数据前事件处理后扩展接口,主要用于加载数据到界面前对控件状态进行设置  


BeforeClosed


页面准备关闭  


BeforeDoOperation


操作开始前调用  


BeforeF7Select


基础资料界面调出之前抛出  


ButtonClick


按钮单击时调用  


EntityRowClick


分录行单击事件  


EntityRowDoubleClick


分录行双击事件  


EntryBarItemClick


分录菜单单击事件处理扩展接口  


EntryButtonCellClick


表格按钮单击时调用  


FieldLabelClick


字段标题单击事件  


FireEntryCheck


单据体列全选事件  


ListViewClick


列表控件单击事件  


OnInitialize


页面初始化  


TabItemSelectedChange


页签控件的页签选中事件  


ToolBarItemClick


工具栏单击事件处理扩展接口  


TreeDragDrop


KDTree 拖拽事件  


TreeNodeClick


TreeView 节点单击之后调用  


TreeNodeDoubleClick


TreeView 节点双击之后调用  


 


模型访问接口

接口名:IdynamicFormModelPlugIn


动态表单Model层插件控制接口;实现本接口的插件,可以接收Model层的事件。主要包括:


 


Name


Description


AfterCreateNewData


业务对象创建后的扩展接口  


AfterCreateNewEntryRow


新增、插入、多行输入后调用  


BeforeUpdateValue


值改变更新前的扩展接口  


CreateNewData


创建新业务对象扩展接口,插件可以更加需要自己创建对象  


DataChanged


字段值改变后扩展接口  


 


加载机制

动态表单元模型包括外观模型和表单逻辑模型,第一次访问时会先加载元数据,初始化视图和模型对象,初始化页面,然后创建数据包并绑定数据。


对于二次开发提供了一系列插件允许二次开发在加载表单时对视图、模型、数据包及界面进行控制,插件在加载过程中的执行顺序如下:


OnInitialize                          页面初始化


CreateNewData                   动态表单数据包创建


AfterCreateNewEntryRow      创建分录行后


AfterCreateNewData        动态表单数据包创建后


BeforeBindData                   绑定数据前事件


AfterBindData                     绑定数据及控件状态


BeforeClosed                页面关闭前


     


 


初始化方法

OnInitialize


该插件负责动态表单实例初始化,包括单据Global参数(当然有些参数仅仅在使用时候才获取),动态初始化控件数据源等。


比如,批量修改界面初始化时将允许修改的字段加入到下拉列表。


C#


 


///<summary>


///界面初始化


///</summary>


///<param name="e"></param>


public override void OnInitialize(Core.DynamicForm.PlugIn.Args.InitializeEventArgs e)


{


    //根据列表的formid,获取元数据


    metadata = (FormMetadata)ServiceHelper.MetaDataServiceHelper.Load


        (this.View.Context, this.View.ParentFormView.BillBusinessInfo.GetForm().Id);


 


    //设置标题 - -!


    string strTitle = string.Format("{1}-[{0}]",


        metadata.GetLayoutInfo().GetFormAppearance().Caption,


        this.View.LayoutInfo.GetFormAppearance().Caption);


    LocaleValue formTitle = new LocaleValue();


    formTitle.Add(new KeyValuePair<int, string>(this.Context.UserLocale.LCID, strTitle));


    this.View.SetFormTitle(formTitle);


 


    List<EnumItem> list = new List<EnumItem>();


    //循环检测哪些字段允许批量修改,加入列表


    foreach (Field field in metadata.BusinessInfo.GetFieldList())


    {


        if ((field.FunControl & Field.FUNCONTROL_BULK_EDIT) != Field.FUNCONTROL_BULK_EDIT) continue;


        //修改时隐藏的字段不予显示


        Appearance app = metadata.GetLayoutInfo().GetAppearance(field.Key);


        if (app != null)


        {


            if ((app.Visible & Appearance.VIS_EDIT) != Appearance.VIS_EDIT)


                continue;


        }


 


        _lstFields.Add(field);


        EnumItem item = new EnumItem();


        item.Value = field.Key;


        item.Caption = field.Name;


        list.Add(item);


    }


 


    //排序并将list加入到下拉列表


    list = list.OrderBy(p => p.Caption[this.View.Context.UserLocale.LCID]).ToList();


 


    if (list.Count != 0)


    {


        selectedFielKey = list.FirstOrDefault().Value;


    }


 


    this.View.GetControl<ComboFieldEditor>("FCombo").SetComboItems(list);


}


 


创建数据包

 创建数据包


CreateNewData


动态表单数据包创建,只在新增时触发,打开表单不触发。


我们在IDE里画好单据和基础资料后,不需要编写任何代码,打开界面,可以看到已经创建好一张新的空单据,这是因为新建时候会调用CreateNewRow创建空数据。很多时候,我们需要创建有缺省值或者新增时候从其他服务获取数据显示过来,我们就可以通过该事件来加载数据。


 


示例:简单的加载动态表单数据。


C#


 


public override void CreateNewData(BizDataEventArgs e)


{


    if (!billFormId.IsNullOrEmptyOrWhiteSpace())


    {


        DynamicObject obj = BusinessDataServiceHelper.LoadBillTypePara(context, businessInfo, formId, false);


        e.BizDataObject = obj;


    }


    base.CreateNewData(e);


}


 


示例:操作结束后,在动态表单上显示操作结果。


C#


 


///<summary>


///创建数据包事件处理;由插件处理数据包的创建过程,界面仅展示


///</summary>


///<param name="e"></param>


public override void CreateNewData(BizDataEventArgs e)


{


    // 创建本界面需要的数据对象


    e.BizDataObject = new DynamicObject(this.View.OpenParameter.FormMetaData.BusinessInfo.GetDynamicObjectType());


 


    BusinessInfo info = this.View.OpenParameter.FormMetaData.BusinessInfo;


 


    // 给角色表格赋值


    Entity resultEntity = info.GetEntity("FEntity");


 


    Field seqField = info.GetField("FSeq");


    Field nameField = info.GetField("FName");


    Field statusField = info.GetField("FStatus");


    Field messageField = info.GetField("FMessage");


    Field typeField = info.GetField("FType");


 


    DynamicObjectCollection resultEntityData = (DynamicObjectCollection)resultEntity.DynamicProperty.GetValue(e.BizDataObject);


 


    int row = 0;


    foreach (OperateResult rowResult in _results)


    {


        // 添加新行


        DynamicObject rowData = new DynamicObject(resultEntity.DynamicObjectType);


 


        // 给行中的字段赋值


        seqField.DynamicProperty.SetValue(rowData, row + 1);


        nameField.DynamicProperty.SetValue(rowData, rowResult.Name);


        statusField.DynamicProperty.SetValue(rowData, (rowResult.SuccessStatus ? "1" : "0"));


        messageField.DynamicProperty.SetValue(rowData, rowResult.Message);


        typeField.DynamicProperty.SetValue(rowData, ((int)rowResult.MessageType).ToString());


        resultEntityData.Add(rowData);


        row++;


    }


}


 


AfterCreateNewEntryRow


创建分录行后事件。字段值设置优先考虑使用IDE进行实体服务规则配置。


该事件通常用于新增分录后对数据进行判断处理。需要注意,这个事件是在每次新增分录都会触发,对于不需要在界面上显示的可以在新建分录后(如AfterCreateNewData事件)一次性处理。


C#


 


///<summary>


///创建新的分录行事件


///</summary>


///<param name="e"></param>


public override void AfterCreateNewEntryRow(CreateNewEntryEventArgs e)


{


    base.AfterCreateNewEntryRow(e);


 


    if (e.Entity.Key.Equals(CONST_ENG_Route.CONST_FSubEntity.ENTITY_FSubEntity))


    {


        IEnumerable<DynamicObject> subEntryDataCol = this.Model.GetEntityDataObject(e.Entity);


        if (e.Row == subEntryDataCol.Count() - 1)   // 插入行不赋值


        {


            // 设置工序号=上取整((MAX(工序号)+1)/10)*10,且不大于9999


            int maxOperNumber = subEntryDataCol.Select(w => w.GetDynamicObjectItemValue<int>(CONST_ENG_Route.CONST_FSubEntity.ORM_OperNumber)).Max();


            int newOperNumber = (int)Math.Ceiling(((decimal)maxOperNumber + 1) / 10) * 10;


            this.Model.SetValue(CONST_ENG_Route.CONST_FSubEntity.KEY_FOperNumber, newOperNumber > 9999 ? 9999 : newOperNumber, e.Row);


        }


    }


}


 


 


AfterCreateNewData


动态表单数据包创建后事件。该方法仅在新增表单后触发。主要用于新建表达根据元数据定义初始化数据包后,根据特殊需求,改变当前数据。


通常我们在IDE里通过配置实体服务规则实现表单字段的缺省值赋值:


但有时需要根据一些参数动态设置值时,就需要用插件实现。下面举一个例子,新增单据时根据当前组织获取邮件的缺省值,赋值到当前数据包。


 


C#


 


public override void AfterCreateNewData(EventArgs e)


{


    base.AfterCreateNewData(e);


    OQLFilter ofilter = new OQLFilter();


    ofilter.Add(new OQLFilterHeadEntityItem { FilterString = string.Format(" FORGID ={0} ", this.Model.Context.CurrentOrganizationInfo.ID) });


 


    DynamicObject[] obj = BusinessDataServiceHelper.Load(this.View.Context, "BAS_MAILDEFAULTSET", null, ofilter);


    if (obj != null && obj.Count() > 0)


    {


        DynamicObject defaultSet = obj[0];


        this.View.Model.SetValue("FMessageType", defaultSet["FMessageType"]);


        this.View.Model.SetValue("FServer", defaultSet["FOutgoingMailServer"]);


        this.View.Model.SetValue("FSMTPPort", defaultSet["FSMTPPort"]);


    }


}


因为该插件属于创建数据包,在该插件里设置的值不会加到状态管理器中,因此该方法设置的值是整个数据包一起发送到客户端的。客户端数据可以通过Http数据监控查询:


 


AfterCreateModelData


模型层数据包创建完毕。该事件只在新增表单模型后触发,用于对新增后表单模型进行相关操作。此插件的操作不会引起Model.DataChanged值改变。


例:


订单变更查询中,需要在界面上,根据查询列表中的版本显示订单内容,在打开查询时缺省打开第一行基准版本的订单。


插件代码:


C#


 


///<summary>


///模型数据包创建完毕,显示订单界面


///</summary>


///<param name="e"></param>


public override void AfterCreateModelData(EventArgs e)


{


    if (listVersions != null && listVersions.Count() > 0)


    {


        baseOrderData = SCMCommon.DeserializeJsonStringToDynamicObject(orderBusinessInfo, listVersions[0].JsonData);


        ShowOrderBillVersion();


    }


}


 


 


数据绑定

 数据绑定


BeforeBindData


绑定数据前事件。该插件可以在数据绑定前对数据进行处理,对数据修改不会被状态管理器记录。


例如:单据插件中根据类型增加分录行。


C#


 


public override void BeforeBindData(EventArgs e)


{


    base.BeforeBindData(e);


    //基础资料


    if (_modelTypeId == ElementType.ELEMENTTYPE_BASE.ToString())


    {


        this.View.Model.CreateNewEntryRow("FSearchControl");


    }


    else if (_modelTypeId == ElementType.ELEMENTTYPE_BILL.ToString()) //业务单据


    {


        this.View.Model.CreateNewEntryRow("FFieldParamControl");


    }


    // 操作参数


    this.View.Model.CreateNewEntryRow("FPARAMOPERATION");


}


 


注:批量新增行用this.Model.BatchCreateNewEntryRow(stringkey, int rowCount)方法。


 


AfterBindData


绑定数据及控件状态,该事件较常用,加载和界面刷新都会调用该插件。通常该事件处理数据可见性样式等。


如:单据插件根据类型设置单据字段可见性。


C#


 


public override void AfterBindData(EventArgs e)


{


base.AfterBindData(e);


//隐藏菜单项


this.View.GetMainBarItem("tbNew").Visible = false;


//显示分录菜单项


    this.View.GetBarItem("Fentity", "tbAdd").Enabled = true;


 


    //基础资料


    if (_modelTypeId == ElementType.ELEMENTTYPE_BASE.ToString())


    {


        this.View.StyleManager.SetVisible("FTab_Field", null, false);


}


else if (_modelTypeId == ElementType.ELEMENTTYPE_BILL.ToString()) //业务单据


{


        //单据不含单据类型字段时,字段参数页签屏蔽


        if (this._metaData.GetLayoutInfo().GetFieldAppearances().Any(f => f is BillTypeFieldAppearance))


        {


            this.View.StyleManager.SetVisible("FTab_Field", null, true);


        }


}


}


 


设置背景颜色。


C#


 


public override void AfterBindData(EventArgs e)


{       


    //获取单据体表格, 参数为单据体Key,示例代码假设为FEntity


    EntryGrid grid = this.View.GetControl<EntryGrid>("FEntity");


    //设置第一行的背景色,参数:颜色,6位16进制符号,每2位代表一种基色;从0开始,行序号


    grid.SetRowBackcolor("#FFC080", 0);


    //设置第二行F1字段的背景色,参数:字段Key;颜色;行序号


    grid.SetBackcolor("F1", "#FFC080", 1);


}


 


 


 


加载和关闭

 加载和关闭


OnLoad


页面加载。该事件在BeforeBindData前触发,并且不受StyleManager管理,在此事件设置单据字段的可见性和锁定性无效。


OnLoad时,数据已经获取到,通常我们在此事件处理一些数据设置。


 


例如:过滤界面插件设置缺省值和页签可见性。


C#


 


public class SaleCollectFilter : AbstractCommonFilterPlugIn


{


public override void OnLoad(EventArgs e)


{


base.OnLoad(e);


//设置日期缺省值


    this.View.Model.SetValue("FStartDate", dateFrom.ToString("yyyy-MM-dd"));


this.View.Model.SetValue("FEndDate", dateTo.ToString("yyyy-MM-dd"));


//隐藏过滤界面排序页签


this.View.StyleManager.SetVisible("FTab_P21", null, false);


}


}


 


列表界面隐藏分组滑动控件。


C#


 


public class SPMPromotionPolicyList : AbstractListPlugIn


{


     public override void OnLoad(EventArgs e)


     {


         base.OnLoad(e);


         // 隐藏分组滑动控件(默认不展开)


         this.View.GetControl<SplitContainer>("FSpliter").HideFirstPanel(true);


this.View.GetControl("FPanel").SetCustomPropertyValue("BackColor", "#FFEEEEEE");


     }


}


 


注:该事件在每次UpdateView()时候都会调用。


 


BeforeClosed


页面关闭前插件。对于单个表单关闭,该插件基本不需要处理。对于多个表单交互,或者嵌入式表单,通常需要关闭窗体时,返回数据时,通过该插件实现。


如:关闭时刷新父窗体。


C#


 


public override void BeforeClosed(BeforeClosedEventArgs e)


{


 


    object isDataChanged = this.View.OpenParameter.GetCustomParameter("Changed");


    if (isDataChanged != null && (bool)isDataChanged)


    {


        this.View.ParentFormView.Refresh();


        this.View.SendDynamicFormAction(this.View.ParentFormView);


}


    base.BeforeClosed(e);


}


 


关闭时传递数据到父窗体。


C#


 


public override void BeforeClosed(BeforeClosedEventArgs e)


{


this.View.ReturnToParentWindow(_data);


    base.BeforeClosed(e);


}


 


关闭窗体判断数据是否修改并提示保存。


C#


 


///<summary>


///界面关闭前事件:判断用户是否修改了数据,提示保存


///</summary>


///<param name="e"></param>


public override void BeforeClosed(BeforeClosedEventArgs e)


{


    if (this._dataChanged == true) // 仅关注模型数据发生了改变的情况


    {


        e.Cancel = true;


        string msg = "内容已经修改,是否保存?";


        this.View.ShowMessage(msg, MessageBoxOptions.YesNoCancel, new Action<MessageBoxResult>((result) =>


        {


            if (result == MessageBoxResult.Yes) // 保存


            {


                       this.View.GetControl("FDesignPanel").InvokeControlMethod("Save");


            }


            else if (result == MessageBoxResult.No) // 不要保存


            {


                this._dataChanged = false;         


                this.View.Close();


            }


        }));


    }


}


 


本文档由未注册的 Word-2-CHM 软件自动从 Word 文件生成。


单据操作

 单据操作


    BeforeSave


单据保存前插件。单据内置保存操作,自动将修改数据保存到数据库。插件BeforeSave可以在保存前对单据数据进行处理。通常处理有两个:


Ø  数据校验;


Ø  计算和更新数据;


在BOS平台当客户端发起请求,到web服务器后,领域模型框架调用运行时,加载插件运行。用户执行操作时,运行时调用操作服务进行数据模型的操作。而插件中调用服务也是先向服务框架请求服务。


通常应用都是在业务保存前进行数据校验,校验通过后,调用保存服务保存,在大多数系统中都是这样应用。在BOS平台中,架构设计上支持集成服务,所有操作都是设计有服务接口,二次开发可以很容易将所有操作发布成服务供外部系统调用。这样对外部系统来说,调用服务保存将会很容易。但如何保证数据的正确性?大部分设计是由外部系统保证,但对复杂业务系统来说,外部系统很难保证每个业务数据的正确性,甚至用大量访问系统来获取验证数据。为此,BOS平台在操作上提供了校验服务,这样在系统内部通过插件调用服务前会自动执行校验服务。而外部系统访问的是BOS操作发布的服务本身也带有校验。


因此建议将数据校验按业务逻辑分开成两类,一类是界面输入校验,如字符、数字类型、格式化和表达式校验等,可以在插件保存前进行校验;而数据业务的校验,如库存校验信用检查等,通过校验服务校验。


校验方法如下:


1.    优先通过IDE配置校验数据,如输入格式,最大最小值限定;


2.    操作控制类校验在表单的操作前插件检查;


3.    业务控制类校验在表单校验服务校验。


 


该事件中可以通过设置参数的Cancel终止保存操作。


 


下面例子是保存前更新数据(信用评分单据保存设置信用等级标准)。


C#


 


public override void BeforeSave(BeforeSaveEventArgs e)


{


    DynamicObject doGradeScheme = this.Model.GetValue("FScheme") as DynamicObject;


decimal deSumScore = Convert.ToDecimal(this.Model.GetValue("FSumScore"));  


int iGradeSchemeId = Convert.ToInt32(doGradeScheme["Id"]);                       


    // 保存前判断当前信用评分表的综合得分属于哪一个信用等级标准


    DynamicObjectCollection docGrades = CreditServiceHelper.GetCreditGrades(this.Context, 0, iGradeSchemeId);


    for (int i = 0; i < docGrades.Count(); i++)


    {


        DynamicObject doGrade = docGrades[i];


        decimal deScoreFrom = Convert.ToDecimal(doGrade["FSCOREFROM"]);


        decimal deScoreTo = Convert.ToDecimal(doGrade["FSCORETO"]);


         if(deSumScore >= deScoreFrom && deSumScore <= deScoreTo)


        {


            this.View.Model.SetValue("FGrade", Convert.ToInt32(doGrade["FID"]));


            this.View.Model.SetValue("FSuggestion", doGrade["FDESCRIPTION"]);


        }


    }


}


 


    AfterSave


单据保存后插件。主要用于保存后界面的控制、控件的显示以及不需要事务保证的其他数据更新。


3.服务插件

BOS平台抽象了领域模型,针对领域模型定义各种操作并提供操作服务。但很多时候,内置的操作并不一定满足需要。为此在APP服务层提供服务插件,以方便二次开发扩展应用。


服务插件配置是在BOS IDE中操作编辑里:


 


服务插件运行在App层,因此,在外部系统调用集成服务接口时,随着操作服务的发布,服务插件也会有效。


 


和校验器配合使用


运行于App层


 


命名空间

Kingdee.BOS.Core.DynamicForm.PlugIn


继承体系

所有服务插件都应继承自抽象服务插件类。


插件模型


继承自抽象类


服务插件


Kingdee.BOS.Core.DynamicForm.PlugIn.AbstractOperationServicePlugIn


接口

 接口


IOperationServicePlugIn


 


 


Name


Description


AfterExecuteOperationTransaction


执行操作事务后的逻辑处理,后续事情不影响当前操作事务的可以放在此处理  


BeforeExecuteOperationTransaction


执行操作事务前事件,通知插件对要处理的数据进行排序等预处理  


BeginOperationTransaction


调用操作事务前触发  


EndOperationTransaction


调用操作事务成功后触发  


InitializeOperationResult


操作成功后触发  


OnAddValidators


通过此事件,通知插件进行添加自定义数据校验器  


OnPrepareOperationServiceOption


通过此事件,通知插件进行选项设置  


OnPreparePropertys


准备操作对象实体属性事件,在此事件中可以将校验过程需要的属性对应的Key添加进来以便统一从数据库中加载数据  


 


 


    BeforeExecuteOperationTransaction


执行操作事务前插件。通常用于执行操作前数据处理,该插件在webservice服务调用时也会执行。该事件是操作事务前允许处理数据的最后一个插件,为保证操作事务时间最短,在性能优化时会将不需要事务保护的部分服务逻辑放到这个插件里处理。


该插件中不适合用于数据校验,数据校验方法请参考数据校验章节。


 


例如:


在直接调拨单中,增加保存服务插件,在保存事务前,计算未结算的关联数量。这个数据在结算业务逻辑中使用,必须保证数据准确有效,不需要调拨界面显示。如果在web插件中计算会有2个问题:


1.    数据操作修改后必须重新计算,多次修改要多次计算,效率低;


2.    外部接口调用保存服务时,需要自己计算好填到数据包,如果涉及到本地化设置(如数据精度)等问题,还要调用方特殊处理;


 


在保存操作增加服务处理步骤:


1.    定义服务插件类StockTransferDirect.SaveService,插件继承AbstractOprerationService;


2.    重载BeforeExecuteOperationTransaction方法,示例代码:


C#


 


// 保存操作事务前,计算单据上的“未结算关联数量”


public override void BeforeExecuteOperationTransaction(BeforeExecuteOperationTransaction e)


{


if (e.SelectedRows == null || e.SelectedRows.Count() == 0)


{


    // 没有数据,取消操作(通常此类判断应在web端进行,避免不必要的资源消耗,此处仅示例如何取消操作)


    e.Cancel = true;


return;


    }


    DynamicObject[] objs = (from p in e.SelectedRows select p.DataEntity).ToArray();


    foreach (DynamicObject data in objs)


    {


        DynamicObjectCollection dataentrys = data["TransferDirectEntry"] as DynamicObjectCollection;


        foreach (DynamicObject entry in dataentrys)


        {


            //“未结算关联数量”=“调拨数量”-“关联退货数量”-“结算关联数量”。


            decimal qty = Convert.ToDecimal(entry["Qty"]);


            decimal baseQty = Convert.ToDecimal(entry["BaseQty"]);


            decimal receiveQty = Convert.ToDecimal(entry["ReceiveQty"]);


            decimal baseJoinQty = Convert.ToDecimal(entry["BaseJoinQty"]);


            decimal baseSettQty = Convert.ToDecimal(entry["JoinBaseSettQty"]);


            decimal SettQty = Convert.ToDecimal(entry["JoinSettleQty"]);


            entry["JoinUnSettleQty"] = qty - receiveQty - SettQty;


            entry["JoinBaseUnSettQty"] = baseQty - baseJoinQty - baseSettQty;


        }


    }


}


 


3.    编译后,运行系统,在IDE中配置保存服务插件;


4.    调试测试;


 


 


    AfterExecuteOperationTransaction


 


执行操作事务后插件。通常用来处理操作后的相关的数据处理,如生成其他单据、更新状态、运行业务运算等。该插件在操作事务外,执行结果不影响操作,因此该插件要考虑执行失败的逻辑处理。


AfterExecuteOperationTransaction参数:


Name


Description


DataEntitys


本次操作事务处理成功的数据实体集合  


SelectedRows


当前操作校验通过的所有行对象  


(参数命名空间:BOS.Core.DynamicForm.PlugIn.Args)


 


审核结束自动生成付款单的代码示例:


C#


 


public override void AfterExecuteOperationTransaction(AfterExecuteOperationTransaction e)


{


    //审核时,如果弹出信用相关提示警示信息时,e.DataEntitys将没有记录。此时直接退出


    if (e.DataEntitys.IsNullOrEmpty() || e.DataEntitys.Count() == 0)


    {


        return;


    }


    foreach (var dataEntity in e.DataEntitys)


    {


        if (this.IsCanSoureBillPush(dataEntity)) //申请借款,下推付款申请单


        {


            DynamicObject[] objs = new DynamicObject[1] { dataEntity };


            //初始化备用信息


            this.OnInit(objs);


            //生成下游单据


            this.ProduceBill(this.OperationResult as ConvertOperationResult);


        }


    }


}


 


    BeginOperationTransaction


操作事务开始插件。用于在执行操作前处理数据,该方法与BeforeExecuteOperationTransaction区别主要在于该插件在操作事务内,出错后系统会回滚事务。该插件开发时要特别关注对性能的影响,建议对分录的所有处理考虑批量进行。


参数:


CancelFormService


是否取消执行本操作所关联的表单服务;即终止服务插件,不执行其他表单服务插件。


CancelOperation


是否取消本操作;即终止操作。


 


简单生产领料单保存前,根据当前单据删除的领料单分录获取关联的源单分录,在保存后,检测简单领料分录是否仍存在该分录ID上拉的行,然后再判断应该更新简单领料分录还是源单分录,重置该分录行的领料标识。


C#


 


//更新操作前,获取删除的分录数据,在更新后做处理


public override void BeginOperationTransaction(BeginOperationTransactionArgs e)


{


//获取删除的行


    IDbDriver driver = new Kingdee.BOS.App.Data.OLEDbDriver(this.Context);


    IDataManager manager = DataManagerUtils.GetDataManager(this.BusinessInfo.GetDynamicObjectType(), driver);


    ISaveDataSet updateData = manager.GetSaveDataSet(e.DataEntitys.ToArray());


    DeleteRows = updateData.Tables["T_SP_PICKMTRLDATA"].DeleteRows;


    if (DeleteRows != null && DeleteRows.Length > 0)


    {


        List<long> lstDeleteIds = DeleteRows.Select(w => w.Oid).ToList();


//根据领料单ENTRYID获取关联的源单SRCENTRYID


        listInStockEntryId = ServiceFactory.GetSpInStockService(this.Context).GetInStcokEntryByPickMtrlEntryId(this.Context, lstDeleteIds);


    base.BeginOperationTransaction(e);


    }


}


 


C#


 


//更新操作后,根据更新前获取的删除分录的数据,重新计算领料标识


public override void EndOperationTransaction(EndOperationTransactionArgs e)


{


    base.EndOperationTransaction(e);


    ISpInStockService service= ServiceFactory.GetSpInStockService(this.Context);


    // 检测简单领料分录是否仍存在该分录ID上拉过来的行,否则更新简单入库的分录行领料标识


    if (listInStockEntryId != null && listInStockEntryId.Count > 0)


{


    // 批量检测


        service.ResetIsPickInInStcokEntry(this.Context, listInStockEntryId);


}


    foreach (var dataEntity in e.DataEntitys)


    {


        IEnumerable<long> lstEntryId = dataEntity.GetDynamicObjectItemValue<DynamicObjectCollection>(ENTITY_ORM_Entity).Where(w => w.GetDynamicObjectItemValue<string>( ORM_SrcBillType_Id) == SCMFormIdConst.SpInStockBill).Distinct().Select(w => w.GetDynamicObjectItemValue<long>( ORM_SrcEnteryId));


        // 根据分录ID重置该分录行的领料标识(批量更新)


        service.UpdateIsPickInInStcokEntry(this.Context, lstEntryId.ToList());


    }


}


 


    EndOperationTransaction


操作事务结束插件,此插件在事务内运行,出错后系统会回滚事务。


示例参照BeginOperationTransaction


 


 


4、应用案例介绍

 4. 应用案例介绍


 


收货单提供以下功能:


1.      增加下拉列表,显示单据头的所有字段;


2.      在分录菜单上增加库存查询(FQueryInventory)菜单项;


3.      点击库存查询时,查询分录上当前焦点所在物料的库存(STK_InvSumQuery);


STK_Inventory


4.      查询库存时按组织隔离,只查询当前组织的库存;


5.      当前分录物料F8时,显示所有组织的物料;


6.      暂存时清空单据类型的值;


7.      物料基础资料增加字段有效期至(F_MCY_ExpiryDate);


8.      F8时只显示有效期〉今天的物料;


9.      保存判断物料的库存,如果〉100则提示“库存〉100,是否入库?”;


10.   保存后锁定“收料部门”、“收料员”;


11.   保存后自动记录收料日志(MCY_stk_ReceiptLog);


 


操作步骤:


1.      增加下拉列表,显示单据头的所有字段;


a)        新建(打开)收货单插件工程(MyDev.K3.SCM.Stock.Business.PlugIn);


b)        重载OnInitialize方法,定义List<EnumItem>用于存储下拉列表枚举值;


c)        通过this.View.BusinessInfo.GetFieldList()方法获取所有字段;


d)        通过this.View.GetControl<ComboFieldEditor>方法获取界面上的下拉列表控件;


e)        SetComboItems绑定值;


f)         代码如下:


C#


 


public override void OnInitialize(InitializeEventArgs e)


{


    base.OnInitialize(e);


    List<EnumItem> list = new List<EnumItem>();


    foreach (Field field in this.View.BusinessInfo.GetFieldList())


    {


        EnumItem item = new EnumItem();


        item.Caption = field.Name;


        item.EnumId = field.Key;


        item.Value = field.Key;


        list.Add(item);


    }


    this.View.GetControl<ComboFieldEditor>("FCombo").SetComboItems(list);


}


2.      在分录菜单上增加库存查询(tbQueryInventory)菜单项;


a)        运行IDE,选择单据体-菜单集合,新增菜单:


b)        保存;


 


3.      点击库存查询时,查询分录上当前焦点所在物料的库存;


a)        打开插件工程,重载方法EntryBarItemClick


b)        判断BarItemKey==库存查询(tbQueryInventory)


c)        取当前分录行


d)        设置ListShowParameter参数,打开表单


 


这里介绍2种获取当前分录字段数据的方法:


TryGetEntryCurrentRow:获取单据体当前行,返回是否取到值以及行数据和行号;


另外一种方法:


先获取单据体当前行号,再取指定行数据;


 


2种方法没什么区别。


 


示例代码如下:


C#


 


public override void EntryBarItemClick(BarItemClickEventArgs e)


{


    base.EntryBarItemClick(e);


    if (e.BarItemKey == "tbQueryInventory")


    {


        ShowQueryInventory();


    }


}


 


private void ShowQueryInventory()


{


    DynamicObject row;


int rowIndex;


// 直接获取当前分录行返回的是分录行对象。


    if (this.Model.TryGetEntryCurrentRow("FEntity", out row, out rowIndex))


    {


        ListShowParameter parameter = new ListShowParameter();


        parameter.FormId = "STK_Inventory";    // 即时库存的FormId


        parameter.MultiSelect = false;


        parameter.ListFilterParameter.Filter = string.Format(" FMaterialId = '{0}' ", Convert.ToString(row["FBase_Id"]));


        this.View.ShowForm(parameter);


    }


}


 


取单据体当前行号,再取指定行的字段数据的方法如下:


C#


 


private void ShowQueryInventory()


{


// 获取当前行


    int rowIndex = this.Model.GetEntryCurrentRowIndex("FEntity");


    if (rowIndex > -1) // 判断当前行有数据


{


    // 取指定行的物料(ide中设置key为FBase)字段数据


        DynamicObject materialObj = (DynamicObject)this.Model.GetValue("FBase", rowIndex);


        ListShowParameter parameter = new ListShowParameter();


        parameter.FormId = "STK_Inventory";


        parameter.MultiSelect = false;


        parameter.ListFilterParameter.Filter = string.Format(" FMaterialId = '{0}' ", materialObj["Id"].ToString());


        this.View.ShowForm(parameter);


    }


}


 


调试状态下,可以屏蔽代码parameter.ListFilterParameter.Filter看看过滤条件的效果。


 


注意:ListFilterParameter 的Filter属性设置的字段是用IDE中的字段标识。


 


 


 


4.      查询库存时按组织隔离,只查询当前组织的库存:


a)        增加过滤条件,组织=当前组织


b)        parameter.ListFilterParameter.Filter= string.Format(" FORGID ={0}",this.Model.Context.CurrentOrganizationInfo.ID)});


 


5.      当前分录物料F8时,显示所有组织的物料;


a)   重载AuthPermissionBeforeF7Select方法,设置参数IsIsolationOrg = false;


b)   同样,如果需要F8时控制只显示当前组织的物料,该参数设置为true。


注意:


在BOS系统中,默认是按组织隔离的,即非共享基础资料,在F8时都是只显示当前组织的物料。


代码示例如下:


C#


 


public override void AuthPermissionBeforeF7Select(AuthPermissionBeforeF7SelectEventArgs e)


{


    base.AuthPermissionBeforeF7Select(e);


    if (e.FieldKey == "FBase")


    {


        e.IsIsolationOrg = false;


    }


}


 


 


6.      暂存时清空单据类型的值;


 


C#


 


public override void BeforeDoOperation(BeforeDoOperationEventArgs e)


{


    base.BeforeDoOperation(e);


    if (e.Operation.FormOperation.Operation.Equals("DRAFT", StringComparison.OrdinalIgnoreCase))


    {


        this.Model.SetValue("FBillTypeID", null);


    }


}


 


7.      F8时只显示审核日期〉2014-03-22的供应商;


a)        重载BeforeF7Select事件;


b)        设置列表过滤参数ListFilterParameter的属性Filter;


C#


 


public override void BeforeF7Select(BeforeF7SelectEventArgs e)


{


    base.BeforeF7Select(e);


    if (e.FieldKey == "FSupplierId1")


    {


        string filter = " FCreateDate > '2014-03-20' ";


        if (string.IsNullOrEmpty(e.ListFilterParameter.Filter))


        {


            e.ListFilterParameter.Filter = filter;


        }


        else


        {


            e.ListFilterParameter.Filter += " AND " + filter;


        }


    }


}


 


 


 


8.      保存判断物料的库存,如果〉100则提示“库存〉100,是否入库?”;


a)        新建收货单服务插件工程MyDev.K3.SCM.App.Stock.ServicePlugIn;


b)        定义保存服务类SaveServicePlugIn,继承自AbstractOperationServicePlugIn;


c)        重载OnAddValidators方法;


代码示例如下:


C#


 


public override void OnAddValidators(AddValidatorsEventArgs e)


{


    base.OnAddValidators(e);


     SaveValidator saveValidator = new SaveValidator();


    saveValidator.EntityKey = "FBillHead";


    e.Validators.Add(saveValidator);


}


 


d)        定义保存校验类SaveValidator,继承自AbstractValidator;


e)        重载方法:Validate:


                        i.             获取单据体分录数据,取到物料Id;


                      ii.             查询物料库存;


                     iii.             检查库存是否〉100;


                     iv.             构造校验结果信息;


代码示例:


C#


 


public override void Validate(ExtendedDataEntity[] dataEntities, ValidateContext validateContext, Kingdee.BOS.Context ctx)


{


    if (dataEntities == null || dataEntities.Length == 0)


    {


        return;


    }


 


    Dictionary<long, decimal> dictErrMaterialId = new Dictionary<long, decimal>();


    //取所有物料


    List<long> listMaterialId = new List<long>();


    foreach (ExtendedDataEntity entityObj in dataEntities)


    {


        DynamicObjectCollection collection = (DynamicObjectCollection)entityObj["FEntity"];


        foreach (DynamicObject rowObj in collection)


        {


            listMaterialId.Add((long)rowObj["FBase_Id"]);


        }


    }


    string sql = " select a.FMATERIALID, sum(a.FBASEQTY) FQTY from  T_STK_INVENTORY a where exists (select 1 from TABLE(fn_StrSplit(@FMATERIALID, ',',1)) t where t.FID = a. FMATERIALID) group by FMATERIALID ";


    SqlParam param = new SqlParam("@FMATERIALID", KDDbType.udt_inttable, listMaterialId.Distinct().ToArray());


    using (IDataReader dr = DBUtils.ExecuteReader(this.Context, sql, param))


    {


        while (dr.Read())


        {


            decimal qty = Convert.ToDecimal(dr["FQTY"]);


            if (qty > 100)


           {


                dictErrMaterialId.Add(Convert.ToInt64(dr["FMATERIALID"]), qty);


            }


        }


    }


 


    foreach (ExtendedDataEntity entityObj in dataEntities)


    {


        DynamicObjectCollection collection = (DynamicObjectCollection)entityObj["FEntity"];


        foreach (DynamicObject rowObj in collection)


        {


            if (dictErrMaterialId.ContainsKey((long)rowObj["FBase_Id"]))


            {


                ValidationErrorInfo errinfo = new ValidationErrorInfo("FMATERIALID", Convert.ToString(entityObj.DataEntity["Id"]), entityObj.DataEntityIndex, Convert.ToInt32(rowObj["Id"]), "SaveValidator", "库存数量大于100", "校验失败", ErrorLevel.Error);


                validateContext.AddError(entityObj, errinfo);


            }


        }


    }


}


 


f)         重载方法:Validate:


 


 


9.      保存后锁定“收料部门”、“收料员”;


a)        锁定字段的方法:this.View.LockField;


b)        该锁定与事务无关,只要在客户端保存后事件(AfterBarItemClick)处理即可;


c)        “收料部门”、“收料员”的key可以在IDE设计器中拷贝;


代码如下:


C#


 


public override void AfterBarItemClick(AfterBarItemClickEventArgs e)


{


    base.AfterBarItemClick(e);


    if (e.BarItemKey == "tbSave")


    {


        this.View.LockField("FBase1", true);


        this.View.LockField("FBase2", true);


    }


}


 


 


10.   保存后自动记录收料日志(KDV_stk_ReceiptLog);


根据需求设计收料日志表:


字段


名称


类型


说明


KDV_ID


日志ID


int


自增长


KDV_UserID


操作用户


Int


关联用户表ID


KDV_Date


操作时间


Datetime


缺省getdate


KDV_Content


日志内容


Nvarchar(2000)


 


 


保存有2种方法:


方法1:


a)  在IDE中定义收料日志基础资料;


b)  打开收货单服务插件保存服务类SaveServicePlugIn;


c)  根据收料日志基础资料的元数据定义,创建动态实体对象;


d)  设置对象属性值;


e)  调用BusinessDataService服务的保存方法保存动态实体对象;


代码如下:


C#


 


public override void AfterExecuteOperationTransaction(AfterExecuteOperationTransaction e)


{


    base.AfterExecuteOperationTransaction(e);


 


    MetaDataService metaService = new MetaDataService();


    FormMetadata formMetaData = (FormMetadata)metaService.Load(this.Context, "1823871d-b9cf-4d8b-93af-39c0c37011a5");


    DynamicObjectType dt = formMetaData.BusinessInfo.GetDynamicObjectType();


    DynamicObject obj = new DynamicObject(dt);


    dt.Properties["KDV_UserID_Id"].SetValueFast(obj, this.Context.UserId);


    dt.Properties["KDV_Content"].SetValueFast(obj, "保存");


    ISaveService saveService = ServiceHelper.GetService<ISaveService>();


    saveService.Save(this.Context, new DynamicObject[] { obj });


}


 


 


方法2:


a)  自定义收料日志表;


b)  获取日志的自增长(序列)值;


c)  执行insert;


代码如下:


C#


 


public override void AfterExecuteOperationTransaction(AfterExecuteOperationTransaction e)


{


    base.AfterExecuteOperationTransaction(e);


 


    SequenceReader sequence = new SequenceReader(this.Context);


    int[] ids = (int[])sequence.GetSequence("KDV_stk_ReceiptLog", 1);


 


    int id = ids[0];


 


    string sql = " insert into KDV_stk_ReceiptLog(FID, KDV_UserID, KDV_Content) values (@FID, @KDV_UserID, @KDV_Content) ";


    SqlParam[] sqlParams = new SqlParam[3];


    sqlParams[0] = new SqlParam("@FID", KDDbType.Int64, id);


    sqlParams[1] = new SqlParam("@KDV_UserID", KDDbType.Int64, this.Context.UserId);


    sqlParams[2] = new SqlParam("@KDV_Content", KDDbType.String, "保存");


 


    DBUtils.Execute(this.Context, sql, sqlParams);


}


金蝶k3cloud 二次开发qq群:188617330

————————————————

版权声明:本文为CSDN博主「kuyz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/kuyz1/article/details/45167247


【桌面云】

一、什么是桌面云

什么是桌面云,我们首先要回答一个问题是什么是云计算。关于云计算的定义很多,大家广泛认可的是维基百科上关于云计算的定义,也即:“云计算(cloud computing,台湾译作云端运算),是一种互联网上的资源利用新方式,可为大众用户依托互联网上异构、自治的服务进行按需即取的计算 , 云计算的资源是动态易扩展而且虚拟化的,通过互联网提供”. 桌面云是合乎上述云计算定义的一种云。在 IBM 云计算智能商务桌面(IBM Smart Business Desktop Cloud)的介绍页面看,我们可以看到桌面云的定义是:“可以通过瘦客户端或者其他任何与网络相连的设备来访问跨平台的应用程序,以及整个客户桌面”。也就是说我们只需要一个瘦客户端设备,或者其他任何可以连接网络的设备,通过专用程序或者浏览器,就可以访问驻留在服务器端的个人桌面以及各种应用,并且用户体验和我们使用传统的个人电脑是一模一样的。

二、桌面云的业务价值

桌面云的业务价值很多,除了上面所提到的随时随地访问桌面以外还有下面一些重要的业务价值:

1、集中化的管理

在使用传统桌面的整体成本中,管理维护成本在其整个生命周期中占很大的一部分,管理成本包括操作系统安装配置,升级,修复的成本,以及硬件安装配置,升级,维修的成本,数据恢复,备份的成本,各种应用程序安装配置,升级,维修的成本。在传统桌面应用中,这些工作基本上都需要在每个桌面上做一次,工作量非常大。对于那些需要频繁替换,更新桌面的行业来说,工作量就更大了。例如对于培训行业来说,他们经常需要配置不同的操作系统和运行程序来满足不同培训课程的需要,对于有上百台机器来说,这个工作量已经非常大了,而且这种工作还要经常。

在桌面云解决方案里,管理是集中化的,IT 工程师通过控制中心管理成百上千的虚拟桌面,所有的更新,打补丁都只需要更新一个“基础镜像”就可以了。对于上面所提到的培训中心来说,管理维护就非常简单了:我们只需要根据课程的不同配置几个基础的镜像,然后不同的培训课程的学员可以分别连接到这些不同的基础镜像,而且我们要做任何修改,只需要在这几个基础镜像上进行就可以了,只要重启虚拟桌面学员就可以看到所有的更新,这样就大大节约了管理成本。

2、安全性提高

安全是 IT 工作中一个非常重要的方面,一方面各单位对自己对安全要求,另一方面政府对安全也有些强制要求,一旦违反,后果非常严重。对于企业来说,数据,知识产权就是他们的生命,例如银行系统中的客户的信用卡帐号,保险系统中用户详细信息,软件企业中的源代码等等。如何保护这些机密数据不被外泄是许多公司 IT 部门的经常面临的一个挑战。为此他们采用了各种安全措施来保证数据并不非法使用,例如禁止使用 USB 设备,禁止使用外面电子邮件等等。对于政府部门来说,数据安全也是非常重要的,英国不久前就发生了某政府官员的笔记本丢失,结果保密文件被记者得到,这个官员不得不自己引咎辞职。

在桌面云解决方案里,首先,所有的数据以及运算都在服务器端进行,客户端只是显示其变化的影像而已,所以在不需要担心客户端来非法窃取资料,我们在电影里面看到的商业间谍拿着 U 盘疯狂的拷贝公司商业机密的情况再也不会出现了。其次,IT 部门根据安全挑战制作出各种各样新规则,这些新规则可以迅速的作用于每个桌面。

3、应用更环保

如何保护我们的有限资源,怎么才能消耗更少的能源,这是现在现在各国科学家在不断探索的问题。因为在我们地球上的资源是有限的,不加以保护的话很快会陷入无资源可用之困境。现在全世界都在想办法减少碳排放量,为之也采取了很多措施,例如利用风能等更清洁的能源等。但是传统个人计算机的耗电量是非凡家人的,一般来说,每台传统个人计算机的功耗在 200W 左右,即使它处于空闲状态时耗电量也至少在 100W 左右,按照每天 10 个小时,每年 240 天工作来计算,每台计算机桌面的耗电量在 480 度左右,非常惊人。在此之外,为了冷却这些计算机使用产生的热量,我们还必须使用一定的空调设备,这些能量的消耗也是非常大的。

采用云桌面解决方案以后,每个瘦客户端的电量消耗在 16w 左右,只有原来传统个人桌面的 8%,所产生的热量也大大减少了。

4、总拥有成本的减少

IT 资产的成本包括很多方面,初期购买成本只是其中的一小部分,其它还包括整个生命周期里的管理,维护,能量消耗等方面的成本,硬件更新升级的成本。从上面的描述中我们可以看到相比传统个人桌面而言,桌面云在整个生命周期里的管理,维护,能量消耗等方面的成本大大降低了,那么硬件成本又是怎么样呢?桌面云在初期硬件上的投资是比较大的,因为我们要购买新的服务器来运行云服务,但是由于传统桌面的更新周期是 3 年,而服务器的更新周期是 5 年,所以硬件上的成本基本相当,但是由于软成本的大大降低,而且软成本在 TCO 中占有非常大的比重,所以采用云桌面方案总体 TCO 大大减少了。根据 Gartner 公司的预计,云桌面的 TCO 相比传统桌面可以减少 40%.

三、桌面云的架构

桌面云的基本架构是什么样呢?下面这张图清晰的表面了桌面云的一个基本架构 ( 选自 IBM 云计算智能商务桌面 )

图 1. 桌面云架构示例

1、瘦终端

瘦终端是我们使用桌面云的设备,一般是一个内嵌了独立的嵌入式操作系统,可以通过各种协议连接到运行在服务器上的桌面的设备,为了充分利用已有资源,实现 IT 资产的最大化应用,架构中也支持对传统桌面做一些改造,安装一些插件,使得它们也有能力连接到运行在服务器上的桌面。

2、网络的接入

桌面云提供了各种接入方式供用户连接。用户可以通过有线或者无线网络连接,这些网络既可以是局域网,也可以是广域网,连接的时候即可以使用普通的连接方式,也可以使用安全连接方式。

3、控制台

控制台可以对运行着虚拟桌面的服务器进行配置,例如配置网络连接,配置存储设备等等。控制台还可以监控运行时服务器的一些基础性能指标,例如内存的使用状况,CPU 的使用率等。如果需要监控更多的资源,我们可以使用 IBM 的 Tivoli 相关产品。

4、身份认证

一个企业级应用解决方案,必须有安全控制的解决方案,安全方案中比较重要的是用户的认证和授权。在桌面云中一般是通过 Active Directory 或者 LDAP 这些产品来进行用户的认证和授权的,这些产品可以很方便对用户进行添加,删除,配置密码,设定其角色,赋予不同的角色不同的权限,修改用户权限等操作。

5、操作系统或者应用程序

有一些特定的应用场景下,例如使用的用户是呼叫中心的操作员,他们一般都是使用同一种标准桌面和标准应该,基本上不需要修改。在这种场景下,云桌面架构提供了共享服务的方式来提供桌面和应用。这样可以在特定的服务器上提供更多的服务。

6、应用服务器

桌面云解决方案中,更多的应用方式是把各种应用分发到虚拟桌面,这样客户只需要连到一个桌面就可以使用所有的应用,就好像这些应用安装在桌面上一样,在这种架构下提供给用户的体验是和使用传统的桌面完全一样的。

当然,上图中的架构只是我们的一个参考实现的大概描述,在具体应用中我们应该根据客户的具体情况作出架构中的各种决定。这些考虑的因素主要有客户的类型,客户的规模,客户的工作负载,客户的使用习惯,客户对服务质量的要求作出相应的等等,这是一个比较复杂的过程。

四、桌面云和无盘工作站的区别

无盘工作站是指本的没有硬盘,通过一些网络协议(例如 PXE)等连接到远程的服务器,但是本地还是保留主板等硬件。无盘工作站的程序执行和桌面云一样,也是在服务器端进行的,也可以有效的保证数据的安全性等。那么它们之间有什么区别呢?其实它们从概念到架构都完全不一样,具体区别如下

1.最主要的区别是桌面云可以动态的调整用户所需要的资源,无盘工作站只能分配固定的资源。
2.桌面云可以根据需要定制化个人信息,安装自己需要的程序,也可以让用户不可以做任何修改,而无盘工作站只能运行一个统一的操作系统。
3.桌面云只需要一个能耗很少的瘦客户端设备,而无盘工作站还是需要保留除了硬盘以外传统 PC 所有硬件。
4.桌面云前端设备的配置很简单,对有的设备来说甚至只要安装一个插件就可以运行,无盘工作站前端设备有特殊的要求

五、桌面云现在的发展现状

桌面云的发展当然也离不开各大厂商的支持,其实 IBM,惠普,SUN 等大公司在其中都有很多投入,例如 IBM 的云集算智能商务桌面解决方案,SUN 的 sunray 解决方案等。 也有很多小公司投入其中,例如瑞典 Xcerion 公司便推出了 iCloud 的测试版,这是一款可以提供虚拟桌面服务的平台,该平台可以通过浏览器来运行整个操作系统。与其他厂商相比,IBM 除了提出整体解决方案之外,还提供了许多增值服务服务,例如提供前期的对象有业务环境的评估,减少磁盘使用量的软件等等。

六、桌面云需要解决的一些问题

虽然桌面云有上面各种优点,但是现在阻碍其发展的一个重要的因素是初期投资问题,虽然桌面云的总拥有成本比传统桌面要低 ,但是桌面云初期需要购买服务器,网络,存储等 , 所以初期投资相对传统桌面而言还是比较高的,所以一些企业特别是小型企业对此比较有疑虑。市场上对这种疑虑也作出了反应,其中 IBM 推出的桌面云解决方案中就包括了一种服务器等后台资源驻留在 IBM 内部,由 IBM 来管理,客户只需要通过网络就可以使用桌面,按照客户的使用量来收费的解决方案,这种解决方案对于哪些成长型的小企业来说是一个非常好的消息。(转载自IBM)

七、桌面云应用案例

华为:9人维护的万人桌面云
【通信产业网讯】(记者 卢子月)根据华为与上海市政府年初签署的云计算战略合作协议,上海云计算应用示范中心正式落户华为上海研究所。不同于传统电信设备领域,云计算对于华为来说尚处于起步阶段。目前示范中心的建设情况如何?云计算如何为人们提供服务?带着这样的疑问,记者走进华为上研所的“Z”字形大楼,对云计算应用示范中心进行了探访。

为10000人提供“桌面云”

来到位于华为上海研究所二层的云计算研发室,给记者的第一印象就是安静。由于没有了电脑主机,整个研发室不再有电脑风扇发出的嗡嗡声。每个研发人员的面前摆着一台液晶显示器,显示器的后端装有一个外置Modem大小的黑色盒子。

据工作人员介绍,由于整个研发中心都采用了办公云,因此每个员工的办公桌前不再有电脑的主机部分,而只有一个华为自己开发的瘦客户端和一台显示器。办公系统中所有的处理工作都集中在数据中心进行。在华为上海研究所,有10000人通过办公云来工作,而为这10000人提供服务的是数据中心里的300台服务器。

工作人员为记者演示了办公云提供的无线办公体验。只需要输入自己的账户,用户就可以在任何一台电脑上使用自己的桌面与系统。据工作人员介绍,利用办公云,可以在会议室通过Ipad直接访问自己的桌面与系统,真正实现移动办公。

记者看到,整个办公云的操作过程基本与个人电脑无异。由于经过规划,办公云的网络传输速度能够完全满足研发人员的需求,不会出现因数据中心与客户端网速而延迟的现象。

据悉,华为上海研发中心的桌面云采用7层端到端安全保护措施,在资源集中的同时,安全更加容易管控。

云计算技术的一大好处就是节省资源。对此,工作人员表示,采用办公云令公司的办公成本大幅降低。仅电脑硬件一项,就节约成本70%。而瘦客户端的功耗仅为2W,端到端的功耗也仅为35W,远远低于PC机的200W。

就维护成本而言,采用办公云后,上海研究所的IT维护人员由200个精简到9个,每个人可以满足大约1000人的IT需求,PUE为1.3。

【云桌面】

一、什么是云桌面

云桌面是由DZZ开发并开源的一个全新应用程序,说白了他就是一个WEB桌面,让所有的网页应用程序可以在一个网页上就能使用。

二、云桌面的用途

云桌面可以用作自定义网址导航,盒子导航,应用之家,一站式服务,或者用于几个页面同时呈现在用户面前的切换系统,暂时目前群网只将云桌面用于自定义导航和群网五大系统切换,至于未来应用API开放完全之后,群网将集合各大类应用API用作一站式服务系统。

三、云桌面的市场前景

云桌面是一个看似很单一的一个粘合性网络程序,但未来随着各大API的开放,云桌面将是一种趋势,既然他能和DZ整合,自然也能和导航,微博,WORDPRESS,只要带SQL数据库的网站系统都能整合到一起,也就是说未来出现在用户面前的将不再只是一个单一的应用图标,有可能是动态新闻,也有可能是微博,还有可能是电影视频,订阅的RSS。所以说云桌面的未来市场很大!

更简单点地说,桌面云就是把显示器、鼠标、键盘插在一个盒子上,盒子再通过网线或者无线链接到服务器,你只要一开盒子的电源开关,显示器上就能显示windows系统的桌面,省略了开机过程,也不用担心中病毒需要重新装操作系统一类的维护成本,你只管用就行。而云桌面则是一个模仿windows桌面的web界面。桌面云目前来说只适合于办公,因为受技术限制,云终端与服务器之间的速率是64K,想用来玩游戏及看电影就甭指望了。

转载于:https://www.cnblogs.com/benjaminfee/p/10187875.html


作为一名公司电脑技术管理员,有必要搞懂无盘系统、桌面云、云桌面等技术,然而这些技术很相似,因此拿来跟大家讲讲,如今计算机正进入一个云的时代,比如云计算机、云存储、云桌面等云技术陆续火爆起来,这对于公司运营和管理非常重要。

作为电脑技术员,对无盘系统、云桌面、桌面云之间区别知道多少?

无盘系统

  无盘一般运用在网吧比较多,无盘一般由服务器、工作站客户机、无盘软件、路由器、交换机、网线组成,但是工作站的电脑只是没有硬盘的,其他跟普通电脑是一样的,还有启动的方式也跟普通电脑不一样,是从网卡启动,而且只能在同一个局域网,安装的方法也要复杂一些,需要配置好主服务器,然后从客户端做好镜像包上传到服务器,无盘工作站对客户端的配置要求高,而对服务器的配置要求不高,只是对硬盘跟网卡的读写速度相对要高些。而且无盘服务器如果不用万能镜像包的话,那么配置最好是一样的。

作为电脑技术员,对无盘系统、云桌面、桌面云之间区别知道多少?

  无盘系统的工作站电脑是不做存储的,因为没有硬盘,如果要安装新的软件或更新游戏需要开超级工作站才能对服务器上的镜像进行修改。否则你在客户端的任何操作都不影响系统,重启便跟原来系统一样了。

云桌面

  云桌面是最新的云技术,它一般运用在公司办公自动化管理比较多,而且它只是建立在WEB的基础上,通过手机、电脑、平板、笔记本等都可以进入,不需要另外购买设备。服务器由厂商提供,只需要向他们购买服务器、存储空间、运维费用即可,其他的你只要通过网页输入帐号便可进入云桌面,这样对于公司员工管理,办公等相对比较方便,但是由于云桌面目前的传输速率不高,因此用于玩游戏、看视频等还不适合,只适合于办公等类型。

作为电脑技术员,对无盘系统、云桌面、桌面云之间区别知道多少?

  云桌面对于人们来说可以在任何地方,只要能连接互联网便可以进入云桌面办公,不一定要在公司,这对于公司和员工来说效率更高,而且办公场所可以随意,通过云服务端对于员工的管理也更加方便。

桌面云

  桌面云一般是由瘦客户机或(云终端、网络终端机)等跟服务器组成,而每一个用户只需要通过瘦客户机上插入显示器,键盘鼠标,接入网线便可以连接服务器,桌面是跟服务器一样的,而且瘦客户机上面不做存储,你所操作的只是服务器上面虚拟的桌面,因此这样对数据的安全性更加高,而且瘦客户机的维护、运用、投入成本远远低于普通PC的价格,因此很多大型公司或单位都开始慢慢的投入桌面云,桌面云一般运用在单位、图书馆、公司、培训机构等,运用的比较广,而且不一定要在局域网,在广域网一样可以进入桌面云。

作为电脑技术员,对无盘系统、云桌面、桌面云之间区别知道多少?

总结

  无盘系统只是PC不需要硬盘的电脑,然后系统是从服务器上面的系统镜像通过网卡启动进入,而且只能在局域网使用无盘系统;云桌面就是为了办公的系统,只需要通过浏览器进入的一个Web桌面,从广域网等联入网络的设备都可以进入,比如手机、电脑、笔记本、平板等;桌面云就相当于一个虚拟桌面,将一台服务器的桌面虚拟成N个相同的桌面,不过它必须通过云终端、瘦客户机等设备连接,相当于这些设备取代了普通PC的主机,而且从广域网也可以进入虚拟桌面。


加密方式

    K/3 Cloud V5.0只有软加密这一种加密方式,这一点与K/3 Wise系统的产品的软加密是一致的,不过稍有区别的是。

与K/3 Wise的对比

其一,Cloud的产品唯一码与Wise的软件特征码均为16位,而区别在于,Cloud的产品唯一码均为大写字母,且由三个中横线将其分为三组,例如ABCD-ABCD-ABCD-ABCD,但Wise的软件特征码由连续的十六位字符或数字组成,例如DC7DBE366084D5B3。

其二、这两者都会因为例如更换或增加硬盘,CPU,网卡,主板,可能会使产品唯一码发生或软件特征码发生改变。

其三、许可文件的注册、下载等操作是相同的,并且通过以记事本的形势均可以查到许可的产品类型和具体版本,分别如图-1和图-2。

                                             image.png

图-1 以记事本方式查看K/3 Wise许可文件

 

image.png

图-2以记事本方式查看K/3 Cloud许可文件

加密许可控制

1.2.1 加密许可申请

K/3 Cloud采用许可文件控制的方式,客户使用产品唯一码在金蝶的MOP系统申请许可文件。MOP系统验证核实客户身份和购买合同后,生成客户的许可文件。客户获取许可文件,引入许可文件即可使用,保证整个过程更保密和安全。

 

1.2.2 许可控制规则

K/3 Cloud正式版的许可主要采用大并发控制规则,除了按照业务范畴进行分组划分之外,还单独提供了多组织、多语言等全局特性控制。

K/3 Cloud演示版按照关键业务单据的数量进行控制,超过控制数量则所属分组下的全部子系统均不可进入。

 

1.2.3 其他说明

演示版的控制规则:

1、业务关键单据控制数量为100,基础资料的主数据除了物料500外,其他均是100

2、BOS平台:控制运行时的数量100。

 

产品唯一码及许可操作

2.1  查看产品唯一码

可登录管理中心(administrator的初始密码为888888),点击右上角的’所有功能’图标找到’许可中心’,可看’软件许可引入’,’许可使用状况’,’产品激活’三个按钮,如图3。

image.png

图-3 登录管理中心找到’所有功能’按钮

接着进入’软件许可引入’可进入查看产品唯一码与许可引入的界面,如图-4。

image.png

图-4 查看产品唯一码与许可引入的界面

许可引入与删除

2.2.1 引入

点击上图-4 中的’+’号按钮可浏览选择许可文件,选择完成后点击’引入许可’的按钮即可。引入成功后如图-5。

image.png

图-5 引入许可成功

引入许可成功后可在许可使用状况中查看许可情况,如图-6。

image.png

图-6 查看许可使用状况

2.2.1 删除

K/3 Cloud的许可文件可以通过两种方式进行清理或删除。

方式一:管理中心→所有功能→许可中心→软件许可引入中’清除’的按钮删除许可文件,如图-7至图9。

image.png

图-7 找到清理按钮

 

image.png

图-8 清理许可

image.png

图-9 清理成功,重启IIS

方式二:通过SQL语句进行清理。

首先登录管理中心的创建站点例如192.168.90.113:8000/silverlight/cmc.aspx,找到对应的数据库实体名称,如图-10。

image.png

图-10 查看管理中心对应的数据库实体名称

然后进入数据库服务器的SQL Server Management Studio找到对应的数据库实体,再找到名为dbo.T.BD_PRODUCTMODEL的表,右键选择前1000行,如图-11。

image.png

图-11 查看该表中的内容

在上图-11中,此时是没有引入许可文件的。

下面再次引入许可并查看该表的内容,发现多了一行内容,如图12。

image.png

图-12 引入许可后再次查看该表的内容

多的这一行内容便是由引入的许可文件所生成,为了验证它确实为引入许可文件所生成,下面用delete语句删除该行,如图-13。

image.png

图-13 用delete语句删除该行

可看到一行收到影响,重新查看该表确认改行的内容已被删除,如图-13。

image.png

图-14 确认该行已被删除

我们回到许可引入的界面可以看到许可已被清除,K/3 Cloud回到演示版状态,如图-15。

image.png

图-15 许可已被删除,产品回到演示版

 

 

许可常见问题

Q1:Cloud 5.0引入许可提示“引入失败!未实现该方法或操作”

如图-16。

image.png

图-16 引入许可出错

A1:该问题属于许可文件本身错误,需要重新申请正确的许可引入即可。

 

Q2:Cloud 3.0 许可引入后按提示要求重启系统,访问Cloud提示:引发类型为"Kingdee.BOS.ServiceFacade.ServiceException"

A2:清理缓存可解决问题。

 

Q3:Cloud 3.0许可引入后按提示要求进行了系统重启,访问Cloud有错误:引发类型为:“Kingdee.BOS.ServiceFacade.ServiceException”的异常。

 标识为“BOS_AuthenticationMethodSetting”的业务对象不存在,或者被删除。

A3:管理中心帐套(如果是访问管理中心站点)或者业务中心帐套(如果是访问业务员中心站点)不完全,或者没创建好或者是没升级好等等;可重新新建管理中心或者业务中心的帐套即可。

 

Q4:Cloud 3.0登陆报错:引发类型为"Kingdee.BOS.ServiceFacade.ServiceException"的异常。无法加载许可文件!您可能使用的是无效的许可文件,或者您的软件唯一码有变化,请重新申请许可文件!

A4:产品唯一码发生改变,需要重新注册。原因可能有以下三种:

1、管理中心库被其他环境(测试环境,开发环境等)注册过去了;

2、Cloud 2.0未打最新补丁,双网卡;

3、应用服务器更换硬件(网卡,硬盘,CPU,主板等)(如果你更换的硬件恰好是用来生成产品唯一码的,那么就会变)

 

Q5: 引入许可提示“引入失败!许可文件验证失败(特征码:……不一致)”

A5:原因是在申请许可的时候提交的产品唯一码与管理中心显示产品唯一码不一致所致,需要以正确的产品唯一码重新申请许可。