Python插件

调试

  • 表单类插件:this.View.ShowMessage("调试信息")
  • 服务类插件:raise Exception("调试信息")

链接

官方 社区
表单插件 表单插件 表单插件
列表插件 列表插件 列表插件
服务插件 服务插件 服务插件
动态表单插件 动态表单插件
单据转换插件 单据转换插件 单据转换插件

表单插件

请求第三方系统(GET请求)

以"某测试单据" -> 增加"某测试按钮" -> 请求"第三方系统"为例

签出"某测试单据"

菜单 -> 编辑 -> 批量编辑字段属性 -> 表单插件 -> 注册Python脚本 -> 输入脚本名(随便输)-> 贴入以下代码 -> 确定

保存"某测试单据"并签入

# 引入clr运行库
import clr

# 添加对cloud插件开发的常用组件的引用
clr.AddReference("System")
clr.AddReference("System.Web.Extensions")
clr.AddReference("Kingdee.BOS")
clr.AddReference("Kingdee.BOS.Core")
# 导入cloud基础库中的常用实体对象(分命名空间导入,不会递归导入)
import sys
from System import *
from System.Collections.Generic import *
from System.Threading import *
from System.IO import *
from System.Net import *
from System.Text import *
from System.Security.Cryptography import *
from System.Web.Script.Serialization import *
from System.Collections.Generic import Dictionary


def BarItemClick(e):
    if e.BarItemKey == "TXF_tbButton":
        test = get("https://www.baidu.com")
        this.View.ShowMessage(test)

def get(url):
    req = WebRequest.Create(url)
    req.Method = "GET"
    rsp = req.GetResponse()
    stream = rsp.GetResponseStream()
    reader = StreamReader(stream, Encoding.GetEncoding("utf-8"))
    result = reader.ReadToEnd()
    return result

选择未审核或禁用的基础数据(字段设置)

import clr

clr.AddReference('System')
clr.AddReference('System.Data')
clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Kingdee.BOS.App')
clr.AddReference('Kingdee.BOS.ServiceHelper')

from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Core.DynamicForm.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *
from System import *
from System.Data import *
from Kingdee.BOS.App.Data import *
from Kingdee.BOS.ServiceHelper import *


def BeforeSetItemValueByNumber(e):
    key = str(e.BaseDataFieldKey.ToUpper())
    if (key == "F_UZOE_BASE"):
        e.IsShowUsed = False  # 允许选择禁用的数据
        e.IsShowApproved = False  # 允许选择未审核的数据
        e.Filter = ""


def BeforeF7Select(e):
    key = str(e.FieldKey.ToUpper())
    if (key == "F_UZOE_BASE"):
        e.IsShowUsed = False
        e.IsShowApproved = False

默认员工基础资料的数据为当前登录用户(sql查询)


import clr

clr.AddReference('System')
clr.AddReference('System.Data')
clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Kingdee.BOS.App')
clr.AddReference('Kingdee.BOS.ServiceHelper')

from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Core.DynamicForm.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *
from System import *
from System.Data import *
from Kingdee.BOS.App.Data import *
from Kingdee.BOS.ServiceHelper import *

empId = 0

def OnInitialize(e):
    userID = str(this.Context.UserId)
    sqlGetEmpId = (""" select E.FID, E_L.FNAME, P.FPERSONID, U.FUSERID, U.FNAME 
    from T_SEC_USER U 
    inner join t_BD_Person P ON (u.FLINKOBJECT = P.FPERSONID) 
    inner join T_HR_EmpInfo E ON (P.FPERSONID = E.FPERSONID) 
    left join T_HR_EmpInfo_L E_L ON (E.FID = E_L.FID AND E_L.FLOCALEID = 2052) 
    where U.FUserId ={0} """).format(userID)
    global empId
    ds = DBServiceHelper.ExecuteDataSet(this.Context, sqlGetEmpId)
    tab = ds.Tables[0]
    empId = tab.Rows[0]["FID"] if tab.Rows.Count > 0 else 0


def AfterCreateNewData(e):
    global empId
    this.Model.SetItemValueByID("F_UZOE_Base", empId, 0)

判断是新增还是修改(保存)

# 引入clr运行库
import clr

# 添加对cloud插件开发的常用组件的引用
clr.AddReference("System")
clr.AddReference("System.Web.Extensions")
clr.AddReference("Kingdee.BOS")
clr.AddReference("Kingdee.BOS.Core")
from System import *
from System.Collections.Generic import *
from System.Threading import *
from System.IO import *
from System.Net import *
from System.Text import *
from System.Security.Cryptography import *
from System.Web.Script.Serialization import *
from System.Collections.Generic import Dictionary


