Skip to content

fix(forecast): drop the column total on the formula field expected_amount - #1986

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-1980-forecast-formula-summary
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-1980-forecast-formula-summary

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #1980

What

The All Forecasts grid (all_forecasts on crm_forecast) asked for a column total (summary: 'sum') on Expected (expected_amount). That field is a formula: Closed Won + Commit, computed per row. A formula has no stored column to aggregate, so the spec's aggregate compatibility table refuses sum on it.

This PR follows the maintainer's ruling on the card (「p3 bug · 去掉该列合计(推荐)」). It deletes only the summary key on that column and adds a one-line comment saying why. The column stays, and so do the totals on Quota, Closed Won, Commit, Best Case and Pipeline. A reader can still get the expected figure as Closed Won total + Commit total. It adds no stored field, no writer and no platform change.

Files: src/sales/views/forecast.view.ts (one key removed, one comment line), and .changeset/1980-forecast-formula-summary.md ('hotcrm': patch, since a total disappears from a list view).

The premise, re-measured on origin/main cf422b83

Main moved from the dispatch base 39ba05e2 to cf422b83 (#1985, which touches src/sales/flows/forecast-snapshot.flow.ts, not this file). The premise still holds on the new head.

  • The defect site. src/sales/views/forecast.view.ts:28 read { field: 'expected_amount', width: 140, align: 'right', summary: 'sum' }. src/sales/objects/forecast.object.ts:177 declares expected_amount: Field.formula({ ... }).
  • The refusal table ships in the installed pin. @objectstack/spec 17.6.0, dist/data/index.mjs, has AGGREGATE_FIELD_TYPE_COMPATIBILITY. Its sum row is [...ADDITIVE_AGGREGATE_FIELD_TYPES, ...BOOLEAN_AGGREGATE_FIELD_TYPES], which reads at runtime as ["number","currency","rating","slider","progress","summary","boolean","toggle"]. formula is not in it, and neither is percent.
  • The view is grouped. The same pin's dist/ui/index.mjs carries compileListViewGroupQuery. It compiles each column summary of a grouped list view into a server-side group-header aggregation node, and sum maps to function sum. all_forecasts is grouped by period_label. I did not measure whether today's renderer issues that query (see Acceptance notes).

Class sweep: every column summary in every registered view

Instrument. A one-off tsx script, not committed. It imports allViews and allObjects from objectstack.composition.ts, walks list and every listViews entry, reads each summarised field's declared type, and maps the summary member to its aggregation function, using COLUMN_SUMMARY_AGGREGATION copied from the installed dist/ui/index.mjs. It then judges the pair with the spec's own isAggregateCompatibleWithFieldType. It walked 14 view sets and found 20 summary sites, the same count as git grep -n "summary:" -- 'src/*/views/*.view.ts' (20 lines: 18 sum, 2 avg).

Positive control. The known pair crm_forecast:all_forecasts expected_amount sum x formula reads REFUSED.

Result before the fix: 1 refused pair out of 20. After: 0 refused out of 19.

view field summary field type verdict
all_accounts annual_revenue sum currency ok
all_accounts number_of_employees avg number ok
all_campaigns budgeted_cost sum currency ok
all_campaigns expected_revenue sum currency ok
all_contracts contract_value sum currency ok
all_forecasts quota, closed_amount, commit_amount, best_case_amount, pipeline_amount sum currency ok
all_forecasts expected_amount sum formula refused, fixed here
all_articles helpful_count sum number ok
open_opportunities, all_opportunities amount, expected_revenue sum currency ok
all_products list_price avg currency ok
all_quotes subtotal, tax, total_price sum currency ok

No other site is in this defect class, so no other view file is edited. src/sales/views/opportunity.view.ts, which #1950 holds, has no refused pair: both expected_revenue columns are currency. A grep for summary: outside views/ returns 10 hits. None is a column summary: they are activity-feed text in opportunity.object.ts and case.object.ts, an action body in contact.actions.ts, and seed rows in service.seed.ts.

Tests that pin these summaries: none. A grep of test/ for summary, expected_amount and all_forecasts finds no view-snapshot test and no summary roster. The docs promise no total on Expected either: content/docs/sales/forecasting*.mdx describe it as a derived per-row formula.

Verification

All at HEAD 3cba8f5f, the commit pnpm verify ran on:

  • OS_VERIFY_LOCK_SLOT=hotcrm-issue-1980 bash .../os-verify-lock.sh -c 'pnpm verify' reads os-verify-lock: VERDICT command-exit 0 · held the lock 169s (2m49s) · waited 0s.
    • validate: 18 Objects, 14 Views, 7 author-time warnings, all pre-existing and none from this diff.
    • typecheck clean.
    • lint: 1 warning(s), 16 suggestion(s).
    • ✓ i18n lint gate: 0 i18n/missing-* issues.
    • ✓ source hygiene clean.
    • ✓ source token ratchet clean.
    • build: Artifact: dist/objectstack.json.
    • test: Test Files 174 passed (174) · Tests 3728 passed | 1 skipped (3729).

Token ratchet (pnpm hygiene:tokens). The comment line costs 0 tokens, since the ratchet counts comment-stripped source.

src/sales base cf422b83 after 3cba8f5f ceiling
business semantics ~54,323 ~54,323 ~55,000
interaction layer ~28,003 ~27,999 ~31,000
authored total ~97,805 ~97,801 ~100,000

The other three packages are untouched.

Acceptance notes

  • Not committed: the sweep instrument above. It was a one-off measurement, and no gate is added (AGENTS.md scope rule 3).
  • Ratchet hint, not acted on. On both base and head, the ratchet prints a hint to re-anchor the src/sales interaction-layer ceiling from ~31,000 to ~30,000. That file is outside this card's claimed surface, and a sibling card shares the container. Carrier: none.
  • Not measured: the grouped-header query path. I did not measure whether the console's grouped All Forecasts view issues compileListViewGroupQuery on 17.6.0. If it does, the old sum_expected_amount node would have been refused server-side rather than rendered as a client footer. Either way the fix is the same, and this PR removes that node.
  • Not touched here: Move the six flow value expressions os validate --strict flags from the {…} template dialect to CEL envelopes, with tests #1984's src/sales/flows/forecast-snapshot.flow.ts.
  • Commit trailer. The one commit's Co-Authored-By trailer names a model. The dispatch contract asks for model-free trailers, and history is not rewritten for it. The squash-merge message can drop that line.

Generated by Claude Code

…ount

The all_forecasts grid summed expected_amount, a formula field. The spec's
AGGREGATE_FIELD_TYPE_COMPATIBILITY sum row admits number, currency, rating,
slider, progress, summary, boolean and toggle, not formula: a formula has no
stored column to aggregate. The column stays; Closed Won and Commit keep their
totals, which add up to the expected figure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT
@vercel

vercel Bot commented Oct 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Oct 2, 2026 10:02pm UTC

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

metadata Declarative metadata — schema, security posture, UI surfaces

Projects

None yet

2 participants