diff --git a/app/Helpers/helpers.php b/app/Helpers/helpers.php index 726a906d..2a61dedb 100644 --- a/app/Helpers/helpers.php +++ b/app/Helpers/helpers.php @@ -73,3 +73,10 @@ if(!function_exists('organisers')){ return \Illuminate\Support\Facades\Auth::user()->account->organisers; } } +if(!function_exists('zanitlananlar')){ + function zanitlananlar($ticket){ + $reserved = $ticket->reserved->pluck('seat_no')->crossJoin(['reserved']); + $booked = $ticket->booked->pluck('booked','seat_no')->dump(); + + } +} diff --git a/app/Http/Controllers/API/PublicController.php b/app/Http/Controllers/API/PublicController.php index ead31ad5..1744230e 100644 --- a/app/Http/Controllers/API/PublicController.php +++ b/app/Http/Controllers/API/PublicController.php @@ -36,7 +36,7 @@ class PublicController extends Controller } } if(!empty($date)){ - $e_query->whereDate('start_date','>=',Carbon::parse($date)); + $e_query->where('start_date','>=',Carbon::parse($date)); } return $e_query->select('id','title','start_date') diff --git a/app/Http/Controllers/EventCheckoutController.php b/app/Http/Controllers/EventCheckoutController.php index 5e719ad4..27d57123 100644 --- a/app/Http/Controllers/EventCheckoutController.php +++ b/app/Http/Controllers/EventCheckoutController.php @@ -66,22 +66,20 @@ class EventCheckoutController extends Controller // ]); // } $event = Event::with('venue')->findOrFail($event_id); - $tickets = Ticket::with('section') + $tickets = Ticket::with(['section','reserved:seat_no,ticket_id','booked:id,seat_no,ticket_id']) ->where('event_id',$event_id) ->where('ticket_date',$request->get('ticket_date')) ->where('is_hidden', false) -// ->where('is_paused', false) -// ->whereDate('start_sale_date','=<',Carbon::now()) ->orderBy('sort_order','asc') ->get(); - if($tickets->count()>0){ - return view('Bilettm.ViewEvent.SeatsPage',compact('event','tickets')); - } - else{ + //dd($tickets->first()->booked->pluck('seat_no')->toJson()); + if($tickets->count()==0){ //todo flash message session()->flash('error','There is no tickets available'); return redirect()->back(); } + + return view('Bilettm.ViewEvent.SeatsPage',compact('event','tickets')); } /** * Validate a ticket request. If successful reserve the tickets and redirect to checkout @@ -90,12 +88,183 @@ class EventCheckoutController extends Controller * @param $event_id * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse */ - public function postValidateTickets(Request $request, $event_id) - { - if (!$request->has('tickets')) { + public function postValidateSeats(Request $request, $event_id){ + if (!$request->has('seats')) { return response()->json([ 'status' => 'error', - 'message' => 'No tickets selected', + 'message' => 'No seats selected', + ]); + } + + /* + * Order expires after X min + */ + $order_expires_time = Carbon::now()->addMinutes(config('attendize.checkout_timeout_after')); + + $event = Event::findOrFail($event_id); + $seats = $request->get('seats'); + + /* + * Remove any tickets the user has reserved + */ + ReservedTickets::where('session_id', '=', session()->getId())->delete(); + + /* + * Go though the selected tickets and check if they're available + * , tot up the price and reserve them to prevent over selling. + */ + $quantity_available_validation_rules = []; + $order_total = 0; + $booking_fee = 0; + $organiser_booking_fee = 0; + $total_ticket_quantity = 0; + $reserved = []; + $tickets = []; + $validation_rules = []; + $validation_messages = []; + foreach ($seats as $ticket_id=>$ticket_seats){ + $seats_count = count($ticket_seats); + if($seats_count<1) + continue; + + $seat_nos = array_values($ticket_seats); + $reserved_tickets = ReservedTickets::where('ticket_id',$ticket_id) + ->where('expires','>',Carbon::now()) + ->whereIn('seat_no',$seat_nos) + ->pluck('seat_no'); + + $booked_tickets = Attendee::where('ticket_id',$ticket_id) + ->where('event_id',$event_id) + ->whereIn('seat_no',$seat_nos) + ->pluck('seat_no'); + + if(count($reserved_tickets)>0 || count($booked_tickets)>0) + return response()->json([ + 'status' => 'error', + 'messages' => 'Some of selected seats are already reserved',//todo show which are reserved + ]); + + $ticket = Ticket::findOrFail($ticket_id); + $max_per_person = min($ticket->quantity_remaining, $ticket->max_per_person); + /* + * Validation max min ticket count + */ + if($seats_count < $ticket->min_per_person){ + $message = 'You must select at least ' . $ticket->min_per_person . ' tickets.'; + }elseif ($seats_count > $max_per_person){ + $message = 'The maximum number of tickets you can register is ' . $ticket->quantity_remaining; + } + + if (isset($message)) { + return response()->json([ + 'status' => 'error', + 'messages' => $message, + ]); + } + + $total_ticket_quantity += $seats_count; + $order_total += ($seats_count * $ticket->price); + $booking_fee += ($seats_count * $ticket->booking_fee); + $organiser_booking_fee += ($seats_count * $ticket->organiser_booking_fee); + $tickets[] = [ + 'ticket' => $ticket, + 'qty' => $seats_count, + 'seats' => $ticket_seats, + 'price' => ($seats_count * $ticket->price), + 'booking_fee' => ($seats_count * $ticket->booking_fee), + 'organiser_booking_fee' => ($seats_count * $ticket->organiser_booking_fee), + 'full_price' => $ticket->price + $ticket->total_booking_fee, + ]; + + + foreach ($ticket_seats as $seat_no){ + $reservedTickets = new ReservedTickets(); + $reservedTickets->ticket_id = $ticket_id; + $reservedTickets->event_id = $event_id; + $reservedTickets->quantity_reserved = 1; + $reservedTickets->expires = $order_expires_time; + $reservedTickets->session_id = session()->getId(); + $reservedTickets->seat_no = $seat_no; + $reserved[] = $reservedTickets->attributesToArray(); + /* + * Create our validation rules here + */ + $validation_rules['ticket_holder_first_name.' . $seat_no . '.' . $ticket_id] = ['required']; + $validation_rules['ticket_holder_last_name.' . $seat_no . '.' . $ticket_id] = ['required']; + $validation_rules['ticket_holder_email.' . $seat_no . '.' . $ticket_id] = ['required', 'email']; + + $validation_messages['ticket_holder_first_name.' . $seat_no . '.' . $ticket_id . '.required'] = 'Ticket holder ' . $seat_no . '\'s first name is required'; + $validation_messages['ticket_holder_last_name.' . $seat_no . '.' . $ticket_id . '.required'] = 'Ticket holder ' . $seat_no . '\'s last name is required'; + $validation_messages['ticket_holder_email.' . $seat_no . '.' . $ticket_id . '.required'] = 'Ticket holder ' . $seat_no . '\'s email is required'; + $validation_messages['ticket_holder_email.' . $seat_no . '.' . $ticket_id . '.email'] = 'Ticket holder ' . $seat_no . '\'s email appears to be invalid'; + /* + * Validation rules for custom questions + */ + foreach ($ticket->questions as $question) { + if ($question->is_required && $question->is_enabled) { + $validation_rules['ticket_holder_questions.' . $ticket_id . '.' . $seat_no . '.' . $question->id] = ['required']; + $validation_messages['ticket_holder_questions.' . $ticket_id . '.' . $seat_no . '.' . $question->id . '.required'] = "This question is required"; + } + } + } + } + ReservedTickets::insert($reserved); + + if (empty($tickets)) { + return response()->json([ + 'status' => 'error', + 'message' => 'No tickets selected.', + ]); + } + /* + * The 'ticket_order_{event_id}' session stores everything we need to complete the transaction. + */ + session()->put('ticket_order_' . $event->id, [ + 'validation_rules' => $validation_rules, + 'validation_messages' => $validation_messages, + 'event_id' => $event->id, + 'tickets' => $tickets, + 'total_ticket_quantity' => $total_ticket_quantity, + 'order_started' => time(), + 'expires' => $order_expires_time, +// 'reserved_tickets_id' => $reservedTickets->id, + 'order_total' => $order_total, + 'booking_fee' => $booking_fee, + 'organiser_booking_fee' => $organiser_booking_fee, + 'total_booking_fee' => $booking_fee + $organiser_booking_fee, + 'order_requires_payment' => (ceil($order_total) == 0) ? false : true, + 'account_id' => $event->account->id, + 'affiliate_referral' => Cookie::get('affiliate_' . $event_id), +// 'account_payment_gateway' => $activeAccountPaymentGateway, +// 'payment_gateway' => $paymentGateway + ]); + + /* + * If we're this far assume everything is OK and redirect them + * to the the checkout page. + */ + if ($request->ajax()) { + return response()->json([ + 'status' => 'success', + 'redirectUrl' => route('showEventCheckout', [ + 'event_id' => $event_id, + 'is_embedded' => $this->is_embedded, + ]) . '#order_form', + ]); + } + + /* + * todo Maybe display something prettier than this? + */ + exit('Please enable Javascript in your browser.'); + } + + public function postValidateTickets(Request $request, $event_id) + { + if (!$request->has('seats')) { + return response()->json([ + 'status' => 'error', + 'message' => 'No seats selected', ]); } /* @@ -291,7 +460,7 @@ class EventCheckoutController extends Controller $secondsToExpire = Carbon::now()->diffInSeconds($order_session['expires']); - $event = Event::findorFail($order_session['event_id']); + $event = Event::with('venue')->findorFail($order_session['event_id']); $orderService = new OrderService($order_session['order_total'], $order_session['total_booking_fee'], $event); $orderService->calculateFinalCosts(); @@ -520,19 +689,6 @@ class EventCheckoutController extends Controller $order->event_id = $ticket_order['event_id']; $order->is_payment_received = isset($request_data['pay_offline']) ? 0 : 1; - // Business details is selected, we need to save the business details -// if (isset($request_data['is_business']) && (bool)$request_data['is_business']) { -// $order->is_business = $request_data['is_business']; -// $order->business_name = sanitise($request_data['business_name']); -// $order->business_tax_number = sanitise($request_data['business_tax_number']); -// $order->business_address_line_one = sanitise($request_data['business_address_line1']); -// $order->business_address_line_two = sanitise($request_data['business_address_line2']); -// $order->business_address_state_province = sanitise($request_data['business_address_state']); -// $order->business_address_city = sanitise($request_data['business_address_city']); -// $order->business_address_code = sanitise($request_data['business_address_code']); -// -// } - // Calculating grand total including tax $orderService = new OrderService($ticket_order['order_total'], $ticket_order['total_booking_fee'], $event); $orderService->calculateFinalCosts(); @@ -603,7 +759,7 @@ class EventCheckoutController extends Controller /* * Create the attendees */ - for ($i = 0; $i < $attendee_details['qty']; $i++) { + foreach ($attendee_details['seats'] as $i) { $attendee = new Attendee(); $attendee->first_name = strip_tags($request_data["ticket_holder_first_name"][$i][$attendee_details['ticket']['id']]); @@ -614,6 +770,7 @@ class EventCheckoutController extends Controller $attendee->ticket_id = $attendee_details['ticket']['id']; $attendee->account_id = $event->account->id; $attendee->reference_index = $attendee_increment; + $attendee->seat_no = $i; $attendee->save(); diff --git a/app/Http/Controllers/EventViewController.php b/app/Http/Controllers/EventViewController.php index 6f525c32..a16c6df8 100644 --- a/app/Http/Controllers/EventViewController.php +++ b/app/Http/Controllers/EventViewController.php @@ -33,12 +33,14 @@ class EventViewController extends Controller return view('Public.ViewEvent.EventNotLivePage'); } + $now =Carbon::now(config('app.timezone')); $tickets = $event->tickets()->select('id','ticket_date') ->where('is_hidden', false) - ->whereDate('ticket_date','>=',Carbon::now(config('app.timezone'))) + ->where('ticket_date','>=',$now) ->orderBy('ticket_date', 'asc') ->groupBy('ticket_date') - ->distinct()->get(); + ->distinct() + ->get(); $ticket_dates = array(); @@ -46,14 +48,14 @@ class EventViewController extends Controller $date = $ticket->ticket_date->format('d M'); $ticket_dates[$date][] = $ticket; } -// dd($ticket_dates); + $data = [ 'event' => $event, 'ticket_dates' =>$ticket_dates, // 'tickets' => $tickets,//$event->tickets()->orderBy('sort_order', 'asc')->get(), 'is_embedded' => 0, ]; -// dd($ticket_dates); + /* * Don't record stats if we're previewing the event page from the backend or if we own the event. */ diff --git a/app/Http/routes.php b/app/Http/routes.php index f8f82329..05331ced 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -168,7 +168,7 @@ Route::group( Route::post('{event_id}/checkout/', [ 'as' => 'postValidateTickets', - 'uses' => 'EventCheckoutController@postValidateTickets', + 'uses' => 'EventCheckoutController@postValidateSeats', ]); Route::post('{event_id}/checkout/validate', [ diff --git a/app/Models/Event.php b/app/Models/Event.php index 318665fc..47335736 100644 --- a/app/Models/Event.php +++ b/app/Models/Event.php @@ -112,7 +112,7 @@ class Event extends MyBaseModel public function starting_ticket(){ return $this->tickets() ->select('id','ticket_date','event_id','price') - ->whereDate('ticket_date','>=',Carbon::now(\config('app.timezone'))) + ->where('ticket_date','>=',Carbon::now(\config('app.timezone'))) ->orderBy('ticket_date') ->orderBy('price') ->limit(2); // limit 1 returns null ??? @@ -483,10 +483,10 @@ ICSTemplate; public function scopeOnLive($query, $start_date = null, $end_date = null){ //if date is null carbon creates now date instance if(isset($start_date) && isset($end_date)) - $query->whereDate('start_date','<',$end_date) - ->whereDate('end_date','>',$start_date); + $query->where('start_date','<',$end_date) + ->where('end_date','>',$start_date); else - $query->whereDate('end_date','>',Carbon::now(config('app.timezone'))); + $query->where('end_date','>',Carbon::now(config('app.timezone'))); return $query->where('is_live',1) ->withCount(['images as image_url' => function($q){ diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index d01a048d..a5eb91f5 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -78,11 +78,16 @@ class Ticket extends MyBaseModel return $this->belongsToMany(\App\Models\Question::class); } - /** - * TODO:implement the reserved method. - */ public function reserved() { + return $this->hasMany(ReservedTickets::class) + ->where('expires','>',Carbon::now()) + ->orderBy('seat_no','asc'); + } + + public function booked(){ + return $this->hasMany(Attendee::class) + ->orderBy('seat_no','asc'); } /** diff --git a/database/migrations/2019_10_30_170931_add_ticket_section_id_to_tickets_table.php b/database/migrations/2019_10_30_170931_add_ticket_section_id_to_tickets_table.php index f437ba74..76313933 100644 --- a/database/migrations/2019_10_30_170931_add_ticket_section_id_to_tickets_table.php +++ b/database/migrations/2019_10_30_170931_add_ticket_section_id_to_tickets_table.php @@ -27,7 +27,8 @@ class AddTicketSectionIdToTicketsTable extends Migration public function down() { Schema::table('tickets', function (Blueprint $table) { - // + $table->dropForeign('section_id'); + $table->dropColumn('section_id'); }); } } diff --git a/database/migrations/2019_10_31_173838_add_seat_to_attendees_table.php b/database/migrations/2019_10_31_173838_add_seat_to_attendees_table.php index 66fe5275..8c13958c 100644 --- a/database/migrations/2019_10_31_173838_add_seat_to_attendees_table.php +++ b/database/migrations/2019_10_31_173838_add_seat_to_attendees_table.php @@ -14,8 +14,11 @@ class AddSeatToAttendeesTable extends Migration public function up() { Schema::table('attendees', function (Blueprint $table) { - $table->string('row')->nullable(); - $table->string('no')->nullable(); + $table->string('seat_no')->nullable(); + }); + + Schema::table('reserved_tickets',function (Blueprint $table){ + $table->string('seat_no')->nullable(); }); } @@ -27,8 +30,10 @@ class AddSeatToAttendeesTable extends Migration public function down() { Schema::table('attendees', function (Blueprint $table) { - $table->dropColumn('row'); - $table->dropColumn('no'); + $table->dropColumn('seat_no'); + }); + Schema::table('reserved_tickets',function (Blueprint $table){ + $table->dropColumn('seat_no'); }); } } diff --git a/public/assets/javascript/frontend.js b/public/assets/javascript/frontend.js index 04466f3d..2ac1c39d 100644 --- a/public/assets/javascript/frontend.js +++ b/public/assets/javascript/frontend.js @@ -4800,7 +4800,7 @@ function processFormErrors($form, errors) var $input = $(selector, $form); if ($input.prop('type') === 'file') { - $('#input-' + $input.prop('name')).append('
| {{$row['row']}} | -- | - @for($i = $row['start_no'];$i<=$row['end_no'];$i++) - | - - - | - @endfor -- |