Completed
Branch BUG-10738-inconsistency-in-ses... (a1eed8)
by
unknown
24:27 queued 12:29
created

EE_Registration::updateStatusBasedOnTotalPaid()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 29
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 20
nc 10
nop 1
dl 0
loc 29
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
use EventEspresso\core\domain\entities\Context;
4
use EventEspresso\core\exceptions\EntityNotFoundException;
5
use EventEspresso\core\exceptions\InvalidDataTypeException;
6
use EventEspresso\core\exceptions\InvalidInterfaceException;
7
8
defined('EVENT_ESPRESSO_VERSION') || exit('No direct access allowed');
9
10
/**
11
 * EE_Registration class
12
 *
13
 * @package               Event Espresso
14
 * @subpackage            includes/classes/EE_Registration.class.php
15
 * @author                Mike Nelson, Brent Christensen
16
 */
17
class EE_Registration extends EE_Soft_Delete_Base_Class implements EEI_Registration, EEI_Admin_Links
18
{
19
20
21
    /**
22
     * Used to reference when a registration has never been checked in.
23
     *
24
     * @deprecated use \EE_Checkin::status_checked_never instead
25
     * @type int
26
     */
27
    const checkin_status_never = 2;
28
29
    /**
30
     * Used to reference when a registration has been checked in.
31
     *
32
     * @deprecated use \EE_Checkin::status_checked_in instead
33
     * @type int
34
     */
35
    const checkin_status_in = 1;
36
37
38
    /**
39
     * Used to reference when a registration has been checked out.
40
     *
41
     * @deprecated use \EE_Checkin::status_checked_out instead
42
     * @type int
43
     */
44
    const checkin_status_out = 0;
45
46
47
    /**
48
     * extra meta key for tracking reg status os trashed registrations
49
     *
50
     * @type string
51
     */
52
    const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
53
54
55
    /**
56
     * extra meta key for tracking if registration has reserved ticket
57
     *
58
     * @type string
59
     */
60
    const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
61
62
63
    /**
64
     * @param array  $props_n_values          incoming values
65
     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
66
     *                                        used.)
67
     * @param array  $date_formats            incoming date_formats in an array where the first value is the
68
     *                                        date_format and the second value is the time format
69
     * @return EE_Registration
70
     * @throws EE_Error
71
     */
72
    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
73
    {
74
        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
75
        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
76
    }
77
78
79
    /**
80
     * @param array  $props_n_values  incoming values from the database
81
     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
     *                                the website will be used.
83
     * @return EE_Registration
84
     */
85
    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
86
    {
87
        return new self($props_n_values, true, $timezone);
88
    }
89
90
91
    /**
92
     *        Set Event ID
93
     *
94
     * @param        int $EVT_ID Event ID
95
     * @throws EE_Error
96
     * @throws RuntimeException
97
     */
98
    public function set_event($EVT_ID = 0)
99
    {
100
        $this->set('EVT_ID', $EVT_ID);
101
    }
102
103
104
    /**
105
     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
106
     * be routed to internal methods
107
     *
108
     * @param string $field_name
109
     * @param mixed  $field_value
110
     * @param bool   $use_default
111
     * @throws EE_Error
112
     * @throws EntityNotFoundException
113
     * @throws InvalidArgumentException
114
     * @throws InvalidDataTypeException
115
     * @throws InvalidInterfaceException
116
     * @throws ReflectionException
117
     * @throws RuntimeException
118
     */
119
    public function set($field_name, $field_value, $use_default = false)
120
    {
121
        switch ($field_name) {
122
            case 'REG_code':
123
                if (! empty($field_value) && $this->reg_code() === null) {
124
                    $this->set_reg_code($field_value, $use_default);
125
                }
126
                break;
127
            case 'STS_ID':
128
                $this->set_status($field_value, $use_default);
129
                break;
130
            default:
131
                parent::set($field_name, $field_value, $use_default);
132
        }
133
    }
134
135
136
    /**
137
     * Set Status ID
138
     * updates the registration status and ALSO...
139
     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
140
     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
141
     *
142
     * @param string       $new_STS_ID
143
     * @param boolean      $use_default
144
     * @param Context|null $context
145
     * @return bool
146
     * @throws EE_Error
147
     * @throws EntityNotFoundException
148
     * @throws InvalidArgumentException
149
     * @throws ReflectionException
150
     * @throws RuntimeException
151
     * @throws InvalidDataTypeException
152
     * @throws InvalidInterfaceException
153
     */
154
    public function set_status($new_STS_ID = null, $use_default = false, Context $context = null)
155
    {
156
        // get current REG_Status
157
        $old_STS_ID = $this->status_ID();
158
        // if status has changed
159
        if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
160
            && ! empty($old_STS_ID) // and that old status is actually set
161
            && ! empty($new_STS_ID) // as well as the new status
162
            && $this->ID() // ensure registration is in the db
163
        ) {
164
            // TO approved
165
            if ($new_STS_ID === EEM_Registration::status_id_approved) {
166
                // reserve a space by incrementing ticket and datetime sold values
167
                $this->_reserve_registration_space();
168
                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
169
                // OR FROM  approved
170
            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
171
                // release a space by decrementing ticket and datetime sold values
172
                $this->_release_registration_space();
173
                do_action(
174
                    'AHEE__EE_Registration__set_status__from_approved',
175
                    $this,
176
                    $old_STS_ID,
177
                    $new_STS_ID,
178
                    $context
179
                );
180
            }
181
            // update status
182
            parent::set('STS_ID', $new_STS_ID, $use_default);
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 Context|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, Context $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 Context|null $context
236
     * @throws EE_Error
237
     * @throws InvalidArgumentException
238
     * @throws InvalidDataTypeException
239
     * @throws InvalidInterfaceException
240
     * @throws ReflectionException
241
     */
242 View Code Duplication
    private function updateIfCanceled(array $closed_reg_statuses, $new_STS_ID, $old_STS_ID, Context $context = null)
243
    {
244
        // true if registration has been cancelled or declined
245
        if (in_array($new_STS_ID, $closed_reg_statuses, true)
246
            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
247
        ) {
248
            /** @type EE_Registration_Processor $registration_processor */
249
            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
250
            /** @type EE_Transaction_Processor $transaction_processor */
251
            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
252
            // cancelled or declined registration
253
            $registration_processor->update_registration_after_being_canceled_or_declined(
254
                $this,
255
                $closed_reg_statuses
256
            );
257
            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
258
                $this,
259
                $closed_reg_statuses,
260
                false
261
            );
262
            do_action(
263
                'AHEE__EE_Registration__set_status__canceled_or_declined',
264
                $this,
265
                $old_STS_ID,
266
                $new_STS_ID,
267
                $context
268
            );
269
            return;
270
        }
271
    }
272
273
274
    /**
275
     * update REGs and TXN when cancelled or declined registrations involved
276
     *
277
     * @param array        $closed_reg_statuses
278
     * @param string       $new_STS_ID
279
     * @param string       $old_STS_ID
280
     * @param Context|null $context
281
     * @throws EE_Error
282
     * @throws InvalidArgumentException
283
     * @throws InvalidDataTypeException
284
     * @throws InvalidInterfaceException
285
     * @throws ReflectionException
286
     */
287 View Code Duplication
    private function updateIfDeclined(array $closed_reg_statuses, $new_STS_ID, $old_STS_ID, Context $context = null)
288
    {
289
        // true if reinstating cancelled or declined registration
290
        if (in_array($old_STS_ID, $closed_reg_statuses, true)
291
            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
292
        ) {
293
            /** @type EE_Registration_Processor $registration_processor */
294
            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
295
            /** @type EE_Transaction_Processor $transaction_processor */
296
            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
297
            // reinstating cancelled or declined registration
298
            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
299
                $this,
300
                $closed_reg_statuses
301
            );
302
            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
303
                $this,
304
                $closed_reg_statuses,
305
                false
306
            );
307
            do_action(
308
                'AHEE__EE_Registration__set_status__after_reinstated',
309
                $this,
310
                $old_STS_ID,
311
                $new_STS_ID,
312
                $context
313
            );
314
        }
