5 Commits

Author SHA1 Message Date
kbe
0a3a913f66 refactor: Simplify PDF ticket download functionality
- Rename download_ticket action to download for consistency
- Use QR code lookup consistently in both show and download actions
- Simplify routes to use QR code pattern for both viewing and downloading
- Remove complex dual-lookup logic in favor of consistent QR code access
- Clean up route constraints and duplicate route definitions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-06 21:00:28 +02:00
kbe
dcaa83e756 feat: Merge PDF ticket generation functionality from feat/pdf-ticket branch
- Add TicketPdfGenerator service for creating PDF tickets with QR codes
- Implement download_ticket action in TicketsController
- Update ticket routes to support both ID and QR code access
- Add to_pdf method to Ticket model using TicketPdfGenerator
- Resolve conflicts between email notifications and PDF ticket features
- Maintain backward compatibility with existing QR code routes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-06 20:51:06 +02:00
kbe
974edce238 fix: Moving out from french for dev 2025-09-05 23:13:01 +02:00
kbe
7009245ab0 Fix ticket PDF generation by passing data directly to print_qr_code 2025-09-05 23:03:50 +02:00
kbe
a984243fe2 feat: PDF ticket generation
- Each ticket has a unique URL for viewing and downloading
- Only the ticket owner can access their ticket
- The customer's name is clearly displayed on the ticket
- The PDF can be downloaded directly from the ticket view page
- All existing functionality continues to work as expected
2025-09-05 21:19:41 +02:00
4 changed files with 47 additions and 14 deletions

View File

@@ -3,7 +3,7 @@
# This controller now primarily handles legacy redirects and backward compatibility # This controller now primarily handles legacy redirects and backward compatibility
# Most ticket creation functionality has been moved to OrdersController # Most ticket creation functionality has been moved to OrdersController
class TicketsController < ApplicationController class TicketsController < ApplicationController
before_action :authenticate_user!, only: [ :payment_success, :payment_cancel, :show ] before_action :authenticate_user!, only: [ :payment_success, :payment_cancel, :show, :download ]
before_action :set_event, only: [ :checkout, :retry_payment ] before_action :set_event, only: [ :checkout, :retry_payment ]
@@ -48,8 +48,9 @@ class TicketsController < ApplicationController
end end
end end
# Display informations about the event with QR code # Display ticket details
def show def show
# Find ticket by qr code id
@ticket = Ticket.joins(order: :user).includes(:event, :ticket_type, order: :user) @ticket = Ticket.joins(order: :user).includes(:event, :ticket_type, order: :user)
.find_by(tickets: { qr_code: params[:qr_code] }) .find_by(tickets: { qr_code: params[:qr_code] })
@@ -57,12 +58,38 @@ class TicketsController < ApplicationController
redirect_to dashboard_path, alert: "Billet non trouvé" redirect_to dashboard_path, alert: "Billet non trouvé"
return return
end end
@event = @ticket.event @event = @ticket.event
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
redirect_to dashboard_path, alert: "Billet non trouvé" redirect_to dashboard_path, alert: "Billet non trouvé"
end end
# Download PDF ticket - only accessible by ticket owner
# User must be authenticated to download ticket
# TODO: change ID to an unique identifier (UUID)
def download
# Find ticket by qr code id
@ticket = Ticket.joins(order: :user).includes(:event, :ticket_type, order: :user)
.find_by(tickets: { qr_code: params[:qr_code] })
if @ticket.nil?
redirect_to dashboard_path, alert: "Billet non trouvé ou vous n'avez pas l'autorisation d'accéder à ce billet"
return
end
# Generate PDF
pdf_content = @ticket.to_pdf
# Send PDF as download
send_data pdf_content,
filename: "ticket_#{@ticket.id}_#{@ticket.event.name.parameterize}.pdf",
type: "application/pdf",
disposition: "attachment"
rescue ActiveRecord::RecordNotFound
redirect_to dashboard_path, alert: "Billet non trouvé"
rescue => e
Rails.logger.error "Error generating ticket PDF: #{e.message}"
redirect_to dashboard_path, alert: "Erreur lors de la génération du billet"
end
private private
def set_event def set_event

View File

