Completed
Branch BUG-9871-email-validation (e62b1a)
by
unknown
350:41 queued 333:27
created

EE_Ticket::name()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php use EventEspresso\core\exceptions\UnexpectedEntityException;
2
3
if ( !defined( 'EVENT_ESPRESSO_VERSION' ) ) {
4
	exit( 'No direct script access allowed' );
5
}
6
/**
7
 * Event Espresso
8
 *
9
 * Event Registration and Management Plugin for WordPress
10
 *
11
 * @ package 		Event Espresso
12
 * @ author 		Event Espresso
13
 * @ copyright 	(c) 2008-2011 Event Espresso  All Rights Reserved.
14
 * @ license 		{@link http://eventespresso.com/support/terms-conditions/}   * see Plugin Licensing *
15
 * @ link 				{@link http://www.eventespresso.com}
16
 * @ since 			4.0
17
 *
18
 */
19
20
21
22
/**
23
 * EE_Ticket class
24
 *
25
 * @package 			Event Espresso
26
 * @subpackage 	includes/classes/EE_Ticket.class.php
27
 * @author             Darren Ethier
28
 */
29
class EE_Ticket extends EE_Soft_Delete_Base_Class implements EEI_Line_Item_Object, EEI_Event_Relation, EEI_Has_Icon {
30
31
	/**
32
	 * The following constants are used by the ticket_status() method to indicate whether a ticket is on sale or not.
33
	 */
34
	const sold_out = 'TKS';
35
36
	/**
37
	 *
38
	 */
39
	const expired = 'TKE';
40
41
	/**
42
	 *
43
	 */
44
	const archived = 'TKA';
45
46
	/**
47
	 *
48
	 */
49
	const pending = 'TKP';
50
51
	/**
52
	 *
53
	 */
54
	const onsale = 'TKO';
55
56
	/**
57
	 * cached result from method of the same name
58
	 * @var float $_ticket_total_with_taxes
59
	 */
60
	private $_ticket_total_with_taxes = NULL;
61
62
	/**
63
	 *
64
	 * @param array $props_n_values  incoming values
65
	 * @param string $timezone  incoming timezone (if not set the timezone set for the website will be
66
	 *                          		used.)
67
	 * @param array $date_formats  incoming date_formats in an array where the first value is the
68
	 *                             		    date_format and the second value is the time format
69
	 * @return EE_Ticket
70
	 */
71
	public static function new_instance( $props_n_values = array(), $timezone = null, $date_formats = array() ) {
72
		$has_object = parent::_check_for_object( $props_n_values, __CLASS__, $timezone, $date_formats );
0 ignored issues
show
Bug introduced by
It seems like $timezone defined by parameter $timezone on line 71 can also be of type string; however, EE_Base_Class::_check_for_object() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (_check_for_object() instead of new_instance()). Are you sure this is correct? If so, you might want to change this to $this->_check_for_object().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
73
		return $has_object ? $has_object : new self( $props_n_values, false, $timezone, $date_formats );
74
	}
75
76
77
78
	/**
79
	 * @param array $props_n_values  incoming values from the database
80
	 * @param string $timezone  incoming timezone as set by the model.  If not set the timezone for
81
	 *                          		the website will be used.
82
	 * @return EE_Ticket
83
	 */
84
	public static function new_instance_from_db( $props_n_values = array(), $timezone = null ) {
85
		return new self( $props_n_values, TRUE, $timezone );
86
	}
87
88
89
90
	/**
91
	 * @return bool
92
	 */
93
	public function parent() {
94
		return $this->get( 'TKT_parent' );
95
	}
96
97
98
99
	/**
100
	 * return if a ticket has quantities available for purchase
101
	 * @param  int $DTT_ID the primary key for a particular datetime
102
	 * @return boolean
103
	 */
104
	public function available( $DTT_ID = 0 ) {
105
		// are we checking availability for a particular datetime ?
106
		if ( $DTT_ID ) {
107
			// get that datetime object
108
			$datetime = $this->get_first_related( 'Datetime', array( array( 'DTT_ID' => $DTT_ID ) ) );
109
			// if  ticket sales for this datetime have exceeded the reg limit...
110
			if ( $datetime instanceof EE_Datetime && $datetime->sold_out() ) {
111
				return FALSE;
112
			}
113
		}
114
		// datetime is still open for registration, but is this ticket sold out ?
115
		return $this->qty() < 1 || $this->qty() > $this->sold() ? TRUE : FALSE;
116
	}
117
118
119
120
	/**
121
	 * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
122
	 * @param bool $display true = we'll return a localized string, otherwise we just return the value of the relevant status const
123
	 * @return mixed(int|string) status int if the display string isn't requested
0 ignored issues
show
Documentation introduced by
The doc-type mixed(int|string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
124
	 */
125
	public function ticket_status( $display = FALSE ) {
126 View Code Duplication
		if ( ! $this->is_remaining() ) {
127
			return $display ? EEH_Template::pretty_status( EE_Ticket::sold_out, FALSE, 'sentence' ) : EE_Ticket::sold_out;
128
		}
129 View Code Duplication
		if ( $this->get( 'TKT_deleted' ) ) {
130
			return $display ? EEH_Template::pretty_status( EE_Ticket::archived, FALSE, 'sentence' ) : EE_Ticket::archived;
131
		}
132
		if ( $this->is_expired() ) {
133
			return $display ? EEH_Template::pretty_status( EE_Ticket::expired, FALSE, 'sentence' ) : EE_Ticket::expired;
134
		}
135
		if ( $this->is_pending() ) {
136
			return $display ? EEH_Template::pretty_status( EE_Ticket::pending, FALSE, 'sentence' ) : EE_Ticket::pending;
137
		}
138
		if ( $this->is_on_sale() ) {
139
			return $display ? EEH_Template::pretty_status( EE_Ticket::onsale, FALSE, 'sentence' ) : EE_Ticket::onsale;
140
		}
141
		return '';
142
	}
143
144
145
146
	/**
147
	 * 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.
148
	 *
149
	 * @access public
150
	 * @param  int $DTT_ID if an int above 0 is included here then we get a specific dtt.
151
	 * @return boolean         true = tickets remaining, false not.
152
	 */
153
	public function is_remaining( $DTT_ID = 0 ) {
154
		$num_remaining = $this->remaining( $DTT_ID );
155
		if ( $num_remaining === 0 ) {
156
			return FALSE;
157
		}
158
		if ( $num_remaining > 0 && $num_remaining < $this->min() ) {
159
			return FALSE;
160
		}
161
		return TRUE;
162
	}
163
164
165
166
	/**
167
	 * return the total number of tickets available for purchase
168
	 * @param  int $DTT_ID the primary key for a particular datetime. set to null for
169
	 *                     all related datetimes
170
	 * @return int
171
	 */
172
	public function remaining( $DTT_ID = 0 ) {
173
		// are we checking availability for a particular datetime ?
174
		if ( $DTT_ID ) {
175
			// get array with the one requested datetime
176
			$datetimes = $this->get_many_related( 'Datetime', array( array( 'DTT_ID' => $DTT_ID ) ) );
177
		} else {
178
			// we need to check availability of ALL datetimes
179
			$datetimes = $this->get_many_related( 'Datetime', array( 'order_by' => array( 'DTT_EVT_start' => 'ASC' ) ) );
180
		}
181
		//		d( $datetimes );
182
		// if datetime reg limit is not unlimited
183
		if ( ! empty( $datetimes ) ) {
184
			// although TKT_qty and $datetime->spaces_remaining() could both be EE_INF
185
			// we only need to check for EE_INF explicitly if we want to optimize.
186
			// because EE_INF - x = EE_INF; and min(x,EE_INF) = x;
187
			$tickets_remaining = $this->qty() - $this->sold();
188
			foreach ( $datetimes as $datetime ) {
189
				if ( $datetime instanceof EE_Datetime ) {
190
					$tickets_remaining = min( $tickets_remaining, $datetime->spaces_remaining() );
191
				}
192
			}
193
			return $tickets_remaining;
194
		}
195
		return 0;
196
	}
197
198
199
200
	/**
201
	 * Gets min
202
	 * @return int
203
	 */
204
	function min() {
205
		return $this->get( 'TKT_min' );
206
	}
207
208
209
210
	/**
211
	 * return if a ticket is no longer available cause its available dates have expired.
212
	 * @return boolean
213
	 */
214
	public function is_expired() {
215
		return ( $this->get_raw( 'TKT_end_date' ) < time() );
216
	}
217
218
219
220
	/**
221
	 * Return if a ticket is yet to go on sale or not
222
	 * @return boolean
223
	 */
224
	public function is_pending() {
225
		return ( $this->get_raw( 'TKT_start_date' ) > time() );
226
	}
227
228
229
230
	/**
231
	 * Return if a ticket is on sale or not
232
	 * @return boolean
233
	 */
234
	public function is_on_sale() {
235
		return ( $this->get_raw( 'TKT_start_date' ) < time() && $this->get_raw( 'TKT_end_date' ) > time() );
236
	}
237
238
239
240
	/**
241
	 * This returns the chronologically last datetime that this ticket is associated with
242
	 * @param string 	$dt_frmt
243
	 * @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
244
	 * @return array
245
	 */
246
	public function date_range( $dt_frmt = '', $conjunction = ' - ' ) {
247
		$first_date = $this->first_datetime() instanceof EE_Datetime ? $this->first_datetime()->start_date( $dt_frmt ) : '';
248
		$last_date = $this->last_datetime() instanceof EE_Datetime ? $this->last_datetime()->end_date( $dt_frmt ) : '';
249
250
		return $first_date && $last_date ? $first_date . $conjunction  . $last_date : '';
251
	}
252
253
254
255
	/**
256
	 * This returns the chronologically first datetime that this ticket is associated with
257
	 * @return EE_Datetime
258
	 */
259
	public function first_datetime() {
260
		$datetimes = $this->datetimes( array( 'limit' => 1 ) );
261
		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 261 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...
262
	}
263
264
265
266
	/**
267
	 * Gets all the datetimes this ticket can be used for attending.
268
	 * Unless otherwise specified, orders datetimes by start date.
269
	 * @param array $query_params see EEM_Base::get_all()
270
	 * @return EE_Datetime[]
271
	 */
272
	public function datetimes( $query_params = array() ) {
273
		if ( ! isset( $query_params[ 'order_by' ] ) ) {
274
			$query_params[ 'order_by' ][ 'DTT_order' ] = 'ASC';
275
		}
276
		return $this->get_many_related( 'Datetime', $query_params );
277
	}
278
279
280
281
	/**
282
	 * This returns the chronologically last datetime that this ticket is associated with
283
	 * @return EE_Datetime
284
	 */
285
	public function last_datetime() {
286
		$datetimes = $this->datetimes( array( 'limit' => 1, 'order_by' => array( 'DTT_EVT_start' => 'DESC' ) ) );
287
		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 287 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...
288
	}
289
290
291
292
	/**
293
	 * This returns the total tickets sold depending on the given parameters.
294
	 * @param  string $what   Can be one of two options: 'ticket', 'datetime'.
295
	 *                        'ticket' = total ticket sales for all datetimes this ticket is related to
296
	 *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
297
	 *                        'datetime' = total ticket sales in the datetime_ticket table. If $dtt_id is not given then we return an array of sales indexed by datetime.  If $dtt_id IS given then we return the tickets sold for that given datetime.
298
	 * @param  int    $dtt_id [optional] include the dtt_id with $what = 'datetime'.
299
	 * @return mixed (array|int)          how many tickets have sold
300
	 */
301
	public function tickets_sold( $what = 'ticket', $dtt_id = NULL ) {
302
		$total = 0;
303
		$tickets_sold = $this->_all_tickets_sold();
304
		switch ( $what ) {
305
			case 'ticket' :
306
				return $tickets_sold[ 'ticket' ];
307
				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...
308
			case 'datetime' :
309
				if ( empty( $tickets_sold[ 'datetime' ] ) ) {
310
					return $total;
311
				}
312
				if ( ! empty( $dtt_id ) && ! isset( $tickets_sold[ 'datetime' ][ $dtt_id ] ) ) {
313
					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__ );
314
					return $total;
315
				}
316
				return empty( $dtt_id ) ? $tickets_sold[ 'datetime' ] : $tickets_sold[ 'datetime' ][ $dtt_id ];
317
				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...
318
			default:
319
				return $total;
320
		}
321
	}
