更新日志

改了什么,
一条条记在这里。

已经上线的直接写;还没上线的会标明「尚未上线」。 目前共 794 条
同一条更新可能同时属于多个模块,因此各模块条数相加会大于总数。

第 31 页

2026-09-2020 条

分析

毛利看板:毛利率高于 92% 会标「成本可能没录对」

  • 毛利率高于 92% 的品项标信息提示,同一格写「成本可能没录对」。
  • 页尾补说明:行业常见区间酒水 76–82%、餐食 65–70%,高出一截通常是进货单价录错;并给出到进销存比对每笔入库单价的查法。
  • 原有低于 70% 标黄、低于 50% 标红不变。
分析

菜单工程页把销量中位数印在平均数旁边

  • 销量平均值旁新增中位数;两者差得很多,就是平均数不具代表性的信号。
  • 没有过滤任何品项,也没有改分界线的算法。
收银

订单查询页新增「折扣」栏

  • 订单查询表头加「折扣」栏;有折扣的行用带正负号的金额(如 −¥120.00)标出,不只靠颜色。
  • 数据本来就有(订单折扣金额),导出 CSV 一直有这一栏,只是画面上没有。
  • 没有加按折扣或按操作人筛选,那要加查询参数、动后端。

结账收款

老板

离线未送出的点单不会再因为读不到而被删掉

  • 本机离线待送队列:读不出来时(档案坏掉/存取失败/App 升版后旧格式 blob 还在),以前除了会被当成「没有待补送」,更糟的是**下一次写入会先读再整份覆写**,把磁碟上还没送出的点单整份写成空的——已点但没进厨房、帐上也查不到的静默删单。
  • 现在读不到就一律不写(新增/更新/删除都回 false 且不动磁碟),原始字串另存一份救援副本;真的没有与读不到在画面上分成两态(读不到时不给「联网补送全部」)。
  • 补送路径同步收紧:删不掉/写不回时不再宣称「已丢弃/已完成」,留在待补送(下一次靠幂等键重试,不会重复下单)。
  • 同族第 2/3 例(票面 §3 清单发现、指挥裁定并入本票):生物辨識名冊(`biometric_roster.dart`)與通知已讀集合(`notification_center_sheet.dart`)也是「先讀再整份寫」——讀失敗時舊碼會把先前已錄的人/已讀標記整批覆蓋掉。兩支現在都改成「讀不到就不寫」,狀態字用庫裡的 `LoadState`(loading/ok/unreadable)。
  • 本條原本掛的模組 id 不存在於 registry,改為不掛(跨全站)。
服务生

订位冲突提示不再把 UTC 当成本地时间印出来

  • 实测:建一笔 14:04 的订位,去点那张桌送单,弹出来的 409 写「桌台已为 王小姐 预留,预约时间 2026-09-20 06:04」——差 8 小时,而画面其他每一处都写 14:04。
  • 伤害:这句话的工作就是拦住服务生。他下午两点读到「06:04」,最自然的解读是早上那笔没清掉的旧订位,于是照样开台——唯一一句会拦他的话,自己把自己讲成不用理会。
  • 真因:settings.TIME_ZONE 是 UTC,aware datetime 直接格式化印出来就是 UTC。
  • 注意:timezone.localtime() 是错的修法(取当前活动时区,而它就是 UTC ⇒ 改完什么都不会变而且看起来很对)。正解是门店营业时区 get_business_timezone(store_id)。
  • 同一支档案两句都改了(排订位撞车 / 开台撞订位),文案不同、在两条动线上,只改一句另一条会继续印 UTC 而完全不出声。
服务生

订位列按「取消」,不会再显示「报到中…」

  • busyId 是整列共用的,五颗钮(确认/报到/带位开桌/爽约/取消)全都掛 disabled,但只有「报到」那颗会换字 ⇒ 不管按哪颗,换字的永远是「报到」。
  • 报到(记录客人已到)、爽约(可能没收订金)、取消(可能退款)后果完全不同;按了取消却看到系统宣称正在报到,而整列同时被 disable,使用者没办法补救。
  • 改成另存「正在跑的是哪一个动作」,五颗钮各自只在自己那个动作在跑时才换字。不是拿掉进度字——拿掉的话网路慢时使用者不知道送出去了没,会再按一次。
  • 六个 handler 全部同步设/清,五颗钮全部接上,避免新 state 变成设了没人读的死码。
  • 宽屏表格与窄屏 RowCard 共用同一份操作区,所以一次改盖到两种版面。
服务生

