Completed
Branch dev (8d9313)
by
unknown
28:54 queued 19:27
created

EE_Ticket::isPublicOnly()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

Loading history...
356
            case 'datetime':
357
                if (empty($tickets_sold['datetime'])) {
358
                    return $total;
359
                }
360
                if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
361
                    EE_Error::add_error(
362
                        __(
363
                            '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?',
364
                            'event_espresso'
365
                        ),
366
                        __FILE__,
367
                        __FUNCTION__,
368
                        __LINE__
369
                    );
370
                    return $total;
371
                }
372
                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
373
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
374
            default:
375
                return $total;
376
        }
377
    }
378
379
380
    /**
381
     * This returns an array indexed by datetime_id for tickets sold with this ticket.
382
     *
383
     * @return EE_Ticket[]
384
     * @throws EE_Error
385
     * @throws ReflectionException
386
     */
387
    protected function _all_tickets_sold()
388
    {
389
        $datetimes    = $this->get_many_related('Datetime');
390
        $tickets_sold = [];
391
        if (! empty($datetimes)) {
392
            foreach ($datetimes as $datetime) {
393
                $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
394
            }
395
        }
396
        // Tickets sold
397
        $tickets_sold['ticket'] = $this->sold();
398
        return $tickets_sold;
399
    }
400
401
402
    /**
403
     * This returns the base price object for the ticket.
404
     *
405
     * @param bool $return_array whether to return as an array indexed by price id or just the object.
406
     * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
407
     * @throws EE_Error
408
     * @throws ReflectionException
409
     */
410
    public function base_price($return_array = false)
411
    {
412
        $_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price];
413
        return $return_array
414
            ? $this->get_many_related('Price', [$_where])
415
            : $this->get_first_related('Price', [$_where]);
416
    }
417
418
419
    /**
420
     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
421
     *
422
     * @access public
423
     * @return EE_Price[]
424
     * @throws EE_Error
425
     * @throws ReflectionException
426
     */
427
    public function price_modifiers()
428
    {
429
        $query_params = [
430
            0 => [
431
                'Price_Type.PBT_ID' => [
432
                    'NOT IN',
433
                    [EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax],
434
                ],
435
            ],
436
        ];
437
        return $this->prices($query_params);
438
    }
439
440
441
    /**
442
     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
443
     *
444
     * @access public
445
     * @return EE_Price[]
446
     * @throws EE_Error
447
     * @throws ReflectionException
448
     */
449
    public function tax_price_modifiers()
450
    {
451
        $query_params = [
452
            0 => [
453
                'Price_Type.PBT_ID' => EEM_Price_Type::base_type_tax,
454
            ],
455
        ];
456
        return $this->prices($query_params);
457
    }
458
459
460
    /**
461
     * Gets all the prices that combine to form the final price of this ticket
462
     *
463
     * @param array $query_params @see
464
     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
465
     * @return EE_Price[]|EE_Base_Class[]
466
     * @throws EE_Error
467
     * @throws ReflectionException
468
     */
469
    public function prices($query_params = [])
470
    {
471
        return $this->get_many_related('Price', $query_params);
472
    }
473
474
475
    /**
476
     * Gets all the ticket datetimes (ie, relations between datetimes and tickets)
477
     *
478
     * @param array $query_params @see
479
     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
480
     * @return EE_Datetime_Ticket|EE_Base_Class[]
481
     * @throws EE_Error
482
     * @throws ReflectionException
483
     */
484
    public function datetime_tickets($query_params = [])
485
    {
486
        return $this->get_many_related('Datetime_Ticket', $query_params);
487
    }
488
489
490
    /**
491
     * Gets all the datetimes from the db ordered by DTT_order
492
     *
493
     * @param boolean $show_expired
494
     * @param boolean $show_deleted
495
     * @return EE_Datetime[]
496
     * @throws EE_Error
497
     * @throws ReflectionException
498
     */
499
    public function datetimes_ordered($show_expired = true, $show_deleted = false)
500
    {
501
        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
502
            $this->ID(),
503
            $show_expired,
504
            $show_deleted
505
        );
506
    }
507
508
509
    /**
510
     * Gets ID
511
     *
512
     * @return string
513
     * @throws EE_Error
514
     * @throws ReflectionException
515
     */
516
    public function ID()
517
    {
518
        return $this->get('TKT_ID');
519
    }
520
521
522
    /**
523
     * get the author of the ticket.
524
     *
525
     * @return int
526
     * @throws EE_Error
527
     * @throws ReflectionException
528
     * @since 4.5.0
529
     */
530
    public function wp_user()
531
    {
532
        return $this->get('TKT_wp_user');
533
    }
534
535
536
    /**
537
     * Gets the template for the ticket
538
     *
539
     * @return EE_Ticket_Template|EE_Base_Class
540
     * @throws EE_Error
541
     * @throws ReflectionException
542
     */
543
    public function template()
544
    {
545
        return $this->get_first_related('Ticket_Template');
546
    }
547
548
549
    /**
550
     * Simply returns an array of EE_Price objects that are taxes.
551
     *
552
     * @return EE_Price[]
553
     * @throws EE_Error
554
     */
555
    public function get_ticket_taxes_for_admin()
556
    {
557
        return EE_Taxes::get_taxes_for_admin();
558
    }
559
560
561
    /**
562
     * @return float
563
     * @throws EE_Error
564
     * @throws ReflectionException
565
     */
566
    public function ticket_price()
567
    {
568
        return $this->get('TKT_price');
569
    }
570
571
572
    /**
573
     * @return mixed
574
     * @throws EE_Error
575
     * @throws ReflectionException
576
     */
577
    public function pretty_price()
578
    {
579
        return $this->get_pretty('TKT_price');
580
    }
