Skip to content

Conversation

hotzenklotz
Copy link
Member

@hotzenklotz hotzenklotz commented Sep 23, 2025

PR renames Basic to Personal plan.

Issues:

  • None

(Please delete unneeded items, merge only when none are left open)

  • Added changelog entry (create a $PR_NUMBER.md file in unreleased_changes or use ./tools/create-changelog-entry.py)
  • Added migration guide entry if applicable (edit the same file as for the changelog)
  • Updated documentation if applicable
  • Adapted wk-libs python client if relevant API parts change
  • Removed dev-only changes like prints and application.conf edits
  • Considered common edge cases
  • Needs datastore update after deployment

@hotzenklotz hotzenklotz self-assigned this Sep 23, 2025
Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

📝 Walkthrough

Walkthrough

Rename pricing plan value "Basic" to "Personal" across DB, backend, and frontend; update DB migration/schema/views, WKORG default initialPricingParameters, UI strings/utilities, fixture, and docs to use the new plan name and adjusted included-user defaults.

Changes

Cohort / File(s) Summary
Database migrations & schema
conf/evolutions/142-personal-plan.sql, conf/evolutions/reversions/142-personal-plan.sql, tools/postgres/schema.sql
Rename enum value BasicPersonal in webknossos.PRICING_PLANS; update includedUsers rows for affected orgs; drop and recreate dependent views webknossos.organizations_ and webknossos.userInfos; update schema/release versions; provide rollback.
Backend pricing & org defaults
app/models/team/PricingPlan.scala, app/models/organization/OrganizationService.scala
Replace Basic with Personal in enum and logic; isPaidPlan now compares to Personal; WKORG initialPricingParameters changed to (PricingPlan.Personal, Some(1), Some(50000000000L)).
Frontend pricing utilities
frontend/javascripts/admin/organization/pricing_plan_utils.ts
Rename PricingPlanEnum.BasicPricingPlanEnum.Personal; rename/export maxInludedUsersInBasicPlanmaxIncludedUsersInPersonalPlan; update feature strings, PLAN_TO_RANK, and gating logic to reference Personal.
Frontend admin UI
frontend/javascripts/admin/onboarding.tsx, frontend/javascripts/admin/organization/organization_overview_view.tsx, frontend/javascripts/admin/organization/upgrade_plan_modal.tsx
Update imports/defaults to use maxIncludedUsersInPersonalPlan; replace UI checks and text from Basic → Personal; adjust upgrade-flow targets and eligibility logic to treat Personal as baseline.
Fixtures & tests
frontend/javascripts/test/fixtures/dummy_organization.ts
Fixture updated: PricingPlanEnum.Personal replaces Basic.
Docs & release notes
docs/automation/animations.md, docs/sharing/dataset_sharing.md, docs/index.md, unreleased_changes/8951.md
Minor docs edits (anchor link, image paths/captions) and release note documenting Basic → Personal rename.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

documentation

Suggested reviewers

  • fm3

Poem

I nibble "Basic" from the log,
Plant "Personal" by the bog.
One seat set at the carrot stand,
Views rebuilt by careful hand.
Hop — a schema cheer across the land! 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately captures the primary change of renaming the Basic plan to the Personal plan, using clear and concise phrasing that aligns directly with the pull request’s objective without extraneous details.
Description Check ✅ Passed The description clearly states that the PR renames the Basic plan to the Personal plan and outlines the relevant checklist for documentation and migration updates, directly reflecting the changes made. It references items such as changelog entries that correspond to the file diffs and migration scripts. Overall the description is on-topic and provides appropriate context for reviewers.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch personal_plan

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3929ff6 and 13edf38.

