Python获取财务报表平台的报表数据(支持自定义报表)原创
金蝶云社区-CQ周玉立
CQ周玉立
48人赞赏了该文章 4807次浏览 未经作者许可,禁止转载编辑于2022年05月12日 15:53:34
封面

独版文章!!!

最近一直在研究如何获取财务报表平台的数据,对于设置了项目公式的报表可以通过查询数据库获取:

(参考此帖:财务报表报表项目数据存取)

但是没有设置项目公式的报表无法直接获取,经过不断的研究和反编译代码,终于皇天不负有心人,研究出了方案。

写下此文做个总结和分享,在此也感谢在这个问题上给我提供帮助和思路的小伙伴们,下面开始正文...


【温馨提示】:新版补丁(V7.7  PT-146881)提供了报表API工具,这个工具似乎不支持自定义报表。

此方案也是基于反编译这个API接口源码研究出来的,我没有升级到这个版本也可以使用(我的版本是V7.6.0.202103) ,如需要借鉴我这个解决方案,如出现低版本不支持的问题,自行升级。


本案例采用Python编写(网页复制代码会有缩进问题,自行调整),如需编写.NET插件,自行转换即可。

以下分享解决方案核心代码:

  1. 首先需要在本身插件环境的基础上,添加关键引用;

    clr.AddReference('Kingdee.BOS.KDSReportEntity')#相当于添加.NET库文件引用

    clr.AddReference('Kingdee.BOS.App.KDSService')

    clr.AddReference('Newtonsoft.Json')


    from Kingdee.BOS.JSON import *#相当于添加.NET插件中的引用命名空间:Using Kingdee.BOS.JSON;

    from Kingdee.BOS.App.KDSService import *

    from Kingdee.BOS.KDSReportEntity.Entity import *

    from Newtonsoft.Json.Linq import *

  2. #读取指定rptId的报表数据成JSON格式

    def getRptData(rptId):

        rptIds = List[Guid]();

        testGuid=Guid(rptId);#报表ID,可查询数据库表:T_KDS_RPT 获取

        rptIds.Add(testGuid);

        kDSReportInfoService =KDSReportInfoService();

        KDSCtx=KDSContext();

        KDSCtx.SubSystemName = "CR";

        #根据报表ID获取报表详情列表,返回值为列表。如果传入多个ID,则可一次性取出多个报表数据

        reportDetail = kDSReportInfoService.GetReportDetail(this.Context, KDSCtx, rptIds, True);

        for current in reportDetail:

            #读取报表JSON格式数据,到此已经完成报表数据读取,报表JSON格式比较复杂,下面核心就是解析JSON了

            #建议将JSON先打印出来,复制到格式化工具中查看分析结构,再进行解析

            contents = current.GetJSON().ToJSONString();

            convertRptJson(contents);#解析报表JSON

#----- C#代码写法可参考如下截图:

image.png

    3.在此分享一下我解析JSON的过程(convertRptJson方法实现)

    #解析报表数据JSON

    def convertRptJson(contents):

        orgSQL=("""/*dialect*/select org.FNUMBER,orgL.FNAME,right(org.FNUMBER,2)  orgNo

        from T_ORG_organizations org

        inner Join T_ORG_organizations_L  orgL on org.FORGID=orgL.FORGID and orgL.FLocalEId=2052

        where len(org.FNUMBER)=5  """);

        ds = DBServiceHelper.ExecuteDataSet(this.Context,orgSQL);

        tab = ds.Tables[0];

        orgDIC={};#本案例是通过组织编码后2位与报表页签名称前2位作为组织对应关系,所以先构建了一个组织编码字典

        for org in tab.Rows:

             orgNo=str(org["orgNo"]);

             orgNum=str(org["FNUMBER"]);

             orgDIC[orgNo]=orgNum;

        dataObj=JObject.Parse(contents);#将报表JSON字符串转换成JSON对象

        itemList=JArray.Parse(dataObj["items"].ToString());#读取报表主体内容数据

        if(itemList.Count<=0):

            return;

        rpt=itemList[0];

        rptData=rpt["data"];#报表关键数据包

        sheetDIC={};#通过字典记录每个Sheet页签名称(key)与页签序号(value)的对应关系,用来对应前面的组织编码

        sheets=JArray.Parse(rptData["sheets"].ToString());#读取报表中的Sheet列表,与数据库表t_KDS_Sheet对应

#注意:JSON里面是根据页签的序号来对应每个页签的数据包的,需要借助这个关系来快速定位我们想要查询的报表页签

        sheetDatas=JArray.Parse(rptData["data"].ToString());#所有页签的数据包(数组),与Sheet列表序号相对应。

        sheetIndex=0;

        for sheet in sheets:

            sheetName=("{0}").format(sheet);

            orgNo=sheetName[0:2];

            if(orgDIC.has_key(orgNo)):#我这里只将组织编码对应得上的页签存入了Sheet字典

                sheetName=orgDIC[orgNo];#在字段中将页签名称转换成组织编码,以此记录每个组织编码对应哪个页签序号

                sheetDIC[sheetName]=sheetIndex;

            sheetIndex=sheetIndex+1;

#循环记录下来的页签字典,依次解析每一个页签的报表数据

        for orgNum in sheetDIC.keys():#我这里已经将关键字转换成了组织编码

            dataIndex=int(sheetDIC[orgNum]);#页签序号

            currentOrgData=sheetDatas[dataIndex];#当前页签的数据包,这是最复杂的JSON部分,也是我们最终的目标数据

            ExcelTAB=createTempTab(currentOrgData);#将页签JSON数据转换成了DataTable对象,还原成二维表




     4.分享一下报表页签数据解析成DataTable的过程和代码(createTempTab方法实现

    #页签JSON数据中(sheetData),实际上是记录了每一个页签中的单元格,构成的一个数组

    #单元格cell也是一个数组,包含4个元素,依次如下:

    #0.行序号(从0开始),1.列序号(从0开始),2.单元格内容值,3.单元格公式(无公式为空) ]

    #注意!!!

    #如有合并单元格,则JSON中会认为该数据在合并单元格的第1行和第1列,后面的单元格会跳过不会出现,参考下图:

image.png

    #如果有数据和公式都为空的单元格,则JSON会自动跳过,不会出现

    #=====根据Sheet的JSON数据,构建成DataTable====================================

    def createTempTab(sheetData):

        ExcelTAB=DataTable();#实例化一个DataTable对象

        rowsDIC={};#记录已经出现过的行号

        columnsDIC={};#记录已经出现过的列号

        for cell in sheetData:#循环页签数据单元格

             x=int(cell[0]);#当前单元格行序号

             y=int(cell[1]);#当前单元格列序号

             value=cell[2];#当前单元格的内容值

            if(columnsDIC.has_key(y)==False):#该列序号第一次出现,新增一列

                ExcelTAB.Columns.Add(str(y), Type.GetType("System.String"));

                columnsDIC[y]=y;#记录下来已经出现过

             newRow=None;#构建数据行

             if(rowsDIC.has_key(x)):

                newRow=ExcelTAB.Rows[x];#前面应出现过的行,直接读取DataTable中的数据行

             else:#该行序号第一次出现,新增一行

                 newRow = ExcelTAB.NewRow();

                 rowsDIC[x]=x;#记录该行已经出现过

                 ExcelTAB.Rows.Add(newRow);#将新的数据行添加到DataTable

             newRow[str(y)]=("{0}").format(value);#对DataTable对应的单元格赋值

     return ExcelTAB;


====至此,核心代码分享结束===================================

            

赞 48