diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 2cc0b4c..3721ea2 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -5,29 +5,98 @@ class OrdersController < ApplicationController before_action :authenticate_user! before_action :set_order, only: [:show, :checkout, :retry_payment, :increment_payment_attempt] + before_action :set_event, only: [:new, :create] - # Display new order form + # Display new order form with name collection # - # On this page user can complete the tickets details (first name and last name), - # add a comment on the order. + # On this page user can see order summary and complete the tickets details + # (first name and last name) for each ticket ordered def new @cart_data = session[:pending_cart] || {} if @cart_data.empty? - redirect_to root_path, alert: "Veuillez d'abord sélectionner vos billets" + redirect_to event_path(@event.slug, @event), alert: "Veuillez d'abord sélectionner vos billets sur la page de l'événement" return end - # Build order preview from cart data - @event_id = session[:event_id] - if @event_id.present? - @event = Event.includes(:ticket_types).find_by(id: @event_id) - redirect_to root_path, alert: "Événement non trouvé" unless @event - else - redirect_to root_path, alert: "Informations manquantes" + # Build list of tickets requiring names + @tickets_needing_names = [] + @cart_data.each do |ticket_type_id, item| + ticket_type = @event.ticket_types.find_by(id: ticket_type_id) + next unless ticket_type + + quantity = item["quantity"].to_i + next if quantity <= 0 + + quantity.times do |i| + @tickets_needing_names << { + ticket_type_id: ticket_type.id, + ticket_type_name: ticket_type.name, + ticket_type_price: ticket_type.price_cents, + index: i + } + end end end + # Create a new order with tickets + # + # Here a new order is created with associated tickets in draft state. + # When user is ready they can proceed to payment via the order checkout + def create + @cart_data = session[:pending_cart] || {} + + if @cart_data.empty? + redirect_to event_path(@event.slug, @event), alert: "Aucun billet sélectionné" + return + end + + success = false + + ActiveRecord::Base.transaction do + @order = current_user.orders.create!(event: @event, status: "draft") + + order_params[:tickets_attributes]&.each do |index, ticket_attrs| + next if ticket_attrs[:first_name].blank? || ticket_attrs[:last_name].blank? + + ticket_type = @event.ticket_types.find(ticket_attrs[:ticket_type_id]) + + ticket = @order.tickets.build( + ticket_type: ticket_type, + first_name: ticket_attrs[:first_name], + last_name: ticket_attrs[:last_name], + status: "draft" + ) + + unless ticket.save + flash[:alert] = "Erreur lors de la création des billets: #{ticket.errors.full_messages.join(', ')}" + raise ActiveRecord::Rollback + end + end + + if @order.tickets.present? + @order.calculate_total! + success = true + else + flash[:alert] = "Aucun billet valide créé" + raise ActiveRecord::Rollback + end + end + + # Handle redirects outside transaction + if success + session[:draft_order_id] = @order.id + session.delete(:pending_cart) + redirect_to checkout_order_path(@order) + else + redirect_to event_order_new_path(@event.slug, @event.id) + end + rescue => e + error_message = e.message.present? ? e.message : "Erreur inconnue" + flash[:alert] = "Une erreur est survenue: #{error_message}" + redirect_to event_order_new_path(@event.slug, @event.id) + end + # Display order summary # # @@ -44,7 +113,7 @@ class OrdersController < ApplicationController if @order.expired? @order.expire_if_overdue! return redirect_to event_path(@order.event.slug, @order.event), - alert: "Votre commande a expiré. Veuillez recommencer." + alert: "Votre commande a expiré. Veuillez recommencer." end @tickets = @order.tickets.includes(:ticket_type) @@ -111,7 +180,12 @@ class OrdersController < ApplicationController # Send confirmation emails @order.tickets.each do |ticket| - TicketMailer.purchase_confirmation(ticket).deliver_now + begin + TicketMailer.purchase_confirmation(ticket).deliver_now + rescue => e + Rails.logger.error "Failed to send confirmation email for ticket #{ticket.id}: #{e.message}" + # Don't fail the entire payment process due to email/PDF generation issues + end end # Clear session data @@ -160,6 +234,16 @@ class OrdersController < ApplicationController redirect_to dashboard_path, alert: "Commande non trouvée" end + def set_event + @event = Event.includes(:ticket_types).find(params[:id]) + rescue ActiveRecord::RecordNotFound + redirect_to events_path, alert: "Événement non trouvé" + end + + def order_params + params.permit(tickets_attributes: [ :ticket_type_id, :first_name, :last_name ]) + end + def create_stripe_session line_items = @order.tickets.map do |ticket| { diff --git a/config/routes.rb b/config/routes.rb index ea9bc3c..e48b4a8 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -38,9 +38,9 @@ Rails.application.routes.draw do get "events", to: "events#index", as: "events" get "events/:slug.:id", to: "events#show", as: "event" - # === Orders === - - get "orders/new", to: "orders#new", as: "order_new" + # === Orders (scoped to events) === + get "events/:slug.:id/orders/new", to: "orders#new", as: "event_order_new" + post "events/:slug.:id/orders", to: "orders#create", as: "event_order_create" resources :orders, only: [:show] do member do @@ -53,11 +53,7 @@ Rails.application.routes.draw do get "orders/payments/success", to: "orders#payment_success", as: "order_payment_success" get "orders/payments/cancel", to: "orders#payment_cancel", as: "order_payment_cancel" - # === Tickets === - get "tickets/new", to: "tickets#new", as: "ticket_new" - post "tickets/create", to: "tickets#create", as: "ticket_create" - - # Keep these for now but they redirect to order system + # Legacy ticket routes - redirect to order system get "events/:slug.:id/tickets/checkout", to: "tickets#checkout", as: "ticket_checkout" post "events/:slug.:id/tickets/retry", to: "tickets#retry_payment", as: "ticket_retry_payment" get "payments/success", to: "tickets#payment_success", as: "payment_success"