Completed
Branch BUG-10502-mijireh-check-all-pa... (d9262a)
by
unknown
60:34 queued 46:59
created

EE_Transaction::tax_total()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 8
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 \EE_Base_Class[]
468
     */
469
	public function pending_payments()
470
    {
471
        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...
472
            'Payment',
473
            array(
474
                array(
475
                    'STS_ID' => EEM_Payment::status_id_pending
476
                ),
477
                'order_by' => array(
478
                    'PAY_timestamp' => 'DESC'
479
                )
480
            )
481
        );
482
    }
483
484
485
486
    /**
487
	 * echoes $this->pretty_status()
488
	 *
489
	 * @param bool $show_icons
490
	 * @return string
491
	 * @throws \EE_Error
492
	 */
493
	public function e_pretty_status( $show_icons = FALSE ) {
494
		echo $this->pretty_status( $show_icons );
495
	}
496
497
498
499
	/**
500
	 * returns a pretty version of the status, good for displaying to users
501
	 *
502
	 * @param bool $show_icons
503
	 * @return string
504
	 * @throws \EE_Error
505
	 */
506
	public function pretty_status( $show_icons = FALSE ) {
507
		$status = EEM_Status::instance()->localized_status( array( $this->status_ID() => __( 'unknown', 'event_espresso' ) ), FALSE, 'sentence' );
508
		$icon = '';
509
		switch ( $this->status_ID() ) {
510
			case EEM_Transaction::complete_status_code:
511
				$icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
512
				break;
513
			case EEM_Transaction::incomplete_status_code:
514
				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>' : '';
515
				break;
516
			case EEM_Transaction::abandoned_status_code:
517
				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
518
				break;
519
			case EEM_Transaction::failed_status_code:
520
				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
521
				break;
522
			case EEM_Transaction::overpaid_status_code:
523
				$icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
524
				break;
525
		}
526
		return $icon . $status[ $this->status_ID() ];
527
	}
528
529
530
531
	/**
532
	 *        get Transaction Status
533
	 *
534
	 * @access        public
535
	 * @throws \EE_Error
536
	 */
537
	public function status_ID() {
538
		return $this->get( 'STS_ID' );
539
	}
540
541
542
543
	/**
544
	 * Returns TRUE or FALSE for whether or not this transaction cost any money
545
	 *
546
	 * @return boolean
547
	 * @throws \EE_Error
548
	 */
549
	public function is_free() {
550
		return EEH_Money::compare_floats( $this->get( 'TXN_total' ), 0, '==' );
551
	}
552
553
554
555
	/**
556
	 * Returns whether this transaction is complete
557
	 * Useful in templates and other logic for deciding if we should ask for another payment...
558
	 *
559
	 * @return boolean
560
	 * @throws \EE_Error
561
	 */
562
	public function is_completed() {
563
		return $this->status_ID() === EEM_Transaction::complete_status_code ? TRUE : FALSE;
564
	}
565
566
567
568
	/**
569
	 * Returns whether this transaction is incomplete
570
	 * Useful in templates and other logic for deciding if we should ask for another payment...
571
	 *
572
	 * @return boolean
573
	 * @throws \EE_Error
574
	 */
575
	public function is_incomplete() {
576
		return $this->status_ID() === EEM_Transaction::incomplete_status_code ? TRUE : FALSE;
577
	}
578
579
580
581
	/**
582
	 * Returns whether this transaction is overpaid
583
	 * Useful in templates and other logic for deciding if monies need to be refunded
584
	 *
585
	 * @return boolean
586
	 * @throws \EE_Error
587
	 */
588
	public function is_overpaid() {
589
		return $this->status_ID() === EEM_Transaction::overpaid_status_code ? TRUE : FALSE;
590
	}
591
592
593
594
	/**
595
	 * Returns whether this transaction was abandoned
596
	 * meaning that the transaction/registration process was somehow interrupted and never completed
597
	 * but that contact information exists for at least one registrant
598
	 *
599
	 * @return boolean
600
	 * @throws \EE_Error
601
	 */
