——把财务部的“祖传公式”,一次性收编

引子:每个财务部都有几条“祖传公式”

最典型的一幕:有人在部门群里问——“谁有大写金额的公式?”

三分钟内收到五个版本,每个都信誓旦旦“我们一直在用”。没人知道出处,没人测过边界,更没人敢改——出了错只能整段删掉,去群里再要一次。

祖传公式三宗罪:

  • 无出处:谁写的、对不对,只有天知道;
  • 会变异:每抄一次被顺手改一点,一年后演化出五种方言;
  • 无人敢动:没有修订记录这个概念,出问题只能推倒重抄。

上篇我们封装了部门第一条准则(账龄分段 ),走完了从条文到治理的全流程。但一个部门只有一条准则是不够的——立法要成体系。

今天交付十条,全部附完整代码。选取标准只有一条:高频、易错、全员共用的“重灾区”。

版本门槛一句话:需要 Microsoft 365(空单元格输入 =LAM 能补全 LAMBDA 即可),老版本的降级方案见上篇第六步。


入编门槛:什么才配进函数库

**并非什么都值得封装。入编标准,比写代码更重要。**四问:

  1. 高频吗?——每月都干、天天都干的活,才配占用立法资源。一年一次的披露口径,手工做加底稿复核足矣。
  2. 易错吗?——有边界、有默认值、有单位坑。越是“人人都会”的活,越容易各干各的。
  3. 共用吗?——一人用叫快捷方式,全组用才叫准则。
  4. 稳定吗?——规则本身不常变。天天变的参数(税率、比例),放参数表里查,别铸进函数——上篇说过的“修政策,不必发新准则”。

**反例警示:复杂不是入选理由。**一户一议的特殊计算(投资分成、对赌条款),口径不稳定、使用面窄,封进去等于每月修法。它们是职业判断,不是准则。

总则:十条函数,五条公约

以下代码全部整段复制进:公式 → 名称管理器 → 新建 → 引用位置(以 = 开头),范围选“工作簿”。五条总则对所有函数生效:

  1. 中文函数名 + 中文参数名——LET 让公式自带注释,参数名就是说明书;
  2. 异常一律亮牌——返回“税率有误”“日期倒挂”这类文字,禁止 IFERROR 吞掉;
  3. 先校验参数,再进行计算——每个函数自带一道“凭证审核”;
  4. 版本 V1.0 起步,修订走函数字典,禁止私改;
  5. 数组友好——多数函数支持整列传入、自动溢出(365 特性)。

第一组:进料检验——数据入口三道闸

第一篇说过:SUM 算出 0,多半不是函数坏了,是原料坏了。函数库的第一优先级不在计算,在入口。

① 清洗金额

痛点:ERP、网银导出的金额是文本,带千分位逗号、货币符号、看不见的空格。每个人都遇过,每个人处理手法还不一样——有人分列,有人乘 1,有人选择性粘贴。

条文:转得动就转;转不动,亮牌“非数值”,不许静默。

=LAMBDA(原值,
    IF(ISNUMBER(原值), 原值,
    LET(
        去符号, SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(
                        原值, ",", ""), ",", ""), "¥", ""), "¥", ""),
        去空格, TRIM(去符号),
        IFERROR(VALUE(去空格), "非数值")
    ))
)
=清洗金额("1,234.56")   → 1234.56
=清洗金额("待补")        → 非数值
=清洗金额(A2:A5000)     → 整列溢出清洗

坑位:空单元格传进来会变 0(Excel 的默认行为)——它管“脏”,不管“缺”,缺数在源头查。遇到全角空格,加一段 SUBSTITUTE(原值, UNICHAR(12288), "")。

② 拆科目

痛点:总账导出的科目编码是 2202.01.05 一整串,做分析要拆层级。每次“分列”或手数 LEFT/MID 位数,数错是常事。

条文:层级填 1、2、3…取对应级;填 0 取末级(最常用)。分隔符定为"."。

=LAMBDA(科目编码, 层级,
    LET(
        片段, TEXTSPLIT(科目编码, "."),
        取第, IF(层级 = 0, COLUMNS(片段), 层级),
        IFERROR(CHOOSECOLS(片段, 取第), "层级有误")
    )
)
=拆科目("2202.01.05", 1)  → "2202"
=拆科目("2202.01.05", 0)  → "05"(末级)
=拆科目(A2:A5000, 0)      → 整列末级

设计点:你的 ERP 用"-“分隔?复制一条、改一个字符、另起函数名——但要在函数字典里登记。科目体系不同就是两套科目表,各立各的条文。

③ 期间末日

痛点:“期间”这东西在各家表里有三种长相:202606(数字)、"2026-06"(文本)、一个日期。EOMONTH 只认第三种。每次先手工统一格式,一个月统一三十次。