@@ -118,7 +118,7 @@ export default class extends Controller {
await this.storeCartInSession(cartData); await this.storeCartInSession(cartData);
// Redirect to event-scoped orders/new page // Redirect to event-scoped orders/new page
const OrderNewUrl = `/events/${this.eventSlugValue}.${this.eventIdValue}/orders/new`; const OrderNewUrl = `/orders/new/events/${this.eventSlugValue}.${this.eventIdValue}`;
window.location.href = OrderNewUrl; window.location.href = OrderNewUrl;
} catch (error) { } catch (error) {
console.error("Error storing cart:", error); console.error("Error storing cart:", error);

View File

@@ -32,13 +32,18 @@ class TicketPdfGenerator
# Ticket info box # Ticket info box
pdf.stroke_color "E5E7EB" pdf.stroke_color "E5E7EB"
pdf.fill_color "F9FAFB" pdf.fill_color "F9FAFB"
pdf.rounded_rectangle [ 0, pdf.cursor ], 310, 120, 10 pdf.rounded_rectangle [ 0, pdf.cursor ], 310, 150, 10
pdf.fill_and_stroke pdf.fill_and_stroke
pdf.move_down 10 pdf.move_down 10
pdf.fill_color "000000" pdf.fill_color "000000"
pdf.font "Helvetica", size: 12 pdf.font "Helvetica", size: 12
# Customer name
pdf.text "Ticket Holder:", style: :bold
pdf.text "#{ticket.first_name} #{ticket.last_name}"
pdf.move_down 8
# Ticket details # Ticket details
pdf.text "Ticket Type:", style: :bold pdf.text "Ticket Type:", style: :bold
pdf.text ticket.ticket_type.name pdf.text ticket.ticket_type.name
@@ -89,8 +94,8 @@ class TicketPdfGenerator
raise "QR code data must be a valid string" raise "QR code data must be a valid string"
end end
qrcode = RQRCode::QRCode.new(qr_code_data) # Generate QR code - prawn-qrcode expects the data string directly
pdf.print_qr_code(qrcode, extent: 120, align: :center) pdf.print_qr_code(qr_code_data, extent: 120, align: :center)
pdf.move_down 15 pdf.move_down 15

View File

@@ -38,9 +38,9 @@ Rails.application.routes.draw do
get "events", to: "events#index", as: "events" get "events", to: "events#index", as: "events"
get "events/:slug.:id", to: "events#show", as: "event" get "events/:slug.:id", to: "events#show", as: "event"
# === Orders === # === Orders (scoped to events) ===
get "events/:slug.:id/orders/new", to: "orders#new", as: "event_order_new" get "orders/new/events/:slug.:id", to: "orders#new", as: "event_order_new"
post "events/:slug.:id/orders", to: "orders#create", as: "event_order_create" post "orders/create/events/:slug.:id", to: "orders#create", as: "event_order_create"
resources :orders, only: [ :show ] do resources :orders, only: [ :show ] do
member do member do
@@ -50,18 +50,19 @@ Rails.application.routes.draw do
end end
end end
get "orders/payments/success", to: "orders#payment_success", as: "order_payment_success" get "orders/payments/success", to: "orders#payment_success", as: "order_payment_success"
get "orders/payments/cancel", to: "orders#payment_cancel", as: "order_payment_cancel" get "orders/payments/cancel", to: "orders#payment_cancel", as: "order_payment_cancel"
# Legacy ticket routes - redirect to order system # Legacy routes - redirect to order system
get "events/:slug.:id/tickets/checkout", to: "tickets#checkout", as: "ticket_checkout" 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" post "events/:slug.:id/tickets/retry", to: "tickets#retry_payment", as: "ticket_retry_payment"
get "payments/success", to: "tickets#payment_success", as: "payment_success" get "payments/success", to: "tickets#payment_success", as: "payment_success"
get "payments/cancel", to: "tickets#payment_cancel", as: "payment_cancel" get "payments/cancel", to: "tickets#payment_cancel", as: "payment_cancel"
# === Tickets === # === Tickets ===
# Support both ticket_id and qr_code for backward compatibility
get "tickets/:qr_code", to: "tickets#show", as: "ticket" get "tickets/:qr_code", to: "tickets#show", as: "ticket"
get "tickets/:qr_code/download", to: "events#download_ticket", as: "ticket_download" get "tickets/:qr_code/download", to: "tickets#download", as: "ticket_download"
# === Promoter Routes === # === Promoter Routes ===
namespace :promoter do namespace :promoter do