The seeded forecast module opens on six rows of zeros: commit_amount / best_case_amount / pipeline_amount are 0 in 6 of 7 snapshots, so 4 of its 13 columns are dead on a fresh install
What a sales manager sees
销售预测 / Forecasts, grouped by period, on a fresh pnpm demo:reset:
| 周期 |
配额 |
已成交金额 |
承诺金额 |
最佳情况 |
管道金额 |
预期金额 |
达成率 |
覆盖倍数 |
| Q4 2025 |
1,200,000 |
1,308,000 |
0 |
0 |
0 |
1308000 |
109 |
0 |
| Q3 2025 |
1,100,000 |
1,045,000 |
0 |
0 |
0 |
1045000 |
95 |
0 |
| Q2 2026 |
1,400,000 |
1,485,000 |
0 |
0 |
0 |
1485000 |
106.07… |
0 |
| Q1 2026 |
1,300,000 |
1,196,000 |
0 |
0 |
0 |
1196000 |
92 |
0 |
| Jun 2026 |
460,000 |
414,000 |
0 |
0 |
0 |
414000 |
90 |
0 |
| Jul 2026 |
480,000 |
505,000 |
0 |
0 |
0 |
505000 |
105.20… |
0 |
Measured: GET /api/v1/data/crm_forecast?top=20 → 7 rows, and commit_amount, best_case_amount, pipeline_amount, coverage_ratio are each 0/null in 6 of the 7.
A forecast whose commit, best-case and pipeline are all zero cannot answer the question the object exists for — will we make the number? Closed-vs-quota alone is a scoreboard, not a forecast. coverage_ratio is 0 for the same reason: its numerator is pipeline_amount.
Where it comes from
src/data/revenue.seed.ts zeroes them explicitly for the historical rows:
// :342-344, and again at :396-398
pipeline_amount: 0,
best_case_amount: 0,
commit_amount: 0,
One row (:381-383) carries real numbers — 760000 / 540000 / 360000 — and it is the only one where the module looks like a forecast. So the shape is already authored; it is just not applied to the rest of the set.
forecast-snapshot.flow.ts computes all three correctly from open opportunities (its header documents the rules: pipeline = every open opp closing in the period, best case = open with forecast_category ∈ {best_case, commit}, and so on). But on a fresh install the scheduled sweep has not run, so the seed is what a first-time user sees.
This is the same shape as #1212 (the quota-attainment dashboard widget that is empty on a fresh install) — the demo dataset not exercising a capability the app actually has.
Suggested
Backfill the three columns in revenue.seed.ts for the historical periods, consistent with each period's closed amount and quota — the arithmetic does not have to be real, it has to be plausible and non-zero so the columns demonstrate what they are for. coverage_ratio then computes itself.
Worth checking at the same time whether the seeded periods should include the current quarter: every row above is historical, so the one forecast a manager would actually open — this quarter's — does not exist until the scheduled flow writes it.
Adjacent, filed upstream
The same list shows 达成率 as 106.07142857142857 and 预期金额 as 1485000 (unseparated) — both are Field.formula({ scale: 2 }) fields, and scale plus number formatting are not applied to computed fields. That is a platform defect, tracked at objectstack-ai/objectstack#10280; nothing to fix here.
Found by driving the app as a sales manager in a browser on 17.1.0.
The seeded forecast module opens on six rows of zeros:
commit_amount/best_case_amount/pipeline_amountare 0 in 6 of 7 snapshots, so 4 of its 13 columns are dead on a fresh installWhat a sales manager sees
销售预测 / Forecasts, grouped by period, on a fresh
pnpm demo:reset:Measured:
GET /api/v1/data/crm_forecast?top=20→ 7 rows, andcommit_amount,best_case_amount,pipeline_amount,coverage_ratioare each0/null in 6 of the 7.A forecast whose commit, best-case and pipeline are all zero cannot answer the question the object exists for — will we make the number? Closed-vs-quota alone is a scoreboard, not a forecast.
coverage_ratiois 0 for the same reason: its numerator ispipeline_amount.Where it comes from
src/data/revenue.seed.tszeroes them explicitly for the historical rows:One row (
:381-383) carries real numbers —760000 / 540000 / 360000— and it is the only one where the module looks like a forecast. So the shape is already authored; it is just not applied to the rest of the set.forecast-snapshot.flow.tscomputes all three correctly from open opportunities (its header documents the rules: pipeline = every open opp closing in the period, best case = open withforecast_category ∈ {best_case, commit}, and so on). But on a fresh install the scheduled sweep has not run, so the seed is what a first-time user sees.This is the same shape as #1212 (the quota-attainment dashboard widget that is empty on a fresh install) — the demo dataset not exercising a capability the app actually has.
Suggested
Backfill the three columns in
revenue.seed.tsfor the historical periods, consistent with each period's closed amount and quota — the arithmetic does not have to be real, it has to be plausible and non-zero so the columns demonstrate what they are for.coverage_ratiothen computes itself.Worth checking at the same time whether the seeded periods should include the current quarter: every row above is historical, so the one forecast a manager would actually open — this quarter's — does not exist until the scheduled flow writes it.
Adjacent, filed upstream
The same list shows
达成率as106.07142857142857and预期金额as1485000(unseparated) — both areField.formula({ scale: 2 })fields, andscaleplus number formatting are not applied to computed fields. That is a platform defect, tracked at objectstack-ai/objectstack#10280; nothing to fix here.Found by driving the app as a sales manager in a browser on
17.1.0.