Completed
Branch BUG/fix-php-warnings-in-pue-cl... (9829ce)
by
unknown
18:46 queued 09:04
created
core/domain/services/event/EventSpacesCalculator.php 1 patch
Indentation   +711 added lines, -711 removed lines patch added patch discarded remove patch
@@ -26,715 +26,715 @@
 block discarded – undo
26 26
 class EventSpacesCalculator
27 27
 {
28 28
 
29
-    /**
30
-     * @var EE_Event $event
31
-     */
32
-    private $event;
33
-
34
-    /**
35
-     * @var array $datetime_query_params
36
-     */
37
-    private $datetime_query_params;
38
-
39
-    /**
40
-     * @var EE_Ticket[] $active_tickets
41
-     */
42
-    private $active_tickets = array();
43
-
44
-    /**
45
-     * @var EE_Datetime[] $datetimes
46
-     */
47
-    private $datetimes = array();
48
-
49
-    /**
50
-     * Array of Ticket IDs grouped by Datetime
51
-     *
52
-     * @var array $datetimes
53
-     */
54
-    private $datetime_tickets = array();
55
-
56
-    /**
57
-     * Max spaces for each Datetime (reg limit - previous sold)
58
-     *
59
-     * @var array $datetime_spaces
60
-     */
61
-    private $datetime_spaces = array();
62
-
63
-    /**
64
-     * Array of Datetime IDs grouped by Ticket
65
-     *
66
-     * @var array[] $ticket_datetimes
67
-     */
68
-    private $ticket_datetimes = array();
69
-
70
-    /**
71
-     * maximum ticket quantities for each ticket (adjusted for reg limit)
72
-     *
73
-     * @var array $ticket_quantities
74
-     */
75
-    private $ticket_quantities = array();
76
-
77
-    /**
78
-     * total quantity of sold and reserved for each ticket
79
-     *
80
-     * @var array $tickets_sold
81
-     */
82
-    private $tickets_sold = array();
83
-
84
-    /**
85
-     * total spaces available across all datetimes
86
-     *
87
-     * @var array $total_spaces
88
-     */
89
-    private $total_spaces = array();
90
-
91
-    /**
92
-     * @var boolean $debug
93
-     */
94
-    private $debug = false; // true false
95
-
96
-    /**
97
-     * @var null|int $spaces_remaining
98
-     */
99
-    private $spaces_remaining;
100
-
101
-    /**
102
-     * @var null|int $total_spaces_available
103
-     */
104
-    private $total_spaces_available;
105
-
106
-
107
-    /**
108
-     * EventSpacesCalculator constructor.
109
-     *
110
-     * @param EE_Event $event
111
-     * @param array    $datetime_query_params
112
-     * @throws EE_Error
113
-     */
114
-    public function __construct(EE_Event $event, array $datetime_query_params = array())
115
-    {
116
-        if ($this->debug) {
117
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 1);
118
-            \EEH_Debug_Tools::printr((string) $event->ID(), 'For event', __FILE__, __LINE__);
119
-        }
120
-        $this->event = $event;
121
-        $this->datetime_query_params = $datetime_query_params + array('order_by' => array('DTT_reg_limit' => 'ASC'));
122
-        $this->setHooks();
123
-    }
124
-
125
-
126
-    /**
127
-     * @return void
128
-     */
129
-    private function setHooks()
130
-    {
131
-        add_action('AHEE__EE_Ticket__increase_sold', array($this, 'clearResults'));
132
-        add_action('AHEE__EE_Ticket__decrease_sold', array($this, 'clearResults'));
133
-        add_action('AHEE__EE_Datetime__increase_sold', array($this, 'clearResults'));
134
-        add_action('AHEE__EE_Datetime__decrease_sold', array($this, 'clearResults'));
135
-        add_action('AHEE__EE_Ticket__increase_reserved', array($this, 'clearResults'));
136
-        add_action('AHEE__EE_Ticket__decrease_reserved', array($this, 'clearResults'));
137
-        add_action('AHEE__EE_Datetime__increase_reserved', array($this, 'clearResults'));
138
-        add_action('AHEE__EE_Datetime__decrease_reserved', array($this, 'clearResults'));
139
-    }
140
-
141
-
142
-    /**
143
-     * @return void
144
-     */
145
-    public function clearResults()
146
-    {
147
-        if ($this->debug) {
148
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 1);
149
-        }
150
-        $this->spaces_remaining = null;
151
-        $this->total_spaces_available = null;
152
-    }
153
-
154
-
155
-    /**
156
-     * @return EE_Ticket[]
157
-     * @throws EE_Error
158
-     * @throws InvalidDataTypeException
159
-     * @throws InvalidInterfaceException
160
-     * @throws InvalidArgumentException
161
-     */
162
-    public function getActiveTickets()
163
-    {
164
-        if (empty($this->active_tickets)) {
165
-            $this->active_tickets = $this->event->tickets(
166
-                array(
167
-                    array('TKT_deleted' => false),
168
-                    'order_by' => array('TKT_qty' => 'ASC'),
169
-                )
170
-            );
171
-        }
172
-        return $this->active_tickets;
173
-    }
174
-
175
-
176
-    /**
177
-     * @param EE_Ticket[] $active_tickets
178
-     * @throws EE_Error
179
-     * @throws DomainException
180
-     * @throws UnexpectedEntityException
181
-     */
182
-    public function setActiveTickets(array $active_tickets = array())
183
-    {
184
-        if (! empty($active_tickets)) {
185
-            foreach ($active_tickets as $active_ticket) {
186
-                $this->validateTicket($active_ticket);
187
-            }
188
-            // sort incoming array by ticket quantity (asc)
189
-            usort(
190
-                $active_tickets,
191
-                function (EE_Ticket $a, EE_Ticket $b) {
192
-                    if ($a->qty() === $b->qty()) {
193
-                        return 0;
194
-                    }
195
-                    return ($a->qty() < $b->qty())
196
-                        ? -1
197
-                        : 1;
198
-                }
199
-            );
200
-        }
201
-        $this->active_tickets = $active_tickets;
202
-    }
203
-
204
-
205
-    /**
206
-     * @param $ticket
207
-     * @throws DomainException
208
-     * @throws EE_Error
209
-     * @throws UnexpectedEntityException
210
-     */
211
-    private function validateTicket($ticket)
212
-    {
213
-        if (! $ticket instanceof EE_Ticket) {
214
-            throw new DomainException(
215
-                esc_html__(
216
-                    'Invalid Ticket. Only EE_Ticket objects can be used to calculate event space availability.',
217
-                    'event_espresso'
218
-                )
219
-            );
220
-        }
221
-        if ($ticket->get_event_ID() !== $this->event->ID()) {
222
-            throw new DomainException(
223
-                sprintf(
224
-                    esc_html__(
225
-                        'An EE_Ticket for Event %1$d was supplied while calculating event space availability for Event %2$d.',
226
-                        'event_espresso'
227
-                    ),
228
-                    $ticket->get_event_ID(),
229
-                    $this->event->ID()
230
-                )
231
-            );
232
-        }
233
-    }
234
-
235
-
236
-    /**
237
-     * @return EE_Datetime[]
238
-     */
239
-    public function getDatetimes()
240
-    {
241
-        return $this->datetimes;
242
-    }
243
-
244
-
245
-    /**
246
-     * @param EE_Datetime $datetime
247
-     * @throws EE_Error
248
-     * @throws DomainException
249
-     */
250
-    public function setDatetime(EE_Datetime $datetime)
251
-    {
252
-        if ($datetime->event()->ID() !== $this->event->ID()) {
253
-            throw new DomainException(
254
-                sprintf(
255
-                    esc_html__(
256
-                        'An EE_Datetime for Event %1$d was supplied while calculating event space availability for Event %2$d.',
257
-                        'event_espresso'
258
-                    ),
259
-                    $datetime->event()->ID(),
260
-                    $this->event->ID()
261
-                )
262
-            );
263
-        }
264
-        $this->datetimes[ $datetime->ID() ] = $datetime;
265
-    }
266
-
267
-
268
-    /**
269
-     * calculate spaces remaining based on "saleable" tickets
270
-     *
271
-     * @return float|int
272
-     * @throws EE_Error
273
-     * @throws DomainException
274
-     * @throws UnexpectedEntityException
275
-     * @throws InvalidDataTypeException
276
-     * @throws InvalidInterfaceException
277
-     * @throws InvalidArgumentException
278
-     */
279
-    public function spacesRemaining()
280
-    {
281
-        if ($this->debug) {
282
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
283
-        }
284
-        if ($this->spaces_remaining === null) {
285
-            $this->initialize();
286
-            $this->spaces_remaining = $this->calculate();
287
-        }
288
-        return $this->spaces_remaining;
289
-    }
290
-
291
-
292
-    /**
293
-     * calculates total available spaces for an event with no regard for sold tickets
294
-     *
295
-     * @return int|float
296
-     * @throws EE_Error
297
-     * @throws DomainException
298
-     * @throws UnexpectedEntityException
299
-     * @throws InvalidDataTypeException
300
-     * @throws InvalidInterfaceException
301
-     * @throws InvalidArgumentException
302
-     */
303
-    public function totalSpacesAvailable()
304
-    {
305
-        if ($this->debug) {
306
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
307
-        }
308
-        if ($this->total_spaces_available === null) {
309
-            $this->initialize();
310
-            $this->total_spaces_available = $this->calculate(false);
311
-        }
312
-        return $this->total_spaces_available;
313
-    }
314
-
315
-
316
-    /**
317
-     * Loops through the active tickets for the event
318
-     * and builds a series of data arrays that will be used for calculating
319
-     * the total maximum available spaces, as well as the spaces remaining.
320
-     * Because ticket quantities affect datetime spaces and vice versa,
321
-     * we need to be constantly updating these data arrays as things change,
322
-     * which is the entire reason for their existence.
323
-     *
324
-     * @throws EE_Error
325
-     * @throws DomainException
326
-     * @throws UnexpectedEntityException
327
-     * @throws InvalidDataTypeException
328
-     * @throws InvalidInterfaceException
329
-     * @throws InvalidArgumentException
330
-     */
331
-    private function initialize()
332
-    {
333
-        if ($this->debug) {
334
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
335
-        }
336
-        $this->datetime_tickets = array();
337
-        $this->datetime_spaces = array();
338
-        $this->ticket_datetimes = array();
339
-        $this->ticket_quantities = array();
340
-        $this->tickets_sold = array();
341
-        $this->total_spaces = array();
342
-        $active_tickets = $this->getActiveTickets();
343
-        if (! empty($active_tickets)) {
344
-            foreach ($active_tickets as $ticket) {
345
-                $this->validateTicket($ticket);
346
-                // we need to index our data arrays using strings for the purpose of sorting,
347
-                // but we also need them to be unique, so  we'll just prepend a letter T to the ID
348
-                $ticket_identifier = "T{$ticket->ID()}";
349
-                // to start, we'll just consider the raw qty to be the maximum availability for this ticket,
350
-                // unless the ticket is past its "sell until" date, in which case the qty will be 0
351
-                $max_tickets = $ticket->is_expired() ? 0 : $ticket->qty();
352
-                // but we'll adjust that after looping over each datetime for the ticket and checking reg limits
353
-                $ticket_datetimes = $ticket->datetimes($this->datetime_query_params);
354
-                foreach ($ticket_datetimes as $datetime) {
355
-                    // save all datetimes
356
-                    $this->setDatetime($datetime);
357
-                    $datetime_identifier = "D{$datetime->ID()}";
358
-                    $reg_limit = $datetime->reg_limit();
359
-                    // ticket quantity can not exceed datetime reg limit
360
-                    $max_tickets = min($max_tickets, $reg_limit);
361
-                    // as described earlier, because we need to be able to constantly adjust numbers for things,
362
-                    // we are going to move all of our data into the following arrays:
363
-                    // datetime spaces initially represents the reg limit for each datetime,
364
-                    // but this will get adjusted as tickets are accounted for
365
-                    $this->datetime_spaces[ $datetime_identifier ] = $reg_limit;
366
-                    // just an array of ticket IDs grouped by datetime
367
-                    $this->datetime_tickets[ $datetime_identifier ][] = $ticket_identifier;
368
-                    // and an array of datetime IDs grouped by ticket
369
-                    $this->ticket_datetimes[ $ticket_identifier ][] = $datetime_identifier;
370
-                }
371
-                // total quantity of sold and reserved for each ticket
372
-                $this->tickets_sold[ $ticket_identifier ] = $ticket->sold() + $ticket->reserved();
373
-                // and the maximum ticket quantities for each ticket (adjusted for reg limit)
374
-                $this->ticket_quantities[ $ticket_identifier ] = $max_tickets;
375
-            }
376
-        }
377
-        // sort datetime spaces by reg limit, but maintain our string indexes
378
-        asort($this->datetime_spaces, SORT_NUMERIC);
379
-        // datetime tickets need to be sorted in the SAME order as the above array...
380
-        // so we'll just use array_merge() to take the structure of datetime_spaces
381
-        // but overwrite all of the data with that from datetime_tickets
382
-        $this->datetime_tickets = array_merge(
383
-            $this->datetime_spaces,
384
-            $this->datetime_tickets
385
-        );
386
-        if ($this->debug) {
387
-            \EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
388
-            \EEH_Debug_Tools::printr($this->datetime_tickets, 'datetime_tickets', __FILE__, __LINE__);
389
-            \EEH_Debug_Tools::printr($this->ticket_quantities, 'ticket_quantities', __FILE__, __LINE__);
390
-        }
391
-    }
392
-
393
-
394
-    /**
395
-     * performs calculations on initialized data
396
-     *
397
-     * @param bool $consider_sold
398
-     * @return int|float
399
-     */
400
-    private function calculate($consider_sold = true)
401
-    {
402
-        if ($this->debug) {
403
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
404
-            \EEH_Debug_Tools::printr($consider_sold, '$consider_sold', __FILE__, __LINE__);
405
-        }
406
-        if ($consider_sold) {
407
-            // subtract amounts sold from all ticket quantities and datetime spaces
408
-            $this->adjustTicketQuantitiesDueToSales();
409
-        }
410
-        foreach ($this->datetime_tickets as $datetime_identifier => $tickets) {
411
-            $this->trackAvailableSpacesForDatetimes($datetime_identifier, $tickets);
412
-        }
413
-        // total spaces available is just the sum of the spaces available for each datetime
414
-        $spaces_remaining = array_sum($this->total_spaces);
415
-        if ($this->debug) {
416
-            \EEH_Debug_Tools::printr($this->total_spaces, '$this->total_spaces', __FILE__, __LINE__);
417
-            \EEH_Debug_Tools::printr($this->tickets_sold, '$this->tickets_sold', __FILE__, __LINE__);
418
-            \EEH_Debug_Tools::printr($spaces_remaining, '$spaces_remaining', __FILE__, __LINE__);
419
-        }
420
-        return $spaces_remaining;
421
-    }
422
-
423
-
424
-    /**
425
-     * subtracts amount of  tickets sold from ticket quantities and datetime spaces
426
-     */
427
-    private function adjustTicketQuantitiesDueToSales()
428
-    {
429
-        if ($this->debug) {
430
-            \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
431
-        }
432
-        foreach ($this->tickets_sold as $ticket_identifier => $tickets_sold) {
433
-            if (isset($this->ticket_quantities[ $ticket_identifier ])) {
434
-                $this->ticket_quantities[ $ticket_identifier ] -= $tickets_sold;
435
-                // don't let values go below zero
436
-                $this->ticket_quantities[ $ticket_identifier ] = max(
437
-                    $this->ticket_quantities[ $ticket_identifier ],
438
-                    0
439
-                );
440
-                if ($this->debug) {
441
-                    \EEH_Debug_Tools::printr(
442
-                        "{$tickets_sold} sales for ticket {$ticket_identifier} ",
443
-                        'subtracting',
444
-                        __FILE__,
445
-                        __LINE__
446
-                    );
447
-                }
448
-            }
449
-            if (isset($this->ticket_datetimes[ $ticket_identifier ])
450
-                && is_array($this->ticket_datetimes[ $ticket_identifier ])
451
-            ) {
452
-                foreach ($this->ticket_datetimes[ $ticket_identifier ] as $ticket_datetime) {
453
-                    if (isset($this->ticket_quantities[ $ticket_identifier ])) {
454
-                        $this->datetime_spaces[ $ticket_datetime ] -= $tickets_sold;
455
-                        // don't let values go below zero
456
-                        $this->datetime_spaces[ $ticket_datetime ] = max(
457
-                            $this->datetime_spaces[ $ticket_datetime ],
458
-                            0
459
-                        );
460
-                        if ($this->debug) {
461
-                            \EEH_Debug_Tools::printr(
462
-                                "{$tickets_sold} sales for datetime {$ticket_datetime} ",
463
-                                'subtracting',
464
-                                __FILE__,
465
-                                __LINE__
466
-                            );
467
-                        }
468
-                    }
469
-                }
470
-            }
471
-        }
472
-    }
473
-
474
-
475
-    /**
476
-     * @param string $datetime_identifier
477
-     * @param array  $tickets
478
-     */
479
-    private function trackAvailableSpacesForDatetimes($datetime_identifier, array $tickets)
480
-    {
481
-        // make sure a reg limit is set for the datetime
482
-        $reg_limit = isset($this->datetime_spaces[ $datetime_identifier ])
483
-            ? $this->datetime_spaces[ $datetime_identifier ]
484
-            : 0;
485
-        // and bail if it is not
486
-        if (! $reg_limit) {
487
-            if ($this->debug) {
488
-                \EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__);
489
-            }
490
-            return;
491
-        }
492
-        if ($this->debug) {
493
-            \EEH_Debug_Tools::printr($datetime_identifier, '* $datetime_identifier', __FILE__, __LINE__, 1);
494
-            \EEH_Debug_Tools::printr(
495
-                "{$reg_limit}",
496
-                'REG LIMIT',
497
-                __FILE__,
498
-                __LINE__
499
-            );
500
-        }
501
-        // number of allocated spaces always starts at zero
502
-        $spaces_allocated = 0;
503
-        $this->total_spaces[ $datetime_identifier ] = 0;
504
-        foreach ($tickets as $ticket_identifier) {
505
-            $spaces_allocated = $this->calculateAvailableSpacesForTicket(
506
-                $datetime_identifier,
507
-                $reg_limit,
508
-                $ticket_identifier,
509
-                $spaces_allocated
510
-            );
511
-        }
512
-        // spaces can't be negative
513
-        $spaces_allocated = max($spaces_allocated, 0);
514
-        if ($spaces_allocated) {
515
-            // track any non-zero values
516
-            $this->total_spaces[ $datetime_identifier ] += $spaces_allocated;
517
-            if ($this->debug) {
518
-                \EEH_Debug_Tools::printr((string) $spaces_allocated, ' . $spaces_allocated: ', __FILE__, __LINE__);
519
-            }
520
-        } else {
521
-            if ($this->debug) {
522
-                \EEH_Debug_Tools::printr(' ', ' . NO TICKETS AVAILABLE FOR DATETIME', __FILE__, __LINE__);
523
-            }
524
-        }
525
-        if ($this->debug) {
526
-            \EEH_Debug_Tools::printr(
527
-                $this->total_spaces[ $datetime_identifier ],
528
-                '$total_spaces',
529
-                __FILE__,
530
-                __LINE__
531
-            );
532
-            \EEH_Debug_Tools::printr($this->ticket_quantities, '$ticket_quantities', __FILE__, __LINE__);
533
-            \EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
534
-        }
535
-    }
536
-
537
-
538
-    /**
539
-     * @param string $datetime_identifier
540
-     * @param int    $reg_limit
541
-     * @param string $ticket_identifier
542
-     * @param int    $spaces_allocated
543
-     * @return int
544
-     */
545
-    private function calculateAvailableSpacesForTicket(
546
-        $datetime_identifier,
547
-        $reg_limit,
548
-        $ticket_identifier,
549
-        $spaces_allocated
550
-    ) {
551
-        // make sure ticket quantity is set
552
-        $ticket_quantity = isset($this->ticket_quantities[ $ticket_identifier ])
553
-            ? $this->ticket_quantities[ $ticket_identifier ]
554
-            : 0;
555
-        if ($this->debug) {
556
-            \EEH_Debug_Tools::printr("{$spaces_allocated}", '$spaces_allocated', __FILE__, __LINE__);
557
-            \EEH_Debug_Tools::printr(
558
-                "{$ticket_quantity}",
559
-                "ticket $ticket_identifier quantity: ",
560
-                __FILE__,
561
-                __LINE__,
562
-                2
563
-            );
564
-        }
565
-        if ($ticket_quantity) {
566
-            if ($this->debug) {
567
-                \EEH_Debug_Tools::printr(
568
-                    ($spaces_allocated <= $reg_limit)
569
-                        ? 'true'
570
-                        : 'false',
571
-                    ' . spaces_allocated <= reg_limit = ',
572
-                    __FILE__,
573
-                    __LINE__
574
-                );
575
-            }
576
-            // if the datetime is NOT at full capacity yet
577
-            if ($spaces_allocated <= $reg_limit) {
578
-                // then the maximum ticket quantity we can allocate is the lowest value of either:
579
-                //  the number of remaining spaces for the datetime, which is the limit - spaces already taken
580
-                //  or the maximum ticket quantity
581
-                $ticket_quantity = min($reg_limit - $spaces_allocated, $ticket_quantity);
582
-                // adjust the available quantity in our tracking array
583
-                $this->ticket_quantities[ $ticket_identifier ] -= $ticket_quantity;
584
-                // and increment spaces allocated for this datetime
585
-                $spaces_allocated += $ticket_quantity;
586
-                $at_capacity = $spaces_allocated >= $reg_limit;
587
-                if ($this->debug) {
588
-                    \EEH_Debug_Tools::printr(
589
-                        "{$ticket_quantity} {$ticket_identifier} tickets",
590
-                        ' > > allocate ',
591
-                        __FILE__,
592
-                        __LINE__,
593
-                        3
594
-                    );
595
-                    if ($at_capacity) {
596
-                        \EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__, 3);
597
-                    }
598
-                }
599
-                // now adjust all other datetimes that allow access to this ticket
600
-                $this->adjustDatetimes(
601
-                    $datetime_identifier,
602
-                    $ticket_identifier,
603
-                    $ticket_quantity,
604
-                    $at_capacity
605
-                );
606
-            }
607
-        }
608
-        return $spaces_allocated;
609
-    }
610
-
611
-
612
-    /**
613
-     * subtracts ticket amounts from all datetime reg limits
614
-     * that allow access to the ticket specified,
615
-     * because that ticket could be used
616
-     * to attend any of the datetimes it has access to
617
-     *
618
-     * @param string $datetime_identifier
619
-     * @param string $ticket_identifier
620
-     * @param bool   $at_capacity
621
-     * @param int    $ticket_quantity
622
-     */
623
-    private function adjustDatetimes(
624
-        $datetime_identifier,
625
-        $ticket_identifier,
626
-        $ticket_quantity,
627
-        $at_capacity
628
-    ) {
629
-        /** @var array $datetime_tickets */
630
-        foreach ($this->datetime_tickets as $datetime_ID => $datetime_tickets) {
631
-            if ($datetime_ID !== $datetime_identifier || ! is_array($datetime_tickets)) {
632
-                continue;
633
-            }
634
-            $adjusted = $this->adjustDatetimeSpaces(
635
-                $datetime_ID,
636
-                $ticket_identifier,
637
-                $ticket_quantity
638
-            );
639
-            // skip to next ticket if nothing changed
640
-            if (! ($adjusted || $at_capacity)) {
641
-                continue;
642
-            }
643
-            // then all of it's tickets are now unavailable
644
-            foreach ($datetime_tickets as $datetime_ticket) {
645
-                if (($ticket_identifier === $datetime_ticket || $at_capacity)
646
-                    && isset($this->ticket_quantities[ $datetime_ticket ])
647
-                    && $this->ticket_quantities[ $datetime_ticket ] > 0
648
-                ) {
649
-                    if ($this->debug) {
650
-                        \EEH_Debug_Tools::printr(
651
-                            $datetime_ticket,
652
-                            ' . . . adjust ticket quantities for',
653
-                            __FILE__,
654
-                            __LINE__
655
-                        );
656
-                    }
657
-                    // if this datetime is at full capacity, set any tracked available quantities to zero
658
-                    // otherwise just subtract the ticket quantity
659
-                    $new_quantity = $at_capacity
660
-                        ? 0
661
-                        : $this->ticket_quantities[ $datetime_ticket ] - $ticket_quantity;
662
-                    // don't let ticket quantity go below zero
663
-                    $this->ticket_quantities[ $datetime_ticket ] = max($new_quantity, 0);
664
-                    if ($this->debug) {
665
-                        \EEH_Debug_Tools::printr(
666
-                            $at_capacity
667
-                                ? "0 because Datetime {$datetime_identifier} is at capacity"
668
-                                : "{$this->ticket_quantities[ $datetime_ticket ]}",
669
-                            " . . . . {$datetime_ticket} quantity set to ",
670
-                            __FILE__,
671
-                            __LINE__
672
-                        );
673
-                    }
674
-                }
675
-                // but we also need to adjust spaces for any other datetimes this ticket has access to
676
-                if ($datetime_ticket === $ticket_identifier) {
677
-                    if (isset($this->ticket_datetimes[ $datetime_ticket ])
678
-                        && is_array($this->ticket_datetimes[ $datetime_ticket ])
679
-                    ) {
680
-                        if ($this->debug) {
681
-                            \EEH_Debug_Tools::printr(
682
-                                $datetime_ticket,
683
-                                ' . . adjust other Datetimes for',
684
-                                __FILE__,
685
-                                __LINE__
686
-                            );
687
-                        }
688
-                        foreach ($this->ticket_datetimes[ $datetime_ticket ] as $datetime) {
689
-                            // don't adjust the current datetime twice
690
-                            if ($datetime !== $datetime_identifier) {
691
-                                $this->adjustDatetimeSpaces(
692
-                                    $datetime,
693
-                                    $datetime_ticket,
694
-                                    $ticket_quantity
695
-                                );
696
-                            }
697
-                        }
698
-                    }
699
-                }
700
-            }
701
-        }
702
-    }
703
-
704
-    private function adjustDatetimeSpaces($datetime_identifier, $ticket_identifier, $ticket_quantity = 0)
705
-    {
706
-        // does datetime have spaces available?
707
-        // and does the supplied ticket have access to this datetime ?
708
-        if ($this->datetime_spaces[ $datetime_identifier ] > 0
709
-            && isset($this->datetime_spaces[ $datetime_identifier ], $this->datetime_tickets[ $datetime_identifier ])
710
-            && in_array($ticket_identifier, $this->datetime_tickets[ $datetime_identifier ], true)
711
-        ) {
712
-            if ($this->debug) {
713
-                \EEH_Debug_Tools::printr($datetime_identifier, ' . . adjust Datetime Spaces for', __FILE__, __LINE__);
714
-                \EEH_Debug_Tools::printr(
715
-                    "{$this->datetime_spaces[ $datetime_identifier ]}",
716
-                    " . . current  {$datetime_identifier} spaces available",
717
-                    __FILE__,
718
-                    __LINE__
719
-                );
720
-            }
721
-            // then decrement the available spaces for the datetime
722
-            $this->datetime_spaces[ $datetime_identifier ] -= $ticket_quantity;
723
-            // but don't let quantities go below zero
724
-            $this->datetime_spaces[ $datetime_identifier ] = max(
725
-                $this->datetime_spaces[ $datetime_identifier ],
726
-                0
727
-            );
728
-            if ($this->debug) {
729
-                \EEH_Debug_Tools::printr(
730
-                    "{$ticket_quantity}",
731
-                    " . . . {$datetime_identifier} capacity reduced by",
732
-                    __FILE__,
733
-                    __LINE__
734
-                );
735
-            }
736
-            return true;
737
-        }
738
-        return false;
739
-    }
29
+	/**
30
+	 * @var EE_Event $event
31
+	 */
32
+	private $event;
33
+
34
+	/**
35
+	 * @var array $datetime_query_params
36
+	 */
37
+	private $datetime_query_params;
38
+
39
+	/**
40
+	 * @var EE_Ticket[] $active_tickets
41
+	 */
42
+	private $active_tickets = array();
43
+
44
+	/**
45
+	 * @var EE_Datetime[] $datetimes
46
+	 */
47
+	private $datetimes = array();
48
+
49
+	/**
50
+	 * Array of Ticket IDs grouped by Datetime
51
+	 *
52
+	 * @var array $datetimes
53
+	 */
54
+	private $datetime_tickets = array();
55
+
56
+	/**
57
+	 * Max spaces for each Datetime (reg limit - previous sold)
58
+	 *
59
+	 * @var array $datetime_spaces
60
+	 */
61
+	private $datetime_spaces = array();
62
+
63
+	/**
64
+	 * Array of Datetime IDs grouped by Ticket
65
+	 *
66
+	 * @var array[] $ticket_datetimes
67
+	 */
68
+	private $ticket_datetimes = array();
69
+
70
+	/**
71
+	 * maximum ticket quantities for each ticket (adjusted for reg limit)
72
+	 *
73
+	 * @var array $ticket_quantities
74
+	 */
75
+	private $ticket_quantities = array();
76
+
77
+	/**
78
+	 * total quantity of sold and reserved for each ticket
79
+	 *
80
+	 * @var array $tickets_sold
81
+	 */
82
+	private $tickets_sold = array();
83
+
84
+	/**
85
+	 * total spaces available across all datetimes
86
+	 *
87
+	 * @var array $total_spaces
88
+	 */
89
+	private $total_spaces = array();
90
+
91
+	/**
92
+	 * @var boolean $debug
93
+	 */
94
+	private $debug = false; // true false
95
+
96
+	/**
97
+	 * @var null|int $spaces_remaining
98
+	 */
99
+	private $spaces_remaining;
100
+
101
+	/**
102
+	 * @var null|int $total_spaces_available
103
+	 */
104
+	private $total_spaces_available;
105
+
106
+
107
+	/**
108
+	 * EventSpacesCalculator constructor.
109
+	 *
110
+	 * @param EE_Event $event
111
+	 * @param array    $datetime_query_params
112
+	 * @throws EE_Error
113
+	 */
114
+	public function __construct(EE_Event $event, array $datetime_query_params = array())
115
+	{
116
+		if ($this->debug) {
117
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 1);
118
+			\EEH_Debug_Tools::printr((string) $event->ID(), 'For event', __FILE__, __LINE__);
119
+		}
120
+		$this->event = $event;
121
+		$this->datetime_query_params = $datetime_query_params + array('order_by' => array('DTT_reg_limit' => 'ASC'));
122
+		$this->setHooks();
123
+	}
124
+
125
+
126
+	/**
127
+	 * @return void
128
+	 */
129
+	private function setHooks()
130
+	{
131
+		add_action('AHEE__EE_Ticket__increase_sold', array($this, 'clearResults'));
132
+		add_action('AHEE__EE_Ticket__decrease_sold', array($this, 'clearResults'));
133
+		add_action('AHEE__EE_Datetime__increase_sold', array($this, 'clearResults'));
134
+		add_action('AHEE__EE_Datetime__decrease_sold', array($this, 'clearResults'));
135
+		add_action('AHEE__EE_Ticket__increase_reserved', array($this, 'clearResults'));
136
+		add_action('AHEE__EE_Ticket__decrease_reserved', array($this, 'clearResults'));
137
+		add_action('AHEE__EE_Datetime__increase_reserved', array($this, 'clearResults'));
138
+		add_action('AHEE__EE_Datetime__decrease_reserved', array($this, 'clearResults'));
139
+	}
140
+
141
+
142
+	/**
143
+	 * @return void
144
+	 */
145
+	public function clearResults()
146
+	{
147
+		if ($this->debug) {
148
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 1);
149
+		}
150
+		$this->spaces_remaining = null;
151
+		$this->total_spaces_available = null;
152
+	}
153
+
154
+
155
+	/**
156
+	 * @return EE_Ticket[]
157
+	 * @throws EE_Error
158
+	 * @throws InvalidDataTypeException
159
+	 * @throws InvalidInterfaceException
160
+	 * @throws InvalidArgumentException
161
+	 */
162
+	public function getActiveTickets()
163
+	{
164
+		if (empty($this->active_tickets)) {
165
+			$this->active_tickets = $this->event->tickets(
166
+				array(
167
+					array('TKT_deleted' => false),
168
+					'order_by' => array('TKT_qty' => 'ASC'),
169
+				)
170
+			);
171
+		}
172
+		return $this->active_tickets;
173
+	}
174
+
175
+
176
+	/**
177
+	 * @param EE_Ticket[] $active_tickets
178
+	 * @throws EE_Error
179
+	 * @throws DomainException
180
+	 * @throws UnexpectedEntityException
181
+	 */
182
+	public function setActiveTickets(array $active_tickets = array())
183
+	{
184
+		if (! empty($active_tickets)) {
185
+			foreach ($active_tickets as $active_ticket) {
186
+				$this->validateTicket($active_ticket);
187
+			}
188
+			// sort incoming array by ticket quantity (asc)
189
+			usort(
190
+				$active_tickets,
191
+				function (EE_Ticket $a, EE_Ticket $b) {
192
+					if ($a->qty() === $b->qty()) {
193
+						return 0;
194
+					}
195
+					return ($a->qty() < $b->qty())
196
+						? -1
197
+						: 1;
198
+				}
199
+			);
200
+		}
201
+		$this->active_tickets = $active_tickets;
202
+	}
203
+
204
+
205
+	/**
206
+	 * @param $ticket
207
+	 * @throws DomainException
208
+	 * @throws EE_Error
209
+	 * @throws UnexpectedEntityException
210
+	 */
211
+	private function validateTicket($ticket)
212
+	{
213
+		if (! $ticket instanceof EE_Ticket) {
214
+			throw new DomainException(
215
+				esc_html__(
216
+					'Invalid Ticket. Only EE_Ticket objects can be used to calculate event space availability.',
217
+					'event_espresso'
218
+				)
219
+			);
220
+		}
221
+		if ($ticket->get_event_ID() !== $this->event->ID()) {
222
+			throw new DomainException(
223
+				sprintf(
224
+					esc_html__(
225
+						'An EE_Ticket for Event %1$d was supplied while calculating event space availability for Event %2$d.',
226
+						'event_espresso'
227
+					),
228
+					$ticket->get_event_ID(),
229
+					$this->event->ID()
230
+				)
231
+			);
232
+		}
233
+	}
234
+
235
+
236
+	/**
237
+	 * @return EE_Datetime[]
238
+	 */
239
+	public function getDatetimes()
240
+	{
241
+		return $this->datetimes;
242
+	}
243
+
244
+
245
+	/**
246
+	 * @param EE_Datetime $datetime
247
+	 * @throws EE_Error
248
+	 * @throws DomainException
249
+	 */
250
+	public function setDatetime(EE_Datetime $datetime)
251
+	{
252
+		if ($datetime->event()->ID() !== $this->event->ID()) {
253
+			throw new DomainException(
254
+				sprintf(
255
+					esc_html__(
256
+						'An EE_Datetime for Event %1$d was supplied while calculating event space availability for Event %2$d.',
257
+						'event_espresso'
258
+					),
259
+					$datetime->event()->ID(),
260
+					$this->event->ID()
261
+				)
262
+			);
263
+		}
264
+		$this->datetimes[ $datetime->ID() ] = $datetime;
265
+	}
266
+
267
+
268
+	/**
269
+	 * calculate spaces remaining based on "saleable" tickets
270
+	 *
271
+	 * @return float|int
272
+	 * @throws EE_Error
273
+	 * @throws DomainException
274
+	 * @throws UnexpectedEntityException
275
+	 * @throws InvalidDataTypeException
276
+	 * @throws InvalidInterfaceException
277
+	 * @throws InvalidArgumentException
278
+	 */
279
+	public function spacesRemaining()
280
+	{
281
+		if ($this->debug) {
282
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
283
+		}
284
+		if ($this->spaces_remaining === null) {
285
+			$this->initialize();
286
+			$this->spaces_remaining = $this->calculate();
287
+		}
288
+		return $this->spaces_remaining;
289
+	}
290
+
291
+
292
+	/**
293
+	 * calculates total available spaces for an event with no regard for sold tickets
294
+	 *
295
+	 * @return int|float
296
+	 * @throws EE_Error
297
+	 * @throws DomainException
298
+	 * @throws UnexpectedEntityException
299
+	 * @throws InvalidDataTypeException
300
+	 * @throws InvalidInterfaceException
301
+	 * @throws InvalidArgumentException
302
+	 */
303
+	public function totalSpacesAvailable()
304
+	{
305
+		if ($this->debug) {
306
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
307
+		}
308
+		if ($this->total_spaces_available === null) {
309
+			$this->initialize();
310
+			$this->total_spaces_available = $this->calculate(false);
311
+		}
312
+		return $this->total_spaces_available;
313
+	}
314
+
315
+
316
+	/**
317
+	 * Loops through the active tickets for the event
318
+	 * and builds a series of data arrays that will be used for calculating
319
+	 * the total maximum available spaces, as well as the spaces remaining.
320
+	 * Because ticket quantities affect datetime spaces and vice versa,
321
+	 * we need to be constantly updating these data arrays as things change,
322
+	 * which is the entire reason for their existence.
323
+	 *
324
+	 * @throws EE_Error
325
+	 * @throws DomainException
326
+	 * @throws UnexpectedEntityException
327
+	 * @throws InvalidDataTypeException
328
+	 * @throws InvalidInterfaceException
329
+	 * @throws InvalidArgumentException
330
+	 */
331
+	private function initialize()
332
+	{
333
+		if ($this->debug) {
334
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
335
+		}
336
+		$this->datetime_tickets = array();
337
+		$this->datetime_spaces = array();
338
+		$this->ticket_datetimes = array();
339
+		$this->ticket_quantities = array();
340
+		$this->tickets_sold = array();
341
+		$this->total_spaces = array();
342
+		$active_tickets = $this->getActiveTickets();
343
+		if (! empty($active_tickets)) {
344
+			foreach ($active_tickets as $ticket) {
345
+				$this->validateTicket($ticket);
346
+				// we need to index our data arrays using strings for the purpose of sorting,
347
+				// but we also need them to be unique, so  we'll just prepend a letter T to the ID
348
+				$ticket_identifier = "T{$ticket->ID()}";
349
+				// to start, we'll just consider the raw qty to be the maximum availability for this ticket,
350
+				// unless the ticket is past its "sell until" date, in which case the qty will be 0
351
+				$max_tickets = $ticket->is_expired() ? 0 : $ticket->qty();
352
+				// but we'll adjust that after looping over each datetime for the ticket and checking reg limits
353
+				$ticket_datetimes = $ticket->datetimes($this->datetime_query_params);
354
+				foreach ($ticket_datetimes as $datetime) {
355
+					// save all datetimes
356
+					$this->setDatetime($datetime);
357
+					$datetime_identifier = "D{$datetime->ID()}";
358
+					$reg_limit = $datetime->reg_limit();
359
+					// ticket quantity can not exceed datetime reg limit
360
+					$max_tickets = min($max_tickets, $reg_limit);
361
+					// as described earlier, because we need to be able to constantly adjust numbers for things,
362
+					// we are going to move all of our data into the following arrays:
363
+					// datetime spaces initially represents the reg limit for each datetime,
364
+					// but this will get adjusted as tickets are accounted for
365
+					$this->datetime_spaces[ $datetime_identifier ] = $reg_limit;
366
+					// just an array of ticket IDs grouped by datetime
367
+					$this->datetime_tickets[ $datetime_identifier ][] = $ticket_identifier;
368
+					// and an array of datetime IDs grouped by ticket
369
+					$this->ticket_datetimes[ $ticket_identifier ][] = $datetime_identifier;
370
+				}
371
+				// total quantity of sold and reserved for each ticket
372
+				$this->tickets_sold[ $ticket_identifier ] = $ticket->sold() + $ticket->reserved();
373
+				// and the maximum ticket quantities for each ticket (adjusted for reg limit)
374
+				$this->ticket_quantities[ $ticket_identifier ] = $max_tickets;
375
+			}
376
+		}
377
+		// sort datetime spaces by reg limit, but maintain our string indexes
378
+		asort($this->datetime_spaces, SORT_NUMERIC);
379
+		// datetime tickets need to be sorted in the SAME order as the above array...
380
+		// so we'll just use array_merge() to take the structure of datetime_spaces
381
+		// but overwrite all of the data with that from datetime_tickets
382
+		$this->datetime_tickets = array_merge(
383
+			$this->datetime_spaces,
384
+			$this->datetime_tickets
385
+		);
386
+		if ($this->debug) {
387
+			\EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
388
+			\EEH_Debug_Tools::printr($this->datetime_tickets, 'datetime_tickets', __FILE__, __LINE__);
389
+			\EEH_Debug_Tools::printr($this->ticket_quantities, 'ticket_quantities', __FILE__, __LINE__);
390
+		}
391
+	}
392
+
393
+
394
+	/**
395
+	 * performs calculations on initialized data
396
+	 *
397
+	 * @param bool $consider_sold
398
+	 * @return int|float
399
+	 */
400
+	private function calculate($consider_sold = true)
401
+	{
402
+		if ($this->debug) {
403
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
404
+			\EEH_Debug_Tools::printr($consider_sold, '$consider_sold', __FILE__, __LINE__);
405
+		}
406
+		if ($consider_sold) {
407
+			// subtract amounts sold from all ticket quantities and datetime spaces
408
+			$this->adjustTicketQuantitiesDueToSales();
409
+		}
410
+		foreach ($this->datetime_tickets as $datetime_identifier => $tickets) {
411
+			$this->trackAvailableSpacesForDatetimes($datetime_identifier, $tickets);
412
+		}
413
+		// total spaces available is just the sum of the spaces available for each datetime
414
+		$spaces_remaining = array_sum($this->total_spaces);
415
+		if ($this->debug) {
416
+			\EEH_Debug_Tools::printr($this->total_spaces, '$this->total_spaces', __FILE__, __LINE__);
417
+			\EEH_Debug_Tools::printr($this->tickets_sold, '$this->tickets_sold', __FILE__, __LINE__);
418
+			\EEH_Debug_Tools::printr($spaces_remaining, '$spaces_remaining', __FILE__, __LINE__);
419
+		}
420
+		return $spaces_remaining;
421
+	}
422
+
423
+
424
+	/**
425
+	 * subtracts amount of  tickets sold from ticket quantities and datetime spaces
426
+	 */
427
+	private function adjustTicketQuantitiesDueToSales()
428
+	{
429
+		if ($this->debug) {
430
+			\EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 2);
431
+		}
432
+		foreach ($this->tickets_sold as $ticket_identifier => $tickets_sold) {
433
+			if (isset($this->ticket_quantities[ $ticket_identifier ])) {
434
+				$this->ticket_quantities[ $ticket_identifier ] -= $tickets_sold;
435
+				// don't let values go below zero
436
+				$this->ticket_quantities[ $ticket_identifier ] = max(
437
+					$this->ticket_quantities[ $ticket_identifier ],
438
+					0
439
+				);
440
+				if ($this->debug) {
441
+					\EEH_Debug_Tools::printr(
442
+						"{$tickets_sold} sales for ticket {$ticket_identifier} ",
443
+						'subtracting',
444
+						__FILE__,
445
+						__LINE__
446
+					);
447
+				}
448
+			}
449
+			if (isset($this->ticket_datetimes[ $ticket_identifier ])
450
+				&& is_array($this->ticket_datetimes[ $ticket_identifier ])
451
+			) {
452
+				foreach ($this->ticket_datetimes[ $ticket_identifier ] as $ticket_datetime) {
453
+					if (isset($this->ticket_quantities[ $ticket_identifier ])) {
454
+						$this->datetime_spaces[ $ticket_datetime ] -= $tickets_sold;
455
+						// don't let values go below zero
456
+						$this->datetime_spaces[ $ticket_datetime ] = max(
457
+							$this->datetime_spaces[ $ticket_datetime ],
458
+							0
459
+						);
460
+						if ($this->debug) {
461
+							\EEH_Debug_Tools::printr(
462
+								"{$tickets_sold} sales for datetime {$ticket_datetime} ",
463
+								'subtracting',
464
+								__FILE__,
465
+								__LINE__
466
+							);
467
+						}
468
+					}
469
+				}
470
+			}
471
+		}
472
+	}
473
+
474
+
475
+	/**
476
+	 * @param string $datetime_identifier
477
+	 * @param array  $tickets
478
+	 */
479
+	private function trackAvailableSpacesForDatetimes($datetime_identifier, array $tickets)
480
+	{
481
+		// make sure a reg limit is set for the datetime
482
+		$reg_limit = isset($this->datetime_spaces[ $datetime_identifier ])
483
+			? $this->datetime_spaces[ $datetime_identifier ]
484
+			: 0;
485
+		// and bail if it is not
486
+		if (! $reg_limit) {
487
+			if ($this->debug) {
488
+				\EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__);
489
+			}
490
+			return;
491
+		}
492
+		if ($this->debug) {
493
+			\EEH_Debug_Tools::printr($datetime_identifier, '* $datetime_identifier', __FILE__, __LINE__, 1);
494
+			\EEH_Debug_Tools::printr(
495
+				"{$reg_limit}",
496
+				'REG LIMIT',
497
+				__FILE__,
498
+				__LINE__
499
+			);
500
+		}
501
+		// number of allocated spaces always starts at zero
502
+		$spaces_allocated = 0;
503
+		$this->total_spaces[ $datetime_identifier ] = 0;
504
+		foreach ($tickets as $ticket_identifier) {
505
+			$spaces_allocated = $this->calculateAvailableSpacesForTicket(
506
+				$datetime_identifier,
507
+				$reg_limit,
508
+				$ticket_identifier,
509
+				$spaces_allocated
510
+			);
511
+		}
512
+		// spaces can't be negative
513
+		$spaces_allocated = max($spaces_allocated, 0);
514
+		if ($spaces_allocated) {
515
+			// track any non-zero values
516
+			$this->total_spaces[ $datetime_identifier ] += $spaces_allocated;
517
+			if ($this->debug) {
518
+				\EEH_Debug_Tools::printr((string) $spaces_allocated, ' . $spaces_allocated: ', __FILE__, __LINE__);
519
+			}
520
+		} else {
521
+			if ($this->debug) {
522
+				\EEH_Debug_Tools::printr(' ', ' . NO TICKETS AVAILABLE FOR DATETIME', __FILE__, __LINE__);
523
+			}
524
+		}
525
+		if ($this->debug) {
526
+			\EEH_Debug_Tools::printr(
527
+				$this->total_spaces[ $datetime_identifier ],
528
+				'$total_spaces',
529
+				__FILE__,
530
+				__LINE__
531
+			);
532
+			\EEH_Debug_Tools::printr($this->ticket_quantities, '$ticket_quantities', __FILE__, __LINE__);
533
+			\EEH_Debug_Tools::printr($this->datetime_spaces, 'datetime_spaces', __FILE__, __LINE__);
534
+		}
535
+	}
536
+
537
+
538
+	/**
539
+	 * @param string $datetime_identifier
540
+	 * @param int    $reg_limit
541
+	 * @param string $ticket_identifier
542
+	 * @param int    $spaces_allocated
543
+	 * @return int
544
+	 */
545
+	private function calculateAvailableSpacesForTicket(
546
+		$datetime_identifier,
547
+		$reg_limit,
548
+		$ticket_identifier,
549
+		$spaces_allocated
550
+	) {
551
+		// make sure ticket quantity is set
552
+		$ticket_quantity = isset($this->ticket_quantities[ $ticket_identifier ])
553
+			? $this->ticket_quantities[ $ticket_identifier ]
554
+			: 0;
555
+		if ($this->debug) {
556
+			\EEH_Debug_Tools::printr("{$spaces_allocated}", '$spaces_allocated', __FILE__, __LINE__);
557
+			\EEH_Debug_Tools::printr(
558
+				"{$ticket_quantity}",
559
+				"ticket $ticket_identifier quantity: ",
560
+				__FILE__,
561
+				__LINE__,
562
+				2
563
+			);
564
+		}
565
+		if ($ticket_quantity) {
566
+			if ($this->debug) {
567
+				\EEH_Debug_Tools::printr(
568
+					($spaces_allocated <= $reg_limit)
569
+						? 'true'
570
+						: 'false',
571
+					' . spaces_allocated <= reg_limit = ',
572
+					__FILE__,
573
+					__LINE__
574
+				);
575
+			}
576
+			// if the datetime is NOT at full capacity yet
577
+			if ($spaces_allocated <= $reg_limit) {
578
+				// then the maximum ticket quantity we can allocate is the lowest value of either:
579
+				//  the number of remaining spaces for the datetime, which is the limit - spaces already taken
580
+				//  or the maximum ticket quantity
581
+				$ticket_quantity = min($reg_limit - $spaces_allocated, $ticket_quantity);
582
+				// adjust the available quantity in our tracking array
583
+				$this->ticket_quantities[ $ticket_identifier ] -= $ticket_quantity;
584
+				// and increment spaces allocated for this datetime
585
+				$spaces_allocated += $ticket_quantity;
586
+				$at_capacity = $spaces_allocated >= $reg_limit;
587
+				if ($this->debug) {
588
+					\EEH_Debug_Tools::printr(
589
+						"{$ticket_quantity} {$ticket_identifier} tickets",
590
+						' > > allocate ',
591
+						__FILE__,
592
+						__LINE__,
593
+						3
594
+					);
595
+					if ($at_capacity) {
596
+						\EEH_Debug_Tools::printr('AT CAPACITY', " . {$datetime_identifier}", __FILE__, __LINE__, 3);
597
+					}
598
+				}
599
+				// now adjust all other datetimes that allow access to this ticket
600
+				$this->adjustDatetimes(
601
+					$datetime_identifier,
602
+					$ticket_identifier,
603
+					$ticket_quantity,
604
+					$at_capacity
605
+				);
606
+			}
607
+		}
608
+		return $spaces_allocated;
609
+	}
610
+
611
+
612
+	/**
613
+	 * subtracts ticket amounts from all datetime reg limits
614
+	 * that allow access to the ticket specified,
615
+	 * because that ticket could be used
616
+	 * to attend any of the datetimes it has access to
617
+	 *
618
+	 * @param string $datetime_identifier
619
+	 * @param string $ticket_identifier
620
+	 * @param bool   $at_capacity
621
+	 * @param int    $ticket_quantity
622
+	 */
623
+	private function adjustDatetimes(
624
+		$datetime_identifier,
625
+		$ticket_identifier,
626
+		$ticket_quantity,
627
+		$at_capacity
628
+	) {
629
+		/** @var array $datetime_tickets */
630
+		foreach ($this->datetime_tickets as $datetime_ID => $datetime_tickets) {
631
+			if ($datetime_ID !== $datetime_identifier || ! is_array($datetime_tickets)) {
632
+				continue;
633
+			}
634
+			$adjusted = $this->adjustDatetimeSpaces(
635
+				$datetime_ID,
636
+				$ticket_identifier,
637
+				$ticket_quantity
638
+			);
639
+			// skip to next ticket if nothing changed
640
+			if (! ($adjusted || $at_capacity)) {
641
+				continue;
642
+			}
643
+			// then all of it's tickets are now unavailable
644
+			foreach ($datetime_tickets as $datetime_ticket) {
645
+				if (($ticket_identifier === $datetime_ticket || $at_capacity)
646
+					&& isset($this->ticket_quantities[ $datetime_ticket ])
647
+					&& $this->ticket_quantities[ $datetime_ticket ] > 0
648
+				) {
649
+					if ($this->debug) {
650
+						\EEH_Debug_Tools::printr(
651
+							$datetime_ticket,
652
+							' . . . adjust ticket quantities for',
653
+							__FILE__,
654
+							__LINE__
655
+						);
656
+					}
657
+					// if this datetime is at full capacity, set any tracked available quantities to zero
658
+					// otherwise just subtract the ticket quantity
659
+					$new_quantity = $at_capacity
660
+						? 0
661
+						: $this->ticket_quantities[ $datetime_ticket ] - $ticket_quantity;
662
+					// don't let ticket quantity go below zero
663
+					$this->ticket_quantities[ $datetime_ticket ] = max($new_quantity, 0);
664
+					if ($this->debug) {
665
+						\EEH_Debug_Tools::printr(
666
+							$at_capacity
667
+								? "0 because Datetime {$datetime_identifier} is at capacity"
668
+								: "{$this->ticket_quantities[ $datetime_ticket ]}",
669
+							" . . . . {$datetime_ticket} quantity set to ",
670
+							__FILE__,
671
+							__LINE__
672
+						);
673
+					}
674
+				}
675
+				// but we also need to adjust spaces for any other datetimes this ticket has access to
676
+				if ($datetime_ticket === $ticket_identifier) {
677
+					if (isset($this->ticket_datetimes[ $datetime_ticket ])
678
+						&& is_array($this->ticket_datetimes[ $datetime_ticket ])
679
+					) {
680
+						if ($this->debug) {
681
+							\EEH_Debug_Tools::printr(
682
+								$datetime_ticket,
683
+								' . . adjust other Datetimes for',
684
+								__FILE__,
685
+								__LINE__
686
+							);
687
+						}
688
+						foreach ($this->ticket_datetimes[ $datetime_ticket ] as $datetime) {
689
+							// don't adjust the current datetime twice
690
+							if ($datetime !== $datetime_identifier) {
691
+								$this->adjustDatetimeSpaces(
692
+									$datetime,
693
+									$datetime_ticket,
694
+									$ticket_quantity
695
+								);
696
+							}
697
+						}
698
+					}
699
+				}
700
+			}
701
+		}
702
+	}
703
+
704
+	private function adjustDatetimeSpaces($datetime_identifier, $ticket_identifier, $ticket_quantity = 0)
705
+	{
706
+		// does datetime have spaces available?
707
+		// and does the supplied ticket have access to this datetime ?
708
+		if ($this->datetime_spaces[ $datetime_identifier ] > 0
709
+			&& isset($this->datetime_spaces[ $datetime_identifier ], $this->datetime_tickets[ $datetime_identifier ])
710
+			&& in_array($ticket_identifier, $this->datetime_tickets[ $datetime_identifier ], true)
711
+		) {
712
+			if ($this->debug) {
713
+				\EEH_Debug_Tools::printr($datetime_identifier, ' . . adjust Datetime Spaces for', __FILE__, __LINE__);
714
+				\EEH_Debug_Tools::printr(
715
+					"{$this->datetime_spaces[ $datetime_identifier ]}",
716
+					" . . current  {$datetime_identifier} spaces available",
717
+					__FILE__,
718
+					__LINE__
719
+				);
720
+			}
721
+			// then decrement the available spaces for the datetime
722
+			$this->datetime_spaces[ $datetime_identifier ] -= $ticket_quantity;
723
+			// but don't let quantities go below zero
724
+			$this->datetime_spaces[ $datetime_identifier ] = max(
725
+				$this->datetime_spaces[ $datetime_identifier ],
726
+				0
727
+			);
728
+			if ($this->debug) {
729
+				\EEH_Debug_Tools::printr(
730
+					"{$ticket_quantity}",
731
+					" . . . {$datetime_identifier} capacity reduced by",
732
+					__FILE__,
733
+					__LINE__
734
+				);
735
+			}
736
+			return true;
737
+		}
738
+		return false;
739
+	}
740 740
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Registration.class.php 1 patch
Indentation   +2068 added lines, -2068 removed lines patch added patch discarded remove patch
@@ -17,2072 +17,2072 @@
 block discarded – undo