602
	public function is_abandoned() {
603
		return $this->status_ID() === EEM_Transaction::abandoned_status_code ? TRUE : FALSE;
604
	}
605
606
607
608
	/**
609
	 * Returns whether this transaction failed
610
	 * meaning that the transaction/registration process was somehow interrupted and never completed
611
	 * and that NO contact information exists for any registrants
612
	 *
613
	 * @return boolean
614
	 * @throws \EE_Error
615
	 */
616
	public function failed() {
617
		return $this->status_ID() === EEM_Transaction::failed_status_code ? TRUE : FALSE;
618
	}
619
620
621
622
	/**
623
	 * This returns the url for the invoice of this transaction
624
	 *
625
	 * @param string $type 'html' or 'pdf' (default is pdf)
626
	 * @access public
627
	 * @return string
628
	 * @throws \EE_Error
629
	 */
630
	public function invoice_url( $type = 'html' ) {
631
		$REG = $this->primary_registration();
632
		if ( ! $REG instanceof EE_Registration ) {
633
			return '';
634
		}
635
		return $REG->invoice_url( $type );
636
	}
637
638
639
640
	/**
641
	 * Gets the primary registration only
642
	 *
643
	 * @return EE_Registration
644
	 * @throws \EE_Error
645
	 */
646
	public function primary_registration() {
647
		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...
648
	}
649
650
651
652
	/**
653
	 * Gets the URL for viewing the receipt
654
	 *
655
	 * @param string $type 'pdf' or 'html' (default is 'html')
656
	 * @return string
657
	 * @throws \EE_Error
658
	 */
659
	public function receipt_url( $type = 'html' ) {
660
		$REG = $this->primary_registration();
661
		if ( ! $REG instanceof EE_Registration ) {
662
			return '';
663
		}
664
		return $REG->receipt_url( $type );
665
	}
666
667
668
669
	/**
670
	 * Gets the URL of the thank you page with this registration REG_url_link added as
671
	 * a query parameter
672
	 *
673
	 * @access public
674
	 * @return string
675
	 * @throws \EE_Error
676
	 */
677
	public function payment_overview_url() {
678
		$primary_registration = $this->primary_registration();
679
		return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : FALSE;
680
	}
681
682
683
684
	/**
685
	 * @return string
686
	 * @throws \EE_Error
687
	 */
688
	public function gateway_response_on_transaction() {
689
		$payment = $this->get_first_related( 'Payment' );
690
		return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
691
	}
692
693
694
695
	/**
696
	 * Get the status object of this object
697
	 *
698
	 * @return EE_Status
699
	 * @throws \EE_Error
700
	 */
701
	public function status_obj() {
702
		return $this->get_first_related( 'Status' );
703
	}
704
705
706
707
	/**
708
	 * Gets all the extra meta info on this payment
709
	 *
710
	 * @param array $query_params like EEM_Base::get_all
711
	 * @return EE_Extra_Meta
712
	 * @throws \EE_Error
713
	 */
714
	public function extra_meta( $query_params = array() ) {
715
		return $this->get_many_related( 'Extra_Meta', $query_params );
716
	}
717
718
719
720
	/**
721
	 * Wrapper for _add_relation_to
722
	 *
723
	 * @param EE_Registration $registration
724
	 * @return EE_Base_Class the relation was added to
725
	 * @throws \EE_Error
726
	 */
727
	public function add_registration( EE_Registration $registration ) {
728
		return $this->_add_relation_to( $registration, 'Registration' );
729
	}
730
731
732
733
	/**
734
	 * Removes the given registration from being related (even before saving this transaction).
735
	 * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
736
	 *
737
	 * @param int $registration_or_id
738
	 * @return EE_Base_Class that was removed from being related
739
	 * @throws \EE_Error
740
	 */
741
	public function remove_registration_with_id( $registration_or_id ) {
742
		return $this->_remove_relation_to( $registration_or_id, 'Registration' );
743
	}