322
323
324
325
	/**
326
	 * This returns an array indexed by datetime_id for tickets sold with this ticket.
327
	 * @return EE_Ticket[]
328
	 */
329
	protected function _all_tickets_sold() {
330
		$datetimes = $this->get_many_related( 'Datetime' );
331
		$tickets_sold = array();
332
		if ( ! empty( $datetimes ) ) {
333
			foreach ( $datetimes as $datetime ) {
334
				$tickets_sold[ 'datetime' ][ $datetime->ID() ] = $datetime->get( 'DTT_sold' );
335
			}
336
		}
337
		//Tickets sold
338
		$tickets_sold[ 'ticket' ] = $this->sold();
339
		return $tickets_sold;
340
	}
341
342
343
344
	/**
345
	 * This returns the base price object for the ticket.
346
	 *
347
	 * @access public
348
	 * @param  bool $return_array whether to return as an array indexed by price id or just the object.
349
	 * @return EE_Price
350
	 */
351
	public function base_price( $return_array = FALSE ) {
352
		$_where = array( 'Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price );
353
		return $return_array ? $this->get_many_related( 'Price', array( $_where ) ) : $this->get_first_related( 'Price', array( $_where ) );
0 ignored issues
show
Bug Compatibility introduced by
The expression $return_array ? $this->g...rice', array($_where)); of type EE_Base_Class[]|EE_Base_Class adds the type EE_Base_Class[] to the return on line 353 which is incompatible with the return type documented by EE_Ticket::base_price of type EE_Price.
Loading history...
354
	}
355
356
357
358
	/**
359
	 * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
360
	 *
361
	 * @access public
362
	 * @return EE_Price[]
363
	 */
364
	public function price_modifiers() {
365
		$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 ) ) ) );