581
582
583
    /**
584
     * @return bool
585
     * @throws EE_Error
586
     * @throws ReflectionException
587
     */
588
    public function is_free()
589
    {
590
        return $this->get_ticket_total_with_taxes() === (float) 0;
591
    }
592
593
594
    /**
595
     * get_ticket_total_with_taxes
596
     *
597
     * @param bool $no_cache
598
     * @return float
599
     * @throws EE_Error
600
     * @throws ReflectionException
601
     */
602
    public function get_ticket_total_with_taxes($no_cache = false)
603
    {
604
        if ($this->_ticket_total_with_taxes === null || $no_cache) {
605
            $this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
606
        }
607
        return (float) $this->_ticket_total_with_taxes;
608
    }
609
610
611
    /**
612
     * @throws EE_Error
613
     * @throws ReflectionException
614
     */
615
    public function ensure_TKT_Price_correct()
616
    {
617
        $this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
618
        $this->save();
619
    }
620
621
622
    /**
623
     * @return float
624
     * @throws EE_Error
625
     * @throws ReflectionException
626
     */
627
    public function get_ticket_subtotal()
628
    {
629
        return EE_Taxes::get_subtotal_for_admin($this);
630
    }
631
632
633
    /**
634
     * Returns the total taxes applied to this ticket
635
     *
636
     * @return float
637
     * @throws EE_Error
638
     * @throws ReflectionException
639
     */
640
    public function get_ticket_taxes_total_for_admin()
641
    {
642
        return EE_Taxes::get_total_taxes_for_admin($this);
643
    }
644
645
646
    /**
647
     * Sets name
648
     *
649
     * @param string $name
650
     * @throws EE_Error
651
     * @throws ReflectionException
652
     */
653
    public function set_name($name)
654
    {
655
        $this->set('TKT_name', $name);
656
    }
657
658
659
    /**
660
     * Gets description
661
     *
662
     * @return string
663
     * @throws EE_Error
664
     * @throws ReflectionException
665
     */
666
    public function description()
667
    {
668
        return $this->get('TKT_description');
669
    }
670
671
672
    /**
673
     * Sets description
674
     *
675
     * @param string $description
676
     * @throws EE_Error
677
     * @throws ReflectionException
678
     */
679
    public function set_description($description)
680
    {
681
        $this->set('TKT_description', $description);
682
    }
683
684
685
    /**
686
     * Gets start_date
687
     *
688
     * @param string $date_format
689
     * @param string $time_format
690
     * @return string
691
     * @throws EE_Error
692
     * @throws ReflectionException
693
     */
694
    public function start_date($date_format = '', $time_format = '')
695
    {
696
        return $this->_get_datetime('TKT_start_date', $date_format, $time_format);
697
    }
698
699
700
    /**
701
     * Sets start_date
702
     *
703
     * @param string $start_date
704
     * @return void
705
     * @throws EE_Error
706
     * @throws ReflectionException
707
     */
708
    public function set_start_date($start_date)
709
    {
710
        $this->_set_date_time('B', $start_date, 'TKT_start_date');
711
    }
712
713
714
    /**
715
     * Gets end_date
716
     *
717
     * @param string $date_format
718
     * @param string $time_format
719
     * @return string
720
     * @throws EE_Error
721
     * @throws ReflectionException
722
     */
723
    public function end_date($date_format = '', $time_format = '')
724
    {
725
        return $this->_get_datetime('TKT_end_date', $date_format, $time_format);
726
    }
727
728
729
    /**
730
     * Sets end_date
731
     *
732
     * @param string $end_date
733
     * @return void
734
     * @throws EE_Error
735
     * @throws ReflectionException
736
     */
737
    public function set_end_date($end_date)
738
    {
739
        $this->_set_date_time('B', $end_date, 'TKT_end_date');
740
    }
741
742
743
    /**
744
     * Sets sell until time
745
     *
746
     * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
747
     * @throws EE_Error
748
     * @throws ReflectionException
749
     * @since 4.5.0
750
     */
751
    public function set_end_time($time)
752
    {
753
        $this->_set_time_for($time, 'TKT_end_date');
754
    }
755
756
757
    /**
758
     * Sets min
759
     *
760
     * @param int $min
761
     * @return void
762
     * @throws EE_Error
763
     * @throws ReflectionException
764
     */
765
    public function set_min($min)
766
    {
767
        $this->set('TKT_min', $min);
768
    }
769
770
771
    /**
772
     * Gets max
773
     *
774
     * @return int
775
     * @throws EE_Error
776
     * @throws ReflectionException
777
     */
778
    public function max()
779
    {
780
        return $this->get('TKT_max');
781
    }
782
783
784
    /**
785
     * Sets max
786
     *
787
     * @param int $max
788
     * @return void
789
     * @throws EE_Error
790
     * @throws ReflectionException
791
     */
792
    public function set_max($max)
793
    {
794
        $this->set('TKT_max', $max);
795
    }
796
797
798
    /**
799
     * Sets price
800
     *
801
     * @param float $price
802
     * @return void
803
     * @throws EE_Error
804
     * @throws ReflectionException
805
     */
806
    public function set_price($price)
807
    {
808
        $this->set('TKT_price', $price);
809
    }
810
811
812
    /**
813
     * Gets sold
814
     *
815
     * @return int
816
     * @throws EE_Error
817
     * @throws ReflectionException
818
     */
819
    public function sold()
820
    {
821
        return $this->get_raw('TKT_sold');
822
    }
823
824
825
    /**
826
     * Sets sold
827
     *
828
     * @param int $sold
829
     * @return void
830
     * @throws EE_Error
831
     * @throws ReflectionException
832
     */
833
    public function set_sold($sold)