def BarItemClick(e):
    # 与 this.View.Model.GetValue("Id") 等效
    if this.View.Model.DataObject["Id"] == 0:
        this.View.ShowMessage('新增')
    else:
        this.View.ShowMessage('修改')

文本、基础资料获取值与设置值

基础资料需要设置引用属性

# 引入clr运行库
import clr

# 添加对cloud插件开发的常用组件的引用
clr.AddReference("System")
clr.AddReference("System.Web.Extensions")
clr.AddReference("Kingdee.BOS")
clr.AddReference("Kingdee.BOS.Core")
from System import *
from System.Collections.Generic import *
from System.Threading import *
from System.IO import *
from System.Net import *
from System.Text import *
from System.Security.Cryptography import *
from System.Web.Script.Serialization import *
from System.Collections.Generic import Dictionary


def BarItemClick(e):
    if e.BarItemKey == "UZOE_tbButton":
        # 文本获取和设置值
        test = this.View.Model.GetValue('F_UZOE_Text').ToString()
        this.View.Model.SetValue('F_UZOE_Text', test + '增加点内容')
        # 基础资料获取和设置值,以部门为例
        try:
            departmentName = str(this.View.Model.GetValue('F_UZOE_Base')['Name'])
            this.View.ShowMessage(departmentName)
        except BaseException as e:
            # 没有选部门,默认一个部门,先找到对应的组织,再查部门内码
            # select * from T_ORG_ORGANIZATIONS_L
            # SELECT FDEPTID FROM T_BD_DEPARTMENT where FUSEORGID=1
            this.View.Model.SetValue('F_UZOE_Base', 112233)
            this.View.ShowMessage('默认部门')

值更新事件

需要在字段上勾选"即时触发更新时间"


def DataChanged(e):
    if e.Field.Key.ToUpperInvariant() == 'XXX':
        # e.Row可以指定到具体的明细行
        aaa = this.View.Model.GetValue("aaa", e.Row)
        if str(aaa) = '123':
            this.View.Model.SetValue("bbb", 1, e.Row)

列表插件

  1. 列表菜单新增一个按钮,按钮标识为:UZOE_tbButton_Batch_Update
  2. 找到需要批改的字段,在"功能控制"中设置允许批改
  3. 操作列表增加修改操作并在其他控制中设置权限项
  4. 列表插件中注册Python插件(保存的时候可能会报Python脚本存在语法错误,找不到"Kingdee.BOS.ServiceHelper",忽略)
import clr

clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Kingdee.BOS.ServiceHelper')

from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Core.Bill.PlugIn import *
from Kingdee.BOS.Core.Permission import *
from Kingdee.BOS.ServiceHelper import *


def BarItemClick(e):
    if e.BarItemKey == "UZOE_tbButton_Batch_Update":
        businessObj = BusinessObject()
        formId = str(this.View.BillBusinessInfo.GetForm().Id)
        businessObj.Id = formId
        if this.ListView.CurrentSelectedRowInfo == None:
            this.View.ShowErrMessage("请选择需要批改的行")
            return
        param = BillShowParameter()
        param.FormId = FormIdConst.BD_BulkEdit
        this.View.ShowForm(param)

点击列表菜单打开外链

参考

import clr

clr.AddReference('mscorlib')
clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')

from Kingdee.BOS.Core.Bill.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.Args import *
from Kingdee.BOS.JSON import *
from Kingdee.BOS.Util import *
from System import *


def BarItemClick(e):
    if e.BarItemKey in ["按钮标识"]:
        # 直接打开
        # this.View.AddAction("ShowWebURL", 'https://www.baidu.com')
        # 方式二:弹窗打开Url地址
        webobj = JSONObject()
        webobj["source"] = 'https://www.baidu.com'
        webobj["height"] = 600
        webobj["width"] = 800
        webobj["isweb"] = True
        webobj["title"] = "百度一下"
        this.View.AddAction("ShowKDWebbrowseForm", webobj)
        this.View.SendDynamicFormAction(this.View)

服务插件

请求第三方系统(POST请求)

以"审核通过"后请求"第三方系统"为例

操作列表 -> 找到"审核" -> 编辑 -> "其他控制"页签 -> 服务插件 -> 注册Python脚本 -> 输入脚本名(随便输)-> 贴入以下代码 -> 确定