条文:三种写法都收;认不出的亮牌;返回该期间最后一天。

=LAMBDA(期间,
    LET(
        末日, IF(
            ISNUMBER(期间),
            IF(期间 > 100000,
                EOMONTH(DATE(INT(期间 / 100), MOD(期间, 100), 1), 0),
                EOMONTH(期间, 0)),
            IFERROR(
                EOMONTH(DATE(VALUE(TEXTBEFORE(期间, "-")), VALUE(TEXTAFTER(期间, "-")), 1), 0),
                "期间格式不识别")),
        末日)
)
=期间末日(202606)     → 2026-06-30
=期间末日("2026-6")   → 2026-06-30
=期间末日(B2)         → B2 所在月的月末

设计点:那个 100000 是什么?——大于十万的数字只能是“年月码”,小于的只能是日期序列值(现在约四万多),两类值域未来几百年不相撞,一行代码省掉三分之二的格式判断。先想清楚值域,再写判断逻辑——跟抽凭先抽边界样本,是同一种思维。(结果记得设成日期格式。)

第二组:加工车间——四台核心设备

车间是产出价值的地方,也是最容易“差一分钱”的地方。

④ 价税分离

痛点:公式简单,坑不少:税率有人填 13 有人填 0.13;价和税各自四舍五入,分录差一分钱借贷不平,月末翻半天凭证找差异。

条文:税率 13 和 0.13 都收;不含税先算先舍入;税额 = 含税额 − 不含税(已舍入),用倒挤,不用独立舍入。这一条是灵魂:价 + 税 = 含税,永远分毫不差。

=LAMBDA(含税额, 税率, 需求,
    LET(
        率, IF(税率 > 1, 税率 / 100, 税率),
        不含税, ROUND(含税额 / (1 + 率), 2),
        税额, ROUND(含税额 - 不含税, 2),
        IF(率 < 0, "税率有误",
        IF(需求 = "价", 不含税,
        IF(需求 = "税", 税额, "需求填 价 或 税")))
    )
)
=价税分离(1130, 13, "价")    → 1000.00
=价税分离(1130, 13, "税")    → 130.00
=价税分离(1000.05, 6, "税")  → 56.61(倒挤,勾稽必平)

设计点:为什么倒挤——借贷记账法天然要求平衡,函数也要有记账思维:**先定锚(价),再倒挤(税)。**各自独立舍入是两个口径,倒挤是一个口径。

⑤ 分摊

痛点:费用分摊、奖金分配、成本结转,各项 ROUND 之后合计对不上,差一分到几分。手工调尾差:调谁头上、调几笔、怎么留痕,各有各的土办法。分差异不罕见,罕见的是处理方法统一。

条文:各项独立舍入到分;尾差全部给权重最大的一项;返回数组合计恒等于分摊总额。权重一律纵向排列、非负。

=LAMBDA(总金额, 权重,
    IF(SUM(权重) <= 0, "权重有误",
    LET(
        权重和, SUM(权重),
        理论额, 权重 / 权重和 * 总金额,
        已舍入, ROUND(理论额, 2),
        尾差, ROUND(总金额 - SUM(已舍入), 2),
        位置, MATCH(MAX(理论额), 理论额, 0),
        已舍入 + (SEQUENCE(ROWS(理论额)) = 位置) * 尾差
    ))
)
权重放 D2:D4(1、1、1):
=分摊(1000, D2:D4)   → 333.34 / 333.33 / 333.33
=SUM(溢出区域)        → 1000.00(永远精确)

设计点:尾差为什么给最大项——占比最小、最不显眼;给最后一项是图省事,给最大项是职业判断。你的部门选哪个都行,但必须选一个写进条文——这就是封装的意义。坑位:横向权重会静默失效(ROWS 数不出来),条文里“纵向”两个字就是防这个的。

⑥ 月折旧

痛点:规则人人知道——当月入账次月计提、直线法、提满停提。但几千行的固定资产表每行手写判断,漏提、多提、提满不停,年审时被一条条挑出来。

条文:①当月入账,次月起提;②月折旧 =(原值 − 残值)÷ 年限月数,舍入到分;③提满即停;④末月找齐——最后一月提“原值 − 残值 − 已提”,累计折旧精确归零;⑤残值是金额,不是比例。

=LAMBDA(原值, 残值, 启用日, 年限月, 期末日,
    LET(
        计提月, EOMONTH(期末日, 0),
        首提月, EOMONTH(启用日, 1),
        末提月, EOMONTH(启用日, 年限月),
        常规月额, ROUND((原值 - 残值) / 年限月, 2),
        末月找齐, ROUND(原值 - 残值 - 常规月额 * (年限月 - 1), 2),
        月额, IF(计提月 = 末提月, 末月找齐, 常规月额),
        IF(OR(年限月 <= 0, 原值 < 残值), "参数有误",
        IF(AND(计提月 >= 首提月, 计提月 <= 末提月), 月额, 0))
    )
)