366
		return $this->prices( $query_params );
367
	}
368
369
370
371
	/**
372
	 * Gets all the prices that combine to form the final price of this ticket
373
	 * @param array $query_params like EEM_Base::get_all
374
	 * @return EE_Price[]
375
	 */
376
	public function prices( $query_params = array() ) {
377
		return $this->get_many_related( 'Price', $query_params );
378
	}
379
380
381
382
	/**
383
	 * Gets all the ticket applicabilities (ie, relations between datetimes and tickets)
384
	 * @param array $query_params see EEM_Base::get_all()
385
	 * @return EE_Datetime_Ticket
386
	 */
387
	public function datetime_tickets( $query_params = array() ) {
388
		return $this->get_many_related( 'Datetime_Ticket', $query_params );
389
	}
390
391
392
393
	/**
394
	 * Gets all the datetimes from the db ordered by DTT_order
395
	 * @param boolean $show_expired
396
	 * @param boolean $show_deleted
397
	 * @return EE_Datetime[]
398
	 */
399
	public function datetimes_ordered( $show_expired = TRUE, $show_deleted = FALSE ) {
400
		return EEM_Datetime::instance( $this->_timezone )->get_datetimes_for_ticket_ordered_by_DTT_order( $this->ID(), $show_expired, $show_deleted );
401
	}