# 引入clr运行库
import clr

# 添加对cloud插件开发的常用组件的引用
clr.AddReference('System')
clr.AddReference("System.Web.Extensions")
clr.AddReference('System.Data')
clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')
# 导入cloud基础库中的常用实体对象(分命名空间导入,不会递归导入)
import sys
from System import *
from System.Data import *
from System.Collections.Generic import *
from System.Threading import *
from System.IO import *
from System.Net import *
from System.Text import *
from System.Security.Cryptography import *
from System.Web.Script.Serialization import *

from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Core.DynamicForm.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *


# 加载指定字段到实体中,因为我们加载的时候只取到部分重要的数据包,可能会有一部分数据没加载进去,通过该事件确保我们需要的字段被加载进来
def OnPreparePropertys(e):
    e.FieldKeys.Add("BillNo")
    e.FieldKeys.Add("Date")
    e.FieldKeys.Add("DocumentStatus")
    e.FieldKeys.Add("SAL_OUTSTOCKENTRY")


# 操作执行后(事务内)事件,在操作处理完毕,未提交事务前触发
# 获取的数据是修改后的数据,可以抛出异常回滚事务
def EndOperationTransaction(e):
    try:
        DBId = str(this.Context.DBId)
        # 正式环境
        if DBId == 'aa2022119f1bbd':
            url = 'https://www.baidu.com?token=aabbcc'
        else:
            url = 'https://beta.baidu.com?token=eeffgg'

        d = dict()

        for billObj in e.DataEntitys:
            d['code'] = str(billObj["BillNo"])
            d['goods_delivery_time'] = str(billObj["Date"])
            d['status'] = str(billObj["DocumentStatus"])
            d['order_code'] = list()

            # 如果字段是一个基础资料类型,我们会获取到一个DynamicObject,需要进一步获取它的名称或编码等
            # 需要在引用属性中设置相关的字段,如:Number、Name等
            d['xxx_code'] = billObj['xxx']['Number']

            for detailInfo in billObj['SAL_OUTSTOCKENTRY']:
                d['order_code'].append({"order_code" : str(detailInfo["SOORDERNO"])})

        res = post(url, d)
        if str(res['code']) == '0':
            print('success')
        else:
            print('fail')

    except BaseException as e:
        pass



def post(url, data):
    req = WebRequest.Create(url)
    req.Method = "POST"
    buf = Encoding.GetEncoding("UTF-8").GetBytes(Json().encode(data))
    req.Accept = "application/json"
    req.ContentType = "application/json; charset=UTF-8"
    req.ContentLength = buf.Length
    stream = req.GetRequestStream()
    stream.Write(buf, 0, buf.Length)
    stream.Close()
    res = req.GetResponse()
    reader = StreamReader(res.GetResponseStream(), Encoding.GetEncoding("utf-8"))
    returnData = Json().decode(reader.ReadToEnd())
    reader.Close()
    res.Close()

    return returnData

自定义验证器


import clr

clr.AddReference('System')
clr.AddReference('System.Data')
clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')

from System import *
from System.Data import *
from System.Collections.Generic import List
from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Log import *
from Kingdee.BOS.Core.Validation import *


def OnAddValidators(e):
    extMoProducTypeValidator = ExtMoProducTypeValidator()
    extMoProducTypeValidator.EntityKey = "FBillHead"
    extMoProducTypeValidator.TimingPointString = ",Save"
    e.Validators.Add(extMoProducTypeValidator)


class ExtMoProducTypeValidator(AbstractValidator):
    def Validate(self, dataEntities, validateContext, ctx):
        for bill in dataEntities:
            billId = str(bill["Id"])
            entryData = bill.DataEntity["TreeEntity"]
            l = []
            for entryInfo in entryData:
                seq = entryInfo["Seq"]
                if seq in l:
                    errorInfo = ValidationErrorInfo(" ", billId, bill.DataEntityIndex, bill.RowIndex, billId, "明细表体存在分录行号重复,不允许保存", "", ErrorLevel.Error)
                    validateContext.AddError(None, errorInfo)
                else:
                    l.append(seq)

提示信息