点被预订的桌时,「带位开桌」不会再从画面上消失

  • 原本「带位开桌」按钮和「当散客开台」确认框是同一个三元式的两臂,互斥。整张桌卡本身就是按钮,服务生一点桌,「带位开桌」当场被卸载,只剩「确定当散客开台」和「取消」——最可能去按的那个手势,把唯一正确的动作从画面上拿掉了。
  • 确认框改成问人而不是预设答案:「此桌 14:04 有 王小姐 的预订(4 位)——现在坐下的是这组客人吗?」,三颗钮:是 王小姐,带位开桌 / 不是,当散客开台 / 取消。
  • 后果:只有走「带位开桌」这条路,订单才会带上 reservation_id,订金抵扣才跑得到(正式机订单 5,095 笔只有 67 笔带,1%;订金 63 笔全部退款、抵扣 0 笔)。
  • 同屏繁体字(订位/确定/开台)一并改简体——这支档案其他字串本来就是简体。
  • 没有改后端:自动按桌+时间比对绑定会把订位客人的订金抵扣记到散客头上,系统分不出早到的订位客人和刚走进来的散客。
收银

低消桌结帐的「刚好」改成填应收:按下去是 500,不再是 280

  • 现象:低消桌(测试门店 B1、低消 ¥500、已点 ¥280)按结帐后,画面写「应收 ¥500」、实收预填也是 500,但一按「刚好」就被覆写成 280(=小计),系统随即红字「实收不足,请向客人补收差额」——一颗叫「刚好」的按钮把正确金额改成系统自己会拒绝的金额。
  • 根因:主收款那颗「刚好」绑的是 remainder,而 remainder 在「伺服器还没核定」(dueTotal == null)这条 fallback 路径上用的是本地估的 total(品项−折扣+服务费),不含低消补差。
  • 修法:把低消补差算进 fallback(minSpendGap = max(0, 桌子的 lockedMinSpend − 折扣后小计)),形状对齐 H5 PaymentSheet.tsx:536 的 cashTarget;不动预填那条路(它本来就是对的);守门保留(按不足金额⇒红字且不送任何请求)。
  • 验收:先 billing 再按「刚好」(未 billing 时应收==小计==280,按下去填 280 是对的、不会显形)。
老板

真的没生意时,第一屏说「今天还没有生意」,不再显示跌 100%

  • 第一屏「今天进账」在**真的还没有生意**(今天已结帐笔数与净销售都是 0)时改说「今天还没有生意」,并保留「明细 →」下钻;判准用既有值组合,不另造旗标。
  • 「金额 0 但有单」(折到 0/全退款)不会走到这句——那句只在笔数也是 0 时才说。
  • 与「读不到」互斥:读失败仍走原本那句(「近7天进账没读到——不是『今天只有 ¥0』,也不是跌了 100%」),两条分支不合并。
  • 跌幅百分比在有一边是 0(≤ −100%)时不再显示那个徽章(不是显示 −100%);没有可比基准时明说「还没有可比的基准」,不留白。

老板经营看板

老板

历史详情读失败不再和「真的没付款」混为一谈

  • 今天历史(paid/refunded 清单)逐笔取详情时,读失败与「真的没付款」原本共用同一条 continue ⇒ 那笔单从历史整张消失。改成两个分支:读失败 ⇒ 记进 unread(带单号)并在画面上说「未列入今日历史(不是『没有交易』)」;真的没付款才照原本跳过。
  • 整批清单读取失败另走一句「今日历史没读到,下面这些数字只含本机记到的部分」;两种句子分开,不共用。
  • 事件水合的 .catch(() => {}) 一并收掉(原本整批失败完全静默)。不得为 unread 合成任何事件(会以 0 元计入营收/提成)。
  • 通知挂在已经挂这支 hook 的两棵树:后台(AdminShell)与 POS 顶栏(App.tsx)。
老板

近7天 KPI 读不到时不再落成另一个口径的数字

  • 后台第一屏的周 KPI(近7天进账/结账笔数/客单价)读取失败时,改成说「没读到」并给重试,而不是改用另一个口径的数字顶上去(窗口语义按营业日边界说清楚)。
  • 验收:dev 阻断周 KPI 请求 ⇒ 笔数与客单价显示「没读到」+出现重试;正式机跨 chunk 指紋「也不是跌了」=1 证明该句在线。

老板经营看板

老板