834
    {
835
        // sold can not go below zero
836
        $sold = max(0, $sold);
837
        $this->set('TKT_sold', $sold);
838
    }
839
840
841
    /**
842
     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
843
     * associated datetimes.
844
     *
845
     * @param int $qty
846
     * @return boolean
847
     * @throws EE_Error
848
     * @throws InvalidArgumentException
849
     * @throws InvalidDataTypeException
850
     * @throws InvalidInterfaceException
851
     * @throws ReflectionException
852
     * @since 4.9.80.p
853
     */
854 View Code Duplication
    public function increaseSold($qty = 1)
855
    {
856
        $qty = absint($qty);
857
        // increment sold and decrement reserved datetime quantities simultaneously
858
        // don't worry about failures, because they must have already had a spot reserved
859
        $this->increaseSoldForDatetimes($qty);
860
        // Increment and decrement ticket quantities simultaneously
861
        $success = $this->adjustNumericFieldsInDb(
862
            [
863
                'TKT_reserved' => $qty * -1,
864
                'TKT_sold'     => $qty,
865
            ]
866
        );
867
        do_action(
868
            'AHEE__EE_Ticket__increase_sold',
869
            $this,
870
            $qty,
871
            $this->sold(),
872
            $success
873
        );
874
        return $success;
875
    }
876
877
878
    /**
879
     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
880
     *
881
     * @param int           $qty positive or negative. Positive means to increase sold counts (and decrease reserved
882
     *                           counts), Negative means to decreases old counts (and increase reserved counts).
883
     * @param EE_Datetime[] $datetimes
884
     * @throws EE_Error
885
     * @throws InvalidArgumentException
886
     * @throws InvalidDataTypeException
887
     * @throws InvalidInterfaceException
888
     * @throws ReflectionException
889
     * @since 4.9.80.p
890
     */
891
    protected function increaseSoldForDatetimes($qty, array $datetimes = [])
892
    {
893
        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
894
        foreach ($datetimes as $datetime) {
895
            $datetime->increaseSold($qty);
896
        }
897
    }
898
899
900
    /**
901
     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
902
     * DB and then updates the model objects.
903
     * Does not affect the reserved counts.
904
     *
905
     * @param int $qty
906
     * @return boolean
907
     * @throws EE_Error
908
     * @throws InvalidArgumentException
909
     * @throws InvalidDataTypeException
910
     * @throws InvalidInterfaceException
911
     * @throws ReflectionException
912
     * @since 4.9.80.p
913
     */
914 View Code Duplication
    public function decreaseSold($qty = 1)
915
    {
916
        $qty = absint($qty);
917
        $this->decreaseSoldForDatetimes($qty);
918
        $success = $this->adjustNumericFieldsInDb(
919
            [
920
                'TKT_sold' => $qty * -1,
921
            ]
922
        );
923
        do_action(
924
            'AHEE__EE_Ticket__decrease_sold',
925
            $this,
926
            $qty,
927
            $this->sold(),
928
            $success
929
        );
930
        return $success;
931
    }
932
933
934
    /**
935
     * Decreases sold on related datetimes
936
     *
937
     * @param int           $qty
938
     * @param EE_Datetime[] $datetimes
939
     * @return void
940
     * @throws EE_Error
941
     * @throws InvalidArgumentException
942
     * @throws InvalidDataTypeException
943
     * @throws InvalidInterfaceException
944
     * @throws ReflectionException
945
     * @since 4.9.80.p
946
     */
947
    protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
948
    {
949
        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
950
        if (is_array($datetimes)) {
951
            foreach ($datetimes as $datetime) {
952
                if ($datetime instanceof EE_Datetime) {
953
                    $datetime->decreaseSold($qty);
954
                }
955
            }
956
        }
957
    }
958
959
960
    /**
961
     * Gets qty of reserved tickets
962
     *
963
     * @return int
964
     * @throws EE_Error
965
     * @throws ReflectionException
966
     */
967
    public function reserved()
968
    {
969
        return $this->get_raw('TKT_reserved');
970
    }
971
972
973
    /**
974
     * Sets reserved
975
     *
976
     * @param int $reserved
977
     * @return void
978
     * @throws EE_Error
979
     * @throws ReflectionException
980
     */
981
    public function set_reserved($reserved)
982
    {
983
        // reserved can not go below zero
984
        $reserved = max(0, (int) $reserved);
985
        $this->set('TKT_reserved', $reserved);
986
    }
987
988
989
    /**
990
     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
991
     *
992
     * @param int    $qty
993
     * @param string $source
994
     * @return bool whether we successfully reserved the ticket or not.
995
     * @throws EE_Error
996
     * @throws InvalidArgumentException
997
     * @throws ReflectionException
998
     * @throws InvalidDataTypeException
999
     * @throws InvalidInterfaceException
1000
     * @since 4.9.80.p
1001
     */
1002
    public function increaseReserved($qty = 1, $source = 'unknown')
1003
    {
1004
        $qty = absint($qty);
1005
        do_action(
1006
            'AHEE__EE_Ticket__increase_reserved__begin',
1007
            $this,
1008
            $qty,
1009
            $source
1010
        );
1011
        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
1012
        $success                         = false;
1013
        $datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
1014
        if ($datetimes_adjusted_successfully) {
1015
            $success = $this->incrementFieldConditionallyInDb(
1016
                'TKT_reserved',
1017
                'TKT_sold',
1018
                'TKT_qty',
1019
                $qty
1020
            );
1021
            if (! $success) {
1022
                // The datetimes were successfully bumped, but not the
1023
                // ticket. So we need to manually rollback the datetimes.
1024
                $this->decreaseReservedForDatetimes($qty);
1025
            }
1026
        }
1027
        do_action(
1028
            'AHEE__EE_Ticket__increase_reserved',
1029
            $this,
1030
            $qty,
1031
            $this->reserved(),
1032
            $success
1033
        );
1034
        return $success;
1035
    }