def AfterExecuteOperationTransaction(e):
    # 这里获取单据数据包方式和前面一样
    for billObj in e.DataEntitys:
        billId = billObj["Id"]  # 单据ID
        billNo = billObj["BillNo"]  # 单据编号
        # 追加自定义提示信息,参考如下
        result = OperateResult()
        result.SuccessStatus = True
        # result.Name = "关键字"
        # result.PKValue = "单据ID"
        # result.Number = "单据编号"
        result.Message = "提示信息"
        this.OperationResult.OperateResult.Add(result)

DynamicObject数据包赋值


for rObj in entity:
    # 物料Id
    matId = 1001

    # --------------------------------------------------------
    # 根据物料Id获取物料数据包,使用字段标识(服务插件)
    matFld = this.BusinessInfo.GetField("FMaterialId")
    # 根据物料Id获取物料数据包,使用字段标识(表单插件)
    # matFld = this.View.BillBusinessInfo.GetField("FMaterialId")
    matObj = BusinessDataServiceHelper.LoadSingle(this.Context, matId, matFld.RefFormDynamicObjectType)
    # --------------------------------------------------------

    # 注意:对资料类型的字段赋值时,需要2行代码,既要给实体里面资料ID赋值,也要给资料数据包赋值
    rObj["MaterialId_Id"] = matId  # 物料Id赋值
    rObj["MaterialId"] = matObj  # 物料数据包赋值

# 如果是表单插件,修改完实体数据包之后,要执行UpdateView刷新界面,界面才会显示更新后的数据
this.View.UpdateView("FBillNo")  # 刷新部分字段,如:单据编号
this.View.UpdateView("FPOOrderEntry")  # 刷新整个单据体

执行操作后刷新字段

以保存操作为例

操作列表 -> "保存" -> 编辑 -> 操作后刷新字段 -> 配置相关字段 -> 确定

获取自增ID

单据种子

基础资料种子

数据库种子

import clr

clr.AddReference('Kingdee.BOS.ServiceHelper')

from Kingdee.BOS.ServiceHelper import *

# 单据种子
# 获取表的种子值=获取表的自增Id=获取表的自增主键值,每获取一次,种子值就会加1,以确保主键值不会重复产生
# 种子值不是来自于当前数据表,而是来自于一个单独的种子表,一个数据表对应一个种子表,种子表名通常为数据表名的首字母t改为z
# 例如,数据表T_SAL_OUTSTOCKTRACE对应的种子表名为Z_SAL_OUTSTOCKTRACE
seqValue = DBServiceHelper.GetSequenceInt32(this.Context, 'T_SAL_OUTSTOCKTRACE', 1)[0]

# 基础资料种子
# 所有的基础资料的主表(单据头表)都是统一使用同一个种子表:Z_BAS_ITEM,基础资料的其它表则仍然使用各自的种子表
seqValue = DBServiceHelper.GetSequenceInt32(this.Context, 'T_BAS_ITEM', 1)[0]

常用属性和方法

Context上下文对象


this.Context.UserId  # 当前用户ID
this.Context.UserName  # 当前用户名称
this.Context.UserPhone  # 当前用户手机号
this.Context.CurrentOrganizationInfo.ID  # 当前组织Id
this.Context.CurrentOrganizationInfo.Name  # 当前组织名称
this.Context.DBId  # 当前数据中心Id
this.Context.DataCenterNumber  # 当前数据中心编码
this.Context.DataCenterName  # 当前数据中心名称
this.Context.IpAddress  # 客户端本机网络信息:IP、MAC等
this.Context.ClientType # 客户端类型
this.Context.ServiceType # 服务类型

判断是否是WebApi请求

请求方式 this.Context.ClientType this.Context.ServiceType WebType.WebService ClientType.WebApi
API WebApi WebService WebService WebApi
浏览器 Html WebSite WebService WebApi
客户端 WPF WebSite WebService WebApi
# 判断是否是WebApi请求
if this.Context.ClientType == ClientType.WebApi:
    pass

日期相关


# 当前日期
print(str(DateTime.Now.Date))

# 昨天
print(str(DateTime.Now.Date.AddDays(-5)))

# 字段转日期格式
Convert.ToDateTime(billObj["XXX_DATE"]).Date

# 示例
t1 = DateTime.Parse(str(time1))  # 要注意判断字段为空:time1=None的情况

t2 = DateTime.Parse(str(time2))  # 要注意判断字段为空:time2=None的情况

ts = t1 - t2  # 计算日期差

onlyDays = ts.Days  # 整数相差天数,向下取整,例如,1天X小时,都是1天

totalDays = ts.TotalDays  # double类型,精确的小数天数

