
学习了两个多月,调试插件真的不是很方便,定位问题显得很难,因此查阅了大量的社区文章,结合自身对python的认知,做了个能定位错误位置的账表插件,同时也抛出一些问题,希望能得到一些帮助,首先上问题:
1 - python如何安装和调用第三方模块?金蝶云使用的是2.6版的ironpython,调用的方法我还不熟悉,没法使用第三方模块大大限制了python的功能,或者如何将ironPython换成3.4版本的(好像没必要,我比较喜欢的是3.5以上版本的dict有了顺序的功能)
2 - 如果调用的数据库表函数或者存储过程报错,定位到的是运行sql的语句,但报的错误完全看不出是sql错误,这里要如何实现能明显看出是sql错误
3 - 如何在更新表列的时候,在文本列中添加单引号(将sql语句完整写入表的一个列中)
c#我不太熟悉,有些系统应该提供有的功能我自己去实现了
然后是插件的具体情况:
描述:简单账表作为报表使用,这里有加一个功能:就是形成一个报表使用的记录表,定义一个存储过程,将报表的使用情况记录下来,用来管理报表的使用率(whoCheck函数)
插件只处理报表显示的功能,报表业务逻辑方面封装到数据库中的表函数或者存储过程中
具体代码:
```python
#-*- encoding:utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding("GB2312")
import clr
clr.AddReference("System")
clr.AddReference("Kingdee.BOS")
clr.AddReference("Kingdee.BOS.Core")
clr.AddReference("Kingdee.BOS.DataEntity")
clr.AddReference("Kingdee.BOS.App")
clr.AddReference("Kingdee.BOS.Contracts")
clr.AddReference("Kingdee.BOS.ServiceHelper")
from Kingdee.BOS import *
from Kingdee.BOS.Contracts import *
from Kingdee.BOS.Contracts.Report import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Metadata import *
from Kingdee.BOS.Core.Report import *
from Kingdee.BOS.Core.SqlBuilder import *
from Kingdee.BOS.App.Data import *
from Kingdee.BOS.Orm.DataEntity import *
from System import *
from System.ComponentModel import *
from System.Collections.Generic import*
from System.Text import *
from System.Threading.Tasks import *
from Kingdee.BOS.ServiceHelper import *
""" 简单账表插件说明:
报错已经能到行数了,差不多了,有一个报错是金蝶云的问题,如果sql报错,反映不出来,你都不知道是C#函数问题还是sql报错
逻辑: 1 - 在形成报表过程中,将查询用户,查询参数等信息写入到一个表中,方便统计报表的使用率
2 - 插件仅做显示的逻辑,具体业务逻辑用存储过程或者表函数实现
"""
class SESSION:
""" 辅助类 新开发的报表要在__init__中填写报表名称,以及过滤参数的标识字符,在形成sql中用到
"""
def __init__(self, ctx=None, userID=None):
self.RPTNAME = "零售职员开单出库查询"
self.sessionid = DateTime.Now.ToString("yyyyMMddHHmmssfffff");
self.ctx = ctx
self.userID = userID
self.keys = ['F_JML_SDate','F_JML_EDate','F_JML_EMPTYPE'] # [key1,key2,...] 没设置过滤参数的留空
self.filters = [] # [v1,v2,...]会根据keys自动补上key对应的值
self.cols = [] # 用做报表列标题陈列,带有_的列标题,按组合表头处理
self.tempTableName = None # 如报错善后用的,貌似不太行。。。
def getFilters(self, key=None, filter=None, reset=0): # 过滤参数获取,过滤界面的字段作为KEY
""" 值类型/私有枚举类型,直接获得值
基础资料类型,通过id获取
"""
if reset == 1:
self.filters = []
if not self.filters and self.keys:
if filter is None :
raise Exception("插件错误:获取过滤参数需要输入filter参数")
pyfilter = filter.FilterParameter.CustomFilter;
if pyfilter is not None:
pass
self.filters.append(pyfilter["F_JML_SDate"].ToString())
self.filters.append(pyfilter["F_JML_EDate"].ToString())
#self.filters.append(pyfilter["F_JML_BRANCHID"]['id'].ToString()) # 基础资料获取ID
self.filters.append(pyfilter["F_JML_EMPTYPE"].ToString()) # 私有枚举类型,直接获得值
self.filters[-1] = 0 if self.filters[-1] == "" or self.filters[-1] is None else self.filters[-1]
if key and self.keys:
return self.filters[self.keys.index(key)]
else:
return self.filters
def getParameters(self):
return {key: self.filters[i] for i, key in enumerate(self.keys)}
def getColumns(self, tableName):
sql = ("""
SELECT T1.system_type_id as FDateTypeId , t3.name AS FDateType,t1.name AS FName
FROM sys.columns t1
INNER JOIN sys.objects t2 ON t2.object_id = t1.object_id
INNER JOIN sys.types t3 ON t3.user_type_id=t1.user_type_id
WHERE t2.name='{0}' AND t2.type='u'
""").format(tableName);
dcObj = DBUtils.ExecuteDynamicObject(self.ctx, sql);
cols = {} # {index:[单列] or index:{组合列}} -- 使用index作为key,是因为可以让字典变成按index顺序输出的字典(PY3.5+没这么麻烦)
splitName = '_'
localEid = self.ctx.UserLocale.LCID;
def ChildArgsList(columnName, showName, dataTypeId):
return [columnName, LocaleValue(showName, localEid), Enum.Parse(SqlStorageType, dataTypeId), True]
for item in dcObj:
columnName = item["FName"].ToString()
if columnName == str("FIDENTITYID"): # 隐藏不显示这个字段 FIDENTITYID
continue
# 判断是否是组合字段——使用分割符_判断
if columnName.find(splitName) > 0:
topName, subName = columnName.split(splitName, 1)
else:
topName = None
subName = columnName
# 区分组合列,单列
if topName:
if topName in [col['topName'] for k, col in cols.items() if isinstance(col, dict)]: # 组合名称已经创建,只需要加子列即可
for iCol, col in cols.items():
if isinstance(col, dict) and col.get('topName') == topName:
cols[iCol]['child'].append(ChildArgsList(columnName, subName, item["FDateTypeId"].ToString()))
break
else:
cols[len(cols)] = {
'topName':topName,
'child':[ChildArgsList(columnName, subName, item["FDateTypeId"].ToString())]
}
else:
cols[len(cols)] = ChildArgsList(columnName, subName, item["FDateTypeId"].ToString())
self.cols = cols
def whoCheck(self,VALUE): # 报表访问记录,怎样才能将特殊字符写入表的字符串列呢?
sql = """/*dialect*/ EXEC JML_P_RPT_RECODE_SET {0},'{1}',{2},'{3}','{4}'""".format(
self.sessionid,
self.RPTNAME,
self.userID,
self.formatSQLValue(self.getParameters()),
self.formatSQLValue(VALUE)
)
#paramList = List<SqlParam>();
#paramList.append(SqlParam("@SQL", KDDbType.String, str(VALUE)))
DBServiceHelper.Execute(self.ctx, sql)
def test(self, value): # 测试使用,写入测试数据到数据库
""" ctx = this.Context"""
dt = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffff")
DBServiceHelper.Execute(self.ctx, """/*dialect*/ INSERT INTO JML_LZF_LOG (T) VALUES('{0} - {1}')""".format(dt,self.formatSQLValue(value)))
def convert2sqltype(self, ctype):
""" 将C#的数据类型转换为sql server的数据类型"""
mappings = {
"System.Int64":"BIGINT",
"System.Int32":"INT",
"System.String":"VARCHAR(1000)",
"System.DateTime":"DATETIME",
"System.Date":"DATE",
"System.Decimal":"DECIMAL(18,4)",
}
return mappings.get(ctype,'VARCHAR(MAX)') # MAX这种方式可能会被金蝶云的sql解析语句报错
def formatSQLValue(self, value):
return str(value).replace("'","")
def GetCreateColumnsSql(self, cols):
""" 获取创建数据库表的列的字符串 即:(a int) 部分 #raise Exception(str(Encoding.GetEncodings()))
"""
maxColi = len(cols) - 1
create_table = []
for i, col in enumerate(cols):
columnName = col.ColumnName
dataType = self.convert2sqltype(col.DataType.ToString()) #Enum.Parse(SqlStorageType, col.DataType.ToString())
create_table.append(" [{0}] {1} ".format(columnName, dataType))
create_table[0] = '(' + create_table[0]
create_table[-1] = create_table[-1] + ')'
return ",".join(create_table)
def DropTempTable(self):
""" 这个功能还有问题,好像报错后不会删除已经生成的表"""
if self.tempTableName and DBServiceHelper.IsExistTable(self.ctx, self.tempTableName):
#DBUtils.DropSessionTemplateTable(self.ctx, self.tempTableName) # 会加#
ListDataServiceHe