402
403
404
405
	/**
406
	 * Gets ID
407
	 * @return string
408
	 */
409
	function ID() {
410
		return $this->get( 'TKT_ID' );
411
	}
412
413
414
415
416
	/**
417
	 * get the author of the ticket.
418
	 *
419
	 * @since 4.5.0
420
	 *
421
	 * @return int
422
	 */
423
	public function wp_user() {
424
		return $this->get('TKT_wp_user');
425
	}
426
427
428
429
	/**
430
	 * Gets the template for the ticket
431
	 * @return EE_Ticket_Template
432
	 */
433
	public function template() {
434
		return $this->get_first_related( 'Ticket_Template' );
435
	}
436
437
438
439
	/**
440
	 * Simply returns an array of EE_Price objects that are taxes.
441
	 * @return EE_Price[]
442
	 */
443
	public function get_ticket_taxes_for_admin() {
444
		return EE_Taxes::get_taxes_for_admin();
445
	}
446
447
448
449
	/**
450
	 * @return bool
451
	 */
452
	public function ticket_price() {
453
		return $this->get( 'TKT_price' );
454
	}
455
456
457
458
	/**
459
	 * @return mixed
460
	 */
461
	public function pretty_price() {
462
		return $this->get_pretty( 'TKT_price' );
463
	}
464
465
466
467
	/**
468
	 * @return bool
469
	 */
470
	public function is_free() {
471
		return $this->get_ticket_total_with_taxes() == 0 ? TRUE : FALSE;
472
	}
473
474
475
476
	/**
477
	 * get_ticket_total_with_taxes
478
	 * @param bool $no_cache
479
	 * @return float
480
	 */
481
	public function get_ticket_total_with_taxes( $no_cache = FALSE ) {
482
		if ( ! isset( $this->_ticket_total_with_taxes ) || $no_cache ) {
483
			$this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
484
		}
485
		return (float)$this->_ticket_total_with_taxes;
486
	}