1036
1037
1038
    /**
1039
     * Increases reserved counts on related datetimes
1040
     *
1041
     * @param int           $qty
1042
     * @param EE_Datetime[] $datetimes
1043
     * @return boolean indicating success
1044
     * @throws EE_Error
1045
     * @throws InvalidArgumentException
1046
     * @throws InvalidDataTypeException
1047
     * @throws InvalidInterfaceException
1048
     * @throws ReflectionException
1049
     * @since 4.9.80.p
1050
     */
1051
    protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
1052
    {
1053
        $datetimes         = ! empty($datetimes) ? $datetimes : $this->datetimes();
1054
        $datetimes_updated = [];
1055
        $limit_exceeded    = false;
1056
        if (is_array($datetimes)) {
1057
            foreach ($datetimes as $datetime) {
1058
                if ($datetime instanceof EE_Datetime) {
1059
                    if ($datetime->increaseReserved($qty)) {
1060
                        $datetimes_updated[] = $datetime;
1061
                    } else {
1062
                        $limit_exceeded = true;
1063
                        break;
1064
                    }
1065
                }
1066
            }
1067
            // If somewhere along the way we detected a datetime whose
1068
            // limit was exceeded, do a manual rollback.
1069
            if ($limit_exceeded) {
1070
                $this->decreaseReservedForDatetimes($qty, $datetimes_updated);
1071
                return false;
1072
            }
1073
        }
1074
        return true;
1075
    }
1076
1077
1078
    /**
1079
     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1080
     *
1081
     * @param int    $qty
1082
     * @param bool   $adjust_datetimes
1083
     * @param string $source
1084
     * @return boolean
1085
     * @throws EE_Error
1086
     * @throws InvalidArgumentException
1087
     * @throws ReflectionException
1088
     * @throws InvalidDataTypeException
1089
     * @throws InvalidInterfaceException
1090
     * @since 4.9.80.p
1091
     */
1092
    public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1093
    {
1094
        $qty = absint($qty);
1095
        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1096
        if ($adjust_datetimes) {
1097
            $this->decreaseReservedForDatetimes($qty);
1098
        }
1099
        $success = $this->adjustNumericFieldsInDb(
1100
            [
1101
                'TKT_reserved' => $qty * -1,
1102
            ]
1103
        );
1104
        do_action(
1105
            'AHEE__EE_Ticket__decrease_reserved',
1106
            $this,
1107
            $qty,
1108
            $this->reserved(),
1109
            $success
1110
        );
1111
        return $success;
1112
    }
1113
1114
1115
    /**
1116
     * Decreases the reserved count on the specified datetimes.
1117
     *
1118
     * @param int           $qty
1119
     * @param EE_Datetime[] $datetimes
1120
     * @throws EE_Error
1121
     * @throws InvalidArgumentException
1122
     * @throws ReflectionException
1123
     * @throws InvalidDataTypeException
1124
     * @throws InvalidInterfaceException
1125
     * @since 4.9.80.p
1126
     */
1127 View Code Duplication
    protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1128
    {
1129
        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1130
        foreach ($datetimes as $datetime) {
1131
            if ($datetime instanceof EE_Datetime) {
1132
                $datetime->decreaseReserved($qty);
1133
            }
1134
        }
1135
    }
1136
1137
1138
    /**
1139
     * Gets ticket quantity
1140
     *
1141
     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1142
     *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1143
     *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1144
     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1145
     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1146
     *                            is therefore the truest measure of tickets that can be purchased at the moment
1147
     * @return int
1148
     * @throws EE_Error
1149
     * @throws ReflectionException
1150
     */
1151
    public function qty($context = '')
1152
    {
1153
        switch ($context) {
1154
            case 'reg_limit':
1155
                return $this->real_quantity_on_ticket();
1156
            case 'saleable':
1157
                return $this->real_quantity_on_ticket('saleable');
1158
            default:
1159
                return $this->get_raw('TKT_qty');
1160
        }
1161
    }
1162
1163
1164
    /**
1165
     * Gets ticket quantity
1166
     *
1167
     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1168
     *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1169
     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1170
     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1171
     *                            is therefore the truest measure of tickets that can be purchased at the moment
1172
     * @param int    $DTT_ID      the primary key for a particular datetime.
1173
     *                            set to 0 for all related datetimes
1174
     * @return int
1175
     * @throws EE_Error
1176
     * @throws ReflectionException
1177
     */
