Description
On the Executive Overview dashboard, the dashboard-level dateRange filter is AND-ed on top of each widget's own close_date filter instead of being suppressed for widgets that already declare their own time window. The intersection of the two windows silently wins, so widgets labelled "YTD" or "last 12 months" actually report only the current quarter.
Four widgets in src/dashboards/executive.dashboard.ts are affected. Because the headline number still renders plausibly, there is no error to notice — the dashboard just reports the wrong figure to leadership.
Steps to Reproduce
pnpm dev and open /_console/apps/app.objectstack.hotcrm/dashboard/executive_dashboard
- Leave the date filter at its default (
this_quarter)
- Inspect the
POST /api/v1/analytics/dataset/query responses (each response echoes its generated sql)
Expected vs Actual
A widget that declares its own close_date filter should own its time semantics; the dashboard dateRange should not be injected into it.
total_revenue_ytd — titled "Total Revenue (YTD)" / 年度累计收入, described "Closed-won revenue this year":
SELECT SUM("crm_opportunity"."amount") AS "total_amount" FROM "crm_opportunity"
WHERE "crm_opportunity"."stage" = $1
AND "crm_opportunity"."close_date" >= $2 -- {current_year_start}, from the widget
AND "crm_opportunity"."close_date" >= $3 -- quarter start, injected
AND "crm_opportunity"."close_date" <= $4 -- quarter end, injected
The card reads 781,012. That is this quarter's closed-won revenue, not YTD.
revenue_trend — titled "Revenue Trend", described "Closed-won revenue over the last 12 months". Same double bound; the chart renders 3 points inside July rather than 12 months.
revenue_by_industry — titled "YTD closed-won revenue split by customer industry". Same double bound; the donut is quarter-scoped.
pipeline_by_stage — this is the most misleading of the four. It reports open pipeline (stage NOT IN (closed_won, closed_lost)), but gets quarter-bounded anyway:
SELECT "crm_opportunity"."stage", SUM("crm_opportunity"."amount") FROM "crm_opportunity"
WHERE "crm_opportunity"."stage" NOT IN ($1, $2)
AND "crm_opportunity"."close_date" >= $3
AND "crm_opportunity"."close_date" <= $4
GROUP BY "crm_opportunity"."stage"
Open deals forecast to close next quarter are silently dropped, so total open pipeline is understated with no indication on screen.
Environment
- Branch
main @ 00c83e9, dev server on :4001
- Affected file:
src/dashboards/executive.dashboard.ts
Suggested Fix
Short term, in this repo: add filterBindings: { dateRange: false } to total_revenue_ytd, revenue_trend, revenue_by_industry, and pipeline_by_stage, matching the existing opt-outs on total_accounts / total_contacts / open_leads.
Longer term, the framework default looks wrong. Today dateRange is injected into every widget bound to an object that has the field, and opting out is manual and easy to forget — the failure mode is a silently incorrect number rather than an error. Worth considering: when a widget already constrains the dateRange field itself, let the widget's filter take precedence (or at least warn at build time). Related context in the ObjectStack 15 / framework#2501 note already recorded at src/dashboards/executive.dashboard.ts:91-97.
Once fixed, the titles and descriptions become accurate as written; no copy changes needed.
Description
On the Executive Overview dashboard, the dashboard-level
dateRangefilter is AND-ed on top of each widget's ownclose_datefilter instead of being suppressed for widgets that already declare their own time window. The intersection of the two windows silently wins, so widgets labelled "YTD" or "last 12 months" actually report only the current quarter.Four widgets in
src/dashboards/executive.dashboard.tsare affected. Because the headline number still renders plausibly, there is no error to notice — the dashboard just reports the wrong figure to leadership.Steps to Reproduce
pnpm devand open/_console/apps/app.objectstack.hotcrm/dashboard/executive_dashboardthis_quarter)POST /api/v1/analytics/dataset/queryresponses (each response echoes its generatedsql)Expected vs Actual
A widget that declares its own
close_datefilter should own its time semantics; the dashboarddateRangeshould not be injected into it.total_revenue_ytd— titled "Total Revenue (YTD)" / 年度累计收入, described "Closed-won revenue this year":The card reads 781,012. That is this quarter's closed-won revenue, not YTD.
revenue_trend— titled "Revenue Trend", described "Closed-won revenue over the last 12 months". Same double bound; the chart renders 3 points inside July rather than 12 months.revenue_by_industry— titled "YTD closed-won revenue split by customer industry". Same double bound; the donut is quarter-scoped.pipeline_by_stage— this is the most misleading of the four. It reports open pipeline (stage NOT IN (closed_won, closed_lost)), but gets quarter-bounded anyway:Open deals forecast to close next quarter are silently dropped, so total open pipeline is understated with no indication on screen.
Environment
main@ 00c83e9, dev server on :4001src/dashboards/executive.dashboard.tsSuggested Fix
Short term, in this repo: add
filterBindings: { dateRange: false }tototal_revenue_ytd,revenue_trend,revenue_by_industry, andpipeline_by_stage, matching the existing opt-outs ontotal_accounts/total_contacts/open_leads.Longer term, the framework default looks wrong. Today
dateRangeis injected into every widget bound to an object that has the field, and opting out is manual and easy to forget — the failure mode is a silently incorrect number rather than an error. Worth considering: when a widget already constrains thedateRangefield itself, let the widget's filter take precedence (or at least warn at build time). Related context in the ObjectStack 15 / framework#2501 note already recorded atsrc/dashboards/executive.dashboard.ts:91-97.Once fixed, the titles and descriptions become accurate as written; no copy changes needed.