487
488
489
490
	public function ensure_TKT_Price_correct() {
491
		$this->set( 'TKT_price', EE_Taxes::get_subtotal_for_admin( $this ) );
492
		$this->save();
493
	}
494
495
496
497
	/**
498
	 * @return float
499
	 */
500
	public function get_ticket_subtotal() {
501
		return EE_Taxes::get_subtotal_for_admin( $this );
502
	}
503
504
505
506
	/**
507
	 * Returns the total taxes applied to this ticket
508
	 * @return float
509
	 */
510
	public function get_ticket_taxes_total_for_admin() {
511
		return EE_Taxes::get_total_taxes_for_admin( $this );
512
	}
513
514
515
516
	/**
517
	 * Sets name
518
	 * @param string $name
519
	 * @return boolean
520
	 */
521
	function set_name( $name ) {
522
		$this->set( 'TKT_name', $name );
523
	}
524
525
526
527
	/**
528
	 * Gets description
529
	 * @return string
530
	 */
531
	function description() {
532
		return $this->get( 'TKT_description' );
533
	}
534
535
536
537
	/**
538
	 * Sets description
539
	 * @param string $description
540
	 * @return boolean
541
	 */
542
	function set_description( $description ) {
543
		$this->set( 'TKT_description', $description );
544
	}
545
546
547
548
	/**
549
	 * Gets start_date
550
	 * @param string $dt_frmt
551
	 * @param string $tm_frmt
552
	 * @return string
553
	 */
554
	function start_date( $dt_frmt = '', $tm_frmt = '' ) {
555
		return $this->_get_datetime( 'TKT_start_date', $dt_frmt, $tm_frmt );
556
	}
557
558
559
560
	/**
561
	 * Sets start_date
562
	 * @param string $start_date
563
	 * @return void
564
	 */
565
	function set_start_date( $start_date ) {
566
		$this->_set_date_time( 'B', $start_date, 'TKT_start_date' );
567
	}
568
569
570
571
	/**
572
	 * Gets end_date
573
	 * @param string $dt_frmt
574
	 * @param string $tm_frmt
575
	 * @return string
576
	 */
577
	function end_date( $dt_frmt = '', $tm_frmt = '' ) {
578
		return $this->_get_datetime( 'TKT_end_date', $dt_frmt, $tm_frmt );
579
	}
580
581
582
583
	/**
584
	 * Sets end_date
585
	 * @param string $end_date
586
	 * @return void
587
	 */
588
	function set_end_date( $end_date ) {
589
		$this->_set_date_time( 'B', $end_date, 'TKT_end_date' );
590
	}
591
592
593
594
	/**
595
	 * Sets sell until time
596
	 *
597
	 * @since 4.5.0
598
	 *
599
	 * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
600
	 */
601
	function set_end_time( $time ) {
602
		$this->_set_time_for( $time, 'TKT_end_date' );
603
	}
604
605
606
607
	/**
608
	 * Sets min
609
	 * @param int $min
610
	 * @return boolean
611
	 */
612
	function set_min( $min ) {
613
		$this->set( 'TKT_min', $min );
614
	}
615
616
617
618
	/**
619
	 * Gets max
620
	 * @return int
621
	 */
622
	function max() {
623
		return $this->get( 'TKT_max' );
624
	}
625
626
627
628
	/**
629
	 * Sets max
630
	 * @param int $max
631
	 * @return boolean
632
	 */
633
	function set_max( $max ) {
634
		$this->set( 'TKT_max', $max );
635
	}
636
637
638
639
	/**
640
	 * Sets price
641
	 * @param float $price
642
	 * @return boolean
643
	 */
644
	function set_price( $price ) {
645
		$this->set( 'TKT_price', $price );
646
	}
647
648
649
650
	/**
651
	 * Gets sold
652
	 * @return int
653
	 */
654
	function sold() {
655
		return $this->get_raw( 'TKT_sold' );
656
	}
657
658
659
660
	/**
661
	 * increments sold by amount passed by $qty
662
	 * @param int $qty
663
	 * @return boolean
664
	 */
665
	function increase_sold( $qty = 1 ) {
666
		$sold = $this->sold() + $qty;
667
		$this->_increase_sold_for_datetimes( $qty );
668
		return $this->set_sold( $sold );
669
	}
670
671
672
673
	/**
674
	 * Increases sold on related datetimes
675
	 * @param int $qty
676
	 * @return boolean
677
	 */
