Completed
Branch BUG-10381-asset-loading (361215)
by
unknown
158:58 queued 146:07
created

EE_Registration::reserve_ticket()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 1
dl 0
loc 11
rs 9.2
c 0
b 0
f 0
1
<?php use EventEspresso\core\exceptions\EntityNotFoundException;
2
3
if ( !defined( 'EVENT_ESPRESSO_VERSION' ) ) {
4
	exit( 'No direct script access allowed' );
5
}
6
/**
7
 * EE_Registration class
8
 *
9
 * @package 			Event Espresso
10
 * @subpackage 	includes/classes/EE_Registration.class.php
11
 * @author 				Mike Nelson, Brent Christensen
12
 */
13
class EE_Registration extends EE_Soft_Delete_Base_Class implements EEI_Registration, EEI_Admin_Links {
14
15
16
	/**
17
	 * Used to reference when a registration has never been checked in.
18
	 * @type int
19
	 */
20
	const checkin_status_never = 2;
21
22
	/**
23
	 * Used to reference when a registration has been checked in.
24
	 * @type int
25
	 */
26
	const checkin_status_in = 1;
27
28
29
	/**
30
	 * Used to reference when a registration has been checked out.
31
	 * @type int
32
	 */
33
	const checkin_status_out = 0;
34
35
36
	/**
37
	 * extra meta key for tracking reg status os trashed registrations
38
     *
39
	 * @type string
40
	 */
41
	const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
42
43
44
	/**
45
	 * extra meta key for tracking if registration has reserved ticket
46
     *
47
	 * @type string
48
	 */
49
	const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
50
51
52
53
	/**
54
	 *
55
	 * @param array $props_n_values  incoming values
56
	 * @param string $timezone  incoming timezone (if not set the timezone set for the website will be
57
	 *                          		used.)
58
	 * @param array $date_formats  incoming date_formats in an array where the first value is the
59
	 *                             		    date_format and the second value is the time format
60
	 * @return EE_Registration
61
	 */
62
	public static function new_instance( $props_n_values = array(), $timezone = null, $date_formats = array() ) {
63
		$has_object = parent::_check_for_object( $props_n_values, __CLASS__, $timezone, $date_formats );
0 ignored issues
show
Bug introduced by
It seems like $timezone defined by parameter $timezone on line 62 can also be of type string; however, EE_Base_Class::_check_for_object() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (_check_for_object() instead of new_instance()). Are you sure this is correct? If so, you might want to change this to $this->_check_for_object().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
64
		return $has_object ? $has_object : new self( $props_n_values, false, $timezone, $date_formats );
65
	}
66
67
68
69
	/**
70
	 * @param array $props_n_values  incoming values from the database
71
	 * @param string $timezone  incoming timezone as set by the model.  If not set the timezone for
72
	 *                          		the website will be used.
73
	 * @return EE_Registration
74
	 */
75
	public static function new_instance_from_db( $props_n_values = array(), $timezone = null ) {
76
		return new self( $props_n_values, TRUE, $timezone );
77
	}
78
79
80
81
	/**
82
	 *        Set Event ID
83
	 *
84
	 * @param        int $EVT_ID Event ID
85
	 */
86
	public function set_event( $EVT_ID = 0 ) {
87
		$this->set( 'EVT_ID', $EVT_ID );
88
	}
89
90
91
92
    /**
93
     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can be routed to internal methods
94
     *
95
     * @param string $field_name
96
     * @param mixed  $field_value
97
     * @param bool   $use_default
98
     * @throws \EE_Error
99
     * @throws \RuntimeException
100
     */
101
	public function set( $field_name, $field_value, $use_default = FALSE ) {
102
		switch( $field_name ) {
103
			case 'REG_code' :
104
				if ( ! empty( $field_value ) && $this->reg_code() === '' ) {
105
					$this->set_reg_code( $field_value, $use_default );
106
				}
107
				break;
108
			case 'STS_ID' :
109
				$this->set_status( $field_value, $use_default );
110
				break;
111
			default :
112
				parent::set( $field_name, $field_value, $use_default );
113
		}
114
	}
115
116
117
118
    /**
119
     * Set Status ID
120
     * updates the registration status and ALSO...
121
     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
122
     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
123
     *
124
     * @param string  $new_STS_ID
125
     * @param boolean $use_default
126
     * @return bool
127
     * @throws \RuntimeException
128
     * @throws \EE_Error
129
     */
130
	public function set_status( $new_STS_ID = NULL, $use_default = FALSE ) {
131
		// get current REG_Status
132
		$old_STS_ID = $this->status_ID();
133
		// if status has changed
134
		if (
135
			$old_STS_ID !== $new_STS_ID // and that status has actually changed
136
			&& ! empty( $old_STS_ID ) // and that old status is actually set
137
			&& ! empty( $new_STS_ID ) // as well as the new status
138
			&& $this->ID() // ensure registration is in the db
139
		) {
140
            // TO approved
141
			if ( $new_STS_ID === EEM_Registration::status_id_approved ) {
142
				// reserve a space by incrementing ticket and datetime sold values
143
				$this->_reserve_registration_space();
144
				do_action( 'AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID );
145
			// OR FROM  approved
146
			} else if ( $old_STS_ID === EEM_Registration::status_id_approved ) {
147
				// release a space by decrementing ticket and datetime sold values
148
				$this->_release_registration_space();
149
				do_action( 'AHEE__EE_Registration__set_status__from_approved', $this, $old_STS_ID, $new_STS_ID );
150
			}
151
            // update status
152
            parent::set('STS_ID', $new_STS_ID, $use_default);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (set() instead of set_status()). Are you sure this is correct? If so, you might want to change this to $this->set().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
153
            $this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID);
154
            /** @type EE_Transaction_Payments $transaction_payments */
155
            $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
156
            $transaction_payments->recalculate_transaction_total( $this->transaction(), false );
157
			$this->transaction()->update_status_based_on_total_paid( true );
158
			do_action( 'AHEE__EE_Registration__set_status__after_update', $this );
159
			return TRUE;
160
		} else {
161
			//even though the old value matches the new value, it's still good to
162
			//allow the parent set method to have a say
163
			parent::set( 'STS_ID', $new_STS_ID, $use_default );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (set() instead of set_status()). Are you sure this is correct? If so, you might want to change this to $this->set().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
164
			return TRUE;
165
		}
166
	}
167
168
169
170
    /**
171
     * update REGs and TXN when cancelled or declined registrations involved
172
     *
173
     * @param string $new_STS_ID
174
     * @param string $old_STS_ID
175
     * @throws \EE_Error
176
     */
177
    private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID)
178
    {
179
        // these reg statuses should not be considered in any calculations involving monies owing
180
        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
181
        // true if registration has been cancelled or declined
182 View Code Duplication
        if (
183
            in_array($new_STS_ID, $closed_reg_statuses, true)
184
            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
185
        ) {
186
            /** @type EE_Registration_Processor $registration_processor */
187
            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
188
            /** @type EE_Transaction_Processor $transaction_processor */
189
            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
190
            // cancelled or declined registration
191
            $registration_processor->update_registration_after_being_canceled_or_declined(
192
                $this,
193
                $closed_reg_statuses
194
            );
195
            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
196
                $this,
197
                $closed_reg_statuses,
198
                false
199
            );
200
            do_action('AHEE__EE_Registration__set_status__canceled_or_declined', $this, $old_STS_ID, $new_STS_ID);
201
            return;
202
        }
203
        // true if reinstating cancelled or declined registration
204 View Code Duplication
        if (
205
            in_array($old_STS_ID, $closed_reg_statuses, true)
206
            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
207
        ) {
208
            /** @type EE_Registration_Processor $registration_processor */
209
            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
210
            /** @type EE_Transaction_Processor $transaction_processor */
211
            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
212
            // reinstating cancelled or declined registration
213
            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
214
                $this,
215
                $closed_reg_statuses
216
            );
217
            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
218
                $this,
219
                $closed_reg_statuses,
220
                false
221
            );
222
            do_action('AHEE__EE_Registration__set_status__after_reinstated', $this, $old_STS_ID, $new_STS_ID);
223
        }
224
	}
225
226
227
	/**
228
	 *        get Status ID
229
	 */
230
	public function status_ID() {
231
		return $this->get( 'STS_ID' );
232
	}
233
234
235
236
    /**
237
     * increments this registration's related ticket sold and corresponding datetime sold values
238
     *
239
     * @return void
240
     * @throws \EE_Error
241
     */
242
	private function _reserve_registration_space() {
243
	    // reserved ticket and datetime counts will be decremented as sold counts are incremented
244
        // so stop tracking that this reg has a ticket reserved
245
	    $this->release_reserved_ticket();
246
		$ticket = $this->ticket();
247
		$ticket->increase_sold();
0 ignored issues
show
Documentation Bug introduced by
The method increase_sold does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
248
		$ticket->save();
249
		// possibly set event status to sold out
250
		$this->event()->perform_sold_out_status_check();
251
	}
252
253
254
255
    /**
256
     * Gets the ticket this registration is for
257
     *
258
     * @param boolean $include_archived whether to include archived tickets or not.
259
     * @return EE_Ticket|EE_Base_Class
260
     * @throws \EE_Error
261
     */
262
	public function ticket( $include_archived = TRUE ) {
263
		$query_params = array();
264
		if ( $include_archived ) {
265
			$query_params[ 'default_where_conditions' ] = 'none';
266
		}
267
		return $this->get_first_related( 'Ticket', $query_params );
268
	}
269
270
271
272
	/**
273
	 * Gets the event this registration is for
274
	 * @return EE_Event
275
	 */
276
	public function event() {
277
		$event = $this->get_first_related('Event');
278
		if ( ! $event instanceof \EE_Event) {
279
			throw new EntityNotFoundException('Event ID', $this->event_ID());
280
		}
281
		return $event;
282
	}
283
284
285
286
	/**
287
	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond with the author of the event this registration is for.
288
	 *
289
	 * @since 4.5.0
290
	 *
291
	 * @return int
292
	 */
293
	public function wp_user() {
294
		$event = $this->event();
295
		if ( $event instanceof EE_Event ) {
296
			return $event->wp_user();
297
		}
298
		return 0;
299
	}
300
301
302
303
    /**
304
     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
305
     *
306
     * @return void
307
     * @throws \EE_Error
308
     */
309
	private function _release_registration_space() {
310
		$ticket = $this->ticket();
311
		$ticket->decrease_sold();
0 ignored issues
show
Documentation Bug introduced by
The method decrease_sold does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
312
		$ticket->save();
313
	}
314
315
316
317
    /**
318
     * tracks this registration's ticket reservation in extra meta
319
     * and can increment related ticket reserved and corresponding datetime reserved values
320
     *
321
     * @param bool $update_ticket if true, will increment ticket and datetime reserved count
322
     * @return void
323
     * @throws \EE_Error
324
     */
325
	public function reserve_ticket($update_ticket = false) {
326
        if ($this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true, false) === false) {
327
            // PLZ NOTE: although checking $update_ticket first would be more efficient,
328
            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
329
            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true, false) && $update_ticket) {
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
330
                $ticket = $this->ticket();
331
                $ticket->increase_reserved();
0 ignored issues
show
Documentation Bug introduced by
The method increase_reserved does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
332
                $ticket->save();
333
            }
334
        }
335
	}
336
337
338
339
    /**
340
     * stops tracking this registration's ticket reservation in extra meta
341
     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
342
     *
343
     * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
344
     * @return void
345
     * @throws \EE_Error
346
     */
347
    public function release_reserved_ticket($update_ticket = false) {
348
        if ($this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true, false) !== false) {
349
            // PLZ NOTE: although checking $update_ticket first would be more efficient,
350
            // we NEED to ALWAYS call delete_extra_meta(), which is why that is done first
351
            if ($this->delete_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY) && $update_ticket) {
352
                $ticket = $this->ticket();
353
                $ticket->decrease_reserved();
0 ignored issues
show
Documentation Bug introduced by
The method decrease_reserved does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
354
                $ticket->save();
355
            }
356
        }
357
	}
358
359
360
361
	/**
362
	 * Set Attendee ID
363
	 *
364
	 * @param        int $ATT_ID Attendee ID
365
	 */
366
	public function set_attendee_id( $ATT_ID = 0 ) {
367
		$this->set( 'ATT_ID', $ATT_ID );
368
	}
369
370
371
372
	/**
373
	 *        Set Transaction ID
374
	 *
375
	 * @param        int $TXN_ID Transaction ID
376
	 */
377
	public function set_transaction_id( $TXN_ID = 0 ) {
378
		$this->set( 'TXN_ID', $TXN_ID );
379
	}
380
381
382
383
	/**
384
	 *        Set Session
385
	 *
386
	 * @param    string $REG_session PHP Session ID
387
	 */
388
	public function set_session( $REG_session = '' ) {
389
		$this->set( 'REG_session', $REG_session );
390
	}
391
392
393
394
	/**
395
	 *        Set Registration URL Link
396
	 *
397
	 * @param    string $REG_url_link Registration URL Link
398
	 */
399
	public function set_reg_url_link( $REG_url_link = '' ) {
400
		$this->set( 'REG_url_link', $REG_url_link );
401
	}
402
403
404
405
	/**
406
	 *        Set Attendee Counter
407
	 *
408
	 * @param        int $REG_count Primary Attendee
409
	 */
410
	public function set_count( $REG_count = 1 ) {
411
		$this->set( 'REG_count', $REG_count );
412
	}
413
414
415
416
	/**
417
	 *        Set Group Size
418
	 *
419
	 * @param        boolean $REG_group_size Group Registration
420
	 */
421
	public function set_group_size( $REG_group_size = FALSE ) {
422
		$this->set( 'REG_group_size', $REG_group_size );
423
	}
424
425
426
427
	/**
428
	 *    is_not_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_not_approved
429
	 *
430
	 * @return        boolean
431
	 */
432
	public function is_not_approved() {
433
		return $this->status_ID() == EEM_Registration::status_id_not_approved ? TRUE : FALSE;
434
	}
435
436
437
438
	/**
439
	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_pending_payment
440
	 *
441
	 * @return        boolean
442
	 */
443
	public function is_pending_payment() {
444
		return $this->status_ID() == EEM_Registration::status_id_pending_payment ? TRUE : FALSE;
445
	}
446
447
448
449
	/**
450
	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
451
	 *
452
	 * @return        boolean
453
	 */
454
	public function is_approved() {
455
		return $this->status_ID() == EEM_Registration::status_id_approved ? TRUE : FALSE;
456
	}
457
458
459
460
	/**
461
	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
462
	 *
463
	 * @return        boolean
464
	 */
465
	public function is_cancelled() {
466
		return $this->status_ID() == EEM_Registration::status_id_cancelled ? TRUE : FALSE;
467
	}
468
469
470
471
	/**
472
	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
473
	 *
474
	 * @return        boolean
475
	 */
476
	public function is_declined() {
477
		return $this->status_ID() == EEM_Registration::status_id_declined ? TRUE : FALSE;
478
	}
479
480
481
482
	/**
483
	 *    is_incomplete -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_incomplete
484
	 *
485
	 * @return        boolean
486
	 */
487
	public function is_incomplete() {
488
		return $this->status_ID() == EEM_Registration::status_id_incomplete ? TRUE : FALSE;
489
	}
490
491
492
493
	/**
494
	 *        Set Registration Date
495
	 *
496
	 * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of Date
497
	 */
498
	public function set_reg_date( $REG_date = FALSE ) {
499
		$this->set( 'REG_date', $REG_date );
500
	}
501
502
503
504
	/**
505
	 *    Set final price owing for this registration after all ticket/price modifications
506
	 *
507
	 * @access    public
508
	 * @param    float $REG_final_price
509
	 */
510
	public function set_final_price( $REG_final_price = 0.00 ) {
511
		$this->set( 'REG_final_price', $REG_final_price );
512
	}
513
514
515
516
	/**
517
	 *    Set amount paid towards this registration's final price
518
	 *
519
	 * @access    public
520
	 * @param    float $REG_paid
521
	 */
522
	public function set_paid( $REG_paid = 0.00 ) {
523
		$this->set( 'REG_paid', $REG_paid );
524
	}
525
526
527
528
	/**
529
	 *        Attendee Is Going
530
	 *
531
	 * @param        boolean $REG_att_is_going Attendee Is Going
532
	 */
533
	public function set_att_is_going( $REG_att_is_going = FALSE ) {
534
		$this->set( 'REG_att_is_going', $REG_att_is_going );
535
	}
536
537
538
539
	/**
540
	 * Gets the related attendee
541
	 * @return EE_Attendee
542
	 */
543
	public function attendee() {
544
		return $this->get_first_related( 'Attendee' );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->get_first_related('Attendee'); (EE_Base_Class) is incompatible with the return type declared by the interface EEI_Registration::attendee of type EEI_Attendee.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
545
	}
546
547
548
549
	/**
550
	 *        get Event ID
551
	 */
552
	public function event_ID() {
553
		return $this->get( 'EVT_ID' );
554
	}
555
556
557
558
	/**
559
	 *        get Event ID
560
	 */
561
	public function event_name() {
562
		$event = $this->event_obj();
563
		if ( $event ) {
564
			return $event->name();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $event->name(); (boolean) is incompatible with the return type declared by the interface EEI_Registration::event_name of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
565
		} else {
566
			return NULL;
567
		}
568
	}
569
570
571
572
	/**
573
	 * Fetches the event this registration is for
574
	 * @return EE_Event
575
	 */
576
	public function event_obj() {
577
		return $this->get_first_related( 'Event' );
578
	}
579
580
581
582
	/**
583
	 *        get Attendee ID
584
	 */
585
	public function attendee_ID() {
586
		return $this->get( 'ATT_ID' );
587
	}
588
589
590
591
	/**
592
	 *        get PHP Session ID
593
	 */
594
	public function session_ID() {
595
		return $this->get( 'REG_session' );
596
	}
597
598
599
600
	/**
601
	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
602
	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
603
	 * @return string
604
	 */
605
	public function receipt_url( $messenger = 'html' ) {
606
607
		/**
608
		 * The below will be deprecated one version after this.  We check first if there is a custom receipt template already in use on old system.  If there is then we just return the standard url for it.
609
		 *
610
		 * @since 4.5.0
611
		 */
612
		$template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
613
		$has_custom = EEH_Template::locate_template( $template_relative_path , array(), TRUE, TRUE, TRUE );
614
615
		if ( $has_custom ) {
616
			return add_query_arg( array( 'receipt' => 'true' ), $this->invoice_url( 'launch' ) );
617
		}
618
		return apply_filters( 'FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt' );
619
	}
620
621
622
623
624
	/**
625
	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
626
	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
627
	 * @return string
628
	 */
629
	public function invoice_url( $messenger = 'html' ) {
630
		/**
631
		 * The below will be deprecated one version after this.  We check first if there is a custom invoice template already in use on old system.  If there is then we just return the standard url for it.
632
		 *
633
		 * @since 4.5.0
634
		 */
635
		$template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
636
		$has_custom = EEH_Template::locate_template( $template_relative_path , array(), TRUE, TRUE, TRUE );
637
638
		if ( $has_custom ) {
639
			if ( $messenger == 'html' ) {
640
				return $this->invoice_url( 'launch' );
641
			}
642
			$route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
643
644
			$query_args = array( 'ee' => $route, 'id' => $this->reg_url_link() );
645
			if ( $messenger == 'html' ) {
646
				$query_args['html'] = TRUE;
647
			}
648
			return add_query_arg( $query_args, get_permalink( EE_Registry::instance()->CFG->core->thank_you_page_id ) );
649
		}
650
		return apply_filters( 'FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice' );
651
	}
652
653
654
655
	/**
656
	 * get Registration URL Link
657
	 *
658
	 * @access public
659
	 * @return string
660
	 * @throws \EE_Error
661
	 */
662
	public function reg_url_link() {
663
		return (string)$this->get( 'REG_url_link' );
664
	}
665
666
667
668
	/**
669
	 * Echoes out invoice_url()
670
	 * @param string $type 'download','launch', or 'html' (default is 'launch')
671
	 * @return void
672
	 */
673
	public function e_invoice_url( $type = 'launch' ) {
674
		echo $this->invoice_url( $type );
675
	}
676
677
678
679
	/**
680
	 * Echoes out payment_overview_url
681
	 */
682
	public function e_payment_overview_url() {
683
		echo $this->payment_overview_url();
684
	}
685
686
687
688
	/**
689
	 * Gets the URL of the thank you page with this registration REG_url_link added as
690
	 * a query parameter
691
	 * @return string
692
	 */
693
	public function payment_overview_url() {
694
		return add_query_arg( array( 'e_reg_url_link' => $this->reg_url_link(), 'step' => 'payment_options', 'revisit' => TRUE ), EE_Registry::instance()->CFG->core->reg_page_url() );
695
	}
696
697
698
699
	/**
700
	 * Gets the URL of the thank you page with this registration REG_url_link added as
701
	 * a query parameter
702
	 * @return string
703
	 */
704
	public function edit_attendee_information_url() {
705
		return add_query_arg( array( 'e_reg_url_link' => $this->reg_url_link(), 'step' => 'attendee_information', 'revisit' => TRUE ), EE_Registry::instance()->CFG->core->reg_page_url() );
706
	}
707
708
709
710
	/**
711
	 * Simply generates and returns the appropriate admin_url link to edit this registration
712
	 * @return string
713
	 */
714
	public function get_admin_edit_url() {
715
		return EEH_URL::add_query_args_and_nonce( array( 'page' => 'espresso_registrations', 'action' => 'view_registration', '_REG_ID' => $this->ID() ), admin_url( 'admin.php' ) );
716
	}
717
718
719
720
	/**
721
	 *    is_primary_registrant?
722
	 */
723
	public function is_primary_registrant() {
724
		return $this->get( 'REG_count' ) == 1 ? TRUE : FALSE;
725
	}
726
727
728
729
	/**
730
	 * This returns the primary registration object for this registration group (which may be this object).
731
	 * @return EE_Registration
732
	 */
733
	public function get_primary_registration()  {
734
		if ( $this->is_primary_registrant() )
735
			return $this;
736
737
		//k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
738
		$primary_registrant = EEM_Registration::instance()->get_one( array( array('TXN_ID' => $this->transaction_ID(), 'REG_count' => 1 ) ) );
739
		return $primary_registrant;
740
	}
741
742
743
744
	/**
745
	*		get  Attendee Number
746
	* 		@access		public
747
	*/
748
	public function count() {
749
		return $this->get( 'REG_count' );
750
	}
751
752
753
754
	/**
755
	 *        get Group Size
756
	 */
757
	public function group_size() {
758
		return $this->get( 'REG_group_size' );
759
	}
760
761
762
763
	/**
764
	 *        get Registration Date
765
	 */
766
	public function date() {
767
		return $this->get( 'REG_date' );
768
	}
769
770
771
772
	/**
773
	 * gets a pretty date
774
	 * @param string $date_format
775
	 * @param string $time_format
776
	 * @return string
777
	 */
778
	public function pretty_date( $date_format = NULL, $time_format = NULL ) {
779
		return $this->get_datetime( 'REG_date', $date_format, $time_format );
780
	}
781
782
783
784
	/**
785
	 * final_price
786
	 * the registration's share of the transaction total, so that the
787
	 * sum of all the transaction's REG_final_prices equal the transaction's total
788
	 * @return    float
789
	 */
790
	public function final_price() {
791
		return $this->get( 'REG_final_price' );
792
	}
793
794
795
796
	/**
797
	 * pretty_final_price
798
	 *  final price as formatted string, with correct decimal places and currency symbol
799
	 * @return string
800
	 */
801
	public function pretty_final_price() {
802
		return $this->get_pretty( 'REG_final_price' );
803
	}
804
805
806
807
	/**
808
	 * get paid (yeah)
809
	 * @return 	float
810
	 */
811
	public function paid() {
812
		return $this->get( 'REG_paid' );
813
	}
814
815
816
817
	/**
818
	 * pretty_paid
819
	 * @return 	float
820
	 */
821
	public function pretty_paid() {
822
		return $this->get_pretty( 'REG_paid' );
823
	}
824
825
826
827
	/**
828
	 * owes_monies_and_can_pay
829
	 * whether or not this registration has monies owing and it's' status allows payment
830
	 * @param array $requires_payment
831
	 * @return bool
832
	 */
833
	public function owes_monies_and_can_pay( $requires_payment = array()) {
834
		// these reg statuses require payment (if event is not free)
835
		$requires_payment = ! empty( $requires_payment ) ? $requires_payment : EEM_Registration::reg_statuses_that_allow_payment();
836
		if (
837
			in_array( $this->status_ID(), $requires_payment ) &&
838
			$this->final_price() != 0 &&
839
			$this->final_price() != $this->paid()
840
		) {
841
			return true;
842
		} else {
843
			return false;
844
		}
845
	}
846
847
848
849
	/**
850
	 * Prints out the return value of $this->pretty_status()
851
	 * @param bool $show_icons
852
	 * @return void
853
	 */
854
	public function e_pretty_status( $show_icons = FALSE ) {
855
		echo $this->pretty_status( $show_icons );
856
	}
857
858
859
860
861
	/**
862
	 * Returns a nice version of the status for displaying to customers
863
	 * @param bool $show_icons
864
	 * @return string
865
	 */
866
	public function pretty_status( $show_icons = FALSE ) {
867
		$status = EEM_Status::instance()->localized_status( array( $this->status_ID() => __( 'unknown', 'event_espresso' ) ), FALSE, 'sentence' );
868
		$icon = '';
869
		switch ( $this->status_ID() ) {
870
			case EEM_Registration::status_id_approved:
871
				$icon = $show_icons ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>' : '';
872
				break;
873
			case EEM_Registration::status_id_pending_payment:
874
				$icon = $show_icons ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>' : '';
875
				break;
876
			case EEM_Registration::status_id_not_approved:
877
				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>' : '';
878
				break;
879
			case EEM_Registration::status_id_cancelled:
880
				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>' : '';
881
				break;
882
			case EEM_Registration::status_id_incomplete:
883
				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>' : '';
884
				break;
885
			case EEM_Registration::status_id_declined:
886
				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
887
				break;
888
			case EEM_Registration::status_id_wait_list:
889
				$icon = $show_icons ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>' : '';
890
				break;
891
		}
892
		return $icon . $status[ $this->status_ID() ];
893
	}
894
895
896
897
	/**
898
	 *        get Attendee Is Going
899
	 */
900
	public function att_is_going() {
901
		return $this->get( 'REG_att_is_going' );
902
	}
903
904
905
906
	/**
907
	 * Gets related answers
908
	 * @param array $query_params like EEM_Base::get_all
909
	 * @return EE_Answer[]
910
	 */
911
	public function answers( $query_params = NULL ) {
912
		return $this->get_many_related( 'Answer', $query_params );
0 ignored issues
show
Bug introduced by
It seems like $query_params defined by parameter $query_params on line 911 can also be of type null; however, EE_Base_Class::get_many_related() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
913
	}
914
915
916
917
	/**
918
	 * Gets the registration's answer value to the specified question
919
	 * (either the question's ID or a question object)
920
	 * @param EE_Question|int $question
921
	 * @param bool            $pretty_value
922
	 * @return array|string if pretty_value= true, the result will always be a string
923
	 * (because the answer might be an array of answer values, so passing pretty_value=true
924
	 * will convert it into some kind of string)
925
	 */
926
	public function answer_value_to_question( $question, $pretty_value=true ) {
927
		$question_id = EEM_Question::instance()->ensure_is_ID($question);
928
		return EEM_Answer::instance()->get_answer_value_to_question($this,$question_id,$pretty_value);
929
	}
930
931
932
933
	/**
934
	 * question_groups
935
	 * returns an array of EE_Question_Group objects for this registration
936
	 *
937
	 * @return EE_Question_Group[]
938
	 */
939
	public function question_groups() {
940
		$question_groups = array();
941
		if ( $this->event() instanceof EE_Event ) {
942
			$question_groups = $this->event()->question_groups(
943
				array(
944
					array(
945
						'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false
946
					),
947
					'order_by' => array( 'QSG_order' => 'ASC' )
948
				)
949
			);
950
		}
951
		return $question_groups;
952
	}
953
954
955
956
	/**
957
	 * count_question_groups
958
	 * returns a count of the number of EE_Question_Group objects for this registration
959
	 *
960
	 * @return int
961
	 */
962
	public function count_question_groups() {
963
		$qg_count = 0;
964
		if ( $this->event() instanceof EE_Event ) {
965
			$qg_count = $this->event()->count_related(
966
				'Question_Group',
967
				array(
968
					array(
969
						'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false
970
					)
971
				)
972
			);
973
		}
974
		return $qg_count;
975
	}
976
977
978
979
	/**
980
	 * Returns the registration date in the 'standard' string format
981
	 * (function may be improved in the future to allow for different formats and timezones)
982
	 * @return string
983
	 */
984
	public function reg_date() {
985
		return $this->get_datetime( 'REG_date' );
986
	}
987
988
989
990
	/**
991
	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
992
	 * the ticket this registration purchased, or the datetime they have registered
993
	 * to attend)
994
	 * @return EE_Datetime_Ticket
995
	 */
996
	public function datetime_ticket() {
997
		return $this->get_first_related( 'Datetime_Ticket' );
998
	}
999
1000
1001
1002
	/**
1003
	 * Sets the registration's datetime_ticket.
1004
	 * @param EE_Datetime_Ticket $datetime_ticket
1005
	 * @return EE_Datetime_Ticket
1006
	 */
1007
	public function set_datetime_ticket( $datetime_ticket ) {
1008
		return $this->_add_relation_to( $datetime_ticket, 'Datetime_Ticket' );
1009
	}
1010
	/**
1011
	 * Gets deleted
1012
	 * @return boolean
1013
	 */
1014
	public function deleted() {
1015
		return $this->get( 'REG_deleted' );
1016
	}
1017
1018
	/**
1019
	 * Sets deleted
1020
	 * @param boolean $deleted
1021
	 * @return boolean
1022
	 */
1023
	public function set_deleted($deleted) {
1024
	    if ( $deleted ) {
1025
	        $this->delete();
1026
        } else {
1027
	        $this->restore();
1028
        }
1029
	}
1030
1031
1032
1033
	/**
1034
	 * Get the status object of this object
1035
	 * @return EE_Status
1036
	 */
1037
	public function status_obj() {
1038
		return $this->get_first_related( 'Status' );
1039
	}
1040
1041
1042
1043
	/**
1044
	 * Returns the number of times this registration has checked into any of the datetimes
1045
	 * its available for
1046
	 * @return int
1047
	 */
1048
	public function count_checkins() {
1049
		return $this->get_model()->count_related( $this, 'Checkin' );
1050
	}
1051
1052
1053
1054
	/**
1055
	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the registration is for.  Note, this is ONLY checked in (does not include checkedout)
1056
	 * @return int
1057
	 */
1058
	public function count_checkins_not_checkedout() {
1059
		return $this->get_model()->count_related( $this, 'Checkin', array( array( 'CHK_in' => 1 ) ) );
1060
	}
1061
1062
1063
1064
	/**
1065
	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1066
	 *
1067
	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1068
	 * @param bool   $check_approved   This is used to indicate whether the caller wants can_checkin to also consider registration status as well as datetime access.
1069
	 *
1070
	 * @return bool
1071
	 */
1072
	public function can_checkin( $DTT_OR_ID, $check_approved = TRUE ) {
1073
		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID( $DTT_OR_ID );
1074
1075
		//first check registration status
1076
		if (  ( $check_approved && ! $this->is_approved() ) || ! $DTT_ID ) {
1077
			return false;
1078
		}
1079
		//is there a datetime ticket that matches this dtt_ID?
1080
		if ( ! ( EEM_Datetime_Ticket::instance()->exists( array( array( 'TKT_ID' => $this->get('TKT_ID' ), 'DTT_ID' => $DTT_ID ) ) ) ) ) {
1081
			return false;
1082
		}
1083
1084
		//final check is against TKT_uses
1085
		return $this->verify_can_checkin_against_TKT_uses( $DTT_ID );
1086
	}
1087
1088
1089
	/**
1090
	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on the ticket.
1091
	 *
1092
	 * To do this,  a query is done to get the count of the datetime records already checked into.  If the datetime given does
1093
	 * not have a check-in record and checking in for that datetime will exceed the allowed uses, then return false.  Otherwise return true.
1094
	 *
1095
	 * @param int | EE_Datetime  $DTT_OR_ID  The datetime the registration is being checked against
1096
	 * @return bool   true means can checkin.  false means cannot checkin.
1097
	 */
1098
	public function verify_can_checkin_against_TKT_uses( $DTT_OR_ID ) {
1099
		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID( $DTT_OR_ID );
1100
1101
		if ( ! $DTT_ID ) {
1102
			return false;
1103
		}
1104
1105
		$max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1106
1107
		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can check-in
1108
		// or not.
1109
		if ( ! $max_uses || $max_uses === EE_INF ) {
1110
			return true;
1111
		}
1112
1113
		//does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1114
		//go ahead and toggle.
1115
		if ( EEM_Checkin::instance()->exists( array( array( 'REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID ) ) ) ) {
1116
			return true;
1117
		}
1118
1119
		//made it here so the last check is whether the number of checkins per unique datetime on this registration
1120
		//disallows further check-ins.
1121
		$count_unique_dtt_checkins = EEM_Checkin::instance()->count( array( array( 'REG_ID' => $this->ID(), 'CHK_in' => true ) ), 'DTT_ID', true );
1122
		// checkins have already reached their max number of uses
1123
		// so registrant can NOT checkin
1124
		if ( $count_unique_dtt_checkins >= $max_uses ) {
1125
			EE_Error::add_error( __( 'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.', 'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
1126
			return false;
1127
		}
1128
		return true;
1129
	}
1130
1131
1132
1133
    /**
1134
     * toggle Check-in status for this registration
1135
     * Check-ins are toggled in the following order:
1136
     * never checked in -> checked in
1137
     * checked in -> checked out
1138
     * checked out -> checked in
1139
     *
1140
     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1141
     *                      If not included or null, then it is assumed latest datetime is being toggled.
1142
     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1143
     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1144
     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1145
     * @throws EE_Error
1146
     */
1147
	public function toggle_checkin_status( $DTT_ID = null, $verify = false ) {
1148
		if ( empty( $DTT_ID ) ) {
1149
			$datetime = $this->get_latest_related_datetime();
1150
			$DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1151
		// verify the registration can checkin for the given DTT_ID
1152
		} elseif ( ! $this->can_checkin( $DTT_ID, $verify ) ) {
1153
			EE_Error::add_error(
1154
					sprintf(
1155
						__( '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', 'event_espresso'),
1156
						$this->ID(),
1157
						$DTT_ID
1158
					),
1159
					__FILE__, __FUNCTION__, __LINE__
1160
			);
1161
			return false;
1162
		}
1163
		$status_paths = array(
1164
			EE_Registration::checkin_status_never => EE_Registration::checkin_status_in,
1165
			EE_Registration::checkin_status_in => EE_Registration::checkin_status_out,
1166
			EE_Registration::checkin_status_out => EE_Registration::checkin_status_in
1167
		);
1168
		//start by getting the current status so we know what status we'll be changing to.
1169
		$cur_status = $this->check_in_status_for_datetime( $DTT_ID, NULL );
1170
		$status_to = $status_paths[ $cur_status ];
1171
		// database only records true for checked IN or false for checked OUT
1172
		// no record ( null ) means checked in NEVER, but we obviously don't save that
1173
		$new_status = $status_to === EE_Registration::checkin_status_in ? true : false;
1174
		// add relation - note Check-ins are always creating new rows
1175
		// because we are keeping track of Check-ins over time.
1176
		// Eventually we'll probably want to show a list table
1177
		// for the individual Check-ins so that they can be managed.
1178
		$checkin = EE_Checkin::new_instance( array(
1179
				'REG_ID' => $this->ID(),
1180
				'DTT_ID' => $DTT_ID,
1181
				'CHK_in' => $new_status
1182
		) );
1183
		// if the record could not be saved then return false
1184
		if ( $checkin->save() === 0 ) {
1185
			if ( WP_DEBUG ) {
1186
				global $wpdb;
1187
				$error = sprintf(
1188
					__( 'Registration check in update failed because of the following database error: %1$s%2$s', 'event_espresso' ),
1189
					'<br />',
1190
					$wpdb->last_error
1191
				);
1192
			} else {
1193
				$error = __( 'Registration check in update failed because of an unknown database error', 'event_espresso' );
1194
			}
1195
			EE_Error::add_error( $error, __FILE__, __FUNCTION__, __LINE__ );
1196
			return false;
1197
		}
1198
		return $status_to;
1199
	}
1200
1201
1202
1203
    /**
1204
     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1205
     * "Latest" is defined by the `DTT_EVT_start` column.
1206
     *
1207
     * @return EE_Datetime|null
1208
     * @throws \EE_Error
1209
     */
1210 View Code Duplication
	public function get_latest_related_datetime() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1211
		return EEM_Datetime::instance()->get_one(
1212
			array(
1213
				array(
1214
					'Ticket.Registration.REG_ID' => $this->ID()
1215
				),
1216
				'order_by' => array( 'DTT_EVT_start' => 'DESC' )
1217
			)
1218
		);
1219
	}
1220
1221
1222
1223
    /**
1224
     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1225
     * "Earliest" is defined by the `DTT_EVT_start` column.
1226
     *
1227
     * @throws \EE_Error
1228
     */
1229 View Code Duplication
	public function get_earliest_related_datetime() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1230
		return EEM_Datetime::instance()->get_one(
1231
			array(
1232
				array(
1233
					'Ticket.Registration.REG_ID' => $this->ID()
1234
				),
1235
				'order_by' => array( 'DTT_EVT_start' => 'ASC' )
1236
			)
1237
		);
1238
	}
1239
1240
1241
1242
    /**
1243
     * This method simply returns the check-in status for this registration and the given datetime.
1244
     * If neither the datetime nor the checkin values are provided as arguments,
1245
     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1246
     *
1247
     * @param  int $DTT_ID        The ID of the datetime we're checking against
1248
     *                            (if empty we'll get the primary datetime for
1249
     *                            this registration (via event) and use it's ID);
1250
     * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1251
     * @return int                Integer representing Check-in status.
1252
     * @throws \EE_Error
1253
     */
1254
	public function check_in_status_for_datetime( $DTT_ID = 0, $checkin = null ) {
1255
		$checkin_query_params = array(
1256
			'order_by' => array( 'CHK_timestamp' => 'DESC' )
1257
		);
1258
1259
		if ( $DTT_ID > 0 ) {
1260
			$checkin_query_params[0] = array( 'DTT_ID' => $DTT_ID );
1261
		}
1262
1263
		//get checkin object (if exists)
1264
		$checkin = $checkin instanceof EE_Checkin
1265
            ? $checkin
1266
            : $this->get_first_related( 'Checkin', $checkin_query_params );
1267
		if ( $checkin instanceof EE_Checkin ) {
1268
			if ( $checkin->get( 'CHK_in' ) ) {
1269
				return EE_Registration::checkin_status_in; //checked in
1270
			}
1271
			return EE_Registration::checkin_status_out; //had checked in but is now checked out.
1272
		}
1273
		return EE_Registration::checkin_status_never; //never been checked in
1274
	}
1275
1276
1277
1278
	/**
1279
	 * This method returns a localized message for the toggled Check-in message.
1280
	 * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null, then it is assumed Check-in for primary datetime was toggled.
1281
	 * @param bool $error  This just flags that you want an error message returned. This is put in so that the error message can be customized with the attendee name.
1282
	 * @return string         internationalized message
1283
	 */
1284
	public function get_checkin_msg( $DTT_ID, $error = FALSE ) {
1285
		//let's get the attendee first so we can include the name of the attendee
1286
		$attendee = $this->get_first_related( 'Attendee' );
1287
		if ( $attendee instanceof EE_Attendee ) {
1288
			if ( $error ) {
1289
				return sprintf( __( "%s's check-in status was not changed.", "event_espresso" ), $attendee->full_name() );
1290
			}
1291
			$cur_status = $this->check_in_status_for_datetime( $DTT_ID );
1292
			//what is the status message going to be?
1293
			switch ( $cur_status ) {
1294
				case EE_Registration::checkin_status_never :
1295
					return sprintf( __( "%s has been removed from Check-in records", "event_espresso" ), $attendee->full_name() );
1296
					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...
1297
				case EE_Registration::checkin_status_in :
1298
					return sprintf( __( '%s has been checked in', 'event_espresso' ), $attendee->full_name() );
1299
					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...
1300
				case EE_Registration::checkin_status_out :
1301
					return sprintf( __( '%s has been checked out', 'event_espresso' ), $attendee->full_name() );
1302
					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...
1303
			}
1304
		}
1305
		return __( "The check-in status could not be determined.", "event_espresso" );
1306
	}
1307
1308
1309
1310
	/**
1311
	 * Returns the related EE_Transaction to this registration
1312
	 * @return EE_Transaction
1313
	 */
1314
	public function transaction() {
1315
		$transaction = $this->get_first_related('Transaction');
1316
		if ( ! $transaction instanceof \EE_Transaction) {
1317
			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1318
		}
1319
		return $transaction;
1320
	}
1321
1322
1323
1324
1325
	/**
1326
	 *        get Registration Code
1327
	 */
1328
	public function reg_code() {
1329
		return $this->get( 'REG_code' );
1330
	}
1331
1332
1333
1334
	/**
1335
	 *        get Transaction ID
1336
	 */
1337
	public function transaction_ID() {
1338
		return $this->get( 'TXN_ID' );
1339
	}
1340
1341
1342
1343
	/**
1344
	 * @return int
1345
	 */
1346
	public function ticket_ID() {
1347
		return $this->get( 'TKT_ID' );
1348
	}
1349
1350
1351
1352
	/**
1353
	 *        Set Registration Code
1354
	 *
1355
	 * @access    public
1356
	 * @param    string $REG_code Registration Code
1357
	 * @param	boolean $use_default
1358
	 */
1359
	public function set_reg_code( $REG_code, $use_default = FALSE ) {
1360
		if ( empty( $REG_code )) {
1361
			EE_Error::add_error( __( 'REG_code can not be empty.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__ );
1362
			return;
1363
		}
1364
		if ( ! $this->reg_code() ) {
1365
			parent::set( 'REG_code', $REG_code, $use_default );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (set() instead of set_reg_code()). Are you sure this is correct? If so, you might want to change this to $this->set().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
1366
		} else {
1367
			EE_Error::doing_it_wrong(
1368
				__CLASS__ . '::' . __FUNCTION__,
1369
				__( 'Can not change a registration REG_code once it has been set.', 'event_espresso' ),
1370
				'4.6.0'
1371
			);
1372
		}
1373
	}
1374
1375
1376
1377
1378
	/**
1379
	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
1380
	 *
1381
	 * Note, if you want to just get all registrations in the same transaction (group), use:
1382
	 * 	$registration->transaction()->registrations();
1383
	 *
1384
	 * @since 4.5.0
1385
	 *
1386
	 * @return EE_Registration[]  or empty array if this isn't a group registration.
1387
	 */
1388
	public function get_all_other_registrations_in_group() {
1389
		if ( $this->group_size() < 2 ) {
1390
			return array();
1391
		}
1392
1393
		$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...
1394
			'TXN_ID' => $this->transaction_ID(),
1395
			'REG_ID' => array( '!=', $this->ID() ),
1396
			'TKT_ID' => $this->ticket_ID()
1397
			);
1398
1399
		$registrations = $this->get_model()->get_all( $query );
1400
		return $registrations;
1401
	}
1402
1403
	/**
1404
	 * Return the link to the admin details for the object.
1405
	 * @return string
1406
	 */
1407 View Code Duplication
	public function get_admin_details_link() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1408
		EE_Registry::instance()->load_helper( 'URL' );
1409
		return EEH_URL::add_query_args_and_nonce(
1410
			array(
1411
				'page' => 'espresso_registrations',
1412
				'action' => 'view_registration',
1413
				'_REG_ID' => $this->ID()
1414
			),
1415
			admin_url( 'admin.php' )
1416
		);
1417
	}
1418
1419
	/**
1420
	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1421
	 * @return string
1422
	 */
1423
	public function get_admin_edit_link() {
1424
		return $this->get_admin_details_link();
1425
	}
1426
1427
	/**
1428
	 * Returns the link to a settings page for the object.
1429
	 * @return string
1430
	 */
1431
	public function get_admin_settings_link() {
1432
		return $this->get_admin_details_link();
1433
	}
1434
1435
	/**
1436
	 * Returns the link to the "overview" for the object (typically the "list table" view).
1437
	 * @return string
1438
	 */
1439
	public function get_admin_overview_link() {
1440
		EE_Registry::instance()->load_helper( 'URL' );
1441
		return EEH_URL::add_query_args_and_nonce(
1442
			array(
1443
				'page' => 'espresso_registrations'
1444
			),
1445
			admin_url( 'admin.php' )
1446
		);
1447
	}
1448
1449
1450
1451
	/**
1452
	 * @param array $query_params
1453
	 * @return \EE_Registration[]
1454
	 * @throws \EE_Error
1455
	 */
1456
	public function payments( $query_params = array() ) {
1457
		return $this->get_many_related( 'Payment', $query_params );
1458
	}
1459
1460
1461
1462
	/**
1463
	 * @param array $query_params
1464
	 * @return \EE_Registration_Payment[]
1465
	 * @throws \EE_Error
1466
	 */
1467
	public function registration_payments( $query_params = array() ) {
1468
		return $this->get_many_related( 'Registration_Payment', $query_params );
1469
	}
1470
1471
1472
1473
1474
	/**
1475
	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1476
	 * Note: if there are no payments on the registration there will be no payment method returned.
1477
	 *
1478
	 * @return EE_Payment_Method|null
1479
	 */
1480
	public function payment_method() {
1481
		return EEM_Payment_Method::instance()->get_last_used_for_registration( $this );
1482
	}
1483
1484
1485
1486
	/**
1487
	 * @return \EE_Line_Item
1488
	 * @throws EntityNotFoundException
1489
	 * @throws \EE_Error
1490
	 */
1491
	public function ticket_line_item()
1492
	{
1493
		$ticket = $this->ticket();
1494
		$transaction = $this->transaction();
1495
		$line_item = null;
1496
		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1497
			$transaction->total_line_item(),
1498
			'Ticket',
1499
			array($ticket->ID())
1500
		);
1501 View Code Duplication
		foreach ($ticket_line_items as $ticket_line_item) {
1502
			if (
1503
				$ticket_line_item instanceof \EE_Line_Item
1504
				&& $ticket_line_item->OBJ_type() === 'Ticket'
1505
				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
1506
			) {
1507
				$line_item = $ticket_line_item;
1508
				break;
1509
			}
1510
		}
1511 View Code Duplication
		if ( ! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1512
			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1513
		}
1514
		return $line_item;
1515
	}
1516
1517
1518
1519
    /**
1520
     * Soft Deletes this model object.
1521
     *
1522
     * @return boolean | int
1523
     * @throws \RuntimeException
1524
     * @throws \EE_Error
1525
     */
1526
    public function delete()
1527
    {
1528
        if($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1529
            $this->set_status(EEM_Registration::status_id_cancelled);
1530
        }
1531
        return parent::delete();
1532
    }
1533
1534
1535
1536
    /**
1537
     * Restores whatever the previous status was on a registration before it was trashed (if possible)
1538
     *
1539
     * @throws \EE_Error
1540
     * @throws \RuntimeException
1541
     */
1542
    public function restore()
1543
    {
1544
        $previous_status = $this->get_extra_meta(
1545
            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1546
            true,
1547
            EEM_Registration::status_id_cancelled
1548
        );
1549
        if ($previous_status) {
1550
            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1551
            $this->set_status($previous_status);
1552
        }
1553
        return parent::restore();
1554
    }
1555
1556
1557
1558
    /*************************** DEPRECATED ***************************/
1559
1560
1561
1562
	/**
1563
	 * @deprecated
1564
	 * @since     4.7.0
1565
	 * @access    public
1566
	 */
1567
	public function price_paid()
1568
	{
1569
		EE_Error::doing_it_wrong('EE_Registration::price_paid()',
1570
			__('This method is deprecated, please use EE_Registration::final_price() instead.', 'event_espresso'),
1571
			'4.7.0');
1572
		return $this->final_price();
1573
	}
1574
1575
1576
1577
	/**
1578
	 * @deprecated
1579
	 * @since     4.7.0
1580
	 * @access    public
1581
	 * @param    float $REG_final_price
1582
	 */
1583
	public function set_price_paid($REG_final_price = 0.00)
1584
	{
1585
		EE_Error::doing_it_wrong('EE_Registration::set_price_paid()',
1586
			__('This method is deprecated, please use EE_Registration::set_final_price() instead.', 'event_espresso'),
1587
			'4.7.0');
1588
		$this->set_final_price($REG_final_price);
1589
	}
1590
1591
1592
1593
	/**
1594
	 * @deprecated
1595
	 * @since 4.7.0
1596
	 * @return string
1597
	 */
1598
	public function pretty_price_paid()
1599
	{
1600
		EE_Error::doing_it_wrong('EE_Registration::pretty_price_paid()',
1601
			__('This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
1602
				'event_espresso'), '4.7.0');
1603
		return $this->pretty_final_price();
1604
	}
1605
1606
1607
1608
    /**
1609
     * Gets the primary datetime related to this registration via the related Event to this registration
1610
     *
1611
     * @deprecated 4.9.17
1612
     * @return EE_Datetime
1613
     */
1614
    public function get_related_primary_datetime() {
1615
        EE_Error::doing_it_wrong(
1616
            __METHOD__,
1617
            esc_html__(
1618
                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
1619
                'event_espresso'
1620
            ),
1621
            '4.9.17',
1622
            '5.0.0'
1623
        );
1624
        return $this->event()->primary_datetime();
1625
    }
1626
1627
1628
1629
}
1630
/* End of file EE_Registration.class.php */
1631
/* Location: includes/classes/EE_Registration.class.php */
1632