17 17
 {
18 18
 
19 19
 
20
-    /**
21
-     * Used to reference when a registration has never been checked in.
22
-     *
23
-     * @deprecated use \EE_Checkin::status_checked_never instead
24
-     * @type int
25
-     */
26
-    const checkin_status_never = 2;
27
-
28
-    /**
29
-     * Used to reference when a registration has been checked in.
30
-     *
31
-     * @deprecated use \EE_Checkin::status_checked_in instead
32
-     * @type int
33
-     */
34
-    const checkin_status_in = 1;
35
-
36
-
37
-    /**
38
-     * Used to reference when a registration has been checked out.
39
-     *
40
-     * @deprecated use \EE_Checkin::status_checked_out instead
41
-     * @type int
42
-     */
43
-    const checkin_status_out = 0;
44
-
45
-
46
-    /**
47
-     * extra meta key for tracking reg status os trashed registrations
48
-     *
49
-     * @type string
50
-     */
51
-    const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
52
-
53
-
54
-    /**
55
-     * extra meta key for tracking if registration has reserved ticket
56
-     *
57
-     * @type string
58
-     */
59
-    const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
60
-
61
-
62
-    /**
63
-     * @param array  $props_n_values          incoming values
64
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
65
-     *                                        used.)
66
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
67
-     *                                        date_format and the second value is the time format
68
-     * @return EE_Registration
69
-     * @throws EE_Error
70
-     */
71
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
-    {
73
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
-    }
76
-
77
-
78
-    /**
79
-     * @param array  $props_n_values  incoming values from the database
80
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
-     *                                the website will be used.
82
-     * @return EE_Registration
83
-     */
84
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
85
-    {
86
-        return new self($props_n_values, true, $timezone);
87
-    }
88
-
89
-
90
-    /**
91
-     *        Set Event ID
92
-     *
93
-     * @param        int $EVT_ID Event ID
94
-     * @throws EE_Error
95
-     * @throws RuntimeException
96
-     */
97
-    public function set_event($EVT_ID = 0)
98
-    {
99
-        $this->set('EVT_ID', $EVT_ID);
100
-    }
101
-
102
-
103
-    /**
104
-     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
105
-     * be routed to internal methods
106
-     *
107
-     * @param string $field_name
108
-     * @param mixed  $field_value
109
-     * @param bool   $use_default
110
-     * @throws EE_Error
111
-     * @throws EntityNotFoundException
112
-     * @throws InvalidArgumentException
113
-     * @throws InvalidDataTypeException
114
-     * @throws InvalidInterfaceException
115
-     * @throws ReflectionException
116
-     * @throws RuntimeException
117
-     */
118
-    public function set($field_name, $field_value, $use_default = false)
119
-    {
120
-        switch ($field_name) {
121
-            case 'REG_code':
122
-                if (! empty($field_value) && $this->reg_code() === null) {
123
-                    $this->set_reg_code($field_value, $use_default);
124
-                }
125
-                break;
126
-            case 'STS_ID':
127
-                $this->set_status($field_value, $use_default);
128
-                break;
129
-            default:
130
-                parent::set($field_name, $field_value, $use_default);
131
-        }
132
-    }
133
-
134
-
135
-    /**
136
-     * Set Status ID
137
-     * updates the registration status and ALSO...
138
-     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
139
-     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
140
-     *
141
-     * @param string                $new_STS_ID
142
-     * @param boolean               $use_default
143
-     * @param ContextInterface|null $context
144
-     * @return bool
145
-     * @throws EE_Error
146
-     * @throws EntityNotFoundException
147
-     * @throws InvalidArgumentException
148
-     * @throws ReflectionException
149
-     * @throws RuntimeException
150
-     * @throws InvalidDataTypeException
151
-     * @throws InvalidInterfaceException
152
-     */
153
-    public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
154
-    {
155
-        // get current REG_Status
156
-        $old_STS_ID = $this->status_ID();
157
-        // if status has changed
158
-        if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
159
-            && ! empty($old_STS_ID) // and that old status is actually set
160
-            && ! empty($new_STS_ID) // as well as the new status
161
-            && $this->ID() // ensure registration is in the db
162
-        ) {
163
-            // update internal status first
164
-            parent::set('STS_ID', $new_STS_ID, $use_default);
165
-            // THEN handle other changes that occur when reg status changes
166
-            // TO approved
167
-            if ($new_STS_ID === EEM_Registration::status_id_approved) {
168
-                // reserve a space by incrementing ticket and datetime sold values
169
-                $this->_reserve_registration_space();
170
-                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
171
-                // OR FROM  approved
172
-            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
173
-                // release a space by decrementing ticket and datetime sold values
174
-                $this->_release_registration_space();
175
-                do_action(
176
-                    'AHEE__EE_Registration__set_status__from_approved',
177
-                    $this,
178
-                    $old_STS_ID,
179
-                    $new_STS_ID,
180
-                    $context
181
-                );
182
-            }
183
-            $this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, $context);
184
-            if ($this->statusChangeUpdatesTransaction($context)) {
185
-                $this->updateTransactionAfterStatusChange();
186
-            }
187
-            do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
188
-            return true;
189
-        }
190
-        // even though the old value matches the new value, it's still good to
191
-        // allow the parent set method to have a say
192
-        parent::set('STS_ID', $new_STS_ID, $use_default);
193
-        return true;
194
-    }
195
-
196
-
197
-    /**
198
-     * update REGs and TXN when cancelled or declined registrations involved
199
-     *
200
-     * @param string                $new_STS_ID
201
-     * @param string                $old_STS_ID
202
-     * @param ContextInterface|null $context
203
-     * @throws EE_Error
204
-     * @throws InvalidArgumentException
205
-     * @throws InvalidDataTypeException
206
-     * @throws InvalidInterfaceException
207
-     * @throws ReflectionException
208
-     */
209
-    private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
210
-    {
211
-        // these reg statuses should not be considered in any calculations involving monies owing
212
-        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
213
-        // true if registration has been cancelled or declined
214
-        $this->updateIfCanceled(
215
-            $closed_reg_statuses,
216
-            $new_STS_ID,
217
-            $old_STS_ID,
218
-            $context
219
-        );
220
-        $this->updateIfDeclined(
221
-            $closed_reg_statuses,
222
-            $new_STS_ID,
223
-            $old_STS_ID,
224
-            $context
225
-        );
226
-    }
227
-
228
-
229
-    /**
230
-     * update REGs and TXN when cancelled or declined registrations involved
231
-     *
232
-     * @param array                 $closed_reg_statuses
233
-     * @param string                $new_STS_ID
234
-     * @param string                $old_STS_ID
235
-     * @param ContextInterface|null $context
236
-     * @throws EE_Error
237
-     * @throws InvalidArgumentException
238
-     * @throws InvalidDataTypeException
239
-     * @throws InvalidInterfaceException
240
-     * @throws ReflectionException
241
-     */
242
-    private function updateIfCanceled(
243
-        array $closed_reg_statuses,
244
-        $new_STS_ID,
245
-        $old_STS_ID,
246
-        ContextInterface $context = null
247
-    ) {
248
-        // true if registration has been cancelled or declined
249
-        if (in_array($new_STS_ID, $closed_reg_statuses, true)
250
-            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
251
-        ) {
252
-            /** @type EE_Registration_Processor $registration_processor */
253
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
254
-            /** @type EE_Transaction_Processor $transaction_processor */
255
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
256
-            // cancelled or declined registration
257
-            $registration_processor->update_registration_after_being_canceled_or_declined(
258
-                $this,
259
-                $closed_reg_statuses
260
-            );
261
-            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
262
-                $this,
263
-                $closed_reg_statuses,
264
-                false
265
-            );
266
-            do_action(
267
-                'AHEE__EE_Registration__set_status__canceled_or_declined',
268
-                $this,
269
-                $old_STS_ID,
270
-                $new_STS_ID,
271
-                $context
272
-            );
273
-            return;
274
-        }
275
-    }
276
-
277
-
278
-    /**
279
-     * update REGs and TXN when cancelled or declined registrations involved
280
-     *
281
-     * @param array                 $closed_reg_statuses
282
-     * @param string                $new_STS_ID
283
-     * @param string                $old_STS_ID
284
-     * @param ContextInterface|null $context
285
-     * @throws EE_Error
286
-     * @throws InvalidArgumentException
287
-     * @throws InvalidDataTypeException
288
-     * @throws InvalidInterfaceException
289
-     * @throws ReflectionException
290
-     */
291
-    private function updateIfDeclined(
292
-        array $closed_reg_statuses,
293
-        $new_STS_ID,
294
-        $old_STS_ID,
295
-        ContextInterface $context = null
296
-    ) {
297
-        // true if reinstating cancelled or declined registration
298
-        if (in_array($old_STS_ID, $closed_reg_statuses, true)
299
-            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
300
-        ) {
301
-            /** @type EE_Registration_Processor $registration_processor */
302
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
303
-            /** @type EE_Transaction_Processor $transaction_processor */
304
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
305
-            // reinstating cancelled or declined registration
306
-            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
307
-                $this,
308
-                $closed_reg_statuses
309
-            );
310
-            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
311
-                $this,
312
-                $closed_reg_statuses,
313
-                false
314
-            );
315
-            do_action(
316
-                'AHEE__EE_Registration__set_status__after_reinstated',
317
-                $this,
318
-                $old_STS_ID,
319
-                $new_STS_ID,
320
-                $context
321
-            );
322
-        }
323
-    }
324
-
325
-
326
-    /**
327
-     * @param ContextInterface|null $context
328
-     * @return bool
329
-     */
330
-    private function statusChangeUpdatesTransaction(ContextInterface $context = null)
331
-    {
332
-        $contexts_that_do_not_update_transaction = (array) apply_filters(
333
-            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
334
-            array('spco_reg_step_attendee_information_process_registrations'),
335
-            $context,
336
-            $this
337
-        );
338
-        return ! (
339
-            $context instanceof ContextInterface
340
-            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
341
-        );
342
-    }
343
-
344
-
345
-    /**
346
-     * @throws EE_Error
347
-     * @throws EntityNotFoundException
348
-     * @throws InvalidArgumentException
349
-     * @throws InvalidDataTypeException
350
-     * @throws InvalidInterfaceException
351
-     * @throws ReflectionException
352
-     * @throws RuntimeException
353
-     */
354
-    private function updateTransactionAfterStatusChange()
355
-    {
356
-        /** @type EE_Transaction_Payments $transaction_payments */
357
-        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
358
-        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
359
-        $this->transaction()->update_status_based_on_total_paid(true);
360
-    }
361
-
362
-
363
-    /**
364
-     *        get Status ID
365
-     */
366
-    public function status_ID()
367
-    {
368
-        return $this->get('STS_ID');
369
-    }
370
-
371
-
372
-    /**
373
-     * Gets the ticket this registration is for
374
-     *
375
-     * @param boolean $include_archived whether to include archived tickets or not.
376
-     *
377
-     * @return EE_Ticket|EE_Base_Class
378
-     * @throws EE_Error
379
-     */
380
-    public function ticket($include_archived = true)
381
-    {
382
-        $query_params = array();
383
-        if ($include_archived) {
384
-            $query_params['default_where_conditions'] = 'none';
385
-        }
386
-        return $this->get_first_related('Ticket', $query_params);
387
-    }
388
-
389
-
390
-    /**
391
-     * Gets the event this registration is for
392
-     *
393
-     * @return EE_Event
394
-     * @throws EE_Error
395
-     * @throws EntityNotFoundException
396
-     */
397
-    public function event()
398
-    {
399
-        $event = $this->get_first_related('Event');
400
-        if (! $event instanceof \EE_Event) {
401
-            throw new EntityNotFoundException('Event ID', $this->event_ID());
402
-        }
403
-        return $event;
404
-    }
405
-
406
-
407
-    /**
408
-     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
409
-     * with the author of the event this registration is for.
410
-     *
411
-     * @since 4.5.0
412
-     * @return int
413
-     * @throws EE_Error
414
-     * @throws EntityNotFoundException
415
-     */
416
-    public function wp_user()
417
-    {
418
-        $event = $this->event();
419
-        if ($event instanceof EE_Event) {
420
-            return $event->wp_user();
421
-        }
422
-        return 0;
423
-    }
424
-
425
-
426
-    /**
427
-     * increments this registration's related ticket sold and corresponding datetime sold values
428
-     *
429
-     * @return void
430
-     * @throws DomainException
431
-     * @throws EE_Error
432
-     * @throws EntityNotFoundException
433
-     * @throws InvalidArgumentException
434
-     * @throws InvalidDataTypeException
435
-     * @throws InvalidInterfaceException
436
-     * @throws ReflectionException
437
-     * @throws UnexpectedEntityException
438
-     */
439
-    private function _reserve_registration_space()
440
-    {
441
-        // reserved ticket and datetime counts will be decremented as sold counts are incremented
442
-        // so stop tracking that this reg has a ticket reserved
443
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
444
-        $ticket = $this->ticket();
445
-        $ticket->increase_sold();
446
-        $ticket->save();
447
-        // possibly set event status to sold out
448
-        $this->event()->perform_sold_out_status_check();
449
-    }
450
-
451
-
452
-    /**
453
-     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
454
-     *
455
-     * @return void
456
-     * @throws DomainException
457
-     * @throws EE_Error
458
-     * @throws EntityNotFoundException
459
-     * @throws InvalidArgumentException
460
-     * @throws InvalidDataTypeException
461
-     * @throws InvalidInterfaceException
462
-     * @throws ReflectionException
463
-     * @throws UnexpectedEntityException
464
-     */
465
-    private function _release_registration_space()
466
-    {
467
-        $ticket = $this->ticket();
468
-        $ticket->decrease_sold();
469
-        $ticket->save();
470
-        // possibly change event status from sold out back to previous status
471
-        $this->event()->perform_sold_out_status_check();
472
-    }
473
-
474
-
475
-    /**
476
-     * tracks this registration's ticket reservation in extra meta
477
-     * and can increment related ticket reserved and corresponding datetime reserved values
478
-     *
479
-     * @param bool $update_ticket if true, will increment ticket and datetime reserved count
480
-     * @return void
481
-     * @throws EE_Error
482
-     * @throws InvalidArgumentException
483
-     * @throws InvalidDataTypeException
484
-     * @throws InvalidInterfaceException
485
-     * @throws ReflectionException
486
-     */
487
-    public function reserve_ticket($update_ticket = false, $source = 'unknown')
488
-    {
489
-        // only reserve ticket if space is not currently reserved
490
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
491
-            $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
492
-            // IMPORTANT !!!
493
-            // although checking $update_ticket first would be more efficient,
494
-            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
495
-            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true)
496
-                && $update_ticket
497
-            ) {
498
-                $ticket = $this->ticket();
499
-                $ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
500
-                $ticket->save();
501
-            }
502
-        }
503
-    }
504
-
505
-
506
-    /**
507
-     * stops tracking this registration's ticket reservation in extra meta
508
-     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
509
-     *
510
-     * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
511
-     * @return void
512
-     * @throws EE_Error
513
-     * @throws InvalidArgumentException
514
-     * @throws InvalidDataTypeException
515
-     * @throws InvalidInterfaceException
516
-     * @throws ReflectionException
517
-     */
518
-    public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
519
-    {
520
-        // only release ticket if space is currently reserved
521
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
522
-            $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
523
-            // IMPORTANT !!!
524
-            // although checking $update_ticket first would be more efficient,
525
-            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
526
-            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false)
527
-                && $update_ticket
528
-            ) {
529
-                $ticket = $this->ticket();
530
-                $ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
531
-                $ticket->save();
532
-            }
533
-        }
534
-    }
535
-
536
-
537
-    /**
538
-     * Set Attendee ID
539
-     *
540
-     * @param        int $ATT_ID Attendee ID
541
-     * @throws EE_Error
542
-     * @throws RuntimeException
543
-     */
544
-    public function set_attendee_id($ATT_ID = 0)
545
-    {
546
-        $this->set('ATT_ID', $ATT_ID);
547
-    }
548
-
549
-
550
-    /**
551
-     *        Set Transaction ID
552
-     *
553
-     * @param        int $TXN_ID Transaction ID
554
-     * @throws EE_Error
555
-     * @throws RuntimeException
556
-     */
557
-    public function set_transaction_id($TXN_ID = 0)
558
-    {
559
-        $this->set('TXN_ID', $TXN_ID);
560
-    }
561
-
562
-
563
-    /**
564
-     *        Set Session
565
-     *
566
-     * @param    string $REG_session PHP Session ID
567
-     * @throws EE_Error
568
-     * @throws RuntimeException
569
-     */
570
-    public function set_session($REG_session = '')
571
-    {
572
-        $this->set('REG_session', $REG_session);
573
-    }
574
-
575
-
576
-    /**
577
-     *        Set Registration URL Link
578
-     *
579
-     * @param    string $REG_url_link Registration URL Link
580
-     * @throws EE_Error
581
-     * @throws RuntimeException
582
-     */
583
-    public function set_reg_url_link($REG_url_link = '')
584
-    {
585
-        $this->set('REG_url_link', $REG_url_link);
586
-    }
587
-
588
-
589
-    /**
590
-     *        Set Attendee Counter
591
-     *
592
-     * @param        int $REG_count Primary Attendee
593
-     * @throws EE_Error
594
-     * @throws RuntimeException
595
-     */
596
-    public function set_count($REG_count = 1)
597
-    {
598
-        $this->set('REG_count', $REG_count);
599
-    }
600
-
601
-
602
-    /**
603
-     *        Set Group Size
604
-     *
605
-     * @param        boolean $REG_group_size Group Registration
606
-     * @throws EE_Error
607
-     * @throws RuntimeException
608
-     */
609
-    public function set_group_size($REG_group_size = false)
610
-    {
611
-        $this->set('REG_group_size', $REG_group_size);
612
-    }
613
-
614
-
615
-    /**
616
-     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
617
-     *    EEM_Registration::status_id_not_approved
618
-     *
619
-     * @return        boolean
620
-     */
621
-    public function is_not_approved()
622
-    {
623
-        return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
624
-    }
625
-
626
-
627
-    /**
628
-     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
629
-     *    EEM_Registration::status_id_pending_payment
630
-     *
631
-     * @return        boolean
632
-     */
633
-    public function is_pending_payment()
634
-    {
635
-        return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
636
-    }
637
-
638
-
639
-    /**
640
-     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
641
-     *
642
-     * @return        boolean
643
-     */
644
-    public function is_approved()
645
-    {
646
-        return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
647
-    }
648
-
649
-
650
-    /**
651
-     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
652
-     *
653
-     * @return        boolean
654
-     */
655
-    public function is_cancelled()
656
-    {
657
-        return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
658
-    }
659
-
660
-
661
-    /**
662
-     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
663
-     *
664
-     * @return        boolean
665
-     */
666
-    public function is_declined()
667
-    {
668
-        return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
669
-    }
670
-
671
-
672
-    /**
673
-     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
674
-     *    EEM_Registration::status_id_incomplete
675
-     *
676
-     * @return        boolean
677
-     */
678
-    public function is_incomplete()
679
-    {
680
-        return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
681
-    }
682
-
683
-
684
-    /**
685
-     *        Set Registration Date
686
-     *
687
-     * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
688
-     *                                                 Date
689
-     * @throws EE_Error
690
-     * @throws RuntimeException
691
-     */
692
-    public function set_reg_date($REG_date = false)
693
-    {
694
-        $this->set('REG_date', $REG_date);
695
-    }
696
-
697
-
698
-    /**
699
-     *    Set final price owing for this registration after all ticket/price modifications
700
-     *
701
-     * @access    public
702
-     * @param    float $REG_final_price
703
-     * @throws EE_Error
704
-     * @throws RuntimeException
705
-     */
706
-    public function set_final_price($REG_final_price = 0.00)
707
-    {
708
-        $this->set('REG_final_price', $REG_final_price);
709
-    }
710
-
711
-
712
-    /**
713
-     *    Set amount paid towards this registration's final price
714
-     *
715
-     * @access    public
716
-     * @param    float $REG_paid
717
-     * @throws EE_Error
718
-     * @throws RuntimeException
719
-     */
720
-    public function set_paid($REG_paid = 0.00)
721
-    {
722
-        $this->set('REG_paid', $REG_paid);
723
-    }
724
-
725
-
726
-    /**
727
-     *        Attendee Is Going
728
-     *
729
-     * @param        boolean $REG_att_is_going Attendee Is Going
730
-     * @throws EE_Error
731
-     * @throws RuntimeException
732
-     */
733
-    public function set_att_is_going($REG_att_is_going = false)
734
-    {
735
-        $this->set('REG_att_is_going', $REG_att_is_going);
736
-    }
737
-
738
-
739
-    /**
740
-     * Gets the related attendee
741
-     *
742
-     * @return EE_Attendee
743
-     * @throws EE_Error
744
-     */
745
-    public function attendee()
746
-    {
747
-        return $this->get_first_related('Attendee');
748
-    }
749
-
750
-
751
-    /**
752
-     *        get Event ID
753
-     */
754
-    public function event_ID()
755
-    {
756
-        return $this->get('EVT_ID');
757
-    }
758
-
759
-
760
-    /**
761
-     *        get Event ID
762
-     */
763
-    public function event_name()
764
-    {
765
-        $event = $this->event_obj();
766
-        if ($event) {
767
-            return $event->name();
768
-        } else {
769
-            return null;
770
-        }
771
-    }
772
-
773
-
774
-    /**
775
-     * Fetches the event this registration is for
776
-     *
777
-     * @return EE_Event
778
-     * @throws EE_Error
779
-     */
780
-    public function event_obj()
781
-    {
782
-        return $this->get_first_related('Event');
783
-    }
784
-
785
-
786
-    /**
787
-     *        get Attendee ID
788
-     */
789
-    public function attendee_ID()
790
-    {
791
-        return $this->get('ATT_ID');
792
-    }
793
-
794
-
795
-    /**
796
-     *        get PHP Session ID
797
-     */
798
-    public function session_ID()
799
-    {
800
-        return $this->get('REG_session');
801
-    }
802
-
803
-
804
-    /**
805
-     * Gets the string which represents the URL trigger for the receipt template in the message template system.
806
-     *
807
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
808
-     * @return string
809
-     */
810
-    public function receipt_url($messenger = 'html')
811
-    {
812
-
813
-        /**
814
-         * The below will be deprecated one version after this.  We check first if there is a custom receipt template
815
-         * already in use on old system.  If there is then we just return the standard url for it.
816
-         *
817
-         * @since 4.5.0
818
-         */
819
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
820
-        $has_custom = EEH_Template::locate_template(
821
-            $template_relative_path,
822
-            array(),
823
-            true,
824
-            true,
825
-            true
826
-        );
827
-
828
-        if ($has_custom) {
829
-            return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
830
-        }
831
-        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
832
-    }
833
-
834
-
835
-    /**
836
-     * Gets the string which represents the URL trigger for the invoice template in the message template system.
837
-     *
838
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
839
-     * @return string
840
-     * @throws EE_Error
841
-     */
842
-    public function invoice_url($messenger = 'html')
843
-    {
844
-        /**
845
-         * The below will be deprecated one version after this.  We check first if there is a custom invoice template
846
-         * already in use on old system.  If there is then we just return the standard url for it.
847
-         *
848
-         * @since 4.5.0
849
-         */
850
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
851
-        $has_custom = EEH_Template::locate_template(
852
-            $template_relative_path,
853
-            array(),
854
-            true,
855
-            true,
856
-            true
857
-        );
858
-
859
-        if ($has_custom) {
860
-            if ($messenger == 'html') {
861
-                return $this->invoice_url('launch');
862
-            }
863
-            $route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
864
-
865
-            $query_args = array('ee' => $route, 'id' => $this->reg_url_link());
866
-            if ($messenger == 'html') {
867
-                $query_args['html'] = true;
868
-            }
869
-            return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
870
-        }
871
-        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
872
-    }
873
-
874
-
875
-    /**
876
-     * get Registration URL Link
877
-     *
878
-     * @access public
879
-     * @return string
880
-     * @throws EE_Error
881
-     */
882
-    public function reg_url_link()
883
-    {
884
-        return (string) $this->get('REG_url_link');
885
-    }
886
-
887
-
888
-    /**
889
-     * Echoes out invoice_url()
890
-     *
891
-     * @param string $type 'download','launch', or 'html' (default is 'launch')
892
-     * @return void
893
-     * @throws EE_Error
894
-     */
895
-    public function e_invoice_url($type = 'launch')
896
-    {
897
-        echo $this->invoice_url($type);
898
-    }
899
-
900
-
901
-    /**
902
-     * Echoes out payment_overview_url
903
-     */
904
-    public function e_payment_overview_url()
905
-    {
906
-        echo $this->payment_overview_url();
907
-    }
908
-
909
-
910
-    /**
911
-     * Gets the URL for the checkout payment options reg step
912
-     * with this registration's REG_url_link added as a query parameter
913
-     *
914
-     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
915
-     *                            payment overview url.
916
-     * @return string
917
-     * @throws InvalidInterfaceException
918
-     * @throws InvalidDataTypeException
919
-     * @throws EE_Error
920
-     * @throws InvalidArgumentException
921
-     */
922
-    public function payment_overview_url($clear_session = false)
923
-    {
924
-        return add_query_arg(
925
-            (array) apply_filters(
926
-                'FHEE__EE_Registration__payment_overview_url__query_args',
927
-                array(
928
-                    'e_reg_url_link' => $this->reg_url_link(),
929
-                    'step'           => 'payment_options',
930
-                    'revisit'        => true,
931
-                    'clear_session'  => (bool) $clear_session,
932
-                ),
933
-                $this
934
-            ),
935
-            EE_Registry::instance()->CFG->core->reg_page_url()
936
-        );
937
-    }
938
-
939
-
940
-    /**
941
-     * Gets the URL for the checkout attendee information reg step
942
-     * with this registration's REG_url_link added as a query parameter
943
-     *
944
-     * @return string
945
-     * @throws InvalidInterfaceException
946
-     * @throws InvalidDataTypeException
947
-     * @throws EE_Error
948
-     * @throws InvalidArgumentException
949
-     */
950
-    public function edit_attendee_information_url()
951
-    {
952
-        return add_query_arg(
953
-            (array) apply_filters(
954
-                'FHEE__EE_Registration__edit_attendee_information_url__query_args',
955
-                array(
956
-                    'e_reg_url_link' => $this->reg_url_link(),
957
-                    'step'           => 'attendee_information',
958
-                    'revisit'        => true,
959
-                ),
960
-                $this
961
-            ),
962
-            EE_Registry::instance()->CFG->core->reg_page_url()
963
-        );
964
-    }
965
-
966
-
967
-    /**
968
-     * Simply generates and returns the appropriate admin_url link to edit this registration
969
-     *
970
-     * @return string
971
-     * @throws EE_Error
972
-     */
973
-    public function get_admin_edit_url()
974
-    {
975
-        return EEH_URL::add_query_args_and_nonce(
976
-            array(
977
-                'page'    => 'espresso_registrations',
978
-                'action'  => 'view_registration',
979
-                '_REG_ID' => $this->ID(),
980
-            ),
981
-            admin_url('admin.php')
982
-        );
983
-    }
984
-
985
-
986
-    /**
987
-     *    is_primary_registrant?
988
-     */
989
-    public function is_primary_registrant()
990
-    {
991
-        return $this->get('REG_count') == 1 ? true : false;
992
-    }
993
-
994
-
995
-    /**
996
-     * This returns the primary registration object for this registration group (which may be this object).
997
-     *
998
-     * @return EE_Registration
999
-     * @throws EE_Error
1000
-     */
1001
-    public function get_primary_registration()
1002
-    {
1003
-        if ($this->is_primary_registrant()) {
1004
-            return $this;
1005
-        }
1006
-
1007
-        // k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1008
-        /** @var EE_Registration $primary_registrant */
1009
-        $primary_registrant = EEM_Registration::instance()->get_one(
1010
-            array(
1011
-                array(
1012
-                    'TXN_ID'    => $this->transaction_ID(),
1013
-                    'REG_count' => 1,
1014
-                ),
1015
-            )
1016
-        );
1017
-        return $primary_registrant;
1018
-    }
1019
-
1020
-
1021
-    /**
1022
-     *        get  Attendee Number
1023
-     *
1024
-     * @access        public
1025
-     */
1026
-    public function count()
1027
-    {
1028
-        return $this->get('REG_count');
1029
-    }
1030
-
1031
-
1032
-    /**
1033
-     *        get Group Size
1034
-     */
1035
-    public function group_size()
1036
-    {
1037
-        return $this->get('REG_group_size');
1038
-    }
1039
-
1040
-
1041
-    /**
1042
-     *        get Registration Date
1043
-     */
1044
-    public function date()
1045
-    {
1046
-        return $this->get('REG_date');
1047
-    }
1048
-
1049
-
1050
-    /**
1051
-     * gets a pretty date
1052
-     *
1053
-     * @param string $date_format
1054
-     * @param string $time_format
1055
-     * @return string
1056
-     * @throws EE_Error
1057
-     */
1058
-    public function pretty_date($date_format = null, $time_format = null)
1059
-    {
1060
-        return $this->get_datetime('REG_date', $date_format, $time_format);
1061
-    }
1062
-
1063
-
1064
-    /**
1065
-     * final_price
1066
-     * the registration's share of the transaction total, so that the
1067
-     * sum of all the transaction's REG_final_prices equal the transaction's total
1068
-     *
1069
-     * @return float
1070
-     * @throws EE_Error
1071
-     */
1072
-    public function final_price()
1073
-    {
1074
-        return $this->get('REG_final_price');
1075
-    }
1076
-
1077
-
1078
-    /**
1079
-     * pretty_final_price
1080
-     *  final price as formatted string, with correct decimal places and currency symbol
1081
-     *
1082
-     * @return string
1083
-     * @throws EE_Error
1084
-     */
1085
-    public function pretty_final_price()
1086
-    {
1087
-        return $this->get_pretty('REG_final_price');
1088
-    }
1089
-
1090
-
1091
-    /**
1092
-     * get paid (yeah)
1093
-     *
1094
-     * @return float
1095
-     * @throws EE_Error
1096
-     */
1097
-    public function paid()
1098
-    {
1099
-        return $this->get('REG_paid');
1100
-    }
1101
-
1102
-
1103
-    /**
1104
-     * pretty_paid
1105
-     *
1106
-     * @return float
1107
-     * @throws EE_Error
1108
-     */
1109
-    public function pretty_paid()
1110
-    {
1111
-        return $this->get_pretty('REG_paid');
1112
-    }
1113
-
1114
-
1115
-    /**
1116
-     * owes_monies_and_can_pay
1117
-     * whether or not this registration has monies owing and it's' status allows payment
1118
-     *
1119
-     * @param array $requires_payment
1120
-     * @return bool
1121
-     * @throws EE_Error
1122
-     */
1123
-    public function owes_monies_and_can_pay($requires_payment = array())
1124
-    {
1125
-        // these reg statuses require payment (if event is not free)
1126
-        $requires_payment = ! empty($requires_payment)
1127
-            ? $requires_payment
1128
-            : EEM_Registration::reg_statuses_that_allow_payment();
1129
-        if (in_array($this->status_ID(), $requires_payment) &&
1130
-            $this->final_price() != 0 &&
1131
-            $this->final_price() != $this->paid()
1132
-        ) {
1133
-            return true;
1134
-        } else {
1135
-            return false;
1136
-        }
1137
-    }
1138
-
1139
-
1140
-    /**
1141
-     * Prints out the return value of $this->pretty_status()
1142
-     *
1143
-     * @param bool $show_icons
1144
-     * @return void
1145
-     * @throws EE_Error
1146
-     */
1147
-    public function e_pretty_status($show_icons = false)
1148
-    {
1149
-        echo $this->pretty_status($show_icons);
1150
-    }
1151
-
1152
-
1153
-    /**
1154
-     * Returns a nice version of the status for displaying to customers
1155
-     *
1156
-     * @param bool $show_icons
1157
-     * @return string
1158
-     * @throws EE_Error
1159
-     */
1160
-    public function pretty_status($show_icons = false)
1161
-    {
1162
-        $status = EEM_Status::instance()->localized_status(
1163
-            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1164
-            false,
1165
-            'sentence'
1166
-        );
1167
-        $icon = '';
1168
-        switch ($this->status_ID()) {
1169
-            case EEM_Registration::status_id_approved:
1170
-                $icon = $show_icons
1171
-                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1172
-                    : '';
1173
-                break;
1174
-            case EEM_Registration::status_id_pending_payment:
1175
-                $icon = $show_icons
1176
-                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1177
-                    : '';
1178
-                break;
1179
-            case EEM_Registration::status_id_not_approved:
1180
-                $icon = $show_icons
1181
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1182
-                    : '';
1183
-                break;
1184
-            case EEM_Registration::status_id_cancelled:
1185
-                $icon = $show_icons
1186
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1187
-                    : '';
1188
-                break;
1189
-            case EEM_Registration::status_id_incomplete:
1190
-                $icon = $show_icons
1191
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1192
-                    : '';
1193
-                break;
1194
-            case EEM_Registration::status_id_declined:
1195
-                $icon = $show_icons
1196
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1197
-                    : '';
1198
-                break;
1199
-            case EEM_Registration::status_id_wait_list:
1200
-                $icon = $show_icons
1201
-                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1202
-                    : '';
1203
-                break;
1204
-        }
1205
-        return $icon . $status[ $this->status_ID() ];
1206
-    }
1207
-
1208
-
1209
-    /**
1210
-     *        get Attendee Is Going
1211
-     */
1212
-    public function att_is_going()
1213
-    {
1214
-        return $this->get('REG_att_is_going');
1215
-    }
1216
-
1217
-
1218
-    /**
1219
-     * Gets related answers
1220
-     *
1221
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1222
-     * @return EE_Answer[]
1223
-     * @throws EE_Error
1224
-     */
1225
-    public function answers($query_params = null)
1226
-    {
1227
-        return $this->get_many_related('Answer', $query_params);
1228
-    }
1229
-
1230
-
1231
-    /**
1232
-     * Gets the registration's answer value to the specified question
1233
-     * (either the question's ID or a question object)
1234
-     *
1235
-     * @param EE_Question|int $question
1236
-     * @param bool            $pretty_value
1237
-     * @return array|string if pretty_value= true, the result will always be a string
1238
-     * (because the answer might be an array of answer values, so passing pretty_value=true
1239
-     * will convert it into some kind of string)
1240
-     * @throws EE_Error
1241
-     */
1242
-    public function answer_value_to_question($question, $pretty_value = true)
1243
-    {
1244
-        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1245
-        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1246
-    }
1247
-
1248
-
1249
-    /**
1250
-     * question_groups
1251
-     * returns an array of EE_Question_Group objects for this registration
1252
-     *
1253
-     * @return EE_Question_Group[]
1254
-     * @throws EE_Error
1255
-     * @throws EntityNotFoundException
1256
-     */
1257
-    public function question_groups()
1258
-    {
1259
-        $question_groups = array();
1260
-        if ($this->event() instanceof EE_Event) {
1261
-            $question_groups = $this->event()->question_groups(
1262
-                array(
1263
-                    array(
1264
-                        'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1265
-                    ),
1266
-                    'order_by' => array('QSG_order' => 'ASC'),
1267
-                )
1268
-            );
1269
-        }
1270
-        return $question_groups;
1271
-    }
1272
-
1273
-
1274
-    /**
1275
-     * count_question_groups
1276
-     * returns a count of the number of EE_Question_Group objects for this registration
1277
-     *
1278
-     * @return int
1279
-     * @throws EE_Error
1280
-     * @throws EntityNotFoundException
1281
-     */
1282
-    public function count_question_groups()
1283
-    {
1284
-        $qg_count = 0;
1285
-        if ($this->event() instanceof EE_Event) {
1286
-            $qg_count = $this->event()->count_related(
1287
-                'Question_Group',
1288
-                array(
1289
-                    array(
1290
-                        'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1291
-                    ),
1292
-                )
1293
-            );
1294
-        }
1295
-        return $qg_count;
1296
-    }
1297
-
1298
-
1299
-    /**
1300
-     * Returns the registration date in the 'standard' string format
1301
-     * (function may be improved in the future to allow for different formats and timezones)
1302
-     *
1303
-     * @return string
1304
-     * @throws EE_Error
1305
-     */
1306
-    public function reg_date()
1307
-    {
1308
-        return $this->get_datetime('REG_date');
1309
-    }
1310
-
1311
-
1312
-    /**
1313
-     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1314
-     * the ticket this registration purchased, or the datetime they have registered
1315
-     * to attend)
1316
-     *
1317
-     * @return EE_Datetime_Ticket
1318
-     * @throws EE_Error
1319
-     */
1320
-    public function datetime_ticket()
1321
-    {
1322
-        return $this->get_first_related('Datetime_Ticket');
1323
-    }
1324
-
1325
-
1326
-    /**
1327
-     * Sets the registration's datetime_ticket.
1328
-     *
1329
-     * @param EE_Datetime_Ticket $datetime_ticket
1330
-     * @return EE_Datetime_Ticket
1331
-     * @throws EE_Error
1332
-     */
1333
-    public function set_datetime_ticket($datetime_ticket)
1334
-    {
1335
-        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1336
-    }
1337
-
1338
-    /**
1339
-     * Gets deleted
1340
-     *
1341
-     * @return bool
1342
-     * @throws EE_Error
1343
-     */
1344
-    public function deleted()
1345
-    {
1346
-        return $this->get('REG_deleted');
1347
-    }
1348
-
1349
-    /**
1350
-     * Sets deleted
1351
-     *
1352
-     * @param boolean $deleted
1353
-     * @return bool
1354
-     * @throws EE_Error
1355
-     * @throws RuntimeException
1356
-     */
1357
-    public function set_deleted($deleted)
1358
-    {
1359
-        if ($deleted) {
1360
-            $this->delete();
1361
-        } else {
1362
-            $this->restore();
1363
-        }
1364
-    }
1365
-
1366
-
1367
-    /**
1368
-     * Get the status object of this object
1369
-     *
1370
-     * @return EE_Status
1371
-     * @throws EE_Error
1372
-     */
1373
-    public function status_obj()
1374
-    {
1375
-        return $this->get_first_related('Status');
1376
-    }
1377
-
1378
-
1379
-    /**
1380
-     * Returns the number of times this registration has checked into any of the datetimes
1381
-     * its available for
1382
-     *
1383
-     * @return int
1384
-     * @throws EE_Error
1385
-     */
1386
-    public function count_checkins()
1387
-    {
1388
-        return $this->get_model()->count_related($this, 'Checkin');
1389
-    }
1390
-
1391
-
1392
-    /**
1393
-     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1394
-     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1395
-     *
1396
-     * @return int
1397
-     * @throws EE_Error
1398
-     */
1399
-    public function count_checkins_not_checkedout()
1400
-    {
1401
-        return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1402
-    }
1403
-
1404
-
1405
-    /**
1406
-     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1407
-     *
1408
-     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1409
-     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1410
-     *                                          consider registration status as well as datetime access.
1411
-     * @return bool
1412
-     * @throws EE_Error
1413
-     */
1414
-    public function can_checkin($DTT_OR_ID, $check_approved = true)
1415
-    {
1416
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1417
-
1418
-        // first check registration status
1419
-        if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1420
-            return false;
1421
-        }
1422
-        // is there a datetime ticket that matches this dtt_ID?
1423
-        if (! (EEM_Datetime_Ticket::instance()->exists(
1424
-            array(
1425
-                array(
1426
-                    'TKT_ID' => $this->get('TKT_ID'),
1427
-                    'DTT_ID' => $DTT_ID,
1428
-                ),
1429
-            )
1430
-        ))
1431
-        ) {
1432
-            return false;
1433
-        }
1434
-
1435
-        // final check is against TKT_uses
1436
-        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1437
-    }
1438
-
1439
-
1440
-    /**
1441
-     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1442
-     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1443
-     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1444
-     * then return false.  Otherwise return true.
1445
-     *
1446
-     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1447
-     * @return bool true means can checkin.  false means cannot checkin.
1448
-     * @throws EE_Error
1449
-     */
1450
-    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1451
-    {
1452
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1453
-
1454
-        if (! $DTT_ID) {
1455
-            return false;
1456
-        }
1457
-
1458
-        $max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1459
-
1460
-        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1461
-        // check-in or not.
1462
-        if (! $max_uses || $max_uses === EE_INF) {
1463
-            return true;
1464
-        }
1465
-
1466
-        // does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1467
-        // go ahead and toggle.
1468
-        if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1469
-            return true;
1470
-        }
1471
-
1472
-        // made it here so the last check is whether the number of checkins per unique datetime on this registration
1473
-        // disallows further check-ins.
1474
-        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1475
-            array(
1476
-                array(
1477
-                    'REG_ID' => $this->ID(),
1478
-                    'CHK_in' => true,
1479
-                ),
1480
-            ),
1481
-            'DTT_ID',
1482
-            true
1483
-        );
1484
-        // checkins have already reached their max number of uses
1485
-        // so registrant can NOT checkin
1486
-        if ($count_unique_dtt_checkins >= $max_uses) {
1487
-            EE_Error::add_error(
1488
-                esc_html__(
1489
-                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1490
-                    'event_espresso'
1491
-                ),
1492
-                __FILE__,
1493
-                __FUNCTION__,
1494
-                __LINE__
1495
-            );
1496
-            return false;
1497
-        }
1498
-        return true;
1499
-    }
1500
-
1501
-
1502
-    /**
1503
-     * toggle Check-in status for this registration
1504
-     * Check-ins are toggled in the following order:
1505
-     * never checked in -> checked in
1506
-     * checked in -> checked out
1507
-     * checked out -> checked in
1508
-     *
1509
-     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1510
-     *                      If not included or null, then it is assumed latest datetime is being toggled.
1511
-     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1512
-     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1513
-     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1514
-     * @throws EE_Error
1515
-     */
1516
-    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1517
-    {
1518
-        if (empty($DTT_ID)) {
1519
-            $datetime = $this->get_latest_related_datetime();
1520
-            $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1521
-            // verify the registration can checkin for the given DTT_ID
1522
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1523
-            EE_Error::add_error(
1524
-                sprintf(
1525
-                    esc_html__(
1526
-                        'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1527
-                        'event_espresso'
1528
-                    ),
1529
-                    $this->ID(),
1530
-                    $DTT_ID
1531
-                ),
1532
-                __FILE__,
1533
-                __FUNCTION__,
1534
-                __LINE__
1535
-            );
1536
-            return false;
1537
-        }
1538
-        $status_paths = array(
1539
-            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1540
-            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1541
-            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1542
-        );
1543
-        // start by getting the current status so we know what status we'll be changing to.
1544
-        $cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1545
-        $status_to = $status_paths[ $cur_status ];
1546
-        // database only records true for checked IN or false for checked OUT
1547
-        // no record ( null ) means checked in NEVER, but we obviously don't save that
1548
-        $new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1549
-        // add relation - note Check-ins are always creating new rows
1550
-        // because we are keeping track of Check-ins over time.
1551
-        // Eventually we'll probably want to show a list table
1552
-        // for the individual Check-ins so that they can be managed.
1553
-        $checkin = EE_Checkin::new_instance(
1554
-            array(
1555
-                'REG_ID' => $this->ID(),
1556
-                'DTT_ID' => $DTT_ID,
1557
-                'CHK_in' => $new_status,
1558
-            )
1559
-        );
1560
-        // if the record could not be saved then return false
1561
-        if ($checkin->save() === 0) {
1562
-            if (WP_DEBUG) {
1563
-                global $wpdb;
1564
-                $error = sprintf(
1565
-                    esc_html__(
1566
-                        'Registration check in update failed because of the following database error: %1$s%2$s',
1567
-                        'event_espresso'
1568
-                    ),
1569
-                    '<br />',
1570
-                    $wpdb->last_error
1571
-                );
1572
-            } else {
1573
-                $error = esc_html__(
1574
-                    'Registration check in update failed because of an unknown database error',
1575
-                    'event_espresso'
1576
-                );
1577
-            }
1578
-            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1579
-            return false;
1580
-        }
1581
-        return $status_to;
1582
-    }
1583
-
1584
-
1585
-    /**
1586
-     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1587
-     * "Latest" is defined by the `DTT_EVT_start` column.
1588
-     *
1589
-     * @return EE_Datetime|null
1590
-     * @throws EE_Error
1591
-     */
1592
-    public function get_latest_related_datetime()
1593
-    {
1594
-        return EEM_Datetime::instance()->get_one(
1595
-            array(
1596
-                array(
1597
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1598
-                ),
1599
-                'order_by' => array('DTT_EVT_start' => 'DESC'),
1600
-            )
1601
-        );
1602
-    }
1603
-
1604
-
1605
-    /**
1606
-     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1607
-     * "Earliest" is defined by the `DTT_EVT_start` column.
1608
-     *
1609
-     * @throws EE_Error
1610
-     */
1611
-    public function get_earliest_related_datetime()
1612
-    {
1613
-        return EEM_Datetime::instance()->get_one(
1614
-            array(
1615
-                array(
1616
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1617
-                ),
1618
-                'order_by' => array('DTT_EVT_start' => 'ASC'),
1619
-            )
1620
-        );
1621
-    }
1622
-
1623
-
1624
-    /**
1625
-     * This method simply returns the check-in status for this registration and the given datetime.
1626
-     * If neither the datetime nor the checkin values are provided as arguments,
1627
-     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1628
-     *
1629
-     * @param  int       $DTT_ID  The ID of the datetime we're checking against
1630
-     *                            (if empty we'll get the primary datetime for
1631
-     *                            this registration (via event) and use it's ID);
1632
-     * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1633
-     *
1634
-     * @return int                Integer representing Check-in status.
1635
-     * @throws EE_Error
1636
-     */
1637
-    public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1638
-    {
1639
-        $checkin_query_params = array(
1640
-            'order_by' => array('CHK_timestamp' => 'DESC'),
1641
-        );
1642
-
1643
-        if ($DTT_ID > 0) {
1644
-            $checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1645
-        }
1646
-
1647
-        // get checkin object (if exists)
1648
-        $checkin = $checkin instanceof EE_Checkin
1649
-            ? $checkin
1650
-            : $this->get_first_related('Checkin', $checkin_query_params);
1651
-        if ($checkin instanceof EE_Checkin) {
1652
-            if ($checkin->get('CHK_in')) {
1653
-                return EE_Checkin::status_checked_in; // checked in
1654
-            }
1655
-            return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1656
-        }
1657
-        return EE_Checkin::status_checked_never; // never been checked in
1658
-    }
1659
-
1660
-
1661
-    /**
1662
-     * This method returns a localized message for the toggled Check-in message.
1663
-     *
1664
-     * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1665
-     *                     then it is assumed Check-in for primary datetime was toggled.
1666
-     * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1667
-     *                     message can be customized with the attendee name.
1668
-     * @return string internationalized message
1669
-     * @throws EE_Error
1670
-     */
1671
-    public function get_checkin_msg($DTT_ID, $error = false)
1672
-    {
1673
-        // let's get the attendee first so we can include the name of the attendee
1674
-        $attendee = $this->get_first_related('Attendee');
1675
-        if ($attendee instanceof EE_Attendee) {
1676
-            if ($error) {
1677
-                return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1678
-            }
1679
-            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1680
-            // what is the status message going to be?
1681
-            switch ($cur_status) {
1682
-                case EE_Checkin::status_checked_never:
1683
-                    return sprintf(
1684
-                        __("%s has been removed from Check-in records", "event_espresso"),
1685
-                        $attendee->full_name()
1686
-                    );
1687
-                    break;
1688
-                case EE_Checkin::status_checked_in:
1689
-                    return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1690
-                    break;
1691
-                case EE_Checkin::status_checked_out:
1692
-                    return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1693
-                    break;
1694
-            }
1695
-        }
1696
-        return esc_html__("The check-in status could not be determined.", "event_espresso");
1697
-    }
1698
-
1699
-
1700
-    /**
1701
-     * Returns the related EE_Transaction to this registration
1702
-     *
1703
-     * @return EE_Transaction
1704
-     * @throws EE_Error
1705
-     * @throws EntityNotFoundException
1706
-     */
1707
-    public function transaction()
1708
-    {
1709
-        $transaction = $this->get_first_related('Transaction');
1710
-        if (! $transaction instanceof \EE_Transaction) {
1711
-            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1712
-        }
1713
-        return $transaction;
1714
-    }
1715
-
1716
-
1717
-    /**
1718
-     *        get Registration Code
1719
-     */
1720
-    public function reg_code()
1721
-    {
1722
-        return $this->get('REG_code');
1723
-    }
1724
-
1725
-
1726
-    /**
1727
-     *        get Transaction ID
1728
-     */
1729
-    public function transaction_ID()
1730
-    {
1731
-        return $this->get('TXN_ID');
1732
-    }
1733
-
1734
-
1735
-    /**
1736
-     * @return int
1737
-     * @throws EE_Error
1738
-     */
1739
-    public function ticket_ID()
1740
-    {
1741
-        return $this->get('TKT_ID');
1742
-    }
1743
-
1744
-
1745
-    /**
1746
-     *        Set Registration Code
1747
-     *
1748
-     * @access    public
1749
-     * @param    string  $REG_code Registration Code
1750
-     * @param    boolean $use_default
1751
-     * @throws EE_Error
1752
-     */
1753
-    public function set_reg_code($REG_code, $use_default = false)
1754
-    {
1755
-        if (empty($REG_code)) {
1756
-            EE_Error::add_error(
1757
-                esc_html__('REG_code can not be empty.', 'event_espresso'),
1758
-                __FILE__,
1759
-                __FUNCTION__,
1760
-                __LINE__
1761
-            );
1762
-            return;
1763
-        }
1764
-        if (! $this->reg_code()) {
1765
-            parent::set('REG_code', $REG_code, $use_default);
1766
-        } else {
1767
-            EE_Error::doing_it_wrong(
1768
-                __CLASS__ . '::' . __FUNCTION__,
1769
-                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1770
-                '4.6.0'
1771
-            );
1772
-        }
1773
-    }
1774
-
1775
-
1776
-    /**
1777
-     * Returns all other registrations in the same group as this registrant who have the same ticket option.
1778
-     * Note, if you want to just get all registrations in the same transaction (group), use:
1779
-     *    $registration->transaction()->registrations();
1780
-     *
1781
-     * @since 4.5.0
1782
-     * @return EE_Registration[] or empty array if this isn't a group registration.
1783
-     * @throws EE_Error
1784
-     */
1785
-    public function get_all_other_registrations_in_group()
1786
-    {
1787
-        if ($this->group_size() < 2) {
1788
-            return array();
1789
-        }
1790
-
1791
-        $query[0] = array(
1792
-            'TXN_ID' => $this->transaction_ID(),
1793
-            'REG_ID' => array('!=', $this->ID()),
1794
-            'TKT_ID' => $this->ticket_ID(),
1795
-        );
1796
-        /** @var EE_Registration[] $registrations */
1797
-        $registrations = $this->get_model()->get_all($query);
1798
-        return $registrations;
1799
-    }
1800
-
1801
-    /**
1802
-     * Return the link to the admin details for the object.
1803
-     *
1804
-     * @return string
1805
-     * @throws EE_Error
1806
-     */
1807
-    public function get_admin_details_link()
1808
-    {
1809
-        EE_Registry::instance()->load_helper('URL');
1810
-        return EEH_URL::add_query_args_and_nonce(
1811
-            array(
1812
-                'page'    => 'espresso_registrations',
1813
-                'action'  => 'view_registration',
1814
-                '_REG_ID' => $this->ID(),
1815
-            ),
1816
-            admin_url('admin.php')
1817
-        );
1818
-    }
1819
-
1820
-    /**
1821
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1822
-     *
1823
-     * @return string
1824
-     * @throws EE_Error
1825
-     */
1826
-    public function get_admin_edit_link()
1827
-    {
1828
-        return $this->get_admin_details_link();
1829
-    }
1830
-
1831
-    /**
1832
-     * Returns the link to a settings page for the object.
1833
-     *
1834
-     * @return string
1835
-     * @throws EE_Error
1836
-     */
1837
-    public function get_admin_settings_link()
1838
-    {
1839
-        return $this->get_admin_details_link();
1840
-    }
1841
-
1842
-    /**
1843
-     * Returns the link to the "overview" for the object (typically the "list table" view).
1844
-     *
1845
-     * @return string
1846
-     */
1847
-    public function get_admin_overview_link()
1848
-    {
1849
-        EE_Registry::instance()->load_helper('URL');
1850
-        return EEH_URL::add_query_args_and_nonce(
1851
-            array(
1852
-                'page' => 'espresso_registrations',
1853
-            ),
1854
-            admin_url('admin.php')
1855
-        );
1856
-    }
1857
-
1858
-
1859
-    /**
1860
-     * @param array $query_params
1861
-     *
1862
-     * @return \EE_Registration[]
1863
-     * @throws EE_Error
1864
-     */
1865
-    public function payments($query_params = array())
1866
-    {
1867
-        return $this->get_many_related('Payment', $query_params);
1868
-    }
1869
-
1870
-
1871
-    /**
1872
-     * @param array $query_params
1873
-     *
1874
-     * @return \EE_Registration_Payment[]
1875
-     * @throws EE_Error
1876
-     */
1877
-    public function registration_payments($query_params = array())
1878
-    {
1879
-        return $this->get_many_related('Registration_Payment', $query_params);
1880
-    }
1881
-
1882
-
1883
-    /**
1884
-     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1885
-     * Note: if there are no payments on the registration there will be no payment method returned.
1886
-     *
1887
-     * @return EE_Payment_Method|null
1888
-     */
1889
-    public function payment_method()
1890
-    {
1891
-        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1892
-    }
1893
-
1894
-
1895
-    /**
1896
-     * @return \EE_Line_Item
1897
-     * @throws EntityNotFoundException
1898
-     * @throws EE_Error
1899
-     */
1900
-    public function ticket_line_item()
1901
-    {
1902
-        $ticket = $this->ticket();
1903
-        $transaction = $this->transaction();
1904
-        $line_item = null;
1905
-        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1906
-            $transaction->total_line_item(),
1907
-            'Ticket',
1908
-            array($ticket->ID())
1909
-        );
1910
-        foreach ($ticket_line_items as $ticket_line_item) {
1911
-            if ($ticket_line_item instanceof \EE_Line_Item
1912
-                && $ticket_line_item->OBJ_type() === 'Ticket'
1913
-                && $ticket_line_item->OBJ_ID() === $ticket->ID()
1914
-            ) {
1915
-                $line_item = $ticket_line_item;
1916
-                break;
1917
-            }
1918
-        }
1919
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1920
-            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1921
-        }
1922
-        return $line_item;
1923
-    }
1924
-
1925
-
1926
-    /**
1927
-     * Soft Deletes this model object.
1928
-     *
1929
-     * @return boolean | int
1930
-     * @throws RuntimeException
1931
-     * @throws EE_Error
1932
-     */
1933
-    public function delete()
1934
-    {
1935
-        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1936
-            $this->set_status(EEM_Registration::status_id_cancelled);
1937
-        }
1938
-        return parent::delete();
1939
-    }
1940
-
1941
-
1942
-    /**
1943
-     * Restores whatever the previous status was on a registration before it was trashed (if possible)
1944
-     *
1945
-     * @throws EE_Error
1946
-     * @throws RuntimeException
1947
-     */
1948
-    public function restore()
1949
-    {
1950
-        $previous_status = $this->get_extra_meta(
1951
-            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1952
-            true,
1953
-            EEM_Registration::status_id_cancelled
1954
-        );
1955
-        if ($previous_status) {
1956
-            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1957
-            $this->set_status($previous_status);
1958
-        }
1959
-        return parent::restore();
1960
-    }
1961
-
1962
-
1963
-    /**
1964
-     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1965
-     *
1966
-     * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1967
-     *                                           depending on whether the reg status changes to or from "Approved"
1968
-     * @return boolean whether the Registration status was updated
1969
-     * @throws EE_Error
1970
-     * @throws RuntimeException
1971
-     */
1972
-    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1973
-    {
1974
-        $paid = $this->paid();
1975
-        $price = $this->final_price();
1976
-        switch (true) {
1977
-            // overpaid or paid
1978
-            case EEH_Money::compare_floats($paid, $price, '>'):
1979
-            case EEH_Money::compare_floats($paid, $price):
1980
-                $new_status = EEM_Registration::status_id_approved;
1981
-                break;
1982
-            //  underpaid
1983
-            case EEH_Money::compare_floats($paid, $price, '<'):
1984
-                $new_status = EEM_Registration::status_id_pending_payment;
1985
-                break;
1986
-            // uhhh Houston...
1987
-            default:
1988
-                throw new RuntimeException(
1989
-                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1990
-                );
1991
-        }
1992
-        if ($new_status !== $this->status_ID()) {
1993
-            if ($trigger_set_status_logic) {
1994
-                return $this->set_status($new_status);
1995
-            }
1996
-            parent::set('STS_ID', $new_status);
1997
-            return true;
1998
-        }
1999
-        return false;
2000
-    }
2001
-
2002
-
2003
-    /*************************** DEPRECATED ***************************/
2004
-
2005
-
2006
-    /**
2007
-     * @deprecated
2008
-     * @since     4.7.0
2009
-     * @access    public
2010
-     */
2011
-    public function price_paid()
2012
-    {
2013
-        EE_Error::doing_it_wrong(
2014
-            'EE_Registration::price_paid()',
2015
-            esc_html__(
2016
-                'This method is deprecated, please use EE_Registration::final_price() instead.',
2017
-                'event_espresso'
2018
-            ),
2019
-            '4.7.0'
2020
-        );
2021
-        return $this->final_price();
2022
-    }
2023
-
2024
-
2025
-    /**
2026
-     * @deprecated
2027
-     * @since     4.7.0
2028
-     * @access    public
2029
-     * @param    float $REG_final_price
2030
-     * @throws EE_Error
2031
-     * @throws RuntimeException
2032
-     */
2033
-    public function set_price_paid($REG_final_price = 0.00)
2034
-    {
2035
-        EE_Error::doing_it_wrong(
2036
-            'EE_Registration::set_price_paid()',
2037
-            esc_html__(
2038
-                'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2039
-                'event_espresso'
2040
-            ),
2041
-            '4.7.0'
2042
-        );
2043
-        $this->set_final_price($REG_final_price);
2044
-    }
2045
-
2046
-
2047
-    /**
2048
-     * @deprecated
2049
-     * @since 4.7.0
2050
-     * @return string
2051
-     * @throws EE_Error
2052
-     */
2053
-    public function pretty_price_paid()
2054
-    {
2055
-        EE_Error::doing_it_wrong(
2056
-            'EE_Registration::pretty_price_paid()',
2057
-            esc_html__(
2058
-                'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2059
-                'event_espresso'
2060
-            ),
2061
-            '4.7.0'
2062
-        );
2063
-        return $this->pretty_final_price();
2064
-    }
2065
-
2066
-
2067
-    /**
2068
-     * Gets the primary datetime related to this registration via the related Event to this registration
2069
-     *
2070
-     * @deprecated 4.9.17
2071
-     * @return EE_Datetime
2072
-     * @throws EE_Error
2073
-     * @throws EntityNotFoundException
2074
-     */
2075
-    public function get_related_primary_datetime()
2076
-    {
2077
-        EE_Error::doing_it_wrong(
2078
-            __METHOD__,
2079
-            esc_html__(
2080
-                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2081
-                'event_espresso'
2082
-            ),
2083
-            '4.9.17',
2084
-            '5.0.0'
2085
-        );
2086
-        return $this->event()->primary_datetime();
2087
-    }
20
+	/**
21
+	 * Used to reference when a registration has never been checked in.
22
+	 *
23
+	 * @deprecated use \EE_Checkin::status_checked_never instead
24
+	 * @type int
25
+	 */
26
+	const checkin_status_never = 2;
27
+
28
+	/**
29
+	 * Used to reference when a registration has been checked in.
30
+	 *
31
+	 * @deprecated use \EE_Checkin::status_checked_in instead
32
+	 * @type int
33
+	 */
34
+	const checkin_status_in = 1;
35
+
36
+
37
+	/**
38
+	 * Used to reference when a registration has been checked out.
39
+	 *
40
+	 * @deprecated use \EE_Checkin::status_checked_out instead
41
+	 * @type int
42
+	 */
43
+	const checkin_status_out = 0;
44
+
45
+
46
+	/**
47
+	 * extra meta key for tracking reg status os trashed registrations
48
+	 *
49
+	 * @type string
50
+	 */
51
+	const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
52
+
53
+
54
+	/**
55
+	 * extra meta key for tracking if registration has reserved ticket
56
+	 *
57
+	 * @type string
58
+	 */
59
+	const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
60
+
61
+
62
+	/**
63
+	 * @param array  $props_n_values          incoming values
64
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
65
+	 *                                        used.)
66
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
67
+	 *                                        date_format and the second value is the time format
68
+	 * @return EE_Registration
69
+	 * @throws EE_Error
70
+	 */
71
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
+	{
73
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
+	}
76
+
77
+
78
+	/**
79
+	 * @param array  $props_n_values  incoming values from the database
80
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
+	 *                                the website will be used.
82
+	 * @return EE_Registration
83
+	 */
84
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
85
+	{
86
+		return new self($props_n_values, true, $timezone);
87
+	}
88
+
89
+
90
+	/**
91
+	 *        Set Event ID
92
+	 *
93
+	 * @param        int $EVT_ID Event ID
94
+	 * @throws EE_Error
95
+	 * @throws RuntimeException
96
+	 */
97
+	public function set_event($EVT_ID = 0)
98
+	{
99
+		$this->set('EVT_ID', $EVT_ID);
100
+	}
101
+
102
+
103
+	/**
104
+	 * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
105
+	 * be routed to internal methods
106
+	 *
107
+	 * @param string $field_name
108
+	 * @param mixed  $field_value
109
+	 * @param bool   $use_default
110
+	 * @throws EE_Error
111
+	 * @throws EntityNotFoundException
112
+	 * @throws InvalidArgumentException
113
+	 * @throws InvalidDataTypeException
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws ReflectionException
116
+	 * @throws RuntimeException
117
+	 */
118
+	public function set($field_name, $field_value, $use_default = false)
119
+	{
120
+		switch ($field_name) {
121
+			case 'REG_code':
122
+				if (! empty($field_value) && $this->reg_code() === null) {
123
+					$this->set_reg_code($field_value, $use_default);
124
+				}
125
+				break;
126
+			case 'STS_ID':
127
+				$this->set_status($field_value, $use_default);
128
+				break;
129
+			default:
130
+				parent::set($field_name, $field_value, $use_default);
131
+		}
132
+	}
133
+
134
+
135
+	/**
136
+	 * Set Status ID
137
+	 * updates the registration status and ALSO...
138
+	 * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
139
+	 * calls release_registration_space() if the reg status changes FROM approved to any other reg status
140
+	 *
141
+	 * @param string                $new_STS_ID
142
+	 * @param boolean               $use_default
143
+	 * @param ContextInterface|null $context
144
+	 * @return bool
145
+	 * @throws EE_Error
146
+	 * @throws EntityNotFoundException
147
+	 * @throws InvalidArgumentException
148
+	 * @throws ReflectionException
149
+	 * @throws RuntimeException
150
+	 * @throws InvalidDataTypeException
151
+	 * @throws InvalidInterfaceException
152
+	 */
153
+	public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
154
+	{
155
+		// get current REG_Status
156
+		$old_STS_ID = $this->status_ID();
157
+		// if status has changed
158
+		if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
159
+			&& ! empty($old_STS_ID) // and that old status is actually set
160
+			&& ! empty($new_STS_ID) // as well as the new status
161
+			&& $this->ID() // ensure registration is in the db
162
+		) {
163
+			// update internal status first
164
+			parent::set('STS_ID', $new_STS_ID, $use_default);
165
+			// THEN handle other changes that occur when reg status changes
166
+			// TO approved
167
+			if ($new_STS_ID === EEM_Registration::status_id_approved) {
168
+				// reserve a space by incrementing ticket and datetime sold values
169
+				$this->_reserve_registration_space();
170
+				do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
171
+				// OR FROM  approved
172
+			} elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
173
+				// release a space by decrementing ticket and datetime sold values
174
+				$this->_release_registration_space();
175
+				do_action(
176
+					'AHEE__EE_Registration__set_status__from_approved',
177
+					$this,
178
+					$old_STS_ID,
179
+					$new_STS_ID,
180
+					$context
181
+				);
182
+			}
183
+			$this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, $context);
184
+			if ($this->statusChangeUpdatesTransaction($context)) {
185
+				$this->updateTransactionAfterStatusChange();
186
+			}
187
+			do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
188
+			return true;
189
+		}
190
+		// even though the old value matches the new value, it's still good to
191
+		// allow the parent set method to have a say
192
+		parent::set('STS_ID', $new_STS_ID, $use_default);
193
+		return true;
194
+	}
195
+
196
+
197
+	/**
198
+	 * update REGs and TXN when cancelled or declined registrations involved
199
+	 *
200
+	 * @param string                $new_STS_ID
201
+	 * @param string                $old_STS_ID
202
+	 * @param ContextInterface|null $context
203
+	 * @throws EE_Error
204
+	 * @throws InvalidArgumentException
205
+	 * @throws InvalidDataTypeException
206
+	 * @throws InvalidInterfaceException
207
+	 * @throws ReflectionException
208
+	 */
209
+	private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
210
+	{
211
+		// these reg statuses should not be considered in any calculations involving monies owing
212
+		$closed_reg_statuses = EEM_Registration::closed_reg_statuses();
213
+		// true if registration has been cancelled or declined
214
+		$this->updateIfCanceled(
215
+			$closed_reg_statuses,
216
+			$new_STS_ID,
217
+			$old_STS_ID,
218
+			$context
219
+		);
220
+		$this->updateIfDeclined(
221
+			$closed_reg_statuses,
222
+			$new_STS_ID,
223
+			$old_STS_ID,
224
+			$context
225
+		);
226
+	}
227
+
228
+
229
+	/**
230
+	 * update REGs and TXN when cancelled or declined registrations involved
231
+	 *
232
+	 * @param array                 $closed_reg_statuses
233
+	 * @param string                $new_STS_ID
234
+	 * @param string                $old_STS_ID
235
+	 * @param ContextInterface|null $context
236
+	 * @throws EE_Error
237
+	 * @throws InvalidArgumentException
238
+	 * @throws InvalidDataTypeException
239
+	 * @throws InvalidInterfaceException
240
+	 * @throws ReflectionException
241
+	 */
242
+	private function updateIfCanceled(
243
+		array $closed_reg_statuses,
244
+		$new_STS_ID,
245
+		$old_STS_ID,
246
+		ContextInterface $context = null
247
+	) {
248
+		// true if registration has been cancelled or declined
249
+		if (in_array($new_STS_ID, $closed_reg_statuses, true)
250
+			&& ! in_array($old_STS_ID, $closed_reg_statuses, true)
251
+		) {
252
+			/** @type EE_Registration_Processor $registration_processor */
253
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
254
+			/** @type EE_Transaction_Processor $transaction_processor */
255
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
256
+			// cancelled or declined registration
257
+			$registration_processor->update_registration_after_being_canceled_or_declined(
258
+				$this,
259
+				$closed_reg_statuses
260
+			);
261
+			$transaction_processor->update_transaction_after_canceled_or_declined_registration(
262
+				$this,
263
+				$closed_reg_statuses,
264
+				false
265
+			);
266
+			do_action(
267
+				'AHEE__EE_Registration__set_status__canceled_or_declined',
268
+				$this,
269
+				$old_STS_ID,
270
+				$new_STS_ID,
271
+				$context
272
+			);
273
+			return;
274
+		}
275
+	}
276
+
277
+
278
+	/**
279
+	 * update REGs and TXN when cancelled or declined registrations involved
280
+	 *
281
+	 * @param array                 $closed_reg_statuses
282
+	 * @param string                $new_STS_ID
283
+	 * @param string                $old_STS_ID
284
+	 * @param ContextInterface|null $context
285
+	 * @throws EE_Error
286
+	 * @throws InvalidArgumentException
287
+	 * @throws InvalidDataTypeException
288
+	 * @throws InvalidInterfaceException
289
+	 * @throws ReflectionException
290
+	 */
291
+	private function updateIfDeclined(
292
+		array $closed_reg_statuses,
293
+		$new_STS_ID,
294
+		$old_STS_ID,
295
+		ContextInterface $context = null
296
+	) {
297
+		// true if reinstating cancelled or declined registration
298
+		if (in_array($old_STS_ID, $closed_reg_statuses, true)
299
+			&& ! in_array($new_STS_ID, $closed_reg_statuses, true)
300
+		) {
301
+			/** @type EE_Registration_Processor $registration_processor */
302
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
303
+			/** @type EE_Transaction_Processor $transaction_processor */
304
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
305
+			// reinstating cancelled or declined registration
306
+			$registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
307
+				$this,
308
+				$closed_reg_statuses
309
+			);
310
+			$transaction_processor->update_transaction_after_reinstating_canceled_registration(
311
+				$this,
312
+				$closed_reg_statuses,
313
+				false
314
+			);
315
+			do_action(
316
+				'AHEE__EE_Registration__set_status__after_reinstated',
317
+				$this,
318
+				$old_STS_ID,
319
+				$new_STS_ID,
320
+				$context
321
+			);
322
+		}
323
+	}
324
+
325
+
326
+	/**
327
+	 * @param ContextInterface|null $context
328
+	 * @return bool
329
+	 */
330
+	private function statusChangeUpdatesTransaction(ContextInterface $context = null)
331
+	{
332
+		$contexts_that_do_not_update_transaction = (array) apply_filters(
333
+			'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
334
+			array('spco_reg_step_attendee_information_process_registrations'),
335
+			$context,
336
+			$this
337
+		);
338
+		return ! (
339
+			$context instanceof ContextInterface
340
+			&& in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
341
+		);
342
+	}
343
+
344
+
345
+	/**
346
+	 * @throws EE_Error
347
+	 * @throws EntityNotFoundException
348
+	 * @throws InvalidArgumentException
349
+	 * @throws InvalidDataTypeException
350
+	 * @throws InvalidInterfaceException
351
+	 * @throws ReflectionException
352
+	 * @throws RuntimeException
353
+	 */
354
+	private function updateTransactionAfterStatusChange()
355
+	{
356
+		/** @type EE_Transaction_Payments $transaction_payments */
357
+		$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
358
+		$transaction_payments->recalculate_transaction_total($this->transaction(), false);
359
+		$this->transaction()->update_status_based_on_total_paid(true);
360
+	}
361
+
362
+
363
+	/**
364
+	 *        get Status ID
365
+	 */
366
+	public function status_ID()
367
+	{
368
+		return $this->get('STS_ID');
369
+	}
370
+
371
+
372
+	/**
373
+	 * Gets the ticket this registration is for
374
+	 *
375
+	 * @param boolean $include_archived whether to include archived tickets or not.
376
+	 *
377
+	 * @return EE_Ticket|EE_Base_Class
378
+	 * @throws EE_Error
379
+	 */
380
+	public function ticket($include_archived = true)
381
+	{
382
+		$query_params = array();
383
+		if ($include_archived) {
384
+			$query_params['default_where_conditions'] = 'none';
385
+		}
386
+		return $this->get_first_related('Ticket', $query_params);
387
+	}
388
+
389
+
390
+	/**
391
+	 * Gets the event this registration is for
392
+	 *
393
+	 * @return EE_Event
394
+	 * @throws EE_Error
395
+	 * @throws EntityNotFoundException
396
+	 */
397
+	public function event()
398
+	{
399
+		$event = $this->get_first_related('Event');
400
+		if (! $event instanceof \EE_Event) {
401
+			throw new EntityNotFoundException('Event ID', $this->event_ID());
402
+		}
403
+		return $event;
404
+	}
405
+
406
+
407
+	/**
408
+	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
409
+	 * with the author of the event this registration is for.
410
+	 *
411
+	 * @since 4.5.0
412
+	 * @return int
413
+	 * @throws EE_Error
414
+	 * @throws EntityNotFoundException
415
+	 */
416
+	public function wp_user()
417
+	{
418
+		$event = $this->event();
419
+		if ($event instanceof EE_Event) {
420
+			return $event->wp_user();
421
+		}
422
+		return 0;
423
+	}
424
+
425
+
426
+	/**
427
+	 * increments this registration's related ticket sold and corresponding datetime sold values
428
+	 *
429
+	 * @return void
430
+	 * @throws DomainException
431
+	 * @throws EE_Error
432
+	 * @throws EntityNotFoundException
433
+	 * @throws InvalidArgumentException
434
+	 * @throws InvalidDataTypeException
435
+	 * @throws InvalidInterfaceException
436
+	 * @throws ReflectionException
437
+	 * @throws UnexpectedEntityException
438
+	 */
439
+	private function _reserve_registration_space()
440
+	{
441
+		// reserved ticket and datetime counts will be decremented as sold counts are incremented
442
+		// so stop tracking that this reg has a ticket reserved
443
+		$this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
444
+		$ticket = $this->ticket();
445
+		$ticket->increase_sold();
446
+		$ticket->save();
447
+		// possibly set event status to sold out
448
+		$this->event()->perform_sold_out_status_check();
449
+	}
450
+
451
+
452
+	/**
453
+	 * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
454
+	 *
455
+	 * @return void
456
+	 * @throws DomainException
457
+	 * @throws EE_Error
458
+	 * @throws EntityNotFoundException
459
+	 * @throws InvalidArgumentException
460
+	 * @throws InvalidDataTypeException
461
+	 * @throws InvalidInterfaceException
462
+	 * @throws ReflectionException
463
+	 * @throws UnexpectedEntityException
464
+	 */
465
+	private function _release_registration_space()
466
+	{
467
+		$ticket = $this->ticket();
468
+		$ticket->decrease_sold();
469
+		$ticket->save();
470
+		// possibly change event status from sold out back to previous status
471
+		$this->event()->perform_sold_out_status_check();
472
+	}
473
+
474
+
475
+	/**
476
+	 * tracks this registration's ticket reservation in extra meta
477
+	 * and can increment related ticket reserved and corresponding datetime reserved values
478
+	 *
479
+	 * @param bool $update_ticket if true, will increment ticket and datetime reserved count
480
+	 * @return void
481
+	 * @throws EE_Error
482
+	 * @throws InvalidArgumentException
483
+	 * @throws InvalidDataTypeException
484
+	 * @throws InvalidInterfaceException
485
+	 * @throws ReflectionException
486
+	 */
487
+	public function reserve_ticket($update_ticket = false, $source = 'unknown')
488
+	{
489
+		// only reserve ticket if space is not currently reserved
490
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
491
+			$this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
492
+			// IMPORTANT !!!
493
+			// although checking $update_ticket first would be more efficient,
494
+			// we NEED to ALWAYS call update_extra_meta(), which is why that is done first
495
+			if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true)
496
+				&& $update_ticket
497
+			) {
498
+				$ticket = $this->ticket();
499
+				$ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
500
+				$ticket->save();
501
+			}
502
+		}
503
+	}
504
+
505
+
506
+	/**
507
+	 * stops tracking this registration's ticket reservation in extra meta
508
+	 * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
509
+	 *
510
+	 * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
511
+	 * @return void
512
+	 * @throws EE_Error
513
+	 * @throws InvalidArgumentException
514
+	 * @throws InvalidDataTypeException
515
+	 * @throws InvalidInterfaceException
516
+	 * @throws ReflectionException
517
+	 */
518
+	public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
519
+	{
520
+		// only release ticket if space is currently reserved
521
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
522
+			$this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
523
+			// IMPORTANT !!!
524
+			// although checking $update_ticket first would be more efficient,
525
+			// we NEED to ALWAYS call update_extra_meta(), which is why that is done first
526
+			if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false)
527
+				&& $update_ticket
528
+			) {
529
+				$ticket = $this->ticket();
530
+				$ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
531
+				$ticket->save();
532
+			}
533
+		}
534
+	}
535
+
536
+
537
+	/**
538
+	 * Set Attendee ID
539
+	 *
540
+	 * @param        int $ATT_ID Attendee ID
541
+	 * @throws EE_Error
542
+	 * @throws RuntimeException
543
+	 */
544
+	public function set_attendee_id($ATT_ID = 0)
545
+	{
546
+		$this->set('ATT_ID', $ATT_ID);
547
+	}
548
+
549
+
550
+	/**
551
+	 *        Set Transaction ID
552
+	 *
553
+	 * @param        int $TXN_ID Transaction ID
554
+	 * @throws EE_Error
555
+	 * @throws RuntimeException
556
+	 */
557
+	public function set_transaction_id($TXN_ID = 0)
558
+	{
559
+		$this->set('TXN_ID', $TXN_ID);
560
+	}
561
+
562
+
563
+	/**
564
+	 *        Set Session
565
+	 *
566
+	 * @param    string $REG_session PHP Session ID
567
+	 * @throws EE_Error
568
+	 * @throws RuntimeException
569
+	 */
570
+	public function set_session($REG_session = '')
571
+	{
572
+		$this->set('REG_session', $REG_session);
573
+	}
574
+
575
+
576
+	/**
577
+	 *        Set Registration URL Link
578
+	 *
579
+	 * @param    string $REG_url_link Registration URL Link
580
+	 * @throws EE_Error
581
+	 * @throws RuntimeException
582
+	 */
583
+	public function set_reg_url_link($REG_url_link = '')
584
+	{
585
+		$this->set('REG_url_link', $REG_url_link);
586
+	}
587
+
588
+
589
+	/**
590
+	 *        Set Attendee Counter
591
+	 *
592
+	 * @param        int $REG_count Primary Attendee
593
+	 * @throws EE_Error
594
+	 * @throws RuntimeException
595
+	 */
596
+	public function set_count($REG_count = 1)
597
+	{
598
+		$this->set('REG_count', $REG_count);
599
+	}
600
+
601
+
602
+	/**
603
+	 *        Set Group Size
604
+	 *
605
+	 * @param        boolean $REG_group_size Group Registration
606
+	 * @throws EE_Error
607
+	 * @throws RuntimeException
608
+	 */
609
+	public function set_group_size($REG_group_size = false)
610
+	{
611
+		$this->set('REG_group_size', $REG_group_size);
612
+	}
613
+
614
+
615
+	/**
616
+	 *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
617
+	 *    EEM_Registration::status_id_not_approved
618
+	 *
619
+	 * @return        boolean
620
+	 */
621
+	public function is_not_approved()
622
+	{
623
+		return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
624
+	}
625
+
626
+
627
+	/**
628
+	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
629
+	 *    EEM_Registration::status_id_pending_payment
630
+	 *
631
+	 * @return        boolean
632
+	 */
633
+	public function is_pending_payment()
634
+	{
635
+		return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
636
+	}
637
+
638
+
639
+	/**
640
+	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
641
+	 *
642
+	 * @return        boolean
643
+	 */
644
+	public function is_approved()
645
+	{
646
+		return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
647
+	}
648
+
649
+
650
+	/**
651
+	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
652
+	 *
653
+	 * @return        boolean
654
+	 */
655
+	public function is_cancelled()
656
+	{
657
+		return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
658
+	}
659
+
660
+
661
+	/**
662
+	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
663
+	 *
664
+	 * @return        boolean
665
+	 */
666
+	public function is_declined()
667
+	{
668
+		return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
669
+	}
670
+
671
+
672
+	/**
673
+	 *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
674
+	 *    EEM_Registration::status_id_incomplete
675
+	 *
676
+	 * @return        boolean
677
+	 */
678
+	public function is_incomplete()
679
+	{
680
+		return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
681
+	}
682
+
683
+
684
+	/**
685
+	 *        Set Registration Date
686
+	 *
687
+	 * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
688
+	 *                                                 Date
689
+	 * @throws EE_Error
690
+	 * @throws RuntimeException
691
+	 */
692
+	public function set_reg_date($REG_date = false)
693
+	{
694
+		$this->set('REG_date', $REG_date);
695
+	}
696
+
697
+
698
+	/**
699
+	 *    Set final price owing for this registration after all ticket/price modifications
700
+	 *
701
+	 * @access    public
702
+	 * @param    float $REG_final_price
703
+	 * @throws EE_Error
704
+	 * @throws RuntimeException
705
+	 */
706
+	public function set_final_price($REG_final_price = 0.00)
707
+	{
708
+		$this->set('REG_final_price', $REG_final_price);
709
+	}
710
+
711
+
712
+	/**
713
+	 *    Set amount paid towards this registration's final price
714
+	 *
715
+	 * @access    public
716
+	 * @param    float $REG_paid
717
+	 * @throws EE_Error
718
+	 * @throws RuntimeException
719
+	 */
720
+	public function set_paid($REG_paid = 0.00)
721
+	{
722
+		$this->set('REG_paid', $REG_paid);
723
+	}
724
+
725
+
726
+	/**
727
+	 *        Attendee Is Going
728
+	 *
729
+	 * @param        boolean $REG_att_is_going Attendee Is Going
730
+	 * @throws EE_Error
731
+	 * @throws RuntimeException
732
+	 */
733
+	public function set_att_is_going($REG_att_is_going = false)
734
+	{
735
+		$this->set('REG_att_is_going', $REG_att_is_going);
736
+	}
737
+
738
+
739
+	/**
740
+	 * Gets the related attendee
741
+	 *
742
+	 * @return EE_Attendee
743
+	 * @throws EE_Error
744
+	 */
745
+	public function attendee()
746
+	{
747
+		return $this->get_first_related('Attendee');
748
+	}
749
+
750
+
751
+	/**
752
+	 *        get Event ID
753
+	 */
754
+	public function event_ID()
755
+	{
756
+		return $this->get('EVT_ID');
757
+	}
758
+
759
+
760
+	/**
761
+	 *        get Event ID
762
+	 */
763
+	public function event_name()
764
+	{
765
+		$event = $this->event_obj();
766
+		if ($event) {
767
+			return $event->name();
768
+		} else {
769
+			return null;
770
+		}
771
+	}
772
+
773
+
774
+	/**
775
+	 * Fetches the event this registration is for
776
+	 *
777
+	 * @return EE_Event
778
+	 * @throws EE_Error
779
+	 */
780
+	public function event_obj()
781
+	{
782
+		return $this->get_first_related('Event');
783
+	}
784
+
785
+
786
+	/**
787
+	 *        get Attendee ID
788
+	 */
789
+	public function attendee_ID()
790
+	{
791
+		return $this->get('ATT_ID');
792
+	}
793
+
794
+
795
+	/**
796
+	 *        get PHP Session ID
797
+	 */
798
+	public function session_ID()
799
+	{
800
+		return $this->get('REG_session');
801
+	}
802
+
803
+
804
+	/**
805
+	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
806
+	 *
807
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
808
+	 * @return string
809
+	 */
810
+	public function receipt_url($messenger = 'html')
811
+	{
812
+
813
+		/**
814
+		 * The below will be deprecated one version after this.  We check first if there is a custom receipt template
815
+		 * already in use on old system.  If there is then we just return the standard url for it.
816
+		 *
817
+		 * @since 4.5.0
818
+		 */
819
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
820
+		$has_custom = EEH_Template::locate_template(
821
+			$template_relative_path,
822
+			array(),
823
+			true,
824
+			true,
825
+			true
826
+		);
827
+
828
+		if ($has_custom) {
829
+			return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
830
+		}
831
+		return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
832
+	}
833
+
834
+
835
+	/**
836
+	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
837
+	 *
838
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
839
+	 * @return string
840
+	 * @throws EE_Error
841
+	 */
842
+	public function invoice_url($messenger = 'html')
843
+	{
844
+		/**
845
+		 * The below will be deprecated one version after this.  We check first if there is a custom invoice template
846
+		 * already in use on old system.  If there is then we just return the standard url for it.
847
+		 *
848
+		 * @since 4.5.0
849
+		 */
850
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
851
+		$has_custom = EEH_Template::locate_template(
852
+			$template_relative_path,
853
+			array(),
854
+			true,
855
+			true,
856
+			true
857
+		);
858
+
859
+		if ($has_custom) {
860
+			if ($messenger == 'html') {
861
+				return $this->invoice_url('launch');
862
+			}
863
+			$route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
864
+
865
+			$query_args = array('ee' => $route, 'id' => $this->reg_url_link());
866
+			if ($messenger == 'html') {
867
+				$query_args['html'] = true;
868
+			}
869
+			return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
870
+		}
871
+		return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
872
+	}
873
+
874
+
875
+	/**
876
+	 * get Registration URL Link
877
+	 *
878
+	 * @access public
879
+	 * @return string
880
+	 * @throws EE_Error
881
+	 */
882
+	public function reg_url_link()
883
+	{
884
+		return (string) $this->get('REG_url_link');
885
+	}
886
+
887
+
888
+	/**
889
+	 * Echoes out invoice_url()
890
+	 *
891
+	 * @param string $type 'download','launch', or 'html' (default is 'launch')
892
+	 * @return void
893
+	 * @throws EE_Error
894
+	 */
895
+	public function e_invoice_url($type = 'launch')
896
+	{
897
+		echo $this->invoice_url($type);
898
+	}
899
+
900
+
901
+	/**
902
+	 * Echoes out payment_overview_url
903
+	 */
904
+	public function e_payment_overview_url()
905
+	{
906
+		echo $this->payment_overview_url();
907
+	}
908
+
909
+
910
+	/**
911
+	 * Gets the URL for the checkout payment options reg step
912
+	 * with this registration's REG_url_link added as a query parameter
913
+	 *
914
+	 * @param bool $clear_session Set to true when you want to clear the session on revisiting the
915
+	 *                            payment overview url.
916
+	 * @return string
917
+	 * @throws InvalidInterfaceException
918
+	 * @throws InvalidDataTypeException
919
+	 * @throws EE_Error
920
+	 * @throws InvalidArgumentException
921
+	 */
922
+	public function payment_overview_url($clear_session = false)
923
+	{
924
+		return add_query_arg(
925
+			(array) apply_filters(
926
+				'FHEE__EE_Registration__payment_overview_url__query_args',
927
+				array(
928
+					'e_reg_url_link' => $this->reg_url_link(),
929
+					'step'           => 'payment_options',
930
+					'revisit'        => true,
931
+					'clear_session'  => (bool) $clear_session,
932
+				),
933
+				$this
934
+			),
935
+			EE_Registry::instance()->CFG->core->reg_page_url()
936
+		);
937
+	}
938
+
939
+
940
+	/**
941
+	 * Gets the URL for the checkout attendee information reg step
942
+	 * with this registration's REG_url_link added as a query parameter
943
+	 *
944
+	 * @return string
945
+	 * @throws InvalidInterfaceException
946
+	 * @throws InvalidDataTypeException
947
+	 * @throws EE_Error
948
+	 * @throws InvalidArgumentException
949
+	 */
950
+	public function edit_attendee_information_url()
951
+	{
952
+		return add_query_arg(
953
+			(array) apply_filters(
954
+				'FHEE__EE_Registration__edit_attendee_information_url__query_args',
955
+				array(
956
+					'e_reg_url_link' => $this->reg_url_link(),
957
+					'step'           => 'attendee_information',
958
+					'revisit'        => true,
959
+				),
960
+				$this
961
+			),
962
+			EE_Registry::instance()->CFG->core->reg_page_url()
963
+		);
964
+	}
965
+
966
+
967
+	/**
968
+	 * Simply generates and returns the appropriate admin_url link to edit this registration
969
+	 *
970
+	 * @return string
971
+	 * @throws EE_Error
972
+	 */
973
+	public function get_admin_edit_url()
974
+	{
975
+		return EEH_URL::add_query_args_and_nonce(
976
+			array(
977
+				'page'    => 'espresso_registrations',
978
+				'action'  => 'view_registration',
979
+				'_REG_ID' => $this->ID(),
980
+			),
981
+			admin_url('admin.php')
982
+		);
983
+	}
984
+
985
+
986
+	/**
987
+	 *    is_primary_registrant?
988
+	 */
989
+	public function is_primary_registrant()
990
+	{
991
+		return $this->get('REG_count') == 1 ? true : false;
992
+	}
993
+
994
+
995
+	/**
996
+	 * This returns the primary registration object for this registration group (which may be this object).
997
+	 *
998
+	 * @return EE_Registration
999
+	 * @throws EE_Error
1000
+	 */
1001
+	public function get_primary_registration()
1002
+	{
1003
+		if ($this->is_primary_registrant()) {
1004
+			return $this;
1005
+		}
1006
+
1007
+		// k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1008
+		/** @var EE_Registration $primary_registrant */
1009
+		$primary_registrant = EEM_Registration::instance()->get_one(
1010
+			array(
1011
+				array(
1012
+					'TXN_ID'    => $this->transaction_ID(),
1013
+					'REG_count' => 1,
1014
+				),
1015
+			)
1016
+		);
1017
+		return $primary_registrant;
1018
+	}
1019
+
1020
+
1021
+	/**
1022
+	 *        get  Attendee Number
1023
+	 *
1024
+	 * @access        public
1025
+	 */
1026
+	public function count()
1027
+	{
1028
+		return $this->get('REG_count');
1029
+	}
1030
+
1031
+
1032
+	/**
1033
+	 *        get Group Size
1034
+	 */
1035
+	public function group_size()
1036
+	{
1037
+		return $this->get('REG_group_size');
1038
+	}
1039
+
1040
+
1041
+	/**
1042
+	 *        get Registration Date
1043
+	 */
1044
+	public function date()
1045
+	{
1046
+		return $this->get('REG_date');
1047
+	}
1048
+
1049
+
1050
+	/**
1051
+	 * gets a pretty date
1052
+	 *
1053
+	 * @param string $date_format
1054
+	 * @param string $time_format
1055
+	 * @return string
1056
+	 * @throws EE_Error
1057
+	 */
1058
+	public function pretty_date($date_format = null, $time_format = null)
1059
+	{
1060
+		return $this->get_datetime('REG_date', $date_format, $time_format);
1061
+	}
1062
+
1063
+
1064
+	/**
1065
+	 * final_price
1066
+	 * the registration's share of the transaction total, so that the
1067
+	 * sum of all the transaction's REG_final_prices equal the transaction's total
1068
+	 *
1069
+	 * @return float
1070
+	 * @throws EE_Error
1071
+	 */
1072
+	public function final_price()
1073
+	{
1074
+		return $this->get('REG_final_price');
1075
+	}
1076
+
1077
+
1078
+	/**
1079
+	 * pretty_final_price
1080
+	 *  final price as formatted string, with correct decimal places and currency symbol
1081
+	 *
1082
+	 * @return string
1083
+	 * @throws EE_Error
1084
+	 */
1085
+	public function pretty_final_price()
1086
+	{
1087
+		return $this->get_pretty('REG_final_price');
1088
+	}
1089
+
1090
+
1091
+	/**
1092
+	 * get paid (yeah)
1093
+	 *
1094
+	 * @return float
1095
+	 * @throws EE_Error
1096
+	 */
1097
+	public function paid()
1098
+	{
1099
+		return $this->get('REG_paid');
1100
+	}
1101
+
1102
+
1103
+	/**
1104
+	 * pretty_paid
1105
+	 *
1106
+	 * @return float
1107
+	 * @throws EE_Error
1108
+	 */
1109
+	public function pretty_paid()
1110
+	{
1111
+		return $this->get_pretty('REG_paid');
1112
+	}
1113
+
1114
+
1115
+	/**
1116
+	 * owes_monies_and_can_pay
1117
+	 * whether or not this registration has monies owing and it's' status allows payment
1118
+	 *
1119
+	 * @param array $requires_payment
1120
+	 * @return bool
1121
+	 * @throws EE_Error
1122
+	 */
1123
+	public function owes_monies_and_can_pay($requires_payment = array())
1124
+	{
1125
+		// these reg statuses require payment (if event is not free)
1126
+		$requires_payment = ! empty($requires_payment)
1127
+			? $requires_payment
1128
+			: EEM_Registration::reg_statuses_that_allow_payment();
1129
+		if (in_array($this->status_ID(), $requires_payment) &&
1130
+			$this->final_price() != 0 &&
1131
+			$this->final_price() != $this->paid()
1132
+		) {
1133
+			return true;
1134
+		} else {
1135
+			return false;
1136
+		}
1137
+	}
1138
+
1139
+
1140
+	/**
1141
+	 * Prints out the return value of $this->pretty_status()
1142
+	 *
1143
+	 * @param bool $show_icons
1144
+	 * @return void
1145
+	 * @throws EE_Error
1146
+	 */
1147
+	public function e_pretty_status($show_icons = false)
1148
+	{
1149
+		echo $this->pretty_status($show_icons);
1150
+	}
1151
+
1152
+
1153
+	/**
1154
+	 * Returns a nice version of the status for displaying to customers
1155
+	 *
1156
+	 * @param bool $show_icons
1157
+	 * @return string
1158
+	 * @throws EE_Error
1159
+	 */
1160
+	public function pretty_status($show_icons = false)
1161
+	{
1162
+		$status = EEM_Status::instance()->localized_status(
1163
+			array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1164
+			false,
1165
+			'sentence'
1166
+		);
1167
+		$icon = '';
1168
+		switch ($this->status_ID()) {
1169
+			case EEM_Registration::status_id_approved:
1170
+				$icon = $show_icons
1171
+					? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1172
+					: '';
1173
+				break;
1174
+			case EEM_Registration::status_id_pending_payment:
1175
+				$icon = $show_icons
1176
+					? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1177
+					: '';
1178
+				break;
1179
+			case EEM_Registration::status_id_not_approved:
1180
+				$icon = $show_icons
1181
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1182
+					: '';
1183
+				break;
1184
+			case EEM_Registration::status_id_cancelled:
1185
+				$icon = $show_icons
1186
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1187
+					: '';
1188
+				break;
1189
+			case EEM_Registration::status_id_incomplete:
1190
+				$icon = $show_icons
1191
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1192
+					: '';
1193
+				break;
1194
+			case EEM_Registration::status_id_declined:
1195
+				$icon = $show_icons
1196
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1197
+					: '';
1198
+				break;
1199
+			case EEM_Registration::status_id_wait_list:
1200
+				$icon = $show_icons
1201
+					? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1202
+					: '';
1203
+				break;
1204
+		}
1205
+		return $icon . $status[ $this->status_ID() ];
1206
+	}
1207
+
1208
+
1209
+	/**
1210
+	 *        get Attendee Is Going
1211
+	 */
1212
+	public function att_is_going()
1213
+	{
1214
+		return $this->get('REG_att_is_going');
1215
+	}
1216
+
1217
+
1218
+	/**
1219
+	 * Gets related answers
1220
+	 *
1221
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1222
+	 * @return EE_Answer[]
1223
+	 * @throws EE_Error
1224
+	 */
1225
+	public function answers($query_params = null)
1226
+	{
1227
+		return $this->get_many_related('Answer', $query_params);
1228
+	}
1229
+
1230
+
1231
+	/**
1232
+	 * Gets the registration's answer value to the specified question
1233
+	 * (either the question's ID or a question object)
1234
+	 *
1235
+	 * @param EE_Question|int $question
1236
+	 * @param bool            $pretty_value
1237
+	 * @return array|string if pretty_value= true, the result will always be a string
1238
+	 * (because the answer might be an array of answer values, so passing pretty_value=true
1239
+	 * will convert it into some kind of string)
1240
+	 * @throws EE_Error
1241
+	 */
1242
+	public function answer_value_to_question($question, $pretty_value = true)
1243
+	{
1244
+		$question_id = EEM_Question::instance()->ensure_is_ID($question);
1245
+		return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1246
+	}
1247
+
1248
+
1249
+	/**
1250
+	 * question_groups
1251
+	 * returns an array of EE_Question_Group objects for this registration
1252
+	 *
1253
+	 * @return EE_Question_Group[]
1254
+	 * @throws EE_Error
1255
+	 * @throws EntityNotFoundException
1256
+	 */
1257
+	public function question_groups()
1258
+	{
1259
+		$question_groups = array();
1260
+		if ($this->event() instanceof EE_Event) {
1261
+			$question_groups = $this->event()->question_groups(
1262
+				array(
1263
+					array(
1264
+						'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1265
+					),
1266
+					'order_by' => array('QSG_order' => 'ASC'),
1267
+				)
1268
+			);
1269
+		}
1270
+		return $question_groups;
1271
+	}
1272
+
1273
+
1274
+	/**
1275
+	 * count_question_groups
1276
+	 * returns a count of the number of EE_Question_Group objects for this registration
1277
+	 *
1278
+	 * @return int
1279
+	 * @throws EE_Error
1280
+	 * @throws EntityNotFoundException
1281
+	 */
1282
+	public function count_question_groups()
1283
+	{
1284
+		$qg_count = 0;
1285
+		if ($this->event() instanceof EE_Event) {
1286
+			$qg_count = $this->event()->count_related(
1287
+				'Question_Group',
1288
+				array(
1289
+					array(
1290
+						'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1291
+					),
1292
+				)
1293
+			);
1294
+		}
1295
+		return $qg_count;
1296
+	}
1297
+
1298
+
1299
+	/**
1300
+	 * Returns the registration date in the 'standard' string format
1301
+	 * (function may be improved in the future to allow for different formats and timezones)
1302
+	 *
1303
+	 * @return string
1304
+	 * @throws EE_Error
1305
+	 */
1306
+	public function reg_date()
1307
+	{
1308
+		return $this->get_datetime('REG_date');
1309
+	}
1310
+
1311
+
1312
+	/**
1313
+	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1314
+	 * the ticket this registration purchased, or the datetime they have registered
1315
+	 * to attend)
1316
+	 *
1317
+	 * @return EE_Datetime_Ticket
1318
+	 * @throws EE_Error
1319
+	 */
1320
+	public function datetime_ticket()
1321
+	{
1322
+		return $this->get_first_related('Datetime_Ticket');
1323
+	}
1324
+
1325
+
1326
+	/**
1327
+	 * Sets the registration's datetime_ticket.
1328
+	 *
1329
+	 * @param EE_Datetime_Ticket $datetime_ticket
1330
+	 * @return EE_Datetime_Ticket
1331
+	 * @throws EE_Error
1332
+	 */
1333
+	public function set_datetime_ticket($datetime_ticket)
1334
+	{
1335
+		return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1336
+	}
1337
+
1338
+	/**
1339
+	 * Gets deleted
1340
+	 *
1341
+	 * @return bool
1342
+	 * @throws EE_Error
1343
+	 */
1344
+	public function deleted()
1345
+	{
1346
+		return $this->get('REG_deleted');
1347
+	}
1348
+
1349
+	/**
1350
+	 * Sets deleted
1351
+	 *
1352
+	 * @param boolean $deleted
1353
+	 * @return bool
1354
+	 * @throws EE_Error
1355
+	 * @throws RuntimeException
1356
+	 */
1357
+	public function set_deleted($deleted)
1358
+	{
1359
+		if ($deleted) {
1360
+			$this->delete();
1361
+		} else {
1362
+			$this->restore();
1363
+		}
1364
+	}
1365
+
1366
+
1367
+	/**
1368
+	 * Get the status object of this object
1369
+	 *
1370
+	 * @return EE_Status
1371
+	 * @throws EE_Error
1372
+	 */
1373
+	public function status_obj()
1374
+	{
1375
+		return $this->get_first_related('Status');
1376
+	}
1377
+
1378
+
1379
+	/**
1380
+	 * Returns the number of times this registration has checked into any of the datetimes
1381
+	 * its available for
1382
+	 *
1383
+	 * @return int
1384
+	 * @throws EE_Error
1385
+	 */
1386
+	public function count_checkins()
1387
+	{
1388
+		return $this->get_model()->count_related($this, 'Checkin');
1389
+	}
1390
+
1391
+
1392
+	/**
1393
+	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1394
+	 * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1395
+	 *
1396
+	 * @return int
1397
+	 * @throws EE_Error
1398
+	 */
1399
+	public function count_checkins_not_checkedout()
1400
+	{
1401
+		return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1402
+	}
1403
+
1404
+
1405
+	/**
1406
+	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1407
+	 *
1408
+	 * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1409
+	 * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1410
+	 *                                          consider registration status as well as datetime access.
1411
+	 * @return bool
1412
+	 * @throws EE_Error
1413
+	 */
1414
+	public function can_checkin($DTT_OR_ID, $check_approved = true)
1415
+	{
1416
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1417
+
1418
+		// first check registration status
1419
+		if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1420
+			return false;
1421
+		}
1422
+		// is there a datetime ticket that matches this dtt_ID?
1423
+		if (! (EEM_Datetime_Ticket::instance()->exists(
1424
+			array(
1425
+				array(
1426
+					'TKT_ID' => $this->get('TKT_ID'),
1427
+					'DTT_ID' => $DTT_ID,
1428
+				),
1429
+			)
1430
+		))
1431
+		) {
1432
+			return false;
1433
+		}
1434
+
1435
+		// final check is against TKT_uses
1436
+		return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1437
+	}
1438
+
1439
+
1440
+	/**
1441
+	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1442
+	 * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1443
+	 * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1444
+	 * then return false.  Otherwise return true.
1445
+	 *
1446
+	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1447
+	 * @return bool true means can checkin.  false means cannot checkin.
1448
+	 * @throws EE_Error
1449
+	 */
1450
+	public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1451
+	{
1452
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1453
+
1454
+		if (! $DTT_ID) {
1455
+			return false;
1456
+		}
1457
+
1458
+		$max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1459
+
1460
+		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1461
+		// check-in or not.
1462
+		if (! $max_uses || $max_uses === EE_INF) {
1463
+			return true;
1464
+		}
1465
+
1466
+		// does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1467
+		// go ahead and toggle.
1468
+		if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1469
+			return true;
1470
+		}
1471
+
1472
+		// made it here so the last check is whether the number of checkins per unique datetime on this registration
1473
+		// disallows further check-ins.
1474
+		$count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1475
+			array(
1476
+				array(
1477
+					'REG_ID' => $this->ID(),
1478
+					'CHK_in' => true,
1479
+				),
1480
+			),
1481
+			'DTT_ID',
1482
+			true
1483
+		);
1484
+		// checkins have already reached their max number of uses
1485
+		// so registrant can NOT checkin
1486
+		if ($count_unique_dtt_checkins >= $max_uses) {
1487
+			EE_Error::add_error(
1488
+				esc_html__(
1489
+					'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1490
+					'event_espresso'
1491
+				),
1492
+				__FILE__,
1493
+				__FUNCTION__,
1494
+				__LINE__
1495
+			);
1496
+			return false;
1497
+		}
1498
+		return true;
1499
+	}
1500
+
1501
+
1502
+	/**
1503
+	 * toggle Check-in status for this registration
1504
+	 * Check-ins are toggled in the following order:
1505
+	 * never checked in -> checked in
1506
+	 * checked in -> checked out
1507
+	 * checked out -> checked in
1508
+	 *
1509
+	 * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1510
+	 *                      If not included or null, then it is assumed latest datetime is being toggled.
1511
+	 * @param bool $verify  If true then can_checkin() is used to verify whether the person
1512
+	 *                      can be checked in or not.  Otherwise this forces change in checkin status.
1513
+	 * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1514
+	 * @throws EE_Error
1515
+	 */
1516
+	public function toggle_checkin_status($DTT_ID = null, $verify = false)
1517
+	{
1518
+		if (empty($DTT_ID)) {
1519
+			$datetime = $this->get_latest_related_datetime();
1520
+			$DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1521
+			// verify the registration can checkin for the given DTT_ID
1522
+		} elseif (! $this->can_checkin($DTT_ID, $verify)) {
1523
+			EE_Error::add_error(
1524
+				sprintf(
1525
+					esc_html__(
1526
+						'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1527
+						'event_espresso'
1528
+					),
1529
+					$this->ID(),
1530
+					$DTT_ID
1531
+				),
1532
+				__FILE__,
1533
+				__FUNCTION__,
1534
+				__LINE__
1535
+			);
1536
+			return false;
1537
+		}
1538
+		$status_paths = array(
1539
+			EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1540
+			EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1541
+			EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1542
+		);
1543
+		// start by getting the current status so we know what status we'll be changing to.
1544
+		$cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1545
+		$status_to = $status_paths[ $cur_status ];
1546
+		// database only records true for checked IN or false for checked OUT
1547
+		// no record ( null ) means checked in NEVER, but we obviously don't save that
1548
+		$new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1549
+		// add relation - note Check-ins are always creating new rows
1550
+		// because we are keeping track of Check-ins over time.
1551
+		// Eventually we'll probably want to show a list table
1552
+		// for the individual Check-ins so that they can be managed.
1553
+		$checkin = EE_Checkin::new_instance(
1554
+			array(
1555
+				'REG_ID' => $this->ID(),
1556
+				'DTT_ID' => $DTT_ID,
1557
+				'CHK_in' => $new_status,
1558
+			)
1559
+		);
1560
+		// if the record could not be saved then return false
1561
+		if ($checkin->save() === 0) {
1562
+			if (WP_DEBUG) {
1563
+				global $wpdb;
1564
+				$error = sprintf(
1565
+					esc_html__(
1566
+						'Registration check in update failed because of the following database error: %1$s%2$s',
1567
+						'event_espresso'
1568
+					),
1569
+					'<br />',
1570
+					$wpdb->last_error
1571
+				);
1572
+			} else {
1573
+				$error = esc_html__(
1574
+					'Registration check in update failed because of an unknown database error',
1575
+					'event_espresso'
1576
+				);
1577
+			}
1578
+			EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1579
+			return false;
1580
+		}
1581
+		return $status_to;
1582
+	}
1583
+
1584
+
1585
+	/**
1586
+	 * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1587
+	 * "Latest" is defined by the `DTT_EVT_start` column.
1588
+	 *
1589
+	 * @return EE_Datetime|null
1590
+	 * @throws EE_Error
1591
+	 */
1592
+	public function get_latest_related_datetime()
1593
+	{
1594
+		return EEM_Datetime::instance()->get_one(
1595
+			array(
1596
+				array(
1597
+					'Ticket.Registration.REG_ID' => $this->ID(),
1598
+				),
1599
+				'order_by' => array('DTT_EVT_start' => 'DESC'),
1600
+			)
1601
+		);
1602
+	}
1603
+
1604
+
1605
+	/**
1606
+	 * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1607
+	 * "Earliest" is defined by the `DTT_EVT_start` column.
1608
+	 *
1609
+	 * @throws EE_Error
1610
+	 */
1611
+	public function get_earliest_related_datetime()
1612
+	{
1613
+		return EEM_Datetime::instance()->get_one(
1614
+			array(
1615
+				array(
1616
+					'Ticket.Registration.REG_ID' => $this->ID(),
1617
+				),
1618
+				'order_by' => array('DTT_EVT_start' => 'ASC'),
1619
+			)
1620
+		);
1621
+	}
1622
+
1623
+
1624
+	/**
1625
+	 * This method simply returns the check-in status for this registration and the given datetime.
1626
+	 * If neither the datetime nor the checkin values are provided as arguments,
1627
+	 * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1628
+	 *
1629
+	 * @param  int       $DTT_ID  The ID of the datetime we're checking against
1630
+	 *                            (if empty we'll get the primary datetime for
1631
+	 *                            this registration (via event) and use it's ID);
1632
+	 * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1633
+	 *
1634
+	 * @return int                Integer representing Check-in status.
1635
+	 * @throws EE_Error
1636
+	 */
1637
+	public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1638
+	{
1639
+		$checkin_query_params = array(
1640
+			'order_by' => array('CHK_timestamp' => 'DESC'),
1641
+		);
1642
+
1643
+		if ($DTT_ID > 0) {
1644
+			$checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1645
+		}
1646
+
1647
+		// get checkin object (if exists)
1648
+		$checkin = $checkin instanceof EE_Checkin
1649
+			? $checkin
1650
+			: $this->get_first_related('Checkin', $checkin_query_params);
1651
+		if ($checkin instanceof EE_Checkin) {
1652
+			if ($checkin->get('CHK_in')) {
1653
+				return EE_Checkin::status_checked_in; // checked in
1654
+			}
1655
+			return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1656
+		}
1657
+		return EE_Checkin::status_checked_never; // never been checked in
1658
+	}
1659
+
1660
+
1661
+	/**
1662
+	 * This method returns a localized message for the toggled Check-in message.
1663
+	 *
1664
+	 * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1665
+	 *                     then it is assumed Check-in for primary datetime was toggled.
1666
+	 * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1667
+	 *                     message can be customized with the attendee name.
1668
+	 * @return string internationalized message
1669
+	 * @throws EE_Error
1670
+	 */
1671
+	public function get_checkin_msg($DTT_ID, $error = false)
1672
+	{
1673
+		// let's get the attendee first so we can include the name of the attendee
1674
+		$attendee = $this->get_first_related('Attendee');
1675
+		if ($attendee instanceof EE_Attendee) {
1676
+			if ($error) {
1677
+				return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1678
+			}
1679
+			$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1680
+			// what is the status message going to be?
1681
+			switch ($cur_status) {
1682
+				case EE_Checkin::status_checked_never:
1683
+					return sprintf(
1684
+						__("%s has been removed from Check-in records", "event_espresso"),
1685
+						$attendee->full_name()
1686
+					);
1687
+					break;
1688
+				case EE_Checkin::status_checked_in:
1689
+					return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1690
+					break;
1691
+				case EE_Checkin::status_checked_out:
1692
+					return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1693
+					break;
1694
+			}
1695
+		}
1696
+		return esc_html__("The check-in status could not be determined.", "event_espresso");
1697
+	}
1698
+
1699
+
1700
+	/**
1701
+	 * Returns the related EE_Transaction to this registration
1702
+	 *
1703
+	 * @return EE_Transaction
1704
+	 * @throws EE_Error
1705
+	 * @throws EntityNotFoundException
1706
+	 */
1707
+	public function transaction()
1708
+	{
1709
+		$transaction = $this->get_first_related('Transaction');
1710
+		if (! $transaction instanceof \EE_Transaction) {
1711
+			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1712
+		}
1713
+		return $transaction;
1714
+	}
1715
+
1716
+
1717
+	/**
1718
+	 *        get Registration Code
1719
+	 */
1720
+	public function reg_code()
1721
+	{
1722
+		return $this->get('REG_code');
1723
+	}
1724
+
1725
+
1726
+	/**
1727
+	 *        get Transaction ID
1728
+	 */
1729
+	public function transaction_ID()
1730
+	{
1731
+		return $this->get('TXN_ID');
1732
+	}
1733
+
1734
+
1735
+	/**
1736
+	 * @return int
1737
+	 * @throws EE_Error
1738
+	 */
1739
+	public function ticket_ID()
1740
+	{
1741
+		return $this->get('TKT_ID');
1742
+	}
1743
+
1744
+
1745
+	/**
1746
+	 *        Set Registration Code
1747
+	 *
1748
+	 * @access    public
1749
+	 * @param    string  $REG_code Registration Code
1750
+	 * @param    boolean $use_default
1751
+	 * @throws EE_Error
1752
+	 */
1753
+	public function set_reg_code($REG_code, $use_default = false)
1754
+	{
1755
+		if (empty($REG_code)) {
1756
+			EE_Error::add_error(
1757
+				esc_html__('REG_code can not be empty.', 'event_espresso'),
1758
+				__FILE__,
1759
+				__FUNCTION__,
1760
+				__LINE__
1761
+			);
1762
+			return;
1763
+		}
1764
+		if (! $this->reg_code()) {
1765
+			parent::set('REG_code', $REG_code, $use_default);
1766
+		} else {
1767
+			EE_Error::doing_it_wrong(
1768
+				__CLASS__ . '::' . __FUNCTION__,
1769
+				esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1770
+				'4.6.0'
1771
+			);
1772
+		}
1773
+	}
1774
+
1775
+
1776
+	/**
1777
+	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
1778
+	 * Note, if you want to just get all registrations in the same transaction (group), use:
1779
+	 *    $registration->transaction()->registrations();
1780
+	 *
1781
+	 * @since 4.5.0
1782
+	 * @return EE_Registration[] or empty array if this isn't a group registration.
1783
+	 * @throws EE_Error
1784
+	 */
1785
+	public function get_all_other_registrations_in_group()
1786
+	{
1787
+		if ($this->group_size() < 2) {
1788
+			return array();
1789
+		}
1790
+
1791
+		$query[0] = array(
1792
+			'TXN_ID' => $this->transaction_ID(),
1793
+			'REG_ID' => array('!=', $this->ID()),
1794
+			'TKT_ID' => $this->ticket_ID(),
1795
+		);
1796
+		/** @var EE_Registration[] $registrations */
1797
+		$registrations = $this->get_model()->get_all($query);
1798
+		return $registrations;
1799
+	}
1800
+
1801
+	/**
1802
+	 * Return the link to the admin details for the object.
1803
+	 *
1804
+	 * @return string
1805
+	 * @throws EE_Error
1806
+	 */
1807
+	public function get_admin_details_link()
1808
+	{
1809
+		EE_Registry::instance()->load_helper('URL');
1810
+		return EEH_URL::add_query_args_and_nonce(
1811
+			array(
1812
+				'page'    => 'espresso_registrations',
1813
+				'action'  => 'view_registration',
1814
+				'_REG_ID' => $this->ID(),
1815
+			),
1816
+			admin_url('admin.php')
1817
+		);
1818
+	}
1819
+
1820
+	/**
1821
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1822
+	 *
1823
+	 * @return string
1824
+	 * @throws EE_Error
1825
+	 */
1826
+	public function get_admin_edit_link()
1827
+	{
1828
+		return $this->get_admin_details_link();
1829
+	}
1830
+
1831
+	/**
1832
+	 * Returns the link to a settings page for the object.
1833
+	 *
1834
+	 * @return string
1835
+	 * @throws EE_Error
1836
+	 */
1837
+	public function get_admin_settings_link()
1838
+	{
1839
+		return $this->get_admin_details_link();
1840
+	}
1841
+
1842
+	/**
1843
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
1844
+	 *
1845
+	 * @return string
1846
+	 */
1847
+	public function get_admin_overview_link()
1848
+	{
1849
+		EE_Registry::instance()->load_helper('URL');
1850
+		return EEH_URL::add_query_args_and_nonce(
1851
+			array(
1852
+				'page' => 'espresso_registrations',
1853
+			),
1854
+			admin_url('admin.php')
1855
+		);
1856
+	}
1857
+
1858
+
1859
+	/**
1860
+	 * @param array $query_params
1861
+	 *
1862
+	 * @return \EE_Registration[]
1863
+	 * @throws EE_Error
1864
+	 */
1865
+	public function payments($query_params = array())
1866
+	{
1867
+		return $this->get_many_related('Payment', $query_params);
1868
+	}
1869
+
1870
+
1871
+	/**
1872
+	 * @param array $query_params
1873
+	 *
1874
+	 * @return \EE_Registration_Payment[]
1875
+	 * @throws EE_Error
1876
+	 */
1877
+	public function registration_payments($query_params = array())
1878
+	{
1879
+		return $this->get_many_related('Registration_Payment', $query_params);
1880
+	}
1881
+
1882
+
1883
+	/**
1884
+	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1885
+	 * Note: if there are no payments on the registration there will be no payment method returned.
1886
+	 *
1887
+	 * @return EE_Payment_Method|null
1888
+	 */
1889
+	public function payment_method()
1890
+	{
1891
+		return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1892
+	}
1893
+
1894
+
1895
+	/**
1896
+	 * @return \EE_Line_Item
1897
+	 * @throws EntityNotFoundException
1898
+	 * @throws EE_Error
1899
+	 */
1900
+	public function ticket_line_item()
1901
+	{
1902
+		$ticket = $this->ticket();
1903
+		$transaction = $this->transaction();
1904
+		$line_item = null;
1905
+		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1906
+			$transaction->total_line_item(),
1907
+			'Ticket',
1908
+			array($ticket->ID())
1909
+		);
1910
+		foreach ($ticket_line_items as $ticket_line_item) {
1911
+			if ($ticket_line_item instanceof \EE_Line_Item
1912
+				&& $ticket_line_item->OBJ_type() === 'Ticket'
1913
+				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
1914
+			) {
1915
+				$line_item = $ticket_line_item;
1916
+				break;
1917
+			}
1918
+		}
1919
+		if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1920
+			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1921
+		}
1922
+		return $line_item;
1923
+	}
1924
+
1925
+
1926
+	/**
1927
+	 * Soft Deletes this model object.
1928
+	 *
1929
+	 * @return boolean | int
1930
+	 * @throws RuntimeException
1931
+	 * @throws EE_Error
1932
+	 */
1933
+	public function delete()
1934
+	{
1935
+		if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1936
+			$this->set_status(EEM_Registration::status_id_cancelled);
1937
+		}
1938
+		return parent::delete();
1939
+	}
1940
+
1941
+
1942
+	/**
1943
+	 * Restores whatever the previous status was on a registration before it was trashed (if possible)
1944
+	 *
1945
+	 * @throws EE_Error
1946
+	 * @throws RuntimeException
1947
+	 */
1948
+	public function restore()
1949
+	{
1950
+		$previous_status = $this->get_extra_meta(
1951
+			EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1952
+			true,
1953
+			EEM_Registration::status_id_cancelled
1954
+		);
1955
+		if ($previous_status) {
1956
+			$this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1957
+			$this->set_status($previous_status);
1958
+		}
1959
+		return parent::restore();
1960
+	}
1961
+
1962
+
1963
+	/**
1964
+	 * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1965
+	 *
1966
+	 * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1967
+	 *                                           depending on whether the reg status changes to or from "Approved"
1968
+	 * @return boolean whether the Registration status was updated
1969
+	 * @throws EE_Error
1970
+	 * @throws RuntimeException
1971
+	 */
1972
+	public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1973
+	{
1974
+		$paid = $this->paid();
1975
+		$price = $this->final_price();
1976
+		switch (true) {
1977
+			// overpaid or paid
1978
+			case EEH_Money::compare_floats($paid, $price, '>'):
1979
+			case EEH_Money::compare_floats($paid, $price):
1980
+				$new_status = EEM_Registration::status_id_approved;
1981
+				break;
1982
+			//  underpaid
1983
+			case EEH_Money::compare_floats($paid, $price, '<'):
1984
+				$new_status = EEM_Registration::status_id_pending_payment;
1985
+				break;
1986
+			// uhhh Houston...
1987
+			default:
1988
+				throw new RuntimeException(
1989
+					esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1990
+				);
1991
+		}
1992
+		if ($new_status !== $this->status_ID()) {
1993
+			if ($trigger_set_status_logic) {
1994
+				return $this->set_status($new_status);
1995
+			}
1996
+			parent::set('STS_ID', $new_status);
1997
+			return true;
1998
+		}
1999
+		return false;
2000
+	}
2001
+
2002
+
2003
+	/*************************** DEPRECATED ***************************/
2004
+
2005
+
2006
+	/**
2007
+	 * @deprecated
2008
+	 * @since     4.7.0
2009
+	 * @access    public
2010
+	 */
2011
+	public function price_paid()
2012
+	{
2013
+		EE_Error::doing_it_wrong(
2014
+			'EE_Registration::price_paid()',
2015
+			esc_html__(
2016
+				'This method is deprecated, please use EE_Registration::final_price() instead.',
2017
+				'event_espresso'
2018
+			),
2019
+			'4.7.0'
2020
+		);
2021
+		return $this->final_price();
2022
+	}
2023
+
2024
+
2025
+	/**
2026
+	 * @deprecated
2027
+	 * @since     4.7.0
2028
+	 * @access    public
2029
+	 * @param    float $REG_final_price
2030
+	 * @throws EE_Error
2031
+	 * @throws RuntimeException
2032
+	 */
2033
+	public function set_price_paid($REG_final_price = 0.00)
2034
+	{
2035
+		EE_Error::doing_it_wrong(
2036
+			'EE_Registration::set_price_paid()',
2037
+			esc_html__(
2038
+				'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2039
+				'event_espresso'
2040
+			),
2041
+			'4.7.0'
2042
+		);
2043
+		$this->set_final_price($REG_final_price);
2044
+	}
2045
+
2046
+
2047
+	/**
2048
+	 * @deprecated
2049
+	 * @since 4.7.0
2050
+	 * @return string
2051
+	 * @throws EE_Error
2052
+	 */
2053
+	public function pretty_price_paid()
2054
+	{
2055
+		EE_Error::doing_it_wrong(
2056
+			'EE_Registration::pretty_price_paid()',
2057
+			esc_html__(
2058
+				'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2059
+				'event_espresso'
2060
+			),
2061
+			'4.7.0'
2062
+		);
2063
+		return $this->pretty_final_price();
2064
+	}
2065
+
2066
+
2067
+	/**
2068
+	 * Gets the primary datetime related to this registration via the related Event to this registration
2069
+	 *
2070
+	 * @deprecated 4.9.17
2071
+	 * @return EE_Datetime
2072
+	 * @throws EE_Error
2073
+	 * @throws EntityNotFoundException
2074
+	 */
2075
+	public function get_related_primary_datetime()
2076
+	{
2077
+		EE_Error::doing_it_wrong(
2078
+			__METHOD__,
2079
+			esc_html__(
2080
+				'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2081
+				'event_espresso'
2082
+			),
2083
+			'4.9.17',
2084
+			'5.0.0'
2085
+		);
2086
+		return $this->event()->primary_datetime();
2087
+	}
2088 2088
 }