678 View Code Duplication
	protected function _increase_sold_for_datetimes( $qty = 1 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
679
		$datetimes = $this->datetimes();
680
		if ( is_array( $datetimes ) ) {
681
			foreach ( $datetimes as $datetime ) {
682
				if ( $datetime instanceof EE_Datetime ) {
683
					$datetime->increase_sold( $qty );
684
					$datetime->save();
685
				}
686
			}
687
		}
688
	}
689
690
691
692
	/**
693
	 * Sets sold
694
	 * @param int $sold
695
	 * @return boolean
696
	 */
697
	function set_sold( $sold ) {
698
		// sold can not go below zero
699
		$sold = max( 0, $sold );
700
		$this->set( 'TKT_sold', $sold );
701
	}
702
703
704
705
	/**
706
	 * decrements (subtracts) sold by amount passed by $qty
707
	 * @param int $qty
708
	 * @return boolean
709
	 */
710
	function decrease_sold( $qty = 1 ) {
711
		$sold = $this->sold() - $qty;
712
		$this->_decrease_sold_for_datetimes( $qty );
713
		return $this->set_sold( $sold );
714
	}
715
716
717
718
	/**
719
	* Decreases sold on related datetimes
720
	*
721
	* @param int $qty
722
	* @return boolean
723
	*/
724 View Code Duplication
	protected function _decrease_sold_for_datetimes( $qty = 1 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
725
		$datetimes = $this->datetimes();
726
		if ( is_array( $datetimes ) ) {
727
			foreach ( $datetimes as $datetime ) {
728
				if ( $datetime instanceof EE_Datetime ) {
729
					$datetime->decrease_sold( $qty );
730
					$datetime->save();
731
				}
732
			}
733
		}
734
	}
735
736
737
738
	/**
739
	 * Gets ticket quantity
740
	 *
741
	 * @param string $context 	ticket quantity is somewhat subjective depending on the exact information sought
742
	 *                         	therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
743
	 *                         	'' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
744
	 *                         	REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
745
	 *                         	SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
746
	 *                         	is therefore the truest measure of tickets that can be purchased at the moment
747
	 *
748
	 * @return int
749
	 */
750
	function qty( $context = '' ) {
751
		switch ( $context ) {
752
			case 'reg_limit' :
753
				return $this->real_quantity_on_ticket();
754
			case 'saleable' :
755
				return $this->real_quantity_on_ticket( 'saleable' );
756
			default:
757
				return $this->get_raw( 'TKT_qty' );
758
		}
759
	}
760
761
762
763
	/**
764
	 * Gets ticket quantity
765
	 *
766
	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
767
	 *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
768
	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
769
	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
770
	 *                            is therefore the truest measure of tickets that can be purchased at the moment
771
	 *
772
	 * @return int
773
	 */
774
	function real_quantity_on_ticket( $context = 'reg_limit' ) {
775
		// start with the original db value for ticket quantity
776
		$raw = $this->get_raw( 'TKT_qty' );
777
		// return immediately if it's zero
778
		if ( $raw === 0 ) {
779
			return $raw;
780
		}
781
		// ensure qty doesn't exceed raw value for THIS ticket
782
		$qty = min( EE_INF, $raw );
783
		// NOW that we know the  maximum number of tickets available for the ticket
784
		// we need to calculate the maximum number of tickets available for the datetime
785
		// without really factoring this ticket into the calculations
786
		$datetimes = $this->datetimes();
787
		foreach ( $datetimes as $datetime ) {
788
			if ( $datetime instanceof EE_Datetime ) {
789
				// initialize with no restrictions for each datetime
790
				// but adjust datetime qty based on datetime reg limit
791
				$datetime_qty = min( EE_INF, $datetime->reg_limit() );
792
				// if we want the actual saleable amount, then we need to consider OTHER ticket sales
793
				// for this datetime, that do NOT include sales for this ticket (so we add THIS ticket's sales back in)
794
				if ( $context == 'saleable' ) {
795
					$datetime_qty = max( $datetime_qty - $datetime->sold() + $this->sold(), 0 );
796
					$datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
797
				}
798
				$qty = min( $datetime_qty, $qty );
799
			}
800
801
		}
802
		// we need to factor in the details for this specific ticket
803
		if ( $qty > 0 && $context == 'saleable' ) {
804
			// and subtract the sales for THIS ticket
805
			$qty = max( $qty - $this->sold(), 0 );
806
			//echo '&nbsp; $qty: ' . $qty . "<br />";
807
		}
808
		//echo '$qty: ' . $qty . "<br />";
809
		return $qty;
810
	}
