feat(payouts): implement promoter earnings viewing, request flow, and admin Stripe processing with webhooks
Add model methods for accurate net calculations (€0.50 + 1.5% fees), eligibility, refund handling Update promoter/payouts controller for index (pending events), create (eligibility checks) Integrate admin processing via Stripe::Transfer, webhook for status sync Enhance views: index pending cards, events/show preview/form Add comprehensive tests (models, controllers, service, integration); run migrations
This commit is contained in:
@@ -12,18 +12,44 @@ class Payout < ApplicationRecord
|
||||
}, default: :pending
|
||||
|
||||
# === Validations ===
|
||||
validates :amount_cents, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
||||
validates :amount_cents, presence: true, numericality: { greater_than: 0 }
|
||||
validates :fee_cents, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
||||
validates :status, presence: true
|
||||
validates :total_orders_count, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
||||
validates :refunded_orders_count, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
||||
validates :stripe_payout_id, allow_blank: true, uniqueness: true
|
||||
validate :unique_pending_event_id, if: :pending?
|
||||
|
||||
validate :net_earnings_greater_than_zero, if: :pending?
|
||||
|
||||
def net_earnings_greater_than_zero
|
||||
if event.net_earnings_cents <= 0
|
||||
errors.add(:base, "net earnings must be greater than 0")
|
||||
end
|
||||
end
|
||||
|
||||
validate :net_earnings_greater_than_zero, if: :pending?
|
||||
|
||||
def net_earnings_greater_than_zero
|
||||
if event.net_earnings_cents <= 0
|
||||
errors.add(:base, "net earnings must be greater than 0")
|
||||
end
|
||||
end
|
||||
|
||||
def unique_pending_event_id
|
||||
if Payout.pending.where(event_id: event_id).where.not(id: id).exists?
|
||||
errors.add(:base, "only one pending payout allowed per event")
|
||||
end
|
||||
end
|
||||
|
||||
# === Scopes ===
|
||||
scope :completed, -> { where(status: :completed) }
|
||||
scope :pending, -> { where(status: :pending) }
|
||||
scope :processing, -> { where(status: :processing) }
|
||||
|
||||
# === Callbacks ===
|
||||
after_create :calculate_refunded_orders_count
|
||||
|
||||
# === Instance Methods ===
|
||||
|
||||
# Amount in euros (formatted)
|
||||
@@ -56,4 +82,14 @@ class Payout < ApplicationRecord
|
||||
service = PayoutService.new(self)
|
||||
service.process!
|
||||
end
|
||||
end
|
||||
public
|
||||
|
||||
# === Instance Methods ===
|
||||
|
||||
def calculate_refunded_orders_count
|
||||
refunded_order_ids = event.tickets.where(status: "refunded").select(:order_id).distinct.pluck(:order_id)
|
||||
paid_statuses = %w[paid completed]
|
||||
count = event.orders.where(status: paid_statuses).where(id: refunded_order_ids).count
|
||||
update_column(:refunded_orders_count, count)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user