744
745
746
747
	/**
748
	 * Gets all the line items which are for ACTUAL items
749
	 *
750
	 * @return EE_Line_Item[]
751
	 * @throws \EE_Error
752
	 */
753
	public function items_purchased() {
754
		return $this->line_items( array( array( 'LIN_type' => EEM_Line_Item::type_line_item ) ) );
755
	}
756
757
758
759
	/**
760
	 * Wrapper for _add_relation_to
761
	 *
762
	 * @param EE_Line_Item $line_item
763
	 * @return EE_Base_Class the relation was added to
764
	 * @throws \EE_Error
765
	 */
766
	public function add_line_item( EE_Line_Item $line_item ) {
767
		return $this->_add_relation_to( $line_item, 'Line_Item' );
768
	}
769
770
771
772
	/**
773
	 * Gets ALL the line items related to this transaction (unstructured)
774
	 *
775
	 * @param array $query_params
776
	 * @return EE_Line_Item[]
777
	 * @throws \EE_Error
778
	 */
779
	public function line_items( $query_params = array() ) {
780
		return $this->get_many_related( 'Line_Item', $query_params );
781
	}
782
783
784
785
	/**
786
	 * Gets all the line items which are taxes on the total
787
	 *
788
	 * @return EE_Line_Item[]
789
	 * @throws \EE_Error
790
	 */
791
	public function tax_items() {
792
		return $this->line_items( array( array( 'LIN_type' => EEM_Line_Item::type_tax ) ) );
793
	}
794
795
796
797
	/**
798
	 * Gets the total line item (which is a parent of all other related line items,
799
	 * meaning it takes them all into account on its total)
800
	 *
801
	 * @param bool $create_if_not_found
802
	 * @return \EE_Line_Item
803
	 * @throws \EE_Error
804
	 */
805
	public function total_line_item( $create_if_not_found = true ) {
806
		$item =  $this->get_first_related( 'Line_Item', array( array( 'LIN_type' => EEM_Line_Item::type_total ) ) );
807
		if( ! $item && $create_if_not_found ){
808
			$item = EEH_Line_Item::create_total_line_item( $this );
809
		}
810
		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...
811
	}
812
813
814
815
	/**
816
	 * Returns the total amount of tax on this transaction
817
	 * (assumes there's only one tax subtotal line item)
818
	 *
819
	 * @return float
820
	 * @throws \EE_Error
821
	 */
822
	public function tax_total() {
823
		$tax_line_item = $this->tax_total_line_item();
824
		if ( $tax_line_item ) {
825
			return (float)$tax_line_item->total();
826
		} else {
827
			return (float)0;
828
		}
829
	}
830
831
832
833
	/**
834
	 * Gets the tax subtotal line item (assumes there's only one)
835
	 *
836
	 * @return EE_Line_Item
837
	 * @throws \EE_Error
838
	 */
839
	public function tax_total_line_item() {
840
		return EEH_Line_Item::get_taxes_subtotal( $this->total_line_item() );
841
	}
842
843
844
845
	/**
846
	 *  Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
847
	 *
848
	 * @return EE_Form_Section_Proper
849
	 * @throws \EE_Error
850
	 */
851
	public function billing_info(){
852
		$payment_method = $this->payment_method();
853
		if ( !$payment_method){
854
			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__);
855
			return false;
856
		}
857
		$primary_reg = $this->primary_registration();
858
		if ( ! $primary_reg ) {
859
			EE_Error::add_error( __( "Cannot get billing info for gateway %s on transaction because no primary registration exists", "event_espresso" ), __FILE__, __FUNCTION__, __LINE__ );
860
			return FALSE;
861
		}
862
		$attendee = $primary_reg->attendee();
863
		if ( ! $attendee ) {
864
			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__ );
865
			return FALSE;
866
		}
867
		return $attendee->billing_info_for_payment_method($payment_method);
868
	}
869
870
871
872
	/**
873
	 * Gets PMD_ID
874
	 *
875
	 * @return int
876
	 * @throws \EE_Error
877
	 */