811
812
813
814
	/**
815
	 * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
816
	 *
817
	 * @param int  $qty
818
	 * @return bool
819
	 * @throws \EE_Error
820
	 */
821 View Code Duplication
	function set_qty( $qty ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
822
		$datetimes = $this->datetimes();
823
		foreach ( $datetimes as $datetime ) {
824
			if ( $datetime instanceof EE_Datetime ) {
825
				$qty = min( $qty, $datetime->reg_limit() );
826
			}
827
		}
828
		$this->set( 'TKT_qty', $qty );
829
	}
830
831
832
833
	/**
834
	 * Gets uses
835
	 * @return int
836
	 */
837
	function uses() {
838
		return $this->get( 'TKT_uses' );
839
	}
840
841
842
843
	/**
844
	 * Sets uses
845
	 * @param int $uses
846
	 * @return boolean
847
	 */
848
	function set_uses( $uses ) {
849
		$this->set( 'TKT_uses', $uses );
850
	}
851
852
853
854
	/**
855
	 * returns whether ticket is required or not.
856
	 * @return boolean
857
	 */
858
	public function required() {
859
		return $this->get( 'TKT_required' );
860
	}
861
862
863
864
	/**
865
	 * sets the TKT_required property
866
	 * @param boolean $required
867
	 * @return boolean
868
	 */
869
	public function set_required( $required ) {
870
		$this->set( 'TKT_required', $required );
871
	}
872
873
874
875
	/**
876
	 * Gets taxable
877
	 * @return boolean
878
	 */
879
	function taxable() {
880
		return $this->get( 'TKT_taxable' );
881
	}
882
883
884
885
	/**
886
	 * Sets taxable
887
	 * @param boolean $taxable
888
	 * @return boolean
889
	 */
890
	function set_taxable( $taxable ) {
891
		$this->set( 'TKT_taxable', $taxable );
892
	}
893
894
895
896
	/**
897
	 * Gets is_default
898
	 * @return boolean
899
	 */
900
	function is_default() {
901
		return $this->get( 'TKT_is_default' );
902
	}
903
904
905
906
	/**
907
	 * Sets is_default
908
	 * @param boolean $is_default
909
	 * @return boolean
910
	 */
911
	function set_is_default( $is_default ) {
912
		$this->set( 'TKT_is_default', $is_default );
913
	}
914
915
916
917
	/**
918
	 * Gets order
919
	 * @return int
920
	 */
921
	function order() {
922
		return $this->get( 'TKT_order' );
923
	}
924
925
926
927
	/**
928
	 * Sets order
929
	 * @param int $order
930
	 * @return boolean
931
	 */
932
	function set_order( $order ) {
933
		$this->set( 'TKT_order', $order );
934
	}
935
936
937
938
	/**
939
	 * Gets row
940
	 * @return int
941
	 */
942
	function row() {
943
		return $this->get( 'TKT_row' );
944
	}
945
946
947
948
	/**
949
	 * Sets row
950
	 * @param int $row
951
	 * @return boolean
952
	 */
953
	function set_row( $row ) {
954
		$this->set( 'TKT_row', $row );
955
	}
956
957
958
959
	/**
960
	 * Gets deleted
961
	 * @return boolean
962
	 */
963
	function deleted() {
964
		return $this->get( 'TKT_deleted' );
965
	}
966
967
968
969
	/**
970
	 * Sets deleted
971
	 * @param boolean $deleted
972
	 * @return boolean
973
	 */
974
	function set_deleted( $deleted ) {
975
		$this->set( 'TKT_deleted', $deleted );
976
	}
977
978
979
980
	/**
981
	 * Gets parent
982
	 * @return int
983
	 */
984
	function parent_ID() {
985
		return $this->get( 'TKT_parent' );
986
	}
987
988
989
990
	/**
991
	 * Sets parent
992
	 * @param int $parent
993
	 * @return boolean
994
	 */
995
	function set_parent_ID( $parent ) {
996
		$this->set( 'TKT_parent', $parent );
997
	}
998
999
1000
1001
	/**
1002
	 * Gets a string which is handy for showing in gateways etc that describes the ticket.
1003
	 * @return string
1004
	 */
