From 06caf3edc1cf3b06bac1e4f586ddf3d56c2d3912 Mon Sep 17 00:00:00 2001 From: rrader2890 Date: Tue, 28 Jul 2026 16:37:44 -0400 Subject: [PATCH] feat(flobyte): enable multi-entity + per-entity from-email for resellers The reseller/agency model creates one Lago billing entity per reseller, but a stock org is capped at 1 entity (MULTI_ENTITIES_MAX) and per-entity from-email is gated on an org premium flag. This adds an idempotent seed step that enables the required premium integrations on the org: - multi_entities_enterprise: unlimited billing entities (one per reseller) - from_email: per-entity branded from-address on agency invoices Non-destructive (only appends missing flags) and dry_run-aware. License.premium? is always-on in this fork, so these flags are the only remaining gate. Activates growth-os#1246 (agency billing entity provisioning). Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/tasks/flobyte.rake | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/tasks/flobyte.rake b/lib/tasks/flobyte.rake index ade0270edfe2..098e325e3686 100644 --- a/lib/tasks/flobyte.rake +++ b/lib/tasks/flobyte.rake @@ -20,6 +20,12 @@ module FlobyteCatalog BILLING_ENTITY_CODE = "flobyte" BILLING_ENTITY_NAME = "Flobyte" + # Premium integrations the reseller/agency "SaaS Mode" needs enabled on the + # org (License.premium? is always-on in this fork): + # - multi_entities_enterprise: unlimited billing entities (one per reseller) + # - from_email: per-entity branded from-address on agency invoices + RESELLER_PREMIUM_INTEGRATIONS = %w[multi_entities_enterprise from_email].freeze + # Only the white-label plan meters usage (active flows). Standard tiers are # flat price + hard feature caps. METRICS = [ @@ -91,6 +97,7 @@ module FlobyteCatalog organization = Organization.find(organization_id) puts "Seeding Flobyte in organization #{organization.id} (#{organization.name}) dry_run=#{dry_run}\n\n" ActiveRecord::Base.transaction do + ensure_premium_integrations!(organization, dry_run:) ensure_billing_entity!(organization, dry_run:) METRICS.each { |m| ensure_metric!(organization, m, dry_run:) } FEATURES.each { |f| ensure_feature!(organization, f, dry_run:) } @@ -100,6 +107,18 @@ module FlobyteCatalog puts "\n#{dry_run ? '[dry_run] no changes persisted' : '✓ done'}" end + def self.ensure_premium_integrations!(organization, dry_run:) + missing = RESELLER_PREMIUM_INTEGRATIONS - organization.premium_integrations + if missing.empty? + puts "✓ premium integrations #{RESELLER_PREMIUM_INTEGRATIONS.inspect}" + return organization + end + puts "+ premium integrations #{missing.inspect}" + return nil if dry_run + organization.update!(premium_integrations: organization.premium_integrations + missing) + organization + end + def self.ensure_billing_entity!(organization, dry_run:) existing = organization.billing_entities.find_by(code: BILLING_ENTITY_CODE) return existing if existing