Please login to merge, or discard this patch.
core/domain/entities/editor/blocks/EventAttendees.php 1 patch
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -21,172 +21,172 @@
 block discarded – undo
21 21
 class EventAttendees extends Block
22 22
 {
23 23
 
24
-    const BLOCK_TYPE = 'event-attendees';
24
+	const BLOCK_TYPE = 'event-attendees';
25 25
 
26
-    /**
27
-     * @var EventAttendeesBlockRenderer $renderer
28
-     */
29
-    protected $renderer;
26
+	/**
27
+	 * @var EventAttendeesBlockRenderer $renderer
28
+	 */
29
+	protected $renderer;
30 30
 
31 31
 
32
-    /**
33
-     * EventAttendees constructor.
34
-     *
35
-     * @param CoreBlocksAssetManager      $block_asset_manager
36
-     * @param RequestInterface            $request
37
-     * @param EventAttendeesBlockRenderer $renderer
38
-     */
39
-    public function __construct(
40
-        CoreBlocksAssetManager $block_asset_manager,
41
-        RequestInterface $request,
42
-        EventAttendeesBlockRenderer $renderer
43
-    ) {
44
-        parent::__construct($block_asset_manager, $request);
45
-        $this->renderer= $renderer;
46
-    }
32
+	/**
33
+	 * EventAttendees constructor.
34
+	 *
35
+	 * @param CoreBlocksAssetManager      $block_asset_manager
36
+	 * @param RequestInterface            $request
37
+	 * @param EventAttendeesBlockRenderer $renderer
38
+	 */
39
+	public function __construct(
40
+		CoreBlocksAssetManager $block_asset_manager,
41
+		RequestInterface $request,
42
+		EventAttendeesBlockRenderer $renderer
43
+	) {
44
+		parent::__construct($block_asset_manager, $request);
45
+		$this->renderer= $renderer;
46
+	}
47 47
 
48 48
 
49
-    /**
50
-     * Perform any early setup required by the block
51
-     * including setting the block type and supported post types
52
-     *
53
-     * @return void
54
-     */
55
-    public function initialize()
56
-    {
57
-        $this->setBlockType(self::BLOCK_TYPE);
58
-        $this->setSupportedRoutes(
59
-            array(
60
-                'EventEspresso\core\domain\entities\route_match\specifications\admin\EspressoStandardPostTypeEditor',
61
-                'EventEspresso\core\domain\entities\route_match\specifications\admin\WordPressPostTypeEditor',
62
-                'EventEspresso\core\domain\entities\route_match\specifications\frontend\EspressoBlockRenderer',
63
-                'EventEspresso\core\domain\entities\route_match\specifications\frontend\AnyFrontendRequest'
64
-            )
65
-        );
66
-        $EVT_ID = $this->request->getRequestParam('page') === 'espresso_events'
67
-            ? $this->request->getRequestParam('post', 0)
68
-            : 0;
69
-        $this->setAttributes(
70
-            array(
71
-                'eventId'           => array(
72
-                    'type'    => 'number',
73
-                    'default' => $EVT_ID,
74
-                ),
75
-                'datetimeId'        => array(
76
-                    'type'    => 'number',
77
-                    'default' => 0,
78
-                ),
79
-                'ticketId'          => array(
80
-                    'type'    => 'number',
81
-                    'default' => 0,
82
-                ),
83
-                'status'            => array(
84
-                    'type'    => 'string',
85
-                    'default' => EEM_Registration::status_id_approved,
86
-                ),
87
-                'limit'             => array(
88
-                    'type'    => 'number',
89
-                    'default' => 100,
90
-                ),
91
-                'order' => array(
92
-                    'type' => 'string',
93
-                    'default' => 'ASC'
94
-                ),
95
-                'orderBy' => array(
96
-                    'type' => 'string',
97
-                    'default' => 'lastThenFirstName',
98
-                ),
99
-                'showGravatar'      => array(
100
-                    'type'    => 'boolean',
101
-                    'default' => false,
102
-                ),
103
-                'avatarClass' => array(
104
-                    'type' => 'string',
105
-                    'default' => 'contact',
106
-                ),
107
-                'avatarSize' => array(
108
-                    'type' => 'number',
109
-                    'default' => 24,
110
-                ),
111
-                'displayOnArchives' => array(
112
-                    'type'    => 'boolean',
113
-                    'default' => false,
114
-                ),
115
-            )
116
-        );
117
-        $this->setDynamic();
118
-    }
49
+	/**
50
+	 * Perform any early setup required by the block
51
+	 * including setting the block type and supported post types
52
+	 *
53
+	 * @return void
54
+	 */
55
+	public function initialize()
56
+	{
57
+		$this->setBlockType(self::BLOCK_TYPE);
58
+		$this->setSupportedRoutes(
59
+			array(
60
+				'EventEspresso\core\domain\entities\route_match\specifications\admin\EspressoStandardPostTypeEditor',
61
+				'EventEspresso\core\domain\entities\route_match\specifications\admin\WordPressPostTypeEditor',
62
+				'EventEspresso\core\domain\entities\route_match\specifications\frontend\EspressoBlockRenderer',
63
+				'EventEspresso\core\domain\entities\route_match\specifications\frontend\AnyFrontendRequest'
64
+			)
65
+		);
66
+		$EVT_ID = $this->request->getRequestParam('page') === 'espresso_events'
67
+			? $this->request->getRequestParam('post', 0)
68
+			: 0;
69
+		$this->setAttributes(
70
+			array(
71
+				'eventId'           => array(
72
+					'type'    => 'number',
73
+					'default' => $EVT_ID,
74
+				),
75
+				'datetimeId'        => array(
76
+					'type'    => 'number',
77
+					'default' => 0,
78
+				),
79
+				'ticketId'          => array(
80
+					'type'    => 'number',
81
+					'default' => 0,
82
+				),
83
+				'status'            => array(
84
+					'type'    => 'string',
85
+					'default' => EEM_Registration::status_id_approved,
86
+				),
87
+				'limit'             => array(
88
+					'type'    => 'number',
89
+					'default' => 100,
90
+				),
91
+				'order' => array(
92
+					'type' => 'string',
93
+					'default' => 'ASC'
94
+				),
95
+				'orderBy' => array(
96
+					'type' => 'string',
97
+					'default' => 'lastThenFirstName',
98
+				),
99
+				'showGravatar'      => array(
100
+					'type'    => 'boolean',
101
+					'default' => false,
102
+				),
103
+				'avatarClass' => array(
104
+					'type' => 'string',
105
+					'default' => 'contact',
106
+				),
107
+				'avatarSize' => array(
108
+					'type' => 'number',
109
+					'default' => 24,
110
+				),
111
+				'displayOnArchives' => array(
112
+					'type'    => 'boolean',
113
+					'default' => false,
114
+				),
115
+			)
116
+		);
117
+		$this->setDynamic();
118
+	}
119 119
 
120 120
 
121
-    /**
122
-     * Returns an array where the key corresponds to the incoming attribute name from the WP block
123
-     * and the value corresponds to the attribute name for the existing EspressoEventAttendees shortcode
124
-     *
125
-     * @since 4.9.71.p
126
-     * @return array
127
-     */
128
-    private function getAttributesMap()
129
-    {
130
-        return array(
131
-            'eventId'           => 'absint',
132
-            'datetimeId'        => 'absint',
133
-            'ticketId'          => 'absint',
134
-            'status'            => 'sanitize_text_field',
135
-            'limit'             => 'intval',
136
-            'showGravatar'      => 'bool',
137
-            'avatarClass'       => 'sanitize_text_field',
138
-            'avatarSize'        => 'absint',
139
-            'displayOnArchives' => 'bool',
140
-            'order' => 'sanitize_text_field',
141
-            'orderBy' => 'sanitize_text_field',
142
-        );
143
-    }
121
+	/**
122
+	 * Returns an array where the key corresponds to the incoming attribute name from the WP block
123
+	 * and the value corresponds to the attribute name for the existing EspressoEventAttendees shortcode
124
+	 *
125
+	 * @since 4.9.71.p
126
+	 * @return array
127
+	 */
128
+	private function getAttributesMap()
129
+	{
130
+		return array(
131
+			'eventId'           => 'absint',
132
+			'datetimeId'        => 'absint',
133
+			'ticketId'          => 'absint',
134
+			'status'            => 'sanitize_text_field',
135
+			'limit'             => 'intval',
136
+			'showGravatar'      => 'bool',
137
+			'avatarClass'       => 'sanitize_text_field',
138
+			'avatarSize'        => 'absint',
139
+			'displayOnArchives' => 'bool',
140
+			'order' => 'sanitize_text_field',
141
+			'orderBy' => 'sanitize_text_field',
142
+		);
143
+	}
144 144
 
145 145
 
146
-    /**
147
-     * Sanitizes attributes.
148
-     *
149
-     * @param array $attributes
150
-     * @return array
151
-     */
152
-    private function sanitizeAttributes(array $attributes)
153
-    {
154
-        $sanitized_attributes = array();
155
-        foreach ($attributes as $attribute => $value) {
156
-            $convert = $this->getAttributesMap();
157
-            if (isset($convert[ $attribute ])) {
158
-                $sanitize = $convert[ $attribute ];
159
-                if ($sanitize === 'bool') {
160
-                    $sanitized_attributes[ $attribute ] = filter_var(
161
-                        $value,
162
-                        FILTER_VALIDATE_BOOLEAN
163
-                    );
164
-                } else {
165
-                    $sanitized_attributes[ $attribute ] = $sanitize($value);
166
-                }
167
-                // don't pass along attributes with a 0 value
168
-                if ($sanitized_attributes[ $attribute ] === 0) {
169
-                    unset($sanitized_attributes[ $attribute ]);
170
-                }
171
-            }
172
-        }
173
-        return $attributes;
174
-    }
146
+	/**
147
+	 * Sanitizes attributes.
148
+	 *
149
+	 * @param array $attributes
150
+	 * @return array
151
+	 */
152
+	private function sanitizeAttributes(array $attributes)
153
+	{
154
+		$sanitized_attributes = array();
155
+		foreach ($attributes as $attribute => $value) {
156
+			$convert = $this->getAttributesMap();
157
+			if (isset($convert[ $attribute ])) {
158
+				$sanitize = $convert[ $attribute ];
159
+				if ($sanitize === 'bool') {
160
+					$sanitized_attributes[ $attribute ] = filter_var(
161
+						$value,
162
+						FILTER_VALIDATE_BOOLEAN
163
+					);
164
+				} else {
165
+					$sanitized_attributes[ $attribute ] = $sanitize($value);
166
+				}
167
+				// don't pass along attributes with a 0 value
168
+				if ($sanitized_attributes[ $attribute ] === 0) {
169
+					unset($sanitized_attributes[ $attribute ]);
170
+				}
171
+			}
172
+		}
173
+		return $attributes;
174
+	}
175 175
 
176 176
 
177
-    /**
178
-     * Returns the rendered HTML for the block
179
-     *
180
-     * @param array $attributes
181
-     * @return string
182
-     * @throws DomainException
183
-     * @throws EE_Error
184
-     */
185
-    public function renderBlock(array $attributes = array())
186
-    {
187
-        $attributes = $this->sanitizeAttributes($attributes);
188
-        return (is_archive() || is_front_page() || is_home()) && ! $attributes['displayOnArchives']
189
-            ? ''
190
-            : $this->renderer->render($attributes);
191
-    }
177
+	/**
178
+	 * Returns the rendered HTML for the block
179
+	 *
180
+	 * @param array $attributes
181
+	 * @return string
182
+	 * @throws DomainException
183
+	 * @throws EE_Error
184
+	 */
185
+	public function renderBlock(array $attributes = array())
186
+	{
187
+		$attributes = $this->sanitizeAttributes($attributes);
188
+		return (is_archive() || is_front_page() || is_home()) && ! $attributes['displayOnArchives']
189
+			? ''
190
+			: $this->renderer->render($attributes);
191
+	}
192 192
 }
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -38,103 +38,103 @@
 block discarded – undo