878
	public function payment_method_ID() {
879
		return $this->get('PMD_ID');
880
	}
881
882
883
884
	/**
885
	 * Sets PMD_ID
886
	 *
887
	 * @param int $PMD_ID
888
	 * @return boolean
889
	 * @throws \EE_Error
890
	 */
891
	public function set_payment_method_ID($PMD_ID) {
892
		$this->set('PMD_ID', $PMD_ID);
893
	}
894
895
896
897
	/**
898
	 * Gets the last-used payment method on this transaction
899
	 * (we COULD just use the last-made payment, but some payment methods, namely
900
	 * offline ones, dont' create payments)
901
	 *
902
	 * @return EE_Payment_Method
903
	 * @throws \EE_Error
904
	 */
905
	public function payment_method(){
906
		$pm = $this->get_first_related('Payment_Method');
907
		if( $pm instanceof EE_Payment_Method ){
908
			return $pm;
909
		}else{
910
			$last_payment = $this->last_payment();
911
			if( $last_payment instanceof EE_Payment && $last_payment->payment_method() ){
912
				return $last_payment->payment_method();
913
			}else{
914
				return NULL;
915
			}
916
		}
917
	}
918
919
920
921
	/**
922
	 * Gets the last payment made
923
	 *
924
	 * @return EE_Payment
925
	 * @throws \EE_Error
926
	 */
927
	public function last_payment() {
928
		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...
929
	}
930
931
932
933
	/**
934
	 * Gets all the line items which are unrelated to tickets on this transaction
935
	 *
936
	 * @return EE_Line_Item[]
937
	 * @throws \EE_Error
938
	 */
939
	public function non_ticket_line_items(){
940
		return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction( $this->ID() );
941
	}
942
943
944
945
	/**
946
	 * possibly toggles TXN status
947
	 *
948
	 * @param  boolean $update whether to save the TXN
949
	 * @return boolean whether the TXN was saved
950
	 * @throws \RuntimeException
951
	 */
952
	public function update_status_based_on_total_paid($update = true)
953
	{
954
		// set transaction status based on comparison of TXN_paid vs TXN_total
955
		if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
956
			$new_txn_status = EEM_Transaction::overpaid_status_code;
957
		} else if (EEH_Money::compare_floats($this->paid(), $this->total())) {
958
			$new_txn_status = EEM_Transaction::complete_status_code;
959
		} else if (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
960
			$new_txn_status = EEM_Transaction::incomplete_status_code;
961
		} else {
962
			throw new RuntimeException(
963
				__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
964
			);
965
		}
966
		if ($new_txn_status !== $this->status_ID()) {
967
			$this->set_status($new_txn_status);
968
			if ($update) {
969
				return $this->save() ? true : false;
970
			}
971
		}
972
		return false;
973
	}
974
975
976
977
	/**
978
	 * Updates the transaction's status and total_paid based on all the payments
979
	 * that apply to it
980
	 *
981
	 * @deprecated
982
	 * @return boolean
983
	 * @throws \EE_Error
984
	 */
985 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...
986
	{
987
		EE_Error::doing_it_wrong(
988
			__CLASS__ . '::' . __FUNCTION__,
989
			sprintf(__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
990
				'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'),
991
			'4.6.0'
992
		);
993
		/** @type EE_Transaction_Processor $transaction_processor */
994
		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
995
		return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
996
	}
997
998
999
1000
	/**
1001
	 * @return string
1002
	 */
1003
	public function old_txn_status() {
1004
		return $this->_old_txn_status;
1005
	}
1006
1007
1008
1009
	/**
1010
	 * @param string $old_txn_status
1011
	 */
1012
	public function set_old_txn_status( $old_txn_status ) {
1013
		// only set the first time
1014
		if ( $this->_old_txn_status === null ) {
1015
			$this->_old_txn_status = $old_txn_status;
1016
		}
1017
	}
1018
1019
1020
1021
	/**
1022
	 * reg_status_updated
1023
	 *
1024
	 * @return bool
1025
	 */
1026
	public function txn_status_updated() {
1027
		return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null ? true : false;
1028
	}
1029
1030
1031
1032
	/**
1033
	 * _reg_steps_completed
1034
	 * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1035
	 * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1036
	 * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1037
	 *
1038
	 * @access private
1039
	 * @param string         $reg_step_slug
1040
	 * @param bool           $check_all
1041
	 * @return boolean | int
1042
	 */
1043
	private function _reg_steps_completed( $reg_step_slug = '', $check_all = true ) {
1044
		$reg_steps = $this->reg_steps();
1045
		if ( ! is_array( $reg_steps ) || empty( $reg_steps ) ) {
1046
			return false;
1047
		}
1048
		// loop thru reg steps array)
1049
		foreach ( $reg_steps as $slug => $reg_step_completed ) {
1050
			// if NOT checking ALL steps (only checking one step)
1051
			if ( ! $check_all ) {
1052
				// and this is the one
1053
				if ( $slug === $reg_step_slug ) {
1054
					return $reg_step_completed;
1055
				} else {
1056
					// skip to next reg step in loop
1057
					continue;
1058
				}
1059
			}
1060
			// $check_all must be true, else we would never have gotten to this point
1061
			if ( $slug === $reg_step_slug ) {
1062
				// if we reach this point, then we are testing either:
1063
				// all_reg_steps_completed_except() or
1064
				// all_reg_steps_completed_except_final_step(),
1065
				// and since this is the reg step EXCEPTION being tested
1066
				// we want to return true (yes true) if this reg step is NOT completed
1067
				// ie: "is everything completed except the final step?"
1068
				// "that is correct... the final step is not completed, but all others are."
1069
				return $reg_step_completed !== true ? true : false;
1070
			} else if ( $reg_step_completed !== true ) {
1071
				// if any reg step is NOT completed, then ALL steps are not completed
1072
				return false;
1073
			}
1074
		}
1075
		return true;
1076
	}
1077
1078
1079
1080
	/**
1081
	 * all_reg_steps_completed
1082
	 * returns:
1083
	 *    true if ALL reg steps have been marked as completed
1084
	 *        or false if any step is not completed
1085
	 *
1086
	 * @return boolean
1087
	 */
1088
	public function all_reg_steps_completed() {
1089
		return $this->_reg_steps_completed();
1090
	}
1091
1092
1093
1094
	/**
1095
	 * all_reg_steps_completed_except
1096
	 * returns:
1097
	 *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1098
	 *        or false if any other step is not completed
1099
	 *        or false if ALL steps are completed including the exception you are testing !!!
1100
	 *
1101
	 * @param string         $exception
1102
	 * @return boolean
1103
	 */
1104
	public function all_reg_steps_completed_except( $exception = '' ) {
1105
		return $this->_reg_steps_completed( $exception );
1106
	}
1107
1108
1109
1110
	/**
1111
	 * all_reg_steps_completed_except
1112
	 * returns:
1113
	 *        true if ALL reg steps, except the final step, have been marked as completed
1114
	 *        or false if any step is not completed
1115
	 *    or false if ALL steps are completed including the final step !!!
1116
	 *
1117
	 * @return boolean
1118
	 */
1119
	public function all_reg_steps_completed_except_final_step() {
1120
		return $this->_reg_steps_completed( 'finalize_registration' );
1121
	}
1122
1123
1124
1125
	/**
1126
	 * reg_step_completed
1127
	 * returns:
1128
	 *    true if a specific reg step has been marked as completed
1129
	 *    a Unix timestamp if it has been initialized but not yet completed,
1130
	 *    or false if it has not yet been initialized
1131
	 *
1132
	 * @param string         $reg_step_slug
1133
	 * @return boolean | int
1134
	 */
1135
	public function reg_step_completed( $reg_step_slug ) {
1136
		return $this->_reg_steps_completed( $reg_step_slug, false );
1137
	}
1138
1139
1140
1141
	/**
1142
	 * completed_final_reg_step
1143
	 * returns:
1144
	 *    true if the finalize_registration reg step has been marked as completed
1145
	 *    a Unix timestamp if it has been initialized but not yet completed,
1146
	 *    or false if it has not yet been initialized
1147
	 *
1148
	 * @return boolean | int
1149
	 */
1150
	public function final_reg_step_completed() {
1151
		return $this->_reg_steps_completed( 'finalize_registration', false );
1152
	}
1153
1154
1155
1156
	/**
1157
	 * set_reg_step_initiated
1158
	 * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1159
	 *
1160
	 * @access public
1161
	 * @param string          $reg_step_slug
1162
	 * @return boolean
1163
	 * @throws \EE_Error
1164
	 */
1165
	public function set_reg_step_initiated( $reg_step_slug ) {
1166
		return $this->_set_reg_step_completed_status( $reg_step_slug, time() );
1167
	}
1168
1169
1170
1171
	/**
1172
	 * set_reg_step_completed
1173
	 * given a valid TXN_reg_step, this sets the step as completed
1174
	 *
1175
	 * @access public
1176
	 * @param string          $reg_step_slug
1177
	 * @return boolean
1178
	 * @throws \EE_Error
1179
	 */
1180
	public function set_reg_step_completed( $reg_step_slug ) {
1181
		return $this->_set_reg_step_completed_status( $reg_step_slug, true );
1182
	}
1183
1184
1185
1186
	/**
1187
	 * set_reg_step_completed
1188
	 * given a valid TXN_reg_step slug, this sets the step as NOT completed
1189
	 *
1190
	 * @access public
1191
	 * @param string          $reg_step_slug
1192
	 * @return boolean
1193
	 * @throws \EE_Error
1194
	 */
1195
	public function set_reg_step_not_completed( $reg_step_slug ) {
1196
		return $this->_set_reg_step_completed_status( $reg_step_slug, false );
1197
	}
1198
1199
1200
1201
	/**
1202
	 * set_reg_step_completed
1203
	 * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1204
	 *
1205
	 * @access private
1206
	 * @param  string          $reg_step_slug
1207
	 * @param  boolean|int     $status
1208
	 * @return boolean
1209
	 * @throws \EE_Error
1210
	 */
1211
	private function _set_reg_step_completed_status( $reg_step_slug, $status ) {
1212
		// validate status
1213
		$status = is_bool( $status ) || is_int( $status ) ? $status : false;
1214
		// get reg steps array
1215
		$txn_reg_steps = $this->reg_steps();
1216
		// if reg step does NOT exist
1217
		if ( ! isset( $txn_reg_steps[ $reg_step_slug ] ) ) {
1218
			return false;
1219
		}
1220
		// if  we're trying to complete a step that is already completed
1221
		if ( $txn_reg_steps[ $reg_step_slug ] === true ) {
1222
			return true;
1223
		}
1224
		// if  we're trying to complete a step that hasn't even started
1225
		if ( $status === true && $txn_reg_steps[ $reg_step_slug ] === false ) {
1226
			return false;
1227
		}
1228
		// if current status value matches the incoming value (no change)
1229
		// type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1230
		if ( (int) $txn_reg_steps[ $reg_step_slug ] === (int) $status ) {
1231
			// this will happen in cases where multiple AJAX requests occur during the same step
1232
			return true;
1233
		}
1234
		// if we're trying to set a start time, but it has already been set...
1235
		if ( is_numeric( $status ) && is_numeric( $txn_reg_steps[ $reg_step_slug ] ) ) {
1236
			// skip the update below, but don't return FALSE so that errors won't be displayed
1237
			return true;
1238
		}
1239
		// update completed status
1240
		$txn_reg_steps[ $reg_step_slug ] = $status;
1241
		$this->set_reg_steps( $txn_reg_steps );
1242
		$this->save();
1243
		return true;
1244
	}
1245
1246
1247
1248
	/**
1249
	 * remove_reg_step
1250
	 * given a valid TXN_reg_step slug, this will remove (unset)
1251
	 * the reg step from the TXN reg step array
1252
	 *
1253
	 * @access public
1254
	 * @param string          $reg_step_slug
1255
	 * @return void
1256
	 */
1257
	public function remove_reg_step( $reg_step_slug ) {
1258
		// get reg steps array
1259
		$txn_reg_steps = $this->reg_steps();
1260
		unset( $txn_reg_steps[ $reg_step_slug ] );
1261
		$this->set_reg_steps( $txn_reg_steps );
1262
	}
1263
1264
1265
1266
	/**
1267
	 *    toggle_failed_transaction_status
1268
	 * upgrades a TXNs status from failed to abandoned,
1269
	 * meaning that contact information has been captured for at least one registrant
1270
	 *
1271
	 * @access public
1272
	 * @param bool $save
1273
	 * @return bool
1274
	 */
1275
	public function toggle_failed_transaction_status( $save = true ) {
1276
		// if TXN status is still set as "failed"...
1277
		if ( $this->status_ID() === EEM_Transaction::failed_status_code ) {
1278
			$this->set_status( EEM_Transaction::abandoned_status_code );
1279
			if ( $save ) {
1280
				$this->save();
1281
			}
1282
			return true;
1283
		}
1284
		return false;
1285
	}
1286
1287
1288
1289
	/**
1290
	 * toggle_abandoned_transaction_status
1291
	 * upgrades a TXNs status from failed or abandoned to incomplete
1292
	 *
1293
	 * @access public
1294
	 * @return boolean
1295
	 */
1296
	public function toggle_abandoned_transaction_status() {
1297
		// if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1298
		$txn_status = $this->status_ID();
1299
		if (
1300
			$txn_status === EEM_Transaction::failed_status_code
1301
			|| $txn_status === EEM_Transaction::abandoned_status_code
1302
		) {
1303
			// if a contact record for the primary registrant has been created
1304
			if (
1305
				$this->primary_registration() instanceof EE_Registration
1306
				&& $this->primary_registration()->attendee() instanceof EE_Attendee
1307
			) {
1308
				$this->set_status( EEM_Transaction::incomplete_status_code );
1309
			} else {
1310
				// no contact record? yer abandoned!
1311
				$this->set_status( EEM_Transaction::abandoned_status_code );
1312
			}
1313
			return true;
1314
		}
1315
		return false;
1316
	}
1317
1318
1319
1320
	/**
1321
	 * checks if an Abandoned TXN has any related payments, and if so,
1322
	 * updates the TXN status based on the amount paid
1323
	 */
1324
	public function verify_abandoned_transaction_status() {
1325
		if ( $this->status_ID() !== EEM_Transaction::abandoned_status_code ) {
1326
			return;
1327
		}
1328
		$payments = $this->get_many_related( 'Payment' );
1329
		if ( ! empty( $payments ) ) {
1330
			foreach ( $payments as $payment ) {
1331
				if ( $payment instanceof EE_Payment ) {
1332
					// kk this TXN should NOT be abandoned
1333
					$this->update_status_based_on_total_paid();
1334
					if ( is_admin() && ! ( defined('DOING_AJAX') && DOING_AJAX ) ) {
1335
						EE_Error::add_attention(
1336
							sprintf(
1337
								esc_html__(
1338
									'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.',
1339
									'event_espresso'
1340
								),
1341
								$this->ID(),
1342
								$this->pretty_status()
1343
							)
1344
						);
1345
					}
1346
					// get final reg step status
1347
					$finalized = $this->final_reg_step_completed();
1348
					// if the 'finalize_registration' step has been initiated (has a timestamp)
1349
					// but has not yet been fully completed (TRUE)
1350
					if ( is_int( $finalized ) && $finalized !== false && $finalized !== true ) {
1351
						$this->set_reg_step_completed( 'finalize_registration' );
1352
						$this->save();
1353
					}
1354
				}
1355
			}
1356
		}
1357
	}
1358
1359
}/* End of file EE_Transaction.class.php */
1360
/* Location: includes/classes/EE_Transaction.class.php */
1361