Resolve merge conflicts in payout system implementation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -2,8 +2,7 @@ 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
|
||||
@admin_user = User.create!(email: "admin@example.com", password: "password123", password_confirmation: "password123", is_professionnal: true, stripe_customer_id: "cus_test_admin", onboarding_completed: true)
|
||||
@payout = payouts(:one)
|
||||
end
|
||||
|
||||
@@ -65,4 +64,25 @@ class Admin::PayoutsControllerTest < ActionDispatch::IntegrationTest
|
||||
patch admin_payout_url(@payout)
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
end
|
||||
|
||||
test "approve payout requires admin authentication" do
|
||||
post approve_admin_payout_url(@payout)
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
|
||||
test "approve payout works for admin users" do
|
||||
sign_in @admin_user
|
||||
@payout.update(status: :pending)
|
||||
|
||||
# Ensure the payout user has complete banking info
|
||||
@payout.user.update!(
|
||||
iban: "FR1420041010050500013M02606",
|
||||
bank_name: "Test Bank",
|
||||
account_holder_name: "Test User"
|
||||
)
|
||||
|
||||
post approve_admin_payout_url(@payout)
|
||||
assert_redirected_to admin_payout_path(@payout)
|
||||
assert_match /Payout approved successfully/, flash[:notice]
|
||||
end
|
||||
end
|
||||
@@ -440,7 +440,7 @@ class EventTest < ActiveSupport::TestCase
|
||||
user = users(:one)
|
||||
user.update!(is_professionnal: true)
|
||||
|
||||
eligible = Event.create!(name: "Eligible", slug: "eligible", 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)
|
||||
eligible = Event.create!(name: "Eligible", slug: "eligible", description: "This is a test event description", venue_name: "Test Venue", venue_address: "Test Address", latitude: 48.0, longitude: 2.0, start_time: 1.day.ago, end_time: 2.days.ago, user: user, state: :published)
|
||||
# Setup net >0
|
||||
ticket = Ticket.create!(order: orders(:one), ticket_type: ticket_types(:one), qr_code: "qr7", price_cents: 1000, status: "active", first_name: "Test", last_name: "User")
|
||||
ticket.event = eligible
|
||||
|
||||
@@ -96,15 +96,12 @@ class UserTest < ActiveSupport::TestCase
|
||||
|
||||
test "can_receive_payouts? returns true if stripe account id present and charges enabled" do
|
||||
user = users(:one)
|
||||
user.update!(stripe_connected_account_id: "acct_12345", is_professionnal: true)
|
||||
|
||||
# Mock Stripe API call
|
||||
Stripe::Account.expects(:retrieve).with("acct_12345").returns(stub(charges_enabled: true))
|
||||
user.update!(iban: "FR1420041010050500013M02606", bank_name: "Test Bank", account_holder_name: "John Doe", is_professionnal: true)
|
||||
|
||||
assert user.can_receive_payouts?
|
||||
end
|
||||
|
||||
test "can_receive_payouts? returns false if no stripe account id" do
|
||||
test "can_receive_payouts? returns false if no banking info" do
|
||||
user = users(:one)
|
||||
user.update!(is_professionnal: true)
|
||||
|
||||
@@ -113,25 +110,21 @@ class UserTest < ActiveSupport::TestCase
|
||||
|
||||
test "can_receive_payouts? returns false if not professional" do
|
||||
user = users(:one)
|
||||
user.update!(stripe_connected_account_id: "acct_12345")
|
||||
user.update!(iban: "FR1420041010050500013M02606", bank_name: "Test Bank", account_holder_name: "John Doe")
|
||||
|
||||
assert_not user.can_receive_payouts?
|
||||
end
|
||||
|
||||
test "can_receive_payouts? returns false if charges not enabled" do
|
||||
test "can_receive_payouts? returns false if missing IBAN" do
|
||||
user = users(:one)
|
||||
user.update!(stripe_connected_account_id: "acct_12345", is_professionnal: true)
|
||||
|
||||
Stripe::Account.expects(:retrieve).with("acct_12345").returns(stub(charges_enabled: false))
|
||||
user.update!(bank_name: "Test Bank", account_holder_name: "John Doe", is_professionnel: true)
|
||||
|
||||
assert_not user.can_receive_payouts?
|
||||
end
|
||||
|
||||
test "can_receive_payouts? handles Stripe API error" do
|
||||
test "can_receive_payouts? returns false if missing bank name" do
|
||||
user = users(:one)
|
||||
user.update!(stripe_connected_account_id: "acct_invalid", is_professionnal: true)
|
||||
|
||||
Stripe::Account.expects(:retrieve).with("acct_invalid").raises(Stripe::InvalidRequestError.new("Account not found"))
|
||||
user.update!(iban: "FR1420041010050500013M02606", account_holder_name: "John Doe", is_professionnel: true)
|
||||
|
||||
assert_not user.can_receive_payouts?
|
||||
end
|
||||
|
||||
@@ -10,64 +10,49 @@ class PayoutServiceTest < ActiveSupport::TestCase
|
||||
Stripe.api_key = "test_key"
|
||||
end
|
||||
|
||||
test "process! success creates transfer and updates status" do
|
||||
# Mock Stripe Transfer
|
||||
Stripe::Transfer.expects(:create).with(
|
||||
amount: 90, # cents to euros
|
||||
currency: "eur",
|
||||
destination: @user.stripe_connected_account_id,
|
||||
description: "Payout for event #{@event.name}"
|
||||
).returns(stub(id: "tr_123", status: "succeeded"))
|
||||
|
||||
test "process! throws error for manual workflow" do
|
||||
@payout.update(status: :pending)
|
||||
|
||||
service = PayoutService.new(@payout)
|
||||
service.process!
|
||||
|
||||
@payout.reload
|
||||
assert_equal :completed, @payout.status
|
||||
assert_equal "tr_123", @payout.stripe_payout_id
|
||||
assert @payout.earnings.update_all(status: :paid) # assume update_earnings_status
|
||||
end
|
||||
|
||||
test "process! failure with Stripe error sets status to failed" do
|
||||
Stripe::Transfer.expects(:create).raises(Stripe::CardError.new("Insufficient funds"))
|
||||
|
||||
@payout.update(status: :pending)
|
||||
|
||||
service = PayoutService.new(@payout)
|
||||
assert_raises Stripe::CardError do
|
||||
error = assert_raises(RuntimeError) do
|
||||
service.process!
|
||||
end
|
||||
|
||||
@payout.reload
|
||||
assert_equal :failed, @payout.status
|
||||
assert_not_nil @payout.error_message # assume logged
|
||||
assert_includes error.message, "Automatic payout processing is disabled"
|
||||
end
|
||||
|
||||
test "process! idempotent for already completed" do
|
||||
@payout.update(status: :completed, stripe_payout_id: "tr_456")
|
||||
|
||||
Stripe::Transfer.expects(:create).never
|
||||
test "generate_transfer_summary returns payout details" do
|
||||
@user.update!(iban: "FR1420041010050500013M02606", bank_name: "Test Bank", account_holder_name: "John Doe")
|
||||
@payout.update(status: :approved)
|
||||
|
||||
service = PayoutService.new(@payout)
|
||||
service.process!
|
||||
summary = service.generate_transfer_summary
|
||||
|
||||
@payout.reload
|
||||
assert_equal :completed, @payout.status
|
||||
assert_not_nil summary
|
||||
assert_equal @payout.id, summary[:payout_id]
|
||||
assert_equal @user.name, summary[:recipient]
|
||||
assert_equal @user.account_holder_name, summary[:account_holder]
|
||||
assert_equal @user.bank_name, summary[:bank_name]
|
||||
assert_equal @user.iban, summary[:iban]
|
||||
end
|
||||
|
||||
test "update_earnings_status marks earnings as paid" do
|
||||
earning1 = Earning.create!(event: @event, user: @user, order: orders(:one), amount_cents: 4500, fee_cents: 500, status: :pending)
|
||||
earning2 = Earning.create!(event: @event, user: @user, order: orders(:two), amount_cents: 4500, fee_cents: 500, status: :pending)
|
||||
@payout.earnings << earning1
|
||||
@payout.earnings << earning2
|
||||
test "validate_banking_info returns errors for missing data" do
|
||||
service = PayoutService.new(@payout)
|
||||
errors = service.validate_banking_info
|
||||
|
||||
assert_includes errors, "Missing IBAN"
|
||||
assert_includes errors, "Missing bank name"
|
||||
assert_includes errors, "Missing account holder name"
|
||||
end
|
||||
|
||||
test "validate_banking_info returns no errors for complete data" do
|
||||
@user.update!(iban: "FR1420041010050500013M02606", bank_name: "Test Bank", account_holder_name: "John Doe")
|
||||
|
||||
service = PayoutService.new(@payout)
|
||||
service.update_earnings_status(:paid)
|
||||
errors = service.validate_banking_info
|
||||
|
||||
assert_equal :paid, earning1.reload.status
|
||||
assert_equal :paid, earning2.reload.status
|
||||
assert_empty errors
|
||||
end
|
||||
|
||||
test "process! handles manual processing when user has no stripe account" do
|
||||
|
||||
@@ -146,10 +146,12 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
|
||||
Stripe::Customer.expects(:retrieve).with("cus_existing123").returns(mock_customer)
|
||||
|
||||
# Mock the rest of the invoice creation process
|
||||
mock_finalized_invoice = mock("finalized_invoice")
|
||||
mock_finalized_invoice.expects(:pay).with(paid_out_of_band: true, payment_method: nil).returns(mock_finalized_invoice)
|
||||
|
||||
mock_invoice = mock("invoice")
|
||||
mock_invoice.stubs(:id).returns("in_test123")
|
||||
mock_invoice.stubs(:finalize_invoice).returns(mock_invoice)
|
||||
mock_invoice.expects(:pay)
|
||||
mock_invoice.expects(:finalize_invoice).returns(mock_finalized_invoice)
|
||||
Stripe::Invoice.expects(:create).returns(mock_invoice)
|
||||
Stripe::InvoiceItem.expects(:create).once # Only for tickets, no service fee
|
||||
|
||||
@@ -168,10 +170,12 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
|
||||
Stripe::Customer.expects(:create).returns(mock_customer)
|
||||
|
||||
# Mock the rest of the invoice creation process
|
||||
mock_finalized_invoice = mock("finalized_invoice")
|
||||
mock_finalized_invoice.expects(:pay).with(paid_out_of_band: true, payment_method: nil).returns(mock_finalized_invoice)
|
||||
|
||||
mock_invoice = mock("invoice")
|
||||
mock_invoice.stubs(:id).returns("in_test123")
|
||||
mock_invoice.stubs(:finalize_invoice).returns(mock_invoice)
|
||||
mock_invoice.expects(:pay)
|
||||
mock_invoice.expects(:finalize_invoice).returns(mock_finalized_invoice)
|
||||
Stripe::Invoice.expects(:create).returns(mock_invoice)
|
||||
Stripe::InvoiceItem.expects(:create).once # Only for tickets, no service fee
|
||||
|
||||
@@ -210,10 +214,12 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
|
||||
}
|
||||
}
|
||||
|
||||
mock_finalized_invoice = mock("finalized_invoice")
|
||||
mock_finalized_invoice.expects(:pay).with(paid_out_of_band: true, payment_method: nil).returns(mock_finalized_invoice)
|
||||
|
||||
mock_invoice = mock("invoice")
|
||||
mock_invoice.stubs(:id).returns("in_test123")
|
||||
mock_invoice.stubs(:finalize_invoice).returns(mock_invoice)
|
||||
mock_invoice.expects(:pay)
|
||||
mock_invoice.expects(:finalize_invoice).returns(mock_finalized_invoice)
|
||||
Stripe::Invoice.expects(:create).returns(mock_invoice)
|
||||
Stripe::InvoiceItem.expects(:create).with(expected_ticket_line_item) # Only for tickets, no service fee
|
||||
|
||||
@@ -242,10 +248,12 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
|
||||
due_date: anything
|
||||
}
|
||||
|
||||
mock_finalized_invoice = mock("finalized_invoice")
|
||||
mock_finalized_invoice.expects(:pay).with(paid_out_of_band: true, payment_method: nil).returns(mock_finalized_invoice)
|
||||
|
||||
mock_invoice = mock("invoice")
|
||||
mock_invoice.stubs(:id).returns("in_test123")
|
||||
mock_invoice.stubs(:finalize_invoice).returns(mock_invoice)
|
||||
mock_invoice.expects(:pay)
|
||||
mock_invoice.expects(:finalize_invoice).returns(mock_finalized_invoice)
|
||||
|
||||
Stripe::Invoice.expects(:create).with(expected_invoice_data).returns(mock_invoice)
|
||||
Stripe::InvoiceItem.expects(:create).once # Only for tickets, no service fee
|
||||
@@ -291,7 +299,7 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
|
||||
mock_invoice.expects(:finalize_invoice).returns(mock_finalized_invoice)
|
||||
|
||||
result = @service.create_post_payment_invoice
|
||||
assert_equal mock_invoice, result
|
||||
assert_equal mock_finalized_invoice, result
|
||||
end
|
||||
|
||||
# === Class Method Tests ===
|
||||
|
||||
Reference in New Issue
Block a user