Completed
Branch BUG/11288/fix-datepicker (d15367)
by
unknown
108:07 queued 94:31
created

EE_Ticket::is_remaining()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 1
dl 0
loc 10
rs 9.2
c 0
b 0
f 0
1
<?php use EventEspresso\core\domain\values\currency\UsesMoneyInterface;
2
use EventEspresso\core\exceptions\InvalidDataTypeException;
3
use EventEspresso\core\exceptions\InvalidEntityException;
4
use EventEspresso\core\exceptions\InvalidIdentifierException;
5
use EventEspresso\core\exceptions\InvalidInterfaceException;
6
use EventEspresso\core\exceptions\UnexpectedEntityException;
7
use EventEspresso\core\services\currency\MoneyFactory;
8
use EventEspresso\core\services\loaders\LoaderFactory;
9
10
if ( !defined( 'EVENT_ESPRESSO_VERSION' ) ) {
11
	exit( 'No direct script access allowed' );
12
}
13
/**
14
 * Event Espresso
15
 *
16
 * Event Registration and Management Plugin for WordPress
17
 *
18
 * @ package 		Event Espresso
19
 * @ author 		Event Espresso
20
 * @ copyright 	(c) 2008-2011 Event Espresso  All Rights Reserved.
21
 * @ license 		{@link http://eventespresso.com/support/terms-conditions/}   * see Plugin Licensing *
22
 * @ link 				{@link http://www.eventespresso.com}
23
 * @ since 			4.0
24
 *
25
 */
26
27
28
29
/**
30
 * EE_Ticket class
31
 *
32
 * @package 			Event Espresso
33
 * @subpackage 	includes/classes/EE_Ticket.class.php
34
 * @author             Darren Ethier
35
 */
36
class EE_Ticket
37
    extends EE_Soft_Delete_Base_Class
38
    implements EEI_Line_Item_Object, EEI_Event_Relation, EEI_Has_Icon, UsesMoneyInterface