315
    }
316
317
318
    /**
319
     * @param Context|null $context
320
     * @return bool
321
     */
322
    private function statusChangeUpdatesTransaction(Context $context = null)
323
    {
324
        $contexts_that_do_not_update_transaction = (array) apply_filters(
325
            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
326
            array('spco_reg_step_attendee_information_process_registrations'),
327
            $context,
328
            $this
329
        );
330
        return ! (
331
            $context instanceof Context
332
            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
333
        );
334
    }
335
336
337
    /**
338
     * @throws EE_Error
339
     * @throws EntityNotFoundException
340
     * @throws InvalidArgumentException
341
     * @throws InvalidDataTypeException
342
     * @throws InvalidInterfaceException
343
     * @throws ReflectionException
344
     * @throws RuntimeException
345
     */
346
    private function updateTransactionAfterStatusChange()
347
    {
348
        /** @type EE_Transaction_Payments $transaction_payments */
349
        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
350
        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
351
        $this->transaction()->update_status_based_on_total_paid(true);
352
    }
353
354
355
    /**
356
     *        get Status ID
357
     */
358
    public function status_ID()
359
    {
360
        return $this->get('STS_ID');
361
    }
362
363
364
    /**
365
     * increments this registration's related ticket sold and corresponding datetime sold values
366
     *
367
     * @return void
368
     * @throws EE_Error
369
     * @throws EntityNotFoundException
370
     */
371
    private function _reserve_registration_space()
372
    {
373
        // reserved ticket and datetime counts will be decremented as sold counts are incremented
374
        // so stop tracking that this reg has a ticket reserved
375
        $this->release_reserved_ticket();
376
        $ticket = $this->ticket();
377
        $ticket->increase_sold();
378
        $ticket->save();
379
        // possibly set event status to sold out
380
        $this->event()->perform_sold_out_status_check();
381
    }
382
383
384
    /**
385
     * Gets the ticket this registration is for
386
     *
387
     * @param boolean $include_archived whether to include archived tickets or not.
388
     *
389
     * @return EE_Ticket|EE_Base_Class
390
     * @throws EE_Error
391
     */
392
    public function ticket($include_archived = true)
393
    {
394
        $query_params = array();
395
        if ($include_archived) {
396
            $query_params['default_where_conditions'] = 'none';
397
        }
398
        return $this->get_first_related('Ticket', $query_params);
399
    }
400
401
402
    /**
403
     * Gets the event this registration is for
404
     *
405
     * @return EE_Event
406
     * @throws EE_Error
407
     * @throws EntityNotFoundException
408
     */
409
    public function event()
410
    {
411
        $event = $this->get_first_related('Event');
412
        if (! $event instanceof \EE_Event) {
413
            throw new EntityNotFoundException('Event ID', $this->event_ID());
414
        }
415
        return $event;
416
    }
417
418
419
    /**
420
     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
421
     * with the author of the event this registration is for.
422
     *
423
     * @since 4.5.0
424
     * @return int
425
     * @throws EE_Error
426
     * @throws EntityNotFoundException
427
     */
428
    public function wp_user()
429
    {
430
        $event = $this->event();
431
        if ($event instanceof EE_Event) {
432
            return $event->wp_user();
433
        }
434
        return 0;
435
    }
436
437
438
    /**
439
     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
440
     *
441
     * @return void
442
     * @throws EE_Error
443
     */
444
    private function _release_registration_space()
445
    {
446
        $ticket = $this->ticket();
447
        $ticket->decrease_sold();
448
        $ticket->save();
449
    }
450
451
452
    /**
453
     * tracks this registration's ticket reservation in extra meta
454
     * and can increment related ticket reserved and corresponding datetime reserved values
455
     *
456
     * @param bool $update_ticket if true, will increment ticket and datetime reserved count
457
     *
458
     * @return void
459
     * @throws EE_Error
460
     */
461
    public function reserve_ticket($update_ticket = false)
462
    {
463
        if ($this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true, false) === false) {
464
            // PLZ NOTE: although checking $update_ticket first would be more efficient,
465
            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
466
            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true, false) && $update_ticket) {
467
                $ticket = $this->ticket();
468
                $ticket->increase_reserved();
469
                $ticket->save();
470
            }
471
        }
472
    }
473
474
475
    /**
476
     * stops tracking this registration's ticket reservation in extra meta
477
     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
478
     *
479
     * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
480
     *
481
     * @return void
482
     * @throws EE_Error
483
     */
484
    public function release_reserved_ticket($update_ticket = false)
485
    {
486
        if ($this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true, false) !== false) {
487
            // PLZ NOTE: although checking $update_ticket first would be more efficient,
488
            // we NEED to ALWAYS call delete_extra_meta(), which is why that is done first
489
            if ($this->delete_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY) && $update_ticket) {
490
                $ticket = $this->ticket();
491
                $ticket->decrease_reserved();
492
                $ticket->save();
493
            }
494
        }
495
    }
496
497
498
    /**
499
     * Set Attendee ID
500
     *
501
     * @param        int $ATT_ID Attendee ID
502
     * @throws EE_Error
503
     * @throws RuntimeException
504
     */
505
    public function set_attendee_id($ATT_ID = 0)
506
    {
507
        $this->set('ATT_ID', $ATT_ID);
508
    }
509
510
511
    /**
512
     *        Set Transaction ID
513
     *
514
     * @param        int $TXN_ID Transaction ID
515
     * @throws EE_Error
516
     * @throws RuntimeException
517
     */
518
    public function set_transaction_id($TXN_ID = 0)
519
    {
520
        $this->set('TXN_ID', $TXN_ID);
521
    }
522
523
524
    /**
525
     *        Set Session
526
     *
527
     * @param    string $REG_session PHP Session ID
528
     * @throws EE_Error
529
     * @throws RuntimeException
530
     */
531
    public function set_session($REG_session = '')
532
    {
533
        $this->set('REG_session', $REG_session);
534
    }
535
536
537
    /**
538
     *        Set Registration URL Link
539
     *
540
     * @param    string $REG_url_link Registration URL Link
541
     * @throws EE_Error
542
     * @throws RuntimeException
543
     */
544
    public function set_reg_url_link($REG_url_link = '')
545
    {
546
        $this->set('REG_url_link', $REG_url_link);
547
    }
548
549
550
    /**
551
     *        Set Attendee Counter
552
     *
553
     * @param        int $REG_count Primary Attendee
554
     * @throws EE_Error
555
     * @throws RuntimeException
556
     */
557
    public function set_count($REG_count = 1)
558
    {
559
        $this->set('REG_count', $REG_count);
560
    }
561
562
563
    /**
564
     *        Set Group Size
565
     *
566
     * @param        boolean $REG_group_size Group Registration
567
     * @throws EE_Error
568
     * @throws RuntimeException
569
     */
570
    public function set_group_size($REG_group_size = false)
571
    {
572
        $this->set('REG_group_size', $REG_group_size);
573
    }
574
575
576
    /**
577
     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
578
     *    EEM_Registration::status_id_not_approved
579
     *
580
     * @return        boolean
581
     */
582
    public function is_not_approved()
583
    {
584
        return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
585
    }
586
587
588
    /**
589
     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
590
     *    EEM_Registration::status_id_pending_payment
591
     *
592
     * @return        boolean
593
     */
594
    public function is_pending_payment()
595
    {
596
        return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
597
    }
598
599
600
    /**
601
     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
602
     *
603
     * @return        boolean
604
     */
605
    public function is_approved()
606
    {
607
        return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
608
    }
609
610
611
    /**
612
     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
613
     *
614
     * @return        boolean
615
     */
616
    public function is_cancelled()
617
    {
618
        return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
619
    }
620
621
622
    /**
623
     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
624
     *
625
     * @return        boolean
626
     */
627
    public function is_declined()
628
    {
629
        return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
630
    }
631
632
633
    /**
634
     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
635
     *    EEM_Registration::status_id_incomplete
636
     *
637
     * @return        boolean
638
     */
639
    public function is_incomplete()
640
    {
641
        return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
642
    }
643
644
645
    /**
646
     *        Set Registration Date
647
     *
648
     * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
649
     *                                                 Date
650
     * @throws EE_Error
651
     * @throws RuntimeException
652
     */
653
    public function set_reg_date($REG_date = false)
654
    {
655
        $this->set('REG_date', $REG_date);
656
    }
657
658
659
    /**
660
     *    Set final price owing for this registration after all ticket/price modifications
661
     *
662
     * @access    public
663
     * @param    float $REG_final_price
664
     * @throws EE_Error
665
     * @throws RuntimeException
666
     */
667
    public function set_final_price($REG_final_price = 0.00)
668
    {
669
        $this->set('REG_final_price', $REG_final_price);
670
    }
671
672
673
    /**
674
     *    Set amount paid towards this registration's final price
675
     *
676
     * @access    public
677
     * @param    float $REG_paid
678
     * @throws EE_Error
679
     * @throws RuntimeException
680
     */
681
    public function set_paid($REG_paid = 0.00)
682
    {
683
        $this->set('REG_paid', $REG_paid);
684
    }
685
686
687
    /**
688
     *        Attendee Is Going
689
     *
690
     * @param        boolean $REG_att_is_going Attendee Is Going
691
     * @throws EE_Error
692
     * @throws RuntimeException
693
     */
694
    public function set_att_is_going($REG_att_is_going = false)
695
    {
696
        $this->set('REG_att_is_going', $REG_att_is_going);
697
    }
698
699
700
    /**
701
     * Gets the related attendee
702
     *
703
     * @return EE_Attendee
704
     * @throws EE_Error
705
     */
706
    public function attendee()
707
    {
708
        return $this->get_first_related('Attendee');
709
    }
710
711
712
    /**
713
     *        get Event ID
714
     */
715
    public function event_ID()
716
    {
717
        return $this->get('EVT_ID');
718
    }
719
720
721
    /**
722
     *        get Event ID
723
     */
724
    public function event_name()
725
    {
726
        $event = $this->event_obj();
727
        if ($event) {
728
            return $event->name();
729
        } else {
730
            return null;
731
        }
732
    }
733
734
735
    /**
736
     * Fetches the event this registration is for
737
     *
738
     * @return EE_Event
739
     * @throws EE_Error
740
     */
741
    public function event_obj()
742
    {
743
        return $this->get_first_related('Event');
744
    }
745
746
747
    /**
748
     *        get Attendee ID
749
     */
750
    public function attendee_ID()
751
    {
752
        return $this->get('ATT_ID');
753
    }
754
755
756
    /**
757
     *        get PHP Session ID
758
     */
759
    public function session_ID()
760
    {
761
        return $this->get('REG_session');
762
    }
763
764
765
    /**
766
     * Gets the string which represents the URL trigger for the receipt template in the message template system.
767
     *
768
     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
769
     * @return string
770
     */
771
    public function receipt_url($messenger = 'html')
772
    {
773
774
        /**
775
         * The below will be deprecated one version after this.  We check first if there is a custom receipt template
776
         * already in use on old system.  If there is then we just return the standard url for it.
777
         *
778
         * @since 4.5.0
779
         */
780
        $template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
781
        $has_custom             = EEH_Template::locate_template(
782
            $template_relative_path,
783
            array(),
784
            true,
785
            true,
786
            true
787
        );
788
789
        if ($has_custom) {
790
            return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
791
        }
792
        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
793
    }
794
795
796
    /**
797
     * Gets the string which represents the URL trigger for the invoice template in the message template system.
798
     *
799
     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
800
     * @return string
801
     * @throws EE_Error
802
     */
803
    public function invoice_url($messenger = 'html')
804
    {
805
        /**
806
         * The below will be deprecated one version after this.  We check first if there is a custom invoice template
807
         * already in use on old system.  If there is then we just return the standard url for it.
808
         *
809
         * @since 4.5.0
810
         */
811
        $template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
812
        $has_custom             = EEH_Template::locate_template(
813
            $template_relative_path,
814
            array(),
815
            true,
816
            true,
817
            true
818
        );
819
820
        if ($has_custom) {
821
            if ($messenger == 'html') {
822
                return $this->invoice_url('launch');
823
            }
824
            $route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
825
826
            $query_args = array('ee' => $route, 'id' => $this->reg_url_link());
827
            if ($messenger == 'html') {
828
                $query_args['html'] = true;
829
            }
830
            return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
831
        }
832
        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
833
    }
834
835
836
    /**
837
     * get Registration URL Link
838
     *
839
     * @access public
840
     * @return string
841
     * @throws EE_Error
842
     */
843
    public function reg_url_link()
844
    {
845
        return (string) $this->get('REG_url_link');
846
    }
847
848
849
    /**
850
     * Echoes out invoice_url()
851
     *
852
     * @param string $type 'download','launch', or 'html' (default is 'launch')
853
     * @return void
854
     * @throws EE_Error
855
     */
856
    public function e_invoice_url($type = 'launch')
857
    {
858
        echo $this->invoice_url($type);
859
    }
860
861
862
    /**
863
     * Echoes out payment_overview_url
864
     */
865
    public function e_payment_overview_url()
866
    {
867
        echo $this->payment_overview_url();
868
    }
869
870
871
    /**
872
     * Gets the URL of the thank you page with this registration REG_url_link added as
873
     * a query parameter
874
     *
875
     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
876
     *                            payment overview url.
877
     * @return string
878
     * @throws EE_Error
879
     */
880 View Code Duplication
    public function payment_overview_url($clear_session = false)
881
    {
882
        return add_query_arg(array(
883
            'e_reg_url_link' => $this->reg_url_link(),
884
            'step'           => 'payment_options',
885
            'revisit'        => true,
886
            'clear_session' => (bool) $clear_session
887
        ), EE_Registry::instance()->CFG->core->reg_page_url());
888
    }
889
890
891
    /**
892
     * Gets the URL of the thank you page with this registration REG_url_link added as
893
     * a query parameter
894
     *
895
     * @return string
896
     * @throws EE_Error
897
     */
898 View Code Duplication
    public function edit_attendee_information_url()
899
    {
900
        return add_query_arg(array(
901
            'e_reg_url_link' => $this->reg_url_link(),
902
            'step'           => 'attendee_information',
903
            'revisit'        => true,
904
        ), EE_Registry::instance()->CFG->core->reg_page_url());
905
    }
906
907
908
    /**
909
     * Simply generates and returns the appropriate admin_url link to edit this registration
910
     *
911
     * @return string
912
     * @throws EE_Error
913
     */
914
    public function get_admin_edit_url()
915
    {
916
        return EEH_URL::add_query_args_and_nonce(array(
917
            'page'    => 'espresso_registrations',
918
            'action'  => 'view_registration',
919
            '_REG_ID' => $this->ID(),
920
        ), admin_url('admin.php'));
921
    }
922
923
924
    /**
925
     *    is_primary_registrant?
926
     */
927
    public function is_primary_registrant()
928
    {
929
        return $this->get('REG_count') == 1 ? true : false;
930
    }
931
932
933
    /**
934
     * This returns the primary registration object for this registration group (which may be this object).
935
     *
936
     * @return EE_Registration
937
     * @throws EE_Error
938
     */
939
    public function get_primary_registration()
940
    {
941
        if ($this->is_primary_registrant()) {
942
            return $this;
943
        }
944
945
        //k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
946
        /** @var EE_Registration $primary_registrant */
947
        $primary_registrant = EEM_Registration::instance()->get_one(array(
948
            array(
949
                'TXN_ID'    => $this->transaction_ID(),
950
                'REG_count' => 1,
951
            ),
952
        ));
953
        return $primary_registrant;
954
    }
955
956
957
    /**
958
     *        get  Attendee Number
959
     *
960
     * @access        public
961
     */
962
    public function count()
963
    {
964
        return $this->get('REG_count');
965
    }
966
967
968
    /**
969
     *        get Group Size
970
     */
971
    public function group_size()
972
    {
973
        return $this->get('REG_group_size');
974
    }
975
976
977
    /**
978
     *        get Registration Date
979
     */
980
    public function date()
981
    {
982
        return $this->get('REG_date');
983
    }
984
985
986
    /**
987
     * gets a pretty date
988
     *
989
     * @param string $date_format
990
     * @param string $time_format
991
     * @return string
992
     * @throws EE_Error
993
     */
994
    public function pretty_date($date_format = null, $time_format = null)
995
    {
996
        return $this->get_datetime('REG_date', $date_format, $time_format);
997
    }
998
999
1000
    /**
1001
     * final_price
1002
     * the registration's share of the transaction total, so that the
1003
     * sum of all the transaction's REG_final_prices equal the transaction's total
1004
     *
1005
     * @return float
1006
     * @throws EE_Error
1007
     */
1008
    public function final_price()
1009
    {
1010
        return $this->get('REG_final_price');
1011
    }
1012
1013
1014
    /**
1015
     * pretty_final_price
1016
     *  final price as formatted string, with correct decimal places and currency symbol
1017
     *
1018
     * @return string
1019
     * @throws EE_Error
1020
     */
1021
    public function pretty_final_price()
1022
    {
1023
        return $this->get_pretty('REG_final_price');
1024
    }
1025
1026
1027
    /**
1028
     * get paid (yeah)
1029
     *
1030
     * @return float
1031
     * @throws EE_Error
1032
     */
1033
    public function paid()
1034
    {
1035
        return $this->get('REG_paid');
1036
    }
1037
1038
1039
    /**
1040
     * pretty_paid
1041
     *
1042
     * @return float
1043
     * @throws EE_Error
1044
     */
1045
    public function pretty_paid()
1046
    {
1047
        return $this->get_pretty('REG_paid');
1048
    }
1049
1050
1051
    /**
1052
     * owes_monies_and_can_pay
1053
     * whether or not this registration has monies owing and it's' status allows payment
1054
     *
1055
     * @param array $requires_payment
1056
     * @return bool
1057
     * @throws EE_Error
1058
     */
1059
    public function owes_monies_and_can_pay($requires_payment = array())
1060
    {
1061
        // these reg statuses require payment (if event is not free)
1062
        $requires_payment = ! empty($requires_payment)
1063
            ? $requires_payment
1064
            : EEM_Registration::reg_statuses_that_allow_payment();
1065
        if (in_array($this->status_ID(), $requires_payment) &&
1066
            $this->final_price() != 0 &&
1067
            $this->final_price() != $this->paid()
1068
        ) {
1069
            return true;
1070
        } else {
1071
            return false;
1072
        }
1073
    }
1074
1075
1076
    /**
1077
     * Prints out the return value of $this->pretty_status()
1078
     *
1079
     * @param bool $show_icons
1080
     * @return void
1081
     * @throws EE_Error
1082
     */
1083
    public function e_pretty_status($show_icons = false)
1084
    {
1085
        echo $this->pretty_status($show_icons);
1086
    }
1087
1088
1089
    /**
1090
     * Returns a nice version of the status for displaying to customers
1091
     *
1092
     * @param bool $show_icons
1093
     * @return string
1094
     * @throws EE_Error
1095
     */
1096
    public function pretty_status($show_icons = false)
1097
    {
1098
        $status = EEM_Status::instance()->localized_status(
1099
            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1100
            false,
1101
            'sentence'
1102
        );
1103
        $icon   = '';
1104
        switch ($this->status_ID()) {
1105
            case EEM_Registration::status_id_approved:
1106
                $icon = $show_icons
1107
                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1108
                    : '';
1109
                break;
1110
            case EEM_Registration::status_id_pending_payment:
1111
                $icon = $show_icons
1112
                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1113
                    : '';
1114
                break;
1115
            case EEM_Registration::status_id_not_approved:
1116
                $icon = $show_icons
1117
                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1118
                    : '';
1119
                break;
1120
            case EEM_Registration::status_id_cancelled:
1121
                $icon = $show_icons
1122
                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1123
                    : '';
1124
                break;
1125
            case EEM_Registration::status_id_incomplete:
1126
                $icon = $show_icons
1127
                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1128
                    : '';
1129
                break;
1130
            case EEM_Registration::status_id_declined:
1131
                $icon = $show_icons
1132
                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1133
                    : '';
1134
                break;
1135
            case EEM_Registration::status_id_wait_list:
1136
                $icon = $show_icons
1137
                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1138
                    : '';
1139
                break;
1140
        }
1141
        return $icon . $status[$this->status_ID()];
1142
    }
1143
1144
1145
    /**
1146
     *        get Attendee Is Going
1147
     */
1148
    public function att_is_going()
1149
    {
1150
        return $this->get('REG_att_is_going');
1151
    }
1152
1153
1154
    /**
1155
     * Gets related answers
1156
     *
1157
     * @param array $query_params like EEM_Base::get_all
1158
     * @return EE_Answer[]
1159
     * @throws EE_Error
1160
     */
1161
    public function answers($query_params = null)
1162
    {
1163
        return $this->get_many_related('Answer', $query_params);
1164
    }
1165
1166
1167
    /**
1168
     * Gets the registration's answer value to the specified question
1169
     * (either the question's ID or a question object)
1170
     *
1171
     * @param EE_Question|int $question
1172
     * @param bool            $pretty_value
1173
     * @return array|string if pretty_value= true, the result will always be a string
1174
     * (because the answer might be an array of answer values, so passing pretty_value=true
1175
     * will convert it into some kind of string)
1176
     * @throws EE_Error
1177
     */
1178
    public function answer_value_to_question($question, $pretty_value = true)
1179
    {
1180
        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1181
        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1182
    }
1183
1184
1185
    /**
1186
     * question_groups
1187
     * returns an array of EE_Question_Group objects for this registration
1188
     *
1189
     * @return EE_Question_Group[]
1190
     * @throws EE_Error
1191
     * @throws EntityNotFoundException
1192
     */
1193
    public function question_groups()
1194
    {
1195
        $question_groups = array();
1196
        if ($this->event() instanceof EE_Event) {
1197
            $question_groups = $this->event()->question_groups(
1198
                array(
1199
                    array(
1200
                        'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1201
                    ),
1202
                    'order_by' => array('QSG_order' => 'ASC'),
1203
                )
1204
            );
1205
        }
1206
        return $question_groups;
1207
    }
1208
1209
1210
    /**
1211
     * count_question_groups
1212
     * returns a count of the number of EE_Question_Group objects for this registration
1213
     *
1214
     * @return int
1215
     * @throws EE_Error
1216
     * @throws EntityNotFoundException
1217
     */
1218
    public function count_question_groups()
1219
    {
1220
        $qg_count = 0;
1221
        if ($this->event() instanceof EE_Event) {
1222
            $qg_count = $this->event()->count_related(
1223
                'Question_Group',
1224
                array(
1225
                    array(
1226
                        'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1227
                    ),
1228
                )
1229
            );
1230
        }
1231
        return $qg_count;
1232
    }
1233
1234
1235
    /**
1236
     * Returns the registration date in the 'standard' string format
1237
     * (function may be improved in the future to allow for different formats and timezones)
1238
     *
1239
     * @return string
1240
     * @throws EE_Error
1241
     */
1242
    public function reg_date()
1243
    {
1244
        return $this->get_datetime('REG_date');
1245
    }
1246
1247
1248
    /**
1249
     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1250
     * the ticket this registration purchased, or the datetime they have registered
1251
     * to attend)
1252
     *
1253
     * @return EE_Datetime_Ticket
1254
     * @throws EE_Error
1255
     */
1256
    public function datetime_ticket()
1257
    {
1258
        return $this->get_first_related('Datetime_Ticket');
1259
    }
1260
1261
1262
    /**
1263
     * Sets the registration's datetime_ticket.
1264
     *
1265
     * @param EE_Datetime_Ticket $datetime_ticket
1266
     * @return EE_Datetime_Ticket
1267
     * @throws EE_Error
1268
     */
1269
    public function set_datetime_ticket($datetime_ticket)
1270
    {
1271
        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1272
    }
1273
1274
    /**
1275
     * Gets deleted
1276
     *
1277
     * @return bool
1278
     * @throws EE_Error
1279
     */
1280
    public function deleted()
1281
    {
1282
        return $this->get('REG_deleted');
1283
    }
1284
1285
    /**
1286
     * Sets deleted
1287
     *
1288
     * @param boolean $deleted
1289
     * @return bool
1290
     * @throws EE_Error
1291
     * @throws RuntimeException
1292
     */
1293
    public function set_deleted($deleted)
1294
    {
1295
        if ($deleted) {
1296
            $this->delete();
1297
        } else {
1298
            $this->restore();
1299
        }
1300
    }
1301
1302
1303
    /**
1304
     * Get the status object of this object
1305
     *
1306
     * @return EE_Status
1307
     * @throws EE_Error
1308
     */
1309
    public function status_obj()
1310
    {
1311
        return $this->get_first_related('Status');
1312
    }
1313
1314
1315
    /**
1316
     * Returns the number of times this registration has checked into any of the datetimes
1317
     * its available for
1318
     *
1319
     * @return int
1320
     * @throws EE_Error
1321
     */
1322
    public function count_checkins()
1323
    {
1324
        return $this->get_model()->count_related($this, 'Checkin');
1325
    }
1326
1327
1328
    /**
1329
     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1330
     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1331
     *
1332
     * @return int
1333
     * @throws EE_Error
1334
     */
1335
    public function count_checkins_not_checkedout()
1336
    {
1337
        return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1338
    }
1339
1340
1341
    /**
1342
     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1343
     *
1344
     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1345
     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1346
     *                                          consider registration status as well as datetime access.
1347
     * @return bool
1348
     * @throws EE_Error
1349
     */
1350
    public function can_checkin($DTT_OR_ID, $check_approved = true)
1351
    {
1352
        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1353
1354
        //first check registration status
1355
        if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1356
            return false;
1357
        }
1358
        //is there a datetime ticket that matches this dtt_ID?
1359
        if (! (EEM_Datetime_Ticket::instance()->exists(array(
1360
            array(
1361
                'TKT_ID' => $this->get('TKT_ID'),
1362
                'DTT_ID' => $DTT_ID,
1363
            ),
1364
        )))
1365
        ) {
1366
            return false;
1367
        }
1368
1369
        //final check is against TKT_uses
1370
        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1371
    }
1372
1373
1374
    /**
1375
     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1376
     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1377
     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1378
     * then return false.  Otherwise return true.
1379
     *
1380
     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1381
     * @return bool true means can checkin.  false means cannot checkin.
1382
     * @throws EE_Error
1383
     */
1384
    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1385
    {
1386
        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1387
1388
        if (! $DTT_ID) {
1389
            return false;
1390
        }
1391
1392
        $max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1393
1394
        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1395
        // check-in or not.
1396
        if (! $max_uses || $max_uses === EE_INF) {
1397
            return true;
1398
        }
1399
1400
        //does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1401
        //go ahead and toggle.
1402
        if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1403
            return true;
1404
        }
1405
1406
        //made it here so the last check is whether the number of checkins per unique datetime on this registration
1407
        //disallows further check-ins.
1408
        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(array(
1409
            array(
1410
                'REG_ID' => $this->ID(),
1411
                'CHK_in' => true,
1412
            ),
1413
        ), 'DTT_ID', true);
1414
        // checkins have already reached their max number of uses
1415
        // so registrant can NOT checkin
1416
        if ($count_unique_dtt_checkins >= $max_uses) {
1417
            EE_Error::add_error(
1418
                esc_html__(
1419
                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1420
                    'event_espresso'
1421
                ),
1422
                __FILE__,
1423
                __FUNCTION__,
1424
                __LINE__
1425
            );
1426
            return false;
1427
        }
1428
        return true;
1429
    }
1430
1431
1432
    /**
1433
     * toggle Check-in status for this registration
1434
     * Check-ins are toggled in the following order:
1435
     * never checked in -> checked in
1436
     * checked in -> checked out
1437
     * checked out -> checked in
1438
     *
1439
     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1440
     *                      If not included or null, then it is assumed latest datetime is being toggled.
1441
     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1442
     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1443
     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1444
     * @throws EE_Error
1445
     */
1446
    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1447
    {
1448
        if (empty($DTT_ID)) {
1449
            $datetime = $this->get_latest_related_datetime();
1450
            $DTT_ID   = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1451
            // verify the registration can checkin for the given DTT_ID
1452 View Code Duplication
        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1453
            EE_Error::add_error(
1454
                sprintf(
1455
                    esc_html__(
1456
                        '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',
1457
                        'event_espresso'
1458
                    ),
1459
                    $this->ID(),
1460
                    $DTT_ID
1461
                ),
1462
                __FILE__,
1463
                __FUNCTION__,
1464
                __LINE__
1465
            );
1466
            return false;
1467
        }
1468
        $status_paths = array(
1469
            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1470
            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1471
            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1472
        );
1473
        //start by getting the current status so we know what status we'll be changing to.
1474
        $cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1475
        $status_to  = $status_paths[$cur_status];
1476
        // database only records true for checked IN or false for checked OUT
1477
        // no record ( null ) means checked in NEVER, but we obviously don't save that
1478
        $new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1479
        // add relation - note Check-ins are always creating new rows
1480
        // because we are keeping track of Check-ins over time.
1481
        // Eventually we'll probably want to show a list table
1482
        // for the individual Check-ins so that they can be managed.
1483
        $checkin = EE_Checkin::new_instance(array(
1484
            'REG_ID' => $this->ID(),
1485
            'DTT_ID' => $DTT_ID,
1486
            'CHK_in' => $new_status,
1487
        ));
1488
        // if the record could not be saved then return false
1489
        if ($checkin->save() === 0) {
1490
            if (WP_DEBUG) {
1491
                global $wpdb;
1492
                $error = sprintf(
1493
                    esc_html__(
1494
                        'Registration check in update failed because of the following database error: %1$s%2$s',
1495
                        'event_espresso'
1496
                    ),
1497
                    '<br />',
1498
                    $wpdb->last_error
1499
                );
1500
            } else {
1501
                $error = esc_html__(
1502
                    'Registration check in update failed because of an unknown database error',
1503
                    'event_espresso'
1504
                );
1505
            }
1506
            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1507
            return false;
1508
        }
1509
        return $status_to;
1510
    }
1511
1512
1513
    /**
1514
     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1515
     * "Latest" is defined by the `DTT_EVT_start` column.
1516
     *
1517
     * @return EE_Datetime|null
1518
     * @throws EE_Error
1519
     */
1520 View Code Duplication
    public function get_latest_related_datetime()
1521
    {
1522
        return EEM_Datetime::instance()->get_one(
1523
            array(
1524
                array(
1525
                    'Ticket.Registration.REG_ID' => $this->ID(),
1526
                ),
1527
                'order_by' => array('DTT_EVT_start' => 'DESC'),
1528
            )
1529
        );
1530
    }
1531
1532
1533
    /**
1534
     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1535
     * "Earliest" is defined by the `DTT_EVT_start` column.
1536
     *
1537
     * @throws EE_Error
1538
     */
1539 View Code Duplication
    public function get_earliest_related_datetime()
1540
    {
1541
        return EEM_Datetime::instance()->get_one(
1542
            array(
1543
                array(
1544
                    'Ticket.Registration.REG_ID' => $this->ID(),
1545
                ),
1546
                'order_by' => array('DTT_EVT_start' => 'ASC'),
1547
            )
1548
        );
1549
    }
1550
1551
1552
    /**
1553
     * This method simply returns the check-in status for this registration and the given datetime.
1554
     * If neither the datetime nor the checkin values are provided as arguments,
1555
     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1556
     *
1557
     * @param  int       $DTT_ID  The ID of the datetime we're checking against
1558
     *                            (if empty we'll get the primary datetime for
1559
     *                            this registration (via event) and use it's ID);
1560
     * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1561
     *
1562
     * @return int                Integer representing Check-in status.
1563
     * @throws EE_Error
1564
     */
1565
    public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1566
    {
1567
        $checkin_query_params = array(
1568
            'order_by' => array('CHK_timestamp' => 'DESC'),
1569
        );
1570
1571
        if ($DTT_ID > 0) {
1572
            $checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1573
        }
1574
1575
        //get checkin object (if exists)
1576
        $checkin = $checkin instanceof EE_Checkin
1577
            ? $checkin
1578
            : $this->get_first_related('Checkin', $checkin_query_params);
1579
        if ($checkin instanceof EE_Checkin) {
1580
            if ($checkin->get('CHK_in')) {
1581
                return EE_Checkin::status_checked_in; //checked in
1582
            }
1583
            return EE_Checkin::status_checked_out; //had checked in but is now checked out.
1584
        }
1585
        return EE_Checkin::status_checked_never; //never been checked in
1586
    }
1587
1588
1589
    /**
1590
     * This method returns a localized message for the toggled Check-in message.
1591
     *
1592
     * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1593
     *                     then it is assumed Check-in for primary datetime was toggled.
1594
     * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1595
     *                     message can be customized with the attendee name.
1596
     * @return string internationalized message
1597
     * @throws EE_Error
1598
     */
1599
    public function get_checkin_msg($DTT_ID, $error = false)
1600
    {
1601
        //let's get the attendee first so we can include the name of the attendee
1602
        $attendee = $this->get_first_related('Attendee');
1603
        if ($attendee instanceof EE_Attendee) {
1604
            if ($error) {
1605
                return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1606
            }
1607
            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1608
            //what is the status message going to be?
1609
            switch ($cur_status) {
1610
                case EE_Checkin::status_checked_never:
1611
                    return sprintf(__("%s has been removed from Check-in records", "event_espresso"),
1612
                        $attendee->full_name());
1613
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
1614
                case EE_Checkin::status_checked_in:
1615
                    return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1616
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
1617
                case EE_Checkin::status_checked_out:
1618
                    return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1619
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

Loading history...
1620
            }
1621
        }
1622
        return esc_html__("The check-in status could not be determined.", "event_espresso");
1623
    }
1624
1625
1626
    /**
1627
     * Returns the related EE_Transaction to this registration
1628
     *
1629
     * @return EE_Transaction
1630
     * @throws EE_Error
1631
     * @throws EntityNotFoundException
1632
     */
1633
    public function transaction()
1634
    {
1635
        $transaction = $this->get_first_related('Transaction');
1636
        if (! $transaction instanceof \EE_Transaction) {
1637
            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1638
        }
1639
        return $transaction;
1640
    }
1641
1642
1643
    /**
1644
     *        get Registration Code
1645
     */
1646
    public function reg_code()
1647
    {
1648
        return $this->get('REG_code');
1649
    }
1650
1651
1652
    /**
1653
     *        get Transaction ID
1654
     */
1655
    public function transaction_ID()
1656
    {
1657
        return $this->get('TXN_ID');
1658
    }
1659
1660
1661
    /**
1662
     * @return int
1663
     * @throws EE_Error
1664
     */
1665
    public function ticket_ID()
1666
    {
1667
        return $this->get('TKT_ID');
1668
    }
1669
1670
1671
    /**
1672
     *        Set Registration Code
1673
     *
1674
     * @access    public
1675
     * @param    string  $REG_code Registration Code
1676
     * @param    boolean $use_default
1677
     * @throws EE_Error
1678
     */
1679
    public function set_reg_code($REG_code, $use_default = false)
1680
    {
1681
        if (empty($REG_code)) {
1682
            EE_Error::add_error(
1683
                esc_html__('REG_code can not be empty.', 'event_espresso'),
1684
                __FILE__,
1685
                __FUNCTION__,
1686
                __LINE__
1687
            );
1688
            return;
1689
        }
1690
        if (! $this->reg_code()) {
1691
            parent::set('REG_code', $REG_code, $use_default);
1692
        } else {
1693
            EE_Error::doing_it_wrong(
1694
                __CLASS__ . '::' . __FUNCTION__,
1695
                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1696
                '4.6.0'
1697
            );
1698
        }
1699
    }
1700
1701
1702
    /**
1703
     * Returns all other registrations in the same group as this registrant who have the same ticket option.
1704
     * Note, if you want to just get all registrations in the same transaction (group), use:
1705
     *    $registration->transaction()->registrations();
1706
     *
1707
     * @since 4.5.0
1708
     * @return EE_Registration[] or empty array if this isn't a group registration.
1709
     * @throws EE_Error
1710
     */
1711
    public function get_all_other_registrations_in_group()
1712
    {
1713
        if ($this->group_size() < 2) {
1714
            return array();
1715
        }
1716
1717
        $query[0] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$query was never initialized. Although not strictly required by PHP, it is generally a good practice to add $query = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1718
            'TXN_ID' => $this->transaction_ID(),
1719
            'REG_ID' => array('!=', $this->ID()),
1720
            'TKT_ID' => $this->ticket_ID(),
1721
        );
1722
        /** @var EE_Registration[] $registrations */
1723
        $registrations = $this->get_model()->get_all($query);
1724
        return $registrations;
1725
    }
1726
1727
    /**
1728
     * Return the link to the admin details for the object.
1729
     *
1730
     * @return string
1731
     * @throws EE_Error
1732
     */
1733 View Code Duplication
    public function get_admin_details_link()
1734
    {
1735
        EE_Registry::instance()->load_helper('URL');
1736
        return EEH_URL::add_query_args_and_nonce(
1737
            array(
1738
                'page'    => 'espresso_registrations',
1739
                'action'  => 'view_registration',
1740
                '_REG_ID' => $this->ID(),
1741
            ),
1742
            admin_url('admin.php')
1743
        );
1744
    }
1745
1746
    /**
1747
     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1748
     *
1749
     * @return string
1750
     * @throws EE_Error
1751
     */
1752
    public function get_admin_edit_link()
1753
    {
1754
        return $this->get_admin_details_link();
1755
    }
1756
1757
    /**
1758
     * Returns the link to a settings page for the object.
1759
     *
1760
     * @return string
1761
     * @throws EE_Error
1762
     */
1763
    public function get_admin_settings_link()
1764
    {
1765
        return $this->get_admin_details_link();
1766
    }
1767
1768
    /**
1769
     * Returns the link to the "overview" for the object (typically the "list table" view).
1770
     *
1771
     * @return string
1772
     */
1773
    public function get_admin_overview_link()
1774
    {
1775
        EE_Registry::instance()->load_helper('URL');
1776
        return EEH_URL::add_query_args_and_nonce(
1777
            array(
1778
                'page' => 'espresso_registrations',
1779
            ),
1780
            admin_url('admin.php')
1781
        );
1782
    }
1783
1784
1785
    /**
1786
     * @param array $query_params
1787
     *
1788
     * @return \EE_Registration[]
1789
     * @throws EE_Error
1790
     */
1791
    public function payments($query_params = array())
1792
    {
1793
        return $this->get_many_related('Payment', $query_params);
1794
    }
1795
1796
1797
    /**
1798
     * @param array $query_params
1799
     *
1800
     * @return \EE_Registration_Payment[]
1801
     * @throws EE_Error
1802
     */
1803
    public function registration_payments($query_params = array())
1804
    {
1805
        return $this->get_many_related('Registration_Payment', $query_params);
1806
    }
1807
1808
1809
    /**
1810
     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1811
     * Note: if there are no payments on the registration there will be no payment method returned.
1812
     *
1813
     * @return EE_Payment_Method|null
1814
     */
1815
    public function payment_method()
1816
    {
1817
        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1818
    }
1819
1820
1821
    /**
1822
     * @return \EE_Line_Item
1823
     * @throws EntityNotFoundException
1824
     * @throws EE_Error
1825
     */
1826
    public function ticket_line_item()
1827
    {
1828
        $ticket            = $this->ticket();
1829
        $transaction       = $this->transaction();
1830
        $line_item         = null;
1831
        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1832
            $transaction->total_line_item(),
1833
            'Ticket',
1834
            array($ticket->ID())
1835
        );
1836 View Code Duplication
        foreach ($ticket_line_items as $ticket_line_item) {
1837
            if (
1838
                $ticket_line_item instanceof \EE_Line_Item
1839
                && $ticket_line_item->OBJ_type() === 'Ticket'
1840
                && $ticket_line_item->OBJ_ID() === $ticket->ID()
1841
            ) {
1842
                $line_item = $ticket_line_item;
1843
                break;
1844
            }
1845
        }
1846 View Code Duplication
        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1847
            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1848
        }
1849
        return $line_item;
1850
    }
1851
1852
1853
    /**
1854
     * Soft Deletes this model object.
1855
     *
1856
     * @return boolean | int
1857
     * @throws RuntimeException
1858
     * @throws EE_Error
1859
     */
1860
    public function delete()
1861
    {
1862
        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1863
            $this->set_status(EEM_Registration::status_id_cancelled);
1864
        }
1865
        return parent::delete();
1866
    }
1867
1868
1869
    /**
1870
     * Restores whatever the previous status was on a registration before it was trashed (if possible)
1871
     *
1872
     * @throws EE_Error
1873
     * @throws RuntimeException
1874
     */
1875
    public function restore()
1876
    {
1877
        $previous_status = $this->get_extra_meta(
1878
            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1879
            true,
1880
            EEM_Registration::status_id_cancelled
1881
        );
1882
        if ($previous_status) {
1883
            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1884
            $this->set_status($previous_status);
1885
        }
1886
        return parent::restore();
1887
    }
1888
1889
1890
    /**
1891
     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1892
     *
1893
     * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1894
     *                                           depending on whether the reg status changes to or from "Approved"
1895
     * @return boolean whether the Registration status was updated
1896
     * @throws EE_Error
1897
     * @throws RuntimeException
1898
     */
1899
    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1900
    {
1901
        $paid = $this->paid();
1902
        $price = $this->final_price();
1903
        switch(true) {
1904
            // overpaid or paid
1905
            case EEH_Money::compare_floats($paid, $price, '>'):
1906
            case EEH_Money::compare_floats($paid, $price):
1907
                $new_status = EEM_Registration::status_id_approved;
1908
                break;
1909
            //  underpaid
1910
            case EEH_Money::compare_floats($paid, $price, '<'):
1911
                $new_status = EEM_Registration::status_id_pending_payment;
1912
                break;
1913
            // uhhh Houston...
1914
            default:
1915
                throw new RuntimeException(
1916
                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1917
                );
1918
        }
1919
        if ($new_status !== $this->status_ID()) {
1920
            if ($trigger_set_status_logic) {
1921
                return $this->set_status($new_status);
1922
            }
1923
            parent::set('STS_ID', $new_status);
1924
            return true;
1925
        }
1926
        return false;
1927
    }
1928
1929
1930
    /*************************** DEPRECATED ***************************/
1931
1932
1933
    /**
1934
     * @deprecated
1935
     * @since     4.7.0
1936
     * @access    public
1937
     */
1938
    public function price_paid()
1939
    {
1940
        EE_Error::doing_it_wrong('EE_Registration::price_paid()',
1941
            esc_html__('This method is deprecated, please use EE_Registration::final_price() instead.', 'event_espresso'),
1942
            '4.7.0');
1943
        return $this->final_price();
1944
    }
1945
1946
1947
    /**
1948
     * @deprecated
1949
     * @since     4.7.0
1950
     * @access    public
1951
     * @param    float $REG_final_price
1952
     * @throws EE_Error
1953
     * @throws RuntimeException
1954
     */
1955
    public function set_price_paid($REG_final_price = 0.00)
1956
    {
1957
        EE_Error::doing_it_wrong('EE_Registration::set_price_paid()',
1958
            esc_html__('This method is deprecated, please use EE_Registration::set_final_price() instead.', 'event_espresso'),
1959
            '4.7.0');
1960
        $this->set_final_price($REG_final_price);
1961
    }
1962
1963
1964
    /**
1965
     * @deprecated
1966
     * @since 4.7.0
1967
     * @return string
1968
     * @throws EE_Error
1969
     */
1970
    public function pretty_price_paid()
1971
    {
1972
        EE_Error::doing_it_wrong('EE_Registration::pretty_price_paid()',
1973
            esc_html__('This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
1974
                'event_espresso'), '4.7.0');
1975
        return $this->pretty_final_price();
1976
    }
1977
1978
1979
    /**
1980
     * Gets the primary datetime related to this registration via the related Event to this registration
1981
     *
1982
     * @deprecated 4.9.17
1983
     * @return EE_Datetime
1984
     * @throws EE_Error
1985
     * @throws EntityNotFoundException
1986
     */
1987
    public function get_related_primary_datetime()
1988
    {
1989
        EE_Error::doing_it_wrong(
1990
            __METHOD__,
1991
            esc_html__(
1992
                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
1993
                'event_espresso'
1994
            ),
1995
            '4.9.17',
1996
            '5.0.0'
1997
        );
1998
        return $this->event()->primary_datetime();
1999
    }
2000
2001
2002
}
2003