Python插件

调试

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

链接

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

表单插件

字段解锁

import clr

clr.AddReference("System")
clr.AddReference("System.Core")
clr.AddReference("Kingdee.BOS")
clr.AddReference("Kingdee.BOS.Core")
clr.AddReference("Kingdee.BOS.DataEntity")
clr.AddReference('Kingdee.BOS.ServiceHelper')

from System import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.DynamicForm import *
from Kingdee.BOS.Orm.DataEntity import *
from Kingdee.BOS.ServiceHelper import *


def AfterBindData(e):
    this.View.GetControl("field_sign").Enabled = True

请求第三方系统(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 ""