- Shipped
- September 28, 2026 at 5:56 AM UTC
- Author
- Kamo
- Commit
- 2bbe8ec
KamoUniverse sells the platform to four audiences - Business, Family, Community, Personal - each on its own plan ladder with packages that fit it (a family gets no CRM). Nothing could tell those catalogs apart: every org resolved its Free floor from the root's plan coded FREE, which includes the CRM and applies whatever the org pays for, so a Family org would have held the CRM forever. - UniverseType (BUSINESS 1, FAMILY 2, COMMUNITY 3, PERSONAL 4) persisted by id through UniverseTypeConverter (unknown id reads null), JSON by name. Null means BUSINESS everywhere; business keeps being stored as null. - Organization.universe (orgs.universe_type) and SubscriptionPlan.universe **************** both nullable. Organization's getter never returns null, so every org payload says "universe":"BUSINESS" for the orgs that predate it. The columns were added and verified in Yugabyte by KamoInitializer's UniverseColumnsMigration before this was pushed. - UniversePlans: pure helpers (no new bean) for a universe's Free plan, top tier, negotiated plan and self-serve seat ceiling. - **************** the Free floor is the org universe's Free plan; a trial grants everything for BUSINESS (unchanged) and the universe's top tier elsewhere. - **************** universe) / getPromotions(..., universe): plans filtered to the universe (null = BUSINESS), add-ons/bundles/promotions pruned to the ones naming its plans; a row naming no plan is business-only. For BUSINESS the result is exactly the pre-universe catalog. Plan DTOs carry "universe".