39
{
40
41
	/**
42
	 * The following constants are used by the ticket_status() method to indicate whether a ticket is on sale or not.
43
	 */
44
	const sold_out = 'TKS';
45
46
	/**
47
	 *
48
	 */
49
	const expired = 'TKE';
50
51
	/**
52
	 *
53
	 */
54
	const archived = 'TKA';
55
56
	/**
57
	 *
58
	 */
59
	const pending = 'TKP';
60
61
	/**
62
	 *
63
	 */
64
	const onsale = 'TKO';
65
66
	/**
67
	 * cached result from method of the same name
68
	 * @var float $_ticket_total_with_taxes
69
	 */
70
	private $_ticket_total_with_taxes;
71
72
73
    /**
74
     * @param array        $props_n_values      incoming values
75
     * @param string       $timezone            incoming timezone (if not set the timezone set for the website will be
76
     *                                          used.)
77
     * @param array        $date_formats        incoming date_formats in an array where the first value is the
78
     *                                          date_format and the second value is the time format
79
     * @param MoneyFactory $money_factory
80
     * @return EE_Ticket
81
     * @throws InvalidArgumentException
82
     * @throws InvalidInterfaceException
83
     * @throws InvalidDataTypeException
84
     * @throws EE_Error
85
     */
86 View Code Duplication
    public static function new_instance(
87
        $props_n_values = array(),
88
        $timezone = null,
89
        $date_formats = array(),
90
        MoneyFactory $money_factory = null
91
    ) {
92
        $has_object = parent::_check_for_object(
93
            $props_n_values,
94
            __CLASS__,
95
            $timezone,
96
            $date_formats
97
        );
98
        return $has_object
99
            ? $has_object
100
            : new self(
101
                $props_n_values,
102
                false,
103
                $timezone,
104
                $date_formats,
105
                $money_factory
106
            );
107
    }
108
109
110
    /**
111
     * @param array        $props_n_values      incoming values from the database
112
     * @param string       $timezone            incoming timezone as set by the model.  If not set the timezone for
113
     *                                          the website will be used.
114
     * @param MoneyFactory $money_factory
115
     * @return EE_Ticket
116
     * @throws InvalidArgumentException
117
     * @throws InvalidInterfaceException
118
     * @throws InvalidDataTypeException
119
     * @throws EE_Error
120
     */
121
    public static function new_instance_from_db(
122
        $props_n_values = array(),
123
        $timezone = null,
124
        MoneyFactory $money_factory = null
125
    ) {
126
        return new self(
127
            $props_n_values,
128
            true,
129
            $timezone,
130
            array(),
131
            $money_factory
132
        );
133
    }
134
135
136
    /**
137
     * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
138
     * play nice
139
     *
140
     * @param array        $fieldValues  where each key is a field (ie, array key in the 2nd layer of the model's
141
     *                                   _fields array, (eg, EVT_ID, TXN_amount, QST_name, etc) and values are their
142
     *                                   values
143
     * @param boolean      $bydb         a flag for setting if the class is instantiated by the corresponding db model
144
     *                                   or not.
145
     * @param string       $timezone     indicate what timezone you want any datetime fields to be in when
146
     *                                   instantiating
147
     *                                   a EE_Base_Class object.
148
     * @param array        $date_formats An array of date formats to set on construct where first value is the
149
     *                                   date_format and second value is the time format.
150
     * @param MoneyFactory $money_factory
151
     * @throws InvalidArgumentException
152
     * @throws InvalidInterfaceException
153
     * @throws InvalidDataTypeException
154
     * @throws EE_Error
155
     */
156 View Code Duplication
    protected function __construct(
157
        array $fieldValues = array(),
158
        $bydb = false,
159
        $timezone = '',
160
        array $date_formats = array(),
161
        MoneyFactory $money_factory = null
162
    ) {
163
        if (! $money_factory instanceof MoneyFactory) {
164
            $money_factory = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\currency\MoneyFactory');
165
        }
166
        $this->money_factory = $money_factory;
167
        parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
168
    }
169
170
171
    /**
172
     * @return bool
173
     * @throws \EE_Error
174
     */
175
	public function parent() {
176
		return $this->get( 'TKT_parent' );
177
	}
178
179
180
181
    /**
182
     * return if a ticket has quantities available for purchase
183
     *
184
     * @param  int $DTT_ID the primary key for a particular datetime
185
     * @return boolean
186
     * @throws \EE_Error
187
     */
188
	public function available( $DTT_ID = 0 ) {
189
		// are we checking availability for a particular datetime ?
190
		if ( $DTT_ID ) {
191
			// get that datetime object
192
			$datetime = $this->get_first_related( 'Datetime', array( array( 'DTT_ID' => $DTT_ID ) ) );
193
			// if  ticket sales for this datetime have exceeded the reg limit...
194
			if ( $datetime instanceof EE_Datetime && $datetime->sold_out() ) {
195
				return FALSE;
196
			}
197
		}
198
		// datetime is still open for registration, but is this ticket sold out ?
199
		return $this->qty() < 1 || $this->qty() > $this->sold() ? TRUE : FALSE;
200
	}
201
202
203
204
    /**
205
     * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
206
     *
207
     * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the relevant status const
208
     * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save further processing
209
     * @return mixed status int if the display string isn't requested
210
     * @throws \EE_Error
211
     */
212
	public function ticket_status( $display = FALSE, $remaining = null ) {
213
		$remaining = is_bool( $remaining ) ? $remaining : $this->is_remaining();
214
		if ( ! $remaining ) {
215
			return $display ? EEH_Template::pretty_status( EE_Ticket::sold_out, FALSE, 'sentence' ) : EE_Ticket::sold_out;
216
		}
217
		if ( $this->get( 'TKT_deleted' ) ) {
218
			return $display ? EEH_Template::pretty_status( EE_Ticket::archived, FALSE, 'sentence' ) : EE_Ticket::archived;
219
		}
220
		if ( $this->is_expired() ) {
221
			return $display ? EEH_Template::pretty_status( EE_Ticket::expired, FALSE, 'sentence' ) : EE_Ticket::expired;
222
		}
223
		if ( $this->is_pending() ) {
224
			return $display ? EEH_Template::pretty_status( EE_Ticket::pending, FALSE, 'sentence' ) : EE_Ticket::pending;
225
		}
226
		if ( $this->is_on_sale() ) {
227
			return $display ? EEH_Template::pretty_status( EE_Ticket::onsale, FALSE, 'sentence' ) : EE_Ticket::onsale;
228
		}
229
		return '';
230
	}
231
232
233
234
    /**
235
     * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale considering ALL the factors used for figuring that out.
236
     *
237
     * @access public
238
     * @param  int $DTT_ID if an int above 0 is included here then we get a specific dtt.
239
     * @return boolean         true = tickets remaining, false not.
240
     * @throws \EE_Error
241
     */
242
	public function is_remaining( $DTT_ID = 0 ) {
243
		$num_remaining = $this->remaining( $DTT_ID );
244
		if ( $num_remaining === 0 ) {
245
			return FALSE;
246
		}
247
		if ( $num_remaining > 0 && $num_remaining < $this->min() ) {
248
			return FALSE;
249
		}
250
		return TRUE;
251
	}
252
253
254
255
    /**
256
     * return the total number of tickets available for purchase
257
     *
258
     * @param  int $DTT_ID the primary key for a particular datetime.
259
     *                     set to 0 for all related datetimes
260
     * @return int
261
     * @throws \EE_Error
262
     */
263
	public function remaining( $DTT_ID = 0 ) {
264
		return $this->real_quantity_on_ticket('saleable', $DTT_ID );
265
	}
266
267
268
269
    /**
270
     * Gets min
271
     *
272
     * @return int
273
     * @throws \EE_Error
274
     */
275
	public function min() {
276
		return $this->get( 'TKT_min' );
277
	}
278
279
280
281
    /**
282
     * return if a ticket is no longer available cause its available dates have expired.
283
     *
284
     * @return boolean
285
     * @throws \EE_Error
286
     */
287
	public function is_expired() {
288
		return ( $this->get_raw( 'TKT_end_date' ) < time() );
289
	}
290
291
292
293
    /**
294
     * Return if a ticket is yet to go on sale or not
295
     *
296
     * @return boolean
297
     * @throws \EE_Error
298
     */
299
	public function is_pending() {
300
		return ( $this->get_raw( 'TKT_start_date' ) > time() );
301
	}
302
303
304
305
    /**
306
     * Return if a ticket is on sale or not
307
     *
308
     * @return boolean
309
     * @throws \EE_Error
310
     */
311
	public function is_on_sale() {
312
		return ( $this->get_raw( 'TKT_start_date' ) < time() && $this->get_raw( 'TKT_end_date' ) > time() );
313
	}
314
315
316
317
    /**
318
     * This returns the chronologically last datetime that this ticket is associated with
319
     *
320
     * @param string $dt_frmt
321
     * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with the end date ie: Jan 01 "to" Dec 31
322
     * @return string
323
     * @throws \EE_Error
324
     */
325
	public function date_range( $dt_frmt = '', $conjunction = ' - ' ) {
326
		$first_date = $this->first_datetime() instanceof EE_Datetime ? $this->first_datetime()->start_date( $dt_frmt ) : '';
327
		$last_date = $this->last_datetime() instanceof EE_Datetime ? $this->last_datetime()->end_date( $dt_frmt ) : '';
328
329
		return $first_date && $last_date ? $first_date . $conjunction  . $last_date : '';
330
	}
331
332
333
334
    /**
335
     * This returns the chronologically first datetime that this ticket is associated with
336
     *
337
     * @return EE_Datetime
338
     * @throws \EE_Error
339
     */
340
	public function first_datetime() {
341
		$datetimes = $this->datetimes( array( 'limit' => 1 ) );
342
		return reset( $datetimes );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression reset($datetimes); of type EE_Base_Class|false adds false to the return on line 342 which is incompatible with the return type documented by EE_Ticket::first_datetime of type EE_Datetime. It seems like you forgot to handle an error condition.
Loading history...
343
	}
344
345
346
347
    /**
348
     * Gets all the datetimes this ticket can be used for attending.
349
     * Unless otherwise specified, orders datetimes by start date.
350
     *
351
     * @param array $query_params see EEM_Base::get_all()
352
     * @return EE_Datetime[]|EE_Base_Class[]
353
     * @throws \EE_Error
354
     */
355
	public function datetimes( $query_params = array() ) {
356
		if ( ! isset( $query_params[ 'order_by' ] ) ) {
357
			$query_params[ 'order_by' ][ 'DTT_order' ] = 'ASC';
358
		}
359
		return $this->get_many_related( 'Datetime', $query_params );
360
	}
361
362
363
364
    /**
365
     * This returns the chronologically last datetime that this ticket is associated with
366
     *
367
     * @return EE_Datetime
368
     * @throws \EE_Error
369
     */
370
	public function last_datetime() {
371
		$datetimes = $this->datetimes( array( 'limit' => 1, 'order_by' => array( 'DTT_EVT_start' => 'DESC' ) ) );
372
		return end( $datetimes );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression end($datetimes); of type EE_Base_Class|false adds false to the return on line 372 which is incompatible with the return type documented by EE_Ticket::last_datetime of type EE_Datetime. It seems like you forgot to handle an error condition.
Loading history...
373
	}
374
375
376
377
    /**
378
     * This returns the total tickets sold depending on the given parameters.
379
     *
380
     * @param  string $what   Can be one of two options: 'ticket', 'datetime'.
381
     *                        'ticket' = total ticket sales for all datetimes this ticket is related to
382
     *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
383
     *                        'datetime' = total ticket sales in the datetime_ticket table.
384
     *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
385
     *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
386
     * @param  int    $dtt_id [optional] include the dtt_id with $what = 'datetime'.
387
     * @return mixed (array|int)          how many tickets have sold
388
     * @throws \EE_Error
389
     */
390
	public function tickets_sold( $what = 'ticket', $dtt_id = NULL ) {
391
		$total = 0;
392
		$tickets_sold = $this->_all_tickets_sold();
393
		switch ( $what ) {
394
			case 'ticket' :
395
				return $tickets_sold[ 'ticket' ];
396
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
397
			case 'datetime' :
398
				if ( empty( $tickets_sold[ 'datetime' ] ) ) {
399
					return $total;
400
				}
401
				if ( ! empty( $dtt_id ) && ! isset( $tickets_sold[ 'datetime' ][ $dtt_id ] ) ) {
402
					EE_Error::add_error( __( 'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included.  Are you SURE that is a datetime related to this ticket?', 'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
403
					return $total;
404
				}
405
				return empty( $dtt_id ) ? $tickets_sold[ 'datetime' ] : $tickets_sold[ 'datetime' ][ $dtt_id ];
406
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
407
			default:
408
				return $total;
409
		}
410
	}
411
412
413
414
    /**
415
     * This returns an array indexed by datetime_id for tickets sold with this ticket.
416
     *
417
     * @return EE_Ticket[]
418
     * @throws \EE_Error
419
     */
420
	protected function _all_tickets_sold() {
421
		$datetimes = $this->get_many_related( 'Datetime' );
422
		$tickets_sold = array();
423
		if ( ! empty( $datetimes ) ) {
424
			foreach ( $datetimes as $datetime ) {
425
				$tickets_sold[ 'datetime' ][ $datetime->ID() ] = $datetime->get( 'DTT_sold' );
426
			}
427
		}
428
		//Tickets sold
429
		$tickets_sold[ 'ticket' ] = $this->sold();
430
		return $tickets_sold;
431
	}
432
433
434
435
    /**
436
     * This returns the base price object for the ticket.
437
     *
438
     * @param  bool $return_array whether to return as an array indexed by price id or just the object.
439
     * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
440
     * @throws \EE_Error
441
     */
442
	public function base_price( $return_array = FALSE ) {
443
		$_where = array( 'Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price );
444
		return $return_array
445
            ? $this->get_many_related( 'Price', array( $_where ) )
446
            : $this->get_first_related( 'Price', array( $_where ) );
447
	}
448
449
450
451
    /**
452
     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
453
     *
454
     * @access public
455
     * @return EE_Price[]
456
     * @throws \EE_Error
457
     */
458
	public function price_modifiers() {
459
		$query_params = array( 0 => array( 'Price_Type.PBT_ID' => array( 'NOT IN', array( EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax ) ) ) );
460
		return $this->prices( $query_params );
461
	}
462
463
464
465
    /**
466
     * Gets all the prices that combine to form the final price of this ticket
467
     *
468
     * @param array $query_params like EEM_Base::get_all
469
     * @return EE_Price[]|EE_Base_Class[]
470
     * @throws \EE_Error
471
     */
472
	public function prices( $query_params = array() ) {
473
		return $this->get_many_related( 'Price', $query_params );
474
	}
475
476
477
478
    /**
479
     * Gets all the ticket applicabilities (ie, relations between datetimes and tickets)
480
     *
481
     * @param array $query_params see EEM_Base::get_all()
482
     * @return EE_Datetime_Ticket|EE_Base_Class[]
483
     * @throws \EE_Error
484
     */
485
	public function datetime_tickets( $query_params = array() ) {
486
		return $this->get_many_related( 'Datetime_Ticket', $query_params );
487
	}
488
489
490
491
    /**
492
     * Gets all the datetimes from the db ordered by DTT_order
493
     *
494
     * @param boolean $show_expired
495
     * @param boolean $show_deleted
496
     * @return EE_Datetime[]
497
     * @throws \EE_Error
498
     */
499
	public function datetimes_ordered( $show_expired = TRUE, $show_deleted = FALSE ) {
500
		return EEM_Datetime::instance( $this->_timezone )->get_datetimes_for_ticket_ordered_by_DTT_order( $this->ID(), $show_expired, $show_deleted );
501
	}
502
503
504
505
    /**
506
     * Gets ID
507
     *
508
     * @return string
509
     * @throws \EE_Error
510
     */
511
	public function ID() {
512
		return $this->get( 'TKT_ID' );
513
	}
514
515
516
517
    /**
518
     * get the author of the ticket.
519
     *
520
     * @since 4.5.0
521
     * @return int
522
     * @throws \EE_Error
523
     */
524
	public function wp_user() {
525
		return $this->get('TKT_wp_user');
526
	}
527
528
529
530
    /**
531
     * Gets the template for the ticket
532
     *
533
     * @return EE_Ticket_Template|EE_Base_Class
534
     * @throws \EE_Error
535
     */
536
	public function template() {
537
		return $this->get_first_related( 'Ticket_Template' );
538
	}
539
540
541
542
    /**
543
     * Simply returns an array of EE_Price objects that are taxes.
544
     *
545
     * @return EE_Price[]
546
     * @throws \EE_Error
547
     */
548
	public function get_ticket_taxes_for_admin() {
549
		return EE_Taxes::get_taxes_for_admin();
550
	}
551
552
553
554
    /**
555
     * @return float
556
     * @throws \EE_Error
557
     */
558
	public function ticket_price() {
559
		return $this->get( 'TKT_price' );
560
	}
561
562
563
564
    /**
565
     * @return mixed
566
     * @throws \EE_Error
567
     */
568
	public function pretty_price() {
569
		return $this->get_pretty( 'TKT_price' );
570
	}
571
572
573
574
    /**
575
     * @return bool
576
     * @throws \EE_Error
577
     */
578
	public function is_free() {
579
		return $this->get_ticket_total_with_taxes() === (float) 0;
580
	}
581
582
583
584
    /**
585
     * get_ticket_total_with_taxes
586
     *
587
     * @param bool $no_cache
588
     * @return float
589
     * @throws \EE_Error
590
     */
591
	public function get_ticket_total_with_taxes( $no_cache = FALSE ) {
592
		if ($this->_ticket_total_with_taxes === null || $no_cache ) {
593
			$this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
594
		}
595
		return (float) $this->_ticket_total_with_taxes;
596
	}
597
598
599
600
	public function ensure_TKT_Price_correct() {
601
		$this->set( 'TKT_price', EE_Taxes::get_subtotal_for_admin( $this ) );
602
		$this->save();
603
	}
604
605
606
607
    /**
608
     * @return float
609
     * @throws \EE_Error
610
     */
611
	public function get_ticket_subtotal() {
612
		return EE_Taxes::get_subtotal_for_admin( $this );
613
	}
614
615
616
617
    /**
618
     * Returns the total taxes applied to this ticket
619
     *
620
     * @return float
621
     * @throws \EE_Error
622
     */
623
	public function get_ticket_taxes_total_for_admin() {
624
		return EE_Taxes::get_total_taxes_for_admin( $this );
625
	}
626
627
628
629
    /**
630
     * Sets name
631
     *
632
     * @param string $name
633
     * @throws \EE_Error
634
     */
635
	public function set_name( $name ) {
636
		$this->set( 'TKT_name', $name );
637
	}
638
639
640
641
    /**
642
     * Gets description
643
     *
644
     * @return string
645
     * @throws \EE_Error
646
     */
647
	public function description() {
648
		return $this->get( 'TKT_description' );
649
	}
650
651
652
653
    /**
654
     * Sets description
655
     *
656
     * @param string $description
657
     * @throws \EE_Error
658
     */
659
	public function set_description( $description ) {
660
		$this->set( 'TKT_description', $description );
661
	}
662
663
664
665
    /**
666
     * Gets start_date
667
     *
668
     * @param string $dt_frmt
669
     * @param string $tm_frmt
670
     * @return string
671
     * @throws \EE_Error
672
     */
673
	public function start_date( $dt_frmt = '', $tm_frmt = '' ) {
674
		return $this->_get_datetime( 'TKT_start_date', $dt_frmt, $tm_frmt );
675
	}
676
677
678
679
    /**
680
     * Sets start_date
681
     *
682
     * @param string $start_date
683
     * @return void
684
     * @throws \EE_Error
685
     */
686
	public function set_start_date( $start_date ) {
687
		$this->_set_date_time( 'B', $start_date, 'TKT_start_date' );
688
	}
689
690
691
692
    /**
693
     * Gets end_date
694
     *
695
     * @param string $dt_frmt
696
     * @param string $tm_frmt
697
     * @return string
698
     * @throws \EE_Error
699
     */
700
	public function end_date( $dt_frmt = '', $tm_frmt = '' ) {
701
		return $this->_get_datetime( 'TKT_end_date', $dt_frmt, $tm_frmt );
702
	}
703
704
705
706
    /**
707
     * Sets end_date
708
     *
709
     * @param string $end_date
710
     * @return void
711
     * @throws \EE_Error
712
     */
713
	public function set_end_date( $end_date ) {
714
		$this->_set_date_time( 'B', $end_date, 'TKT_end_date' );
715
	}
716
717
718
719
    /**
720
     * Sets sell until time
721
     *
722
     * @since 4.5.0
723
     * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
724
     * @throws \EE_Error
725
     */
726
	public function set_end_time( $time ) {
727
		$this->_set_time_for( $time, 'TKT_end_date' );
728
	}
729
730
731
732
    /**
733
     * Sets min
734
     *
735
     * @param int $min
736
     * @return void
737
     * @throws \EE_Error
738
     */
739
	public function set_min( $min ) {
740
		$this->set( 'TKT_min', $min );
741
	}
742
743
744
745
    /**
746
     * Gets max
747
     *
748
     * @return int
749
     * @throws \EE_Error
750
     */
751
	public function max() {
752
		return $this->get( 'TKT_max' );
753
	}
754
755
756
757
    /**
758
     * Sets max
759
     *
760
     * @param int $max
761
     * @return void
762
     * @throws \EE_Error
763
     */
764
	public function set_max( $max ) {
765
		$this->set( 'TKT_max', $max );
766
	}
767
768
769
770
    /**
771
     * Sets price
772
     *
773
     * @param float $price
774
     * @return void
775
     * @throws \EE_Error
776
     */
777
	public function set_price( $price ) {
778
		$this->set( 'TKT_price', $price );
779
	}
780
781
782
783
    /**
784
     * Gets sold
785
     *
786
     * @return int
787
     * @throws \EE_Error
788
     */
789
	public function sold() {
790
		return $this->get_raw( 'TKT_sold' );
791
	}
792
793
794
795
    /**
796
     * Sets sold
797
     *
798
     * @param int $sold
799
     * @return void
800
     * @throws \EE_Error
801
     */
802
	public function set_sold( $sold ) {
803
		// sold can not go below zero
804
		$sold = max( 0, $sold );
805
		$this->set( 'TKT_sold', $sold );
806
	}
807
808
809
810
    /**
811
     * increments sold by amount passed by $qty
812
     *
813
     * @param int $qty
814
     * @return void
815
     * @throws \EE_Error
816
     */
817
	public function increase_sold( $qty = 1 ) {
818
		$sold = $this->sold() + $qty;
819
		// remove ticket reservation, but don't adjust datetime reservations,  because that will happen
820
		// via \EE_Datetime::increase_sold() when \EE_Ticket::_increase_sold_for_datetimes() is called
821
		$this->decrease_reserved( $qty, false );
822
		$this->_increase_sold_for_datetimes( $qty );
823
		$this->set_sold( $sold );
824
		do_action(
825
		    'AHEE__EE_Ticket__increase_sold',
826
            $this,
827
            $qty,
828
            $sold
829
        );
830
	}
831
832
833
834
    /**
835
     * Increases sold on related datetimes
836
     *
837
     * @param int $qty
838
     * @return void
839
     * @throws \EE_Error
840
     */
841 View Code Duplication
	protected function _increase_sold_for_datetimes( $qty = 1 ) {
842
		$datetimes = $this->datetimes();
843
		if ( is_array( $datetimes ) ) {
844
			foreach ( $datetimes as $datetime ) {
845
				if ( $datetime instanceof EE_Datetime ) {
846
					$datetime->increase_sold( $qty );
847
					$datetime->save();
848
				}
849
			}
850
		}
851
	}
852
853
854
855
    /**
856
     * decrements (subtracts) sold by amount passed by $qty
857
     *
858
     * @param int $qty
859
     * @return void
860
     * @throws \EE_Error
861
     */
862
	public function decrease_sold( $qty = 1 ) {
863
		$sold = $this->sold() - $qty;
864
		$this->_decrease_sold_for_datetimes( $qty );
865
		$this->set_sold( $sold );
866
        do_action(
867
            'AHEE__EE_Ticket__decrease_sold',
868
            $this,
869
            $qty,
870
            $sold
871
        );
872
    }
873
874
875
876
    /**
877
     * Decreases sold on related datetimes
878
     *
879
     * @param int $qty
880
     * @return void
881
     * @throws \EE_Error
882
     */
883 View Code Duplication
	protected function _decrease_sold_for_datetimes( $qty = 1 ) {
884
		$datetimes = $this->datetimes();
885
		if ( is_array( $datetimes ) ) {
886
			foreach ( $datetimes as $datetime ) {
887
				if ( $datetime instanceof EE_Datetime ) {
888
					$datetime->decrease_sold( $qty );
889
					$datetime->save();
890
				}
891
			}
892
		}
893
	}
894
895
896
897
    /**
898
     * Gets qty of reserved tickets
899
     *
900
     * @return int
901
     * @throws \EE_Error
902
     */
903
	public function reserved() {
904
		return $this->get_raw( 'TKT_reserved' );
905
	}
906
907
908
909
    /**
910
     * Sets reserved
911
     *
912
     * @param int $reserved
913
     * @return void
914
     * @throws \EE_Error
915
     */
916
	public function set_reserved( $reserved ) {
917
		// reserved can not go below zero
918
		$reserved = max( 0, (int) $reserved );
919
		$this->set( 'TKT_reserved', $reserved );
920
	}
921
922
923
924
    /**
925
     * increments reserved by amount passed by $qty
926
     *
927
     * @param int $qty
928
     * @return void
929
     * @throws \EE_Error
930
     */
931 View Code Duplication
	public function increase_reserved( $qty = 1 ) {
932
		$qty = absint( $qty );
933
		$reserved = $this->reserved() + $qty;
934
		$this->_increase_reserved_for_datetimes( $qty );
935
		$this->set_reserved( $reserved );
936
        do_action(
937
            'AHEE__EE_Ticket__increase_reserved',
938
            $this,
939
            $qty,
940
            $reserved
941
        );
942
    }
943
944
945
946
    /**
947
     * Increases sold on related datetimes
948
     *
949
     * @param int $qty
950
     * @return void
951
     * @throws \EE_Error
952
     */
953 View Code Duplication
	protected function _increase_reserved_for_datetimes( $qty = 1 ) {
954
		$datetimes = $this->datetimes();
955
		if ( is_array( $datetimes ) ) {
956
			foreach ( $datetimes as $datetime ) {
957
				if ( $datetime instanceof EE_Datetime ) {
958
					$datetime->increase_reserved( $qty );
959
					$datetime->save();
960
				}
961
			}
962
		}
963
	}
964
965
966
967
    /**
968
     * decrements (subtracts) reserved by amount passed by $qty
969
     *
970
     * @param int  $qty
971
     * @param bool $adjust_datetimes
972
     * @return void
973
     * @throws \EE_Error
974
     */
975 View Code Duplication
	public function decrease_reserved( $qty = 1, $adjust_datetimes = true ) {
976
		$reserved = $this->reserved() - absint( $qty );
977
		if ( $adjust_datetimes ) {
978
			$this->_decrease_reserved_for_datetimes( $qty );
979
		}
980
		$this->set_reserved( $reserved );
981
        do_action(
982
            'AHEE__EE_Ticket__decrease_reserved',
983
            $this,
984
            $qty,
985
            $reserved
986
        );
987
    }
988
989
990
991
    /**
992
     * Increases sold on related datetimes
993
     *
994
     * @param int $qty
995
     * @return void
996
     * @throws \EE_Error
997
     */
998 View Code Duplication
	protected function _decrease_reserved_for_datetimes( $qty = 1 ) {
999
		$datetimes = $this->datetimes();
1000
		if ( is_array( $datetimes ) ) {
1001
			foreach ( $datetimes as $datetime ) {
1002
				if ( $datetime instanceof EE_Datetime ) {
1003
					$datetime->decrease_reserved( $qty );
1004
					$datetime->save();
1005
				}
1006
			}
1007
		}
1008
	}
1009
1010
1011
1012
    /**
1013
     * Gets ticket quantity
1014
     *
1015
     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1016
     *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1017
     *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1018
     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1019
     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1020
     *                            is therefore the truest measure of tickets that can be purchased at the moment
1021
     * @return int
1022
     * @throws \EE_Error
1023
     */
1024
	public function qty( $context = '' ) {
1025
		switch ( $context ) {
1026
			case 'reg_limit' :
1027
				return $this->real_quantity_on_ticket();
1028
			case 'saleable' :
1029
				return $this->real_quantity_on_ticket( 'saleable' );
1030
			default:
1031
				return $this->get_raw( 'TKT_qty' );
1032
		}
1033
	}
1034
1035
1036
1037
    /**
1038
     * Gets ticket quantity
1039
     *
1040
     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1041
     *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1042
     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1043
     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1044
     *                            is therefore the truest measure of tickets that can be purchased at the moment
1045
     * @param  int   $DTT_ID      the primary key for a particular datetime.
1046
     *                            set to 0 for all related datetimes
1047
     * @return int
1048
     * @throws \EE_Error
1049
     */
1050
	public function real_quantity_on_ticket( $context = 'reg_limit', $DTT_ID = 0 ) {
1051
		$raw = $this->get_raw( 'TKT_qty' );
1052
		// return immediately if it's zero
1053
		if ( $raw === 0 ) {
1054
			return $raw;
1055
		}
1056
		//echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1057
		// ensure qty doesn't exceed raw value for THIS ticket
1058
		$qty = min( EE_INF, $raw );
1059
		//echo "\n . qty: " . $qty . '<br />';
1060
		// calculate this ticket's total sales and reservations
1061
		$sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1062
		//echo "\n . sold: " . $this->sold() . '<br />';
1063
		//echo "\n . reserved: " . $this->reserved() . '<br />';
1064
		//echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1065
		// first we need to calculate the maximum number of tickets available for the datetime
1066
		// do we want data for one datetime or all of them ?
1067
		$query_params = $DTT_ID ? array( array( 'DTT_ID' => $DTT_ID ) ) : array();
1068
		$datetimes = $this->datetimes( $query_params );
1069
		if ( is_array( $datetimes ) && ! empty( $datetimes ) ) {
1070
			foreach ( $datetimes as $datetime ) {
1071
				if ( $datetime instanceof EE_Datetime ) {
1072
					$datetime->refresh_from_db();
1073
					//echo "\n . . datetime name: " . $datetime->name() . '<br />';
1074
					//echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1075
					// initialize with no restrictions for each datetime
1076
					// but adjust datetime qty based on datetime reg limit
1077
					$datetime_qty = min( EE_INF, $datetime->reg_limit() );
1078
					//echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1079
					//echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1080
					// if we want the actual saleable amount, then we need to consider OTHER ticket sales
1081
					// and reservations for this datetime, that do NOT include sales and reservations
1082
					// for this ticket (so we add $this->sold() and $this->reserved() back in)
1083
					if ( $context === 'saleable' ) {
1084
						$datetime_qty = max(
1085
							$datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1086
							0
1087
						);
1088
						//echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1089
						//echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1090
						//echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1091
						//echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1092
						$datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1093
						//echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1094
					}
1095
					$qty = min( $datetime_qty, $qty );
1096
					//echo "\n . . qty: " . $qty . '<br />';
1097
				}
1098
			}
1099
		}
1100
		// NOW that we know the  maximum number of tickets available for the datetime
1101
		// we can finally factor in the details for this specific ticket
1102
		if ( $qty > 0 && $context === 'saleable' ) {
1103
			// and subtract the sales for THIS ticket
1104
			$qty = max( $qty - $sold_and_reserved_for_this_ticket, 0 );
1105
			//echo "\n . qty: " . $qty . '<br />';
1106
		}
1107
		//echo "\nFINAL QTY: " . $qty . "<br /><br />";
1108
		return $qty;
1109
	}
1110
1111
1112
1113
	/**
1114
	 * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1115
	 *
1116
	 * @param int  $qty
1117
	 * @return void
1118
	 * @throws \EE_Error
1119
	 */
1120 View Code Duplication
	public function set_qty( $qty ) {
1121
		$datetimes = $this->datetimes();
1122
		foreach ( $datetimes as $datetime ) {
1123
			if ( $datetime instanceof EE_Datetime ) {
1124
				$qty = min( $qty, $datetime->reg_limit() );
1125
			}
1126
		}
1127
		$this->set( 'TKT_qty', $qty );
1128
	}
1129
1130
1131
1132
    /**
1133
     * Gets uses
1134
     *
1135
     * @return int
1136
     * @throws \EE_Error
1137
     */
1138
	public function uses() {
1139
		return $this->get( 'TKT_uses' );
1140
	}
1141
1142
1143
1144
    /**
1145
     * Sets uses
1146
     *
1147
     * @param int $uses
1148
     * @return void
1149
     * @throws \EE_Error
1150
     */
1151
	public function set_uses( $uses ) {
1152
		$this->set( 'TKT_uses', $uses );
1153
	}
1154
1155
1156
1157
    /**
1158
     * returns whether ticket is required or not.
1159
     *
1160
     * @return boolean
1161
     * @throws \EE_Error
1162
     */
1163
	public function required() {
1164
		return $this->get( 'TKT_required' );
1165
	}
1166
1167
1168
1169
    /**
1170
     * sets the TKT_required property
1171
     *
1172
     * @param boolean $required
1173
     * @return void
1174
     * @throws \EE_Error
1175
     */
1176
	public function set_required( $required ) {
1177
		$this->set( 'TKT_required', $required );
1178
	}
1179
1180
1181
1182
    /**
1183
     * Gets taxable
1184
     *
1185
     * @return boolean
1186
     * @throws \EE_Error
1187
     */
1188
	public function taxable() {
1189
		return $this->get( 'TKT_taxable' );
1190
	}
1191
1192
1193
1194
    /**
1195
     * Sets taxable
1196
     *
1197
     * @param boolean $taxable
1198
     * @return void
1199
     * @throws \EE_Error
1200
     */
1201
	public function set_taxable( $taxable ) {
1202
		$this->set( 'TKT_taxable', $taxable );
1203
	}
1204
1205
1206
1207
    /**
1208
     * Gets is_default
1209
     *
1210
     * @return boolean
1211
     * @throws \EE_Error
1212
     */
1213
	public function is_default() {
1214
		return $this->get( 'TKT_is_default' );
1215
	}
1216
1217
1218
1219
    /**
1220
     * Sets is_default
1221
     *
1222
     * @param boolean $is_default
1223
     * @return void
1224
     * @throws \EE_Error
1225
     */
1226
	public function set_is_default( $is_default ) {
1227
		$this->set( 'TKT_is_default', $is_default );
1228
	}
1229
1230
1231
1232
    /**
1233
     * Gets order
1234
     *
1235
     * @return int
1236
     * @throws \EE_Error
1237
     */
1238
	public function order() {
1239
		return $this->get( 'TKT_order' );
1240
	}
1241
1242
1243
1244
    /**
1245
     * Sets order
1246
     *
1247
     * @param int $order
1248
     * @return void
1249
     * @throws \EE_Error
1250
     */
1251
	public function set_order( $order ) {
1252
		$this->set( 'TKT_order', $order );
1253
	}
1254
1255
1256
1257
    /**
1258
     * Gets row
1259
     *
1260
     * @return int
1261
     * @throws \EE_Error
1262
     */
1263
	public function row() {
1264
		return $this->get( 'TKT_row' );
1265
	}
1266
1267
1268
1269
    /**
1270
     * Sets row
1271
     *
1272
     * @param int $row
1273
     * @return void
1274
     * @throws \EE_Error
1275
     */
1276
	public function set_row( $row ) {
1277
		$this->set( 'TKT_row', $row );
1278
	}
1279
1280
1281
1282
    /**
1283
     * Gets deleted
1284
     *
1285
     * @return boolean
1286
     * @throws \EE_Error
1287
     */
1288
	public function deleted() {
1289
		return $this->get( 'TKT_deleted' );
1290
	}
1291
1292
1293
1294
    /**
1295
     * Sets deleted
1296
     *
1297
     * @param boolean $deleted
1298
     * @return void
1299
     * @throws \EE_Error
1300
     */
1301
	public function set_deleted( $deleted ) {
1302
		$this->set( 'TKT_deleted', $deleted );
1303
	}
1304
1305
1306
1307
    /**
1308
     * Gets parent
1309
     *
1310
     * @return int
1311
     * @throws \EE_Error
1312
     */
1313
	public function parent_ID() {
1314
		return $this->get( 'TKT_parent' );
1315
	}
1316
1317
1318
1319
    /**
1320
     * Sets parent
1321
     *
1322
     * @param int $parent
1323
     * @return void
1324
     * @throws \EE_Error
1325
     */
1326
	public function set_parent_ID( $parent ) {
1327
		$this->set( 'TKT_parent', $parent );
1328
	}
1329
1330
1331
1332
    /**
1333
     * Gets a string which is handy for showing in gateways etc that describes the ticket.
1334
     *
1335
     * @return string
1336
     * @throws \EE_Error
1337
     */
1338
	public function name_and_info() {
1339
		$times = array();
1340
		foreach ( $this->datetimes() as $datetime ) {
1341
			$times[] = $datetime->start_date_and_time();
1342
		}
1343
		return $this->name() . ' @ ' . implode( ', ', $times ) . ' for ' . $this->pretty_price();
1344
	}
1345
1346
1347
1348
    /**
1349
     * Gets name
1350
     *
1351
     * @return string
1352
     * @throws \EE_Error
1353
     */
1354
	public function name() {
1355
		return $this->get( 'TKT_name' );
1356
	}
1357
1358
1359
1360
    /**
1361
     * Gets price
1362
     *
1363
     * @return float
1364
     * @throws \EE_Error
1365
     */
1366
	public function price() {
1367
		return $this->get( 'TKT_price' );
1368
	}
1369
1370
1371
1372
    /**
1373
     * Gets all the registrations for this ticket
1374
     *
1375
     * @param array $query_params like EEM_Base::get_all's
1376
     * @return EE_Registration[]|EE_Base_Class[]
1377
     * @throws \EE_Error
1378
     */
1379
	public function registrations( $query_params = array() ) {
1380
		return $this->get_many_related( 'Registration', $query_params );
1381
	}
1382
1383
1384
1385
    /**
1386
     * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1387
     * into account
1388
     *
1389
     * @return int
1390
     * @throws \EE_Error
1391
     */
1392 View Code Duplication
	public function update_tickets_sold() {
1393
        $count_regs_for_this_ticket = $this->count_registrations(
1394
            array(
1395
                array(
1396
                    'STS_ID'      => EEM_Registration::status_id_approved,
1397
                    'REG_deleted' => 0,
1398
                ),
1399
            )
1400
        );
1401
        $sold = $this->sold();
1402
        if ($count_regs_for_this_ticket > $sold) {
1403
            $this->increase_sold($count_regs_for_this_ticket - $sold);
1404
            $this->save();
1405
        } else if ($count_regs_for_this_ticket < $sold) {
1406
            $this->decrease_sold($count_regs_for_this_ticket - $sold);
1407
            $this->save();
1408
        }
1409
		return $count_regs_for_this_ticket;
1410
	}
1411
1412
1413
1414
	/**
1415
	 * Counts the registrations for this ticket
1416
	 * @param array $query_params like EEM_Base::get_all's
1417
	 * @return int
1418
	 */
1419
	public function count_registrations( $query_params = array() ) {
1420
		return $this->count_related('Registration', $query_params);
1421
	}
1422
1423
1424
1425
	/**
1426
	 * Implementation for EEI_Has_Icon interface method.
1427
	 * @see EEI_Visual_Representation for comments
1428
	 * @return string
1429
	 */
1430
	public function get_icon() {
1431
		return '<span class="dashicons dashicons-tickets-alt"></span>';
1432
	}
1433
1434
1435
1436
    /**
1437
     * Implementation of the EEI_Event_Relation interface method
1438
     *
1439
     * @see EEI_Event_Relation for comments
1440
     * @return EE_Event
1441
     * @throws \EE_Error
1442
     * @throws UnexpectedEntityException
1443
     */
1444
	public function get_related_event() {
1445
		//get one datetime to use for getting the event
1446
		$datetime = $this->first_datetime();
1447
		if ( ! $datetime instanceof \EE_Datetime ) {
1448
			throw new UnexpectedEntityException(
1449
				$datetime,
1450
                'EE_Datetime',
1451
				sprintf(
1452
					__( 'The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1453
					$this->name()
1454
				)
1455
			);
1456
		}
1457
		$event = $datetime->event();
1458
		if ( ! $event instanceof \EE_Event ) {
1459
			throw new UnexpectedEntityException(
1460
				$event,
1461
                'EE_Event',
1462
				sprintf(
1463
					__( 'The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1464
					$this->name()
1465
				)
1466
			);
1467
		}
1468
		return $event;
1469
	}
1470
1471
1472
1473
    /**
1474
     * Implementation of the EEI_Event_Relation interface method
1475
     *
1476
     * @see EEI_Event_Relation for comments
1477
     * @return string
1478
     * @throws UnexpectedEntityException
1479
     * @throws \EE_Error
1480
     */
1481
	public function get_event_name() {
1482
		$event = $this->get_related_event();
1483
		return $event instanceof EE_Event ? $event->name() : '';
1484
	}
1485
1486
1487
1488
    /**
1489
     * Implementation of the EEI_Event_Relation interface method
1490
     *
1491
     * @see EEI_Event_Relation for comments
1492
     * @return int
1493
     * @throws UnexpectedEntityException
1494
     * @throws \EE_Error
1495
     */
1496
	public function get_event_ID() {
1497
		$event = $this->get_related_event();
1498
		return $event instanceof EE_Event ? $event->ID() : 0;
1499
	}
1500
1501
1502
    /**
1503
     * This simply returns whether a ticket can be permanently deleted or not.
1504
     * The criteria for determining this is whether the ticket has any related registrations.
1505
     * If there are none then it can be permanently deleted.
1506
     *
1507
     * @return bool
1508
     */
1509
	public function is_permanently_deleteable() {
1510
	    return $this->count_registrations() === 0;
1511
    }
1512
1513
1514
    /**
1515
     * Returns the payment's amount in subunits (if the currency has subunits; otherwise this will actually be
1516
     * in the currency's main units)
1517
     *
1518
     * @return int
1519
     * @throws EE_Error
1520
     * @throws InvalidEntityException
1521
     * @throws DomainException
1522
     */
1523
    public function amountInSubunits()
1524
    {
1525
        return $this->moneyInSubunits('TKT_price');
1526
    }
1527
1528
1529
    /**
1530
     * Sets the payment's amount based on the incoming monetary subunits (eg pennies). If the currency has no subunits,
1531
     * the amount is actually assumed to be in the currency's main units
1532
     *
1533
     * @param int $amount_in_subunits
1534
     * @return void
1535
     * @throws EE_Error
1536
     * @throws InvalidArgumentException
1537
     * @throws InvalidInterfaceException
1538
     * @throws InvalidIdentifierException
1539
     * @throws InvalidDataTypeException
1540
     * @throws DomainException
1541
     */
1542
    public function setAmountInSubunits($amount_in_subunits)
1543
    {
1544
        $this->setMoneySubunits('TKT_price', $amount_in_subunits);
1545
    }
1546
} //end EE_Ticket class
1547