使用python做简单账表插件(调试辅助)

栏目:云星空知识作者:金蝶来源:金蝶云社区发布:2024-09-16浏览:1

使用python做简单账表插件(调试辅助)

学习了两个多月,调试插件真的不是很方便,定位问题显得很难,因此查阅了大量的社区文章,结合自身对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) # 会加# ListDataServiceHelper.TryDropTable(self.ctx, self.tempTableName) # global sets sets = SESSION(None, None) def extract_tb(tb, limit = None): """ 来自于 trackback 模块的逻辑改,因金蝶云的ipy2.6还不知道怎么引用外部模块 trackback,只能自己实现用于报错代码的追踪""" if limit is None: if hasattr(sys, 'tracebacklimit'): limit = sys.tracebacklimit list = [] n = 0 while tb is not None and (limit is None or n < limit): lineno = tb.tb_lineno co = tb.tb_frame.f_code list.append("错误位置:文件名:{0}|行号:{1}|函数名:{2}".format(co.co_filename, lineno, co.co_name)) tb = tb.tb_next n = n + 1 return list class LogClass: """ 报错装饰器,""" def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): try: result = self.func(*args, **kwargs) except Exception as(e): sets.DropTempTable() # 存在一个初始化先后的问题,由于内部条件限制(tempTable),initialize时候报错无碍 etype, value, tb = sys.exc_info() err = "\n".join(extract_tb(tb)) raise Exception("函数{0}|{1}|{2}运行错误:\n{3}\n{4}".format( self.func.func_name, str(args), str(kwargs), err, str(e) )) return result @LogClass def Initialize(): """ 账表初始化 普通简单账表 ReportType.REPORTTYPE_NORMAL=0 树形帐表 ReportType.REPORTTYPE_TREE = 1 分页账表 ReportType.REPORTTYPE_MOVE = 2 """ global sets sets = SESSION(this.Context, str(this.Context.UserId)) # 每一次查询都会有一条记录 this.ReportProperty.ReportType = ReportType.REPORTTYPE_NORMAL; this.ReportProperty.ReportName = LocaleValue(sets.RPTNAME, this.Context.UserLocale.LCID); # 报表名称,貌似不起作用 this.IsCreateTempTableByPlugin = True; # 插件创建临时表数据 this.ReportProperty.IsGroupSummary = True; # 支持分组汇总 this.ReportProperty.IsUIDesignerColumns = False; # ;#账表列头是否是通过BOSIDE设计,默认False #this.ReportProperty.GroupSummaryInfoData.DefaultGroupbyString = "单据编号"; this.ReportProperty.IdentityFieldName="FIDENTITYID"; # 临时表的主键,默认字段名是 FIDENTITYID 是从1开始的整数序列, 必须从1开始、不能重复,不能跳号。否则报表页面会显示空白 @LogClass def BuilderReportSqlAndTempTable(rptfilter,tableName): """ 获取报表数据并写入临时表,tableName说是临时表,在数据库,建立的还是事实表 方式1:能select数据的,直接 select into tableName 方式2:存储过程不能直接 select into,需要先exec出数据,然后通过dataSet的列信息创建临时表,再将dataSet的数据导入这个临时表中 """ sets.tempTableName = None sets.getFilters(filter=rptfilter, reset=1) sets.whoCheck(tableName) #sets.test("BuilderReportSqlAndTempTable-0- {}".format(tableName)) ##!!!!! FIDENTITYID 一定要从1开始,不然前台显示没数据!!!! sql="""/*dialect*/ SELECT * into {0} FROM JML_TF_RPT_LS_EMP_SALE_AND_OUTSTOCK('{2}','{3}',{4},{1}) """.format(tableName, this.Context.UserId, *sets.filters); #sql = """exec [JML_P_RPT_LS_MONTHLY_AWARD] 202308,{0},1""".format(*sets.filters) #sets.whoCheck(sql) # sql中的字符'还不能很好的处理,这里就看看咯 ds = DBServiceHelper.ExecuteDataSet(this.Context, sql) if ds.Tables.Count != 0: # 如果返回了数据集,将数据集导回数据库的指定表中 dt = ds.Tables[ds.Tables.Count - 1] # 默认最后一个数据集出数,不是数据集的语句不会在数据集中占索引 dt.TableName = tableName #tmpTableName = DBUtils.CreateSessionTemplateTable(this.Context, tableName, sets.GetCreateColumnsSql(dt.Columns)) DBUtils.Execute(this.Context, "create table {0} ".format(tableName) + sets.GetCreateColumnsSql(dt.Columns)) DBUtils.BulkInserts(this.Context, dt) # 将dataTable的数据返插回数据库中的临时表中 # 获取列信息,方式1不能通过datatable的方式获取,因此到数据库直接查看表结构(数据库临时表#x是不能这样查的) sets.tempTableName = tableName sets.getColumns(tableName) #sets.test("BuilderReportSqlAndTempTable-99") # def GetSummaryColumnInfo(filter): # 设置汇总列 # var result = base.GetSummaryColumnInfo(filter); # result.Add(new SummaryField("FQty", Kingdee.BOS.Core.Enums.BOSEnums.Enu_SummaryType.SUM)); # return result @LogClass def GetReportTitles(Filter): reportTitles = ReportTitles(); #customFiler=Filter.FilterParameter.CustomFilter; #reportTitles.AddTitle("F_JML_test1", sets.getFilters("F_JML_test1")) #reportTitles.AddTitle("F_JML_Test2", "345") return reportTitles; @LogClass def GetReportHeaders(Filter): header = ReportHeader(); for iCol, col in sets.cols.items(): if isinstance(col, list): listHeader = header.AddChild(*col) else: # dict if len(col['child']) == 1: # 只有一个子列的情况下还原回单独的列 args = col['child'][0] args[1] = args[0] + '_' + args[1] # 还原显示名称为数据库列名 listHeader = header.AddChild(*args) else: listHeader = header.AddChild() listHeader.Caption = LocaleValue(col['topName']) for subCol in col['child']: listHeader.AddChild(*subCol) listHeader.ColIndex = iCol #listHeader.IsHyperlink = true; // 支持超链接 return header; def CloseReport(): this.DropTempTable(); ``` ​效果图: ![报错效果图.webp](/download/01001030ba9b0da34e789e85a0b6c24733a8.webp)

使用python做简单账表插件(调试辅助)

学习了两个多月,调试插件真的不是很方便,定位问题显得很难,因此查阅了大量的社区文章,结合自身对python的认知,做了个能定位错误位置的...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息