feat: Implement promoter payout system for event revenue processing

- Add Payout model with associations to User and Event
- Create payout requests for completed events with proper earnings calculation
- Exclude refunded tickets from payout calculations
- Add promoter dashboard views for managing payouts
- Implement admin interface for processing payouts
- Integrate with Stripe for actual payment processing
- Add comprehensive tests for payout functionality

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
kbe
2025-09-17 00:29:20 +02:00
parent 58141dca94
commit 59e1854803
20 changed files with 587 additions and 254 deletions

View File

@@ -1,39 +1,70 @@
<div class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold mb-6">My Payouts</h1>
<div class="flex justify-between items-center mb-6">
<h1 class="text-3xl font-bold text-gray-900">Payouts</h1>
</div>
<% if @events.any? %>
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<% @events.each do |event| %>
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-xl font-semibold mb-2"><%= event.name %></h3>
<p class="text-gray-600 mb-2">Date: <%= event.start_time.strftime('%B %d, %Y at %I:%M %p') %></p>
<p class="text-gray-600 mb-4">Status: <span class="font-medium"><%= event.payout_status.humanize %></span></p>
<% if event.earnings.pending.any? %>
<div class="mb-4">
<p class="text-lg font-semibold text-green-600">Gross: €<%= (event.total_earnings_cents / 100.0).round(2) %></p>
<p class="text-sm text-gray-500">Fees (10%): €<%= (event.total_fees_cents / 100.0).round(2) %></p>
<p class="text-lg font-semibold text-blue-600">Net: €<%= (event.net_earnings_cents / 100.0).round(2) %></p>
</div>
<% if event.can_request_payout? %>
<%= button_to "Request Payout", promoter_payouts_path(event_id: event.id),
method: :post,
class: "w-full bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" %>
<% else %>
<p class="text-sm text-gray-500">Payout not available yet</p>
<% end %>
<% else %>
<p class="text-gray-500">No pending earnings</p>
<% if @payouts.any? %>
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Event</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<% @payouts.each do |payout| %>
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900"><%= payout.event&.name || "Event not found" %></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">€<%= payout.amount_euros %></div>
<div class="text-sm text-gray-500">Net: €<%= payout.net_amount_euros %> (Fee: €<%= payout.fee_euros %>)</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<% case payout.status %>
<% when 'pending' %>
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
Pending
</span>
<% when 'processing' %>
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">
Processing
</span>
<% when 'completed' %>
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
Completed
</span>
<% when 'failed' %>
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
Failed
</span>
<% end %>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<%= payout.created_at.strftime("%b %d, %Y") %>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<%= link_to "View", promoter_payout_path(payout), class: "text-indigo-600 hover:text-indigo-900" %>
</td>
</tr>
<% end %>
</div>
<% end %>
</tbody>
</table>
</div>
<% if @payouts.respond_to?(:total_pages) %>
<div class="mt-6">
<%= paginate @payouts %>
</div>
<% end %>
<% else %>
<div class="text-center py-12">
<h2 class="text-2xl font-semibold mb-2">No events found</h2>
<p class="text-gray-600 mb-4">You haven't created any events yet.</p>
<%= link_to "Create Event", new_promoter_event_path, class: "bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" %>
<div class="bg-white rounded-lg shadow p-6 text-center">
<p class="text-gray-500">No payouts found.</p>
</div>
<% end %>
</div>
</div>