AfterXDaysDate = str(t2.AddDays(x))  # t2加上x天之后的日期,若x为负数,则标识x天前的日期

View视图层对象(服务类的插件没有这个)


# View的常用属性
this.View.BillBusinessInfo  # 单据的业务逻辑元数据,元数据包含的信息很多,下面列了一下常用的信息
this.View.BillBusinessInfo.GetBillNoField().FieldName  # 单据编号字段名
this.View.BillBusinessInfo.GetBillStatusField().FieldName  # 单据状态字段名
this.View.BillBusinessInfo.GetBillTypeField().FieldName  # 单据类型字段名
this.View.BillBusinessInfo.GetForm().Id  # 单据FormId
this.View.BillBusinessInfo.MainOrgField.FieldName  # 主业务组织字段名
this.View.BillBusinessInfo.GetEntity("FBillHead").TableName  # 单据头表名
this.View.BillBusinessInfo.GetEntity("实体标识").TableName  # 实体主表名
this.View.BillBusinessInfo.GetEntity("实体标识").SplitTables  # 实体所有拆分表
this.View.OpenParameter  # 表单入口参数
this.View.OpenParameter.Status  # 当前界面状态:0-新增 1-查看 2-修改
this.View.OpenParameter.GetCustomParameter("参数标识")  # 获取单据打开传入的参数
this.View.ParentFormView  # 获取父页面的View
this.View.ParentFormView.BillBusinessInfo.GetForm().Id  # 父页面的FormId,用来判断单据是从哪里进来的②View的常用方法

# View的常用方法
this.View.GetFormTitle()  # 获取单据标题
this.View.SetFormTitle(LocaleValue("新标题"))  # 修改单据标题
this.View.GetFormOperation("操作代码")  # 获取单据的一个操作实例对象
this.View.InvokeFormOperation("操作代码")  # 触发单据的某个操作:保存、提交、审核、关闭等!
this.View.GetControl[控件类]("控件标识")  # 获取单据上的控件:按钮、菜单等,可用来设置控件的状态(可见性,锁定性等)
this.View.InvokeFieldUpdateService("字段标识", 行号)  # 触发字段值更新,单据头字段行号填0

# 触发实体服务规则,下面2行代码
obj = BOSActionExecuteContext(this.View)
this.View.RuleContainer.RaiseDataChanged("字段标识", 字段所在实体行数据包, obj)  # 触发实体服务规则

# 显示3种提示信息
this.View.ShowMessage("绿色背景提示信息")  # 显示正常提示信息
this.View.ShowWarnningMessage("黄色背景提示信息")  # 显示警告提示信息
this.View.ShowErrMessage("红色背景提示信息")  # 显示错误提示信息

# 刷新界面数据,修改实体数据包后需要刷新重新读取,必须传标识,不建议粗暴刷新整个单据
this.View.UpdateView("字段标识/单据体标识")  # 刷新界面数据

Model单据数据模型

this.View.Model  # 单据数据模型,很重要,单据的实体数据包获取从这里开始,结合上文
this.View.Model.DataObject  # 单据的完整数据包,相当于前面讲的单据头实体数据包
this.View.Model.GetEntryCurrentRowIndex("单据体标识")  # 获取单据体当前焦点行号
this.View.Model.GetEntryRowCount("单据体标识")  # 获取单据体行数
this.View.Model.CreateNewEntryRow("单据体标识")  # 为单据体新增一行
this.View.Model.BatchCreateNewEntryRow("单据体标识", x)  # 批量为单据体新增x行
this.View.Model.InsertEntryRow("单据体标识", i)  # 在第i行前插入1行
this.View.Model.DeleteEntryRow("单据体标识", i)  # 删除第i行
this.View.Model.DeleteEntryData("单据体标识")  # 清空整个单据体数据#获取第x行(单据头不传x)某字段的值,不同字段类型返回不同类型数据,参考第3篇讲解
this.View.Model.GetValue("字段标识", x)  # 更新第x行(单据头不传x)某字段的值,不同字段类型赋值不同类型数据,参考第3篇讲解
this.View.Model.SetValue("字段标识", 字段值, 行号)
this.View.Model.SetItemValueByID("字段标识", "id", x)  # 用资料内码id更新第x行资料字段的值
this.View.Model.SetItemValueByNumber("字段标识", "id", x)  # 用资料编码更新第x行资料字段的值

ListView列表视图层对象


