平台

六个部分,一本账

每个部分独立部署,彼此都不读对方的数据库。它们通过发布事件达成一致 — 这正是为什么其中一处出问题,不会悄悄污染另一处的数字。

01

目录与配方

为你所采购、生产、存放或售卖的一切建立同一个概念 — 以及把其中一些变成另一些的配方。

  • 只有一种物料类型。不必再维护原料、基础配方、成品配方三套东西并保持同步。
  • 一个物料“是什么” — 外购、半成品还是成品 — 由该分店自己的配方结构推算得出,从不靠人工填写。
  • 公司默认配方、分店级覆盖、明确的外购声明,三者可以同时成立且成本都算对。
  • 损耗率按配方行计算,因此去皮、修整这类损失记录在它真正发生的位置。
  • 外带包装与配方分开核算,因为盒子是“卖出去”消耗的,不是“做出来”消耗的。
02

库存与成本

一本记录每一笔库存变动、只增不改的账,以及建在它之上的成本模型。

  • 每一笔变动一行不可修改的记录:收货、销售、生产、损耗、调拨、盘点更正、期初余额。
  • 移动加权平均成本,同时保留运行均价与每一笔业务实际观测到的成本。
  • 成本展开会沿配方树把成品成本一路推算到原料,并把结果物化保存。
  • 盘点声明的是“现在有多少”,差额由系统自行推算并入账。
  • 损耗是一份需要审批的单据,而不是某人往合计里敲进去的一个调整数。
  • 调拨会把货挂在在途账上,必须结平为零,单据才允许关闭。
03

生产

一天要做些什么的意图,以及实际做了什么的记录。

  • 每间分店每天一份计划,其状态由内容推算得出,不做存储。
  • 一次生产记录产出数量与真正消耗的投入,过账后不可修改。
  • 理论用量与实际用量都保留,因此每批的差异永远可以推算出来。
  • 允许计划外批次,因为一份已审批的计划不该逼任何人谎报。
  • 自助餐菜单定义一次,应用时复制进当天的场次,因此日后改菜单不会重写已经生产过的场次。
04

采购与网络

采购单、发货单、收货单,以及一套对从没听说过我们的供应商同样有效的往来登记。

  • 采购单到发货单到收货单,部分交付表现为多张发货单,而不是一张单收一半。
  • 修订作为独立分录记录,从不原地修改已过账的单据。
  • 没有账号的供应商可以通过分享链接打开采购单,日后注册时还能认领此前的往来历史。
  • 供应商产品、包装规格与带时间区间的价格 — 六月一个价、七月一个价是两个事实,而不是一次修改。
  • 到岸成本在收货进入库存之前,先把运费与关税分摊完毕。
05

销售与 POS

来自自有收银台与外卖平台的交易,只扣减一次库存。

  • 按批次从设备与渠道接入,并做去重,因此重试上传不会把同一批库存扣两次。
  • 营业日按分店自己的时区划分,而不是按 UTC 的自然日。
  • 菜品是某间分店“以某个价格售卖某样东西”的决定,与目录同属一处。
  • 设备以自身身份认证,其注册、轮换与吊销独立于任何一个人。
  • 菜单与配置同步只下发自设备上次同步以来发生变化的部分。
06

报表

按人们真正会问的问题来组织的读取模型。

  • 由其他部分发布的事件构建,因此出报表的负担永远不会拖慢营业中的分店。
  • 库存汇总、采购明细、成本历史与数据质量,每一张都为回答一个问题而设计。
  • 无成本物料作为数据质量问题被报出来,绝不允许被当成零来读。
  • 仪表板按人组装,并跟着人在不同设备之间走。

底层的几条规矩

这些是在代码里强制执行的,不是幻灯片上的愿景。

任何服务都不读另一个服务的数据库

每个部分拥有自己的数据并对外发布事实。没有任何代码会跨过边界去读不属于自己的表 — 这正是防止一处的改动悄悄搞坏另一处的关键。

推算出来的数字从不存储

状态、差异、物料层级与角色,全部从底层事实重新计算。存下来的副本,就是可能出错的副本。

停用,而不是删除

不再使用的东西会停止出现在新业务的可选项里,但绝不会从已经引用它的单据上消失,也绝不会被移除。

最新的事件胜出,而不是最后到达的

每一份对其他部分数据的副本,都记录了其背后事实的新鲜度,因此迟到的事件无法覆盖更新的那一个。

看看这些数字是怎么产生的

平台页说明每个部分各自做什么。下一页说明的是,为什么它产出的东西值得你相信。