十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Midday 的 Runway 计算为什么把负债排除在现金余额之外?

Midday 的 Runway 计算为什么把负债排除在现金余额之外? Midday 的 Runway 计算为什么把负债排除在现金余额之外【免费下载链接】middayInvoicing, Time tracking, File reconciliation, Storage, Financial Overview your own Assistant made for Freelancers项目地址: https://gitcode.com/GitHub_Trending/mi/midday如果你给 Midday 连接了多个银行账号包含信用卡和贷款账户再看财务概览里的 Runway 数字会发现它并不等于所有账号余额的总和——信用卡欠款、贷款余额都不会加进现金余额甚至不参与抵消。这篇文档解释这个设计在代码里的具体落点并给出在项目里实际核对这条计算路径的方法。适用对象是已经能阅读仓库源码、想确认或排查 Runway 数值来源的开发者。先看哪些账户类型算现金Midday 把所有银行账号归为五类Runway 只对其中两类求和类型说明例子是否计入 Runway 现金depository流动现金账户Checking、Savings✅other_asset其他流动资产Treasury、Money Market✅credit信用卡债务Credit cards❌loan贷款账户Business loans、Lines of credit❌other_liability其他负债-❌这个边界在代码里由两个共享常量固化定义在 account.ts/** * Account types that represent liquid cash or cash-equivalents. * Used for: Runway, Net Position (cash side), Balance Sheet (assets) */ export const CASH_ACCOUNT_TYPES [depository, other_asset] as const; /** * Account types that represent debt/liabilities. * Used for: Net Position (debt side), Balance Sheet (liabilities) */ export const DEBT_ACCOUNT_TYPES [credit, loan] as const;注意注释里明确写了各常量的用途CASH_ACCOUNT_TYPES用于 Runway、Net Position 的现金侧和资产负债表的资产侧DEBT_ACCOUNT_TYPES只出现在 Net Position 的负债侧和资产负债表的负债侧Runway 不在其使用范围内。docs/runway-burn-rate-analysis.md 给出的设计理由是Runway shows how long you can operate with available cash. Debt is not cash.也就是说Runway 回答的问题是现有现金能支撑多少个月把负债加进分子或者做减法都会偏离这个问题。负债并没有被丢掉只是被放到别的报表里见下文。getRunway 里负债是如何被过滤掉的Runway 查询实现位于 reports.ts 的getRunway()。现金侧的过滤条件就是直接排除负债类型的关键const balanceConditions [ eq(bankAccounts.teamId, teamId), eq(bankAccounts.enabled, true), inArray(bankAccounts.type, [...CASH_ACCOUNT_TYPES]), ];三个条件叠加只看本团队的账号、只看启用状态enabled true停用账号不计入、类型必须落在CASH_ACCOUNT_TYPES内。credit和loan账号在WHERE阶段就被排除不会以任何形式进入分子。余额汇总时对币种做了处理账号币种等于目标币种时取balance账号的baseCurrency等于目标币种时取baseBalance折算后余额否则该账号记 0。分母是烧钱率文档和代码一致Runway (months) Cash Balance / Median Monthly Burn Rate (last 3 completed months)getRunway()取最近 3 个完整月不含当月的 burn rate过滤掉非正值后取中位数再Math.round(totalBalance / medianBurn)。代码注释解释了用中位数而非平均值的原因中位数对一次性支出尖峰如季度报税、年度订阅不敏感而简单平均会被这些值拉偏。两个早退条件也直接影响结果团队没有目标币种、或 3 个月内没有任何 burn 数据时直接返回{ months: 0, medianBurn: 0 }。对外入口是 tRPC 的reports.runway端点见 reports.ts 路由它只是把teamId和currency透传给getRunway。负债去了哪里Net Position 与 Balance Sheet排除出 Runway 不等于排除出系统。不同报表对各账户类型的取舍在 docs/runway-burn-rate-analysis.md 的 Account Type Usage by Report 表中列得很清楚报表depositoryother_assetcreditloanRunway (cash)✅✅❌❌Cash Balance✅✅❌❌Net Position (cash)✅✅--Net Position (debt)--✅❌Balance Sheet✅✅✅✅Net Position Cash - Credit Card Debt实现见 bank-accounts.ts 的getNetPosition()。现金侧同样只取CASH_ACCOUNT_TYPES信用卡侧对余额取Math.abs()后求和因为不同银行提供商对信用卡余额的正负约定不一致Plaid 存正值表示欠款GoCardless 存负值接入时已做归一化查询时再用Math.abs()兜底。loan不在 Net Position 里文档对此的说明是Net Position 提供的是简单的 cash vs credit card 视图贷款在 Balance Sheet 中单独处理。Balance Sheet才包含全部账户类型资产侧是现金depositoryother_asset加应收账款负债侧包含信用卡债务、贷款账户债务并按 loan-proceeds 是否超过 12 个月拆分为短期/长期贷款。所以核对负债为什么不进现金余额时判断标准是Runway 的分子只含depository/other_asset信用卡只在 Net Position 的减项出现贷款只出现在 Balance Sheet 的负债侧。另外信用卡虽然不计入现金余额它的支出会进入 burn rate。为避免双重计数分类 categories.ts 中credit-card-payment和internal-transfer两个类别带excluded: true标记同步信用卡时会同时产生卡上消费和从活期账户的还款两笔记录文档示例CC purchase $5k CC payment $5k实际支出是 $5k 不是 $10k排除还款类别就是为了防止 burn rate 翻倍。在项目里验证这条计算路径按顺序做以下检查即可确认负债确实被排除1. 确认常量定义。打开 packages/banking/src/utils/account.ts确认CASH_ACCOUNT_TYPES只有depository和other_assetDEBT_ACCOUNT_TYPES是credit、loan。2. 确认查询过滤条件。打开 getRunway 实现确认现金余额查询使用了inArray(bankAccounts.type, [...CASH_ACCOUNT_TYPES])且不存在任何对credit/loan账号的加项或减项。3. 运行 e2e 计算测试。packages/db/package.json 里提供了测试脚本需要先初始化测试数据库会连接TEST_DB_HOST/TEST_DB_PORT默认localhost:5433创建并写入midday_test测试库不影响业务数据再跑测试# 在仓库根目录下先初始化测试数据库需要 PostgreSQL 可达 cd packages/db bun run test:e2e:setup # 运行含 Runway 用例的计算 e2e 测试 cd packages/db bun run test:e2e测试文件 calculations-e2e.test.ts 中有一个 Runway 用例其注释展示了测试种子的示例数据现金侧由 Checking(50000) Savings(25000) 一个欧元账户的 baseBalance(11000) 组成共 86000最近 3 个完整月的 burn 为 [2000, 1500, 500]中位数 1500断言为result.months Math.round(86000 / 1500)且medianBurn 1500。以上数值是测试种子的示例用于说明计算口径不是生产数据预期值。同文件还有一个无银行账号的团队返回 0的用例对应现金账户为空 → Runway 为 0的早退分支。测试通过即验证了中位数取法、币种折算和现金账户过滤这整条路径。4.可选用 SQL 核对生产数据中的现金账户口径。文档给了排查用的查询其中YOUR_TEAM_ID需替换为实际团队 ID-- Check cash accounts SELECT name, type, balance, enabled FROM bank_accounts WHERE team_id YOUR_TEAM_ID AND type IN (depository, other_asset);如果 Runway 数值对不上先把这个查询的结果和getRunway的三个过滤条件逐一对照账号类型是否为depository/other_asset、enabled是否为 true、外币账号的baseBalance是否有值。相关边界与已知现象以下几点在核对 Runway 数值时容易被误判均来自 docs/runway-burn-rate-analysis.md 的 Troubleshooting 一节Runway 显示 0三个文档列出的可能原因是所选日期范围内没有可计费的支出注意未分类的支出仍会计入 burn rate、所有现金账户被停用、外币账户缺少baseBalance或团队基础币种设置不正确。信用卡余额是正值是正常的数据库中信用卡余额以正值存储表示欠款Plaid 原生返回正值GoCardless 返回负值并在接入时归一化为正值查询侧另有Math.abs()兜底处理历史数据。停用账号不计入所有现金侧查询都带enabled true条件停用的depository账号不会贡献余额。再同步不会更新已有记录文档指出 GoCardless 历史负值余额的重同步不会覆盖旧记录upsert 忽略重复项文档给出了强制修正的 SQLUPDATE bank_accounts SET balance ABS(balance) WHERE type credit AND balance 0;。这是一条会写库的语句执行前请自行确认影响范围本文不把它作为默认步骤。Runway、Net Position 和 Balance Sheet 三个数字口径不同是有意的设计Runway 只回答现金还能撑几个月想看到信用卡抵扣后的净额看 Net Position想看含贷款在内的完整财务状况看 Balance Sheet。三者各自过滤哪些账户类型以本文先看哪些账户类型算现金一节的对照表和 docs/runway-burn-rate-analysis.md 为准。【免费下载链接】middayInvoicing, Time tracking, File reconciliation, Storage, Financial Overview your own Assistant made for Freelancers项目地址: https://gitcode.com/GitHub_Trending/mi/midday创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表