月度目标读不到时不再清空其他月份(HQ 与店级同一道红线)

  • 根因:目标是一个「整份字典」的设定(value 覆盖不是逐栏位 patch),而读取失败被吞成空字典 ⇒ 在 HQ 下达当月目标时,送出的内容只有那一个月 ⇒ 该店其他月份的设定被整份删掉。
  • 读取层改成只吞 404(这家店没设过 ⇒ 空字典),其余错误一律往上抛;写入端加红线:只有「已成功读到」才允许下达(成功读到可以是空字典=真的没设过,功能不因此消失),读不到时保存钮不可按并写出原因。
  • 验收:阻断读取 ⇒ 出现「目标没读到」标示+保存钮 disabled(带说明)+容器内强制点击后写入计数 0;happy path 在二號店(原有 2026-08=50000)下达 2026-09=50000 ⇒ POST body 仍为 {"2026-08":50000,"2026-09":50000}(其他月份没被吃掉);反例:真的没设过的测试门店仍可下达({} → {"2026-09":50000})。
  • 已部署:index-B7FCGkW1.js(本地 2026-09-20 11:22),跨 chunk 指紋 目标没读到,不可下达=1/目标没读到,不可设定=1(部署前皆 0)。
  • 写入申报(验收用、已批准):二號店与测试门店 monthly_sales_goal 各设 2026-09=50000。
老板

导出毛利表读不到成本时会标示「成本未读到」,不再当 0 计入

  • 毛利看板导出 CSV 时,若某项的配方成本读不到(断网/伺服器出错),该列会写「成本未读到」(与真的算不出来的「成本未知」分开),毛利额留空、不以 0 计入,并在档头加一列说明「本表有 N 项成本未读到(不是零成本)」。
  • 若整批成本一项都没读到,直接中止导出并在画面上说明(留在原地,不用会消失的提示)——一份每列都「成本未知」的表被拿去算毛利/提成,比不出档更危险。
  • 验收:阻断全部成本读取(HTTP 500)⇒ 拦到 21 次、导出中止、没有下载;happy path 正常下载且不误标;反例(同一份表两种标示同时出现)=人為阻断 6 项(显示「成本未读到」)+测试门店天然算不全的「莫吉托」(显示「成本未知」),莫吉托那列毛利留空。
  • 已部署:index-B7FCGkW1.js(本地 2026-09-20 11:22),跨 chunk 指紋 成本未读到=2/已中止导出=2(部署前皆 0)。

老板经营看板

老板

其余设置卡也改成四态:读不到就不画表单、平台预设会明说

  • 营业时间、桌台开台提醒、呼叫队列零时效提醒、股东订台窗口这四张卡,改成跟「钱的那一组」同一套四态:读失败时整卡换成一句人话(「这次没读到这家店的设置。为了不把平台默认值当成店家设置写回去,这里先不显示可以保存的表单。」)+「重新读取」,表单与保存钮都不出现;载入中有自己的一格画面,不与读不到共用。
  • 根因在读取层:这四张卡(外加月目标、AI 建议问题)的读取原本是 catch-all——500/断网/权限不足全部被吞成平台预设,于是画面上「看起来有值」、保存钮可按。改成只吞 404/CONFIG_NOT_FOUND(未配置才走平台预设),其余一律往上抛,卡片才拿得到「读不到」。
  • 顺手把三处与程序行为不符的注释改成真话(原本逐字写「退回默认,不是载入失败」,而码是 catch-all,下一个人会相信它已经处理好了)。
  • 验收(本机 dev,阻断 4 支设定读取、回 500):拦截命中 8 次(证明拦到)+页上出现 4 个「这次没读到」状态、其中表单 0 个、保存钮 0 个、强制点击后写入 0 笔。已验、未部署(与 P1-106/P1-107 同一批上)。

功能模块开关

老板

HQ 进店检视不再把本店低库存写进别家店的交班留言板

  • 闸门加在写入点 src/state/autoLowStockNote.ts 的 effect 开头:if (isStoreOverridden()) return。与「正在查看某店」横幅用同一颗判断(规则 70 附则三:有旗标≠有闸门)。
  • 不采「改 GET」「延后到使用者动手」「只说不做」;.catch(() => {}) 不动(档头写明的 best-effort,补上闸门之后它才真的是 best-effort 而不是掩盖)。功能不删:本店进后台的低库存自动留言(T-013③)行为完全不变。
  • 验收(正式机 index-CViaaPrK.js,规则 74 判准=计数为 0):进 HQ →「查看这家店」进二號店(横幅「🔍 正在查看 rico 測試門店二號…返回本店」证明真的进了检视)⇒ 该 session 非 GET 请求总数 0、其中 handover-notes POST 0;被检视店留言板 16→16 笔不变(读回来数,不是「我没看到请求」)。
  • 反例与去重(诚实声明):本店进后台 handover POST 0、留言 30→30 笔;同一营业日再进一次仍 0(既有去重成立)。**我没有制造出「当日本店还没写过」的状态**,所以只证到「去重后不再写」,没证到「换一个新营业日会写」;19 笔既存自动留言证明写入路径活着、没被我删掉。
  • 跨 chunk 指纹(线上 149 个 chunk):这是平台预设 1、未启用 3、咨客台 2、接手结帐 1、财务健康 5 ⇒ P1-108 上线且 P1-101/P1-104/P1-102/WALK-182 无回退。
