module Billing class SetClubQuote class Error < StandardError; end PLAN_SLUGS = %w[premium_light premium_full].freeze def self.upsert(club:, plan_slug:, interval:, amount_euros:, note:, admin:) new(club: club, plan_slug: plan_slug, interval: interval, amount_euros: amount_euros, note: note, admin: admin).upsert end def self.revoke(club:, admin:) new(club: club, admin: admin).revoke end def initialize(club:, plan_slug: nil, interval: nil, amount_euros: nil, note: nil, admin: nil) @club = club @plan_slug = plan_slug.to_s.presence @interval = interval @amount_euros = amount_euros @note = note.to_s.strip.presence @admin = admin end def upsert raise Error, "Piano non valido" unless @plan_slug.in?(PLAN_SLUGS) interval = Billing::Stripe::PriceCatalog.normalize_interval(@interval) amount_cents = EuroAmount.to_cents(@amount_euros) quote = @club.billing_quotes.active.first || @club.billing_quotes.build ApplicationRecord.transaction do quote.assign_attributes( plan_slug: @plan_slug, billing_interval: interval, amount_cents: amount_cents, currency: "eur", note: @note, active: true, created_by_admin: @admin || quote.created_by_admin ) quote.save! cancel_incompatible_orders!(quote) end quote rescue EuroAmount::Error, ArgumentError => e raise Error, e.message end def revoke quote = @club.active_billing_quote raise Error, "Nessun prezzo concordato attivo" if quote.blank? ApplicationRecord.transaction do quote.update!(active: false) @club.billing_transfer_orders.awaiting_payment.where(kind: "commercial_quote").find_each(&:cancel!) end quote end private def cancel_incompatible_orders!(quote) @club.billing_transfer_orders.awaiting_payment.find_each do |order| next if order.plan_slug == quote.plan_slug && order.billing_interval == quote.billing_interval && order.amount_cents == quote.amount_cents order.cancel! end end end end