this.ListView.BillBusinessInfo.GetForm().Id  # 当前列表对应的单据标识
this.ListView.OpenParameter  # 列表入口参数
this.ListView.OpenParameter.HideListMenu = True  # 隐藏列表菜单
this.ListView.OpenParameter.IsShowFilter = True  # 打开列表自动弹出过滤框,默认为False
this.ListView.OpenParameter.IsShowQuickFilter = False  # 是否显示快捷过滤
this.ListView.OpenParameter.FilterSchemeId  # 当前过滤方案ID
this.ListView.OpenParameter.IsTrackBillList()  # 是否上下查列表

# 获取自定义参数,可获取发布主控台的参数,例如,将不同单据类型发布成不同列表
this.ListView.OpenParameter.GetCustomParameter("参数标识")
this.ListView.SelectedRowsInfo  # 当前列表上被选中的行记录,复选框勾选的记录集合
this.ListView.CurrentSelectedRowInfo  # 当前列表上当前选择行数据,即焦点行
this.ListView.CurrentPageRowsInfo  # 当前列表页所有单据的行信息
grid = this.View.GetControl[EntryGrid]("FList")  # 获取列表表格控件对象
selectedRowIndexs = List[int]()
selectedRowIndexs.Add(1)
selectedRowIndexs.Add(3)
grid.SelectRows(selectedRowIndexs.ToArray())
grid.SetRowHeight(80)  # 设置行高

ListModel列表数据模型


this.ListModel  # 获取列表数据模型
selectedRowsInfo = this.ListView.SelectedRowsInfo
this.ListModel.GetData(selectedRowsInfo)  # 获取选中的数据
selectedRowsInfo.GetPrimaryKeyValues()  # 获取选中行所有单据ID,字符串数组
selectedRowsInfo.GetEntryPrimaryKeyValues()  # 获取所有选中行所有明细ID,字符串数组
this.ListModel.FieldKeyMap  # 列表上显示的字段,其字段名 FieldName 和字段标识Key的对应关系
this.ListModel.FilterParameter  # 列表过滤参数对象
this.ListModel.FilterParameter.CustomFilter  # 列表过滤框实体数据包
this.ListModel.FilterParameter.FilterRows  # 列表条件过滤行数据
this.ListModel.FilterParameter.FilterString  # 列表条件过滤数据系统自动转换出的条件表达式
this.ListModel.GlobalParameter  # 单据参数配置中配置的单据全局参数
this.ListModel.ParameterData  # 用户参数实体数据包,选项菜单界面的配置数据
this.ListModel.Header  # 列表表头对象
this.ListModel.Header.GetChilds()  # 列表所有的列头集合
this.ListModel.Limit  # 当前列表每页行数
this.ListModel.StartRow  # 开始行索引,从0开始
this.ListModel.Refresh()  # 刷新列表
this.ListModel.RefreshByFilter()  # 根据过滤条件,重新取数,刷新列表

操作插件常用属性及方法


this.BusinessInfo.GetForm().Id     #执行当前操作的单据FormId
this.BusinessInfo.GetBillNoField().FieldName       #单据编号字段名
this.BusinessInfo.GetBillStatusField().FieldName       #单据状态字段名
this.BusinessInfo.GetBillTypeField().FieldName     #单据类型字段名
this.BusinessInfo.MainOrgField.FieldName       #主业务组织字段名
this.BusinessInfo.GetEntity("FBillHead").TableName     #单据头表名
this.BusinessInfo.GetEntity("实体标识").TableName      #实体主表名
this.BusinessInfo.GetEntity("实体标识").SplitTables        #实体所有拆分表
this.BusinessInfo.GetField("字段标识")     #获取字段元素

this.FormOperation.Operation               #当前执行的操作代码    
this.FormOperation.OperationName.GetString(2052)               #当前执行的操作中文名称    
this.FormOperation.OperationId             #当前执行的操作Id    
this.FormOperation.PermissionItemId                #当前操作绑定的权限项Id    
this.FormOperation.ServicePlugins              #当前执行操作下所有的操作服务插件集合

this.Option        #当前操作执行时的选项参数,代码调用单据操作时,也可以通过此变量传入自定义参数
this.Option.ContainsVariable("参数标识")        #判断是否存在某个参数,返回true或false
DIC=this.Option.GetVariables()        #获取所有的参数,字典类型,根据参数标识获取
a=DIC["参数标识"]       #获取某个参数
IsIgnoreWarning=DIC["IgnoreWarning"]        #是否忽略警告提示,返回true或false
selectRows=DIC["_BillOperationSelectedRows_"]       #如果是单据体行操作可以从这里获取选中行
for r in selectRows:
    billId=r["PrimaryKeyValue"]     #单据ID
    billId=r["EntryPrimaryKeyValue"]        #单据体分录ID,若为整单操作,此值为null