老板

设置页「钱的那一组」改成四态:读不到就不给存,平台预设会明说

  • 收银规则(13% 税率/纳税人类型/抹零偏好)、外带打包费、Happy Hour 时段价、退款自动核准门槛、积分规则、满赠、交班时间(营业日边界)、股东银行卡与提现这 8 张设置卡,改成两端共用的「四态」:载入中/这次没读到/未配置(平台预设)/有值。
  • **读失败(断网/伺服器出错/权限不足)不再显示可保存的表单**:整格换成一句人话「这次没读到这家店的设置。为了不把平台默认值当成店家设置写回去,这里先不显示可以保存的表单。」+「重新读取」按钮。技术细节(HTTP 状态/错误码)另起一段,不混进主句。
  • 未配置(这家店没设过)与读不到是两件事:未配置仍可保存,但栏位旁会挂一枚「这是平台预设(这家店没设过)」标签。实测 Happy Hour 与满赠这两张在测试店就是未配置,标签确实出现;收银规则的 13% 是店家值,标签不出现。
  • 外带打包费/交班时间/股东提领这三个系统设定值改成「值+有没有设过」一起回来(`{fee,configured}`/`{hour,configured}`/`{enabled,configured}`),画面因此分得出「平台预设」与「店家设的 0」。
  • 验收实测(本机 dev,拦截每个 key 的读取各一次):8 张卡 × 两种失败型态(连线层中断 + HTTP 500)=16 组,全部「没有表单、没有保存钮、有重试、主句不含英文、强行点/拨每一颗控件后 0 笔写入」。happy path 两张卡做同值回写,值前后不变。

功能模块开关

全员

App 狀態判準與讀失敗守衛(本機佇列/庫存七格/滿贈/模組閘)+工具線…

  • 改了什麼:fix(P1-112/P1-116/P2-116/P2-114): App 狀態判準與讀失敗守衛(本機佇列/庫存七格/滿贈/模組閘)+工具線 [P1-112/P1-116/P2-116/P2-114/P2-123]
  • 本票含 2 顆 commit,主旨各異:App 狀態判準與讀失敗守衛(本機佇列/庫存七格/滿贈/模組閘)+工具線… / 桌面工作區第二階段(財務健康)+ inventory_api 資料層縫 + 死旗標清理…
  • 動到的檔:lib/application/cart_send_controller.dart、lib/application/offline_queue_controller.dart、lib/application/providers.dart、lib/core/theme/rico_theme.dart(共 12 檔)
  • 怎麼查:git show 2e039e23(rico_flutter)
全员

財務健康敘述卡 paint exception(非同色 border + 圓角)

  • 改了什麼:fix(P1-128): 財務健康敘述卡 paint exception(非同色 border + 圓角)
  • 本票含 2 顆 commit,主旨各異:財務健康敘述卡 paint exception(非同色 border + 圓角) / 桌面工作區第二階段(財務健康)+ inventory_api 資料層縫 + 死旗標清理…
  • 動到的檔:lib/features/owner/finance_health_sheet.dart、lib/features/owner/finance_narration_card.dart、test/p1128_paint_exception_test.dart、lib/data/inventory/inventory_api.dart(共 12 檔)
  • 怎麼查:git show 7744b6a1(rico_flutter)
老板

收银台在「这张单已被别处结帐」时会说「不要再收一次」

  • 同一张单被两台装置接手时,输的那台按结帐会看到:「这张单已经在别处结帐(或已被作废)——多半是另一台装置接手收的款。这里不要再收一次;请按『重新读取』看未結單清单的最新状态。」
  • 若那张单连伺服器上都找不到了,讯息改成「可能已被另一台装置结帐或作废」并指出去哪看未結單清单。
  • 三个结帐出口(直接收款/分账收款/记部分结账)与「未結單接手结帐」都接上同一句;认不出来的错误一律保留原本的通用文案,不把「网路断了」讲成「别人结掉了」。
老板

脏数据不再被读成数字:推广预算这类的「12abc」一律显示为读不到

  • 推广活动 ROI 卡上的「预算金额」若后端回的是「12abc」这种带尾巴的字串,现在显示为**读不到**(不是硬转成 12)。
  • 同一支检查也用在桌台开台提醒分钟数上:非数字、布林、空值一律视为未设定,不会被读成 0 分钟。

这份日志从 2026 年 7 月记起,更早的没有逐条列。想看它现在都会什么,回全部功能,或者进演示店自己点一点。