1005
	function name_and_info() {
1006
		$times = array();
1007
		foreach ( $this->datetimes() as $datetime ) {
1008
			$times[] = $datetime->start_date_and_time();
0 ignored issues
show
Documentation Bug introduced by
The method start_date_and_time does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1009
		}
1010
		return $this->name() . " @ " . implode( ", ", $times ) . " for " . $this->pretty_price();
1011
	}
1012
1013
1014
1015
	/**
1016
	 * Gets name
1017
	 * @return string
1018
	 */
1019
	function name() {
1020
		return $this->get( 'TKT_name' );
1021
	}
1022
1023
1024
1025
	/**
1026
	 * Gets price
1027
	 * @return float
1028
	 */
1029
	function price() {
1030
		return $this->get( 'TKT_price' );
1031
	}
1032
1033
1034
1035
	/**
1036
	 * Gets all the registrations for this ticket
1037
	 * @param array $query_params like EEM_Base::get_all's
1038
	 * @return EE_Registration[]
1039
	 */
1040
	public function registrations( $query_params = array() ) {
1041
		return $this->get_many_related( 'Registration', $query_params );
1042
	}
1043
1044
1045
1046
	/**
1047
	 * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1048
	 * into account
1049
	 * @return int
1050
	 */
1051
	public function update_tickets_sold() {
1052
		$count_regs_for_this_ticket = $this->count_registrations( array( array( 'STS_ID' => EEM_Registration::status_id_approved, 'REG_deleted' => 0 ) ) );
1053
		$this->set_sold( $count_regs_for_this_ticket );
1054
		$this->save();
1055
		return $count_regs_for_this_ticket;
1056
	}
1057
1058
1059
1060
	/**
1061
	 * Counts the registrations for this ticket
1062
	 * @param array $query_params like EEM_Base::get_all's
1063
	 * @return int
1064
	 */
1065
	public function count_registrations( $query_params = array() ) {
1066
		return $this->count_related('Registration', $query_params);
1067
	}
1068
1069
1070
1071
	/**
1072
	 * Implementation for EEI_Has_Icon interface method.
1073
	 * @see EEI_Visual_Representation for comments
1074
	 * @return string
1075
	 */
1076
	public function get_icon() {
1077
		return '<span class="dashicons dashicons-tickets-alt"></span>';
1078
	}
1079
1080
1081
1082
	/**
1083
	 * Implementation of the EEI_Event_Relation interface method
1084
	 * @see EEI_Event_Relation for comments
1085
	 * @return EE_Event
1086
	 */
1087
	public function get_related_event() {
1088
		//get one datetime to use for getting the event
1089
		$datetime = $this->first_datetime();
1090
		if ( ! $datetime instanceof \EE_Datetime ) {
1091
			throw new UnexpectedEntityException(
1092
				$datetime,
1093
				'EE_Datetime',
1094
				sprintf(
1095
					__( "The ticket (%s) is not associated with any valid datetimes.", "event_espresso" ),
1096
					$datetime->name()
1097
				)
1098
			);
1099
		}
1100
		$event = $datetime->event();
1101
		if ( ! $event instanceof \EE_Event ) {
1102
			throw new UnexpectedEntityException(
1103
				$event,
1104
				'EE_Event',
1105
				sprintf(
1106
					__( "The ticket (%s) is not associated with a valid event.", "event_espresso" ),
1107
					$this->name()
1108
				)
1109
			);
1110
		}
1111
		return $event;
1112
	}
1113
1114
1115
	/**
1116
	 * Implementation of the EEI_Event_Relation interface method
1117
	 * @see EEI_Event_Relation for comments
1118
	 * @return string
1119
	 */
1120
	public function get_event_name() {
1121
		$event = $this->get_related_event();
1122
		return $event instanceof EE_Event ? $event->name() : '';
0 ignored issues
show
Bug Compatibility introduced by
The expression $event instanceof \EE_Ev... ? $event->name() : ''; of type boolean|string adds the type boolean to the return on line 1122 which is incompatible with the return type declared by the interface EEI_Event_Relation::get_event_name of type string.
Loading history...
1123
	}
1124
1125
1126
	/**
1127
	 * Implementation of the EEI_Event_Relation interface method
1128
	 * @see EEI_Event_Relation for comments
1129
	 * @return int
1130
	 */
1131
	public function get_event_ID() {
1132
		$event = $this->get_related_event();
1133
		return $event instanceof EE_Event ? $event->ID() : 0;
1134
	}
1135
1136
1137
} //end EE_Ticket class
1138