Completed
Pull Request — master (#1133)
by
unknown
106:54 queued 85:22
created
core/db_classes/EE_Ticket.class.php 2 patches
Indentation   +1723 added lines, -1723 removed lines patch added patch discarded remove patch
@@ -14,1731 +14,1731 @@
 block discarded – undo
14 14
 class EE_Ticket extends EE_Soft_Delete_Base_Class implements EEI_Line_Item_Object, EEI_Event_Relation, EEI_Has_Icon
15 15
 {
16 16
 
17
-    /**
18
-     * The following constants are used by the ticket_status() method to indicate whether a ticket is on sale or not.
19
-     */
20
-    const sold_out = 'TKS';
21
-
22
-    /**
23
-     *
24
-     */
25
-    const expired = 'TKE';
26
-
27
-    /**
28
-     *
29
-     */
30
-    const archived = 'TKA';
31
-
32
-    /**
33
-     *
34
-     */
35
-    const pending = 'TKP';
36
-
37
-    /**
38
-     *
39
-     */
40
-    const onsale = 'TKO';
41
-
42
-    /**
43
-     * extra meta key for tracking ticket reservations
44
-     *
45
-     * @type string
46
-     */
47
-    const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
48
-
49
-    /**
50
-     * cached result from method of the same name
51
-     *
52
-     * @var float $_ticket_total_with_taxes
53
-     */
54
-    private $_ticket_total_with_taxes;
55
-
56
-
57
-    /**
58
-     * @param array  $props_n_values          incoming values
59
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
60
-     *                                        used.)
61
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
62
-     *                                        date_format and the second value is the time format
63
-     * @return EE_Ticket
64
-     * @throws EE_Error
65
-     */
66
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
67
-    {
68
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
69
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
70
-    }
71
-
72
-
73
-    /**
74
-     * @param array  $props_n_values  incoming values from the database
75
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
76
-     *                                the website will be used.
77
-     * @return EE_Ticket
78
-     * @throws EE_Error
79
-     */
80
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
81
-    {
82
-        return new self($props_n_values, true, $timezone);
83
-    }
84
-
85
-
86
-    /**
87
-     * @return bool
88
-     * @throws EE_Error
89
-     */
90
-    public function parent()
91
-    {
92
-        return $this->get('TKT_parent');
93
-    }
94
-
95
-
96
-    /**
97
-     * return if a ticket has quantities available for purchase
98
-     *
99
-     * @param  int $DTT_ID the primary key for a particular datetime
100
-     * @return boolean
101
-     * @throws EE_Error
102
-     */
103
-    public function available($DTT_ID = 0)
104
-    {
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
-     * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
121
-     *
122
-     * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
123
-     *                               relevant status const
124
-     * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
125
-     *               further processing
126
-     * @return mixed status int if the display string isn't requested
127
-     * @throws EE_Error
128
-     */
129
-    public function ticket_status($display = false, $remaining = null)
130
-    {
131
-        $remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
132
-        if (! $remaining) {
133
-            return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
134
-        }
135
-        if ($this->get('TKT_deleted')) {
136
-            return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
137
-        }
138
-        if ($this->is_expired()) {
139
-            return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
140
-        }
141
-        if ($this->is_pending()) {
142
-            return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
143
-        }
144
-        if ($this->is_on_sale()) {
145
-            return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
146
-        }
147
-        return '';
148
-    }
149
-
150
-
151
-    /**
152
-     * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
153
-     * considering ALL the factors used for figuring that out.
154
-     *
155
-     * @access public
156
-     * @param  int $DTT_ID if an int above 0 is included here then we get a specific dtt.
157
-     * @return boolean         true = tickets remaining, false not.
158
-     * @throws EE_Error
159
-     */
160
-    public function is_remaining($DTT_ID = 0)
161
-    {
162
-        $num_remaining = $this->remaining($DTT_ID);
163
-        if ($num_remaining === 0) {
164
-            return false;
165
-        }
166
-        if ($num_remaining > 0 && $num_remaining < $this->min()) {
167
-            return false;
168
-        }
169
-        return true;
170
-    }
171
-
172
-
173
-    /**
174
-     * return the total number of tickets available for purchase
175
-     *
176
-     * @param  int $DTT_ID the primary key for a particular datetime.
177
-     *                     set to 0 for all related datetimes
178
-     * @return int
179
-     * @throws EE_Error
180
-     */
181
-    public function remaining($DTT_ID = 0)
182
-    {
183
-        return $this->real_quantity_on_ticket('saleable', $DTT_ID);
184
-    }
185
-
186
-
187
-    /**
188
-     * Gets min
189
-     *
190
-     * @return int
191
-     * @throws EE_Error
192
-     */
193
-    public function min()
194
-    {
195
-        return $this->get('TKT_min');
196
-    }
197
-
198
-
199
-    /**
200
-     * return if a ticket is no longer available cause its available dates have expired.
201
-     *
202
-     * @return boolean
203
-     * @throws EE_Error
204
-     */
205
-    public function is_expired()
206
-    {
207
-        return ($this->get_raw('TKT_end_date') < time());
208
-    }
209
-
210
-
211
-    /**
212
-     * Return if a ticket is yet to go on sale or not
213
-     *
214
-     * @return boolean
215
-     * @throws EE_Error
216
-     */
217
-    public function is_pending()
218
-    {
219
-        return ($this->get_raw('TKT_start_date') > time());
220
-    }
221
-
222
-
223
-    /**
224
-     * Return if a ticket is on sale or not
225
-     *
226
-     * @return boolean
227
-     * @throws EE_Error
228
-     */
229
-    public function is_on_sale()
230
-    {
231
-        return ($this->get_raw('TKT_start_date') < time() && $this->get_raw('TKT_end_date') > time());
232
-    }
233
-
234
-
235
-    /**
236
-     * This returns the chronologically last datetime that this ticket is associated with
237
-     *
238
-     * @param string $dt_frmt
239
-     * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
240
-     *                            the end date ie: Jan 01 "to" Dec 31
241
-     * @return string
242
-     * @throws EE_Error
243
-     */
244
-    public function date_range($dt_frmt = '', $conjunction = ' - ')
245
-    {
246
-        $first_date = $this->first_datetime() instanceof EE_Datetime ? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
247
-            : '';
248
-        $last_date = $this->last_datetime() instanceof EE_Datetime ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $dt_frmt) : '';
249
-
250
-        return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
251
-    }
252
-
253
-
254
-    /**
255
-     * This returns the chronologically first datetime that this ticket is associated with
256
-     *
257
-     * @return EE_Datetime
258
-     * @throws EE_Error
259
-     */
260
-    public function first_datetime()
261
-    {
262
-        $datetimes = $this->datetimes(array('limit' => 1));
263
-        return reset($datetimes);
264
-    }
265
-
266
-
267
-    /**
268
-     * Gets all the datetimes this ticket can be used for attending.
269
-     * Unless otherwise specified, orders datetimes by start date.
270
-     *
271
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
272
-     * @return EE_Datetime[]|EE_Base_Class[]
273
-     * @throws EE_Error
274
-     */
275
-    public function datetimes($query_params = array())
276
-    {
277
-        if (! isset($query_params['order_by'])) {
278
-            $query_params['order_by']['DTT_order'] = 'ASC';
279
-        }
280
-        return $this->get_many_related('Datetime', $query_params);
281
-    }
282
-
283
-
284
-    /**
285
-     * This returns the chronologically last datetime that this ticket is associated with
286
-     *
287
-     * @return EE_Datetime
288
-     * @throws EE_Error
289
-     */
290
-    public function last_datetime()
291
-    {
292
-        $datetimes = $this->datetimes(array('limit' => 1, 'order_by' => array('DTT_EVT_start' => 'DESC')));
293
-        return end($datetimes);
294
-    }
295
-
296
-
297
-    /**
298
-     * This returns the total tickets sold depending on the given parameters.
299
-     *
300
-     * @param  string $what   Can be one of two options: 'ticket', 'datetime'.
301
-     *                        'ticket' = total ticket sales for all datetimes this ticket is related to
302
-     *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
303
-     *                        'datetime' = total ticket sales in the datetime_ticket table.
304
-     *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
305
-     *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
306
-     * @param  int    $dtt_id [optional] include the dtt_id with $what = 'datetime'.
307
-     * @return mixed (array|int)          how many tickets have sold
308
-     * @throws EE_Error
309
-     */
310
-    public function tickets_sold($what = 'ticket', $dtt_id = null)
311
-    {
312
-        $total = 0;
313
-        $tickets_sold = $this->_all_tickets_sold();
314
-        switch ($what) {
315
-            case 'ticket':
316
-                return $tickets_sold['ticket'];
317
-                break;
318
-            case 'datetime':
319
-                if (empty($tickets_sold['datetime'])) {
320
-                    return $total;
321
-                }
322
-                if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
323
-                    EE_Error::add_error(
324
-                        __(
325
-                            '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?',
326
-                            'event_espresso'
327
-                        ),
328
-                        __FILE__,
329
-                        __FUNCTION__,
330
-                        __LINE__
331
-                    );
332
-                    return $total;
333
-                }
334
-                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
335
-                break;
336
-            default:
337
-                return $total;
338
-        }
339
-    }
340
-
341
-
342
-    /**
343
-     * This returns an array indexed by datetime_id for tickets sold with this ticket.
344
-     *
345
-     * @return EE_Ticket[]
346
-     * @throws EE_Error
347
-     */
348
-    protected function _all_tickets_sold()
349
-    {
350
-        $datetimes = $this->get_many_related('Datetime');
351
-        $tickets_sold = array();
352
-        if (! empty($datetimes)) {
353
-            foreach ($datetimes as $datetime) {
354
-                $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
355
-            }
356
-        }
357
-        // Tickets sold
358
-        $tickets_sold['ticket'] = $this->sold();
359
-        return $tickets_sold;
360
-    }
361
-
362
-
363
-    /**
364
-     * This returns the base price object for the ticket.
365
-     *
366
-     * @param  bool $return_array whether to return as an array indexed by price id or just the object.
367
-     * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
368
-     * @throws EE_Error
369
-     */
370
-    public function base_price($return_array = false)
371
-    {
372
-        $_where = array('Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price);
373
-        return $return_array
374
-            ? $this->get_many_related('Price', array($_where))
375
-            : $this->get_first_related('Price', array($_where));
376
-    }
377
-
378
-
379
-    /**
380
-     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
381
-     *
382
-     * @access public
383
-     * @return EE_Price[]
384
-     * @throws EE_Error
385
-     */
386
-    public function price_modifiers()
387
-    {
388
-        $query_params = array(
389
-            0 => array(
390
-                'Price_Type.PBT_ID' => array(
391
-                    'NOT IN',
392
-                    array(EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax),
393
-                ),
394
-            ),
395
-        );
396
-        return $this->prices($query_params);
397
-    }
398
-
399
-
400
-    /**
401
-     * Gets all the prices that combine to form the final price of this ticket
402
-     *
403
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
404
-     * @return EE_Price[]|EE_Base_Class[]
405
-     * @throws EE_Error
406
-     */
407
-    public function prices($query_params = array())
408
-    {
409
-        return $this->get_many_related('Price', $query_params);
410
-    }
411
-
412
-
413
-    /**
414
-     * Gets all the ticket applicabilities (ie, relations between datetimes and tickets)
415
-     *
416
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
417
-     * @return EE_Datetime_Ticket|EE_Base_Class[]
418
-     * @throws EE_Error
419
-     */
420
-    public function datetime_tickets($query_params = array())
421
-    {
422
-        return $this->get_many_related('Datetime_Ticket', $query_params);
423
-    }
424
-
425
-
426
-    /**
427
-     * Gets all the datetimes from the db ordered by DTT_order
428
-     *
429
-     * @param boolean $show_expired
430
-     * @param boolean $show_deleted
431
-     * @return EE_Datetime[]
432
-     * @throws EE_Error
433
-     */
434
-    public function datetimes_ordered($show_expired = true, $show_deleted = false)
435
-    {
436
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
437
-            $this->ID(),
438
-            $show_expired,
439
-            $show_deleted
440
-        );
441
-    }
442
-
443
-
444
-    /**
445
-     * Gets ID
446
-     *
447
-     * @return string
448
-     * @throws EE_Error
449
-     */
450
-    public function ID()
451
-    {
452
-        return $this->get('TKT_ID');
453
-    }
454
-
455
-
456
-    /**
457
-     * get the author of the ticket.
458
-     *
459
-     * @since 4.5.0
460
-     * @return int
461
-     * @throws EE_Error
462
-     */
463
-    public function wp_user()
464
-    {
465
-        return $this->get('TKT_wp_user');
466
-    }
467
-
468
-
469
-    /**
470
-     * Gets the template for the ticket
471
-     *
472
-     * @return EE_Ticket_Template|EE_Base_Class
473
-     * @throws EE_Error
474
-     */
475
-    public function template()
476
-    {
477
-        return $this->get_first_related('Ticket_Template');
478
-    }
479
-
480
-
481
-    /**
482
-     * Simply returns an array of EE_Price objects that are taxes.
483
-     *
484
-     * @return EE_Price[]
485
-     * @throws EE_Error
486
-     */
487
-    public function get_ticket_taxes_for_admin()
488
-    {
489
-        return EE_Taxes::get_taxes_for_admin();
490
-    }
491
-
492
-
493
-    /**
494
-     * @return float
495
-     * @throws EE_Error
496
-     */
497
-    public function ticket_price()
498
-    {
499
-        return $this->get('TKT_price');
500
-    }
501
-
502
-
503
-    /**
504
-     * @return mixed
505
-     * @throws EE_Error
506
-     */
507
-    public function pretty_price()
508
-    {
509
-        return $this->get_pretty('TKT_price');
510
-    }
511
-
512
-
513
-    /**
514
-     * @return bool
515
-     * @throws EE_Error
516
-     */
517
-    public function is_free()
518
-    {
519
-        return $this->get_ticket_total_with_taxes() === (float) 0;
520
-    }
521
-
522
-
523
-    /**
524
-     * get_ticket_total_with_taxes
525
-     *
526
-     * @param bool $no_cache
527
-     * @return float
528
-     * @throws EE_Error
529
-     */
530
-    public function get_ticket_total_with_taxes($no_cache = false)
531
-    {
532
-        if ($this->_ticket_total_with_taxes === null || $no_cache) {
533
-            $this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
534
-        }
535
-        return (float) $this->_ticket_total_with_taxes;
536
-    }
537
-
538
-
539
-    public function ensure_TKT_Price_correct()
540
-    {
541
-        $this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
542
-        $this->save();
543
-    }
544
-
545
-
546
-    /**
547
-     * @return float
548
-     * @throws EE_Error
549
-     */
550
-    public function get_ticket_subtotal()
551
-    {
552
-        return EE_Taxes::get_subtotal_for_admin($this);
553
-    }
554
-
555
-
556
-    /**
557
-     * Returns the total taxes applied to this ticket
558
-     *
559
-     * @return float
560
-     * @throws EE_Error
561
-     */
562
-    public function get_ticket_taxes_total_for_admin()
563
-    {
564
-        return EE_Taxes::get_total_taxes_for_admin($this);
565
-    }
566
-
567
-
568
-    /**
569
-     * Sets name
570
-     *
571
-     * @param string $name
572
-     * @throws EE_Error
573
-     */
574
-    public function set_name($name)
575
-    {
576
-        $this->set('TKT_name', $name);
577
-    }
578
-
579
-
580
-    /**
581
-     * Gets description
582
-     *
583
-     * @return string
584
-     * @throws EE_Error
585
-     */
586
-    public function description()
587
-    {
588
-        return $this->get('TKT_description');
589
-    }
590
-
591
-
592
-    /**
593
-     * Sets description
594
-     *
595
-     * @param string $description
596
-     * @throws EE_Error
597
-     */
598
-    public function set_description($description)
599
-    {
600
-        $this->set('TKT_description', $description);
601
-    }
602
-
603
-
604
-    /**
605
-     * Gets start_date
606
-     *
607
-     * @param string $dt_frmt
608
-     * @param string $tm_frmt
609
-     * @return string
610
-     * @throws EE_Error
611
-     */
612
-    public function start_date($dt_frmt = '', $tm_frmt = '')
613
-    {
614
-        return $this->_get_datetime('TKT_start_date', $dt_frmt, $tm_frmt);
615
-    }
616
-
617
-
618
-    /**
619
-     * Sets start_date
620
-     *
621
-     * @param string $start_date
622
-     * @return void
623
-     * @throws EE_Error
624
-     */
625
-    public function set_start_date($start_date)
626
-    {
627
-        $this->_set_date_time('B', $start_date, 'TKT_start_date');
628
-    }
629
-
630
-
631
-    /**
632
-     * Gets end_date
633
-     *
634
-     * @param string $dt_frmt
635
-     * @param string $tm_frmt
636
-     * @return string
637
-     * @throws EE_Error
638
-     */
639
-    public function end_date($dt_frmt = '', $tm_frmt = '')
640
-    {
641
-        return $this->_get_datetime('TKT_end_date', $dt_frmt, $tm_frmt);
642
-    }
643
-
644
-
645
-    /**
646
-     * Sets end_date
647
-     *
648
-     * @param string $end_date
649
-     * @return void
650
-     * @throws EE_Error
651
-     */
652
-    public function set_end_date($end_date)
653
-    {
654
-        $this->_set_date_time('B', $end_date, 'TKT_end_date');
655
-    }
656
-
657
-
658
-    /**
659
-     * Sets sell until time
660
-     *
661
-     * @since 4.5.0
662
-     * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
663
-     * @throws EE_Error
664
-     */
665
-    public function set_end_time($time)
666
-    {
667
-        $this->_set_time_for($time, 'TKT_end_date');
668
-    }
669
-
670
-
671
-    /**
672
-     * Sets min
673
-     *
674
-     * @param int $min
675
-     * @return void
676
-     * @throws EE_Error
677
-     */
678
-    public function set_min($min)
679
-    {
680
-        $this->set('TKT_min', $min);
681
-    }
682
-
683
-
684
-    /**
685
-     * Gets max
686
-     *
687
-     * @return int
688
-     * @throws EE_Error
689
-     */
690
-    public function max()
691
-    {
692
-        return $this->get('TKT_max');
693
-    }
694
-
695
-
696
-    /**
697
-     * Sets max
698
-     *
699
-     * @param int $max
700
-     * @return void
701
-     * @throws EE_Error
702
-     */
703
-    public function set_max($max)
704
-    {
705
-        $this->set('TKT_max', $max);
706
-    }
707
-
708
-
709
-    /**
710
-     * Sets price
711
-     *
712
-     * @param float $price
713
-     * @return void
714
-     * @throws EE_Error
715
-     */
716
-    public function set_price($price)
717
-    {
718
-        $this->set('TKT_price', $price);
719
-    }
720
-
721
-
722
-    /**
723
-     * Gets sold
724
-     *
725
-     * @return int
726
-     * @throws EE_Error
727
-     */
728
-    public function sold()
729
-    {
730
-        return $this->get_raw('TKT_sold');
731
-    }
732
-
733
-
734
-    /**
735
-     * Sets sold
736
-     *
737
-     * @param int $sold
738
-     * @return void
739
-     * @throws EE_Error
740
-     */
741
-    public function set_sold($sold)
742
-    {
743
-        // sold can not go below zero
744
-        $sold = max(0, $sold);
745
-        $this->set('TKT_sold', $sold);
746
-    }
747
-
748
-
749
-    /**
750
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
751
-     * associated datetimes.
752
-     *
753
-     * @since $VID:$
754
-     * @param int $qty
755
-     * @return boolean
756
-     * @throws EE_Error
757
-     * @throws InvalidArgumentException
758
-     * @throws InvalidDataTypeException
759
-     * @throws InvalidInterfaceException
760
-     * @throws ReflectionException
761
-     */
762
-    public function increaseSold($qty = 1)
763
-    {
764
-        $qty = absint($qty);
765
-        // increment sold and decrement reserved datetime quantities simultaneously
766
-        // don't worry about failures, because they must have already had a spot reserved
767
-        $this->increaseSoldForDatetimes($qty);
768
-        // Increment and decrement ticket quantities simultaneously
769
-        $success = $this->adjustNumericFieldsInDb(
770
-            [
771
-                'TKT_reserved' => $qty * -1,
772
-                'TKT_sold' => $qty
773
-            ]
774
-        );
775
-        do_action(
776
-            'AHEE__EE_Ticket__increase_sold',
777
-            $this,
778
-            $qty,
779
-            $this->sold(),
780
-            $success
781
-        );
782
-        return $success;
783
-    }
784
-
785
-    /**
786
-     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
787
-     *
788
-     * @since $VID:$
789
-     * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
790
-     *             Negative means to decreases old counts (and increase reserved counts).
791
-     * @param EE_Datetime[] $datetimes
792
-     * @throws EE_Error
793
-     * @throws InvalidArgumentException
794
-     * @throws InvalidDataTypeException
795
-     * @throws InvalidInterfaceException
796
-     * @throws ReflectionException
797
-     */
798
-    protected function increaseSoldForDatetimes($qty, array $datetimes = [])
799
-    {
800
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
801
-        foreach ($datetimes as $datetime) {
802
-            $datetime->increaseSold($qty);
803
-        }
804
-    }
805
-
806
-
807
-
808
-    /**
809
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
810
-     * DB and then updates the model objects.
811
-     * Does not affect the reserved counts.
812
-     *
813
-     * @since $VID:$
814
-     * @param int $qty
815
-     * @return boolean
816
-     * @throws EE_Error
817
-     * @throws InvalidArgumentException
818
-     * @throws InvalidDataTypeException
819
-     * @throws InvalidInterfaceException
820
-     * @throws ReflectionException
821
-     */
822
-    public function decreaseSold($qty = 1)
823
-    {
824
-        $qty = absint($qty);
825
-        $this->decreaseSoldForDatetimes($qty);
826
-        $success = $this->adjustNumericFieldsInDb(
827
-            [
828
-                'TKT_sold' => $qty * -1
829
-            ]
830
-        );
831
-        do_action(
832
-            'AHEE__EE_Ticket__decrease_sold',
833
-            $this,
834
-            $qty,
835
-            $this->sold(),
836
-            $success
837
-        );
838
-        return $success;
839
-    }
840
-
841
-
842
-    /**
843
-     * Decreases sold on related datetimes
844
-     *
845
-     * @since $VID:$
846
-     * @param int $qty
847
-     * @param EE_Datetime[] $datetimes
848
-     * @return void
849
-     * @throws EE_Error
850
-     * @throws InvalidArgumentException
851
-     * @throws InvalidDataTypeException
852
-     * @throws InvalidInterfaceException
853
-     * @throws ReflectionException
854
-     */
855
-    protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
856
-    {
857
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
858
-        if (is_array($datetimes)) {
859
-            foreach ($datetimes as $datetime) {
860
-                if ($datetime instanceof EE_Datetime) {
861
-                    $datetime->decreaseSold($qty);
862
-                }
863
-            }
864
-        }
865
-    }
866
-
867
-
868
-    /**
869
-     * Gets qty of reserved tickets
870
-     *
871
-     * @return int
872
-     * @throws EE_Error
873
-     */
874
-    public function reserved()
875
-    {
876
-        return $this->get_raw('TKT_reserved');
877
-    }
878
-
879
-
880
-    /**
881
-     * Sets reserved
882
-     *
883
-     * @param int $reserved
884
-     * @return void
885
-     * @throws EE_Error
886
-     */
887
-    public function set_reserved($reserved)
888
-    {
889
-        // reserved can not go below zero
890
-        $reserved = max(0, (int) $reserved);
891
-        $this->set('TKT_reserved', $reserved);
892
-    }
893
-
894
-
895
-    /**
896
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
897
-     *
898
-     * @since $VID:$
899
-     * @param int    $qty
900
-     * @param string $source
901
-     * @return bool whether we successfully reserved the ticket or not.
902
-     * @throws EE_Error
903
-     * @throws InvalidArgumentException
904
-     * @throws ReflectionException
905
-     * @throws InvalidDataTypeException
906
-     * @throws InvalidInterfaceException
907
-     */
908
-    public function increaseReserved($qty = 1, $source = 'unknown')
909
-    {
910
-        $qty = absint($qty);
911
-        do_action(
912
-            'AHEE__EE_Ticket__increase_reserved__begin',
913
-            $this,
914
-            $qty,
915
-            $source
916
-        );
917
-        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
918
-        $success = false;
919
-        $datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
920
-        if ($datetimes_adjusted_successfully) {
921
-            $success = $this->incrementFieldConditionallyInDb(
922
-                'TKT_reserved',
923
-                'TKT_sold',
924
-                'TKT_qty',
925
-                $qty
926
-            );
927
-            if (! $success) {
928
-                // The datetimes were successfully bumped, but not the
929
-                // ticket. So we need to manually rollback the datetimes.
930
-                $this->decreaseReservedForDatetimes($qty);
931
-            }
932
-        }
933
-        do_action(
934
-            'AHEE__EE_Ticket__increase_reserved',
935
-            $this,
936
-            $qty,
937
-            $this->reserved(),
938
-            $success
939
-        );
940
-        return $success;
941
-    }
942
-
943
-
944
-    /**
945
-     * Increases reserved counts on related datetimes
946
-     *
947
-     * @since $VID:$
948
-     * @param int $qty
949
-     * @param EE_Datetime[] $datetimes
950
-     * @return boolean indicating success
951
-     * @throws EE_Error
952
-     * @throws InvalidArgumentException
953
-     * @throws InvalidDataTypeException
954
-     * @throws InvalidInterfaceException
955
-     * @throws ReflectionException
956
-     */
957
-    protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
958
-    {
959
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
960
-        $datetimes_updated = [];
961
-        $limit_exceeded = false;
962
-        if (is_array($datetimes)) {
963
-            foreach ($datetimes as $datetime) {
964
-                if ($datetime instanceof EE_Datetime) {
965
-                    if ($datetime->increaseReserved($qty)) {
966
-                        $datetimes_updated[] = $datetime;
967
-                    } else {
968
-                        $limit_exceeded = true;
969
-                        break;
970
-                    }
971
-                }
972
-            }
973
-            // If somewhere along the way we detected a datetime whose
974
-            // limit was exceeded, do a manual rollback.
975
-            if ($limit_exceeded) {
976
-                $this->decreaseReservedForDatetimes($qty, $datetimes_updated);
977
-                return false;
978
-            }
979
-        }
980
-        return true;
981
-    }
982
-
983
-
984
-    /**
985
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
986
-     *
987
-     * @since $VID:$
988
-     * @param int    $qty
989
-     * @param bool   $adjust_datetimes
990
-     * @param string $source
991
-     * @return boolean
992
-     * @throws EE_Error
993
-     * @throws InvalidArgumentException
994
-     * @throws ReflectionException
995
-     * @throws InvalidDataTypeException
996
-     * @throws InvalidInterfaceException
997
-     */
998
-    public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
999
-    {
1000
-        $qty = absint($qty);
1001
-        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1002
-        if ($adjust_datetimes) {
1003
-            $this->decreaseReservedForDatetimes($qty);
1004
-        }
1005
-        $success = $this->adjustNumericFieldsInDb(
1006
-            [
1007
-                'TKT_reserved' => $qty * -1
1008
-            ]
1009
-        );
1010
-        do_action(
1011
-            'AHEE__EE_Ticket__decrease_reserved',
1012
-            $this,
1013
-            $qty,
1014
-            $this->reserved(),
1015
-            $success
1016
-        );
1017
-        return $success;
1018
-    }
1019
-
1020
-
1021
-    /**
1022
-     * Decreases the reserved count on the specified datetimes.
1023
-     *
1024
-     * @since $VID:$
1025
-     * @param int           $qty
1026
-     * @param EE_Datetime[] $datetimes
1027
-     * @throws EE_Error
1028
-     * @throws InvalidArgumentException
1029
-     * @throws ReflectionException
1030
-     * @throws InvalidDataTypeException
1031
-     * @throws InvalidInterfaceException
1032
-     */
1033
-    protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1034
-    {
1035
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1036
-        foreach ($datetimes as $datetime) {
1037
-            if ($datetime instanceof EE_Datetime) {
1038
-                $datetime->decreaseReserved($qty);
1039
-            }
1040
-        }
1041
-    }
1042
-
1043
-
1044
-    /**
1045
-     * Gets ticket quantity
1046
-     *
1047
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1048
-     *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1049
-     *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1050
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1051
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1052
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1053
-     * @return int
1054
-     * @throws EE_Error
1055
-     */
1056
-    public function qty($context = '')
1057
-    {
1058
-        switch ($context) {
1059
-            case 'reg_limit':
1060
-                return $this->real_quantity_on_ticket();
1061
-            case 'saleable':
1062
-                return $this->real_quantity_on_ticket('saleable');
1063
-            default:
1064
-                return $this->get_raw('TKT_qty');
1065
-        }
1066
-    }
1067
-
1068
-
1069
-    /**
1070
-     * Gets ticket quantity
1071
-     *
1072
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1073
-     *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1074
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1075
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1076
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1077
-     * @param  int   $DTT_ID      the primary key for a particular datetime.
1078
-     *                            set to 0 for all related datetimes
1079
-     * @return int
1080
-     * @throws EE_Error
1081
-     */
1082
-    public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1083
-    {
1084
-        $raw = $this->get_raw('TKT_qty');
1085
-        // return immediately if it's zero
1086
-        if ($raw === 0) {
1087
-            return $raw;
1088
-        }
1089
-        // echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1090
-        // ensure qty doesn't exceed raw value for THIS ticket
1091
-        $qty = min(EE_INF, $raw);
1092
-        // echo "\n . qty: " . $qty . '<br />';
1093
-        // calculate this ticket's total sales and reservations
1094
-        $sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1095
-        // echo "\n . sold: " . $this->sold() . '<br />';
1096
-        // echo "\n . reserved: " . $this->reserved() . '<br />';
1097
-        // echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1098
-        // first we need to calculate the maximum number of tickets available for the datetime
1099
-        // do we want data for one datetime or all of them ?
1100
-        $query_params = $DTT_ID ? array(array('DTT_ID' => $DTT_ID)) : array();
1101
-        $datetimes = $this->datetimes($query_params);
1102
-        if (is_array($datetimes) && ! empty($datetimes)) {
1103
-            foreach ($datetimes as $datetime) {
1104
-                if ($datetime instanceof EE_Datetime) {
1105
-                    $datetime->refresh_from_db();
1106
-                    // echo "\n . . datetime name: " . $datetime->name() . '<br />';
1107
-                    // echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1108
-                    // initialize with no restrictions for each datetime
1109
-                    // but adjust datetime qty based on datetime reg limit
1110
-                    $datetime_qty = min(EE_INF, $datetime->reg_limit());
1111
-                    // echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1112
-                    // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1113
-                    // if we want the actual saleable amount, then we need to consider OTHER ticket sales
1114
-                    // and reservations for this datetime, that do NOT include sales and reservations
1115
-                    // for this ticket (so we add $this->sold() and $this->reserved() back in)
1116
-                    if ($context === 'saleable') {
1117
-                        $datetime_qty = max(
1118
-                            $datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1119
-                            0
1120
-                        );
1121
-                        // echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1122
-                        // echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1123
-                        // echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1124
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1125
-                        $datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1126
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1127
-                    }
1128
-                    $qty = min($datetime_qty, $qty);
1129
-                    // echo "\n . . qty: " . $qty . '<br />';
1130
-                }
1131
-            }
1132
-        }
1133
-        // NOW that we know the  maximum number of tickets available for the datetime
1134
-        // we can finally factor in the details for this specific ticket
1135
-        if ($qty > 0 && $context === 'saleable') {
1136
-            // and subtract the sales for THIS ticket
1137
-            $qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1138
-            // echo "\n . qty: " . $qty . '<br />';
1139
-        }
1140
-        // echo "\nFINAL QTY: " . $qty . "<br /><br />";
1141
-        return $qty;
1142
-    }
1143
-
1144
-
1145
-    /**
1146
-     * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1147
-     *
1148
-     * @param int $qty
1149
-     * @return void
1150
-     * @throws EE_Error
1151
-     */
1152
-    public function set_qty($qty)
1153
-    {
1154
-        $datetimes = $this->datetimes();
1155
-        foreach ($datetimes as $datetime) {
1156
-            if ($datetime instanceof EE_Datetime) {
1157
-                $qty = min($qty, $datetime->reg_limit());
1158
-            }
1159
-        }
1160
-        $this->set('TKT_qty', $qty);
1161
-    }
1162
-
1163
-
1164
-    /**
1165
-     * Gets uses
1166
-     *
1167
-     * @return int
1168
-     * @throws EE_Error
1169
-     */
1170
-    public function uses()
1171
-    {
1172
-        return $this->get('TKT_uses');
1173
-    }
1174
-
1175
-
1176
-    /**
1177
-     * Sets uses
1178
-     *
1179
-     * @param int $uses
1180
-     * @return void
1181
-     * @throws EE_Error
1182
-     */
1183
-    public function set_uses($uses)
1184
-    {
1185
-        $this->set('TKT_uses', $uses);
1186
-    }
1187
-
1188
-
1189
-    /**
1190
-     * returns whether ticket is required or not.
1191
-     *
1192
-     * @return boolean
1193
-     * @throws EE_Error
1194
-     */
1195
-    public function required()
1196
-    {
1197
-        return $this->get('TKT_required');
1198
-    }
1199
-
1200
-
1201
-    /**
1202
-     * sets the TKT_required property
1203
-     *
1204
-     * @param boolean $required
1205
-     * @return void
1206
-     * @throws EE_Error
1207
-     */
1208
-    public function set_required($required)
1209
-    {
1210
-        $this->set('TKT_required', $required);
1211
-    }
1212
-
1213
-
1214
-    /**
1215
-     * Gets taxable
1216
-     *
1217
-     * @return boolean
1218
-     * @throws EE_Error
1219
-     */
1220
-    public function taxable()
1221
-    {
1222
-        return $this->get('TKT_taxable');
1223
-    }
1224
-
1225
-
1226
-    /**
1227
-     * Sets taxable
1228
-     *
1229
-     * @param boolean $taxable
1230
-     * @return void
1231
-     * @throws EE_Error
1232
-     */
1233
-    public function set_taxable($taxable)
1234
-    {
1235
-        $this->set('TKT_taxable', $taxable);
1236
-    }
1237
-
1238
-
1239
-    /**
1240
-     * Gets is_default
1241
-     *
1242
-     * @return boolean
1243
-     * @throws EE_Error
1244
-     */
1245
-    public function is_default()
1246
-    {
1247
-        return $this->get('TKT_is_default');
1248
-    }
1249
-
1250
-
1251
-    /**
1252
-     * Sets is_default
1253
-     *
1254
-     * @param boolean $is_default
1255
-     * @return void
1256
-     * @throws EE_Error
1257
-     */
1258
-    public function set_is_default($is_default)
1259
-    {
1260
-        $this->set('TKT_is_default', $is_default);
1261
-    }
1262
-
1263
-
1264
-    /**
1265
-     * Gets order
1266
-     *
1267
-     * @return int
1268
-     * @throws EE_Error
1269
-     */
1270
-    public function order()
1271
-    {
1272
-        return $this->get('TKT_order');
1273
-    }
1274
-
1275
-
1276
-    /**
1277
-     * Sets order
1278
-     *
1279
-     * @param int $order
1280
-     * @return void
1281
-     * @throws EE_Error
1282
-     */
1283
-    public function set_order($order)
1284
-    {
1285
-        $this->set('TKT_order', $order);
1286
-    }
1287
-
1288
-
1289
-    /**
1290
-     * Gets row
1291
-     *
1292
-     * @return int
1293
-     * @throws EE_Error
1294
-     */
1295
-    public function row()
1296
-    {
1297
-        return $this->get('TKT_row');
1298
-    }
1299
-
1300
-
1301
-    /**
1302
-     * Sets row
1303
-     *
1304
-     * @param int $row
1305
-     * @return void
1306
-     * @throws EE_Error
1307
-     */
1308
-    public function set_row($row)
1309
-    {
1310
-        $this->set('TKT_row', $row);
1311
-    }
1312
-
1313
-
1314
-    /**
1315
-     * Gets deleted
1316
-     *
1317
-     * @return boolean
1318
-     * @throws EE_Error
1319
-     */
1320
-    public function deleted()
1321
-    {
1322
-        return $this->get('TKT_deleted');
1323
-    }
1324
-
1325
-
1326
-    /**
1327
-     * Sets deleted
1328
-     *
1329
-     * @param boolean $deleted
1330
-     * @return void
1331
-     * @throws EE_Error
1332
-     */
1333
-    public function set_deleted($deleted)
1334
-    {
1335
-        $this->set('TKT_deleted', $deleted);
1336
-    }
1337
-
1338
-
1339
-    /**
1340
-     * Gets parent
1341
-     *
1342
-     * @return int
1343
-     * @throws EE_Error
1344
-     */
1345
-    public function parent_ID()
1346
-    {
1347
-        return $this->get('TKT_parent');
1348
-    }
1349
-
1350
-
1351
-    /**
1352
-     * Sets parent
1353
-     *
1354
-     * @param int $parent
1355
-     * @return void
1356
-     * @throws EE_Error
1357
-     */
1358
-    public function set_parent_ID($parent)
1359
-    {
1360
-        $this->set('TKT_parent', $parent);
1361
-    }
1362
-
1363
-
1364
-    /**
1365
-     * Gets a string which is handy for showing in gateways etc that describes the ticket.
1366
-     *
1367
-     * @return string
1368
-     * @throws EE_Error
1369
-     */
1370
-    public function name_and_info()
1371
-    {
1372
-        $times = array();
1373
-        foreach ($this->datetimes() as $datetime) {
1374
-            $times[] = $datetime->start_date_and_time();
1375
-        }
1376
-        return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1377
-    }
1378
-
1379
-
1380
-    /**
1381
-     * Gets name
1382
-     *
1383
-     * @return string
1384
-     * @throws EE_Error
1385
-     */
1386
-    public function name()
1387
-    {
1388
-        return $this->get('TKT_name');
1389
-    }
1390
-
1391
-
1392
-    /**
1393
-     * Gets price
1394
-     *
1395
-     * @return float
1396
-     * @throws EE_Error
1397
-     */
1398
-    public function price()
1399
-    {
1400
-        return $this->get('TKT_price');
1401
-    }
1402
-
1403
-
1404
-    /**
1405
-     * Gets all the registrations for this ticket
1406
-     *
1407
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1408
-     * @return EE_Registration[]|EE_Base_Class[]
1409
-     * @throws EE_Error
1410
-     */
1411
-    public function registrations($query_params = array())
1412
-    {
1413
-        return $this->get_many_related('Registration', $query_params);
1414
-    }
1415
-
1416
-
1417
-    /**
1418
-     * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1419
-     *
1420
-     * @return int
1421
-     * @throws EE_Error
1422
-     */
1423
-    public function update_tickets_sold()
1424
-    {
1425
-        $count_regs_for_this_ticket = $this->count_registrations(
1426
-            array(
1427
-                array(
1428
-                    'STS_ID'      => EEM_Registration::status_id_approved,
1429
-                    'REG_deleted' => 0,
1430
-                ),
1431
-            )
1432
-        );
1433
-        $this->set_sold($count_regs_for_this_ticket);
1434
-        $this->save();
1435
-        return $count_regs_for_this_ticket;
1436
-    }
1437
-
1438
-
1439
-    /**
1440
-     * Counts the registrations for this ticket
1441
-     *
1442
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1443
-     * @return int
1444
-     */
1445
-    public function count_registrations($query_params = array())
1446
-    {
1447
-        return $this->count_related('Registration', $query_params);
1448
-    }
1449
-
1450
-
1451
-    /**
1452
-     * Implementation for EEI_Has_Icon interface method.
1453
-     *
1454
-     * @see EEI_Visual_Representation for comments
1455
-     * @return string
1456
-     */
1457
-    public function get_icon()
1458
-    {
1459
-        return '<span class="dashicons dashicons-tickets-alt"></span>';
1460
-    }
1461
-
1462
-
1463
-    /**
1464
-     * Implementation of the EEI_Event_Relation interface method
1465
-     *
1466
-     * @see EEI_Event_Relation for comments
1467
-     * @return EE_Event
1468
-     * @throws EE_Error
1469
-     * @throws UnexpectedEntityException
1470
-     */
1471
-    public function get_related_event()
1472
-    {
1473
-        // get one datetime to use for getting the event
1474
-        $datetime = $this->first_datetime();
1475
-        if (! $datetime instanceof \EE_Datetime) {
1476
-            throw new UnexpectedEntityException(
1477
-                $datetime,
1478
-                'EE_Datetime',
1479
-                sprintf(
1480
-                    __('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1481
-                    $this->name()
1482
-                )
1483
-            );
1484
-        }
1485
-        $event = $datetime->event();
1486
-        if (! $event instanceof \EE_Event) {
1487
-            throw new UnexpectedEntityException(
1488
-                $event,
1489
-                'EE_Event',
1490
-                sprintf(
1491
-                    __('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1492
-                    $this->name()
1493
-                )
1494
-            );
1495
-        }
1496
-        return $event;
1497
-    }
1498
-
1499
-
1500
-    /**
1501
-     * Implementation of the EEI_Event_Relation interface method
1502
-     *
1503
-     * @see EEI_Event_Relation for comments
1504
-     * @return string
1505
-     * @throws UnexpectedEntityException
1506
-     * @throws EE_Error
1507
-     */
1508
-    public function get_event_name()
1509
-    {
1510
-        $event = $this->get_related_event();
1511
-        return $event instanceof EE_Event ? $event->name() : '';
1512
-    }
1513
-
1514
-
1515
-    /**
1516
-     * Implementation of the EEI_Event_Relation interface method
1517
-     *
1518
-     * @see EEI_Event_Relation for comments
1519
-     * @return int
1520
-     * @throws UnexpectedEntityException
1521
-     * @throws EE_Error
1522
-     */
1523
-    public function get_event_ID()
1524
-    {
1525
-        $event = $this->get_related_event();
1526
-        return $event instanceof EE_Event ? $event->ID() : 0;
1527
-    }
1528
-
1529
-
1530
-    /**
1531
-     * This simply returns whether a ticket can be permanently deleted or not.
1532
-     * The criteria for determining this is whether the ticket has any related registrations.
1533
-     * If there are none then it can be permanently deleted.
1534
-     *
1535
-     * @return bool
1536
-     */
1537
-    public function is_permanently_deleteable()
1538
-    {
1539
-        return $this->count_registrations() === 0;
1540
-    }
1541
-
1542
-
1543
-    /*******************************************************************
17
+	/**
18
+	 * The following constants are used by the ticket_status() method to indicate whether a ticket is on sale or not.
19
+	 */
20
+	const sold_out = 'TKS';
21
+
22
+	/**
23
+	 *
24
+	 */
25
+	const expired = 'TKE';
26
+
27
+	/**
28
+	 *
29
+	 */
30
+	const archived = 'TKA';
31
+
32
+	/**
33
+	 *
34
+	 */
35
+	const pending = 'TKP';
36
+
37
+	/**
38
+	 *
39
+	 */
40
+	const onsale = 'TKO';
41
+
42
+	/**
43
+	 * extra meta key for tracking ticket reservations
44
+	 *
45
+	 * @type string
46
+	 */
47
+	const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
48
+
49
+	/**
50
+	 * cached result from method of the same name
51
+	 *
52
+	 * @var float $_ticket_total_with_taxes
53
+	 */
54
+	private $_ticket_total_with_taxes;
55
+
56
+
57
+	/**
58
+	 * @param array  $props_n_values          incoming values
59
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
60
+	 *                                        used.)
61
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
62
+	 *                                        date_format and the second value is the time format
63
+	 * @return EE_Ticket
64
+	 * @throws EE_Error
65
+	 */
66
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
67
+	{
68
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
69
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
70
+	}
71
+
72
+
73
+	/**
74
+	 * @param array  $props_n_values  incoming values from the database
75
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
76
+	 *                                the website will be used.
77
+	 * @return EE_Ticket
78
+	 * @throws EE_Error
79
+	 */
80
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
81
+	{
82
+		return new self($props_n_values, true, $timezone);
83
+	}
84
+
85
+
86
+	/**
87
+	 * @return bool
88
+	 * @throws EE_Error
89
+	 */
90
+	public function parent()
91
+	{
92
+		return $this->get('TKT_parent');
93
+	}
94
+
95
+
96
+	/**
97
+	 * return if a ticket has quantities available for purchase
98
+	 *
99
+	 * @param  int $DTT_ID the primary key for a particular datetime
100
+	 * @return boolean
101
+	 * @throws EE_Error
102
+	 */
103
+	public function available($DTT_ID = 0)
104
+	{
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
+	 * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
121
+	 *
122
+	 * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
123
+	 *                               relevant status const
124
+	 * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
125
+	 *               further processing
126
+	 * @return mixed status int if the display string isn't requested
127
+	 * @throws EE_Error
128
+	 */
129
+	public function ticket_status($display = false, $remaining = null)
130
+	{
131
+		$remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
132
+		if (! $remaining) {
133
+			return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
134
+		}
135
+		if ($this->get('TKT_deleted')) {
136
+			return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
137
+		}
138
+		if ($this->is_expired()) {
139
+			return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
140
+		}
141
+		if ($this->is_pending()) {
142
+			return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
143
+		}
144
+		if ($this->is_on_sale()) {
145
+			return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
146
+		}
147
+		return '';
148
+	}
149
+
150
+
151
+	/**
152
+	 * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
153
+	 * considering ALL the factors used for figuring that out.
154
+	 *
155
+	 * @access public
156
+	 * @param  int $DTT_ID if an int above 0 is included here then we get a specific dtt.
157
+	 * @return boolean         true = tickets remaining, false not.
158
+	 * @throws EE_Error
159
+	 */
160
+	public function is_remaining($DTT_ID = 0)
161
+	{
162
+		$num_remaining = $this->remaining($DTT_ID);
163
+		if ($num_remaining === 0) {
164
+			return false;
165
+		}
166
+		if ($num_remaining > 0 && $num_remaining < $this->min()) {
167
+			return false;
168
+		}
169
+		return true;
170
+	}
171
+
172
+
173
+	/**
174
+	 * return the total number of tickets available for purchase
175
+	 *
176
+	 * @param  int $DTT_ID the primary key for a particular datetime.
177
+	 *                     set to 0 for all related datetimes
178
+	 * @return int
179
+	 * @throws EE_Error
180
+	 */
181
+	public function remaining($DTT_ID = 0)
182
+	{
183
+		return $this->real_quantity_on_ticket('saleable', $DTT_ID);
184
+	}
185
+
186
+
187
+	/**
188
+	 * Gets min
189
+	 *
190
+	 * @return int
191
+	 * @throws EE_Error
192
+	 */
193
+	public function min()
194
+	{
195
+		return $this->get('TKT_min');
196
+	}
197
+
198
+
199
+	/**
200
+	 * return if a ticket is no longer available cause its available dates have expired.
201
+	 *
202
+	 * @return boolean
203
+	 * @throws EE_Error
204
+	 */
205
+	public function is_expired()
206
+	{
207
+		return ($this->get_raw('TKT_end_date') < time());
208
+	}
209
+
210
+
211
+	/**
212
+	 * Return if a ticket is yet to go on sale or not
213
+	 *
214
+	 * @return boolean
215
+	 * @throws EE_Error
216
+	 */
217
+	public function is_pending()
218
+	{
219
+		return ($this->get_raw('TKT_start_date') > time());
220
+	}
221
+
222
+
223
+	/**
224
+	 * Return if a ticket is on sale or not
225
+	 *
226
+	 * @return boolean
227
+	 * @throws EE_Error
228
+	 */
229
+	public function is_on_sale()
230
+	{
231
+		return ($this->get_raw('TKT_start_date') < time() && $this->get_raw('TKT_end_date') > time());
232
+	}
233
+
234
+
235
+	/**
236
+	 * This returns the chronologically last datetime that this ticket is associated with
237
+	 *
238
+	 * @param string $dt_frmt
239
+	 * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
240
+	 *                            the end date ie: Jan 01 "to" Dec 31
241
+	 * @return string
242
+	 * @throws EE_Error
243
+	 */
244
+	public function date_range($dt_frmt = '', $conjunction = ' - ')
245
+	{
246
+		$first_date = $this->first_datetime() instanceof EE_Datetime ? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
247
+			: '';
248
+		$last_date = $this->last_datetime() instanceof EE_Datetime ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $dt_frmt) : '';
249
+
250
+		return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
251
+	}
252
+
253
+
254
+	/**
255
+	 * This returns the chronologically first datetime that this ticket is associated with
256
+	 *
257
+	 * @return EE_Datetime
258
+	 * @throws EE_Error
259
+	 */
260
+	public function first_datetime()
261
+	{
262
+		$datetimes = $this->datetimes(array('limit' => 1));
263
+		return reset($datetimes);
264
+	}
265
+
266
+
267
+	/**
268
+	 * Gets all the datetimes this ticket can be used for attending.
269
+	 * Unless otherwise specified, orders datetimes by start date.
270
+	 *
271
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
272
+	 * @return EE_Datetime[]|EE_Base_Class[]
273
+	 * @throws EE_Error
274
+	 */
275
+	public function datetimes($query_params = array())
276
+	{
277
+		if (! isset($query_params['order_by'])) {
278
+			$query_params['order_by']['DTT_order'] = 'ASC';
279
+		}
280
+		return $this->get_many_related('Datetime', $query_params);
281
+	}
282
+
283
+
284
+	/**
285
+	 * This returns the chronologically last datetime that this ticket is associated with
286
+	 *
287
+	 * @return EE_Datetime
288
+	 * @throws EE_Error
289
+	 */
290
+	public function last_datetime()
291
+	{
292
+		$datetimes = $this->datetimes(array('limit' => 1, 'order_by' => array('DTT_EVT_start' => 'DESC')));
293
+		return end($datetimes);
294
+	}
295
+
296
+
297
+	/**
298
+	 * This returns the total tickets sold depending on the given parameters.
299
+	 *
300
+	 * @param  string $what   Can be one of two options: 'ticket', 'datetime'.
301
+	 *                        'ticket' = total ticket sales for all datetimes this ticket is related to
302
+	 *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
303
+	 *                        'datetime' = total ticket sales in the datetime_ticket table.
304
+	 *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
305
+	 *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
306
+	 * @param  int    $dtt_id [optional] include the dtt_id with $what = 'datetime'.
307
+	 * @return mixed (array|int)          how many tickets have sold
308
+	 * @throws EE_Error
309
+	 */
310
+	public function tickets_sold($what = 'ticket', $dtt_id = null)
311
+	{
312
+		$total = 0;
313
+		$tickets_sold = $this->_all_tickets_sold();
314
+		switch ($what) {
315
+			case 'ticket':
316
+				return $tickets_sold['ticket'];
317
+				break;
318
+			case 'datetime':
319
+				if (empty($tickets_sold['datetime'])) {
320
+					return $total;
321
+				}
322
+				if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
323
+					EE_Error::add_error(
324
+						__(
325
+							'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?',
326
+							'event_espresso'
327
+						),
328
+						__FILE__,
329
+						__FUNCTION__,
330
+						__LINE__
331
+					);
332
+					return $total;
333
+				}
334
+				return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
335
+				break;
336
+			default:
337
+				return $total;
338
+		}
339
+	}
340
+
341
+
342
+	/**
343
+	 * This returns an array indexed by datetime_id for tickets sold with this ticket.
344
+	 *
345
+	 * @return EE_Ticket[]
346
+	 * @throws EE_Error
347
+	 */
348
+	protected function _all_tickets_sold()
349
+	{
350
+		$datetimes = $this->get_many_related('Datetime');
351
+		$tickets_sold = array();
352
+		if (! empty($datetimes)) {
353
+			foreach ($datetimes as $datetime) {
354
+				$tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
355
+			}
356
+		}
357
+		// Tickets sold
358
+		$tickets_sold['ticket'] = $this->sold();
359
+		return $tickets_sold;
360
+	}
361
+
362
+
363
+	/**
364
+	 * This returns the base price object for the ticket.
365
+	 *
366
+	 * @param  bool $return_array whether to return as an array indexed by price id or just the object.
367
+	 * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
368
+	 * @throws EE_Error
369
+	 */
370
+	public function base_price($return_array = false)
371
+	{
372
+		$_where = array('Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price);
373
+		return $return_array
374
+			? $this->get_many_related('Price', array($_where))
375
+			: $this->get_first_related('Price', array($_where));
376
+	}
377
+
378
+
379
+	/**
380
+	 * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
381
+	 *
382
+	 * @access public
383
+	 * @return EE_Price[]
384
+	 * @throws EE_Error
385
+	 */
386
+	public function price_modifiers()
387
+	{
388
+		$query_params = array(
389
+			0 => array(
390
+				'Price_Type.PBT_ID' => array(
391
+					'NOT IN',
392
+					array(EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax),
393
+				),
394
+			),
395
+		);
396
+		return $this->prices($query_params);
397
+	}
398
+
399
+
400
+	/**
401
+	 * Gets all the prices that combine to form the final price of this ticket
402
+	 *
403
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
404
+	 * @return EE_Price[]|EE_Base_Class[]
405
+	 * @throws EE_Error
406
+	 */
407
+	public function prices($query_params = array())
408
+	{
409
+		return $this->get_many_related('Price', $query_params);
410
+	}
411
+
412
+
413
+	/**
414
+	 * Gets all the ticket applicabilities (ie, relations between datetimes and tickets)
415
+	 *
416
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
417
+	 * @return EE_Datetime_Ticket|EE_Base_Class[]
418
+	 * @throws EE_Error
419
+	 */
420
+	public function datetime_tickets($query_params = array())
421
+	{
422
+		return $this->get_many_related('Datetime_Ticket', $query_params);
423
+	}
424
+
425
+
426
+	/**
427
+	 * Gets all the datetimes from the db ordered by DTT_order
428
+	 *
429
+	 * @param boolean $show_expired
430
+	 * @param boolean $show_deleted
431
+	 * @return EE_Datetime[]
432
+	 * @throws EE_Error
433
+	 */
434
+	public function datetimes_ordered($show_expired = true, $show_deleted = false)
435
+	{
436
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
437
+			$this->ID(),
438
+			$show_expired,
439
+			$show_deleted
440
+		);
441
+	}
442
+
443
+
444
+	/**
445
+	 * Gets ID
446
+	 *
447
+	 * @return string
448
+	 * @throws EE_Error
449
+	 */
450
+	public function ID()
451
+	{
452
+		return $this->get('TKT_ID');
453
+	}
454
+
455
+
456
+	/**
457
+	 * get the author of the ticket.
458
+	 *
459
+	 * @since 4.5.0
460
+	 * @return int
461
+	 * @throws EE_Error
462
+	 */
463
+	public function wp_user()
464
+	{
465
+		return $this->get('TKT_wp_user');
466
+	}
467
+
468
+
469
+	/**
470
+	 * Gets the template for the ticket
471
+	 *
472
+	 * @return EE_Ticket_Template|EE_Base_Class
473
+	 * @throws EE_Error
474
+	 */
475
+	public function template()
476
+	{
477
+		return $this->get_first_related('Ticket_Template');
478
+	}
479
+
480
+
481
+	/**
482
+	 * Simply returns an array of EE_Price objects that are taxes.
483
+	 *
484
+	 * @return EE_Price[]
485
+	 * @throws EE_Error
486
+	 */
487
+	public function get_ticket_taxes_for_admin()
488
+	{
489
+		return EE_Taxes::get_taxes_for_admin();
490
+	}
491
+
492
+
493
+	/**
494
+	 * @return float
495
+	 * @throws EE_Error
496
+	 */
497
+	public function ticket_price()
498
+	{
499
+		return $this->get('TKT_price');
500
+	}
501
+
502
+
503
+	/**
504
+	 * @return mixed
505
+	 * @throws EE_Error
506
+	 */
507
+	public function pretty_price()
508
+	{
509
+		return $this->get_pretty('TKT_price');
510
+	}
511
+
512
+
513
+	/**
514
+	 * @return bool
515
+	 * @throws EE_Error
516
+	 */
517
+	public function is_free()
518
+	{
519
+		return $this->get_ticket_total_with_taxes() === (float) 0;
520
+	}
521
+
522
+
523
+	/**
524
+	 * get_ticket_total_with_taxes
525
+	 *
526
+	 * @param bool $no_cache
527
+	 * @return float
528
+	 * @throws EE_Error
529
+	 */
530
+	public function get_ticket_total_with_taxes($no_cache = false)
531
+	{
532
+		if ($this->_ticket_total_with_taxes === null || $no_cache) {
533
+			$this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
534
+		}
535
+		return (float) $this->_ticket_total_with_taxes;
536
+	}
537
+
538
+
539
+	public function ensure_TKT_Price_correct()
540
+	{
541
+		$this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
542
+		$this->save();
543
+	}
544
+
545
+
546
+	/**
547
+	 * @return float
548
+	 * @throws EE_Error
549
+	 */
550
+	public function get_ticket_subtotal()
551
+	{
552
+		return EE_Taxes::get_subtotal_for_admin($this);
553
+	}
554
+
555
+
556
+	/**
557
+	 * Returns the total taxes applied to this ticket
558
+	 *
559
+	 * @return float
560
+	 * @throws EE_Error
561
+	 */
562
+	public function get_ticket_taxes_total_for_admin()
563
+	{
564
+		return EE_Taxes::get_total_taxes_for_admin($this);
565
+	}
566
+
567
+
568
+	/**
569
+	 * Sets name
570
+	 *
571
+	 * @param string $name
572
+	 * @throws EE_Error
573
+	 */
574
+	public function set_name($name)
575
+	{
576
+		$this->set('TKT_name', $name);
577
+	}
578
+
579
+
580
+	/**
581
+	 * Gets description
582
+	 *
583
+	 * @return string
584
+	 * @throws EE_Error
585
+	 */
586
+	public function description()
587
+	{
588
+		return $this->get('TKT_description');
589
+	}
590
+
591
+
592
+	/**
593
+	 * Sets description
594
+	 *
595
+	 * @param string $description
596
+	 * @throws EE_Error
597
+	 */
598
+	public function set_description($description)
599
+	{
600
+		$this->set('TKT_description', $description);
601
+	}
602
+
603
+
604
+	/**
605
+	 * Gets start_date
606
+	 *
607
+	 * @param string $dt_frmt
608
+	 * @param string $tm_frmt
609
+	 * @return string
610
+	 * @throws EE_Error
611
+	 */
612
+	public function start_date($dt_frmt = '', $tm_frmt = '')
613
+	{
614
+		return $this->_get_datetime('TKT_start_date', $dt_frmt, $tm_frmt);
615
+	}
616
+
617
+
618
+	/**
619
+	 * Sets start_date
620
+	 *
621
+	 * @param string $start_date
622
+	 * @return void
623
+	 * @throws EE_Error
624
+	 */
625
+	public function set_start_date($start_date)
626
+	{
627
+		$this->_set_date_time('B', $start_date, 'TKT_start_date');
628
+	}
629
+
630
+
631
+	/**
632
+	 * Gets end_date
633
+	 *
634
+	 * @param string $dt_frmt
635
+	 * @param string $tm_frmt
636
+	 * @return string
637
+	 * @throws EE_Error
638
+	 */
639
+	public function end_date($dt_frmt = '', $tm_frmt = '')
640
+	{
641
+		return $this->_get_datetime('TKT_end_date', $dt_frmt, $tm_frmt);
642
+	}
643
+
644
+
645
+	/**
646
+	 * Sets end_date
647
+	 *
648
+	 * @param string $end_date
649
+	 * @return void
650
+	 * @throws EE_Error
651
+	 */
652
+	public function set_end_date($end_date)
653
+	{
654
+		$this->_set_date_time('B', $end_date, 'TKT_end_date');
655
+	}
656
+
657
+
658
+	/**
659
+	 * Sets sell until time
660
+	 *
661
+	 * @since 4.5.0
662
+	 * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
663
+	 * @throws EE_Error
664
+	 */
665
+	public function set_end_time($time)
666
+	{
667
+		$this->_set_time_for($time, 'TKT_end_date');
668
+	}
669
+
670
+
671
+	/**
672
+	 * Sets min
673
+	 *
674
+	 * @param int $min
675
+	 * @return void
676
+	 * @throws EE_Error
677
+	 */
678
+	public function set_min($min)
679
+	{
680
+		$this->set('TKT_min', $min);
681
+	}
682
+
683
+
684
+	/**
685
+	 * Gets max
686
+	 *
687
+	 * @return int
688
+	 * @throws EE_Error
689
+	 */
690
+	public function max()
691
+	{
692
+		return $this->get('TKT_max');
693
+	}
694
+
695
+
696
+	/**
697
+	 * Sets max
698
+	 *
699
+	 * @param int $max
700
+	 * @return void
701
+	 * @throws EE_Error
702
+	 */
703
+	public function set_max($max)
704
+	{
705
+		$this->set('TKT_max', $max);
706
+	}
707
+
708
+
709
+	/**
710
+	 * Sets price
711
+	 *
712
+	 * @param float $price
713
+	 * @return void
714
+	 * @throws EE_Error
715
+	 */
716
+	public function set_price($price)
717
+	{
718
+		$this->set('TKT_price', $price);
719
+	}
720
+
721
+
722
+	/**
723
+	 * Gets sold
724
+	 *
725
+	 * @return int
726
+	 * @throws EE_Error
727
+	 */
728
+	public function sold()
729
+	{
730
+		return $this->get_raw('TKT_sold');
731
+	}
732
+
733
+
734
+	/**
735
+	 * Sets sold
736
+	 *
737
+	 * @param int $sold
738
+	 * @return void
739
+	 * @throws EE_Error
740
+	 */
741
+	public function set_sold($sold)
742
+	{
743
+		// sold can not go below zero
744
+		$sold = max(0, $sold);
745
+		$this->set('TKT_sold', $sold);
746
+	}
747
+
748
+
749
+	/**
750
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
751
+	 * associated datetimes.
752
+	 *
753
+	 * @since $VID:$
754
+	 * @param int $qty
755
+	 * @return boolean
756
+	 * @throws EE_Error
757
+	 * @throws InvalidArgumentException
758
+	 * @throws InvalidDataTypeException
759
+	 * @throws InvalidInterfaceException
760
+	 * @throws ReflectionException
761
+	 */
762
+	public function increaseSold($qty = 1)
763
+	{
764
+		$qty = absint($qty);
765
+		// increment sold and decrement reserved datetime quantities simultaneously
766
+		// don't worry about failures, because they must have already had a spot reserved
767
+		$this->increaseSoldForDatetimes($qty);
768
+		// Increment and decrement ticket quantities simultaneously
769
+		$success = $this->adjustNumericFieldsInDb(
770
+			[
771
+				'TKT_reserved' => $qty * -1,
772
+				'TKT_sold' => $qty
773
+			]
774
+		);
775
+		do_action(
776
+			'AHEE__EE_Ticket__increase_sold',
777
+			$this,
778
+			$qty,
779
+			$this->sold(),
780
+			$success
781
+		);
782
+		return $success;
783
+	}
784
+
785
+	/**
786
+	 * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
787
+	 *
788
+	 * @since $VID:$
789
+	 * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
790
+	 *             Negative means to decreases old counts (and increase reserved counts).
791
+	 * @param EE_Datetime[] $datetimes
792
+	 * @throws EE_Error
793
+	 * @throws InvalidArgumentException
794
+	 * @throws InvalidDataTypeException
795
+	 * @throws InvalidInterfaceException
796
+	 * @throws ReflectionException
797
+	 */
798
+	protected function increaseSoldForDatetimes($qty, array $datetimes = [])
799
+	{
800
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
801
+		foreach ($datetimes as $datetime) {
802
+			$datetime->increaseSold($qty);
803
+		}
804
+	}
805
+
806
+
807
+
808
+	/**
809
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
810
+	 * DB and then updates the model objects.
811
+	 * Does not affect the reserved counts.
812
+	 *
813
+	 * @since $VID:$
814
+	 * @param int $qty
815
+	 * @return boolean
816
+	 * @throws EE_Error
817
+	 * @throws InvalidArgumentException
818
+	 * @throws InvalidDataTypeException
819
+	 * @throws InvalidInterfaceException
820
+	 * @throws ReflectionException
821
+	 */
822
+	public function decreaseSold($qty = 1)
823
+	{
824
+		$qty = absint($qty);
825
+		$this->decreaseSoldForDatetimes($qty);
826
+		$success = $this->adjustNumericFieldsInDb(
827
+			[
828
+				'TKT_sold' => $qty * -1
829
+			]
830
+		);
831
+		do_action(
832
+			'AHEE__EE_Ticket__decrease_sold',
833
+			$this,
834
+			$qty,
835
+			$this->sold(),
836
+			$success
837
+		);
838
+		return $success;
839
+	}
840
+
841
+
842
+	/**
843
+	 * Decreases sold on related datetimes
844
+	 *
845
+	 * @since $VID:$
846
+	 * @param int $qty
847
+	 * @param EE_Datetime[] $datetimes
848
+	 * @return void
849
+	 * @throws EE_Error
850
+	 * @throws InvalidArgumentException
851
+	 * @throws InvalidDataTypeException
852
+	 * @throws InvalidInterfaceException
853
+	 * @throws ReflectionException
854
+	 */
855
+	protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
856
+	{
857
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
858
+		if (is_array($datetimes)) {
859
+			foreach ($datetimes as $datetime) {
860
+				if ($datetime instanceof EE_Datetime) {
861
+					$datetime->decreaseSold($qty);
862
+				}
863
+			}
864
+		}
865
+	}
866
+
867
+
868
+	/**
869
+	 * Gets qty of reserved tickets
870
+	 *
871
+	 * @return int
872
+	 * @throws EE_Error
873
+	 */
874
+	public function reserved()
875
+	{
876
+		return $this->get_raw('TKT_reserved');
877
+	}
878
+
879
+
880
+	/**
881
+	 * Sets reserved
882
+	 *
883
+	 * @param int $reserved
884
+	 * @return void
885
+	 * @throws EE_Error
886
+	 */
887
+	public function set_reserved($reserved)
888
+	{
889
+		// reserved can not go below zero
890
+		$reserved = max(0, (int) $reserved);
891
+		$this->set('TKT_reserved', $reserved);
892
+	}
893
+
894
+
895
+	/**
896
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
897
+	 *
898
+	 * @since $VID:$
899
+	 * @param int    $qty
900
+	 * @param string $source
901
+	 * @return bool whether we successfully reserved the ticket or not.
902
+	 * @throws EE_Error
903
+	 * @throws InvalidArgumentException
904
+	 * @throws ReflectionException
905
+	 * @throws InvalidDataTypeException
906
+	 * @throws InvalidInterfaceException
907
+	 */
908
+	public function increaseReserved($qty = 1, $source = 'unknown')
909
+	{
910
+		$qty = absint($qty);
911
+		do_action(
912
+			'AHEE__EE_Ticket__increase_reserved__begin',
913
+			$this,
914
+			$qty,
915
+			$source
916
+		);
917
+		$this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
918
+		$success = false;
919
+		$datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
920
+		if ($datetimes_adjusted_successfully) {
921
+			$success = $this->incrementFieldConditionallyInDb(
922
+				'TKT_reserved',
923
+				'TKT_sold',
924
+				'TKT_qty',
925
+				$qty
926
+			);
927
+			if (! $success) {
928
+				// The datetimes were successfully bumped, but not the
929
+				// ticket. So we need to manually rollback the datetimes.
930
+				$this->decreaseReservedForDatetimes($qty);
931
+			}
932
+		}
933
+		do_action(
934
+			'AHEE__EE_Ticket__increase_reserved',
935
+			$this,
936
+			$qty,
937
+			$this->reserved(),
938
+			$success
939
+		);
940
+		return $success;
941
+	}
942
+
943
+
944
+	/**
945
+	 * Increases reserved counts on related datetimes
946
+	 *
947
+	 * @since $VID:$
948
+	 * @param int $qty
949
+	 * @param EE_Datetime[] $datetimes
950
+	 * @return boolean indicating success
951
+	 * @throws EE_Error
952
+	 * @throws InvalidArgumentException
953
+	 * @throws InvalidDataTypeException
954
+	 * @throws InvalidInterfaceException
955
+	 * @throws ReflectionException
956
+	 */
957
+	protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
958
+	{
959
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
960
+		$datetimes_updated = [];
961
+		$limit_exceeded = false;
962
+		if (is_array($datetimes)) {
963
+			foreach ($datetimes as $datetime) {
964
+				if ($datetime instanceof EE_Datetime) {
965
+					if ($datetime->increaseReserved($qty)) {
966
+						$datetimes_updated[] = $datetime;
967
+					} else {
968
+						$limit_exceeded = true;
969
+						break;
970
+					}
971
+				}
972
+			}
973
+			// If somewhere along the way we detected a datetime whose
974
+			// limit was exceeded, do a manual rollback.
975
+			if ($limit_exceeded) {
976
+				$this->decreaseReservedForDatetimes($qty, $datetimes_updated);
977
+				return false;
978
+			}
979
+		}
980
+		return true;
981
+	}
982
+
983
+
984
+	/**
985
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
986
+	 *
987
+	 * @since $VID:$
988
+	 * @param int    $qty
989
+	 * @param bool   $adjust_datetimes
990
+	 * @param string $source
991
+	 * @return boolean
992
+	 * @throws EE_Error
993
+	 * @throws InvalidArgumentException
994
+	 * @throws ReflectionException
995
+	 * @throws InvalidDataTypeException
996
+	 * @throws InvalidInterfaceException
997
+	 */
998
+	public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
999
+	{
1000
+		$qty = absint($qty);
1001
+		$this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1002
+		if ($adjust_datetimes) {
1003
+			$this->decreaseReservedForDatetimes($qty);
1004
+		}
1005
+		$success = $this->adjustNumericFieldsInDb(
1006
+			[
1007
+				'TKT_reserved' => $qty * -1
1008
+			]
1009
+		);
1010
+		do_action(
1011
+			'AHEE__EE_Ticket__decrease_reserved',
1012
+			$this,
1013
+			$qty,
1014
+			$this->reserved(),
1015
+			$success
1016
+		);
1017
+		return $success;
1018
+	}
1019
+
1020
+
1021
+	/**
1022
+	 * Decreases the reserved count on the specified datetimes.
1023
+	 *
1024
+	 * @since $VID:$
1025
+	 * @param int           $qty
1026
+	 * @param EE_Datetime[] $datetimes
1027
+	 * @throws EE_Error
1028
+	 * @throws InvalidArgumentException
1029
+	 * @throws ReflectionException
1030
+	 * @throws InvalidDataTypeException
1031
+	 * @throws InvalidInterfaceException
1032
+	 */
1033
+	protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1034
+	{
1035
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1036
+		foreach ($datetimes as $datetime) {
1037
+			if ($datetime instanceof EE_Datetime) {
1038
+				$datetime->decreaseReserved($qty);
1039
+			}
1040
+		}
1041
+	}
1042
+
1043
+
1044
+	/**
1045
+	 * Gets ticket quantity
1046
+	 *
1047
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1048
+	 *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1049
+	 *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1050
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1051
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1052
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1053
+	 * @return int
1054
+	 * @throws EE_Error
1055
+	 */
1056
+	public function qty($context = '')
1057
+	{
1058
+		switch ($context) {
1059
+			case 'reg_limit':
1060
+				return $this->real_quantity_on_ticket();
1061
+			case 'saleable':
1062
+				return $this->real_quantity_on_ticket('saleable');
1063
+			default:
1064
+				return $this->get_raw('TKT_qty');
1065
+		}
1066
+	}
1067
+
1068
+
1069
+	/**
1070
+	 * Gets ticket quantity
1071
+	 *
1072
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1073
+	 *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1074
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1075
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1076
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1077
+	 * @param  int   $DTT_ID      the primary key for a particular datetime.
1078
+	 *                            set to 0 for all related datetimes
1079
+	 * @return int
1080
+	 * @throws EE_Error
1081
+	 */
1082
+	public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1083
+	{
1084
+		$raw = $this->get_raw('TKT_qty');
1085
+		// return immediately if it's zero
1086
+		if ($raw === 0) {
1087
+			return $raw;
1088
+		}
1089
+		// echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1090
+		// ensure qty doesn't exceed raw value for THIS ticket
1091
+		$qty = min(EE_INF, $raw);
1092
+		// echo "\n . qty: " . $qty . '<br />';
1093
+		// calculate this ticket's total sales and reservations
1094
+		$sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1095
+		// echo "\n . sold: " . $this->sold() . '<br />';
1096
+		// echo "\n . reserved: " . $this->reserved() . '<br />';
1097
+		// echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1098
+		// first we need to calculate the maximum number of tickets available for the datetime
1099
+		// do we want data for one datetime or all of them ?
1100
+		$query_params = $DTT_ID ? array(array('DTT_ID' => $DTT_ID)) : array();
1101
+		$datetimes = $this->datetimes($query_params);
1102
+		if (is_array($datetimes) && ! empty($datetimes)) {
1103
+			foreach ($datetimes as $datetime) {
1104
+				if ($datetime instanceof EE_Datetime) {
1105
+					$datetime->refresh_from_db();
1106
+					// echo "\n . . datetime name: " . $datetime->name() . '<br />';
1107
+					// echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1108
+					// initialize with no restrictions for each datetime
1109
+					// but adjust datetime qty based on datetime reg limit
1110
+					$datetime_qty = min(EE_INF, $datetime->reg_limit());
1111
+					// echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1112
+					// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1113
+					// if we want the actual saleable amount, then we need to consider OTHER ticket sales
1114
+					// and reservations for this datetime, that do NOT include sales and reservations
1115
+					// for this ticket (so we add $this->sold() and $this->reserved() back in)
1116
+					if ($context === 'saleable') {
1117
+						$datetime_qty = max(
1118
+							$datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1119
+							0
1120
+						);
1121
+						// echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1122
+						// echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1123
+						// echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1124
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1125
+						$datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1126
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1127
+					}
1128
+					$qty = min($datetime_qty, $qty);
1129
+					// echo "\n . . qty: " . $qty . '<br />';
1130
+				}
1131
+			}
1132
+		}
1133
+		// NOW that we know the  maximum number of tickets available for the datetime
1134
+		// we can finally factor in the details for this specific ticket
1135
+		if ($qty > 0 && $context === 'saleable') {
1136
+			// and subtract the sales for THIS ticket
1137
+			$qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1138
+			// echo "\n . qty: " . $qty . '<br />';
1139
+		}
1140
+		// echo "\nFINAL QTY: " . $qty . "<br /><br />";
1141
+		return $qty;
1142
+	}
1143
+
1144
+
1145
+	/**
1146
+	 * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1147
+	 *
1148
+	 * @param int $qty
1149
+	 * @return void
1150
+	 * @throws EE_Error
1151
+	 */
1152
+	public function set_qty($qty)
1153
+	{
1154
+		$datetimes = $this->datetimes();
1155
+		foreach ($datetimes as $datetime) {
1156
+			if ($datetime instanceof EE_Datetime) {
1157
+				$qty = min($qty, $datetime->reg_limit());
1158
+			}
1159
+		}
1160
+		$this->set('TKT_qty', $qty);
1161
+	}
1162
+
1163
+
1164
+	/**
1165
+	 * Gets uses
1166
+	 *
1167
+	 * @return int
1168
+	 * @throws EE_Error
1169
+	 */
1170
+	public function uses()
1171
+	{
1172
+		return $this->get('TKT_uses');
1173
+	}
1174
+
1175
+
1176
+	/**
1177
+	 * Sets uses
1178
+	 *
1179
+	 * @param int $uses
1180
+	 * @return void
1181
+	 * @throws EE_Error
1182
+	 */
1183
+	public function set_uses($uses)
1184
+	{
1185
+		$this->set('TKT_uses', $uses);
1186
+	}
1187
+
1188
+
1189
+	/**
1190
+	 * returns whether ticket is required or not.
1191
+	 *
1192
+	 * @return boolean
1193
+	 * @throws EE_Error
1194
+	 */
1195
+	public function required()
1196
+	{
1197
+		return $this->get('TKT_required');
1198
+	}
1199
+
1200
+
1201
+	/**
1202
+	 * sets the TKT_required property
1203
+	 *
1204
+	 * @param boolean $required
1205
+	 * @return void
1206
+	 * @throws EE_Error
1207
+	 */
1208
+	public function set_required($required)
1209
+	{
1210
+		$this->set('TKT_required', $required);
1211
+	}
1212
+
1213
+
1214
+	/**
1215
+	 * Gets taxable
1216
+	 *
1217
+	 * @return boolean
1218
+	 * @throws EE_Error
1219
+	 */
1220
+	public function taxable()
1221
+	{
1222
+		return $this->get('TKT_taxable');
1223
+	}
1224
+
1225
+
1226
+	/**
1227
+	 * Sets taxable
1228
+	 *
1229
+	 * @param boolean $taxable
1230
+	 * @return void
1231
+	 * @throws EE_Error
1232
+	 */
1233
+	public function set_taxable($taxable)
1234
+	{
1235
+		$this->set('TKT_taxable', $taxable);
1236
+	}
1237
+
1238
+
1239
+	/**
1240
+	 * Gets is_default
1241
+	 *
1242
+	 * @return boolean
1243
+	 * @throws EE_Error
1244
+	 */
1245
+	public function is_default()
1246
+	{
1247
+		return $this->get('TKT_is_default');
1248
+	}
1249
+
1250
+
1251
+	/**
1252
+	 * Sets is_default
1253
+	 *
1254
+	 * @param boolean $is_default
1255
+	 * @return void
1256
+	 * @throws EE_Error
1257
+	 */
1258
+	public function set_is_default($is_default)
1259
+	{
1260
+		$this->set('TKT_is_default', $is_default);
1261
+	}
1262
+
1263
+
1264
+	/**
1265
+	 * Gets order
1266
+	 *
1267
+	 * @return int
1268
+	 * @throws EE_Error
1269
+	 */
1270
+	public function order()
1271
+	{
1272
+		return $this->get('TKT_order');
1273
+	}
1274
+
1275
+
1276
+	/**
1277
+	 * Sets order
1278
+	 *
1279
+	 * @param int $order
1280
+	 * @return void
1281
+	 * @throws EE_Error
1282
+	 */
1283
+	public function set_order($order)
1284
+	{
1285
+		$this->set('TKT_order', $order);
1286
+	}
1287
+
1288
+
1289
+	/**
1290
+	 * Gets row
1291
+	 *
1292
+	 * @return int
1293
+	 * @throws EE_Error
1294
+	 */
1295
+	public function row()
1296
+	{
1297
+		return $this->get('TKT_row');
1298
+	}
1299
+
1300
+
1301
+	/**
1302
+	 * Sets row
1303
+	 *
1304
+	 * @param int $row
1305
+	 * @return void
1306
+	 * @throws EE_Error
1307
+	 */
1308
+	public function set_row($row)
1309
+	{
1310
+		$this->set('TKT_row', $row);
1311
+	}
1312
+
1313
+
1314
+	/**
1315
+	 * Gets deleted
1316
+	 *
1317
+	 * @return boolean
1318
+	 * @throws EE_Error
1319
+	 */
1320
+	public function deleted()
1321
+	{
1322
+		return $this->get('TKT_deleted');
1323
+	}
1324
+
1325
+
1326
+	/**
1327
+	 * Sets deleted
1328
+	 *
1329
+	 * @param boolean $deleted
1330
+	 * @return void
1331
+	 * @throws EE_Error
1332
+	 */
1333
+	public function set_deleted($deleted)
1334
+	{
1335
+		$this->set('TKT_deleted', $deleted);
1336
+	}
1337
+
1338
+
1339
+	/**
1340
+	 * Gets parent
1341
+	 *
1342
+	 * @return int
1343
+	 * @throws EE_Error
1344
+	 */
1345
+	public function parent_ID()
1346
+	{
1347
+		return $this->get('TKT_parent');
1348
+	}
1349
+
1350
+
1351
+	/**
1352
+	 * Sets parent
1353
+	 *
1354
+	 * @param int $parent
1355
+	 * @return void
1356
+	 * @throws EE_Error
1357
+	 */
1358
+	public function set_parent_ID($parent)
1359
+	{
1360
+		$this->set('TKT_parent', $parent);
1361
+	}
1362
+
1363
+
1364
+	/**
1365
+	 * Gets a string which is handy for showing in gateways etc that describes the ticket.
1366
+	 *
1367
+	 * @return string
1368
+	 * @throws EE_Error
1369
+	 */
1370
+	public function name_and_info()
1371
+	{
1372
+		$times = array();
1373
+		foreach ($this->datetimes() as $datetime) {
1374
+			$times[] = $datetime->start_date_and_time();
1375
+		}
1376
+		return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1377
+	}
1378
+
1379
+
1380
+	/**
1381
+	 * Gets name
1382
+	 *
1383
+	 * @return string
1384
+	 * @throws EE_Error
1385
+	 */
1386
+	public function name()
1387
+	{
1388
+		return $this->get('TKT_name');
1389
+	}
1390
+
1391
+
1392
+	/**
1393
+	 * Gets price
1394
+	 *
1395
+	 * @return float
1396
+	 * @throws EE_Error
1397
+	 */
1398
+	public function price()
1399
+	{
1400
+		return $this->get('TKT_price');
1401
+	}
1402
+
1403
+
1404
+	/**
1405
+	 * Gets all the registrations for this ticket
1406
+	 *
1407
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1408
+	 * @return EE_Registration[]|EE_Base_Class[]
1409
+	 * @throws EE_Error
1410
+	 */
1411
+	public function registrations($query_params = array())
1412
+	{
1413
+		return $this->get_many_related('Registration', $query_params);
1414
+	}
1415
+
1416
+
1417
+	/**
1418
+	 * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1419
+	 *
1420
+	 * @return int
1421
+	 * @throws EE_Error
1422
+	 */
1423
+	public function update_tickets_sold()
1424
+	{
1425
+		$count_regs_for_this_ticket = $this->count_registrations(
1426
+			array(
1427
+				array(
1428
+					'STS_ID'      => EEM_Registration::status_id_approved,
1429
+					'REG_deleted' => 0,
1430
+				),
1431
+			)
1432
+		);
1433
+		$this->set_sold($count_regs_for_this_ticket);
1434
+		$this->save();
1435
+		return $count_regs_for_this_ticket;
1436
+	}
1437
+
1438
+
1439
+	/**
1440
+	 * Counts the registrations for this ticket
1441
+	 *
1442
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1443
+	 * @return int
1444
+	 */
1445
+	public function count_registrations($query_params = array())
1446
+	{
1447
+		return $this->count_related('Registration', $query_params);
1448
+	}
1449
+
1450
+
1451
+	/**
1452
+	 * Implementation for EEI_Has_Icon interface method.
1453
+	 *
1454
+	 * @see EEI_Visual_Representation for comments
1455
+	 * @return string
1456
+	 */
1457
+	public function get_icon()
1458
+	{
1459
+		return '<span class="dashicons dashicons-tickets-alt"></span>';
1460
+	}
1461
+
1462
+
1463
+	/**
1464
+	 * Implementation of the EEI_Event_Relation interface method
1465
+	 *
1466
+	 * @see EEI_Event_Relation for comments
1467
+	 * @return EE_Event
1468
+	 * @throws EE_Error
1469
+	 * @throws UnexpectedEntityException
1470
+	 */
1471
+	public function get_related_event()
1472
+	{
1473
+		// get one datetime to use for getting the event
1474
+		$datetime = $this->first_datetime();
1475
+		if (! $datetime instanceof \EE_Datetime) {
1476
+			throw new UnexpectedEntityException(
1477
+				$datetime,
1478
+				'EE_Datetime',
1479
+				sprintf(
1480
+					__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1481
+					$this->name()
1482
+				)
1483
+			);
1484
+		}
1485
+		$event = $datetime->event();
1486
+		if (! $event instanceof \EE_Event) {
1487
+			throw new UnexpectedEntityException(
1488
+				$event,
1489
+				'EE_Event',
1490
+				sprintf(
1491
+					__('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1492
+					$this->name()
1493
+				)
1494
+			);
1495
+		}
1496
+		return $event;
1497
+	}
1498
+
1499
+
1500
+	/**
1501
+	 * Implementation of the EEI_Event_Relation interface method
1502
+	 *
1503
+	 * @see EEI_Event_Relation for comments
1504
+	 * @return string
1505
+	 * @throws UnexpectedEntityException
1506
+	 * @throws EE_Error
1507
+	 */
1508
+	public function get_event_name()
1509
+	{
1510
+		$event = $this->get_related_event();
1511
+		return $event instanceof EE_Event ? $event->name() : '';
1512
+	}
1513
+
1514
+
1515
+	/**
1516
+	 * Implementation of the EEI_Event_Relation interface method
1517
+	 *
1518
+	 * @see EEI_Event_Relation for comments
1519
+	 * @return int
1520
+	 * @throws UnexpectedEntityException
1521
+	 * @throws EE_Error
1522
+	 */
1523
+	public function get_event_ID()
1524
+	{
1525
+		$event = $this->get_related_event();
1526
+		return $event instanceof EE_Event ? $event->ID() : 0;
1527
+	}
1528
+
1529
+
1530
+	/**
1531
+	 * This simply returns whether a ticket can be permanently deleted or not.
1532
+	 * The criteria for determining this is whether the ticket has any related registrations.
1533
+	 * If there are none then it can be permanently deleted.
1534
+	 *
1535
+	 * @return bool
1536
+	 */
1537
+	public function is_permanently_deleteable()
1538
+	{
1539
+		return $this->count_registrations() === 0;
1540
+	}
1541
+
1542
+
1543
+	/*******************************************************************
1544 1544
      ***********************  DEPRECATED METHODS  **********************
1545 1545
      *******************************************************************/
1546 1546
 
1547 1547
 
1548
-    /**
1549
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1550
-     * associated datetimes.
1551
-     *
1552
-     * @deprecated $VID:$
1553
-     * @param int $qty
1554
-     * @return void
1555
-     * @throws EE_Error
1556
-     * @throws InvalidArgumentException
1557
-     * @throws InvalidDataTypeException
1558
-     * @throws InvalidInterfaceException
1559
-     * @throws ReflectionException
1560
-     */
1561
-    public function increase_sold($qty = 1)
1562
-    {
1563
-        EE_Error::doing_it_wrong(
1564
-            __FUNCTION__,
1565
-            esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1566
-            '$VID:$',
1567
-            '5.0.0.p'
1568
-        );
1569
-        $this->increaseSold($qty);
1570
-    }
1571
-
1572
-
1573
-    /**
1574
-     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1575
-     *
1576
-     * @deprecated $VID:$
1577
-     * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
1578
-     *                 Negative means to decreases old counts (and increase reserved counts).
1579
-     * @throws EE_Error
1580
-     * @throws InvalidArgumentException
1581
-     * @throws InvalidDataTypeException
1582
-     * @throws InvalidInterfaceException
1583
-     * @throws ReflectionException
1584
-     */
1585
-    protected function _increase_sold_for_datetimes($qty)
1586
-    {
1587
-        EE_Error::doing_it_wrong(
1588
-            __FUNCTION__,
1589
-            esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
1590
-            '$VID:$',
1591
-            '5.0.0.p'
1592
-        );
1593
-        $this->increaseSoldForDatetimes($qty);
1594
-    }
1595
-
1596
-
1597
-    /**
1598
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
1599
-     * DB and then updates the model objects.
1600
-     * Does not affect the reserved counts.
1601
-     *
1602
-     * @deprecated $VID:$
1603
-     * @param int $qty
1604
-     * @return void
1605
-     * @throws EE_Error
1606
-     * @throws InvalidArgumentException
1607
-     * @throws InvalidDataTypeException
1608
-     * @throws InvalidInterfaceException
1609
-     * @throws ReflectionException
1610
-     */
1611
-    public function decrease_sold($qty = 1)
1612
-    {
1613
-        EE_Error::doing_it_wrong(
1614
-            __FUNCTION__,
1615
-            esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
1616
-            '$VID:$',
1617
-            '5.0.0.p'
1618
-        );
1619
-        $this->decreaseSold($qty);
1620
-    }
1621
-
1622
-
1623
-    /**
1624
-     * Decreases sold on related datetimes
1625
-     *
1626
-     * @deprecated $VID:$
1627
-     * @param int $qty
1628
-     * @return void
1629
-     * @throws EE_Error
1630
-     * @throws InvalidArgumentException
1631
-     * @throws InvalidDataTypeException
1632
-     * @throws InvalidInterfaceException
1633
-     * @throws ReflectionException
1634
-     */
1635
-    protected function _decrease_sold_for_datetimes($qty = 1)
1636
-    {
1637
-        EE_Error::doing_it_wrong(
1638
-            __FUNCTION__,
1639
-            esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
1640
-            '$VID:$',
1641
-            '5.0.0.p'
1642
-        );
1643
-        $this->decreaseSoldForDatetimes($qty);
1644
-    }
1645
-
1646
-
1647
-    /**
1648
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1649
-     *
1650
-     * @deprecated $VID:$
1651
-     * @param int    $qty
1652
-     * @param string $source
1653
-     * @return bool whether we successfully reserved the ticket or not.
1654
-     * @throws EE_Error
1655
-     * @throws InvalidArgumentException
1656
-     * @throws ReflectionException
1657
-     * @throws InvalidDataTypeException
1658
-     * @throws InvalidInterfaceException
1659
-     */
1660
-    public function increase_reserved($qty = 1, $source = 'unknown')
1661
-    {
1662
-        EE_Error::doing_it_wrong(
1663
-            __FUNCTION__,
1664
-            esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
1665
-            '$VID:$',
1666
-            '5.0.0.p'
1667
-        );
1668
-        return $this->increaseReserved($qty);
1669
-    }
1670
-
1671
-
1672
-    /**
1673
-     * Increases sold on related datetimes
1674
-     *
1675
-     * @deprecated $VID:$
1676
-     * @param int $qty
1677
-     * @return boolean indicating success
1678
-     * @throws EE_Error
1679
-     * @throws InvalidArgumentException
1680
-     * @throws InvalidDataTypeException
1681
-     * @throws InvalidInterfaceException
1682
-     * @throws ReflectionException
1683
-     */
1684
-    protected function _increase_reserved_for_datetimes($qty = 1)
1685
-    {
1686
-        EE_Error::doing_it_wrong(
1687
-            __FUNCTION__,
1688
-            esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
1689
-            '$VID:$',
1690
-            '5.0.0.p'
1691
-        );
1692
-        return $this->increaseReservedForDatetimes($qty);
1693
-    }
1694
-
1695
-
1696
-    /**
1697
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1698
-     *
1699
-     * @deprecated $VID:$
1700
-     * @param int    $qty
1701
-     * @param bool   $adjust_datetimes
1702
-     * @param string $source
1703
-     * @return void
1704
-     * @throws EE_Error
1705
-     * @throws InvalidArgumentException
1706
-     * @throws ReflectionException
1707
-     * @throws InvalidDataTypeException
1708
-     * @throws InvalidInterfaceException
1709
-     */
1710
-    public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1711
-    {
1712
-        EE_Error::doing_it_wrong(
1713
-            __FUNCTION__,
1714
-            esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
1715
-            '$VID:$',
1716
-            '5.0.0.p'
1717
-        );
1718
-        $this->decreaseReserved($qty);
1719
-    }
1720
-
1721
-
1722
-    /**
1723
-     * Decreases reserved on related datetimes
1724
-     *
1725
-     * @deprecated $VID:$
1726
-     * @param int $qty
1727
-     * @return void
1728
-     * @throws EE_Error
1729
-     * @throws InvalidArgumentException
1730
-     * @throws ReflectionException
1731
-     * @throws InvalidDataTypeException
1732
-     * @throws InvalidInterfaceException
1733
-     */
1734
-    protected function _decrease_reserved_for_datetimes($qty = 1)
1735
-    {
1736
-        EE_Error::doing_it_wrong(
1737
-            __FUNCTION__,
1738
-            esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
1739
-            '$VID:$',
1740
-            '5.0.0.p'
1741
-        );
1742
-        $this->decreaseReservedForDatetimes($qty);
1743
-    }
1548
+	/**
1549
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1550
+	 * associated datetimes.
1551
+	 *
1552
+	 * @deprecated $VID:$
1553
+	 * @param int $qty
1554
+	 * @return void
1555
+	 * @throws EE_Error
1556
+	 * @throws InvalidArgumentException
1557
+	 * @throws InvalidDataTypeException
1558
+	 * @throws InvalidInterfaceException
1559
+	 * @throws ReflectionException
1560
+	 */
1561
+	public function increase_sold($qty = 1)
1562
+	{
1563
+		EE_Error::doing_it_wrong(
1564
+			__FUNCTION__,
1565
+			esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1566
+			'$VID:$',
1567
+			'5.0.0.p'
1568
+		);
1569
+		$this->increaseSold($qty);
1570
+	}
1571
+
1572
+
1573
+	/**
1574
+	 * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1575
+	 *
1576
+	 * @deprecated $VID:$
1577
+	 * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
1578
+	 *                 Negative means to decreases old counts (and increase reserved counts).
1579
+	 * @throws EE_Error
1580
+	 * @throws InvalidArgumentException
1581
+	 * @throws InvalidDataTypeException
1582
+	 * @throws InvalidInterfaceException
1583
+	 * @throws ReflectionException
1584
+	 */
1585
+	protected function _increase_sold_for_datetimes($qty)
1586
+	{
1587
+		EE_Error::doing_it_wrong(
1588
+			__FUNCTION__,
1589
+			esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
1590
+			'$VID:$',
1591
+			'5.0.0.p'
1592
+		);
1593
+		$this->increaseSoldForDatetimes($qty);
1594
+	}
1595
+
1596
+
1597
+	/**
1598
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
1599
+	 * DB and then updates the model objects.
1600
+	 * Does not affect the reserved counts.
1601
+	 *
1602
+	 * @deprecated $VID:$
1603
+	 * @param int $qty
1604
+	 * @return void
1605
+	 * @throws EE_Error
1606
+	 * @throws InvalidArgumentException
1607
+	 * @throws InvalidDataTypeException
1608
+	 * @throws InvalidInterfaceException
1609
+	 * @throws ReflectionException
1610
+	 */
1611
+	public function decrease_sold($qty = 1)
1612
+	{
1613
+		EE_Error::doing_it_wrong(
1614
+			__FUNCTION__,
1615
+			esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
1616
+			'$VID:$',
1617
+			'5.0.0.p'
1618
+		);
1619
+		$this->decreaseSold($qty);
1620
+	}
1621
+
1622
+
1623
+	/**
1624
+	 * Decreases sold on related datetimes
1625
+	 *
1626
+	 * @deprecated $VID:$
1627
+	 * @param int $qty
1628
+	 * @return void
1629
+	 * @throws EE_Error
1630
+	 * @throws InvalidArgumentException
1631
+	 * @throws InvalidDataTypeException
1632
+	 * @throws InvalidInterfaceException
1633
+	 * @throws ReflectionException
1634
+	 */
1635
+	protected function _decrease_sold_for_datetimes($qty = 1)
1636
+	{
1637
+		EE_Error::doing_it_wrong(
1638
+			__FUNCTION__,
1639
+			esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
1640
+			'$VID:$',
1641
+			'5.0.0.p'
1642
+		);
1643
+		$this->decreaseSoldForDatetimes($qty);
1644
+	}
1645
+
1646
+
1647
+	/**
1648
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1649
+	 *
1650
+	 * @deprecated $VID:$
1651
+	 * @param int    $qty
1652
+	 * @param string $source
1653
+	 * @return bool whether we successfully reserved the ticket or not.
1654
+	 * @throws EE_Error
1655
+	 * @throws InvalidArgumentException
1656
+	 * @throws ReflectionException
1657
+	 * @throws InvalidDataTypeException
1658
+	 * @throws InvalidInterfaceException
1659
+	 */
1660
+	public function increase_reserved($qty = 1, $source = 'unknown')
1661
+	{
1662
+		EE_Error::doing_it_wrong(
1663
+			__FUNCTION__,
1664
+			esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
1665
+			'$VID:$',
1666
+			'5.0.0.p'
1667
+		);
1668
+		return $this->increaseReserved($qty);
1669
+	}
1670
+
1671
+
1672
+	/**
1673
+	 * Increases sold on related datetimes
1674
+	 *
1675
+	 * @deprecated $VID:$
1676
+	 * @param int $qty
1677
+	 * @return boolean indicating success
1678
+	 * @throws EE_Error
1679
+	 * @throws InvalidArgumentException
1680
+	 * @throws InvalidDataTypeException
1681
+	 * @throws InvalidInterfaceException
1682
+	 * @throws ReflectionException
1683
+	 */
1684
+	protected function _increase_reserved_for_datetimes($qty = 1)
1685
+	{
1686
+		EE_Error::doing_it_wrong(
1687
+			__FUNCTION__,
1688
+			esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
1689
+			'$VID:$',
1690
+			'5.0.0.p'
1691
+		);
1692
+		return $this->increaseReservedForDatetimes($qty);
1693
+	}
1694
+
1695
+
1696
+	/**
1697
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1698
+	 *
1699
+	 * @deprecated $VID:$
1700
+	 * @param int    $qty
1701
+	 * @param bool   $adjust_datetimes
1702
+	 * @param string $source
1703
+	 * @return void
1704
+	 * @throws EE_Error
1705
+	 * @throws InvalidArgumentException
1706
+	 * @throws ReflectionException
1707
+	 * @throws InvalidDataTypeException
1708
+	 * @throws InvalidInterfaceException
1709
+	 */
1710
+	public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1711
+	{
1712
+		EE_Error::doing_it_wrong(
1713
+			__FUNCTION__,
1714
+			esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
1715
+			'$VID:$',
1716
+			'5.0.0.p'
1717
+		);
1718
+		$this->decreaseReserved($qty);
1719
+	}
1720
+
1721
+
1722
+	/**
1723
+	 * Decreases reserved on related datetimes
1724
+	 *
1725
+	 * @deprecated $VID:$
1726
+	 * @param int $qty
1727
+	 * @return void
1728
+	 * @throws EE_Error
1729
+	 * @throws InvalidArgumentException
1730
+	 * @throws ReflectionException
1731
+	 * @throws InvalidDataTypeException
1732
+	 * @throws InvalidInterfaceException
1733
+	 */
1734
+	protected function _decrease_reserved_for_datetimes($qty = 1)
1735
+	{
1736
+		EE_Error::doing_it_wrong(
1737
+			__FUNCTION__,
1738
+			esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
1739
+			'$VID:$',
1740
+			'5.0.0.p'
1741
+		);
1742
+		$this->decreaseReservedForDatetimes($qty);
1743
+	}
1744 1744
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -129,7 +129,7 @@  discard block
 block discarded – undo
129 129
     public function ticket_status($display = false, $remaining = null)
130 130
     {
131 131
         $remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
132
-        if (! $remaining) {
132
+        if ( ! $remaining) {
133 133
             return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
134 134
         }
135 135
         if ($this->get('TKT_deleted')) {
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
             : '';
248 248
         $last_date = $this->last_datetime() instanceof EE_Datetime ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $dt_frmt) : '';
249 249
 
250
-        return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
250
+        return $first_date && $last_date ? $first_date.$conjunction.$last_date : '';
251 251
     }
252 252
 
253 253
 
@@ -274,7 +274,7 @@  discard block
 block discarded – undo
274 274
      */
275 275
     public function datetimes($query_params = array())
276 276
     {
277
-        if (! isset($query_params['order_by'])) {
277
+        if ( ! isset($query_params['order_by'])) {
278 278
             $query_params['order_by']['DTT_order'] = 'ASC';
279 279
         }
280 280
         return $this->get_many_related('Datetime', $query_params);
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
                 if (empty($tickets_sold['datetime'])) {
320 320
                     return $total;
321 321
                 }
322
-                if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
322
+                if ( ! empty($dtt_id) && ! isset($tickets_sold['datetime'][$dtt_id])) {
323 323
                     EE_Error::add_error(
324 324
                         __(
325 325
                             '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?',
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
                     );
332 332
                     return $total;
333 333
                 }
334
-                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
334
+                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][$dtt_id];
335 335
                 break;
336 336
             default:
337 337
                 return $total;
@@ -349,9 +349,9 @@  discard block
 block discarded – undo
349 349
     {
350 350
         $datetimes = $this->get_many_related('Datetime');
351 351
         $tickets_sold = array();
352
-        if (! empty($datetimes)) {
352
+        if ( ! empty($datetimes)) {
353 353
             foreach ($datetimes as $datetime) {
354
-                $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
354
+                $tickets_sold['datetime'][$datetime->ID()] = $datetime->get('DTT_sold');
355 355
             }
356 356
         }
357 357
         // Tickets sold
@@ -924,7 +924,7 @@  discard block
 block discarded – undo
924 924
                 'TKT_qty',
925 925
                 $qty
926 926
             );
927
-            if (! $success) {
927
+            if ( ! $success) {
928 928
                 // The datetimes were successfully bumped, but not the
929 929
                 // ticket. So we need to manually rollback the datetimes.
930 930
                 $this->decreaseReservedForDatetimes($qty);
@@ -1373,7 +1373,7 @@  discard block
 block discarded – undo
1373 1373
         foreach ($this->datetimes() as $datetime) {
1374 1374
             $times[] = $datetime->start_date_and_time();
1375 1375
         }
1376
-        return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1376
+        return $this->name().' @ '.implode(', ', $times).' for '.$this->pretty_price();
1377 1377
     }
1378 1378
 
1379 1379
 
@@ -1472,7 +1472,7 @@  discard block
 block discarded – undo
1472 1472
     {
1473 1473
         // get one datetime to use for getting the event
1474 1474
         $datetime = $this->first_datetime();
1475
-        if (! $datetime instanceof \EE_Datetime) {
1475
+        if ( ! $datetime instanceof \EE_Datetime) {
1476 1476
             throw new UnexpectedEntityException(
1477 1477
                 $datetime,
1478 1478
                 'EE_Datetime',
@@ -1483,7 +1483,7 @@  discard block
 block discarded – undo
1483 1483
             );
1484 1484
         }
1485 1485
         $event = $datetime->event();
1486
-        if (! $event instanceof \EE_Event) {
1486
+        if ( ! $event instanceof \EE_Event) {
1487 1487
             throw new UnexpectedEntityException(
1488 1488
                 $event,
1489 1489
                 'EE_Event',
Please login to merge, or discard this patch.