报告与结账锁定

每次月度结账会向您的工作区写入哪些文件,每个已锁定月份如何记录其是否已全部核对一致,以及锁定如何确保已报出的数字不会在没有书面理由的情况下被更改。

结账会生成哪些文件

每次结账针对一家公司、一个月份运行。以下是结账在复核工作区中留下的内容,以及它读取的内容。Haruno 将每家公司称为 client(客户);文件夹名称、命令和仪表板中用的都是这个词。在下列路径中,<client> 是该公司在 workbooks/ 下的文件夹名称,例如 cobalt-systems

产出内容何时变化
报表包按公司自身版式列报的报表。该公司每次结账时。
对账底稿对照账簿重新计算的每张明细表。该公司每次结账时。
投资者报告某一家公司的数字,面向其投资者。该公司每次结账时。
reports/<client>/adjustments.csv调整记录。该公司每次结账时,在锁定之后。
投资组合仪表板包含在此工作区中结过账、且仍有映射配置(spec)的每一家公司。每次结账运行结束时(该次运行中至少有一家公司完成结账)。
ledger/ledger.sqlite所有报告据以生成的标准化账簿:整个工作区只有一个文件,其中每家公司的账目分开存放。每次结账时。已锁定月份只有在重新开启后才会变化。
ledger/staging/结账在应用您的科目映射之前,为每个源文件制作的工作副本。您无需打开它们。每次结账时重建。
ledger/evidence/每个源文件的只读副本。导入新文件或更正后的文件时。
.haruno/skills/QuickBooksCFO/specs/<client>.yaml映射配置:科目映射以及您记录下的判断。结账从不修改它。仅在您作答或批准某项更改时变化。

公司自己的文件保留在它们送达的位置,即 workbooks/<client>/。结账只读取这些文件,从不修改。

您在 macOS 应用中查看各份报告和仪表板,也可以将其中任意一份导出为 PDF 发给他人。

结账本身在您的电脑上运行,无论助手是否在运行,都不会传输任何客户数据。对话发送的,就是您发送的内容:当您让 Haruno 执行某项操作时,您输入的内容以及工具输出的内容(科目名称、说明文字、数字、发现事项)会经 Inferara 的托管服务发送给作答的模型提供方。请参阅“您的数据去向”。

应发送哪份报告
向公司的投资者发送投资者报告。仪表板包含工作区中的每一家公司,不要把它发给投资者。

报表包

报表包是按公司自身版式呈现的月度报表包:报表项目名称、小计和金额单位都与公司发送的财务资料包相同,因为它依据该工作簿本身的结构生成。其中每个数字都来自账簿,没有任何内容是手工录入的。

从上到下依次为:

  1. 封面。公司名称、“Financial Reporting Package”(财务报表包)、“Unaudited Monthly Results Through”(截至……的未经审计月度业绩)及结账月份,以及只注明一次的报告货币。
  2. “Restated periods”(重述期间)。仅当某个已锁定月份被重新开启、且其数字随后发生变动时才出现。
  3. “Issued with unresolved findings”(附未解决发现事项报出)。仅当存在未解决的高或中严重程度发现事项时才出现。它按严重程度、检查项和所涉对象逐项列出这些发现事项,并说明相关月份为“reported with exception”(附例外事项报出),而非“fully reconciled”(已全部核对一致)。
  4. 资产负债表。账簿中的每个月各占一列。公司自己的 CHECK 行(通常为资产总计减去负债和所有者权益总计)在任何月份若未能在 1 美分以内勾稽一致,都会被标记。
  5. 利润表。列与资产负债表相同,另加一列最近月份所在日历年度的年初至今累计数,从不列示自成立以来的累计数。
  6. “Solvency and going concern”(偿债能力与持续经营)。始终包含:营运资金、流动比率、现金、所有者权益合计,以及现金跑道及其计算依据。若会计科目表中设有关联方科目,关联方余额会与第三方债务分开列示。它列出触发了哪些 ASC 205-40 持续经营迹象,或说明没有任何迹象被触发。它只报告迹象,从不对持续经营下结论。
  7. “Collections against revenue”(回款与收入对比)。由报表推算的实际到账金额与已确认收入的对比,标注为推算估计,而非银行流水合计。
  8. “Gross margin”(毛利率)。每月收入减去营业成本;若映射配置将每个收入来源的收入项目与其成本项目配对,还会按收入来源分别列示。

报表沿用公司的金额单位:公司以千为单位列报时,报表也以千为单位列示;最后三个部分以及重述行按全额(不以千为单位)列示,并加以注明。每家公司只用一种货币,从不换算。如果没有导入现金流量表,现金跑道依据现金变动(含融资)计算,并如此标注。请参阅“偿债能力迹象”。

对账底稿

对账底稿列示公司在结账月份的各明细表与总账的核对情况;结账月份即账簿中存有对账数据的最近一个月。其页眉注明该月份和货币。

“Status summary”(状态汇总)。公司对账底稿中核对的每个资产负债表科目各占一行,列为“Account”(科目)、“Schedule”(明细表)、“Per GL”(总账数)、“Per schedule”(明细表数)、“Difference”(差额)和“Status”(状态)。“Schedule”列注明该工作表被识别为哪种类型,例如 rollforward(期初、本期发生额和期末)、prepaid_amort(预付费用摊销)、fixed_assets(固定资产登记簿)或 accrual_reverse_new(预提冲回后重新计提)。

状态含义
reconciled已核对一致:明细表与总账的差额在 1 美分以内。
exception例外:二者差额超过 1 美分。该判定不因重要性水平而放宽:$200 的差额也属于例外。
open待定:无法计算出差额。

“Coverage alerts”(覆盖提醒)。完全没有对账、且达到重要性水平的余额会按科目名称连同其余额列出;是否达到,以映射配置中该公司的重要性水平衡量。您已豁免的科目不会列出。

每个科目一个部分。列示各明细表的行,这些行经过重新计算,而不是照抄。滚动表和预提表的行带有“Recomputed”(重新计算)列,若某行未能得出其所列的期末余额,会被标记。预付费用明细表显示截至结账月已确认的金额和剩余金额;固定资产登记簿显示月折旧额、累计折旧和账面净值。

重新计算正是关键所在。对账底稿独立汇总明细表自身的各行,不依赖工作表上手工填写的任何差额,因此过时的或手工填写的差额无法制造出明细表本身并不支持的核对一致。如果某个工作表未列示总账数,则与从报表导入的余额进行核对。

例外和缺失的明细表也会进入发现事项,分别为 recon_exceptionsrecon_coverage。请参阅“对账状态”。

投资者报告

投资者报告是仪表板中某一家公司的页面,经过裁剪,面向为公司出资的人,而不是为公司记账的人。每次结账都会重写它,因此它总能反映最新一次导出的数据。当创始人向您索要一份给投资者的进展更新时,应发送的就是这份报告。

保留的内容

  • 现金、现金消耗和现金跑道。现金跑道指标卡依据最近期间的现金变动(含融资)计算,并如此标注。
  • 按报表包的列报方式呈现的各张报表,但不含衡量勾稽差额的 CHECK 行。
  • 期间对比。
  • 回款与收入对比。
  • 毛利率。
  • 按类别列示的运营费用;公司设有部门划分的,还按部门列示。
  • 员工人数及人均数据(公司提供员工人数时)。
  • 经常性收入(MRR、ARR 和流失率),以及获客成本(CAC)及其回收期(公司有这些数据时)。
  • 偿债能力面板及其列出的迹象。缺少它们,报告就会既准确又具误导性。
  • 关于账目能支持和不能支持哪些数字的说明,例如某个数字需要两个月的数据,而账簿中只有一个月。

不包含的内容

  • 完整性检查发现事项。
  • 科目映射表。
  • 对账状态。
  • 差异关注清单。
  • 结账状态和锁定记录。
  • 报告控制状态的指标卡:报表勾稽、对账例外和偏离最大的预算项目。
  • 预算与实际对比(预实对比),除非公司的映射配置允许将其纳入。
  • 客户层面的明细:按客户列示的收入表,以及某个数字背后的发票行和明细表行。该数字本身保留。
  • 写给映射配置维护者的说明。

不包含是指根本不放入,而不是隐藏:上述部分从不写入报告,因此其 PDF 中也没有。仪表板上那些把提问预先放入聊天的按钮也不会出现。

报告页眉说明其性质:为该公司的投资者编制,数据截至账簿中存有余额的最近一个月;如有导出日期戳,会注明这些数字所依据的导出文件的日期;并以报告货币列示。每份报告只涵盖一家公司。

投资者能否看到创始人的计划与账面数据的对比,由创始人决定。因此,除非公司的映射配置通过下面的设置将其纳入,否则预实对比不会出现在报告中。该值必须是 true 或 false。其他任何值(包括加了引号的 "no")都会被拒绝,而不会去猜测其含义。

yaml
investor:
  budget: true

结账状态

每个已锁定月份都带有一条记录,记载锁定时已知的情况。仪表板在每家公司页面的顶部以一个状态标签显示它,旁边是已锁定的月数。

结账状态标签颜色含义
fully reconciled绿色已全部核对一致:每个已锁定月份结账时都没有未解决的事项。
reported with exception红色附例外事项报出:至少有一个已锁定月份在仍有未解决的高或中严重程度发现事项时结账。
open琥珀色尚未锁定:还没有任何月份被锁定。
not recorded灰色未记录:某个已锁定月份的结账没有记录其发现事项,较早版本所做的结账即是如此。

以最弱者为准。一个状态标签代表所有已锁定月份,因此它只做出对所有月份都成立的那一种表述:任何月份存在例外,优先于某个月份没有任何记录;而没有记录又优先于已核对一致的月份。标签旁的说明会列出已锁定的月数,以及仍未锁定的月数(如有)。它不会说明其中有多少个月份带有例外。要查看具体是哪些月份,请运行 ingest.py periods,它会在每个月份的行上打印其状态(参见“追溯数字来源”)。

只有高和中严重程度的发现事项才算作未解决事项。低严重程度的发现事项、偿债能力迹象和待确认事项都不算:一家公司现金短缺,并不意味着它的账目没有核对一致。请参阅“严重程度”。

示例输出(合成数据):Ironwood Materials, Inc. 的仪表板页眉,显示绿色的“fully reconciled”状态标签和已锁定 41 个月,下方的指标卡显示“ties”(勾稽一致),对账例外为 0。
“fully reconciled”。合成演示数据,公司和数字均为虚构。
示例输出(合成数据):Cobalt Systems, Inc. 的仪表板页眉,显示红色的“reported with exception”状态标签、已锁定 41 个月以及三项未解决的检查,下方的报表勾稽指标卡显示 ($6,600),对账例外为 1 个。
“reported with exception”:照样锁定,并列出未解决的检查项。合成演示数据,公司和数字均为虚构。

同一状态会以相同的措辞出现在以下位置:仪表板页眉;期间列表中每个月份的行(ingest.py periods,参见“追溯数字来源”);报表包中的“Issued with unresolved findings”说明;以及结账完成时打印的摘要。在合成演示数据中,Cobalt Systems 的摘要如下:

text
locked 2023-02-28 … 2026-06-30 (41 periods) — reported with exception: 3 unresolved finding(s) (presentation_tieout, recon_exceptions, unmapped_active_account); re-opening one needs a reason (`ingest.py reopen`)
未解决清单是一份快照
被列为未解决的检查项(仪表板页眉中“unresolved:”(未解决:)之后的内容,以及结账摘要中括号里的内容)是这些月份被锁定时仍未解决的检查项,而且一次结账会在它锁定的每个月份上写入同一份清单。之后已解决的事项,或只在最后一个月才发现的事项,仍会记在所有这些月份名下。将指针悬停在状态标签上即可查看该清单。

锁定

结账完成时,该次结账报出数字的每个月份都会被锁定,无论是否勾稽一致;发现事项不参与这一决定。仅因某份文件提及才存在的月份(例如前瞻性预算中的月份)不会被锁定。

锁定不等于对账目下了定论。
每个已报出的月份无论如何都会被锁定。锁定记录的是该月已全部核对一致还是附例外事项报出,并且意味着已报出的数字不会在没有记录的情况下变动。

锁定会拒绝什么

某个月份一旦锁定,任何会改变该月列报内容的操作都会中止该公司的结账:不会向其账簿写入任何内容,不会为其刷新任何报告,也不会锁定任何月份。同一次运行中结账的其他公司照常进行。这既包括更改、删除或新增某个数字的更正后工作簿,也同样包括映射配置中对某个科目列报位置或某个报表项目计算方式的更改:这类更改不触及任何已存储的数字,却会使报表项目发生变动。拒绝信息会列出它将影响的每个已锁定月份,并写明将会变动的数字:前十二个完整列出,其余只给出数量。以下取自合成演示数据,在批准 Cobalt 的 1450 科目的映射之后:

text
  2023-02-28  presentation  BS line 'CHECK': -6,600.00 -> 0.00
  2023-02-28  presentation  BS line 'Other assets': 19,500.00 -> 26,100.00
  2023-02-28  presentation  BS line 'TOTAL ASSETS': 5,394,868.29 -> 5,401,468.29

以下情况不会触发拒绝:用相同的数字对某个月份重新结账;公司将某个科目改名但科目编号不变;导出文件不再包含某个零值行,因为缺失的数字与 0.00 是同一个报出数字;新时间跨度的年初至今现金流量表;以及不属于任何报表项目的明细,例如交易明细报告和账单或 CRM 导出文件。

重新开启某个月份

已锁定的月份仍可更正,但不能悄无声息地更正。重新开启需要说明原因:一句话,用决定更改已报出月份的那个人自己的话来写。该原因会与该期间一起永久保存,并在数字变动时列示在报表包中。空白的原因、tbd 之类的占位文字,或任何用尖括号括起来的内容都会被拒绝。

当 Haruno 在对话中遇到拒绝时,它会转述拒绝信息并停下。它从不自行重新开启月份,也从不替您撰写原因。用您自己的话告诉它原因,它就会重新开启拒绝信息所列的那些月份(不多不少),在每个月份下记录您的这句话,应用更改,然后再次结账。在终端中,同样的操作只需一条命令:

bash
python3 .haruno/skills/QuickBooksCFO/scripts/close.py --workspace . --client cobalt-systems --reopen-reason "1450 was unmapped; approved as Other assets"

如需手动逐月重新开启,请使用 ingest.py reopen。此处 --client 是映射配置中记录、报告中显示的公司名称(例如“Cobalt Systems, Inc.”),而不是文件夹名称;--period 是月末日期,可多次指定。请用您自己的文字替换占位符。

bash
python3 .haruno/skills/QuickBooksCFO/scripts/ingest.py reopen --ledger ledger/ledger.sqlite --client "<client name>" --period YYYY-MM-DD --reason "<their words>"

要查看某家公司的当前状态,请运行 ingest.py periods(完整命令见“追溯数字来源”)。它会列出每个月份是已锁定还是未锁定,以及其结账状态;对于重新开启过的月份,还会列出每次状态转换、操作人和原因。

重述

Haruno 中没有可供编辑的单元格,也无法在其中过入调整分录。更正在公司自己的会计系统中入账,因此会出现在下一次导出的文件中;重新导入这份更正后的工作簿就是重述:说明原因后重新开启它涉及的月份,结账流程随即对这些月份重新结账。

如果数字确实发生了变动,此后的每份报表包都会带有一张“Restated periods”表:列出月份、重新开启的时间、操作人、原因以及变动的数字。重新开启后未作改动的月份,列报的仍是已经发送的内容,因此它会出现在 ingest.py periods 中,而不会出现在报表包中。每一次结账和每一次重新开启都会按顺序保存,并附有各月份在当时列报的数字;这段历史从不改写。

关于结账被拒绝后在复核流程中如何处理,请参阅“结账被拒绝时”。

调整记录

reports/<client>/adjustments.csv 相当于会计实务中手工维护的调整明细表,只不过它是从账簿中生成的。每次结账都会在锁定之后,用该公司的完整历史重写它:每次重述中的每个数字各占一行。

内容
period月末日期;对于现金流量表项目,则为时间跨度。
kind账簿数字为“balance”(余额)或“activity”(发生额);没有底层数字变动、仅报表项目发生变动的(例如科目映射更改之后)为“presentation”(列报);导入的现金流量表中的项目为“cash flow”(现金流量);由较早版本记录、未保存数字的重新开启为“not itemized”(未逐项列示)。
subject科目或报表项目。
was, now, delta此前报出的数字、当前数字及变动额。
reopened_at, reason, actor月份被重新开启的时间、原因以及执行操作的用户。actor 只是一项记录,不代表签字认可。
closed_at该月份再次结账的时间。

表头总会写入,因此空的调整记录表示没有任何重述。“not itemized”行表示该月份发生了变动,但已无从得知是哪个数字;这就是完整的答案,而不是需要靠猜测来填补的空缺。

在合成演示数据中,批准 Cobalt 的 1450 科目的映射后,41 个月中的每个月都会为 CHECK、Other assets 和 TOTAL ASSETS 各列出一行“presentation”,共 123 行,因为科目映射改变的是读者看到的内容,而不是任何已存储的数字。更正后的银行余额则会列为一行“balance”,并注明该科目。报表包中的“Restated periods”表包含相同的行,按全额而非以千为单位列示。

询问发生了哪些变化
问 Haruno“3 月份有哪些变化?”,它会根据此调整记录或报表包中的“Restated periods”表作答,而不是从两份导出文件中重新推导答案。

保留的源工作簿

每个源文件在其数字被导入时都会复制到 ledger/evidence/ 中,因此日后仍能提供某个报出数字背后的文件,即使公司已经在自己的副本上覆盖保存了十几次。

  • 按公司分别存放、只读,并按文件内容命名,因此对未更改的月份重新结账时不会复制任何文件。该存储保存公司发送过的每一个不同版本。
  • 更正之后,被取代的副本仍然保留:它是此前报出内容的依据。
  • 如果文件在被读取之后、被存档之前发生了变化,则不会保留,结账会在当天说明这一点;当天是该文件可能还能找回的唯一时机。保留副本从不会中止结账。

要查找某个数字来自哪个文件:

bash
python3 .haruno/skills/QuickBooksCFO/scripts/ingest.py evidence --ledger ledger/ledger.sqlite --client "<client name>"

它会列出每个源文件、从中读取了哪些内容、其中数字所属的日期,以及保留的副本是否仍是该文件:ok、missing、altered 或 unreadable(分别表示完好、缺失、已被改动、无法读取),每次查询时都会重新检查。

每次结账还会重新检查这些副本;对于已丢失、被写入过或无法读取的副本,会产生一项 evidence_integrity 发现事项(严重程度为中)。报表本身不受影响;失去的是提供报表背后文件的能力。再次导入同一文件时,缺失的副本会被恢复。重新导入从不覆盖已被改动的副本,因此该发现事项会一直存在,直到有人用已知完好的原件手动替换该副本。

追溯数字来源

仪表板上的每个数字都可以追溯到结账生成的某份报告、某个源文件或账簿本身。

各份报告、调整记录和映射配置列于“结账会生成哪些文件”;公司发来的原始文件位于 workbooks/<client>/。如需深入到报告之下,请直接读取账簿。

要读取账簿本身,请在工作区文件夹中使用 query.py。它以只读方式打开账簿,没有任何写入选项;数字只会通过结账、依据映射配置和工作簿发生变化。这些命令只需要标准的 Python 3,无需其他任何东西。第一条命令返回某家公司在某个月末的现金,汇总方式与仪表板相同:在合成演示数据中,即 Meridian Robotics 的 $1,442,000,也就是其“Cash”(现金)指标卡上的数字。在查询中,'10%' 表示编号以 10 开头的所有科目,这是默认的现金科目前缀。如果该公司的现金科目编号方式不同,请改用其自己的前缀。

bash
# 某个月末的现金,汇总方式与仪表板相同
python3 .haruno/skills/QuickBooksCFO/scripts/query.py ledger/ledger.sqlite "SELECT p.end_date, SUM(b.balance) AS cash FROM account_balance b JOIN gl_account a ON a.account_id = b.account_id JOIN period p ON p.period_id = b.period_id JOIN client c ON c.client_id = a.client_id WHERE c.name = 'Meridian Robotics, Inc.' AND a.acct_num LIKE '10%' AND p.end_date = '2026-06-30' GROUP BY 1"

# 期间对比的所有项目,而不只是变动最大的几项
python3 .haruno/skills/QuickBooksCFO/scripts/ingest.py compare --ledger ledger/ledger.sqlite --client "Meridian Robotics, Inc." --grain quarter

# 哪些月份已锁定
python3 .haruno/skills/QuickBooksCFO/scripts/ingest.py periods --ledger ledger/ledger.sqlite --client "Meridian Robotics, Inc."

# 账簿中的表和列
python3 .haruno/skills/QuickBooksCFO/scripts/query.py ledger/ledger.sqlite --schema

您自己运行的命令只读取工作区,不发送任何内容。当您让 Haruno 替您追溯某个数字时,您输入的内容以及工具输出的内容会经 Inferara 的托管服务发送给作答的模型提供方。请参阅“您的数据去向”和“自行运行”。

后续步骤

了解每项检查的含义、仪表板如何呈现一次结账,以及当您向 Haruno 询问相关情况时,您的数据会发送到哪里。