Resolve merge conflicts in payout system implementation

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
kbe
2025-09-18 00:18:02 +02:00
32 changed files with 997 additions and 358 deletions

View File

@@ -2,13 +2,16 @@ class Payout < ApplicationRecord
# === Relations ===
belongs_to :user
belongs_to :event
belongs_to :processed_by, class_name: "User", optional: true
# === Enums ===
enum :status, {
pending: 0, # Payout requested but not processed
processing: 1, # Payout being processed
completed: 2, # Payout successfully completed
failed: 3 # Payout failed
pending: 0, # Payout requested but not reviewed
approved: 1, # Payout approved by admin, ready for transfer
processing: 2, # Payout being processed (bank transfer initiated)
completed: 3, # Payout successfully completed
failed: 4, # Payout failed
rejected: 5 # Payout rejected by admin
}, default: :pending
# === Validations ===
@@ -28,14 +31,6 @@ class Payout < ApplicationRecord
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")
@@ -45,7 +40,11 @@ class Payout < ApplicationRecord
# === Scopes ===
scope :completed, -> { where(status: :completed) }
scope :pending, -> { where(status: :pending) }
scope :approved, -> { where(status: :approved) }
scope :processing, -> { where(status: :processing) }
scope :rejected, -> { where(status: :rejected) }
scope :failed, -> { where(status: :failed) }
scope :eligible_for_payout, -> { joins(:event).where(events: { state: "published" }) }
# === Callbacks ===
after_create :calculate_refunded_orders_count
@@ -72,15 +71,74 @@ class Payout < ApplicationRecord
net_amount_cents / 100.0
end
# Check if payout can be processed
def can_process?
pending? && amount_cents > 0
# Check if payout can be approved (was pending)
def can_approve?
pending? && amount_cents > 0 && user.has_complete_banking_info?
end
# Process the payout through Stripe
def process_payout!
service = PayoutService.new(self)
service.process!
# Check if payout can be manually processed (was approved)
def can_process?
approved? && amount_cents > 0
end
# Check if payout can be rejected
def can_reject?
pending?
end
# Approve the payout for manual processing
def approve!(admin_user)
return false unless can_approve?
update!(
status: :approved,
processed_by: admin_user,
processed_at: Time.current
)
end
# Reject the payout with reason
def reject!(admin_user, reason)
return false unless can_reject?
update!(
status: :rejected,
processed_by: admin_user,
processed_at: Time.current,
rejection_reason: reason
)
end
# Mark as processing (bank transfer initiated)
def mark_processing!(admin_user, transfer_reference = nil)
return false unless can_process?
update!(
status: :processing,
processed_by: admin_user,
processed_at: Time.current,
bank_transfer_reference: transfer_reference
)
end
# Mark as completed (bank transfer confirmed)
def mark_completed!(admin_user, transfer_reference = nil)
return false unless processing?
update!(
status: :completed,
processed_by: admin_user,
processed_at: Time.current,
bank_transfer_reference: transfer_reference || bank_transfer_reference
)
update_earnings_status
end
# Mark as failed
def mark_failed!(admin_user, reason)
return false unless processing?
update!(
status: :failed,
processed_by: admin_user,
processed_at: Time.current,
rejection_reason: reason
)
end
# Mark payout as manually processed (for countries where Stripe payouts are not available)
@@ -116,4 +174,10 @@ class Payout < ApplicationRecord
count = event.orders.where(status: paid_statuses).where(id: refunded_order_ids).count
update_column(:refunded_orders_count, count)
end
private
def update_earnings_status
event.earnings.where(status: 0).update_all(status: 1) # pending to paid
end
end