边界自测(原值 10100,残值 100,启用 2023-06-10,36 个月):

期末日 结果 考点
2023-06-30 0 当月入账,次月才提
2026-05-31 277.78 常规月
2026-06-30 277.70 末月找齐(不是 277.78)
2026-09-30 0 提满停提

设计点:首月、末月、提满后——恰好是折旧错报最爱藏的三条接缝。这张表本身就是上篇第五步“边界抽样”的迷你版,每个函数发文前都该有自己的一张。

⑦ 利息

痛点:借款、资金拆借计息:天数算法(算不算头尾)、年化基准(360 还是 365)、利率单位(3.65 还是 0.0365)——三个坑叠着,谁跟谁都不一样,对账全靠吵架。

条文:①天数 = 止日 − 起日,算头不算尾(信贷通行惯例,写死);②基准三选:“360” / “365” / “自动”(止日在闰年按 366);③利率两种写法都收;④日期倒挂亮牌。

=LAMBDA(本金, 年利率, 起日, 止日, 基准,
    LET(
        率, IF(年利率 > 1, 年利率 / 100, 年利率),
        天数, 止日 - 起日,
        年基数, SWITCH(基准,
            "360", 360,
            "365", 365,
            "自动", IF(OR(MOD(YEAR(止日), 400) = 0,
                        AND(MOD(YEAR(止日), 4) = 0, MOD(YEAR(止日), 100) <> 0)),
                    366, 365),
            "基准不识别"),
        IF(天数 < 0, "日期倒挂",
        IF(ISNUMBER(年基数), ROUND(本金 * 率 * 天数 / 年基数, 2), 年基数))
    )
)
=利息(10000000, 3.65, DATE(2026,1,1), DATE(2026,4,1), "360")
→ 91,250.00(90 天,360 基准)

设计点:“自动”是简化处理(按止日所在年判断闰年)。精算口径的 ACT/ACT 要按年度切分天数——真有这种合同,别封装,逐笔算、留底稿。知道函数的边界,和会用函数一样重要——准则也有适用范围。

第三组:成品包装——报表出口三道工序

报表是给外人看的,出口处的口径漂移最丢人——想想第一篇里华东华南的账龄打架。

⑧ 重分类

痛点:往来科目期末按明细方向重列:应收账款贷方余额转预收/合同负债,应付账款借方余额转预付。规则一句话,执行一屋子 IF,每张表一个写法。

条文:只做一件事——按余额方向取数:方向填“借”,取借方余额(贷方返回 0);填“贷”,取贷方余额(借方返回 0)。科目映射不写进函数,用对照表查。

=LAMBDA(余额, 方向,
    IF(方向 = "借", MAX(余额, 0),
    IF(方向 = "贷", MAX(-余额, 0),
    "方向填 借 或 贷"))
)
客户余额在 B 列:
=重分类(B2:B500, "借")   → 借方余额列(应收)
=重分类(B2:B500, "贷")   → 贷方余额列(预收)

设计点:十行不到的代码,管住全公司的列报口径。入选函数库的从来不是复杂度,是出错成本。这可能是十条里最短的一条,也可能是报表上最值钱的一条。

⑨ 变动分析

痛点:管理报表的本期对比上期:上期为零或为负时,要么 #DIV/0! 满屏,要么算出一个“看起来很正常、其实很荒谬”的负增长率。前者是报错,后者是披着数字外衣的静默失败——更危险。

条文:上期 > 0 才算率;否则只报金额差,并标注“率不可比”。

=LAMBDA(上期, 本期,
    LET(
        差额, 本期 - 上期,
        差额文, TEXT(差额, "+#,##0;-#,##0;0"),
        IF(上期 > 0,
            差额文 & "(" & TEXT(差额 / 上期, "+0.0%;-0.0%") & ")",
            差额文 & "(上期非正数,率不可比)")
    )
)
=变动分析(100, 115)   → +15(+15.0%)
=变动分析(0, 500)     → +500(上期非正数,率不可比)

设计点:为什么不用 IFERROR 一把梭——因为负基数不是“错误”,是“不可比”。错误该报错,不可比该声明;一把梭会把两者一起埋掉。会计语言里,这叫披露,不叫容错。

⑩ 大写金额

痛点:回到开头,群里那句“谁有大写金额的公式”。那条祖传公式三百字,没人测过边界——而它最大的坑,藏在最想不到的地方。

