package billing import ( "context" "strings" "github.com/google/uuid" ) // IsCustomPackage reports whether a plan should get the "custom deal" feature // treatment (all dashboard features ON by default for non-A1 deals). // // Product semantics (see IsPublicProductPlan + plans.is_custom): // - Client / admin deals with is_custom=true → custom (including A1 PAYG) // - Public Enterprise (and any row with is_custom=true) → custom // - Exact "Legacy" plan name → never enable-all (restricted migrated matrix) // - Free / Starter / Growth / Business with is_custom=false → not custom // // is_custom wins over A1* name patterns for PAYG billing / PlanProfileCustom, // but A1* custom deals use A1PaygPlanFeatures (not literal enable-all) so // Stores, Marketing, and Integrations stay off. func IsCustomPackage(name string, isCustom bool) bool { if strings.EqualFold(strings.TrimSpace(name), LegacyPlanName) { return false } if isCustom { return true } // Legacy-named plans without is_custom stay on the limited matrix. if IsLegacyPlanName(name) { return false } return !IsPublicProductPlan(name) } // A1PaygFeatureDenied reports keys that stay OFF on A1 PAYG custom deals. // Nav: Stores → stores.*; Marketing → marketing.*; Integrations → integrations.*. // Cutover honesty chrome (ETL gaps / reconnect / migrated checklist) stays OFF — A1 is not a // hypercare merchant surface (see MigratedEtlGapsPanel + IsA1CohortCompany product rules). func A1PaygFeatureDenied(key string) bool { switch key { case "dashboard.etl_gaps", "dashboard.store_reconnect", "dashboard.migrated_checklist": return true } return strings.HasPrefix(key, "stores.") || strings.HasPrefix(key, "marketing.") || strings.HasPrefix(key, "integrations.") } // A1PaygPlanFeatures is the dump-faithful A1 PAYG matrix: custom enable-all // minus Stores, Marketing, and Integrations sections. func A1PaygPlanFeatures() map[string]bool { out := AllRegistryFeatures(true) for k := range out { if A1PaygFeatureDenied(k) { out[k] = false } } return out } // SparseA1PaygOverrides returns explicit false overrides for A1 PAYG denied keys. func SparseA1PaygOverrides() map[string]bool { out := make(map[string]bool) for _, k := range FeatureCatalogKeys { if A1PaygFeatureDenied(k) { out[k] = false } } return out } // AllRegistryFeatures returns every FeatureCatalogKeys entry set to enabled. func AllRegistryFeatures(enabled bool) map[string]bool { out := make(map[string]bool, len(FeatureCatalogKeys)) for _, k := range FeatureCatalogKeys { out[k] = enabled } return out } // EnableSectionForAllPlans turns a section master switch ON for every plan // (global gate; missing rows already default ON). func (s *Service) EnableSectionForAllPlans(ctx context.Context, section string, updatedBy *uuid.UUID) (FeatureGatesView, error) { return s.SetSectionGate(ctx, section, true, updatedBy) } // DisableSectionForAllPlans turns a section master switch OFF for every plan. func (s *Service) DisableSectionForAllPlans(ctx context.Context, section string, updatedBy *uuid.UUID) (FeatureGatesView, error) { return s.SetSectionGate(ctx, section, false, updatedBy) } // prepareCustomPackageCreateFeatures applies create-time defaults for custom packages: // when the caller omitted features, materialize enable-all overrides so admin UIs // show an explicit all-ON matrix (resolve already treats empty+is_custom as all ON). func prepareCustomPackageCreateFeatures(p *Plan, creating, featuresProvided bool) { if !creating { return } if IsLegacyPlan(p.Name, p.IsLegacy) && !IsCustomPackage(p.Name, p.IsCustom) { p.IsLegacy = true if strings.EqualFold(strings.TrimSpace(p.Name), LegacyPlanName) { p.IsCustom = false } else if !IsPublicProductPlan(p.Name) { p.IsCustom = true } if !featuresProvided { p.Features = SparseLegacyOverrides() } return } // Non-public ladder names are client deals — keep is_custom aligned. if !IsPublicProductPlan(p.Name) { p.IsCustom = true } // A1 PAYG / custom deals are never the restricted Legacy matrix. if IsCustomPackage(p.Name, p.IsCustom) { p.IsLegacy = false } if featuresProvided { return } if IsCustomPackage(p.Name, p.IsCustom) { if IsLegacyPlanName(p.Name) { p.Features = A1PaygPlanFeatures() return } p.Features = AllRegistryFeatures(true) } }