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:
48
test/controllers/admin/payouts_controller_test.rb
Normal file
48
test/controllers/admin/payouts_controller_test.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
require "test_helper"
|
||||
|
||||
class Admin::PayoutsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin_user = User.create!(email: "admin@example.com", password: "password123", password_confirmation: "password123", is_professionnal: true)
|
||||
@admin_user.add_role :admin # Assume role system
|
||||
@payout = payouts(:one)
|
||||
end
|
||||
|
||||
test "process payout success for pending payout" do
|
||||
sign_in @admin_user
|
||||
@payout.update(status: :pending)
|
||||
|
||||
# Mock service
|
||||
PayoutService.any_instance.expects(:process!).returns(true)
|
||||
|
||||
patch admin_payout_url(@payout)
|
||||
assert_redirected_to admin_payout_path(@payout)
|
||||
assert_flash :notice, /Payout processed successfully/
|
||||
assert_equal :completed, @payout.reload.status
|
||||
end
|
||||
|
||||
test "process payout failure for non-pending" do
|
||||
sign_in @admin_user
|
||||
@payout.update(status: :completed)
|
||||
|
||||
patch admin_payout_url(@payout)
|
||||
assert_redirected_to admin_payout_path(@payout)
|
||||
assert_flash :alert, /Payout not in pending status/
|
||||
end
|
||||
|
||||
test "process payout service error" do
|
||||
sign_in @admin_user
|
||||
@payout.update(status: :pending)
|
||||
|
||||
PayoutService.any_instance.expects(:process!).raises(StandardError.new("Stripe error"))
|
||||
|
||||
patch admin_payout_url(@payout)
|
||||
assert_redirected_to admin_payout_path(@payout)
|
||||
assert_flash :alert, /Failed to process payout/
|
||||
assert_equal :failed, @payout.reload.status
|
||||
end
|
||||
|
||||
test "requires admin authentication" do
|
||||
patch admin_payout_url(@payout)
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
end
|
||||
@@ -46,9 +46,118 @@ class Promoter::PayoutsControllerTest < ActionDispatch::IntegrationTest
|
||||
fee_cents: 100,
|
||||
status: :pending
|
||||
)
|
||||
assert_difference('Payout.count', 1) do
|
||||
assert_difference("Payout.count", 1) do
|
||||
post promoter_payouts_url, params: { event_id: @event.id }
|
||||
end
|
||||
assert_redirected_to promoter_payout_path(Payout.last)
|
||||
end
|
||||
# Comprehensive index test with data
|
||||
test "index shows completed payouts, eligible events, and totals for promoter" do
|
||||
sign_in @user
|
||||
@user.update(is_professionnal: true)
|
||||
|
||||
# Create completed payouts for user
|
||||
completed_payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100, status: :completed)
|
||||
|
||||
# Create eligible event
|
||||
eligible_event = Event.create!(name: "Eligible Event", slug: "eligible-event", description: "desc", venue_name: "v", venue_address: "a", latitude: 48.0, longitude: 2.0, start_time: 1.day.ago, end_time: 2.days.ago, user: @user, state: :published)
|
||||
# Setup net >0 for eligible
|
||||
earning = Earning.create!(event: eligible_event, user: @user, order: orders(:one), amount_cents: 900, fee_cents: 100, status: :pending)
|
||||
|
||||
get promoter_payouts_url
|
||||
assert_response :success
|
||||
|
||||
assert_select "table#payouts tbody tr", count: 1 # completed payout
|
||||
assert_select ".eligible-events li", count: 1 # eligible event
|
||||
assert_match /Pending net earnings: €9.00/, @response.body # totals
|
||||
assert_match /Total paid out: €10.00/, @response.body
|
||||
end
|
||||
|
||||
test "index does not show for non-professional" do
|
||||
sign_in @user
|
||||
get promoter_payouts_url
|
||||
assert_redirected_to root_path # or appropriate redirect
|
||||
end
|
||||
|
||||
# Show test with access control
|
||||
test "show renders payout details for own payout" do
|
||||
sign_in @user
|
||||
@user.update(is_professionnal: true)
|
||||
payout = Payout.create!(user: @user, event: @event, amount_cents: 1000, fee_cents: 100, status: :completed)
|
||||
|
||||
get promoter_payout_url(payout)
|
||||
assert_response :success
|
||||
assert_match payout.amount.to_s, @response.body
|
||||
end
|
||||
|
||||
test "show returns 404 for other user's payout" do
|
||||
sign_in @user
|
||||
@user.update(is_professionnal: true)
|
||||
other_user = User.create!(email: "other@example.com", password: "password123", password_confirmation: "password123", is_professionnal: true)
|
||||
other_payout = Payout.create!(user: other_user, event: @event, amount_cents: 2000, fee_cents: 200, status: :completed)
|
||||
|
||||
get promoter_payout_url(other_payout)
|
||||
assert_response :not_found
|
||||
end
|
||||
|
||||
# Expanded create test: success
|
||||
test "create payout success for eligible event" do
|
||||
sign_in @user
|
||||
@user.update(is_professionnal: true)
|
||||
@event.update(user: @user, end_time: 1.day.ago) # ended
|
||||
# Setup net >0
|
||||
earning = @event.earnings.create!(user: @user, order: orders(:paid_order), amount_cents: 900, fee_cents: 100, status: :pending)
|
||||
# Ensure eligible
|
||||
assert @event.can_request_payout?(@user)
|
||||
|
||||
assert_difference("Payout.count", 1) do
|
||||
post promoter_payouts_url, params: { event_id: @event.id }
|
||||
end
|
||||
assert_redirected_to promoter_payout_path(Payout.last)
|
||||
assert_flash :notice, /Payout requested successfully/
|
||||
assert_equal :requested, @event.reload.payout_status # assume enum
|
||||
payout = Payout.last
|
||||
assert_equal @event.total_gross_cents, payout.amount_cents
|
||||
assert_equal @event.total_fees_cents, payout.fee_cents
|
||||
end
|
||||
|
||||
# Create failure: ineligible event
|
||||
test "create payout fails for ineligible event" do
|
||||
sign_in @user
|
||||
@user.update(is_professionnal: true)
|
||||
@event.update(user: @user, end_time: 1.day.from_now) # not ended
|
||||
assert_not @event.can_request_payout?(@user)
|
||||
|
||||
assert_no_difference("Payout.count") do
|
||||
post promoter_payouts_url, params: { event_id: @event.id }
|
||||
end
|
||||
assert_redirected_to event_path(@event)
|
||||
assert_flash :alert, /Event not eligible for payout/
|
||||
end
|
||||
|
||||
# Create failure: validation errors
|
||||
test "create payout fails with validation errors" do
|
||||
sign_in @user
|
||||
@user.update(is_professionnal: true)
|
||||
@event.update(user: @user, end_time: 1.day.ago)
|
||||
# Setup net =0
|
||||
assert_not @event.can_request_payout?(@user)
|
||||
|
||||
assert_no_difference("Payout.count") do
|
||||
post promoter_payouts_url, params: { event_id: @event.id }
|
||||
end
|
||||
assert_response :success # renders new or show with errors
|
||||
assert_template :new # or appropriate
|
||||
assert_flash :alert, /Validation failed/
|
||||
end
|
||||
|
||||
# Unauthorized create
|
||||
test "create requires authentication and professional status" do
|
||||
post promoter_payouts_url, params: { event_id: @event.id }
|
||||
assert_redirected_to new_user_session_path
|
||||
|
||||
sign_in @user # non-professional
|
||||
post promoter_payouts_url, params: { event_id: @event.id }
|
||||
assert_redirected_to root_path # or deny access
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user