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

EE_Transaction::pending_payments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
<?php if ( ! defined( 'EVENT_ESPRESSO_VERSION' ) ) {
2
	exit( 'No direct script access allowed' );
3
}
4
/**
5
 * EE_Transaction class
6
 *
7
 * @package     Event Espresso
8
 * @subpackage 	includes/classes/EE_Transaction.class.php
9
 * @author      Brent Christensen
10
 */
11
class EE_Transaction extends EE_Base_Class implements EEI_Transaction {
12
13
	/**
14
	 * The length of time in seconds that a lock is applied before being considered expired.
15
	 * It is not long because a transaction should only be locked for the duration of the request that locked it
16
	 */
17
	const LOCK_EXPIRATION = 2;
18
19
	/**
20
	 * txn status upon initial construction.
21
	 *
22
	 * @var string
23
	 */
24
	protected $_old_txn_status;
25
26
27
28
	/**
29
	 * @param array  $props_n_values          incoming values
30
	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
31
	 *                                        used.)
32
	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
33
	 *                                        date_format and the second value is the time format
34
	 * @return EE_Transaction
35
	 * @throws \EE_Error
36
	 */
37
	public static function new_instance( $props_n_values = array(), $timezone = null, $date_formats = array() ) {
38
		$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 37 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...
39
		$txn = $has_object
40
			? $has_object
41
			: new self( $props_n_values, false, $timezone, $date_formats );
42
		if ( ! $has_object ) {
43
			$txn->set_old_txn_status( $txn->status_ID() );
44
		}
45
		return $txn;
46
	}
47
48
49
50
	/**
51
	 * @param array  $props_n_values  incoming values from the database
52
	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
53
	 *                                the website will be used.
54
	 * @return EE_Transaction
55
	 * @throws \EE_Error
56
	 */
57
	public static function new_instance_from_db( $props_n_values = array(), $timezone = null ) {
58
		$txn = new self( $props_n_values, TRUE, $timezone );
59
		$txn->set_old_txn_status( $txn->status_ID() );
60
		return $txn;
61
	}
62
63
64
65
	/**
66
	 * lock
67
	 * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
68
	 * If a lock has already been set, then we will attempt to remove it in case it has expired.
69
	 * If that also fails, then an exception is thrown.
70
	 *
71
	 * @access public
72
	 * @throws \EE_Error
73
	 */
74
	public function lock() {
75
		// attempt to set lock, but if that fails...
76
		if ( ! $this->add_extra_meta( 'lock', time(), true )  ) {
77
			// then attempt to remove the lock in case it is expired
78
			if ( $this->_remove_expired_lock() ) {
79
				// if removal was successful, then try setting lock again
80
				$this->lock();
81
			} else {
82
				// but if the lock can not be removed, then throw an exception
83
				throw new EE_Error(
84
					sprintf(
85
						__( 'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.', 'event_espresso' ),
86
						$this->ID()
87
					)
88
				);
89
			}
90
		}
91
	}
92
93
94
95
	/**
96
	 * unlock
97
	 * removes transaction lock applied in EE_Transaction::lock()
98
	 *
99
	 * @access public
100
	 * @return int
101
	 * @throws \EE_Error
102
	 */
103
	public function unlock() {
104
		return $this->delete_extra_meta( 'lock' );
105
	}
106
107
108
109
	/**
110
	 * is_locked
111
	 * Decides whether or not now is the right time to update the transaction.
112
	 * This is useful because we don't always know if it is safe to update the transaction
113
	 * and its related data. why?
114
	 * because it's possible that the transaction is being used in another
115
	 * request and could overwrite anything we save.
116
	 * So we want to only update the txn once we know that won't happen.
117
	 * We also check that the lock isn't expired, and remove it if it is
118
	 *
119
	 * @access public
120
	 * @return boolean
121
	 * @throws \EE_Error
122
	 */
123
	public function is_locked() {
124
		// if TXN is not locked, then return false immediately
125
		if ( ! $this->_get_lock() ) {
126
			return false;
127
		}
128
		// if not, then let's try and remove the lock in case it's expired...
129
		// _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
130
		// and a positive number if the lock was removed (ie: number of locks deleted),
131
		// so we need to return the opposite
132
		return ! $this->_remove_expired_lock() ? true : false;
133
	}
134
135
136
137
	/**
138
	 * _get_lock
139
	 * Gets the meta field indicating that this TXN is locked
140
	 *
141
	 * @access protected
142
	 * @return int
143
	 * @throws \EE_Error
144
	 */
145
	protected function _get_lock() {
146
		return (int)$this->get_extra_meta( 'lock', true, 0 );
147
	}
148
149
150
151
	/**
152
	 * remove_expired_lock
153
	 * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
154
	 *
155
	 * @access public
156
	 * @return int
157
	 * @throws \EE_Error
158
	 */
159
	protected function _remove_expired_lock() {
160
		$locked = $this->_get_lock();
161
		if ( $locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked ) {
162
			return $this->unlock();
163
		}
164
		return 0;
165
	}
166
167
168
169
	/**
170
	 *        Set transaction total
171
	 *
172
	 * @access        public
173
	 * @param        float $total total value of transaction
174
	 * @throws \EE_Error
175
	 */
176
	public function set_total( $total = 0.00 ) {
177
		$this->set( 'TXN_total', (float)$total );
178
	}
179
180
181
182
	/**
183
	 *        Set Total Amount Paid to Date
184
	 *
185
	 * @access        public
186
	 * @param        float $total_paid total amount paid to date (sum of all payments)
187
	 * @throws \EE_Error
188
	 */
189
	public function set_paid( $total_paid = 0.00 ) {
190
		$this->set( 'TXN_paid', (float)$total_paid );
191
	}
192
193
194
195
	/**
196
	 *        Set transaction status
197
	 *
198
	 * @access        public
199
	 * @param        string $status whether the transaction is open, declined, accepted, or any number of custom values that can be set
200
	 * @throws \EE_Error
201
	 */
202
	public function set_status( $status = '' ) {
203
		$this->set( 'STS_ID', $status );
204
	}
205
206
207
208
	/**
209
	 *        Set hash salt
210
	 *
211
	 * @access        public
212
	 * @param        string $hash_salt required for some payment gateways
213
	 * @throws \EE_Error
214
	 */
215
	public function set_hash_salt( $hash_salt = '' ) {
216
		$this->set( 'TXN_hash_salt', $hash_salt );
217
	}
218
219
220
221
	/**
222
	 * Sets TXN_reg_steps array
223
	 *
224
	 * @param array $txn_reg_steps
225
	 * @throws \EE_Error
226
	 */
227
	public function set_reg_steps( array $txn_reg_steps ) {
228
		$this->set( 'TXN_reg_steps', $txn_reg_steps );
229
	}
230
231
232
233
	/**
234
	 * Gets TXN_reg_steps
235
	 *
236
	 * @return array
237
	 * @throws \EE_Error
238
	 */
239
	public function reg_steps() {
240
		$TXN_reg_steps = $this->get( 'TXN_reg_steps' );
241
		return is_array( $TXN_reg_steps ) ? (array)$TXN_reg_steps : array();
242
	}
243
244
245
246
	/**
247
	 * @return string of transaction's total cost, with currency symbol and decimal
248
	 * @throws \EE_Error
249
	 */
250
	public function pretty_total() {
251
		return $this->get_pretty( 'TXN_total' );
252
	}
253
254
255
256
	/**
257
	 * Gets the amount paid in a pretty string (formatted and with currency symbol)
258
	 *
259
	 * @return string
260
	 * @throws \EE_Error
261
	 */
262
	public function pretty_paid() {
263
		return $this->get_pretty( 'TXN_paid' );
264
	}
265
266
267
268
	/**
269
	 * calculate the amount remaining for this transaction and return;
270
	 *
271
	 * @access public
272
	 * @return float amount remaining
273
	 * @throws \EE_Error
274
	 */
275
	public function remaining() {
276
		return (float)( $this->total() - $this->paid() );
277
	}
278
279
280
281
	/**
282
	 *        get Transaction Total
283
	 *
284
	 * @access        public
285
	 * @return float
286
	 * @throws \EE_Error
287
	 */
288
	public function total() {
289
		return (float)$this->get( 'TXN_total' );
290
	}
291
292
293
294
	/**
295
	 *        get Total Amount Paid to Date
296
	 *
297
	 * @access        public
298
	 * @return float
299
	 * @throws \EE_Error
300
	 */
301
	public function paid() {
302
		return (float)$this->get( 'TXN_paid' );
303
	}
304
305
306
307
	/**
308
	 *    get_cart_session
309
	 *
310
	 * @access        public
311
	 * @throws \EE_Error
312
	 */
313
	public function get_cart_session() {
314
		$session_data = (array)$this->get( 'TXN_session_data' );
315
		return isset( $session_data[ 'cart' ] ) && $session_data[ 'cart' ] instanceof EE_Cart
316
			? $session_data[ 'cart' ]
317
			: null;
318
	}
319
320
321
322
	/**
323
	 *        get Transaction session data
324
	 *
325
	 * @access        public
326
	 * @throws \EE_Error
327
	 */
328
	public function session_data() {
329
		$session_data = $this->get( 'TXN_session_data' );
330
		if ( empty( $session_data ) ) {
331
			$session_data = array(
332
				'id'            => null,
333
				'user_id'       => null,
334
				'ip_address'    => null,
335
				'user_agent'    => null,
336
				'init_access'   => null,
337
				'last_access'   => null,
338
				'pages_visited' => array()
339
			);
340
		}
341
		return $session_data;
342
	}
343
344
345
346
	/**
347
	 *        Set session data within the TXN object
348
	 *
349
	 * @access        public
350
	 * @param        EE_Session|array $session_data
351
	 * @throws \EE_Error
352
	 */
353
	public function set_txn_session_data( $session_data ) {
354
		if ( $session_data instanceof EE_Session ) {
355
			$this->set( 'TXN_session_data', $session_data->get_session_data( NULL, TRUE ));
356
		} else {
357
			$this->set( 'TXN_session_data', $session_data );
358
		}
359
	}
360
361
362
363
	/**
364
	 *        get Transaction hash salt
365
	 *
366
	 * @access        public
367
	 * @throws \EE_Error
368
	 */
369
	public function hash_salt_() {
370
		return $this->get( 'TXN_hash_salt' );
371
	}
372
373
374
375
	/**
376
	 *    datetime
377
	 *    Returns the transaction datetime as either:
378
	 *            - unix timestamp format ($format = false, $gmt = true)
379
	 *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
380
	 *              has no affect with this option)), this also may include a timezone abbreviation if the
381
	 *              set timezone in this class differs from what the timezone is on the blog.
382
	 *            - formatted date string including the UTC (timezone) offset (default).
383
	 *
384
	 * @access    public
385
	 * @param    boolean $format - whether to return a unix timestamp (default) or formatted date string
386
	 * @param    boolean $gmt    - whether to return a unix timestamp with UTC offset applied (default) or no UTC offset applied
387
	 * @return    string | int
388
	 * @throws \EE_Error
389
	 */
390
	public function datetime( $format = FALSE, $gmt = FALSE ) {
391
		if ( $format ) {
392
			return $this->get_pretty( 'TXN_timestamp' );
393
		} else if ( $gmt ) {
394
			return $this->get_raw( 'TXN_timestamp' );
395
		} else {
396
			return $this->get( 'TXN_timestamp' );
397
		}
398
	}
399
400
401
402
	/**
403
	 *    Gets registrations on this transaction
404
	 *
405
	 * @param        array   $query_params array of query parameters
406
	 * @param        boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
407
	 * @return EE_Registration[]
408
	 * @throws \EE_Error
409
	 */
410
	public function registrations( $query_params = array(), $get_cached = FALSE ) {
411
		$query_params = ( empty( $query_params ) || ! is_array( $query_params ) )
412
			? array(
413
				'order_by' => array(
414
					'Event.EVT_name' => 'ASC',
415
					'Attendee.ATT_lname' => 'ASC',
416
					'Attendee.ATT_fname' => 'ASC'
417
				)
418
			)
419
			: $query_params;
420
		$query_params = $get_cached ? array() : $query_params;
421
		return $this->get_many_related( 'Registration', $query_params );
422
	}
423
424
425
426
	/**
427
	 * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event function
428
	 * for getting attendees and how many registrations they each have for an event)
429
	 *
430
	 * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
431
	 * @throws \EE_Error
432
	 */
433
	public function attendees() {
434
		return $this->get_many_related( 'Attendee', array( array( 'Registration.Transaction.TXN_ID' => $this->ID() ) ) );
435
	}
436
437
438
439
	/**
440
	 * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
441
	 *
442
	 * @param array $query_params like EEM_Base::get_all
443
	 * @return EE_Payment[]
444
	 * @throws \EE_Error
445
	 */
446
	public function payments( $query_params = array() ) {
447
		return $this->get_many_related( 'Payment', $query_params );
448
	}
449
450
451
452
	/**
453
	 * gets only approved payments for this transaction
454
	 *
455
	 * @return EE_Payment[]
456
	 * @throws \EE_Error
457
	 */
458
	public function approved_payments() {
459
		EE_Registry::instance()->load_model( 'Payment' );
460
		return $this->get_many_related( 'Payment', array( array( 'STS_ID' => EEM_Payment::status_id_approved ), 'order_by' => array( 'PAY_timestamp' => 'DESC' ) ) );
461
	}
462
463
464
465
    /**
466
     * Gets all payments which have not been approved
467
     * @return \EEI_Payment[]
468
     * @throws EE_Error if a model is misconfigured somehow
469
     */
470
	public function pending_payments()
471
    {
472
        return $this->get_many_related(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->get_many_r...imestamp' => 'DESC'))); (EE_Base_Class[]) is incompatible with the return type declared by the interface EEI_Transaction::pending_payments of type EEI_Payment[].

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...
473
            'Payment',
474
            array(
475
                array(
476
                    'STS_ID' => EEM_Payment::status_id_pending
477
                ),
478
                'order_by' => array(
479
                    'PAY_timestamp' => 'DESC'
480
                )
481
            )
482
        );
483
    }
484
485
486
487
    /**
488
	 * echoes $this->pretty_status()
489
	 *
490
	 * @param bool $show_icons
491
	 * @return string
492
	 * @throws \EE_Error
493
	 */
494
	public function e_pretty_status( $show_icons = FALSE ) {
495
		echo $this->pretty_status( $show_icons );
496
	}
497
498
499
500
	/**
501
	 * returns a pretty version of the status, good for displaying to users
502
	 *
503
	 * @param bool $show_icons
504
	 * @return string
505
	 * @throws \EE_Error
506
	 */
507
	public function pretty_status( $show_icons = FALSE ) {
508
		$status = EEM_Status::instance()->localized_status( array( $this->status_ID() => __( 'unknown', 'event_espresso' ) ), FALSE, 'sentence' );
509
		$icon = '';
510
		switch ( $this->status_ID() ) {
511
			case EEM_Transaction::complete_status_code:
512
				$icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
513
				break;
514
			case EEM_Transaction::incomplete_status_code:
515
				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>' : '';
516
				break;
517
			case EEM_Transaction::abandoned_status_code:
518
				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
519
				break;
520
			case EEM_Transaction::failed_status_code:
521
				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
522
				break;
523
			case EEM_Transaction::overpaid_status_code:
524
				$icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
525
				break;
526
		}
527
		return $icon . $status[ $this->status_ID() ];
528
	}
529
530
531
532
	/**
533
	 *        get Transaction Status
534
	 *
535
	 * @access        public
536
	 * @throws \EE_Error
537
	 */
538
	public function status_ID() {
539
		return $this->get( 'STS_ID' );
540
	}
541
542
543
544
	/**
545
	 * Returns TRUE or FALSE for whether or not this transaction cost any money
546
	 *
547
	 * @return boolean
548
	 * @throws \EE_Error
549
	 */
550
	public function is_free() {
551
		return EEH_Money::compare_floats( $this->get( 'TXN_total' ), 0, '==' );
552
	}
553
554
555
556
	/**
557
	 * Returns whether this transaction is complete
558
	 * Useful in templates and other logic for deciding if we should ask for another payment...
559
	 *
560
	 * @return boolean
561
	 * @throws \EE_Error
562
	 */
563
	public function is_completed() {
564
		return $this->status_ID() === EEM_Transaction::complete_status_code ? TRUE : FALSE;
565
	}
566
567
568
569
	/**
570
	 * Returns whether this transaction is incomplete
571
	 * Useful in templates and other logic for deciding if we should ask for another payment...
572
	 *
573
	 * @return boolean
574
	 * @throws \EE_Error
575
	 */
576
	public function is_incomplete() {
577
		return $this->status_ID() === EEM_Transaction::incomplete_status_code ? TRUE : FALSE;
578
	}
579
580
581
582
	/**
583
	 * Returns whether this transaction is overpaid
584
	 * Useful in templates and other logic for deciding if monies need to be refunded
585
	 *
586
	 * @return boolean
587
	 * @throws \EE_Error
588
	 */
589
	public function is_overpaid() {
590
		return $this->status_ID() === EEM_Transaction::overpaid_status_code ? TRUE : FALSE;
591
	}
592
593
594
595
	/**
596
	 * Returns whether this transaction was abandoned
597
	 * meaning that the transaction/registration process was somehow interrupted and never completed
598
	 * but that contact information exists for at least one registrant
599
	 *
600
	 * @return boolean
601
	 * @throws \EE_Error
602
	 */
603
	public function is_abandoned() {
604
		return $this->status_ID() === EEM_Transaction::abandoned_status_code ? TRUE : FALSE;
605
	}
606
607
608
609
	/**
610
	 * Returns whether this transaction failed
611
	 * meaning that the transaction/registration process was somehow interrupted and never completed
612
	 * and that NO contact information exists for any registrants
613
	 *
614
	 * @return boolean
615
	 * @throws \EE_Error
616
	 */
617
	public function failed() {
618
		return $this->status_ID() === EEM_Transaction::failed_status_code ? TRUE : FALSE;
619
	}
620
621
622
623
	/**
624
	 * This returns the url for the invoice of this transaction
625
	 *
626
	 * @param string $type 'html' or 'pdf' (default is pdf)
627
	 * @access public
628
	 * @return string
629
	 * @throws \EE_Error
630
	 */
631
	public function invoice_url( $type = 'html' ) {
632
		$REG = $this->primary_registration();
633
		if ( ! $REG instanceof EE_Registration ) {
634
			return '';
635
		}
636
		return $REG->invoice_url( $type );
637
	}
638
639
640
641
	/**
642
	 * Gets the primary registration only
643
	 *
644
	 * @return EE_Registration
645
	 * @throws \EE_Error
646
	 */
647
	public function primary_registration() {
648
		return $this->get_first_related( 'Registration', array( array( 'REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT ) ) );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->get_first_...RY_REGISTRANT_COUNT))); (EE_Base_Class) is incompatible with the return type declared by the interface EEI_Transaction::primary_registration of type EEI_Registration.

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...
649
	}
650
651
652
653
	/**
654
	 * Gets the URL for viewing the receipt
655
	 *
656
	 * @param string $type 'pdf' or 'html' (default is 'html')
657
	 * @return string
658
	 * @throws \EE_Error
659
	 */
660
	public function receipt_url( $type = 'html' ) {
661
		$REG = $this->primary_registration();
662
		if ( ! $REG instanceof EE_Registration ) {
663
			return '';
664
		}
665
		return $REG->receipt_url( $type );
666
	}
667
668
669
670
	/**
671
	 * Gets the URL of the thank you page with this registration REG_url_link added as
672
	 * a query parameter
673
	 *
674
	 * @access public
675
	 * @return string
676
	 * @throws \EE_Error
677
	 */
678
	public function payment_overview_url() {
679
		$primary_registration = $this->primary_registration();
680
		return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : FALSE;
681
	}
682
683
684
685
	/**
686
	 * @return string
687
	 * @throws \EE_Error
688
	 */
689
	public function gateway_response_on_transaction() {
690
		$payment = $this->get_first_related( 'Payment' );
691
		return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
692
	}
693
694
695
696
	/**
697
	 * Get the status object of this object
698
	 *
699
	 * @return EE_Status
700
	 * @throws \EE_Error
701
	 */
702
	public function status_obj() {
703
		return $this->get_first_related( 'Status' );
704
	}
705
706
707
708
	/**
709
	 * Gets all the extra meta info on this payment
710
	 *
711
	 * @param array $query_params like EEM_Base::get_all
712
	 * @return EE_Extra_Meta
713
	 * @throws \EE_Error
714
	 */
715
	public function extra_meta( $query_params = array() ) {
716
		return $this->get_many_related( 'Extra_Meta', $query_params );
717
	}
718
719
720
721
	/**
722
	 * Wrapper for _add_relation_to
723
	 *
724
	 * @param EE_Registration $registration
725
	 * @return EE_Base_Class the relation was added to
726
	 * @throws \EE_Error
727
	 */
728
	public function add_registration( EE_Registration $registration ) {
729
		return $this->_add_relation_to( $registration, 'Registration' );
730
	}
731
732
733
734
	/**
735
	 * Removes the given registration from being related (even before saving this transaction).
736
	 * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
737
	 *
738
	 * @param int $registration_or_id
739
	 * @return EE_Base_Class that was removed from being related
740
	 * @throws \EE_Error
741
	 */
742
	public function remove_registration_with_id( $registration_or_id ) {
743
		return $this->_remove_relation_to( $registration_or_id, 'Registration' );
744
	}
745
746
747
748
	/**
749
	 * Gets all the line items which are for ACTUAL items
750
	 *
751
	 * @return EE_Line_Item[]
752
	 * @throws \EE_Error
753
	 */
754
	public function items_purchased() {
755
		return $this->line_items( array( array( 'LIN_type' => EEM_Line_Item::type_line_item ) ) );
756
	}
757
758
759
760
	/**
761
	 * Wrapper for _add_relation_to
762
	 *
763
	 * @param EE_Line_Item $line_item
764
	 * @return EE_Base_Class the relation was added to
765
	 * @throws \EE_Error
766
	 */
767
	public function add_line_item( EE_Line_Item $line_item ) {
768
		return $this->_add_relation_to( $line_item, 'Line_Item' );
769
	}
770
771
772
773
	/**
774
	 * Gets ALL the line items related to this transaction (unstructured)
775
	 *
776
	 * @param array $query_params
777
	 * @return EE_Line_Item[]
778
	 * @throws \EE_Error
779
	 */
780
	public function line_items( $query_params = array() ) {
781
		return $this->get_many_related( 'Line_Item', $query_params );
782
	}
783
784
785
786
	/**
787
	 * Gets all the line items which are taxes on the total
788
	 *
789
	 * @return EE_Line_Item[]
790
	 * @throws \EE_Error
791
	 */
792
	public function tax_items() {
793
		return $this->line_items( array( array( 'LIN_type' => EEM_Line_Item::type_tax ) ) );
794
	}
795
796
797
798
	/**
799
	 * Gets the total line item (which is a parent of all other related line items,
800
	 * meaning it takes them all into account on its total)
801
	 *
802
	 * @param bool $create_if_not_found
803
	 * @return \EE_Line_Item
804
	 * @throws \EE_Error
805
	 */
806
	public function total_line_item( $create_if_not_found = true ) {
807
		$item =  $this->get_first_related( 'Line_Item', array( array( 'LIN_type' => EEM_Line_Item::type_total ) ) );
808
		if( ! $item && $create_if_not_found ){
809
			$item = EEH_Line_Item::create_total_line_item( $this );
810
		}
811
		return $item;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $item; (EE_Base_Class) is incompatible with the return type declared by the interface EEI_Transaction::total_line_item of type EEI_Line_Item.

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...
812
	}
813
814
815
816
	/**
817
	 * Returns the total amount of tax on this transaction
818
	 * (assumes there's only one tax subtotal line item)
819
	 *
820
	 * @return float
821
	 * @throws \EE_Error
822
	 */
823
	public function tax_total() {
824
		$tax_line_item = $this->tax_total_line_item();
825
		if ( $tax_line_item ) {
826
			return (float)$tax_line_item->total();
827
		} else {
828
			return (float)0;
829
		}
830
	}
831
832
833
834
	/**
835
	 * Gets the tax subtotal line item (assumes there's only one)
836
	 *
837
	 * @return EE_Line_Item
838
	 * @throws \EE_Error
839
	 */
840
	public function tax_total_line_item() {
841
		return EEH_Line_Item::get_taxes_subtotal( $this->total_line_item() );
842
	}
843
844
845
846
	/**
847
	 *  Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
848
	 *
849
	 * @return EE_Form_Section_Proper
850
	 * @throws \EE_Error
851
	 */
852
	public function billing_info(){
853
		$payment_method = $this->payment_method();
854
		if ( !$payment_method){
855
			EE_Error::add_error(__("Could not find billing info for transaction because no gateway has been used for it yet", "event_espresso"), __FILE__, __FUNCTION__, __LINE__);
856
			return false;
857
		}
858
		$primary_reg = $this->primary_registration();
859
		if ( ! $primary_reg ) {
860
			EE_Error::add_error( __( "Cannot get billing info for gateway %s on transaction because no primary registration exists", "event_espresso" ), __FILE__, __FUNCTION__, __LINE__ );
861
			return FALSE;
862
		}
863
		$attendee = $primary_reg->attendee();
864
		if ( ! $attendee ) {
865
			EE_Error::add_error( __( "Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists", "event_espresso" ), __FILE__, __FUNCTION__, __LINE__ );
866
			return FALSE;
867
		}
868
		return $attendee->billing_info_for_payment_method($payment_method);
869
	}
870
871
872
873
	/**
874
	 * Gets PMD_ID
875
	 *
876
	 * @return int
877
	 * @throws \EE_Error
878
	 */
879
	public function payment_method_ID() {
880
		return $this->get('PMD_ID');
881
	}
882
883
884
885
	/**
886
	 * Sets PMD_ID
887
	 *
888
	 * @param int $PMD_ID
889
	 * @return boolean
890
	 * @throws \EE_Error
891
	 */
892
	public function set_payment_method_ID($PMD_ID) {
893
		$this->set('PMD_ID', $PMD_ID);
894
	}
895
896
897
898
	/**
899
	 * Gets the last-used payment method on this transaction
900
	 * (we COULD just use the last-made payment, but some payment methods, namely
901
	 * offline ones, dont' create payments)
902
	 *
903
	 * @return EE_Payment_Method
904
	 * @throws \EE_Error
905
	 */
906
	public function payment_method(){
907
		$pm = $this->get_first_related('Payment_Method');
908
		if( $pm instanceof EE_Payment_Method ){
909
			return $pm;
910
		}else{
911
			$last_payment = $this->last_payment();
912
			if( $last_payment instanceof EE_Payment && $last_payment->payment_method() ){
913
				return $last_payment->payment_method();
914
			}else{
915
				return NULL;
916
			}
917
		}
918
	}
919
920
921
922
	/**
923
	 * Gets the last payment made
924
	 *
925
	 * @return EE_Payment
926
	 * @throws \EE_Error
927
	 */
928
	public function last_payment() {
929
		return $this->get_first_related( 'Payment', array( 'order_by' => array( 'PAY_ID' => 'desc' ) ) );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->get_first_...('PAY_ID' => 'desc'))); (EE_Base_Class) is incompatible with the return type declared by the interface EEI_Transaction::last_payment of type EEI_Payment.

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...
930
	}
931
932
933
934
	/**
935
	 * Gets all the line items which are unrelated to tickets on this transaction
936
	 *
937
	 * @return EE_Line_Item[]
938
	 * @throws \EE_Error
939
	 */
940
	public function non_ticket_line_items(){
941
		return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction( $this->ID() );
942
	}
943
944
945
946
	/**
947
	 * possibly toggles TXN status
948
	 *
949
	 * @param  boolean $update whether to save the TXN
950
	 * @return boolean whether the TXN was saved
951
	 * @throws \RuntimeException
952
	 */
953
	public function update_status_based_on_total_paid($update = true)
954
	{
955
		// set transaction status based on comparison of TXN_paid vs TXN_total
956
		if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
957
			$new_txn_status = EEM_Transaction::overpaid_status_code;
958
		} else if (EEH_Money::compare_floats($this->paid(), $this->total())) {
959
			$new_txn_status = EEM_Transaction::complete_status_code;
960
		} else if (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
961
			$new_txn_status = EEM_Transaction::incomplete_status_code;
962
		} else {
963
			throw new RuntimeException(
964
				__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
965
			);
966
		}
967
		if ($new_txn_status !== $this->status_ID()) {
968
			$this->set_status($new_txn_status);
969
			if ($update) {
970
				return $this->save() ? true : false;
971
			}
972
		}
973
		return false;
974
	}
975
976
977
978
	/**
979
	 * Updates the transaction's status and total_paid based on all the payments
980
	 * that apply to it
981
	 *
982
	 * @deprecated
983
	 * @return boolean
984
	 * @throws \EE_Error
985
	 */
986 View Code Duplication
	public function update_based_on_payments()
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...
987
	{
988
		EE_Error::doing_it_wrong(
989
			__CLASS__ . '::' . __FUNCTION__,
990
			sprintf(__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
991
				'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'),
992
			'4.6.0'
993
		);
994
		/** @type EE_Transaction_Processor $transaction_processor */
995
		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
996
		return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
997
	}
998
999
1000
1001
	/**
1002
	 * @return string
1003
	 */
1004
	public function old_txn_status() {
1005
		return $this->_old_txn_status;
1006
	}
1007
1008
1009
1010
	/**
1011
	 * @param string $old_txn_status
1012
	 */
1013
	public function set_old_txn_status( $old_txn_status ) {
1014
		// only set the first time
1015
		if ( $this->_old_txn_status === null ) {
1016
			$this->_old_txn_status = $old_txn_status;
1017
		}
1018
	}
1019
1020
1021
1022
	/**
1023
	 * reg_status_updated
1024
	 *
1025
	 * @return bool
1026
	 */
1027
	public function txn_status_updated() {
1028
		return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null ? true : false;
1029
	}
1030
1031
1032
1033
	/**
1034
	 * _reg_steps_completed
1035
	 * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1036
	 * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1037
	 * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1038
	 *
1039
	 * @access private
1040
	 * @param string         $reg_step_slug
1041
	 * @param bool           $check_all
1042
	 * @return boolean | int
1043
	 */
1044
	private function _reg_steps_completed( $reg_step_slug = '', $check_all = true ) {
1045
		$reg_steps = $this->reg_steps();
1046
		if ( ! is_array( $reg_steps ) || empty( $reg_steps ) ) {
1047
			return false;
1048
		}
1049
		// loop thru reg steps array)
1050
		foreach ( $reg_steps as $slug => $reg_step_completed ) {
1051
			// if NOT checking ALL steps (only checking one step)
1052
			if ( ! $check_all ) {
1053
				// and this is the one
1054
				if ( $slug === $reg_step_slug ) {
1055
					return $reg_step_completed;
1056
				} else {
1057
					// skip to next reg step in loop
1058
					continue;
1059
				}
1060
			}
1061
			// $check_all must be true, else we would never have gotten to this point
1062
			if ( $slug === $reg_step_slug ) {
1063
				// if we reach this point, then we are testing either:
1064
				// all_reg_steps_completed_except() or
1065
				// all_reg_steps_completed_except_final_step(),
1066
				// and since this is the reg step EXCEPTION being tested
1067
				// we want to return true (yes true) if this reg step is NOT completed
1068
				// ie: "is everything completed except the final step?"
1069
				// "that is correct... the final step is not completed, but all others are."
1070
				return $reg_step_completed !== true ? true : false;
1071
			} else if ( $reg_step_completed !== true ) {
1072
				// if any reg step is NOT completed, then ALL steps are not completed
1073
				return false;
1074
			}
1075
		}
1076
		return true;
1077
	}
1078
1079
1080
1081
	/**
1082
	 * all_reg_steps_completed
1083
	 * returns:
1084
	 *    true if ALL reg steps have been marked as completed
1085
	 *        or false if any step is not completed
1086
	 *
1087
	 * @return boolean
1088
	 */
1089
	public function all_reg_steps_completed() {
1090
		return $this->_reg_steps_completed();
1091
	}
1092
1093
1094
1095
	/**
1096
	 * all_reg_steps_completed_except
1097
	 * returns:
1098
	 *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1099
	 *        or false if any other step is not completed
1100
	 *        or false if ALL steps are completed including the exception you are testing !!!
1101
	 *
1102
	 * @param string         $exception
1103
	 * @return boolean
1104
	 */
1105
	public function all_reg_steps_completed_except( $exception = '' ) {
1106
		return $this->_reg_steps_completed( $exception );
1107
	}
1108
1109
1110
1111
	/**
1112
	 * all_reg_steps_completed_except
1113
	 * returns:
1114
	 *        true if ALL reg steps, except the final step, have been marked as completed
1115
	 *        or false if any step is not completed
1116
	 *    or false if ALL steps are completed including the final step !!!
1117
	 *
1118
	 * @return boolean
1119
	 */
1120
	public function all_reg_steps_completed_except_final_step() {
1121
		return $this->_reg_steps_completed( 'finalize_registration' );
1122
	}
1123
1124
1125
1126
	/**
1127
	 * reg_step_completed
1128
	 * returns:
1129
	 *    true if a specific reg step has been marked as completed
1130
	 *    a Unix timestamp if it has been initialized but not yet completed,
1131
	 *    or false if it has not yet been initialized
1132
	 *
1133
	 * @param string         $reg_step_slug
1134
	 * @return boolean | int
1135
	 */
1136
	public function reg_step_completed( $reg_step_slug ) {
1137
		return $this->_reg_steps_completed( $reg_step_slug, false );
1138
	}
1139
1140
1141
1142
	/**
1143
	 * completed_final_reg_step
1144
	 * returns:
1145
	 *    true if the finalize_registration reg step has been marked as completed
1146
	 *    a Unix timestamp if it has been initialized but not yet completed,
1147
	 *    or false if it has not yet been initialized
1148
	 *
1149
	 * @return boolean | int
1150
	 */
1151
	public function final_reg_step_completed() {
1152
		return $this->_reg_steps_completed( 'finalize_registration', false );
1153
	}
1154
1155
1156
1157
	/**
1158
	 * set_reg_step_initiated
1159
	 * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1160
	 *
1161
	 * @access public
1162
	 * @param string          $reg_step_slug
1163
	 * @return boolean
1164
	 * @throws \EE_Error
1165
	 */
1166
	public function set_reg_step_initiated( $reg_step_slug ) {
1167
		return $this->_set_reg_step_completed_status( $reg_step_slug, time() );
1168
	}
1169
1170
1171
1172
	/**
1173
	 * set_reg_step_completed
1174
	 * given a valid TXN_reg_step, this sets the step as completed
1175
	 *
1176
	 * @access public
1177
	 * @param string          $reg_step_slug
1178
	 * @return boolean
1179
	 * @throws \EE_Error
1180
	 */
1181
	public function set_reg_step_completed( $reg_step_slug ) {
1182
		return $this->_set_reg_step_completed_status( $reg_step_slug, true );
1183
	}
1184
1185
1186
1187
	/**
1188
	 * set_reg_step_completed
1189
	 * given a valid TXN_reg_step slug, this sets the step as NOT completed
1190
	 *
1191
	 * @access public
1192
	 * @param string          $reg_step_slug
1193
	 * @return boolean
1194
	 * @throws \EE_Error
1195
	 */
1196
	public function set_reg_step_not_completed( $reg_step_slug ) {
1197
		return $this->_set_reg_step_completed_status( $reg_step_slug, false );
1198
	}
1199
1200
1201
1202
	/**
1203
	 * set_reg_step_completed
1204
	 * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1205
	 *
1206
	 * @access private
1207
	 * @param  string          $reg_step_slug
1208
	 * @param  boolean|int     $status
1209
	 * @return boolean
1210
	 * @throws \EE_Error
1211
	 */
1212
	private function _set_reg_step_completed_status( $reg_step_slug, $status ) {
1213
		// validate status
1214
		$status = is_bool( $status ) || is_int( $status ) ? $status : false;
1215
		// get reg steps array
1216
		$txn_reg_steps = $this->reg_steps();
1217
		// if reg step does NOT exist
1218
		if ( ! isset( $txn_reg_steps[ $reg_step_slug ] ) ) {
1219
			return false;
1220
		}
1221
		// if  we're trying to complete a step that is already completed
1222
		if ( $txn_reg_steps[ $reg_step_slug ] === true ) {
1223
			return true;
1224
		}
1225
		// if  we're trying to complete a step that hasn't even started
1226
		if ( $status === true && $txn_reg_steps[ $reg_step_slug ] === false ) {
1227
			return false;
1228
		}
1229
		// if current status value matches the incoming value (no change)
1230
		// type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1231
		if ( (int) $txn_reg_steps[ $reg_step_slug ] === (int) $status ) {
1232
			// this will happen in cases where multiple AJAX requests occur during the same step
1233
			return true;
1234
		}
1235
		// if we're trying to set a start time, but it has already been set...
1236
		if ( is_numeric( $status ) && is_numeric( $txn_reg_steps[ $reg_step_slug ] ) ) {
1237
			// skip the update below, but don't return FALSE so that errors won't be displayed
1238
			return true;
1239
		}
1240
		// update completed status
1241
		$txn_reg_steps[ $reg_step_slug ] = $status;
1242
		$this->set_reg_steps( $txn_reg_steps );
1243
		$this->save();
1244
		return true;
1245
	}
1246
1247
1248
1249
	/**
1250
	 * remove_reg_step
1251
	 * given a valid TXN_reg_step slug, this will remove (unset)
1252
	 * the reg step from the TXN reg step array
1253
	 *
1254
	 * @access public
1255
	 * @param string          $reg_step_slug
1256
	 * @return void
1257
	 */
1258
	public function remove_reg_step( $reg_step_slug ) {
1259
		// get reg steps array
1260
		$txn_reg_steps = $this->reg_steps();
1261
		unset( $txn_reg_steps[ $reg_step_slug ] );
1262
		$this->set_reg_steps( $txn_reg_steps );
1263
	}
1264
1265
1266
1267
	/**
1268
	 *    toggle_failed_transaction_status
1269
	 * upgrades a TXNs status from failed to abandoned,
1270
	 * meaning that contact information has been captured for at least one registrant
1271
	 *
1272
	 * @access public
1273
	 * @param bool $save
1274
	 * @return bool
1275
	 */
1276
	public function toggle_failed_transaction_status( $save = true ) {
1277
		// if TXN status is still set as "failed"...
1278
		if ( $this->status_ID() === EEM_Transaction::failed_status_code ) {
1279
			$this->set_status( EEM_Transaction::abandoned_status_code );
1280
			if ( $save ) {
1281
				$this->save();
1282
			}
1283
			return true;
1284
		}
1285
		return false;
1286
	}
1287
1288
1289
1290
	/**
1291
	 * toggle_abandoned_transaction_status
1292
	 * upgrades a TXNs status from failed or abandoned to incomplete
1293
	 *
1294
	 * @access public
1295
	 * @return boolean
1296
	 */
1297
	public function toggle_abandoned_transaction_status() {
1298
		// if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1299
		$txn_status = $this->status_ID();
1300
		if (
1301
			$txn_status === EEM_Transaction::failed_status_code
1302
			|| $txn_status === EEM_Transaction::abandoned_status_code
1303
		) {
1304
			// if a contact record for the primary registrant has been created
1305
			if (
1306
				$this->primary_registration() instanceof EE_Registration
1307
				&& $this->primary_registration()->attendee() instanceof EE_Attendee
1308
			) {
1309
				$this->set_status( EEM_Transaction::incomplete_status_code );
1310
			} else {
1311
				// no contact record? yer abandoned!
1312
				$this->set_status( EEM_Transaction::abandoned_status_code );
1313
			}
1314
			return true;
1315
		}
1316
		return false;
1317
	}
1318
1319
1320
1321
	/**
1322
	 * checks if an Abandoned TXN has any related payments, and if so,
1323
	 * updates the TXN status based on the amount paid
1324
	 */
1325
	public function verify_abandoned_transaction_status() {
1326
		if ( $this->status_ID() !== EEM_Transaction::abandoned_status_code ) {
1327
			return;
1328
		}
1329
		$payments = $this->get_many_related( 'Payment' );
1330
		if ( ! empty( $payments ) ) {
1331
			foreach ( $payments as $payment ) {
1332
				if ( $payment instanceof EE_Payment ) {
1333
					// kk this TXN should NOT be abandoned
1334
					$this->update_status_based_on_total_paid();
1335
					if ( is_admin() && ! ( defined('DOING_AJAX') && DOING_AJAX ) ) {
1336
						EE_Error::add_attention(
1337
							sprintf(
1338
								esc_html__(
1339
									'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1340
									'event_espresso'
1341
								),
1342
								$this->ID(),
1343
								$this->pretty_status()
1344
							)
1345
						);
1346
					}
1347
					// get final reg step status
1348
					$finalized = $this->final_reg_step_completed();
1349
					// if the 'finalize_registration' step has been initiated (has a timestamp)
1350
					// but has not yet been fully completed (TRUE)
1351
					if ( is_int( $finalized ) && $finalized !== false && $finalized !== true ) {
1352
						$this->set_reg_step_completed( 'finalize_registration' );
1353
						$this->save();
1354
					}
1355
				}
1356
			}
1357
		}
1358
	}
1359
1360
}/* End of file EE_Transaction.class.php */
1361
/* Location: includes/classes/EE_Transaction.class.php */
1362