📒 Files selected for processing (1)
  • conf/evolutions/reversions/142-personal-plan.sql (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
🔇 Additional comments (1)
conf/evolutions/reversions/142-personal-plan.sql (1)

15-25: Downgrade needs to revert both 1- and 2-seat org adjustments

Right now the UPDATE only targets rows with includedUsers = 1, so any Basic org that the forward migration set to includedUsers = 2 stays stuck at 2 after the downgrade. That breaks reversibility. Please mirror the forward migration’s filters so both 1-seat and 2-seat orgs are reset.

 UPDATE webknossos.organizations
 SET includedUsers = 3
 WHERE pricingplan = 'Basic'::webknossos.PRICING_PLANS
-  AND includedUsers = 1
+  AND includedUsers IN (1, 2)
   AND (
     SELECT COUNT(*)
     FROM webknossos.users_ u
     JOIN webknossos.multiUsers_ m ON u._multiUser = m._id
       WHERE u._organization = webknossos.organizations._id
         AND m.isSuperUser = FALSE
-  ) < 3;
+  ) <= 2;

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
conf/evolutions/142-personal-plan.sql (2)

5-8: Make view drops idempotent

Use IF EXISTS to avoid failures on environments where views might be absent.

Apply:

-DROP VIEW webknossos.userinfos;
-DROP VIEW webknossos.organizations_;
+DROP VIEW IF EXISTS webknossos.userinfos;
+DROP VIEW IF EXISTS webknossos.organizations_;

13-23: Exclude deleted users in the migration count

Currently counts from base tables; use filtered views to avoid counting deleted rows.

Apply:

-AND (
-  SELECT COUNT(*)
-  FROM webknossos.users u
-  JOIN webknossos.multiUsers m ON u._multiUser = m._id
-  WHERE u._organization = webknossos.organizations._id
-  AND m.isSuperUser = FALSE
-) = 1;
+AND (
+  SELECT COUNT(*)
+  FROM webknossos.users_ u
+  JOIN webknossos.multiUsers_ m ON u._multiUser = m._id
+  WHERE u._organization = webknossos.organizations._id
+  AND m.isSuperUser = FALSE
+) = 1;
conf/evolutions/reversions/142-personal-plan.sql (4)

6-7: Make drops idempotent with IF EXISTS

Safer reversions when the views might not exist (local/dev DBs or partial runs).

-DROP VIEW webknossos.userinfos;
-DROP VIEW webknossos.organizations_;
+DROP VIEW IF EXISTS webknossos.userinfos;
+DROP VIEW IF EXISTS webknossos.organizations_;

10-11: Typo in temporary type name: use pricing_plans_old (not prizing_plans_old)

Purely a naming/clarity fix; avoids confusion for future maintainers.

-ALTER TYPE webknossos.PRICING_PLANS RENAME TO prizing_plans_old;
+ALTER TYPE webknossos.PRICING_PLANS RENAME TO pricing_plans_old;
@@
-      WHEN 'Personal'::webknossos.prizing_plans_old THEN 'Basic'::webknossos.PRICING_PLANS
-      WHEN 'Team'::webknossos.prizing_plans_old THEN 'Team'::webknossos.PRICING_PLANS
-      WHEN 'Power'::webknossos.prizing_plans_old THEN 'Power'::webknossos.PRICING_PLANS
-      WHEN 'Team_Trial'::webknossos.prizing_plans_old THEN 'Team_Trial'::webknossos.PRICING_PLANS
-      WHEN 'Power_Trial'::webknossos.prizing_plans_old THEN 'Power_Trial'::webknossos.PRICING_PLANS
-      WHEN 'Custom'::webknossos.prizing_plans_old THEN 'Custom'::webknossos.PRICING_PLANS
+      WHEN 'Personal'::webknossos.pricing_plans_old THEN 'Basic'::webknossos.PRICING_PLANS
+      WHEN 'Team'::webknossos.pricing_plans_old THEN 'Team'::webknossos.PRICING_PLANS
+      WHEN 'Power'::webknossos.pricing_plans_old THEN 'Power'::webknossos.PRICING_PLANS
+      WHEN 'Team_Trial'::webknossos.pricing_plans_old THEN 'Team_Trial'::webknossos.PRICING_PLANS
+      WHEN 'Power_Trial'::webknossos.pricing_plans_old THEN 'Power_Trial'::webknossos.PRICING_PLANS
+      WHEN 'Custom'::webknossos.pricing_plans_old THEN 'Custom'::webknossos.PRICING_PLANS
@@
-DROP TYPE webknossos.prizing_plans_old;
+DROP TYPE webknossos.pricing_plans_old;