38 38
  * @since           4.0
39 39
  */
40 40
 if (function_exists('espresso_version')) {
41
-    if (! function_exists('espresso_duplicate_plugin_error')) {
42
-        /**
43
-         *    espresso_duplicate_plugin_error
44
-         *    displays if more than one version of EE is activated at the same time
45
-         */
46
-        function espresso_duplicate_plugin_error()
47
-        {
48
-            ?>
41
+	if (! function_exists('espresso_duplicate_plugin_error')) {
42
+		/**
43
+		 *    espresso_duplicate_plugin_error
44
+		 *    displays if more than one version of EE is activated at the same time
45
+		 */
46
+		function espresso_duplicate_plugin_error()
47
+		{
48
+			?>
49 49
             <div class="error">
50 50
                 <p>
51 51
                     <?php
52
-                    echo esc_html__(
53
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
-                        'event_espresso'
55
-                    ); ?>
52
+					echo esc_html__(
53
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
+						'event_espresso'
55
+					); ?>
56 56
                 </p>
57 57
             </div>
58 58
             <?php
59
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
60
-        }
61
-    }
62
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
59
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
60
+		}
61
+	}
62
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
63 63
 } else {
64
-    define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
-        /**
67
-         * espresso_minimum_php_version_error
68
-         *
69
-         * @return void
70
-         */
71
-        function espresso_minimum_php_version_error()
72
-        {
73
-            ?>
64
+	define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
+		/**
67
+		 * espresso_minimum_php_version_error
68
+		 *
69
+		 * @return void
70
+		 */
71
+		function espresso_minimum_php_version_error()
72
+		{
73
+			?>
74 74
             <div class="error">
75 75
                 <p>
76 76
                     <?php
77
-                    printf(
78
-                        esc_html__(
79
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
-                            'event_espresso'
81
-                        ),
82
-                        EE_MIN_PHP_VER_REQUIRED,
83
-                        PHP_VERSION,
84
-                        '<br/>',
85
-                        '<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
-                    );
87
-                    ?>
77
+					printf(
78
+						esc_html__(
79
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
+							'event_espresso'
81
+						),
82
+						EE_MIN_PHP_VER_REQUIRED,
83
+						PHP_VERSION,
84
+						'<br/>',
85
+						'<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
+					);
87
+					?>
88 88
                 </p>
89 89
             </div>
90 90
             <?php
91
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
92
-        }
91
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
92
+		}
93 93
 
94
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
-    } else {
96
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
-        /**
98
-         * espresso_version
99
-         * Returns the plugin version
100
-         *
101
-         * @return string
102
-         */
103
-        function espresso_version()
104
-        {
105
-            return apply_filters('FHEE__espresso__espresso_version', '4.9.78.rc.013');
106
-        }
94
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
+	} else {
96
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
+		/**
98
+		 * espresso_version
99
+		 * Returns the plugin version
100
+		 *
101
+		 * @return string
102
+		 */
103
+		function espresso_version()
104
+		{
105
+			return apply_filters('FHEE__espresso__espresso_version', '4.9.78.rc.013');
106
+		}
107 107
 
108
-        /**
109
-         * espresso_plugin_activation
110
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
-         */
112
-        function espresso_plugin_activation()
113
-        {
114
-            update_option('ee_espresso_activation', true);
115
-        }
108
+		/**
109
+		 * espresso_plugin_activation
110
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
+		 */
112
+		function espresso_plugin_activation()
113
+		{
114
+			update_option('ee_espresso_activation', true);
115
+		}
116 116
 
117
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
117
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
118 118
 
119
-        require_once __DIR__ . '/core/bootstrap_espresso.php';
120
-        bootstrap_espresso();
121
-    }
119
+		require_once __DIR__ . '/core/bootstrap_espresso.php';
120
+		bootstrap_espresso();
121
+	}
122 122
 }
123 123
 if (! function_exists('espresso_deactivate_plugin')) {
124
-    /**
125
-     *    deactivate_plugin
126
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
-     *
128
-     * @access public
129
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
-     * @return    void
131
-     */
132
-    function espresso_deactivate_plugin($plugin_basename = '')
133
-    {
134
-        if (! function_exists('deactivate_plugins')) {
135
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
-        }
137
-        unset($_GET['activate'], $_REQUEST['activate']);
138
-        deactivate_plugins($plugin_basename);
139
-    }
124
+	/**
125
+	 *    deactivate_plugin
126
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
+	 *
128
+	 * @access public
129
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
+	 * @return    void
131
+	 */
132
+	function espresso_deactivate_plugin($plugin_basename = '')
133
+	{
134
+		if (! function_exists('deactivate_plugins')) {
135
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
+		}
137
+		unset($_GET['activate'], $_REQUEST['activate']);
138
+		deactivate_plugins($plugin_basename);
139
+	}
140 140
 }
Please login to merge, or discard this patch.