1178
    public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1179
    {
1180
        $raw = $this->get_raw('TKT_qty');
1181
        // return immediately if it's zero
1182
        if ($raw === 0) {
1183
            return $raw;
1184
        }
1185
        // echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1186
        // ensure qty doesn't exceed raw value for THIS ticket
1187
        $qty = min(EE_INF, $raw);
1188
        // echo "\n . qty: " . $qty . '<br />';
1189
        // calculate this ticket's total sales and reservations
1190
        $sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1191
        // echo "\n . sold: " . $this->sold() . '<br />';
1192
        // echo "\n . reserved: " . $this->reserved() . '<br />';
1193
        // echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1194
        // first we need to calculate the maximum number of tickets available for the datetime
1195
        // do we want data for one datetime or all of them ?
1196
        $query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : [];
1197
        $datetimes    = $this->datetimes($query_params);
1198
        if (is_array($datetimes) && ! empty($datetimes)) {
1199
            foreach ($datetimes as $datetime) {
1200
                if ($datetime instanceof EE_Datetime) {
1201
                    $datetime->refresh_from_db();
1202
                    // echo "\n . . datetime name: " . $datetime->name() . '<br />';
1203
                    // echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1204
                    // initialize with no restrictions for each datetime
1205
                    // but adjust datetime qty based on datetime reg limit
1206
                    $datetime_qty = min(EE_INF, $datetime->reg_limit());
1207
                    // echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1208
                    // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1209
                    // if we want the actual saleable amount, then we need to consider OTHER ticket sales
1210
                    // and reservations for this datetime, that do NOT include sales and reservations
1211
                    // for this ticket (so we add $this->sold() and $this->reserved() back in)
1212
                    if ($context === 'saleable') {
1213
                        $datetime_qty = max(
1214
                            $datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1215
                            0
1216
                        );
1217
                        // echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1218
                        // echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1219
                        // echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1220
                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1221
                        $datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1222
                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1223
                    }
1224
                    $qty = min($datetime_qty, $qty);
1225
                    // echo "\n . . qty: " . $qty . '<br />';
1226
                }
1227
            }
1228
        }
1229
        // NOW that we know the  maximum number of tickets available for the datetime
1230
        // we can finally factor in the details for this specific ticket
1231
        if ($qty > 0 && $context === 'saleable') {
1232
            // and subtract the sales for THIS ticket
1233
            $qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1234
            // echo "\n . qty: " . $qty . '<br />';
1235
        }
1236
        // echo "\nFINAL QTY: " . $qty . "<br /><br />";
1237
        return $qty;
1238
    }
1239
1240
1241
    /**
1242
     * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1243
     *
1244
     * @param int $qty
1245
     * @return void
1246
     * @throws EE_Error
1247
     * @throws ReflectionException
1248
     */
1249 View Code Duplication
    public function set_qty($qty)
1250
    {
1251
        $datetimes = $this->datetimes();
1252
        foreach ($datetimes as $datetime) {
1253
            if ($datetime instanceof EE_Datetime) {
1254
                $qty = min($qty, $datetime->reg_limit());
1255
            }
1256
        }
1257
        $this->set('TKT_qty', $qty);
1258
    }
1259
1260
1261
    /**
1262
     * Gets uses
1263
     *
1264
     * @return int
1265
     * @throws EE_Error
1266
     * @throws ReflectionException
1267
     */
1268
    public function uses()
1269
    {
1270
        return $this->get('TKT_uses');
1271
    }
1272
1273
1274
    /**
1275
     * Sets uses
1276
     *
1277
     * @param int $uses
1278
     * @return void
1279
     * @throws EE_Error
1280
     * @throws ReflectionException
1281
     */
1282
    public function set_uses($uses)
1283
    {
1284
        $this->set('TKT_uses', $uses);
1285
    }
1286
1287
1288
    /**
1289
     * returns whether ticket is required or not.
1290
     *
1291
     * @return boolean
1292
     * @throws EE_Error
1293
     * @throws ReflectionException
1294
     */
1295
    public function required()
1296
    {
1297
        return $this->get('TKT_required');
1298
    }
1299
1300
1301
    /**
1302
     * sets the TKT_required property
1303
     *
1304
     * @param boolean $required
1305
     * @return void
1306
     * @throws EE_Error
1307
     * @throws ReflectionException
1308
     */
1309
    public function set_required($required)
1310
    {
1311
        $this->set('TKT_required', $required);
1312
    }
1313
1314
1315
    /**
1316
     * Gets taxable
1317
     *
1318
     * @return boolean
1319
     * @throws EE_Error
1320
     * @throws ReflectionException
1321
     */
1322
    public function taxable()
1323
    {
1324
        return $this->get('TKT_taxable');
1325
    }
1326
1327
1328
    /**
1329
     * Sets taxable
1330
     *
1331
     * @param boolean $taxable
1332
     * @return void
1333
     * @throws EE_Error
1334
     * @throws ReflectionException
1335
     */
1336
    public function set_taxable($taxable)
1337
    {
1338
        $this->set('TKT_taxable', $taxable);
1339
    }
1340
1341
1342
    /**
1343
     * Gets is_default
1344
     *
1345
     * @return boolean
1346
     * @throws EE_Error
1347
     * @throws ReflectionException
1348
     */
1349
    public function is_default()
1350
    {
1351
        return $this->get('TKT_is_default');
1352
    }
1353
1354
1355
    /**
1356
     * Sets is_default
1357
     *
1358
     * @param boolean $is_default
1359
     * @return void
1360
     * @throws EE_Error
1361
     * @throws ReflectionException
1362
     */
1363
    public function set_is_default($is_default)
1364
    {
1365
        $this->set('TKT_is_default', $is_default);
1366
    }
1367
1368
1369
    /**
1370
     * Gets order
1371
     *
1372
     * @return int
1373
     * @throws EE_Error
1374
     * @throws ReflectionException
1375
     */
1376
    public function order()
1377
    {
1378
        return $this->get('TKT_order');
1379
    }
1380
1381
1382
    /**
1383
     * Sets order
1384
     *
1385
     * @param int $order
1386
     * @return void
1387
     * @throws EE_Error
1388
     * @throws ReflectionException
1389
     */
1390
    public function set_order($order)
1391
    {
1392
        $this->set('TKT_order', $order);
1393
    }
1394
1395
1396
    /**
1397
     * Gets row
1398
     *
1399
     * @return int
1400
     * @throws EE_Error
1401
     * @throws ReflectionException
1402
     */
1403
    public function row()
1404
    {
1405
        return $this->get('TKT_row');
1406
    }
1407
1408
1409
    /**
1410
     * Sets row
1411
     *
1412
     * @param int $row
1413
     * @return void
1414
     * @throws EE_Error
1415
     * @throws ReflectionException
1416
     */
1417
    public function set_row($row)