Also applies to: 16-21, 24-24


13-13: Fix column name casing for consistency

Unquoted identifiers are case-folded, but this reads like a typo.

-  ALTER COLUMN pricingPLan DROP DEFAULT,
+  ALTER COLUMN pricingPlan DROP DEFAULT,

15-22: Add ELSE branch in CASE to future-proof mapping

Prevents unexpected NULLs if new enum values appear.

   ALTER COLUMN pricingPlan TYPE webknossos.PRICING_PLANS USING
     CASE pricingPlan
-      WHEN 'Personal'::webknossos.pricing_plans_old THEN 'Basic'::webknossos.PRICING_PLANS
+      WHEN 'Personal'::webknossos.pricing_plans_old THEN 'Basic'::webknossos.PRICING_PLANS
       WHEN 'Team'::webknossos.pricing_plans_old THEN 'Team'::webknossos.PRICING_PLANS
       WHEN 'Power'::webknossos.pricing_plans_old THEN 'Power'::webknossos.PRICING_PLANS
       WHEN 'Team_Trial'::webknossos.pricing_plans_old THEN 'Team_Trial'::webknossos.PRICING_PLANS
       WHEN 'Power_Trial'::webknossos.pricing_plans_old THEN 'Power_Trial'::webknossos.PRICING_PLANS
       WHEN 'Custom'::webknossos.pricing_plans_old THEN 'Custom'::webknossos.PRICING_PLANS
+      ELSE pricingPlan::text::webknossos.PRICING_PLANS
     END,
frontend/javascripts/admin/organization/pricing_plan_utils.ts (1)

79-84: Tighten grammar in comment and debug message

Minor polish; avoids noisy console wording.

-  // This function should not be called to check for "Personal" plans since its the default plan for all users anyway.
+  // This function should not be called to check for "Personal" plans since it's the default plan for all users anyway.
@@
-      "Restricting a feature to Personal Plan does not make sense. Consider removing the restriction",
+      "Restricting a feature to the Personal plan doesn't make sense. Consider removing the restriction",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a9cd8a and 2390aee.

