Skip to Content

← All archived runs

Run: admin-commercial-config

run.md

Run: admin-commercial-config

  • issue: #451 # canonical home of the spec + state (labels, comments)
  • branch: claude/admin-commercial-config-bv1rfa
  • pr: #452

00_intake/stub.md

Stub: Admin dashboard — commercial configuration

  • feature-slug: admin-commercial-config
  • epic: admin-dashboard-data
  • personas: Admin
  • initiative: Build the Bridge / objective: Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • depends-on: admin-data-foundation
  • sequence: 4 of 7

Problem

The CommercialConfigCard (vendor-percentage set, pricing-rules set, overall status) is dummy. There is no stored commercial configuration, so the commercial stage of the journey cannot be marked ready. Per the storage decision, this simple config lives in the existing TenantSetting key/value collection rather than a dedicated model.

Proposed change

  • Define the commercial-config tenant-settings keys (e.g. vendor revenue %, whether pricing rules are set) under a commercial category in TenantSetting.
  • Wire the CommercialConfigCard to read those settings and compute its Complete | Partial | Missing status.
  • Build the config UI: admin can set the vendor percentage and pricing-rules flag/values.

Acceptance criteria (rough)

  • Admin can set vendor % and pricing rules; values persist per tenant in TenantSetting.
  • CommercialConfigCard reflects the real saved values and a computed status.
  • Empty/never-configured state renders as Missing, not a mock "Partial".

Out of scope (this feature)

  • Applying pricing rules to real quotes/invoices — this is configuration capture only.
  • Escalation rules (admin-escalation-config) and SLA (admin-sla-config).

Notes for Define

  • Pin down the exact tenant-setting keys + value shapes; admin-readiness-gaps derives commercialRulesSet from them, so name them to be reusable.
  • touches: packages/services/src/db/models/tenant-setting.ts (keys, read/write), apps/web/components/admin/dashboard/commercial-config-card.tsx.

01_define/output/spec.md

Spec: Admin commercial configuration

  • slug: admin-commercial-config
  • issue: #451
  • personas: Admin
  • touches: packages/services/src/db/services/tenant-setting, apps/web/app/(app)/admin/commercial, apps/web/components/admin/dashboard/commercial-config-card.tsx, apps/web/app/(app)/admin/dashboard/page.tsx, apps/web/lib/admin-dashboard-data.ts, apps/web/lib/mock/admin.ts
  • complexity: standard

Problem

On the admin "Platform setup" dashboard, the CommercialConfigCard ("Vendor % set" and "Pricing rules set" yes/no badges, plus a Complete | Partial | Missing status) is driven entirely by dummy data in lib/mock/admin.ts — there is no stored commercial configuration, so the commercial stage of the journey can never be marked ready from real state. This is feature 4 of the admin-dashboard-data epic, which advances Build the Bridge / Q2 2026 Objective 3 — validate technical infrastructure & payout flow by making each dashboard section operational from real, per-tenant data rather than a mock. Per the epic's storage decision, this simple config lives in the existing TenantSetting key/value collection rather than a dedicated model, mirroring the escalation config (admin-escalation-config) that shipped before it.

Proposed change

  • Commercial tenant-settings keys. Store commercial config under a commercial category in TenantSetting, with two keys this round:
    • commercial.vendorRevenuePercent — number, 0–100. The vendor's share of revenue. Presence of this setting is what "vendor % set" means (so 0 is a valid configured value, distinct from never-configured).
    • commercial.pricingRulesSet — boolean. Whether the tenant has confirmed its pricing rules.
    • The category is kept as a namespace so future commercial keys can slot in without a schema change, exactly as escalation does.
  • Typed service helpers. Add getCommercialConfig(tenantId) and setCommercialConfig(tenantId, config) to TenantSettingService, following the existing getEscalationConfig / setEscalationConfig precedent (upsert under the commercial category; values stored independently per key). Export a CommercialConfig type ({ vendorRevenuePercent: number | null; pricingRulesSet: boolean }). getCommercialConfig returns { vendorRevenuePercent: null, pricingRulesSet: false } when nothing is stored.
  • Config UI — a dedicated /admin/commercial page. A server component reads the current config; a client form (backed by a server action, following the /admin/escalation actions pattern) lets the admin set the vendor revenue percentage (0–100) and toggle "pricing rules set", then save. Sentence-case copy throughout.
  • Wire CommercialConfigCard to real state. The dashboard passes a real commercial slice from loadAdminDashboardData instead of d.commercialConfig. The card's two badges and its status are derived from the stored values:
    • vendorPercentSet = a commercial.vendorRevenuePercent value is stored (not null).
    • pricingRulesSet = the stored commercial.pricingRulesSet flag.
    • status = Complete when both are set, Partial when exactly one is set, Missing when neither is set. A never-configured tenant therefore renders as Missing, not the old mock "Partial".
    • The card links to /admin/commercial, and the matching "No pricing rule set" entry in the config-gaps panel points its action at /admin/commercial (currently /admin/setup, which is not the commercial config route).

Acceptance criteria

  • An admin can set the vendor revenue percentage (0–100) and toggle "pricing rules set" on /admin/commercial, and the values persist per tenant in TenantSetting under the commercial category (commercial.vendorRevenuePercent, commercial.pricingRulesSet).
  • getCommercialConfig / setCommercialConfig exist on TenantSettingService and a CommercialConfig type is exported; get returns { vendorRevenuePercent: null, pricingRulesSet: false } when no setting is stored.
  • The CommercialConfigCard on the admin dashboard reflects the real saved state — both badges and the Complete/Partial/Missing status come from the stored config, not the mock.
  • The card's status is derived: Complete when both vendor % and pricing rules are set, Partial when exactly one is set, Missing when neither is set.
  • A tenant that has never configured commercial settings renders as Missing (no stored value is treated as unset), not the old mock "Partial".
  • The dashboard card links to /admin/commercial, and the "No pricing rule set" config-gap action targets /admin/commercial.

Out of scope

  • Applying pricing rules to real quotes/invoices, or computing payouts from the vendor percentage — this round is configuration capture only.
  • Storing a structured pricing-rules table/rows — commercial.pricingRulesSet is a single boolean flag this round, not a rules engine.
  • SLA config (admin-sla-config) and escalation config (admin-escalation-config, already shipped).
  • The derived readiness / config-gaps aggregation layer — that is the capstone admin-readiness-gaps feature, which will derive commercialRulesSet from getCommercialConfig (e.g. status Complete). This feature only fixes the single "No pricing rule set" gap's link target; it keeps the key names reusable so the capstone can consume them unchanged.
  • Replicating the change into apps/dashboards — that app stays the dummy source of truth.

Open questions

  • none. Prerequisite (not a blocker on this spec): this feature builds on admin-data-foundation (already spun out), which converted /admin/dashboard to a server-fetched per-tenant page via loadAdminDashboardData. Build adds the commercial slice to that existing server fetch rather than re-introducing client-side fetching.

02_build/output/notes.md

Build notes: admin-commercial-config

  • branch: claude/admin-commercial-config-bv1rfa
  • commits:
    • feat: admin-commercial-config — commercial tenant-settings + config UI

What changed

  • packages/services/src/db/services/tenant-setting/index.ts: added CommercialConfig type and getCommercialConfig / setCommercialConfig on TenantSettingService, mirroring the escalation precedent. Two keys under the commercial category: commercial.vendorRevenuePercent (number; presence = "set", so 0 is a valid configured value) and commercial.pricingRulesSet (boolean). get returns { vendorRevenuePercent: null, pricingRulesSet: false } when nothing is stored; set only writes the percent key when a number is provided (never stores null).
  • packages/services/src/db/services/index.ts: re-export the CommercialConfig type from the services barrel (so it's available via @sustentus/services/server).
  • apps/web/app/(app)/admin/commercial/{page.tsx,actions.ts,_components/commercial-config-form.tsx}: new admin config page. Server component reads the config; client form (number input for vendor %, switch for pricing rules) saves via the saveCommercialConfig server action, which revalidates /admin/commercial and /admin/dashboard. Mirrors the /admin/escalation pattern.
  • apps/web/lib/admin-dashboard-data.ts: added the commercial slice (AdminDashboardCommercial) to the server fetch — vendorPercentSet, pricingRulesSet, and a derived Complete/Partial/Missing status (Complete when both set, Partial when one, Missing when neither).
  • apps/web/app/(app)/admin/dashboard/page.tsx: pass the real commercial slice to CommercialConfigCard instead of d.commercialConfig.
  • apps/web/components/admin/dashboard/commercial-config-card.tsx: added a footer linking to /admin/commercial (badges/status unchanged, now fed real data).
  • apps/web/lib/mock/admin.ts: retargeted the "No pricing rule set" config-gap action from /admin/setup to /admin/commercial.

Acceptance criteria status

  • Admin can set vendor % (0–100) and toggle pricing rules on /admin/commercial; values persist per tenant under the commercial category — page + form + action + setCommercialConfig.
  • getCommercialConfig / setCommercialConfig exist and CommercialConfig is exported; get returns { vendorRevenuePercent: null, pricingRulesSet: false } when nothing is stored.
  • CommercialConfigCard reflects real saved state — fed from loadAdminDashboardData, not the mock.
  • Status is derived (Complete / Partial / Missing) in the data seam.
  • Never-configured tenant renders Missing (both unset → status Missing).
  • Card links to /admin/commercial; the "No pricing rule set" gap action targets /admin/commercial.

Verify result

  • Mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview, not here. Ship reads them back from gh pr checks. No check is expected to fail.

Notes for review

  • setCommercialConfig intentionally skips writing the vendor-percent key when null, so a blank input leaves the tenant "unset" (Missing) rather than persisting a null value the required-Mixed schema would reject. Unsetting an already-stored percent is out of scope this round (capture only).
  • Key names (commercial.vendorRevenuePercent, commercial.pricingRulesSet) are deliberately reusable so the admin-readiness-gaps capstone can derive commercialRulesSet from them unchanged.
  • adminData.commercialConfig in the mock is now unused by the dashboard but left in place (the mock remains the dummy source of truth for apps/dashboards).

03_ship/output/changelog.md

Changelog: admin-commercial-config

Persona: admin

Configure commercial terms for your organisation

Admins can now set up commercial terms from the platform setup dashboard:

  • Set your vendor revenue percentage and confirm whether your pricing rules are set, from the new commercial settings page — your choices are saved per organisation.
  • The commercial card on your setup dashboard now shows your real status — complete, partial, or missing — instead of sample content.

This is part of making your platform setup dashboard reflect your organisation's real configuration.

03_ship/output/investor-update.md

Admins can now configure commercial terms for their organisation

We've made commercial setup configurable per organisation — admins can set their vendor revenue share and confirm their pricing rules, and the platform setup dashboard now reflects the real, saved status instead of placeholder content. It continues turning the admin control surface into a working control panel, and brings the commercial inputs that underpin payouts under explicit, per-organisation control.

  • Admins can set the vendor revenue percentage and mark pricing rules as set, and the values persist per organisation.
  • The commercial card on the setup dashboard now shows real status — complete, partial, or missing — rather than sample content.
  • The settings build on our existing tenant-settings foundation, so pricing and payout logic can read from them later without new infrastructure.

This advances the Build the Bridge initiative and our Q2 2026 objective to validate technical infrastructure & payout flow — putting the commercial inputs behind payouts under real, per-organisation control.

03_ship/output/pr.md

Ship: admin-commercial-config

  • PR: #452 — https://github.com/sustentus/sustentus/pull/452
  • branch: claude/admin-commercial-config-bv1rfa
  • CI: format pass · lint pass · typecheck pass · pr-issue-link pass · vercel preview pass (confirming on final commit)
  • technical docs: no technical docs impact (no apps/docs/technical page enumerates admin routes or tenant-setting keys; mirrors the escalation sibling's determination)
  • business docs: no business docs impact (no feature-role-matrix/service-journey entity covers admin platform-setup config; this is operational configuration capture, not a service-journey step)
  • release notes: both — investor draft + changelog entry included in this PR (changelog published to apps/help/app/changelog/page.mdx)

Review summary

  • Focused review of the diff (19 files, +521/-16 vs origin/main). The change is a near-exact mirror of admin-escalation-config, which already passed a high-effort /code-review with no correctness bugs; the new logic (commercial status derivation, vendor-percent presence check) is small and was checked directly.
  • No correctness bugs. value: false (pricing-rules flag) and value: 0 (vendor %) both satisfy the TenantSetting schema's required (escalation already exercises the false case in prod). Status derivation (Complete/Partial/Missing) and the null-percent presence check are correct.
  • Intentional, non-blocking: setCommercialConfig does not write the vendor-percent key when blank, so an already-stored percent cannot be cleared this round — documented "capture only, no unset" scope, not a defect.
  • Mid-ship catch: the local main ref was stale, making git diff main...HEAD show admin-people-data files. Confirmed against fetched origin/main that the PR is commercial-config only (those files are already on main).

Acceptance check (vs spec)

  • Admin can set vendor % (0–100) and toggle pricing rules on /admin/commercial; persisted per tenant under the commercial category (commercial.vendorRevenuePercent, commercial.pricingRulesSet).
  • getCommercialConfig / setCommercialConfig exist and CommercialConfig is exported; get returns { vendorRevenuePercent: null, pricingRulesSet: false } when unset.
  • CommercialConfigCard reflects real saved state — both badges + status from the server-fetched slice, not the mock.
  • Status derived: Complete (both set) / Partial (one) / Missing (neither).
  • Never-configured tenant renders Missing, not the old mock "Partial".
  • Card links to /admin/commercial; the "No pricing rule set" config-gap action targets it.
  • (beyond spec) /admin/commercial is reachable — registered in ROUTE_POLICIES (the deny-by-default middleware was redirecting it to the dashboard).

Merge & deploy

  • merged: no — awaiting explicit human approval at the merge gate.
  • deploy: pending merge.