Json封装

在金蝶环境中运行的Python不能引入Python扩展,所以需要用到自定义JSON类

class Json:
    def __init__(self):
        pass

    def __json_object(self, tokener):
        obj = {}
        if tokener.cur_token() != '{':
            raise Exception('Json must start with "{"')

        while True:
            tokener.next()
            tk_temp = tokener.cur_token()
            if tk_temp == '}':
                return {}

            if not isinstance(tk_temp, str):
                raise Exception('invalid key %s' % tk_temp)
            key = tk_temp
            tokener.next()
            if tokener.cur_token() != ':':
                raise Exception('expect ":" after "%s"' % key)

            tokener.next()
            val = tokener.cur_token()
            if val == '[':
                val = self.__json_array(tokener)
            elif val == '{':
                val = self.__json_object(tokener)
            obj[key] = val

            tokener.next()
            tk_split = tokener.cur_token()
            if tk_split == ',':
                continue
            elif tk_split == '}':
                break
            else:
                if tk_split is None:
                    raise Exception('missing "}" at at the end of object')
                raise Exception('unexpected token "%s" at key "%s"' % (tk_split, key))
        return obj

    def __json_array(self, tokener):
        if tokener.cur_token() != '[':
            raise Exception('Json array must start with "["')

        arr = []
        while True:
            tokener.next()
            tk_temp = tokener.cur_token()
            if tk_temp == ']':
                return []
            if tk_temp == '{':
                val = self.__json_object(tokener)
            elif tk_temp == '[':
                val = self.__json_array(tokener)
            elif tk_temp in (',', ':', '}'):
                raise Exception('unexpected token "%s"' % tk_temp)
            else:
                val = tk_temp
            arr.append(val)

            tokener.next()
            tk_end = tokener.cur_token()
            if tk_end == ',':
                continue
            if tk_end == ']':
                break
            else:
                if tk_end is None:
                    raise Exception('missing "]" at the end of array')
        return arr

    def encode(self, data):  # encode的兼容性有待调整且会数字化bool以及字符串化None
        s = '{'
        for k, v in data.items():
            if type(v) is bool:
                v = int(v)
            elif v is None:
                v = ''

            # 兼容数组+字典场景
            if type(v) is list:
                temp = '['
                for i in v:
                    temp = temp + self.encode(i) + ','
                v = temp.rstrip(",") + ']'

            if type(v) is str and (len(v) == 0 or v[0] not in '{['):
                s = s + '"{0}":"{1}",'.format(k, v)
            else:
                s = s + '"{0}":{1},'.format(k, v)
        return s.rstrip(",") + "}"

    def decode(self, json_str):
        tokener = Json.__Tokener(json_str)
        if not tokener.next():
            return None
        first_token = tokener.cur_token()

        if first_token == '{':
            decode_val = self.__json_object(tokener)
        elif first_token == '[':
            decode_val = self.__json_array(tokener)
        else:
            raise Exception('Json must start with "{"')
        if tokener.next():
            raise Exception('unexpected token "%s"' % tokener.cur_token())
        return decode_val

    class __Tokener:  # Tokener 作为一个内部类
        def __init__(self, json_str):
            self.__str = json_str
            self.__i = 0
            self.__cur_token = None

        def __cur_char(self):
            if self.__i < len(self.__str):
                return self.__str[self.__i]
            return ''

        def __move_i(self, step=1):
            if self.__i < len(self.__str):
                self.__i += step

        def __next_string(self):
            outstr = ''
            trans_flag = False
            self.__move_i()
            while self.__cur_char() != '':
                ch = self.__cur_char()
                if ch == '\\':
                    trans_flag = True
                else:
                    if not trans_flag:
                        if ch == '"':
                            break
                    else:
                        trans_flag = False
                outstr += ch
                self.__move_i()
            return outstr

        def __next_number(self):
            expr = ''
            while self.__cur_char().isdigit() or self.__cur_char() in ('.', '+', '-'):
                expr += self.__cur_char()
                self.__move_i()
            self.__move_i(-1)
            if '.' in expr:
                return float(expr)
            else:
                return int(expr)

        def __next_const(self):
            outstr = ''
            while self.__cur_char().isalpha():
                outstr += self.__cur_char()
                self.__move_i()

            self.__move_i(-1)

            outstr = str(outstr).lower()

            if outstr in ('true', 'false', 'null', 'none'):
                return {'true': True, 'false': False, 'null': None, 'none': None}[outstr]
            raise Exception('Invalid const "%s"' % outstr)

        def next(self):
            is_white_space = lambda a_char: a_char in ('\x20', '\n', '\r', '\t')

            while is_white_space(self.__cur_char()):
                self.__move_i()

            ch = self.__cur_char()
            if ch == '':
                cur_token = None
            elif ch in ('{', '}', '[', ']', ',', ':'):
                cur_token = ch
            elif ch == '"':
                cur_token = self.__next_string()
            elif ch.isalpha():
                cur_token = self.__next_const()
            elif ch.isdigit() or ch in ('.', '-', '+'):
                cur_token = self.__next_number()
            else:
                raise Exception('Invalid symbol "%s"' % ch)
            self.__move_i()
            self.__cur_token = cur_token

            return cur_token is not None

        def cur_token(self):
            return self.__cur_token


d = dict()

d['aaa'] = 'bbb'
d['ccc'] = ['ddd', 'eee']
d['fff'] = {"ggg": "hhh", "iii": {"jjj": "kkk", "lll": ["mmm", False, None], 'uuu': True, 'vvv': None}, "sss": False,
            "ttt": None}
d['ooo'] = [111, +222, -333, 3.1415, 'ppp']
d['qqq'] = True
d['rrr'] = None

jsonData = Json().encode(d)
print(jsonData)
data = Json().decode(jsonData)
print(data)

错误日志

通常在应用对应的日期文件夹下

  • C:\Program Files (x86)\Kingdee\K3Cloud\WebSite\App_Data\Log\2022-01-01

自定义错误日志

  • 建议用Error(),如果用Info()的话,需要调整错误级别
  • C:\Program Files (x86)\Kingdee\K3Cloud\WebSite\Web.config
  • 搜索log4net的配置,要把Level的值从ERROR改为INFO或者ALL
  • 需要重启IIS
import clr

clr.AddReference('System')
clr.AddReference('System.Data')
clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Kingdee.BOS.App')
clr.AddReference('Kingdee.BOS.Contracts')
clr.AddReference('Kingdee.BOS.ServiceHelper')
clr.AddReference('Kingdee.BOS.Log')

from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Orm import *
from Kingdee.BOS.Contracts import *
from Kingdee.BOS.App import *
from Kingdee.BOS.Core.DynamicForm.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *
from System import *
from System.Data import *
from System.Text import *
from System.Collections import *
from Kingdee.BOS.App.Data import *
from System.Collections.Generic import List
from Kingdee.BOS.ServiceHelper import *
from Kingdee.BOS.Log import *


def AfterExecuteOperationTransaction(e):
    try:
        idList = List[object]()
        idList.Add('123')
        pkArray = idList.ToArray()
        formID = this.BusinessInfo.GetForm().Id
        meta = MetaDataServiceHelper.Load(this.Context, formID)

        submitOption = OperateOption.Create()
        # 自动提交配置忽略警告信息
        submitOption.SetVariableValue("IgnoreWarning", True)

        subResult = BusinessDataServiceHelper.Submit(this.Context, meta.BusinessInfo, pkArray, "Submit", submitOption)
        if (subResult.IsSuccess == True):
            auditOption = OperateOption.Create()
            # 自动审核配置忽略警告信息
            auditOption.SetVariableValue("IgnoreWarning", True)
            auditResult = BusinessDataServiceHelper.Audit(this.Context, meta.BusinessInfo, pkArray, auditOption)
            if (auditResult.IsSuccess == True):
                return
            auditErrorMessage = ''
            for errMsg in auditResult.ValidationErrors:
                auditErrorMessage += errMsg.Message
            raise BaseException(auditErrorMessage)

        subErrorMessage = ''
        for errMsg in subResult.ValidationErrors:
            subErrorMessage += errMsg.Message
        raise BaseException(subErrorMessage)

    except BaseException as ex:
        # 记录Error日志到,C:\Program Files (x86)\Kingdee\K3Cloud\WebSite\App_Data\Log\{date}
        Logger.Error('sal_order', 'submit_approval_error', KDException("?", str(ex)))

results matching ""

    No results matching ""