⛔ Files ignored due to path filters (1)
  • test/db/organizations.csv is excluded by !**/*.csv
📒 Files selected for processing (10)
  • app/models/organization/OrganizationService.scala (1 hunks)
  • app/models/team/PricingPlan.scala (1 hunks)
  • conf/evolutions/142-personal-plan.sql (1 hunks)
  • conf/evolutions/reversions/142-personal-plan.sql (1 hunks)
  • frontend/javascripts/admin/onboarding.tsx (2 hunks)
  • frontend/javascripts/admin/organization/organization_overview_view.tsx (4 hunks)
  • frontend/javascripts/admin/organization/pricing_plan_utils.ts (5 hunks)
  • frontend/javascripts/admin/organization/upgrade_plan_modal.tsx (2 hunks)
  • frontend/javascripts/test/fixtures/dummy_organization.ts (1 hunks)
  • tools/postgres/schema.sql (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/javascripts/admin/onboarding.tsx (1)
frontend/javascripts/admin/organization/pricing_plan_utils.ts (1)
  • maxInludedUsersInPersonalPlan (30-30)
app/models/organization/OrganizationService.scala (2)
app/utils/WkConf.scala (1)
  • Features (134-154)
app/models/team/PricingPlan.scala (1)
  • PricingPlan (5-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
🔇 Additional comments (16)
frontend/javascripts/test/fixtures/dummy_organization.ts (1)

8-8: Fixture plan rename to Personal: LGTM

Aligned with enum rename and limits (includedUsers=1).

app/models/organization/OrganizationService.scala (1)

116-118: Default plan change to Personal with 1 included user: LGTM

Matches the migration and frontend expectations.

Please verify tests/seed data that previously assumed 3 included users for the base plan.

tools/postgres/schema.sql (2)

24-24: Schema version bump to 142: LGTM


335-335: Enum now includes 'Personal': LGTM

In sync with Scala/TS enums and migration.

frontend/javascripts/admin/organization/upgrade_plan_modal.tsx (2)

59-62: Copy updated to reference Personal: LGTM


170-175: Default upgrade branch for Personal: LGTM

frontend/javascripts/admin/onboarding.tsx (2)

18-18: Import switched to Personal constant: LGTM


213-214: InviteUsersModal default max users set for Personal plan: LGTM

frontend/javascripts/admin/organization/organization_overview_view.tsx (2)

76-104: Upgrade actions treat Personal as baseline: LGTM

Click handlers correctly route Personal to the plan upgrade modal.


197-207: “Unlock more features” gating updated for Personal: LGTM

app/models/team/PricingPlan.scala (1)

7-9: LGTM — no code usages of Basic remain; only migrations/docs mention it

No references to PricingPlan.Basic / PricingPlanEnum.Basic found in runtime code. Remaining "Basic" hits are in DB evolutions (e.g. conf/evolutions/142-personal-plan.sql, 094-pricing-plans.sql, 068-pricing-plan.sql) and docs/tests — expected and safe to keep as-is.

conf/evolutions/reversions/142-personal-plan.sql (1)

44-46: LGTM: schemaVersion reverted to 141

Matches this being the down migration for 142.

frontend/javascripts/admin/organization/pricing_plan_utils.ts (4)

14-14: LGTM: updated feature text

Copy aligns with the new plan name.


23-23: LGTM: updated feature text

Consistent wording with Personal plan.


60-66: LGTM: PLAN_TO_RANK baseline switched to Personal

Ranking logic remains consistent for trials and paid tiers.


5-5: Rename OK — no PricingPlan.Basic usages found

Search found no references to PricingPlan.Basic or PricingPlanEnum.Basic; remaining "Basic" hits are test titles (frontend/javascripts/test/...) and a UI label ("Basic authentication" in frontend/javascripts/admin/dataset/remote/add_remote_layer.tsx), not pricing-enum call sites.

@hotzenklotz hotzenklotz requested a review from fm3 September 23, 2025 11:31
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
conf/evolutions/142-personal-plan.sql (3)

5-8: Use IF EXISTS to make view drops resilient.

Avoids migration failures if views are missing in some environments.

-DROP VIEW webknossos.userinfos;
-DROP VIEW webknossos.organizations_;
+DROP VIEW IF EXISTS webknossos.userinfos;
+DROP VIEW IF EXISTS webknossos.organizations_;

13-23: Count non-deleted users to avoid skew from soft-deleted rows.

Use the filtered views (users_, multiUsers_) or add isDeleted filters to ensure COUNT = 1 reflects active records.

-AND (
-  SELECT COUNT(*)
-  FROM webknossos.users u
-  JOIN webknossos.multiUsers m ON u._multiUser = m._id
-  WHERE u._organization = webknossos.organizations._id
-  AND m.isSuperUser = FALSE
-) = 1;
+AND (
+  SELECT COUNT(*)
+  FROM webknossos.users_ u
+  JOIN webknossos.multiUsers_ m ON u._multiUser = m._id
+  WHERE u._organization = webknossos.organizations._id
+  AND m.isSuperUser = FALSE
+) = 1;

25-35: Minor: Normalize identifier casing for consistency.

Unquoted identifiers are folded to lowercase; prefer a consistent style throughout (e.g., userinfos vs userInfos). No functional change required.

-CREATE VIEW webknossos.userInfos AS
+CREATE VIEW webknossos.userinfos AS
 SELECT
 u._id AS _user, m.email, u.firstName, u.lastName, o.name AS organization_name,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2390aee and 9186cc2.

📒 Files selected for processing (7)
  • conf/evolutions/142-personal-plan.sql (1 hunks)
  • conf/evolutions/reversions/142-personal-plan.sql (1 hunks)
  • docs/automation/animations.md (1 hunks)
  • docs/sharing/dataset_sharing.md (2 hunks)
  • frontend/javascripts/admin/onboarding.tsx (2 hunks)
  • frontend/javascripts/admin/organization/pricing_plan_utils.ts (5 hunks)
  • unreleased_changes/8951.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • docs/automation/animations.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/javascripts/admin/organization/pricing_plan_utils.ts
  • conf/evolutions/reversions/142-personal-plan.sql
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/javascripts/admin/onboarding.tsx (1)
frontend/javascripts/admin/organization/pricing_plan_utils.ts (1)
  • maxIncludedUsersInPersonalPlan (30-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: backend-tests
  • GitHub Check: frontend-tests
  • GitHub Check: build-smoketest-push
🔇 Additional comments (5)
frontend/javascripts/admin/onboarding.tsx (2)

18-18: Updated import aligns with Personal plan rename.

Consistent with pricing_plan_utils export.


213-214: Default prop matches Personal plan limit.

Matches InviteUsersModal defaults; I couldn't verify removal of the old Basic-plan constant because the repo search didn't complete — please run locally: rg -nP -uu 'maxInludedUsersInBasicPlan|maxIncludedUsersInBasicPlan|maxIncludedUsersInPersonalPlan|maxUserCountPerOrganization' -g '!/node_modules/' -g '!/dist/' and confirm no matches.

docs/sharing/dataset_sharing.md (2)

64-66: Alt text/caption update looks good — image present

Confirmed docs/images/dataset_settings_metadata.jpeg exists; caption/alt update is fine.


32-34: Alt text/caption update verified — image present
docs/images/dataset_settings_sharing.jpeg exists.

unreleased_changes/8951.md (1)

1-5: Changelog entry LGTM — migration present; update remaining "Basic" user-facing references.

  • Migration present: conf/evolutions/142-personal-plan.sql
  • Action: replace user-facing "Basic" → "Personal" in docs/UI: docs/ui/toolbar.md, docs/skeleton_annotation/tools.md, docs/users/new_users.md; confirm whether frontend/javascripts/admin/dataset/remote/add_remote_layer.tsx ("Basic authentication") is unrelated to pricing and should remain. Leave historical evolutions/reversions under conf/evolutions/* unchanged.

Copy link
Member

@fm3 fm3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backend code and new schema look good to me. I added some comments concerning the sql evolutions

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
conf/evolutions/reversions/142-personal-plan.sql (1)

8-9: Use IF EXISTS on DROP VIEW

Makes the script more robust without changing semantics.

-DROP VIEW webknossos.userinfos;
-DROP VIEW webknossos.organizations_;
+DROP VIEW IF EXISTS webknossos.userinfos;
+DROP VIEW IF EXISTS webknossos.organizations_;
conf/evolutions/142-personal-plan.sql (2)

15-25: Optional: Guard against overwriting customized includedUsers

Only adjust orgs still at the old default (3) to avoid touching customized values.

-SET includedUsers = 1
-WHERE pricingplan = 'Personal'::webknossos.PRICING_PLANS
+SET includedUsers = 1
+WHERE pricingplan = 'Personal'::webknossos.PRICING_PLANS
+AND includedUsers = 3
 AND (

8-9: Use IF EXISTS on DROP VIEW

Safer and consistent with resilient evolutions.

-DROP VIEW webknossos.userinfos;
-DROP VIEW webknossos.organizations_;
+DROP VIEW IF EXISTS webknossos.userinfos;
+DROP VIEW IF EXISTS webknossos.organizations_;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d83aa3 and 184acc7.

📒 Files selected for processing (2)
  • conf/evolutions/142-personal-plan.sql (1 hunks)
  • conf/evolutions/reversions/142-personal-plan.sql (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
  • GitHub Check: backend-tests
🔇 Additional comments (7)
conf/evolutions/reversions/142-personal-plan.sql (3)

1-1: Remove issue link from evolution header

Matches house style noted in prior reviews.

--- https://github.com/scalableminds/webknossos/issues/8951

3-6: LGTM: version guard and transaction block

Version assertion and transactional DDL are correct.


11-13: LGTM: enum value rename (Personal -> Basic)

Appropriate use of ALTER TYPE RENAME VALUE.

conf/evolutions/142-personal-plan.sql (4)

1-1: Remove issue link from evolution header

Per prior feedback, omit issue/PR URLs in evolutions.

--- https://github.com/scalableminds/webknossos/issues/8951

11-13: LGTM: enum value rename (Basic -> Personal)

Correct and minimal change.


27-37: LGTM: view definitions

Recreated views look consistent and correct.


3-6: LGTM: version guard and transaction block

Matches evolution conventions.

hotzenklotz and others added 4 commits September 25, 2025 09:41
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c385c6 and 3929ff6.

📒 Files selected for processing (2)
  • conf/evolutions/142-personal-plan.sql (1 hunks)
  • conf/evolutions/reversions/142-personal-plan.sql (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
  • GitHub Check: frontend-tests

Comment on lines 15 to 25
UPDATE webknossos.organizations
SET includedUsers = 3
WHERE pricingplan = 'Basic'::webknossos.PRICING_PLANS
AND includedUsers = 1
AND (
SELECT COUNT(*)
FROM webknossos.users u
JOIN webknossos.multiUsers m ON u._multiUser = m._id
WHERE u._organization = webknossos.organizations._id
AND m.isSuperUser = FALSE
) < 3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix downgrade to revert both 1- and 2-seat orgs using the same active-user filter as the upgrade.

Right now the downgrade only touches rows where includedUsers = 1 and counts on the base tables. Any org that the upgrade moved to includedUsers = 2 (two active non-superusers) is left at 2 after the downgrade, so the schema isn’t actually reversible. Counting on webknossos.users / multiUsers also pulls in soft-deleted rows, which means we can even miss the =1 case if there are deleted accounts. Please align the filter with the forward migration so we reset every row the upgrade touched.

 UPDATE webknossos.organizations
 SET includedUsers = 3
 WHERE pricingplan = 'Basic'::webknossos.PRICING_PLANS
-  AND includedUsers = 1
-  AND (
-    SELECT COUNT(*)
-    FROM webknossos.users u
-    JOIN webknossos.multiUsers m ON u._multiUser = m._id
+  AND includedUsers IN (1, 2)
+  AND (
+    SELECT COUNT(*)
+    FROM webknossos.users_ u
+    JOIN webknossos.multiUsers_ m ON u._multiUser = m._id
     WHERE u._organization = webknossos.organizations._id
       AND m.isSuperUser = FALSE
-  ) < 3;
+  ) <= 2;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
UPDATE webknossos.organizations
SET includedUsers = 3
WHERE pricingplan = 'Basic'::webknossos.PRICING_PLANS
AND includedUsers = 1
AND (
SELECT COUNT(*)
FROM webknossos.users u
JOIN webknossos.multiUsers m ON u._multiUser = m._id
WHERE u._organization = webknossos.organizations._id
AND m.isSuperUser = FALSE
) < 3;
UPDATE webknossos.organizations
SET includedUsers = 3
WHERE pricingplan = 'Basic'::webknossos.PRICING_PLANS
AND includedUsers IN (1, 2)
AND (
SELECT COUNT(*)
FROM webknossos.users_ u
JOIN webknossos.multiUsers_ m ON u._multiUser = m._id
WHERE u._organization = webknossos.organizations._id
AND m.isSuperUser = FALSE
) <= 2;

Copy link
Member

@fm3 fm3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@hotzenklotz hotzenklotz merged commit f29dcc7 into master Sep 25, 2025
5 checks passed
@hotzenklotz hotzenklotz deleted the personal_plan branch September 25, 2025 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants