From c520cfbdd8af0c210c1e7696f8dc5f2b761e9104 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 02:03:42 +0000 Subject: [PATCH 1/2] fix(dashboards): leave the List Price by Category tile out when revenue is disabled The tile binds revenue's product_metrics. With the revenue module disabled, the platform stops serving its objects and datasets and the tile drew a 'dataset not defined' error. requiresObject: 'crm_product' makes the dashboard omit it instead; with revenue on it draws as before. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01J8VVAnoe7eWhDPCCPzDBh5 --- .changeset/2383-module-off-revenue-tile.md | 7 +++++++ src/sales/dashboards/crm.dashboard.ts | 6 ++++++ test/package-ownership.test.ts | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 .changeset/2383-module-off-revenue-tile.md diff --git a/.changeset/2383-module-off-revenue-tile.md b/.changeset/2383-module-off-revenue-tile.md new file mode 100644 index 000000000..ed3256513 --- /dev/null +++ b/.changeset/2383-module-off-revenue-tile.md @@ -0,0 +1,7 @@ +--- +"hotcrm": patch +--- + +**The List Price by Category tile is left out when the revenue module is disabled (#2383).** The tile on the CRM Overview dashboard reads revenue's product catalogue. When revenue is disabled, the platform stops serving that module's objects and datasets, and the tile drew a red "Dataset "product_metrics" is not defined in this environment" error. It now asks for the product object first, so the dashboard simply leaves it out and the other tiles fill the page. With revenue enabled, nothing changes: the tile draws as before. + +The other screens of the app that reach a disabled module were measured on ObjectStack 18.2.0 too. The Campaign lookup on the opportunity form and the Related Case lookup on tasks and events show their value on the record page. On the edit form they read empty, with an empty picker, and saving keeps the stored value. The related lists (campaign members on a lead, quotes and line items on an opportunity) read empty. None of these raises an error. **Add to Campaign** on leads and contacts and **Generate Quote** on opportunities still show when their module is off and fail when used. Hiding them needs a platform change, because the permission check those buttons read still grants a disabled module's objects. That change is reported to the platform. diff --git a/src/sales/dashboards/crm.dashboard.ts b/src/sales/dashboards/crm.dashboard.ts index b9858f0b1..67a3e3a93 100644 --- a/src/sales/dashboards/crm.dashboard.ts +++ b/src/sales/dashboards/crm.dashboard.ts @@ -145,6 +145,12 @@ export const CrmOverviewDashboard: Dashboard = { // resolve — it just means nothing on a shared product catalog, where the // column records whoever created the entry. filterBindings: { dateRange: false, owner_id: false }, + // `product_metrics` ships in the revenue module, so the tile asks for + // that dataset's object and is left out when revenue is disabled (#2383). + // Measured on 18.2.0: a disabled package's objects drop out of the served + // object list this key is checked against; without it the tile drew a + // red "Dataset "product_metrics" is not defined in this environment". + requiresObject: 'crm_product', dataset: 'product_metrics', dimensions: ['category'], values: ['list_price_sum'], layout: { x: 0, y: 6, w: 12, h: 4 }, chartConfig: { diff --git a/test/package-ownership.test.ts b/test/package-ownership.test.ts index 7c6d9c9b3..e2a136861 100644 --- a/test/package-ownership.test.ts +++ b/test/package-ownership.test.ts @@ -174,6 +174,25 @@ describe('every object has exactly one owner', () => { } }); + it('an app tile on a module dataset asks for that dataset\'s object, so a disabled module leaves it out (#2383)', () => { + // A disabled package's objects drop out of the served object list a + // widget's `requiresObject` is checked against (measured on 18.2.0), so + // the tile is omitted instead of drawing "dataset not defined". With the + // module on, the object is served and the tile draws as before. + const moduleDatasets = new Map(); + for (const id of MODULE_IDS) { + for (const ds of (body(id).datasets ?? []) as AnyRec[]) moduleDatasets.set(ds.name, { owner: id, object: ds.object }); + } + const tiles = ((body(APP_ID).dashboards ?? []) as AnyRec[]).flatMap((d) => + ((d.widgets ?? []) as AnyRec[]).filter((w) => moduleDatasets.has(w.dataset)).map((w) => ({ dashboard: d.name, w }))); + expect(tiles.map(({ dashboard, w }) => `${dashboard}.${w.id}`)).toEqual(['crm_overview_dashboard.top_products']); + for (const { dashboard, w } of tiles) { + const { owner, object } = moduleDatasets.get(w.dataset)!; + expect(w.requiresObject, `${dashboard}.${w.id}`).toBe(object); + expect(namesOf(body(owner).objects), owner).toContain(object); + } + }); + it('the permission sets stay whole in the app package (ADR-0130, 2026-09-02 addendum)', () => { for (const id of MODULE_IDS) expect(namesOf(body(id).permissions), id).toEqual([]); const grantingOn = (objects: string[]) => ((body(APP_ID).permissions ?? []) as AnyRec[]).filter((set) => From 22d288e74251e535d804de29dda112c593e2a9a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 02:18:28 +0000 Subject: [PATCH 2/2] chore(changeset): carry the claim's Clause and Metadata-protocol lines (#2383) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01J8VVAnoe7eWhDPCCPzDBh5 --- .changeset/2383-module-off-revenue-tile.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.changeset/2383-module-off-revenue-tile.md b/.changeset/2383-module-off-revenue-tile.md index ed3256513..77ca247b9 100644 --- a/.changeset/2383-module-off-revenue-tile.md +++ b/.changeset/2383-module-off-revenue-tile.md @@ -2,6 +2,9 @@ "hotcrm": patch --- +Clause-②: no +Metadata-protocol: none + **The List Price by Category tile is left out when the revenue module is disabled (#2383).** The tile on the CRM Overview dashboard reads revenue's product catalogue. When revenue is disabled, the platform stops serving that module's objects and datasets, and the tile drew a red "Dataset "product_metrics" is not defined in this environment" error. It now asks for the product object first, so the dashboard simply leaves it out and the other tiles fill the page. With revenue enabled, nothing changes: the tile draws as before. The other screens of the app that reach a disabled module were measured on ObjectStack 18.2.0 too. The Campaign lookup on the opportunity form and the Related Case lookup on tasks and events show their value on the record page. On the edit form they read empty, with an empty picker, and saving keeps the stored value. The related lists (campaign members on a lead, quotes and line items on an opportunity) read empty. None of these raises an error. **Add to Campaign** on leads and contacts and **Generate Quote** on opportunities still show when their module is off and fail when used. Hiding them needs a platform change, because the permission check those buttons read still grants a disabled module's objects. That change is reported to the platform.