平台介绍
金蝶云星空平台(企业版)进行插件开发,利用平台IronPython能力,开发python插件。python在平台中,是一种借壳执行的操作方式,故无法使用任何外部的import动作,只能引用内部的组件和实体对象。目前平台使用的是 IronPython 2.7.x 版本,开发时必需严格使用 [Python2.7] 语法及标准库,绝不可运用 Python3.x 的新特性,以免出现兼容性问题。
开发技巧
- 开发组件引用(clr.AddReference) 和 实体对象导入(from XXX import *) 尽量多、全,不用考虑性能问题,避免从哪里复制过来的代码,因为引用和导入的遗漏导致方法无法使用报错
- 区分字段属性的
(标识)
、字段名
、绑定实体属性
,字段预加载事件@OnPreparePropertys 中使用的是(标识)
,DataEntitys取值中使用的是绑定实体属性
- 测试过程中,尽管加上[操作前确认提示]、[操作成功后提示],测试中就会有弹窗反馈,就可以知道有没有走到服务插件代码中;另外需要注意!服务插件重新编辑保存后,单据要关闭重新打开后才生效!
- 有部分数据处理,如实在无法理解到基础库实体对象的方法如何使用,可以尝试在python中使用内置函数解决;如一些基础的字符串转换、处理,无需import导入的动作(服务插件中不支持导入)
实体类型Entity中不存在名为XXX的属性,注意看下属性是归属于当前单据,还是由其他属性引用带入。如果是由其他属性(元素类型为:基础资料)引用,可以将该属性打印或者保存,观察下数据结构再进行获取
# 以下示例仅供参考 F_PVRH_BaseProperty_qtr = ("{0}").format(FEItem['F_PVRH_Base2']['Specification']) # 产品编号 F_PVRH_Creator = ("{0}").format(billObj['F_PVRH_CreatorId']['Name']) # 制单人名称 F_PVRH_Base = ("{0}").format(billObj['F_PVRH_Base']['Name']) # 客户名称 _name = first_bill_obj['Name'][2052] # 多语言文本示例 取得商品名称 2052=>中文
写日志到上机操作日志控制台
log = LogObject() log.pkValue = bill_no + " start" # 唯一标志 log.Description = bill_no + " 推送开始" log.OperateName = "推送开始" log.ObjectTypeId = this.BusinessInfo.GetForm().Id # 操作的业务对象ID log.SubSystemId = this.BusinessInfo.GetForm().SubsysId # 子系统Id log.Environment = OperatingEnvironment.BizOperate # 操作员 LogServiceHelper.WriteLog(this.Context, log)
- 调试常用
- 表单类插件:this.View.ShowMessage("调试信息")
- 服务类插件:raise Exception("调试信息")
- 数据库操作示例
在金蝶云星空中,ExecuteScalar
、Execute
、ExecuteDataSet
和ExecuteDynamicObject
是四个常用的数据库操作方法,它们的区别如下: - ExecuteScalar
- 功能:执行SQL语句并返回结果集的第一行第一列的值。
- 适用场景:用于查询单一值,例如统计结果(如
COUNT
、SUM
等)。 - 返回值:指定的数据类型(如
int
、string
等)。 Python插件代码示例:
ctx = e.Context sql = "SELECT COUNT(1) FROM T_BD_MATERIAL" CNT = DBUtils.ExecuteScalar(ctx, sql, None) return int(CNT)
- Execute
- 功能:执行SQL语句(如
INSERT
、UPDATE
、DELETE
等),返回受影响的行数。 - 适用场景:用于执行修改数据库的操作,如插入、更新或删除数据。
- 返回值:整数,表示受影响的行数。
Python插件代码示例:
sql = """ /*dialect*/ UPDATE T_XXXX_YYENTRY -- 替换为实际的明细表名 SET FNEWVALUE = '新值' -- 替换为需要更新的字段名和值 WHERE FDETAILID = '{0}' -- 替换为明细ID字段名 AND FID = {1} -- 替换为主表ID字段名 """.format( this.View.Model.GetValue("FDETAILID", 0).ToString(), # 明细ID mainTableId # 主表ID ) affected_rows = DBUtils.Execute(this.Context, sql)
- ExecuteDataSet
- 功能:执行SQL语句并返回一个
DataSet
对象,包含一个或多个DataTable
。 - 适用场景:用于查询多行多列的数据,适合需要处理复杂结果集的场景。
- 返回值:
DataSet
对象,可以通过Tables
属性访问具体的数据表。 Python插件代码示例:
sql = "/*dialect*/SELECT FMATERIALID, FNumber FROM T_BD_MATERIAL" ds = DBUtils.ExecuteDataSet(this.Context, sql) dt = ds.Tables[0]
- ExecuteDynamicObject
- 功能:执行SQL语句并返回一个
dynamic
对象,适合查询单行多列的数据。 - 适用场景:查询单行数据,且需要直接访问列值。
- 返回值:一个
dynamic
对象,其中每个属性对应查询结果的一列。 Python插件代码示例:
sql = "/*dialect*/SELECT FMATERIALID, FNumber FROM T_BD_MATERIAL WHERE FMATERIALID = 1" result = DBUtils.ExecuteDynamicObject(this.Context, sql) first = next(iter(result), None) if first: saler_id = first["FMATERIALID"].ToString() create_date = first["FNumber"].ToString()
- 总结
ExecuteScalar
:适合查询单一值。Execute
:适合执行修改操作,返回受影响行数。ExecuteDataSet
:适合查询多行多列数据,返回DataSet
。ExecuteDynamicObject
:适合查询单行多列数据,返回动态对象。示例1
from Kingdee.BOS.ServiceHelper import DBUtils def SomeFunction(e): # 查询销售订单信息 sql = """ /*dialect*/SELECT FSalerId,FCreateDate FROM T_SAL_ORDER WHERE FID = '{0}' """ . format(str(FID)) ds = DBUtils.ExecuteDataSet(ctx, sql) saler_id = ds.Tables[0].Rows[0]["FSalerId"] create_date = ds.Tables[0].Rows[0]["FCreateDate"]
参考代码
# 字段预加载事件
def OnPreparePropertys(e):
# 预加载字段-单据头
e.FieldKeys.Add('FBillNo')
e.FieldKeys.Add('F_PVRH_Base3')
e.FieldKeys.Add('F_PVRH_Base') # 客户
e.FieldKeys.Add('F_PVRH_Text') # 联系人
e.FieldKeys.Add('F_PVRH_Text2') # 电话
e.FieldKeys.Add('F_PVRH_Text1') # 地址
# 预加载字段-单据体(明细信息)
e.FieldKeys.Add('F_PVRH_Base2')
e.FieldKeys.Add('F_PVRH_Qty')
e.FieldKeys.Add('F_PVRH_Text15')
e.FieldKeys.Add('F_PVRH_Text16')
e.FieldKeys.Add('F_PVRH_Remarks2')
# 执行操作事务后事件 AfterExecuteOperationTransaction
def AfterExecuteOperationTransaction(e):
first_bill_obj = e.DataEntitys[0]
bill_no = first_bill_obj['BillNo']
# 上机日志
LogObject1 = LogObject()
LogObject1.pkValue = bill_no + " start"
LogObject1.Description = bill_no + " 进入推送"
LogObject1.OperateName = "推送进入"
LogObject1.ObjectTypeId = this.BusinessInfo.GetForm().Id
LogObject1.SubSystemId = this.BusinessInfo.GetForm().SubsysId
LogObject1.Environment = OperatingEnvironment.BizOperate
LogServiceHelper.WriteLog(this.Context, LogObject1)
for billObj in e.DataEntitys:
F_PVRH_Base = ("{0}").format(billObj['F_PVRH_Base']['Name']) # 一些数据类型需要进入下一级抓取
F_PVRH_Base3 = ("{0}").format(billObj['F_PVRH_Base3']['Name'])
postParam = {
'FBillNo': billObj['BillNo'],
'F_PVRH_Base': tidyString(F_PVRH_Base),
'F_PVRH_Base3': tidyString(F_PVRH_Base3),
'F_PVRH_Text': tidyString(billObj['F_PVRH_Text']),
'F_PVRH_Text2': tidyString(billObj['F_PVRH_Text2']),
'F_PVRH_Text1': tidyString(billObj['F_PVRH_Text1']),
'F_PVRH_Remarks': tidyString(billObj['F_PVRH_Remarks']),
'FEntity': [],
}
FEntity = billObj['FEntity'] # 单据体(明细信息)
for FEItem in FEntity:
F_PVRH_Base2 = ("{0}").format(FEItem['F_PVRH_Base2']['Name'])
F_PVRH_BaseProperty_qtr = ("{0}").format(FEItem['F_PVRH_Base2']['Specification'])
postParam['FEntity'].append({
'F_PVRH_Base2': tidyString(F_PVRH_Base2),
'F_PVRH_Qty': FEItem['F_PVRH_Qty'],
'F_PVRH_Text15': tidyString(FEItem['F_PVRH_Text15']),
'F_PVRH_Text16': FEItem['F_PVRH_Text16'],
'F_PVRH_Remarks2': FEItem['F_PVRH_Remarks2'],
})
try:
postData = {
'type': 'k3_business_bill',
'data': postParam,
}
postData = JsonUtil.Serialize(postData)
post('https://xxx.com/api/push', postData)
except Exception as e:
error_message = str(e)
LogObject2 = LogObject()
LogObject2.pkValue = bill_no + " end"
LogObject2.Description = bill_no + " 结束推送,异常:" + error_message
LogObject2.OperateName = "推送异常"
LogObject2.ObjectTypeId = this.BusinessInfo.GetForm().Id
LogObject2.SubSystemId = this.BusinessInfo.GetForm().SubsysId
LogObject2.Environment = OperatingEnvironment.BizOperate
LogServiceHelper.WriteLog(this.Context, LogObject2)
finally:
LogObject2 = LogObject()
LogObject2.pkValue = bill_no + " end"
LogObject2.Description = bill_no + " 结束推送"
LogObject2.OperateName = "推送结束"
LogObject2.ObjectTypeId = this.BusinessInfo.GetForm().Id
LogObject2.SubSystemId = this.BusinessInfo.GetForm().SubsysId
LogObject2.Environment = OperatingEnvironment.BizOperate
LogServiceHelper.WriteLog(this.Context, LogObject2)
def post(url, postData):
bytes = Encoding.ASCII.GetBytes(postData)
webRequest = HttpWebRequest.Create(url)
webRequest.Method = 'POST'
webRequest.ContentType = 'application/json'
webRequest.Timeout = 1000 * 60 * 10
webRequest.ContentLength = bytes.Length
webRequest.GetRequestStream().Write(bytes, 0, bytes.Length)
webRequest.GetRequestStream().Flush()
webRequest.GetRequestStream().Close()
webResponse = webRequest.GetResponse()
streamReader = StreamReader(webResponse.GetResponseStream(), Encoding.GetEncoding('utf-8'))
result = streamReader.ReadToEnd()
return result
def get(url):
webRequest = HttpWebRequest.Create(url)
webRequest.Method = 'GET'
webResponse = webRequest.GetResponse()
streamReader = StreamReader(webResponse.GetResponseStream(), Encoding.GetEncoding('utf-8'))
result = streamReader.ReadToEnd()
return result
# 中文字符串转换成Unicode编码 避免在 Encoding.ASCII.GetBytes 方法中被转成乱码
def tidyString(s):
return s.encode('unicode_escape').decode('ascii') if isinstance(s, str) else s
参考资料
插件实战开发-新手入门教程-服务插件
【新手入门】插件实操【分享汇总】
Python开发
服务插件 代表性"e.DataEntitys"取值
服务插件如何遍历单据体明细,获取单据体明细行内码?
#使用技巧#Python插件调试技巧及常见报错分析(新手必看,老手看不上的 干货)原创
本文由 ben 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Apr 29, 2025 at 10:51 am