Completed
Branch EDTR/master (8c09db)
by
unknown
09:27 queued 29s
created

EE_Ticket::tax_price_modifiers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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