1418
    {
1419
        $this->set('TKT_row', $row);
1420
    }
1421
1422
1423
    /**
1424
     * Gets deleted
1425
     *
1426
     * @return boolean
1427
     * @throws EE_Error
1428
     * @throws ReflectionException
1429
     */
1430
    public function deleted()
1431
    {
1432
        return $this->get('TKT_deleted');
1433
    }
1434
1435
1436
    /**
1437
     * Sets deleted
1438
     *
1439
     * @param boolean $deleted
1440
     * @return void
1441
     * @throws EE_Error
1442
     * @throws ReflectionException
1443
     */
1444
    public function set_deleted($deleted)
1445
    {
1446
        $this->set('TKT_deleted', $deleted);
1447
    }
1448
1449
1450
    /**
1451
     * Gets parent
1452
     *
1453
     * @return int
1454
     * @throws EE_Error
1455
     * @throws ReflectionException
1456
     */
1457
    public function parent_ID()
1458
    {
1459
        return $this->get('TKT_parent');
1460
    }
1461
1462
1463
    /**
1464
     * Sets parent
1465
     *
1466
     * @param int $parent
1467
     * @return void
1468
     * @throws EE_Error
1469
     * @throws ReflectionException
1470
     */
1471
    public function set_parent_ID($parent)
1472
    {
1473
        $this->set('TKT_parent', $parent);
1474
    }
1475
1476
1477
    /**
1478
     * @return boolean
1479
     * @throws EE_Error
1480
     * @throws InvalidArgumentException
1481
     * @throws InvalidDataTypeException
1482
     * @throws InvalidInterfaceException
1483
     * @throws ReflectionException
1484
     */
1485
    public function reverse_calculate()
1486
    {
1487
        return $this->get('TKT_reverse_calculate');
1488
    }
1489
1490
1491
    /**
1492
     * @param boolean $reverse_calculate
1493
     * @throws EE_Error
1494
     * @throws InvalidArgumentException
1495
     * @throws InvalidDataTypeException
1496
     * @throws InvalidInterfaceException
1497
     * @throws ReflectionException
1498
     */
1499
    public function set_reverse_calculate($reverse_calculate)
1500
    {
1501
        $this->set('TKT_reverse_calculate', $reverse_calculate);
1502
    }
1503
1504
1505
    /**
1506
     * Gets a string which is handy for showing in gateways etc that describes the ticket.
1507
     *
1508
     * @return string
1509
     * @throws EE_Error
1510
     * @throws ReflectionException
1511
     */
1512
    public function name_and_info()
1513
    {
1514
        $times = [];
1515
        foreach ($this->datetimes() as $datetime) {
1516
            $times[] = $datetime->start_date_and_time();
1517
        }
1518
        return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1519
    }
1520
1521
1522
    /**
1523
     * Gets name
1524
     *
1525
     * @return string
1526
     * @throws EE_Error
1527
     * @throws ReflectionException
1528
     */
1529
    public function name()
1530
    {
1531
        return $this->get('TKT_name');
1532
    }
1533
1534
1535
    /**
1536
     * Gets price
1537
     *
1538
     * @return float
1539
     * @throws EE_Error
1540
     * @throws ReflectionException
1541
     */
1542
    public function price()
1543
    {
1544
        return $this->get('TKT_price');
1545
    }
1546
1547
1548
    /**
1549
     * Gets all the registrations for this ticket
1550
     *
1551
     * @param array $query_params @see
1552
     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1553
     * @return EE_Registration[]|EE_Base_Class[]
1554
     * @throws EE_Error
1555
     * @throws ReflectionException
1556
     */
1557
    public function registrations($query_params = [])
1558
    {
1559
        return $this->get_many_related('Registration', $query_params);
1560
    }
1561
1562
1563
    /**
1564
     * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1565
     *
1566
     * @return int
1567
     * @throws EE_Error
1568
     * @throws ReflectionException
1569
     */
1570 View Code Duplication
    public function update_tickets_sold()
1571
    {
1572
        $count_regs_for_this_ticket = $this->count_registrations(
1573
            [
1574
                [
1575
                    'STS_ID'      => EEM_Registration::status_id_approved,
1576
                    'REG_deleted' => 0,
1577
                ],
1578
            ]
1579
        );
1580
        $this->set_sold($count_regs_for_this_ticket);
1581
        $this->save();
1582
        return $count_regs_for_this_ticket;
1583
    }
1584
1585
1586
    /**
1587
     * Counts the registrations for this ticket
1588
     *
1589
     * @param array $query_params @see
1590
     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1591
     * @return int
1592
     * @throws EE_Error
1593
     * @throws ReflectionException
1594
     */
1595
    public function count_registrations($query_params = [])
1596
    {
1597
        return $this->count_related('Registration', $query_params);
1598
    }
1599
1600
1601
    /**
1602
     * Implementation for EEI_Has_Icon interface method.
1603
     *
1604
     * @return string
1605
     * @see EEI_Visual_Representation for comments
1606
     */
1607
    public function get_icon()
1608
    {
1609
        return '<span class="dashicons dashicons-tickets-alt"/>';
1610
    }
1611
1612
1613
    /**
1614
     * Implementation of the EEI_Event_Relation interface method
1615
     *
1616
     * @return EE_Event
1617
     * @throws EE_Error
1618
     * @throws UnexpectedEntityException
1619
     * @throws ReflectionException
1620
     * @see EEI_Event_Relation for comments
1621
     */
1622
    public function get_related_event()
1623
    {
1624
        // get one datetime to use for getting the event
1625
        $datetime = $this->first_datetime();
1626
        if (! $datetime instanceof EE_Datetime) {
1627
            throw new UnexpectedEntityException(
1628
                $datetime,
1629
                'EE_Datetime',
1630
                sprintf(
1631
                    __('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1632
                    $this->name()
1633
                )
1634
            );
1635
        }
1636
        $event = $datetime->event();
1637
        if (! $event instanceof EE_Event) {
1638
            throw new UnexpectedEntityException(
1639
                $event,
1640
                'EE_Event',
1641
                sprintf(
1642
                    __('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1643
                    $this->name()
1644
                )
1645
            );
1646
        }
1647
        return $event;
1648
    }
1649
1650
1651
    /**
1652
     * Implementation of the EEI_Event_Relation interface method
1653
     *
1654
     * @return string
1655
     * @throws UnexpectedEntityException
1656
     * @throws EE_Error
1657
     * @throws ReflectionException
1658
     * @see EEI_Event_Relation for comments
1659
     */
1660
    public function get_event_name()
1661
    {
1662
        $event = $this->get_related_event();
1663
        return $event instanceof EE_Event ? $event->name() : '';
1664
    }
1665
1666
1667
    /**
1668
     * Implementation of the EEI_Event_Relation interface method
1669
     *
1670
     * @return int
1671
     * @throws UnexpectedEntityException
1672
     * @throws EE_Error
1673
     * @throws ReflectionException
1674
     * @see EEI_Event_Relation for comments
1675
     */
1676
    public function get_event_ID()
1677
    {
1678
        $event = $this->get_related_event();
1679
        return $event instanceof EE_Event ? $event->ID() : 0;
1680
    }
1681
1682
1683
    /**
1684
     * This simply returns whether a ticket can be permanently deleted or not.
1685
     * The criteria for determining this is whether the ticket has any related registrations.
1686
     * If there are none then it can be permanently deleted.
1687
     *
1688
     * @return bool
1689
     * @throws EE_Error
1690
     * @throws ReflectionException
1691
     */
1692
    public function is_permanently_deleteable()
1693
    {
1694
        return $this->count_registrations() === 0;
1695
    }
1696
1697
1698
    /**
1699
     * @return int
1700
     * @throws EE_Error
1701
     * @throws ReflectionException
1702
     * @since   $VID:$
1703
     */
1704
    public function visibility(): int
1705
    {
1706
        return $this->get('TKT_visibility');
1707
    }
1708
1709
1710
    /**
1711
     * @return int
1712
     * @throws EE_Error
1713
     * @throws ReflectionException
1714
     * @since   $VID:$
1715
     */
1716
    public function isHidden(): int
1717
    {
1718
        return $this->visibility() === EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1719
    }
1720
1721
1722
    /**
1723
     * @return int
1724
     * @throws EE_Error
1725
     * @throws ReflectionException
1726
     * @since   $VID:$
1727
     */
1728
    public function isNotHidden(): int
1729
    {
1730
        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1731
    }
1732
1733
1734
    /**
1735
     * @return int
1736
     * @throws EE_Error
1737
     * @throws ReflectionException
1738
     * @since   $VID:$
1739
     */
1740
    public function isPublicOnly(): int
1741
    {
1742
        return $this->isNotHidden() && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE;
1743
    }
1744
1745
1746
    /**
1747
     * @return int
1748
     * @throws EE_Error
1749
     * @throws ReflectionException
1750
     * @since   $VID:$
1751
     */
1752
    public function isMembersOnly(): int
1753
    {
1754
        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE
1755
               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE;
1756
    }
1757
1758
1759
    /**
1760
     * @return int
1761
     * @throws EE_Error
1762
     * @throws ReflectionException
1763
     * @since   $VID:$
1764
     */
1765
    public function isAdminsOnly(): int
1766
    {
1767
        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE
1768
               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE;
1769
    }
1770
1771
1772
    /**
1773
     * @return int
1774
     * @throws EE_Error
1775
     * @throws ReflectionException
1776
     * @since   $VID:$
1777
     */
1778
    public function isAdminUiOnly(): int
1779
    {
1780
        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE
1781
               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE;
1782
    }
1783
1784
1785
    /**
1786
     * @param int $visibility
1787
     * @throws EE_Error
1788
     * @throws ReflectionException
1789
     * @since   $VID:$
1790
     */
1791
    public function set_visibility(int $visibility)
1792
    {
1793
1794
        $ticket_visibility_options = $this->_model->ticketVisibilityOptions();
1795
        $ticket_visibility         = -1;
1796
        foreach ($ticket_visibility_options as $ticket_visibility_option) {
1797
            if ($visibility === $ticket_visibility_option) {
1798
                $ticket_visibility = $visibility;
1799
            }
1800
        }
1801
        if ($ticket_visibility === -1) {
1802
            throw new DomainException(
1803
                sprintf(
1804
                    esc_html__(
1805
                        'The supplied ticket visibility setting of "%1$s" is not valid. It needs to match one of the keys in the following array:%2$s %3$s ',
1806
                        'event_espresso'
1807
                    ),
1808
                    $visibility,
1809
                    '<br />',
1810
                    var_export($ticket_visibility_options, true)
1811
                )
1812
            );
1813
        }
1814
        $this->set('TKT_visibility', $ticket_visibility);
1815
    }
1816
1817
1818
    /*******************************************************************
1819
     ***********************  DEPRECATED METHODS  **********************
1820
     *******************************************************************/
1821
1822
1823
    /**
1824
     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1825
     * associated datetimes.
1826
     *
1827
     * @param int $qty
1828
     * @return void
1829
     * @throws EE_Error
1830
     * @throws InvalidArgumentException
1831
     * @throws InvalidDataTypeException
1832
     * @throws InvalidInterfaceException
1833
     * @throws ReflectionException
1834
     * @deprecated 4.9.80.p
1835
     */
1836
    public function increase_sold($qty = 1)
1837
    {
1838
        EE_Error::doing_it_wrong(
1839
            __FUNCTION__,
1840
            esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1841
            '4.9.80.p',
1842
            '5.0.0.p'
1843
        );
1844
        $this->increaseSold($qty);
1845
    }
1846
1847
1848
    /**
1849
     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1850
     *
1851
     * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
1852
     *                 Negative means to decreases old counts (and increase reserved counts).
1853
     * @throws EE_Error
1854
     * @throws InvalidArgumentException
1855
     * @throws InvalidDataTypeException
1856
     * @throws InvalidInterfaceException
1857
     * @throws ReflectionException
1858
     * @deprecated 4.9.80.p
1859
     */
1860
    protected function _increase_sold_for_datetimes($qty)
1861
    {
1862
        EE_Error::doing_it_wrong(
1863
            __FUNCTION__,
1864
            esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
1865
            '4.9.80.p',
1866
            '5.0.0.p'
1867
        );
1868
        $this->increaseSoldForDatetimes($qty);
1869
    }
1870
1871
1872
    /**
1873
     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
1874
     * DB and then updates the model objects.
1875
     * Does not affect the reserved counts.
1876
     *
1877
     * @param int $qty
1878
     * @return void
1879
     * @throws EE_Error
1880
     * @throws InvalidArgumentException
1881
     * @throws InvalidDataTypeException
1882
     * @throws InvalidInterfaceException
1883
     * @throws ReflectionException
1884
     * @deprecated 4.9.80.p
1885
     */
1886
    public function decrease_sold($qty = 1)
1887
    {
1888
        EE_Error::doing_it_wrong(
1889
            __FUNCTION__,
1890
            esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
1891
            '4.9.80.p',
1892
            '5.0.0.p'
1893
        );
1894
        $this->decreaseSold($qty);
1895
    }
1896
1897
1898
    /**
1899
     * Decreases sold on related datetimes
1900
     *
1901
     * @param int $qty
1902
     * @return void
1903
     * @throws EE_Error
1904
     * @throws InvalidArgumentException
1905
     * @throws InvalidDataTypeException
1906
     * @throws InvalidInterfaceException
1907
     * @throws ReflectionException
1908
     * @deprecated 4.9.80.p
1909
     */
1910
    protected function _decrease_sold_for_datetimes($qty = 1)
1911
    {
1912
        EE_Error::doing_it_wrong(
1913
            __FUNCTION__,
1914
            esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
1915
            '4.9.80.p',
1916
            '5.0.0.p'
1917
        );
1918
        $this->decreaseSoldForDatetimes($qty);
1919
    }
1920
1921
1922
    /**
1923
     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1924
     *
1925
     * @param int    $qty
1926
     * @param string $source
1927
     * @return bool whether we successfully reserved the ticket or not.
1928
     * @throws EE_Error
1929
     * @throws InvalidArgumentException
1930
     * @throws ReflectionException
1931
     * @throws InvalidDataTypeException
1932
     * @throws InvalidInterfaceException
1933
     * @deprecated 4.9.80.p
1934
     */
1935
    public function increase_reserved($qty = 1, $source = 'unknown')
1936
    {
1937
        EE_Error::doing_it_wrong(
1938
            __FUNCTION__,
1939
            esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
1940
            '4.9.80.p',
1941
            '5.0.0.p'
1942
        );
1943
        return $this->increaseReserved($qty);
1944
    }
1945
1946
1947
    /**
1948
     * Increases sold on related datetimes
1949
     *
1950
     * @param int $qty
1951
     * @return boolean indicating success
1952
     * @throws EE_Error
1953
     * @throws InvalidArgumentException
1954
     * @throws InvalidDataTypeException
1955
     * @throws InvalidInterfaceException
1956
     * @throws ReflectionException
1957
     * @deprecated 4.9.80.p
1958
     */
1959
    protected function _increase_reserved_for_datetimes($qty = 1)
1960
    {
1961
        EE_Error::doing_it_wrong(
1962
            __FUNCTION__,
1963
            esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
1964
            '4.9.80.p',
1965
            '5.0.0.p'
1966
        );
1967
        return $this->increaseReservedForDatetimes($qty);
1968
    }
1969
1970
1971
    /**
1972
     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1973
     *
1974
     * @param int    $qty
1975
     * @param bool   $adjust_datetimes
1976
     * @param string $source
1977
     * @return void
1978
     * @throws EE_Error
1979
     * @throws InvalidArgumentException
1980
     * @throws ReflectionException
1981
     * @throws InvalidDataTypeException
1982
     * @throws InvalidInterfaceException
1983
     * @deprecated 4.9.80.p
1984
     */
1985
    public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1986
    {
1987
        EE_Error::doing_it_wrong(
1988
            __FUNCTION__,
1989
            esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
1990
            '4.9.80.p',
1991
            '5.0.0.p'
1992
        );
1993
        $this->decreaseReserved($qty);
1994
    }
1995
1996
1997
    /**
1998
     * Decreases reserved on related datetimes
1999
     *
2000
     * @param int $qty
2001
     * @return void
2002
     * @throws EE_Error
2003
     * @throws InvalidArgumentException
2004
     * @throws ReflectionException
2005
     * @throws InvalidDataTypeException
2006
     * @throws InvalidInterfaceException
2007
     * @deprecated 4.9.80.p
2008
     */
2009
    protected function _decrease_reserved_for_datetimes($qty = 1)
2010
    {
2011
        EE_Error::doing_it_wrong(
2012
            __FUNCTION__,
2013
            esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
2014
            '4.9.80.p',
2015
            '5.0.0.p'
2016
        );
2017
        $this->decreaseReservedForDatetimes($qty);
2018
    }
2019
}
2020