退费差一元,两套系统都说自己是对的
一句话总结
中途退保的退还金额看起来只是“年保费乘以未到期天数再除以总天数”这一行公式,但天数的计算基准、取整的位置,以及分段的方式,各自都会得出不同的答案。把这三点定为策略,并把计算路径统一为一条,就是这个问题的全部。
为什么需要它
客户在 5 月解除了合同。计费系统告知的退还金额是 412,331 韩元,几天后会计系统生成的结算文件里写的却是 411,982 韩元,相差 349 韩元。把两个团队叫来逐项核对计算,双方都没有算错。一方按实际日历天数相除,另一方沿用了把每个月按 30 天计算的旧惯例。一方在韩元位四舍五入,另一方则舍去。一方反映了 3 月发生的保障变更,把期间拆成两段;另一方则用签约时的保费一次性计算整个期间。
这样的差异在单笔上只有几百韩元。但每月有几千笔经过,它就变成对账项目,而对账项目就成了每月都要有人手工解释的事。而且客户来问时,不能回答“每个系统都不一样”。
工作原理
第一,天数计算基准(day count convention)。实际天数(actual/actual)完全按日历计数。2 月有 28 天或 29 天,一年有 365 天或 366 天。30/360 则把每个月都当作 30 天、一年当作 360 天,在债券利息计算中沿用已久,保留了手工计算时代的简便。两种方式对同一段期间给出的天数不同。Python 的 date 对象通过减法就能直接得到实际天数,而 30/360 需要手工调整日期数字来得到。
确定期间的结束日同样有陷阱。2024 年 2 月 29 日开始的一年期合同,到期日是哪一天?2025 年没有 2 月 29 日。常见的处理方式是用 calendar 模块的 monthrange() 求出当月最后一天,并提前到那一天,但如果条款没有规定往哪一边提前,这就会成为两个系统出现分歧的第一个地方。日期按 RFC 3339 的 YYYY-MM-DD 格式传递,可以避免为了字段顺序而争执。
第二,取整的位置和舍入方式。取整到韩元,这一点很容易达成一致,但 0.5 韩元是进位还是舍去则各不相同。一家公司里常常同时存在两种做法:会计上保守地舍去,计费上则为了不让客户吃亏而四舍五入。Python decimal 模块的 quantize() 可以明确选择 ROUND_HALF_UP 或 ROUND_DOWN 这样的模式。如果用浮点数(float)计算,这个选择本身就失去意义——因为 0.5 一开始就不是精确的 0.5。
第三,分段。保险期间内更改保障,保费也会从那一天起变化。退还金额应当把变更前后分开计算再相加才对。但这样一拆就会舍入两次,每段取整后的合计,与整体一次取整的值会相差 1 韩元。这不是错误,而是一种选择。必须决定是每段都取整,还是只在最后取整一次,并把这条规则写下来。
계약기간 |-----------------------------------------|
변경일 ^ 해지일 ^
구간1 |-------| (옛 보험료)
구간2 |-------------------------|(새 보험료)
미경과분 |----| ← 이 겹침만 환급
在现场相遇的样子
遇到差异时,最没用的报告是“相差 349 韩元”。有用的报告是“349 韩元中,300 韩元来自天数基准,49 韩元来自舍入,其余取决于是否反映了分段”。按规则拆分原因的方法很简单:在一个系统的计算中,只把一条规则换成对方的做法,看结果是否变化。把三条规则逐一切换,就能看出哪条规则对这份合同实际产生了影响。
另一个常见情形是按月缴费合同的拆分。年保费除以 12 通常会有余数,如果舍去余数,一年的计费合计就达不到年保费,而每个月都进位,则会超出。必须连“哪个月多加 1 韩元”也规定好,计费系统与收款总账才能在年底对上。有的公司从前面的月份开始加,有的公司集中加在最后一个月,无论哪种,都要有规则。
此外,更正不只是改数字。要获得审批,一页纸上必须写清:决定以哪种计算为基准、这个决定会使多少金额发生变动、涉及多少份合同。只列出差额的清单,到第二次更正时就没用了——因为没有人知道起点是什么。
实际工作中真正重要的事
- 天数基准、舍入方式、分段,是由条款和公司内部规定确定的值。如果代码抢先定下来,以后就改不了了。
- 把计算路径统一为一条。两个团队各写各的,差异就一定会出现。
- 差异要按规则解释,而不是只给合计。
- 更正清单要同时写出更正前金额和更正后金额。只写差额,追溯就会中断。
- 报告用哈希指向作为依据的数据。数据变了,数字也要重新算。
下一项实验要做什么
你将亲自创建 60 份合同和 15 笔保险期间内的保障变更,把同一笔退保分别按实际天数和 30/360 计算,看两者相差多少。接着把有变更的合同拆成分段,统计每段取整的合计与只取整一次的值之间的差异,并让按月缴费的 12 期拆分与年保费完全吻合。最后让计费和会计两条路径并排运行,按规则指出每份合同差异的原因,并输出更正清单。