Skip to content

fix(dashboards): leave the List Price by Category tile out when revenue is disabled (#2383) - #2390

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-jcpwiz
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-jcpwiz

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread

Part of #2383. This PR fixes the dashboard tile. The other surfaces measured either already tolerate a disabled module, or need a platform change before HotCRM can hide them. The report on #2383 lists each one, and the seat decides what happens to the card.

Clause-②: no
Metadata-protocol: none

维护者速读

What changes

src/sales/dashboards/crm.dashboard.ts: the top_products widget (List Price by Category) now carries requiresObject: 'crm_product'. Its dataset product_metrics ships in the revenue module and reads crm_product.

Measured on 18.2.0: when a package is disabled, its objects drop out of GET /api/v1/meta/object (the served list requiresObject is checked against). So with revenue off, the console leaves the tile out. Without this change it drew Dataset "product_metrics" is not defined in this environment in red. With revenue on, the object is served and the tile draws as before.

test/package-ownership.test.ts adds one case. Every app-package dashboard widget bound to a dataset that ships in a module asks, through requiresObject, for that dataset's own object, and that object belongs to the same module. Today the only such widget is crm_overview_dashboard.top_products.

No in-app docs change. The reasons are under Acceptance notes.

Tests

  • pnpm verify on c520cfb: validate, typecheck, lint, hygiene, hygiene:tokens and build all exit 0, and vitest run gives Test Files 253 passed (253) · Tests 4881 passed, 1 skipped. VERIFY_EXIT=0.
  • 22d288e only adds the claim's two lines to the changeset. On that head, the six suites that read .changeset or this test file give 6 files and 143 tests passed. pnpm hygiene and pnpm hygiene:tokens exit 0.
  • Ablation, done after the fix was committed: deleting the requiresObject line (one hit before, zero after) turns the new case red with crm_overview_dashboard.top_products: expected undefined to be 'crm_product'. Restored with git checkout HEAD --, and git diff HEAD is empty afterwards.

Acceptance notes

How a module was disabled. PATCH /api/v1/packages/app.objectstack.hotcrm.{marketing,revenue,service}/disable answers 422 WRITABLE_PACKAGE_REQUIRED on 18.2.0 for all three modules, because objectstack#28117 is still open. Instead I used the platform's own persisted-state path. I wrote OS_HOME/package-state/env_local.json as {"disabled":["app.objectstack.hotcrm.MODULE"]} and booted objectstack dev, where AppPlugin.seedPersistedDisabledPackages reads it. GET /api/v1/packages then reports the module enabled: false, and its objects answer 404 OBJECT_PACKAGE_DISABLED on the data plane and on the by-name reads. That matches the order's mechanism assumption 1. Each module was off on its own, all on one database, browsed as the dev admin and, for the actions, as a marketing_user holder.

Each surface:

Surface Module With the module off (18.2.0) Verdict
List Price by Category tile (CRM Overview) revenue red "dataset not defined" error before; left out after fixed
crm_opportunity.crm_campaign lookup marketing the record page shows the stored campaign title. On the edit form it reads Select… and the picker says "No options found". A save sends only the dirty fields, so the value is kept. No error. already tolerant
crm_task.related_to_case, crm_event.related_to_case service the record page shows the case title. On the edit form it reads Select…. A save PATCHes {"description":…} only, so related_to_case keeps its value (measured). No error. already tolerant
Related lists: Campaign Members (lead), Quotes and Opportunity Line Items (opportunity) marketing / revenue read 0 · No related records found, or fold into "+N empty". No error. already tolerant
Other dashboards (Sales Performance, Executive, Sales Activity) all no request fails, no tile changes already tolerant
Add to Campaign on lead (create_campaign) and contact (add_contact_to_campaign) marketing still in the row menu and the selection bar. The dialog says "This parameter cannot be shown: the field it is backed by is missing from the object metadata…". A REST launch gives 404 Flow 'campaign_lead_member_enroll' not found. platform-only
Generate Quote (generate_quote) revenue still in the header. A click shows the toast Flow 'quote_generation' not found. platform-only

Why the actions are platform-only. Their visible predicates read current_user.can(...). With marketing off, GET /api/v1/auth/me/permissions, as the admin and as a marketing_user holder, still lists crm_campaign_member with allowCreate: true while the data plane answers 404. So can() answers true and the button draws. The console's predicate context offers record, current_user and features, which holds server feature flags, not package state. 18.2.0 therefore has no metadata way to hide an action from a disabled module. Per AGENTS.md rules 1 and 2, this goes upstream rather than being worked around here, and the report hands it to the seat. generate_quote has no can() gate today. Adding one would not hide it on 18.2.0 either, so it is not added (no half landing).

Lookups are not hidden, only read-empty. A form field's visibleWhen sees the same context, so nothing in it can tell that a module is off. The card accepts "read-only-empty, not an error", and that is what 18.2.0 already does.

No in-app docs change. On 18.2.0 an administrator has no door to disable a module: Setup's Disable is refused with 422, so the only switch is an operator's state file. And two of the screens a doc would describe, the actions, still behave in a way the platform is meant to change. A page written now would describe a switch admins cannot use, plus behaviour that is waiting on a fix. The doc fits better once objectstack#28117 and its console card land.

Also seen, not app-package items: the module sidebar entries (Campaigns, Quotes, Products, Price Books) stay with their module off. That is objectstack#28117 item 2, and those entries are module navigation contributions anyway. The Help count drops by the disabled module's manual, as expected.

Verification

The tile, revenue off, before and after; all modules on, after
  • Before: /_console/apps/app.objectstack.hotcrm/dashboard/crm_overview_dashboard drew "List Price by Category", and POST /api/v1/analytics/dataset/query answered 404 Dataset "product_metrics" not found.
  • After, revenue off: the page text no longer contains "List Price by Category" (0 hits, 1 before) and no /api/ request fails.
  • After, all modules on: the tile draws with the categories Software, Service, Subscription and Support, and no /api/ request fails.
The disable door and the permission payload
PATCH /api/v1/packages/app.objectstack.hotcrm.marketing/disable  -> HTTP 422 WRITABLE_PACKAGE_REQUIRED
(state file) GET /api/v1/packages -> app.objectstack.hotcrm.marketing enabled=false
GET /api/v1/data/crm_campaign_member -> 404 OBJECT_PACKAGE_DISABLED
GET /api/v1/auth/me/permissions (marketing_user holder) -> objects.crm_campaign_member.allowCreate = true
POST /api/v1/actions/crm_lead/create_campaign/ID -> 404 RESOURCE_NOT_FOUND "Flow 'campaign_lead_member_enroll' not found"
POST /api/v1/actions/crm_opportunity/generate_quote/ID (revenue off) -> 404 "Flow 'quote_generation' not found"

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01J8VVAnoe7eWhDPCCPzDBh5

…ue 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnoe7eWhDPCCPzDBh5
@vercel

vercel Bot commented Nov 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 Nov 2, 2026 2:20am UTC

Request Review

@github-actions github-actions Bot added ci/cd CI plumbing and the verification pipeline metadata Declarative metadata — schema, security posture, UI surfaces labels Nov 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Verdict: ACCEPT · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-11-02T02:23Z · head 22d288e
Implemented-by: card thread session session_01J8VVAnoe7eWhDPCCPzDBh5 (report 6567537276 on #2383). Reviewed-by: the seat, against the 3-file diff and hotcrm origin/main 27f310f.

  • Shape:
  • Measured (18.2.0):
    • The Setup disable door answers 422 WRITABLE_PACKAGE_REQUIRED (objectstack#28117 item 1), so each module was disabled through the persisted package state. Assumption 1 holds: the data plane answers 404 OBJECT_PACKAGE_DISABLED.
    • Fixed: the CRM Overview "List Price by Category" tile now declares requiresObject: 'crm_product', so it is left out with revenue off, where it used to draw a red "Dataset not defined".
    • Already tolerant: the Campaign and Related Case lookups (a save keeps the value), the related lists (read empty), and the other dashboards.
    • Platform-only: Add to Campaign (lead and contact) and Generate Quote stay drawn. /auth/me/permissions still grants a disabled package's objects, so current_user.can() is true, and a flow target in a disabled package is not withheld. Filed as objectstack#28877.
  • Tests:
    • pnpm verify exits 0 at c520cfb: 253 files, 4881 passed, 1 skipped.
    • 22d288e is a changeset only: 6 targeted suites, 143 passed, and hygiene ×2 exit 0.
    • package-ownership pins the tile key, red under ablation.
  • Gates: CI on 22d288e: every finished run is success. Build and Test is still running, and auto-merge waits for it.
  • Open questions (seat rulings):
  • Landing: non-governed. In this stroke the seat marks the PR ready and turns on auto-merge.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 02:24
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit d4ffe99 Nov 2, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd CI plumbing and the verification pipeline metadata Declarative metadata — schema, security posture, UI surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants