feat: Add manual payout system for countries without Stripe Global Payouts

This commit is contained in:
kbe
2025-09-17 08:35:20 +02:00
parent 3c1e17c2af
commit c74140c431
12 changed files with 375 additions and 28 deletions

View File

@@ -41,6 +41,26 @@ class Admin::PayoutsControllerTest < ActionDispatch::IntegrationTest
assert_equal :failed, @payout.reload.status
end
test "mark_as_manually_processed updates payout status" do
sign_in @admin_user
@payout.update(status: :pending)
post mark_as_manually_processed_admin_payout_url(@payout)
assert_redirected_to admin_payouts_path
assert_flash :notice, /marked as manually processed/
assert @payout.reload.completed?
assert @payout.manual_payout?
end
test "mark_as_manually_processed fails for completed payout" do
sign_in @admin_user
@payout.update(status: :completed)
post mark_as_manually_processed_admin_payout_url(@payout)
assert_redirected_to admin_payouts_path
assert_flash :alert, /Cannot mark this payout as manually processed/
end
test "requires admin authentication" do
patch admin_payout_url(@payout)
assert_redirected_to new_user_session_path

View File

@@ -135,6 +135,17 @@ class Promoter::PayoutsControllerTest < ActionDispatch::IntegrationTest
assert_flash :alert, /Event not eligible for payout/
end
test "show renders manual payout details correctly" do
sign_in @user
@user.update(is_professionnal: true)
payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100, status: :completed, stripe_payout_id: "MANUAL_abc123")
get promoter_payout_url(payout)
assert_response :success
assert_match "Manual Payout ID", @response.body
assert_match "Manual Transfer", @response.body
end
# Create failure: validation errors
test "create payout fails with validation errors" do
sign_in @user

View File

@@ -2,9 +2,23 @@ require "test_helper"
class PayoutTest < ActiveSupport::TestCase
setup do
@payout = payouts(:one)
@user = users(:one)
@event = events(:concert_event)
@user = User.create!(email: "test@example.com", password: "password123", is_professionnal: true)
@event = Event.create!(
user: @user,
name: "Test Event",
slug: "test-event",
description: "Test event description",
venue_name: "Test Venue",
venue_address: "Test Address",
latitude: 48.8566,
longitude: 2.3522,
start_time: 1.day.ago,
end_time: 1.hour.ago,
state: :published
)
# Create some earnings for the event
Earning.create!(event: @event, user: @user, order: Order.create!(user: @user, event: @event, status: :paid, total_amount_cents: 1000), amount_cents: 2000, fee_cents: 200, status: :pending)
@payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100)
end
test "should be valid" do
@@ -36,14 +50,24 @@ class PayoutTest < ActiveSupport::TestCase
end
test "validations: net earnings must be greater than 0" do
# Assuming event.net_earnings_cents is a method that calculates >0
@event.earnings.create!(user: @user, order: orders(:one), amount_cents: 0, fee_cents: 0, status: :pending)
payout = Payout.new(user: @user, event: @event, amount_cents: 1000, fee_cents: 100)
# Create an event with no earnings (net earnings = 0)
event_without_earnings = Event.create!(
user: @user,
name: "Test Event",
slug: "test-event-2",
description: "Test event description",
venue_name: "Test Venue",
venue_address: "Test Address",
latitude: 48.8566,
longitude: 2.3522,
start_time: 1.day.ago,
end_time: 1.hour.ago,
state: :published
)
payout = Payout.new(user: @user, event: event_without_earnings, amount_cents: 1000, fee_cents: 100)
assert_not payout.valid?
assert_includes payout.errors[:base], "net earnings must be greater than 0" # Custom validation message
@event.earnings.first.update(amount_cents: 2000)
assert payout.valid?
end
test "validations: only one pending payout per event" do
@@ -62,9 +86,14 @@ class PayoutTest < ActiveSupport::TestCase
end
test "after_create callback sets refunded_orders_count" do
refund_count = @event.orders.refunded.count # Assuming orders have refunded status
# Create some refunded tickets to test the callback
order = Order.create!(user: @user, event: @event, status: :paid, total_amount_cents: 1000)
ticket_type = TicketType.create!(event: @event, name: "General Admission", price_cents: 1000, quantity: 10)
ticket = Ticket.create!(order: order, ticket_type: ticket_type, price_cents: 1000, status: :refunded)
payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100)
assert_equal refund_count, payout.refunded_orders_count
# The refunded_orders_count should be set by the callback
assert_equal 1, payout.refunded_orders_count
end
test "associations: belongs to user" do
@@ -98,12 +127,29 @@ class PayoutTest < ActiveSupport::TestCase
assert_not_includes Payout.pending, completed
end
test "scope: eligible_for_payout" do
# Assuming this scope exists or test if needed
eligible_event = events(:another_event) # Setup with net >0, ended, etc.
ineligible = events(:ineligible)
eligible_payouts = Payout.eligible_for_payout
assert_includes eligible_payouts, eligible_event.payouts.first if eligible_event.can_request_payout?
test "manual_payout? returns true for manual payouts" do
payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100,
stripe_payout_id: "MANUAL_abc123")
assert payout.manual_payout?
end
end
test "manual_payout? returns false for Stripe payouts" do
payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100,
stripe_payout_id: "tr_123")
assert_not payout.manual_payout?
end
test "manual_payout? returns false when no stripe_payout_id" do
payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100)
assert_not payout.manual_payout?
end
test "mark_as_manually_processed! updates status and creates manual ID" do
payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100, status: :pending)
payout.mark_as_manually_processed!
assert payout.completed?
assert payout.manual_payout?
assert_match /MANUAL_/, payout.stripe_payout_id
end
end

View File

@@ -69,4 +69,40 @@ class PayoutServiceTest < ActiveSupport::TestCase
assert_equal :paid, earning1.reload.status
assert_equal :paid, earning2.reload.status
end
test "process! handles manual processing when user has no stripe account" do
# Create a user without a stripe account
user_without_stripe = User.create!(
email: "test@example.com",
password: "password123",
is_professionnal: true
)
event = Event.create!(
user: user_without_stripe,
name: "Test Event",
slug: "test-event",
description: "Test event description",
venue_name: "Test Venue",
venue_address: "Test Address",
latitude: 48.8566,
longitude: 2.3522,
start_time: 1.day.ago,
end_time: 1.hour.ago,
state: :published
)
payout = Payout.create!(user: user_without_stripe, event: event, amount_cents: 9000, fee_cents: 1000, status: :pending)
# Mock that Stripe is not available for this user
user_without_stripe.stubs(:has_stripe_account?).returns(false)
service = PayoutService.new(payout)
service.process!
payout.reload
assert_equal :completed, payout.status
assert payout.manual_payout?
assert_match /MANUAL_/, payout.stripe_payout_id
end
end