require "prawn" require "prawn/qrcode" require "rqrcode" # PDF ticket generator service using Prawn # # Generates PDF tickets with QR codes for event entry validation # Includes event details, venue information, and unique QR code for each ticket class TicketPdfGenerator # Suppress Prawn's internationalization warning for built-in fonts Prawn::Fonts::AFM.hide_m17n_warning = true attr_reader :ticket def initialize(ticket) @ticket = ticket end def generate Prawn::Document.new(page_size: [ 350, 600 ], margin: 20) do |pdf| # Header pdf.fill_color "2D1B69" pdf.font "Helvetica", style: :bold, size: 24 pdf.text "ApéroNight", align: :center pdf.move_down 10 # Event name pdf.fill_color "000000" pdf.font "Helvetica", style: :bold, size: 18 pdf.text ticket.event.name, align: :center pdf.move_down 20 # Ticket info box pdf.stroke_color "E5E7EB" pdf.fill_color "F9FAFB" pdf.rounded_rectangle [ 0, pdf.cursor ], 310, 120, 10 pdf.fill_and_stroke pdf.move_down 10 pdf.fill_color "000000" pdf.font "Helvetica", size: 12 # Ticket details pdf.text "Ticket Type:", style: :bold pdf.text ticket.ticket_type.name pdf.move_down 8 pdf.text "Price:", style: :bold pdf.text "€#{ticket.price_euros}" pdf.move_down 8 pdf.text "Date & Time:", style: :bold pdf.text ticket.event.start_time.strftime("%B %d, %Y at %I:%M %p") pdf.move_down 20 # Venue information pdf.fill_color "374151" pdf.font "Helvetica", style: :bold, size: 14 pdf.text "Venue Information" pdf.move_down 8 pdf.font "Helvetica", size: 11 pdf.text ticket.event.venue_name, style: :bold pdf.text ticket.event.venue_address pdf.move_down 20 # QR Code pdf.fill_color "000000" pdf.font "Helvetica", style: :bold, size: 14 pdf.text "Ticket QR Code", align: :center pdf.move_down 10 # Ensure all required data is present before generating QR code if ticket.qr_code.blank? raise "Ticket QR code is missing" end # Build QR code data with safe association loading qr_code_data = build_qr_code_data(ticket) # Validate QR code data before creating QR code if qr_code_data.blank? || qr_code_data == "{}" Rails.logger.error "QR code data is empty: ticket_id=#{ticket.id}, qr_code=#{ticket.qr_code}, event_id=#{ticket.ticket_type&.event_id}, user_id=#{ticket.order&.user_id}" raise "QR code data is empty or invalid" end # Ensure qr_code_data is a proper string for QR code generation unless qr_code_data.is_a?(String) && qr_code_data.length > 2 Rails.logger.error "QR code data is not a valid string: #{qr_code_data.inspect} (class: #{qr_code_data.class})" raise "QR code data must be a valid string" end qrcode = RQRCode::QRCode.new(qr_code_data) pdf.print_qr_code(qrcode, extent: 120, align: :center) pdf.move_down 15 # QR code text pdf.font "Helvetica", size: 8 pdf.fill_color "6B7280" pdf.text "QR Code: #{ticket.qr_code[0..7]}...", align: :center # Footer pdf.move_down 30 pdf.stroke_color "E5E7EB" pdf.horizontal_line 0, 310 pdf.move_down 10 pdf.font "Helvetica", size: 8 pdf.fill_color "6B7280" pdf.text "This ticket is valid for one entry only.", align: :center pdf.text "Present this ticket at the venue entrance.", align: :center pdf.move_down 5 pdf.text "Generated on #{Time.current.strftime('%B %d, %Y at %I:%M %p')}", align: :center end.render end private def build_qr_code_data(ticket) # Try multiple approaches to get valid QR code data begin # Primary approach: full JSON with all data data = { ticket_id: ticket.id, qr_code: ticket.qr_code, event_id: ticket.ticket_type&.event_id, user_id: ticket.order&.user_id }.compact # Ensure we have the minimum required data if data[:ticket_id] && data[:qr_code] return data.to_json end rescue StandardError => e Rails.logger.warn "Failed to build complex QR data: #{e.message}" end # Fallback approach: just use the ticket's QR code string begin return ticket.qr_code.to_s if ticket.qr_code.present? rescue StandardError => e Rails.logger.warn "Failed to use ticket QR code: #{e.message}" end # Final fallback: simple ticket identifier "TICKET-#{ticket.id}" end end