很多人把 SAF-T 导出理解成“把总账报表另存为 XML”。但企业版 account_saft 的实现清楚说明:它做的不是“导出当前页面”,而是把公司资料检查、报表行抽取、税务明细补全、模板值组装整合成一条合规导出管线。
一、第一步不是取账,而是先检查公司资料是否够合规
_customize_warnings() 会检查 company registry、电话、城市/邮编等信息,不足时往 warnings 里注入 account_saft.company_data_warning。这说明 SAF-T 从一开始就不是“有账就能导”。
审计交换文件的逻辑是:会计数据正确还不够,主体身份信息也必须完整。
二、总账值提取不是直接读 move line,而是先走报表层
_get_report_values() 会重新构造 report options,强制 export_mode=file、unfold_all=True,再用报表 line 解析账户汇总与未分配利润提示。这一步很重要,因为它说明 SAF-T 依赖的是 account reports 的既有汇总能力,而不是重新发明一套账务统计。
这带来的好处是:
- 报表层口径更统一;
- 导出和报表展示不会轻易分叉;
- 某些异常(如 undistributed earnings)可以在导出前就被捕获。
三、账户、分录、税务明细是分段准备的
源码把导出值拆成多段填充:
_saft_fill_report_general_ledger_accounts():准备账户层 opening/closing balance;_saft_fill_report_general_ledger_entries():准备分录、journal、期间借贷总额;_saft_fill_report_tax_details_values():补税务明细映射;_saft_prepare_report_values():最后统一格式化为模板渲染值。
这说明 SAF-T 的本质不是“一个 SQL 查完所有列”,而是把不同粒度的会计/税务语义分层准备,再汇总成最终模板数据。
四、最容易被忽略的是导出约束本身
_saft_prepare_report_values() 里明确限制:只支持一个 column group,否则直接报错。这个细节很典型,它说明 SAF-T 不是任意组合报表过滤器都能套进去。合规导出要的是稳定、单义、可审计,而不是前端报表的自由度。
五、新手误区
1. 以为只要账对,导出就会成功
公司资料缺字段,一样会在导出前被 warning 甚至被审计端拒绝。
2. 以为 SAF-T 就是把总账行转 XML
实际上还要准备账户摘要、税务细节、模板层格式化值。
3. 以为报表能显示的所有筛选都能导出
源码明确限制了 column group 语义。
六、实战注意事项
- 导出前先补齐公司主数据,别等 warning 出来再补;
- 如果数字和报表不一致,先确认 report options 是否处于 export 模式;
- 审计失败时,别只看 XML 文件本身,也要回头看账户、税务、主体字段准备过程;
- 二开国家本地化时,优先扩展
_saft_get_account_type()和各段 fill 方法,而不是粗暴改模板。
结语
企业版 SAF-T 真正解决的问题,不是“导出一份账务文件”,而是把报表口径、主体信息、税务明细和模板约束压成一条审计可接受的导出管线。理解这层,才能真正把它当成框架能力来看。
DISCUSSION
评论区