ARTICLE · 1111736
从Excel公式到六层数据仓库,我设计并实现了一套后台自动生成财务报表的系统
做财务的人,对这样一个场景应该并不陌生。
月末一到,打开Excel,导数据、复制数据、检查公式、刷新链接、核对科目、调整数字;如果公司多、报表多,还要反复确认不同单位使用的是不是同一个口径。
很多所谓的“报表自动化”,其实只是把这个过程从Excel搬到了网页上。
以前是打开Excel以后算,现在是打开系统页面以后算。表面上看自动化了,但底层逻辑并没有真正改变。
我一直在想一个问题,财务人员打开报表的时候,为什么系统还要临时开始计算?
如果数据已经进入数据库,规则也已经确定,那么更合理的方式应该是后台提前把数据整理好、规则算好、报表生成好。财务人员打开系统时,看到的应该已经是一张成品报表。
沿着这个思路,我最近把自己一直在研究的一套财务报表自动生成体系真正写成了软件。
它现在已经不是一个概念图,也不是几段SQL,而是一套能够在本地直接运行的财务数据仓库和报表自动生成程序。
它有Excel导入、有数据库、有六层数据仓库、有映射规则、有公式引擎、有报表预览、有人工调整、有数据血缘,也可以直接看到一张报表的数据到底来自哪里。
这篇文章,我想介绍一下这套软件到底做了什么,以及我认为它真正有价值的地方在哪里。
一、我想解决的,其实不是“做一张报表”
最开始做这套系统时,我也可以选择最简单的路径,做一个资产负债表页面,再做一个利润表页面,然后针对每一个报表项目写一条SQL。
例如,货币资金查哪些科目,应收账款查哪些科目,营业收入查哪些科目,资产总计再把前面的数字加起来。
这样当然可以做出来,但很快就会遇到一个问题,下一张报表怎么办?
月报之后还有季报、半年报、年报,还有管理报表、预算执行表、费用分析表、投资分析表……
如果每增加一张报表,就重新开发一套SQL,那么所谓的“自动化报表系统”,最终只不过变成了一大堆越来越难维护的代码。
所以我后来把问题重新定义了一遍,我真正需要建设的,不是一张自动报表,而是一套“能够持续生产报表的数据体系”。
这也是整个软件架构发生变化的起点。
二、这套软件到底是什么?
简单来说,它是一套整合了财务数据仓库、报表规则引擎、自动成表与审核调整功能的综合工作台。
整个系统被拆成六层,分别是原始数据层、标准维度层、映射规则层、标准明细层、主题汇总层、报表应用层。
(一)ODS原始数据层,负责保存原始数据
第一层ODS原始数据层,原则非常简单,即原来的数据是什么样,就尽可能什么样保存下来。
例如科目余额、辅助余额、凭证明细,以及导入文件、工作表、源行号、原始内容等。
ODS不急着做复杂判断,因为一旦原始数据被加工后覆盖,后面再想追溯“这个数字原来到底是什么”,就很困难。所以这一层首先解决的是数据有没有完整留下来。
(二)DIM标准维度层,负责建立统一标准
第二层DIM标准维度层,是以“单位、科目代码、科目名称”主线,建立起一个“标准科目主轴”,再把科目余额、辅助核算余额、凭证明细,业务指标等数据挂到这条主轴上。
于是原来散落在不同Excel、不同Sheet里的数据,开始拥有共同的标准语言。
这一步解决的是,不同来源的数据,能不能被统一理解。
(三)MAP映射规则层,负责管理业务规则
第三层MAP映射规则层,这是我认为整套软件里最重要的一层之一。
传统做法里,报表公式经常存在三个地方,Excel里一部分、SQL语句以及程序代码。
时间久了以后,很难有人说清楚一个报表项目究竟是怎么算出来的。所以我把规则单独做成了一层数据库,MAP层负责回答“这个报表单元格,到底应该从哪里取数,又应该怎么计算?”
现在系统中的规则目标,可以精确到“工作表+ 报表项目 + 报表列”。
这件事情非常重要,因为利润表里的“本月数”和“本年累计数”,资产负债表里的“期末数”,虽然可能属于同一个报表项目,但本质上是不同的取数口径,它们不能再被一句模糊的“营业收入取6001科目”全部概括。
(四)DWD标准明细层,负责形成标准明细
第四层DWD标准明细层,解决的问题是如何把原始财务数据加工成可以被业务直接使用的标准明细数据。
DWD 层的核心思想是,将不同来源的数据按照统一业务标准重新组织,形成面向财务分析的标准明细层。
这一层不再关注原始数据来自哪个文件、哪个系统,而关注这笔数据属于哪个单位、对应哪个标准科目、属于哪个期间、对应哪个业务维度、应该进入哪个报表计算体系。
经过DWD 加工以后,后续报表不再需要理解复杂的数据来源,而只需要面对统一后的标准明细。
(五)DWS主题汇总层,负责主题计算和汇总
第五层DWS主题汇总层,让复杂计算集中管理。
财务报表中大量指标并不是直接取数,而是要经过汇总、计算和加工形成。例如,流动资产合计、非流动资产合计、资产总计、营业收入、营业成本、利润总额、净利润等等。
如果这些计算逻辑分散在不同报表、不同SQL或者不同页面中,随着系统规模扩大,维护难度会快速增加。
因此,在系统设计中,我将复杂计算集中放到了DWS 层。
DWS 的核心作用是,围绕财务主题,对标准明细数据进行统一加工和汇总。
通过DWS层,系统可以完成科目向报表项目的归集、多层级指标汇总、期间累计计算、组织维度汇总、业务规则计算等等。
这一层实际上承担的是财务报表中的“计算中心”,MAP负责定义规则,DWS负责执行规则。这样,财务口径不再隐藏在程序代码中,而是成为一套可以管理、调整和追溯的计算体系。
(六)ADS报表应用层,负责生成最终报表
经过ODS、DIM、MAP、DWD、DWS五层加工以后,最终需要形成业务人员真正使用的报表结果,这就是ADS 层。
ADS 的目标不是再次加工复杂数据,而是将已经计算完成的数据组织成业务可以直接使用的报表产品。
在这个设计中,ADS保存的是已经生成好的报表结果。报表不是打开以后才计算,而是在后台提前生产。
三、我后来把复杂的“规则配置”,又做简单了一次
系统开发过程中,我自己也遇到过一个问题。规则能力做得越来越强以后,配置页面也越来越复杂。
可以配置数据库表、金额字段、筛选条件、跨表引用、加减乘除、多个组成项……技术上越来越强,但财务人员反而越来越难用。
于是到了现在这个版本,我又做了一次反向设计,把复杂能力留在后台,把日常入口重新做简单。
现在软件里有一个“简易映射”,财务人员只需要做几件事,例如选择目标报表、选择报表项目、选择目标列、选择科目、选择金额字段,保存以后,系统会自动生成正式的MAP规则,并重新计算对应的ADS报表结果。
甚至在目前还不知道取哪个科目的情况下,也可以先把报表项目占下来。系统允许“取数对象为空”,把这条规则保存成待配置状态。它不会参与计算,也不会污染现有报表结果。等以后明确了规则,再把来源补进去即可。
我觉得这一点很符合真实的财务系统建设过程。因为实际项目中,不可能第一天就把几千个指标的全部规则一次性确定。
系统应该允许“先把框架建起来,再逐步完善口径”,而不是逼着使用者在一开始就回答所有问题。
四、报表不是打开的时候算,而是在后台提前生成
这是整套系统里我最想强调的一个设计。
很多报表系统的运行逻辑是用户打开报表,然后系统开始查数据库,过程中执行一堆SQL、计算公式,最后显示结果
我采用的是另一套思路,源数据进入ODS,然后标准化,再执行映射规则,形成标准明细,完成主题汇总,后台生成ADS报表结果。用户打开报表时,报表已是成表状态。
也就是说,计算发生在后台,查看发生在前台。前台打开资产负债表、利润表时,不再临时执行一大堆复杂计算,而是读取已经形成好的报表结果。
这带来几个直接好处。第一,报表打开速度不再随着公式越来越复杂而明显下降。第二,同一个单位、同一个期间、同一张报表,不需要每个人打开一次就重新计算一次。第三,报表数字可以真正形成“版本”,而不是底层数据一变化,历史报表跟着悄悄变化。
这也是我整个软件最核心的设计理念之一,从“打开报表再计算”,变成“后台先生成,前台只查看、审核和确认”。
五、33张月报,不再是33套程序
现在这套软件已经把一套月报模板抽象进了ADS报表应用层。目前系统中已经接入33张工作表、366个报表列定义、1765个报表行项目定义。其中包,括资产负债表、利润表、现金流量表、带息负债情况表等。
但我认为重点不在“33”这个数字,真正重要的是,这33张表没有被写成33套独立程序。系统把“报表是什么”本身也数据化了,哪一张Sheet是什么报表、有哪些行、有哪些列、项目顺序是什么、层级是什么、哪个单元格需要取、哪个单元格需要公式等等,最终都可以通过数据库定义。
这意味着以后增加一张新报表,理论上应该越来越接近增加报表定义与配置规则,而不是重新开发一个程序。
这是“做报表”和“做报表平台”之间非常大的区别。
六、合计项、利润项,不应该再写死在前台
资产负债表和利润表里有大量“计算项目”,例如,流动资产合计、非流动资产合计、资产总计、营业总收入、营业总成本、营业利润、利润总额、净利润等等。
以前最容易出现的问题,就是这些公式被分散在Excel或者页面代码里。这次我把报表项目公式放到了MAP规则层,在DWS汇总主题层统一执行。
也就是说,MAP负责定义“怎么算”,DWS负责真正计算,ADS负责保存结果,前台只负责展示。
这样“营业利润到底怎么计算”就不再隐藏在某段程序代码里,而是成为一条可以查看、维护和追溯的业务规则。
这也是我认为财务数字化非常重要的一件事,把财务人员脑子里的口径,逐渐变成数据库里能够被管理的规则。
七、系统数不能被人工调整覆盖
报表自动化还有一个经常被忽略的问题,现实中,财务报表不可能永远百分之百不需要人工判断。
那么系统应该允许调整吗?我的答案是,应该。但人工调整绝不能直接把系统生成的数据覆盖掉。
所以现在ADS里,一个报表结果实际上分成了三部分,分别是系统生成数、人工调整数、最终报表数
关系很简单,最终数= 系统生成数 + 调整数。
与此同时,系统还记录调整原因、调整依据、调整人和调整记录。于是以后再看到一个数字时,就可以区分这是系统根据规则自动算出来的,还是财务人员后来调整过的;如果调整过,又为什么调整。我认为这比单纯追求“100%无人干预”更符合真实财务工作。
自动化不是取消专业判断,更合理的方式应该是,机器负责大量确定性的计算,人负责少量需要判断的例外。
八、一个数字,最终应该能够追到源头
如果管理层问“这个资产总计为什么是这个数?”,一个成熟的系统不能只回答“程序算的”,所以我在软件里增加了数据血缘和依赖关系。
报表结果可以沿着链路向下追,从报表单元格穿透到MAP规则,再穿透到DWS汇总,再穿透到标准科目,然后是ODS源数据,最终到原始记录
同时,规则执行本身也有批次、日志、缓存、依赖关系以及版本快照。也就是说,不仅数字能够追溯,规则本身也开始可以追溯。例如,某一条规则是什么时候修改的,过去是什么、现在是什么,哪些报表单元格依赖它,源数据变化以后哪些规则需要重新执行等等。
这些东西看起来不像“报表功能”,但恰恰是报表系统从工具走向真正管理系统必须具备的基础能力。
九、我还给数据库本身做了一个“可视化窗口”
这套软件还有一个我自己很喜欢的功能就是六层数据库可视化。
因为数据仓库最容易出现一个问题,表越来越多以后,只有开发人员知道它们之间是什么关系。
所以我做了一个可视化入口,可以直接看到ODS、DIM、MAP、DWD、DWS、ADS六层中的核心数据库表,以及它们之间的数据流向。
点击一张表以后,还可以继续查看字段结构、建表SQL、索引、上下游关系、样例数据、数据行数等等。
除此之外,系统里还做了一个数据库表设计器,可以直接在前台快速创建新的六层数据库表,并把它加入数据仓库关系图中。
从这个角度看,这套软件现在已经不仅是一套“报表查看工具”,它同时也是我继续研究财务数据仓库的一套实验平台。
十一、我认为这套软件真正的创新,不是“我会写代码了”
如果只是从技术角度看,这套软件使用的并不是什么神秘技术。Python、SQLite、SQL、Excel解析、Web页面等等,这些技术本身都非常成熟,所以我并不认为它的价值在于“用了什么新技术”。
真正让我觉得有意思的是,我尝试用财务逻辑重新组织了这些技术,把原始数据和标准数据分开、把规则从代码里拿出来、把报表公式放进规则层、把计算从前台搬到后台、把报表结果提前生成、把系统数和人工调整分开,让数字可以向下穿透、让规则可以版本化,让33张不同的月报逐渐变成同一个通用报表模型。
这些设计单独看都不复杂,但把它们组合起来以后,是一条把财务报表逐渐变成企业自己的数据资产和计算规则资产的过程,我认为这也是财务数字化真正值得继续研究的方向。
十二、这套软件现在还远远不是终点
目前这个版本主要还是本地化运行,使用SQLite作为数据库,更适合作为验证体系、设计逻辑和试点应用的平台。
如果未来继续向正式企业级系统演进,还可以迁移到PostgreSQL、MySQL或Oracle,并进一步增加任务调度、权限体系、规则审批、多组织并行计算、数据版本锁定、规则回滚、异常预警、集团合并与抵消以及接入更多业务系统数据。
相比这些未来功能,这个软件demo想证明的是这条链路可以跑通。从原始财务数据进入数据库开始,到标准化、规则映射、明细加工、汇总计算,再到最终报表结果生成和人工审核调整,它已经形成了一套完整闭环。对我来说,这也是这段时间编程最大的收获。