Billing: fatture manuali, profilo obbligatorio e piani Stripe mensile/annuale.
Rimuove link al portale Stripe in area cliente, aggiunge flusso admin per PDF fattura e email, blocca checkout senza dati di fatturazione, allinea prezzi a €4,90/€39,90 e €9,90/€69,90, locale italiano e documentazione deploy. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
9
backend/app/helpers/application_helper.rb
Normal file
9
backend/app/helpers/application_helper.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module ApplicationHelper
|
||||
# Data/ora nel fuso dell'app (Europe/Rome) e nella lingua del sito.
|
||||
def l_local(date_or_time, format: :long)
|
||||
return nil if date_or_time.blank?
|
||||
|
||||
value = date_or_time.respond_to?(:in_time_zone) ? date_or_time.in_time_zone : date_or_time
|
||||
I18n.with_locale(:it) { I18n.l(value, format: format) }
|
||||
end
|
||||
end
|
||||
@@ -1,29 +1,81 @@
|
||||
module Public
|
||||
module BillingHelper
|
||||
def plan_billing_action(current_slug:, target_plan:, stripe_subscription_active:)
|
||||
return { kind: :current, label: "Piano attuale" } if current_slug == target_plan.slug
|
||||
|
||||
def plan_billing_action(current_slug:, target_plan:, stripe_subscription_active:, current_interval: nil)
|
||||
if target_plan.slug == "free"
|
||||
return { kind: :contact, label: "Contatta il supporto per downgrade." }
|
||||
return { kind: :contact, label: "Contatta il supporto per downgrade." } unless current_slug == "free"
|
||||
|
||||
return { kind: :current, label: "Piano attuale" }
|
||||
end
|
||||
|
||||
unless MatchLiveTv.stripe_enabled?
|
||||
return { kind: :disabled, label: "Stripe non configurato" }
|
||||
end
|
||||
|
||||
if current_slug == "free" || !stripe_subscription_active
|
||||
return { kind: :checkout, label: "Attiva #{target_plan.name}" }
|
||||
intervals = Billing::Stripe::PriceCatalog.available_intervals(plan_slug: target_plan.slug)
|
||||
return { kind: :disabled, label: "Prezzi Stripe non configurati" } if intervals.empty?
|
||||
|
||||
active_interval = current_interval.presence || Billing::Stripe::PriceCatalog::DEFAULT_INTERVAL
|
||||
|
||||
if current_slug == target_plan.slug && stripe_subscription_active
|
||||
other_intervals = intervals - [active_interval]
|
||||
if other_intervals.empty?
|
||||
label = "Piano attuale — #{Billing::Stripe::PriceCatalog.label(plan_slug: target_plan.slug, interval: active_interval)}"
|
||||
return { kind: :current, label: label }
|
||||
end
|
||||
|
||||
return { kind: :interval_switch, plan: target_plan, intervals: other_intervals, current_interval: active_interval }
|
||||
end
|
||||
|
||||
if Plan.tier(target_plan.slug) > Plan.tier(current_slug)
|
||||
{ kind: :change, label: "Passa a #{target_plan.name}" }
|
||||
if current_slug == "free" || !stripe_subscription_active
|
||||
{ kind: :checkout_options, plan: target_plan, intervals: intervals }
|
||||
else
|
||||
{ kind: :change, label: "Passa a #{target_plan.name}" }
|
||||
{ kind: :change_options, plan: target_plan, intervals: intervals }
|
||||
end
|
||||
end
|
||||
|
||||
def plan_interval_checkout_path(club, plan, interval)
|
||||
public_club_checkout_path(club, plan: plan.slug, interval: interval)
|
||||
end
|
||||
|
||||
def plan_interval_button_label(plan, interval, kind: :checkout)
|
||||
price = Billing::Stripe::PriceCatalog.label(plan_slug: plan.slug, interval: interval)
|
||||
case kind
|
||||
when :change, :interval_switch
|
||||
"Passa a #{plan.name} — #{price}"
|
||||
else
|
||||
"Attiva #{plan.name} — #{price}"
|
||||
end
|
||||
end
|
||||
|
||||
def stripe_subscription_active?(subscription)
|
||||
subscription&.stripe_subscription_id.present? && subscription.active?
|
||||
end
|
||||
|
||||
def stripe_interval_available?(plan_slug, interval)
|
||||
Billing::Stripe::PriceCatalog.configured?(plan_slug: plan_slug, interval: interval)
|
||||
end
|
||||
|
||||
def plan_interval_button_class(interval)
|
||||
if interval.to_s == "yearly"
|
||||
"btn btn-primary plan-interval-btn plan-interval-btn--yearly"
|
||||
else
|
||||
"btn btn-outline plan-interval-btn plan-interval-btn--monthly"
|
||||
end
|
||||
end
|
||||
|
||||
def plan_intervals_for_display(intervals)
|
||||
Array(intervals).sort_by { |i| i.to_s == "yearly" ? 0 : 1 }
|
||||
end
|
||||
|
||||
def billing_profile_blocks_premium?(club)
|
||||
club.present? && !club.billing_profile_complete?
|
||||
end
|
||||
|
||||
def billing_profile_incomplete_message(club)
|
||||
missing = club.billing_profile_errors
|
||||
return "Completa i dati di fatturazione prima di attivare un piano premium." if missing.empty?
|
||||
|
||||
"Mancano: #{missing.join(", ")}."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user