先做个实验:在单元格里输入 =INT(0.29*100)。你以为结果是 29?**实际是 28。**浮点误差:0.29 在机器里是 0.28999…,INT 一刀切下去,少一分钱。大写公式里全是 INT 和 MOD——一分钱的误差,支票大写直接错一个字。

条文:①先把金额 ×100 整体 ROUND 成“总分”,再拆元角分(浮点免疫);②角分规则四条——角分全零→“整”;分零角有→“X角整”;角零分有→“零X分”;都有→“X角Y分”;③负数、非数值、超万亿亮牌。

=LAMBDA(金额,
    IF(NOT(ISNUMBER(金额)), "非数值",
    IF(金额 < 0, "负数请人工处理",
    IF(金额 >= 10^12, "超出封装范围",
    LET(
        总分, ROUND(金额 * 100, 0),
        元, INT(总分 / 100),
        角, MOD(INT(总分 / 10), 10),
        分, MOD(总分, 10),
        元文, TEXT(元, "[DBNum2]"),
        角文, TEXT(角, "[DBNum2]0"),
        分文, TEXT(分, "[DBNum2]0"),
        尾文, IF(AND(角 = 0, 分 = 0), "整",
              IF(角 = 0, "零" & 分文 & "分",
              IF(分 = 0, 角文 & "角整",
                 角文 & "角" & 分文 & "分"))),
        元文 & "元" & 尾文
    ))))
)
=大写金额(1234.56)  → 壹仟贰佰叁拾肆元伍角陆分
=大写金额(100.05)   → 壹佰元零伍分
=大写金额(100)      → 壹佰元整
=大写金额(0.29)     → 零元贰角玖分(浮点免疫的受益者)

设计点:一行 ROUND,防住一类错。网上的大写公式几十个版本,差别全在角分补零的写法上——现在你们部门只有一个版本,条文就写在那四行 IF 里。

加餐:月末自动锁定——上篇预告兑现

这一条故意不做成函数,它是三道闸拼出来的内控:

  1. 参数表放一个格子:锁定日(已结账截止,如 2026-05-31),只有主管能改;
  2. 录入区“日期”列设数据有效性 → 自定义:=B2>参数!$B$1——想往已结账的 5 月补一笔?弹窗直接拒绝;
  3. 条件格式:=$B2<=参数!$B$1 整行置灰——已锁区间一眼可辨。

结账 = 主管把锁定日往前推一格。要硬锁,再叠加工作表保护。

设计点:锁定是权限与流程问题,函数管计算、内控管权限——别让一个工具干两种活。知道什么不该封装,和知道什么该封装,一样重要。


收官:函数字典(可直接贴进模板)

这张表就是模板里“函数字典”页的种子,补上制定人和日期即可“发文”:

# 函数 定位 关键条文 版本
1 清洗金额 文本数字→真数字 转不动亮牌;空单元格会变 0 V1.0
2 拆科目 科目编码取任意层级 层级 0 = 末级;分隔符”." V1.0
3 期间末日 期间→月末日期 三种写法;认不出亮牌 V1.0
4 价税分离 含税额拆价、税 倒挤保平,价 + 税 = 含税 V1.0
5 分摊 按权重分摊不差分 尾差进最大项;权重纵向 V1.0
6 月折旧 直线法当月折旧 次月起提;末月找齐 V1.0
7 利息 计息 算头不算尾;360/365/自动 V1.0
8 重分类 往来余额拆借贷列 科目映射查表,不进函数 V1.0
9 变动分析 差额 + 变动率 上期 ≤ 0,率不可比 V1.0
10 大写金额 人民币大写 先成总分再拆;角分四规则 V1.0

治理三动作(细节见上篇):贴字典、做边界抽样、修订留痕。


结语:不上资产负债表的资产

回到开头那个微信群。下次再有人问“谁有大写金额的公式”,你的回复只需要一行:

模板里有。函数名:大写金额。条文见函数字典。

五个祖传公式的时代,到你们部门这一代为止。

会计的世界里,资产大多在折旧——设备越用越旧,技术越用越淘汰。函数库是难得的反向资产:每复用一次,口径稳一分;每修订一次,组织聪明一分。它不上资产负债表,但它让资产负债表更可信。顺手,它还解决了财务部的另一个老大难——从前口径长在人脑子里,人走口径散;现在口径长在模板里,人走,函数还在。离职交接清单上终于可以有一行:“函数字典在模板第三个工作表。”

十段代码加起来不到一百行。而把它们写出来花的功夫,远不如把那十条“条文”想清楚花得多——上篇说过,最难的一步不在 Excel 里,这一篇,十条又验证了一遍。

你封装的从来不是公式,是部门的执行纪律。