Completed
Branch FET-9414-allow-prices-to-be-mo... (2c9812)
by
unknown
654:02 queued 637:10
created

Transactions_Admin_Page   F

Complexity

Total Complexity 197

Size/Duplication

Total Lines 1805
Duplicated Lines 3.43 %

Coupling/Cohesion

Components 2
Dependencies 39

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 62
loc 1805
rs 0.5217
wmc 197
lcom 2
cbo 39

50 Methods

Rating   Name   Duplication   Size   Complexity  
B _transaction_legend_items() 9 84 6
A __construct() 0 3 1
A _init_page_props() 6 6 1
A _ajax_hooks() 0 5 1
A _define_page_props() 10 10 1
B _set_page_routes() 0 46 3
A _set_page_config() 0 63 2
A _add_screen_options() 0 1 1
A _add_feature_pointers() 0 1 1
A admin_init() 0 20 2
A admin_notices() 0 1 1
A admin_footer_scripts() 0 1 1
A _set_transaction_status_array() 0 3 1
A get_transaction_status_array() 0 3 1
A _get_payment_status_array() 0 5 1
A _add_screen_options_default() 0 3 1
A load_scripts_styles() 0 13 1
A load_scripts_styles_view_transaction() 0 4 1
A load_scripts_styles_default() 0 4 1
A _set_list_table_views_default() 0 19 1
B _set_transaction_object() 0 17 5
A _transactions_overview_list_table() 0 7 3
F _transaction_details() 8 171 21
A _transaction_details_metaboxes() 0 18 1
F txn_details_meta_box() 0 83 11
C _get_registration_payment_IDs() 0 27 8
C _get_registrations_to_apply_payment_to() 0 52 8
A _get_reg_status_selection() 0 10 1
A _get_payment_methods() 0 16 4
C txn_attendees_meta_box() 0 60 10
A txn_registrant_side_meta_box() 0 16 3
A txn_billing_info_side_meta_box() 0 11 1
B apply_payments_or_refunds() 0 44 4
C _validate_payment_request_data() 7 29 7
B _generate_payment_form_section() 0 93 1
B _create_payment_from_request_data() 0 38 5
A _process_transaction_payments() 0 13 2
A _get_REG_IDs_to_apply_payment_to() 0 14 4
A existing_reg_payment_REG_IDs() 0 3 1
A set_existing_reg_payment_REG_IDs() 0 3 1
A _get_existing_reg_payment_REG_IDs() 0 10 3
A _remove_existing_registration_payments() 0 21 3
A _update_registration_payments() 0 21 3
A _process_registration_status_change() 0 18 3
B _build_payment_json_response() 0 34 4
C delete_payment() 0 42 7
A _registration_payment_data_array() 0 16 4
C _maybe_send_notifications() 22 28 7
A _send_payment_reminder() 0 7 3
F get_transactions() 0 108 30

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Transactions_Admin_Page often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Transactions_Admin_Page, and based on these observations, apply Extract Interface, too.

1
<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed');
2
/**
3
 * Event Espresso
4
 *
5
 * Event Registration and Management Plugin for WordPress
6
 *
7
 * @ package			Event Espresso
8
 * @ author				Seth Shoultes
9
 * @ copyright		(c) 2008-2011 Event Espresso  All Rights Reserved.
10
 * @ license			{@link http://eventespresso.com/support/terms-conditions/}   * see Plugin Licensing *
11
 * @ link					{@link http://www.eventespresso.com}
12
 * @ since		 		4.0
13
 *
14
 * ------------------------------------------------------------------------
15
 *
16
 * EE_Admin_Transactions class
17
 *
18
 * @package			Event Espresso
19
 * @subpackage	includes/core/admin/transactions/Transactions_Admin_Page.core.php
20
 * @author				Brent Christensen
21
 *
22
 * ------------------------------------------------------------------------
23
 */
24
class Transactions_Admin_Page extends EE_Admin_Page {
25
26
	/**
27
	 * @var EE_Transaction
28
	 */
29
	private $_transaction;
30
31
	/**
32
	 * @var EE_Session
33
	 */
34
	private $_session;
35
36
	/**
37
	 * @var array $_txn_status
38
	 */
39
	private static $_txn_status;
40
41
	/**
42
	 * @var array $_pay_status
43
	 */
44
	private static $_pay_status;
45
46
	/**
47
	 * @var array $_existing_reg_payment_REG_IDs
48
	 */
49
	protected $_existing_reg_payment_REG_IDs = null;
50
51
52
53
	/**
54
	 * @Constructor
55
	 * @access public
56
	 * @param bool $routing
57
	 * @return Transactions_Admin_Page
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
58
	 */
59
	public function __construct( $routing = TRUE ) {
60
		parent::__construct( $routing );
61
	}
62
63
64
65
	/**
66
	 * 	_init_page_props
67
	 * @return void
68
	 */
69 View Code Duplication
	protected function _init_page_props() {
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...
70
		$this->page_slug = TXN_PG_SLUG;
71
		$this->page_label = __('Transactions', 'event_espresso');
72
		$this->_admin_base_url = TXN_ADMIN_URL;
73
		$this->_admin_base_path = TXN_ADMIN;
74
	}
75
76
77
78
	/**
79
	 * 	_ajax_hooks
80
	 * @return void
81
	 */
82
	protected function _ajax_hooks() {
83
		add_action('wp_ajax_espresso_apply_payment', array( $this, 'apply_payments_or_refunds'));
84
		add_action('wp_ajax_espresso_apply_refund', array( $this, 'apply_payments_or_refunds'));
85
		add_action('wp_ajax_espresso_delete_payment', array( $this, 'delete_payment'));
86
	}
87
88
89
90
	/**
91
	 * 	_define_page_props
92
	 * @return void
93
	 */
94 View Code Duplication
	protected function  _define_page_props() {
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...
95
		$this->_admin_page_title = $this->page_label;
96
		$this->_labels = array(
97
			'buttons' => array(
98
				'add' => __('Add New Transaction', 'event_espresso'),
99
				'edit' => __('Edit Transaction', 'event_espresso'),
100
				'delete' => __('Delete Transaction','event_espresso'),
101
			)
102
		);
103
	}
104
105
106
107
	/**
108
	 * 		grab url requests and route them
109
	*		@access private
110
	*		@return void
111
	*/
112
	public function _set_page_routes() {
113
114
		$this->_set_transaction_status_array();
115
116
		$txn_id = ! empty( $this->_req_data['TXN_ID'] ) && ! is_array( $this->_req_data['TXN_ID'] ) ? $this->_req_data['TXN_ID'] : 0;
117
118
		$this->_page_routes = array(
119
120
				'default' => array(
121
					'func' => '_transactions_overview_list_table',
122
					'capability' => 'ee_read_transactions'
123
					),
124
125
				'view_transaction' => array(
126
					'func' => '_transaction_details',
127
					'capability' => 'ee_read_transaction',
128
					'obj_id' => $txn_id
129
					),
130
131
				'send_payment_reminder'	=> array(
132
					'func' => '_send_payment_reminder',
133
					'noheader' => TRUE,
134
					'capability' => 'ee_send_message'
135
					),
136
137
				'espresso_apply_payment' => array(
138
				 	'func' => 'apply_payments_or_refunds',
139
				 	'noheader' => TRUE,
140
				 	'capability' => 'ee_edit_payments'
141
				 	),
142
143
				'espresso_apply_refund'	=> array(
144
					'func' => 'apply_payments_or_refunds',
145
					'noheader' => TRUE,
146
					'capability' => 'ee_edit_payments'
147
					),
148
149
				'espresso_delete_payment' => array(
150
					'func' => 'delete_payment',
151
					'noheader' => TRUE,
152
					'capability' => 'ee_delete_payments'
153
					),
154
155
		);
156
157
	}
158
159
160
161
162
163
164
165
166
	protected function _set_page_config() {
167
		$this->_page_config = array(
168
			'default' => array(
169
				'nav' => array(
170
					'label' => __('Overview', 'event_espresso'),
171
					'order' => 10
172
					),
173
				'list_table' => 'EE_Admin_Transactions_List_Table',
174
				'help_tabs' => array(
175
					'transactions_overview_help_tab' => array(
176
						'title' => __('Transactions Overview', 'event_espresso'),
177
						'filename' => 'transactions_overview'
178
					),
179
					'transactions_overview_table_column_headings_help_tab' => array(
180
						'title' => __('Transactions Table Column Headings', 'event_espresso'),
181
						'filename' => 'transactions_overview_table_column_headings'
182
					),
183
					'transactions_overview_views_filters_help_tab' => array(
184
						'title' => __('Transaction Views & Filters & Search', 'event_espresso'),
185
						'filename' => 'transactions_overview_views_filters_search'
186
					),
187
				),
188
				'help_tour' => array( 'Transactions_Overview_Help_Tour' ),
189
				/**
190
				 * commented out because currently we are not displaying tips for transaction list table status but this
191
				 * may change in a later iteration so want to keep the code for then.
192
				 */
193
				//'qtips' => array( 'Transactions_List_Table_Tips' ),
194
				'require_nonce' => FALSE
195
				),
196
			'view_transaction' => array(
197
				'nav' => array(
198
					'label' => __('View Transaction', 'event_espresso'),
199
					'order' => 5,
200
					'url' => isset($this->_req_data['TXN_ID']) ? add_query_arg(array('TXN_ID' => $this->_req_data['TXN_ID'] ), $this->_current_page_view_url )  : $this->_admin_base_url,
201
					'persistent' => FALSE
202
					),
203
				'help_tabs' => array(
204
					'transactions_view_transaction_help_tab' => array(
205
						'title' => __('View Transaction', 'event_espresso'),
206
						'filename' => 'transactions_view_transaction'
207
					),
208
					'transactions_view_transaction_transaction_details_table_help_tab' => array(
209
						'title' => __('Transaction Details Table', 'event_espresso'),
210
						'filename' => 'transactions_view_transaction_transaction_details_table'
211
					),
212
					'transactions_view_transaction_attendees_registered_help_tab' => array(
213
						'title' => __('Attendees Registered', 'event_espresso'),
214
						'filename' => 'transactions_view_transaction_attendees_registered'
215
					),
216
					'transactions_view_transaction_views_primary_registrant_billing_information_help_tab' => array(
217
						'title' => __('Primary Registrant & Billing Information', 'event_espresso'),
218
						'filename' => 'transactions_view_transaction_primary_registrant_billing_information'
219
					),
220
				),
221
				'qtips' => array( 'Transaction_Details_Tips' ),
222
				'help_tour' => array( 'Transaction_Details_Help_Tour' ),
223
				'metaboxes' => array('_transaction_details_metaboxes'),
224
225
				'require_nonce' => FALSE
226
				)
227
		);
228
	}
229
230
231
	/**
232
	 * The below methods aren't used by this class currently
233
	 */
234
	protected function _add_screen_options() {}
235
	protected function _add_feature_pointers() {}
236
	public function admin_init() {
237
		// IF a registration was JUST added via the admin...
238
		if (
239
		isset(
240
			$this->_req_data[ 'redirect_from' ],
241
			$this->_req_data[ 'EVT_ID' ],
242
			$this->_req_data[ 'event_name' ]
243
		)
244
		) {
245
			// then set a cookie so that we can block any attempts to use
246
			// the back button as a way to enter another registration.
247
			setcookie( 'ee_registration_added', $this->_req_data[ 'EVT_ID' ], time() + WEEK_IN_SECONDS, '/' );
248
			// and update the global
249
			$_COOKIE[ 'ee_registration_added' ] = $this->_req_data[ 'EVT_ID' ];
250
		}
251
		EE_Registry::$i18n_js_strings[ 'invalid_server_response' ] = __( 'An error occurred! Your request may have been processed, but a valid response from the server was not received. Please refresh the page and try again.', 'event_espresso' );
252
		EE_Registry::$i18n_js_strings[ 'error_occurred' ] = __( 'An error occurred! Please refresh the page and try again.', 'event_espresso' );
253
		EE_Registry::$i18n_js_strings[ 'txn_status_array' ] = self::$_txn_status;
254
		EE_Registry::$i18n_js_strings[ 'pay_status_array' ] = self::$_pay_status;
255
	}
256
	public function admin_notices() {}
257
	public function admin_footer_scripts() {}
258
259
260
261
	/**
262
	 * _set_transaction_status_array
263
	 * sets list of transaction statuses
264
	*
265
	 * @access private
266
	*	@return void
267
	*/
268
	private function _set_transaction_status_array() {
269
		self::$_txn_status = EEM_Transaction::instance()->status_array(TRUE);
270
	}
271
272
273
274
	/**
275
	 * get_transaction_status_array
276
	 * return the transaction status array for wp_list_table
277
	 *
278
	 * @access public
279
	 * @return array
280
	 */
281
	public function get_transaction_status_array() {
282
		return self::$_txn_status;
283
	}
284
285
286
287
	/**
288
	 * 	get list of payment statuses
289
	*
290
	 * @access private
291
	*	@return void
292
	*/
293
	private function _get_payment_status_array() {
294
		self::$_pay_status = EEM_Payment::instance()->status_array(TRUE);
295
		$this->_template_args['payment_status'] = self::$_pay_status;
296
297
	}
298
299
300
301
	/**
302
	 * 	_add_screen_options_default
303
	 *
304
	 * 	@access protected
305
	 *	@return void
306
	 */
307
	protected function _add_screen_options_default() {
308
		$this->_per_page_screen_option();
309
	}
310
311
312
313
	/**
314
	 * load_scripts_styles
315
	 *
316
	 * @access public
317
	 *	@return void
318
	 */
319
	public function load_scripts_styles() {
320
		//enqueue style
321
		wp_register_style( 'espresso_txn', TXN_ASSETS_URL . 'espresso_transactions_admin.css', array(), EVENT_ESPRESSO_VERSION );
322
		wp_enqueue_style('espresso_txn');
323
324
		//scripts
325
		add_filter('FHEE_load_accounting_js', '__return_true');
326
327
		//scripts
328
		wp_register_script('espresso_txn', TXN_ASSETS_URL . 'espresso_transactions_admin.js', array('ee_admin_js', 'ee-datepicker', 'jquery-ui-datepicker', 'jquery-ui-draggable', 'ee-dialog', 'ee-accounting', 'ee-serialize-full-array'), EVENT_ESPRESSO_VERSION, TRUE);
329
		wp_enqueue_script('espresso_txn');
330
331
	}
332
333
334
335
	/**
336
	 * 	load_scripts_styles_view_transaction
337
	 *
338
	 *	@access public
339
	 *	@return void
340
	 */
341
	public function load_scripts_styles_view_transaction() {
342
		//styles
343
		wp_enqueue_style('espresso-ui-theme');
344
	}
345
346
347
348
	/**
349
	 * 	load_scripts_styles_default
350
	 *
351
	 * @access public
352
	 *	@return void
353
	 */
354
	public function load_scripts_styles_default() {
355
		//styles
356
		wp_enqueue_style('espresso-ui-theme');
357
	}
358
359
360
361
	/**
362
	 * 	_set_list_table_views_default
363
	 *
364
	 *	@access protected
365
	 *	@return void
366
	 */
367
	protected function _set_list_table_views_default() {
368
		$this->_views = array (
369
			'all' => array (
370
				'slug' 		=> 'all',
371
				'label' 		=> __('View All Transactions', 'event_espresso'),
372
				'count' 	=> 0
373
				),
374
			'abandoned' => array(
375
				'slug' 		=> 'abandoned',
376
				'label' 		=> __('Abandoned Transactions', 'event_espresso'),
377
				'count' 	=> 0
378
			),
379
			'failed' => array(
380
				'slug' 		=> 'failed',
381
				'label' 		=> __('Failed Transactions', 'event_espresso'),
382
				'count' 	=> 0
383
			)
384
		);
385
	}
386
387
388
389
	/**
390
	 * _set_transaction_object
391
	 * This sets the _transaction property for the transaction details screen
392
	 *
393
	 *	@access private
394
	 *	@return void
395
	 */
396
	private function _set_transaction_object() {
397
		if ( is_object( $this->_transaction) )
398
			return; //get out we've already set the object
399
400
	    $TXN = EEM_Transaction::instance();
401
402
	    $TXN_ID = ( ! empty( $this->_req_data['TXN_ID'] )) ? absint( $this->_req_data['TXN_ID'] ) : FALSE;
403
404
	    //get transaction object
405
	    $this->_transaction = $TXN->get_one_by_ID($TXN_ID);
0 ignored issues
show
Documentation Bug introduced by
It seems like $TXN->get_one_by_ID($TXN_ID) can also be of type object<EE_Base_Class>. However, the property $_transaction is declared as type object<EE_Transaction>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
406
	    $this->_session = !empty( $this->_transaction ) ? $this->_transaction->get('TXN_session_data') : NULL;
407
408
	 	if ( empty( $this->_transaction ) ) {
409
	    	$error_msg = __('An error occurred and the details for Transaction ID #', 'event_espresso') . $TXN_ID .  __(' could not be retrieved.', 'event_espresso');
410
			EE_Error::add_error( $error_msg, __FILE__, __FUNCTION__, __LINE__ );
411
	    }
412
	}
413
414
415
416
	/**
417
	 * 	_transaction_legend_items
418
	 *
419
	 *	@access protected
420
	 *	@return array
421
	 */
422
	protected function _transaction_legend_items() {
423
		EE_Registry::instance()->load_helper( 'MSG_Template' );
424
		$items = array();
425
426 View Code Duplication
		if ( EE_Registry::instance()->CAP->current_user_can( 'ee_read_global_messages', 'view_filtered_messages' ) ) {
427
			$related_for_icon = EEH_MSG_Template::get_message_action_icon( 'see_notifications_for' );
428
			if ( isset( $related_for_icon['css_class']) && isset( $related_for_icon['label'] ) ) {
429
				$items['view_related_messages'] = array(
430
					'class' => $related_for_icon['css_class'],
431
					'desc' => $related_for_icon['label'],
432
				);
433
			}
434
		}
435
436
		$items = apply_filters(
437
			'FHEE__Transactions_Admin_Page___transaction_legend_items__items',
438
			array_merge( $items,
439
				array(
440
					'view_details' => array(
441
						'class' => 'dashicons dashicons-cart',
442
						'desc' => __('View Transaction Details', 'event_espresso')
443
					),
444
					'view_invoice' => array(
445
						'class' => 'dashicons dashicons-media-spreadsheet',
446
						'desc' => __('View Transaction Invoice', 'event_espresso')
447
					),
448
					'view_receipt' => array(
449
						'class' => 'dashicons dashicons-media-default',
450
						'desc' => __('View Transaction Receipt', 'event_espresso' )
451
					),
452
					'view_registration' => array(
453
						'class' => 'dashicons dashicons-clipboard',
454
						'desc' => __('View Registration Details', 'event_espresso')
455
					)
456
				)
457
			)
458
		);
459
460
		if ( EE_Registry::instance()->CAP->current_user_can( 'ee_send_message', 'espresso_transactions_send_payment_reminder' ) ) {
461
			if ( EEH_MSG_Template::is_mt_active( 'payment_reminder' ) ) {
462
				$items['send_payment_reminder'] = array(
463
					'class' => 'dashicons dashicons-email-alt',
464
					'desc' => __('Send Payment Reminder', 'event_espresso')
465
					);
466
			} else {
467
				$items['blank*'] = array(
468
					'class'=> '',
469
					'desc' => ''
470
					);
471
			}
472
		} else {
473
			$items['blank*'] = array(
474
				'class'=> '',
475
				'desc' => ''
476
				);
477
		}
478
		$more_items = apply_filters(
479
			'FHEE__Transactions_Admin_Page___transaction_legend_items__more_items',
480
			array(
481
				'overpaid'   => array(
482
					'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::overpaid_status_code,
483
					'desc'  => EEH_Template::pretty_status( EEM_Transaction::overpaid_status_code, FALSE, 'sentence' )
484
				),
485
				'complete'   => array(
486
					'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::complete_status_code,
487
					'desc'  => EEH_Template::pretty_status( EEM_Transaction::complete_status_code, FALSE, 'sentence' )
488
				),
489
				'incomplete' => array(
490
					'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::incomplete_status_code,
491
					'desc'  => EEH_Template::pretty_status( EEM_Transaction::incomplete_status_code, FALSE, 'sentence' )
492
				),
493
				'abandoned'  => array(
494
					'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::abandoned_status_code,
495
					'desc'  => EEH_Template::pretty_status( EEM_Transaction::abandoned_status_code, FALSE, 'sentence' )
496
				),
497
				'failed'     => array(
498
					'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::failed_status_code,
499
					'desc'  => EEH_Template::pretty_status( EEM_Transaction::failed_status_code, FALSE, 'sentence' )
500
				)
501
			)
502
		);
503
504
		return array_merge( $items, $more_items);
505
	}
506
507
508
509
	/**
510
	 * 	_transactions_overview_list_table
511
	 *
512
	 * @access protected
513
	 *	@return void
514
	 */
515
	protected function _transactions_overview_list_table() {
516
		$this->_admin_page_title = __('Transactions', 'event_espresso');
517
		$event = isset($this->_req_data['EVT_ID']) ? EEM_Event::instance()->get_one_by_ID($this->_req_data['EVT_ID'] ) : NULL;
518
		$this->_template_args['admin_page_header'] = $event instanceof EE_Event ? sprintf( __('%sViewing Transactions for the Event: %s%s', 'event_espresso'), '<h3>', '<a href="' . EE_Admin_Page::add_query_args_and_nonce(array('action' => 'edit', 'post' => $event->ID()), EVENTS_ADMIN_URL ) . '" title="' . esc_attr__('Click to Edit event', 'event_espresso') . '">' . $event->get('EVT_name') . '</a>', '</h3>' ) : '';
519
		$this->_template_args['after_list_table'] = $this->_display_legend( $this->_transaction_legend_items() );
520
		$this->display_admin_list_table_page_with_no_sidebar();
521
	}
522
523
524
525
	/**
526
	* 	_transaction_details
527
	 * generates HTML for the View Transaction Details Admin page
528
	*
529
	 * @access protected
530
	*	@return void
531
	*/
532
	protected function _transaction_details() {
533
		do_action( 'AHEE__Transactions_Admin_Page__transaction_details__start', $this->_transaction );
534
535
		$this->_set_transaction_status_array();
536
537
		$this->_template_args = array();
538
		$this->_template_args['transactions_page'] = $this->_wp_page_slug;
539
540
		$this->_set_transaction_object();
541
542
		$primary_registration = $this->_transaction->primary_registration();
543
		$attendee = $primary_registration instanceof EE_Registration ? $primary_registration->attendee() : NULL;
544
545
		$this->_template_args['txn_nmbr']['value'] = $this->_transaction->ID();
546
		$this->_template_args['txn_nmbr']['label'] = __( 'Transaction Number', 'event_espresso' );
547
548
		$this->_template_args['txn_datetime']['value'] = $this->_transaction->get_i18n_datetime('TXN_timestamp');
549
		$this->_template_args['txn_datetime']['label'] = __( 'Date', 'event_espresso' );
550
551
		$this->_template_args['txn_status']['value'] = self::$_txn_status[ $this->_transaction->get('STS_ID') ];
552
		$this->_template_args['txn_status']['label'] = __( 'Transaction Status', 'event_espresso' );
553
		$this->_template_args['txn_status']['class'] = 'status-' . $this->_transaction->get('STS_ID');
554
555
		$this->_template_args['grand_total'] = $this->_transaction->get('TXN_total');
556
		$this->_template_args['total_paid'] = $this->_transaction->get('TXN_paid');
557
558
		if (
559
			$attendee instanceof EE_Attendee
560
			&& EE_Registry::instance()->CAP->current_user_can(
561
				'ee_send_message',
562
				'espresso_transactions_send_payment_reminder'
563
			)
564
		) {
565
			$this->_template_args['send_payment_reminder_button'] =
566
				EEH_MSG_Template::is_mt_active( 'payment_reminder' )
567
				&& $this->_transaction->get('STS_ID') != EEM_Transaction::complete_status_code
568
				&& $this->_transaction->get('STS_ID') != EEM_Transaction::overpaid_status_code
569
					? EEH_Template::get_button_or_link(
570
						EE_Admin_Page::add_query_args_and_nonce(
571
							array(
572
								'action'=>'send_payment_reminder',
573
								'TXN_ID'=>$this->_transaction->ID(),
574
								'redirect_to' => 'view_transaction'
575
							),
576
							TXN_ADMIN_URL
577
						),
578
						__(' Send Payment Reminder', 'event_espresso'),
579
						'button secondary-button right',
580
						'dashicons dashicons-email-alt'
581
					)
582
				    : '';
583
		} else {
584
			$this->_template_args['send_payment_reminder_button'] = '';
585
		}
586
587
		$amount_due = $this->_transaction->get('TXN_total') - $this->_transaction->get('TXN_paid');
588
		$this->_template_args['amount_due'] = EEH_Template::format_currency( $amount_due, TRUE );
589
		if ( EE_Registry::instance()->CFG->currency->sign_b4 ) {
590
			$this->_template_args['amount_due'] = EE_Registry::instance()->CFG->currency->sign . $this->_template_args['amount_due'];
591
		} else {
592
			$this->_template_args['amount_due'] = $this->_template_args['amount_due'] . EE_Registry::instance()->CFG->currency->sign;
593
		}
594
		$this->_template_args['amount_due_class'] =  '';
595
596
		if ( $this->_transaction->get('TXN_paid') == $this->_transaction->get('TXN_total') ) {
597
			// paid in full
598
			$this->_template_args['amount_due'] =  FALSE;
599 View Code Duplication
		} elseif ( $this->_transaction->get('TXN_paid') > $this->_transaction->get('TXN_total') ) {
600
			// overpaid
601
			$this->_template_args['amount_due_class'] =  'txn-overview-no-payment-spn';
602
		} elseif (( $this->_transaction->get('TXN_total') > 0 ) && ( $this->_transaction->get('TXN_paid') > 0 )) {
603
			// monies owing
604
			$this->_template_args['amount_due_class'] =  'txn-overview-part-payment-spn';
605 View Code Duplication
		} elseif (( $this->_transaction->get('TXN_total') > 0 ) && ( $this->_transaction->get('TXN_paid') == 0 )) {
606
			// no payments made yet
607
			$this->_template_args['amount_due_class'] =  'txn-overview-no-payment-spn';
608
		} elseif ( $this->_transaction->get('TXN_total') == 0 ) {
609
			// free event
610
			$this->_template_args['amount_due'] =  FALSE;
611
		}
612
613
		$payment_method = $this->_transaction->payment_method();
614
615
		$this->_template_args['method_of_payment_name'] = $payment_method instanceof EE_Payment_Method
616
			? $payment_method->admin_name()
617
			: __( 'Unknown', 'event_espresso' );
618
619
		$this->_template_args['currency_sign'] = EE_Registry::instance()->CFG->currency->sign;
620
		// link back to overview
621
		$this->_template_args['txn_overview_url'] = ! empty ( $_SERVER['HTTP_REFERER'] )
622
			? $_SERVER['HTTP_REFERER']
623
			: TXN_ADMIN_URL;
624
625
626
		// next link
627
		$next_txn = $this->_transaction->next(
628
			null,
629
			array( array( 'STS_ID' => array( '!=', EEM_Transaction::failed_status_code ) ) ),
630
			'TXN_ID'
631
		);
632
		$this->_template_args['next_transaction'] = $next_txn
633
			? $this->_next_link(
634
				EE_Admin_Page::add_query_args_and_nonce(
635
					array( 'action' => 'view_transaction', 'TXN_ID' => $next_txn['TXN_ID'] ),
636
					TXN_ADMIN_URL
637
				),
638
				'dashicons dashicons-arrow-right ee-icon-size-22'
639
			)
640
			: '';
641
		// previous link
642
		$previous_txn = $this->_transaction->previous(
643
			null,
644
			array( array( 'STS_ID' => array( '!=', EEM_Transaction::failed_status_code ) ) ),
645
			'TXN_ID'
646
		);
647
		$this->_template_args['previous_transaction'] = $previous_txn
648
			? $this->_previous_link(
649
				EE_Admin_Page::add_query_args_and_nonce(
650
					array( 'action' => 'view_transaction', 'TXN_ID' => $previous_txn['TXN_ID'] ),
651
					TXN_ADMIN_URL
652
				),
653
				'dashicons dashicons-arrow-left ee-icon-size-22'
654
			)
655
			: '';
656
657
		// were we just redirected here after adding a new registration ???
658
		if (
659
			isset(
660
				$this->_req_data[ 'redirect_from' ],
661
				$this->_req_data[ 'EVT_ID' ],
662
				$this->_req_data[ 'event_name' ]
663
			)
664
		) {
665
			if (
666
				EE_Registry::instance()->CAP->current_user_can(
667
					'ee_edit_registrations',
668
					'espresso_registrations_new_registration',
669
					$this->_req_data[ 'EVT_ID' ]
670
				)
671
			) {
672
				$this->_admin_page_title .= '<a id="add-new-registration" class="add-new-h2 button-primary" href="';
673
				$this->_admin_page_title .= EE_Admin_Page::add_query_args_and_nonce(
674
					array(
675
						'page'     => 'espresso_registrations',
676
						'action'   => 'new_registration',
677
						'return'   => 'default',
678
						'TXN_ID'   => $this->_transaction->ID(),
679
						'event_id' => $this->_req_data[ 'EVT_ID' ],
680
					),
681
					REG_ADMIN_URL
682
				);
683
				$this->_admin_page_title .= '">';
684
685
				$this->_admin_page_title .= sprintf(
686
					__('Add Another New Registration to Event: "%1$s" ?'),
687
					htmlentities( urldecode( $this->_req_data[ 'event_name' ] ), ENT_QUOTES, 'UTF-8' )
688
				);
689
				$this->_admin_page_title .= '</a>';
690
			}
691
			EE_Registry::instance()->SSN->clear_session( __CLASS__, __FUNCTION__ );
692
		}
693
		// grab messages at the last second
694
		$this->_template_args['notices'] = EE_Error::get_notices();
695
		// path to template
696
		$template_path = TXN_TEMPLATE_PATH . 'txn_admin_details_header.template.php';
697
		$this->_template_args['admin_page_header'] = EEH_Template::display_template( $template_path, $this->_template_args, TRUE );
698
699
		// the details template wrapper
700
		$this->display_admin_page_with_sidebar();
701
702
	}
703
704
705
706
	/**
707
	 * 		_transaction_details_metaboxes
708
	 *
709
	 *		@access protected
710
	 *		@return void
711
	 */
712
	protected function _transaction_details_metaboxes() {
713
714
		$this->_set_transaction_object();
715
716
		add_meta_box( 'edit-txn-details-mbox', __( 'Transaction Details', 'event_espresso' ), array( $this, 'txn_details_meta_box' ), $this->_wp_page_slug, 'normal', 'high' );
717
		add_meta_box(
718
			'edit-txn-attendees-mbox',
719
			__( 'Attendees Registered in this Transaction', 'event_espresso' ),
720
			array( $this, 'txn_attendees_meta_box' ),
721
			$this->_wp_page_slug,
722
			'normal',
723
			'high',
724
			array( 'TXN_ID' => $this->_transaction->ID() )
725
		);
726
		add_meta_box( 'edit-txn-registrant-mbox', __( 'Primary Contact', 'event_espresso' ), array( $this, 'txn_registrant_side_meta_box' ), $this->_wp_page_slug, 'side', 'high' );
727
		add_meta_box( 'edit-txn-billing-info-mbox', __( 'Billing Information', 'event_espresso' ), array( $this, 'txn_billing_info_side_meta_box' ), $this->_wp_page_slug, 'side', 'high' );
728
729
	}
730
731
732
733
	/**
734
	 * txn_details_meta_box
735
	 * generates HTML for the Transaction main meta box
736
	*
737
	 * @access public
738
	*	@return void
739
	*/
740
	public function txn_details_meta_box() {
741
742
		$this->_set_transaction_object();
743
		$this->_template_args['TXN_ID'] = $this->_transaction->ID();
744
		$this->_template_args['attendee'] = $this->_transaction->primary_registration() instanceof EE_Registration ? $this->_transaction->primary_registration()->attendee() : null;
745
746
		//get line table
747
		EEH_Autoloader::register_line_item_display_autoloaders();
748
		$Line_Item_Display = new EE_Line_Item_Display( 'admin_table', 'EE_Admin_Table_Line_Item_Display_Strategy' );
749
		$this->_template_args['line_item_table'] = $Line_Item_Display->display_line_item( $this->_transaction->total_line_item() );
750
		$this->_template_args['REG_code'] = $this->_transaction->get_first_related('Registration')->get('REG_code');
751
752
		// process taxes
753
		$taxes = $this->_transaction->get_many_related( 'Line_Item', array( array( 'LIN_type' => EEM_Line_Item::type_tax )));
754
		$this->_template_args['taxes'] = ! empty( $taxes ) ? $taxes : FALSE;
755
756
		$this->_template_args['grand_total'] = EEH_Template::format_currency($this->_transaction->get('TXN_total'), FALSE, FALSE );
757
		$this->_template_args['grand_raw_total'] = $this->_transaction->get('TXN_total');
758
		$this->_template_args['TXN_status'] = $this->_transaction->get('STS_ID');
759
760
//		$txn_status_class = 'status-' . $this->_transaction->get('STS_ID');
761
762
		// process payment details
763
		$payments = $this->_transaction->get_many_related('Payment');
764
		if( ! empty(  $payments ) ) {
765
			$this->_template_args[ 'payments' ] = $payments;
766
			$this->_template_args[ 'existing_reg_payments' ] = $this->_get_registration_payment_IDs( $payments );
0 ignored issues
show
Documentation introduced by
$payments is of type array<integer,object<EE_Base_Class>>, but the function expects a array<integer,object<EE_Payment>>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
767
		} else {
768
			$this->_template_args[ 'payments' ] = false;
769
			$this->_template_args[ 'existing_reg_payments' ] = array();
770
		}
771
772
		$this->_template_args['edit_payment_url'] = add_query_arg( array( 'action' => 'edit_payment'  ), TXN_ADMIN_URL );
773
		$this->_template_args['delete_payment_url'] = add_query_arg( array( 'action' => 'espresso_delete_payment' ), TXN_ADMIN_URL );
774
775
		if ( isset( $txn_details['invoice_number'] )) {
0 ignored issues
show
Bug introduced by
The variable $txn_details seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
776
			$this->_template_args['txn_details']['invoice_number']['value'] = $this->_template_args['REG_code'];
777
			$this->_template_args['txn_details']['invoice_number']['label'] = __( 'Invoice Number', 'event_espresso' );
778
		}
779
780
		$this->_template_args['txn_details']['registration_session']['value'] = $this->_transaction->get_first_related('Registration')->get('REG_session');
781
		$this->_template_args['txn_details']['registration_session']['label'] = __( 'Registration Session', 'event_espresso' );
782
783
		$this->_template_args['txn_details']['ip_address']['value'] = isset( $this->_session['ip_address'] ) ? $this->_session['ip_address'] : '';
784
		$this->_template_args['txn_details']['ip_address']['label'] = __( 'Transaction placed from IP', 'event_espresso' );
785
786
		$this->_template_args['txn_details']['user_agent']['value'] = isset( $this->_session['user_agent'] ) ? $this->_session['user_agent'] : '';
787
		$this->_template_args['txn_details']['user_agent']['label'] = __( 'Registrant User Agent', 'event_espresso' );
788
789
		$reg_steps = '<ul>';
790
		foreach ( $this->_transaction->reg_steps() as $reg_step => $reg_step_status ) {
791
			if ( $reg_step_status === true ) {
792
				$reg_steps .= '<li style="color:#70cc50">' . sprintf( __( '%1$s : Completed', 'event_espresso' ), ucwords( str_replace( '_', ' ', $reg_step ) ) ) . '</li>';
793
			} else if ( is_numeric( $reg_step_status ) && $reg_step_status !== false ) {
794
					$reg_steps .= '<li style="color:#2EA2CC">' . sprintf(
795
							__( '%1$s : Initiated %2$s', 'event_espresso' ),
796
							ucwords( str_replace( '_', ' ', $reg_step ) ),
797
							gmdate( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), ( $reg_step_status + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) ) )
798
						) . '</li>';
799
				} else {
800
				$reg_steps .= '<li style="color:#E76700">' . sprintf( __( '%1$s : Never Initiated', 'event_espresso' ), ucwords( str_replace( '_', ' ', $reg_step ) ) ) . '</li>';
801
			}
802
		}
803
		$reg_steps .= '</ul>';
804
		$this->_template_args['txn_details']['reg_steps']['value'] = $reg_steps;
805
		$this->_template_args['txn_details']['reg_steps']['label'] = __( 'Registration Step Progress', 'event_espresso' );
806
807
808
		$this->_get_registrations_to_apply_payment_to();
809
		$this->_get_payment_methods( $payments );
810
		$this->_get_payment_status_array();
811
		$this->_get_reg_status_selection(); //sets up the template args for the reg status array for the transaction.
812
813
		$this->_template_args['transaction_form_url'] = add_query_arg( array( 'action' => 'edit_transaction', 'process' => 'transaction'  ), TXN_ADMIN_URL );
814
		$this->_template_args['apply_payment_form_url'] = add_query_arg( array( 'page' => 'espresso_transactions', 'action' => 'espresso_apply_payment' ), WP_AJAX_URL );
815
		$this->_template_args['delete_payment_form_url'] = add_query_arg( array( 'page' => 'espresso_transactions', 'action' => 'espresso_delete_payment' ), WP_AJAX_URL );
816
817
		// 'espresso_delete_payment_nonce'
818
819
		$template_path = TXN_TEMPLATE_PATH . 'txn_admin_details_main_meta_box_txn_details.template.php';
820
		echo EEH_Template::display_template( $template_path, $this->_template_args, TRUE );
821
822
	}
823
824
825
826
	/**
827
	 * _get_registration_payment_IDs
828
	 *
829
	 *    generates an array of Payment IDs and their corresponding Registration IDs
830
	 *
831
	 * @access protected
832
	 * @param EE_Payment[] $payments
833
	 * @return array
834
	 */
835
	protected function _get_registration_payment_IDs( $payments = array() ) {
836
		$existing_reg_payments = array();
837
		// get all reg payments for these payments
838
		$reg_payments = EEM_Registration_Payment::instance()->get_all( array(
839
			array(
840
				'PAY_ID' => array(
841
					'IN',
842
					array_keys( $payments )
843
				)
844
			)
845
		) );
846
		if ( ! empty( $reg_payments ) ) {
847
			foreach ( $payments as $payment ) {
848
				if ( ! $payment instanceof EE_Payment ) {
849
					continue;
850
				} else if ( ! isset( $existing_reg_payments[ $payment->ID() ] ) ) {
851
					$existing_reg_payments[ $payment->ID() ] = array();
852
				}
853
				foreach ( $reg_payments as $reg_payment ) {
854
					if ( $reg_payment instanceof EE_Registration_Payment && $reg_payment->payment_ID() === $payment->ID() ) {
855
						$existing_reg_payments[ $payment->ID() ][ ] = $reg_payment->registration_ID();
856
					}
857
				}
858
			}
859
		}
860
		return $existing_reg_payments;
861
	}
862
863
864
865
	/**
866
	 * _get_registrations_to_apply_payment_to
867
	 *
868
	 * 	generates HTML for displaying a series of checkboxes in the admin payment modal window
869
	 * which allows the admin to only apply the payment to the specific registrations
870
	 *
871
	 *	@access protected
872
	 * @return void
873
	 */
874
	protected function _get_registrations_to_apply_payment_to() {
875
		// we want any registration with an active status (ie: not deleted or cancelled)
876
		$query_params = array(
877
			array(
878
				'STS_ID' => array(
879
					'IN',
880
					array(
881
						EEM_Registration::status_id_approved,
882
						EEM_Registration::status_id_pending_payment,
883
						EEM_Registration::status_id_not_approved,
884
					)
885
				)
886
			)
887
		);
888
		$registrations_to_apply_payment_to = '<br /><div id="txn-admin-apply-payment-to-registrations-dv"  style="clear: both; margin: 1.5em 0 0; display: none;">';
889
		$registrations_to_apply_payment_to .= '<br /><div class="admin-primary-mbox-tbl-wrap">';
890
		$registrations_to_apply_payment_to .= '<table class="admin-primary-mbox-tbl">';
891
		$registrations_to_apply_payment_to .= '<thead><tr>';
892
		$registrations_to_apply_payment_to .= '<td>' . __( 'ID', 'event_espresso' ) . '</td>';
893
		$registrations_to_apply_payment_to .= '<td>' . __( 'Registrant', 'event_espresso' ) . '</td>';
894
		$registrations_to_apply_payment_to .= '<td>' . __( 'Ticket', 'event_espresso' ) . '</td>';
895
		$registrations_to_apply_payment_to .= '<td>' . __( 'Event', 'event_espresso' ) . '</td>';
896
		$registrations_to_apply_payment_to .= '<td class="txn-admin-payment-paid-td jst-cntr">' . __( 'Paid', 'event_espresso' ) . '</td>';
897
		$registrations_to_apply_payment_to .= '<td class="txn-admin-payment-owing-td jst-cntr">' . __( 'Owing', 'event_espresso' ) . '</td>';
898
		$registrations_to_apply_payment_to .= '<td class="jst-cntr">' . __( 'Apply', 'event_espresso' ) . '</td>';
899
		$registrations_to_apply_payment_to .= '</tr></thead><tbody>';
900
		// get registrations for TXN
901
		$registrations = $this->_transaction->registrations( $query_params );
902
		foreach ( $registrations as $registration ) {
903
			if ( $registration instanceof EE_Registration ) {
904
				$owing = $registration->final_price() - $registration->paid();
905
				$taxable = $registration->ticket()->taxable() ? ' <span class="smaller-text lt-grey-text"> ' . __( '+ tax', 'event_espresso' ) . '</span>' : '';
906
				$checked = empty( $existing_reg_payments ) || in_array( $registration->ID(), $existing_reg_payments ) ? ' checked="checked"' : '';
0 ignored issues
show
Bug introduced by
The variable $existing_reg_payments seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
907
				$registrations_to_apply_payment_to .= '<tr id="apply-payment-registration-row-' . $registration->ID() . '">';
908
				// add html for checkbox input and label
909
				$registrations_to_apply_payment_to .= '<td>' . $registration->ID() . '</td>';
910
				$registrations_to_apply_payment_to .= '<td>' . $registration->attendee() instanceof EE_Attendee ? $registration->attendee()->full_name() : __( 'Unknown Attendee', 'event_espresso' ) . '</td>';
911
				$registrations_to_apply_payment_to .= '<td>' . $registration->ticket()->name() . ' : ' . $registration->ticket()->pretty_price() . $taxable . '</td>';
912
				$registrations_to_apply_payment_to .= '<td>' . $registration->event_name() . '</td>';
913
				$registrations_to_apply_payment_to .= '<td class="txn-admin-payment-paid-td jst-rght">' . $registration->pretty_paid() . '</td>';
914
				$registrations_to_apply_payment_to .= '<td class="txn-admin-payment-owing-td jst-rght">' . EEH_Template::format_currency( $owing ) . '</td>';
915
				$registrations_to_apply_payment_to .= '<td class="jst-cntr">';
916
				$disabled = $registration->final_price() > 0 ? '' : ' disabled';
917
				$registrations_to_apply_payment_to .= '<input type="checkbox" value="' . $registration->ID() . '" name="txn_admin_payment[registrations]"' . $checked . $disabled . '>';
918
				$registrations_to_apply_payment_to .= '</td>';
919
				$registrations_to_apply_payment_to .= '</tr>';
920
			}
921
		}
922
		$registrations_to_apply_payment_to .= '</tbody></table></div>';
923
		$registrations_to_apply_payment_to .= '<p class="clear description">' . __( 'The payment will only be applied to the registrations that have a check mark in their corresponding check box. Checkboxes for free registrations have been disabled.', 'event_espresso' ) . '</p></div>';
924
		$this->_template_args[ 'registrations_to_apply_payment_to' ] = $registrations_to_apply_payment_to;
925
	}
926
927
928
929
	/**
930
	 * _get_reg_status_selection
931
	 *
932
	 * @todo this will need to be adjusted either once MER comes along OR we move default reg status to tickets instead of events.
933
	 *	@access protected
934
	 * @return void
935
	 */
936
	protected function _get_reg_status_selection() {
937
		//first get all possible statuses
938
		$statuses = EEM_Registration::reg_status_array(array(), TRUE);
939
		//let's add a "don't change" option.
940
		$status_array['NAN'] = __('Leave the Same', 'event_espresso');
0 ignored issues
show
Coding Style Comprehensibility introduced by
$status_array was never initialized. Although not strictly required by PHP, it is generally a good practice to add $status_array = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
941
		$status_array = array_merge( $status_array, $statuses );
942
		$this->_template_args['status_change_select'] = EEH_Form_Fields::select_input( 'txn_reg_status_change[reg_status]', $status_array, 'NAN', 'id="txn-admin-payment-reg-status-inp"', 'txn-reg-status-change-reg-status' );
943
		$this->_template_args['delete_status_change_select'] = EEH_Form_Fields::select_input( 'delete_txn_reg_status_change[reg_status]', $status_array, 'NAN', 'delete-txn-admin-payment-reg-status-inp', 'delete-txn-reg-status-change-reg-status' );
944
945
	}
946
947
948
949
	/**
950
	 * 	_get_payment_methods
951
	 * Gets all the payment methods available generally, or the ones that are already
952
	 * selected on these payments (in case their payment methods are no longer active).
953
	 * Has the side-effect of updating the template args' payment_methods item
954
	 *	@access private
955
	 * @param EE_Payment[] to show on this page
956
	 *	@return void
957
	 */
958
	private function _get_payment_methods( $payments = array() ) {
959
		$payment_methods_of_payments = array();
960
		foreach( $payments as $payment ){
961
			if( $payment instanceof EE_Payment ){
962
				$payment_methods_of_payments[] = $payment->get( 'PMD_ID' );
963
			}
964
		}
965
		if( $payment_methods_of_payments ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $payment_methods_of_payments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
966
			$query_args = array( array( 'OR*payment_method_for_payment' => array(
967
					'PMD_ID' => array( 'IN', $payment_methods_of_payments ),
968
					'PMD_scope' => array( 'LIKE', '%' . EEM_Payment_Method::scope_admin . '%' ) ) ) );
969
		}else{
970
			$query_args = array( array( 'PMD_scope' => array( 'LIKE', '%' . EEM_Payment_Method::scope_admin . '%' ) ) );
971
		}
972
		$this->_template_args['payment_methods'] = EEM_Payment_Method::instance()->get_all( $query_args );
973
	}
974
975
976
977
	/**
978
	 * txn_attendees_meta_box
979
	 *    generates HTML for the Attendees Transaction main meta box
980
	 *
981
	 * @access public
982
	 * @param WP_Post $post
983
	 * @param array $metabox
984
	 * @return void
985
	 */
986
	public function txn_attendees_meta_box( $post, $metabox = array( 'args' => array() )) {
987
988
		extract( $metabox['args'] );
989
		$this->_template_args['post'] = $post;
990
		$this->_template_args['event_attendees'] = array();
991
		// process items in cart
992
		$line_items = $this->_transaction->get_many_related('Line_Item', array( array( 'LIN_type' => 'line-item' ) ) );
993
		if ( ! empty( $line_items )) {
994
			foreach ( $line_items as $item ) {
995
				if ( $item instanceof EE_Line_Item ) {
996
					switch( $item->OBJ_type() ) {
997
998
						case 'Event' :
999
							break;
1000
1001
						case 'Ticket' :
1002
							$ticket = $item->ticket();
1003
							if ( empty( $ticket )) {
1004
								continue; //right now we're only handling tickets here.  Cause its expected that only tickets will have attendees right?
1005
							}
1006
							$ticket_price = EEH_Template::format_currency( $item->get( 'LIN_unit_price' ));
1007
							$event = $ticket->get_first_related('Registration')->get_first_related('Event');
1008
							$event_name = $event instanceof EE_Event ? $event->get('EVT_name') . ' - ' . $item->get('LIN_name') : '';
1009
1010
							$registrations = $ticket->get_many_related('Registration', array( array('TXN_ID' => $this->_transaction->ID() )));
1011
							foreach( $registrations as $registration ) {
1012
								$this->_template_args['event_attendees'][$registration->ID()]['att_num'] 						= $registration->get('REG_count');
1013
								$this->_template_args['event_attendees'][$registration->ID()]['event_ticket_name'] 	= $event_name;
1014
								$this->_template_args['event_attendees'][$registration->ID()]['ticket_price'] 				= $ticket_price;
1015
								// attendee info
1016
								$attendee = $registration->get_first_related('Attendee');
1017
								if ( $attendee instanceof EE_Attendee ) {
1018
									$this->_template_args['event_attendees'][$registration->ID()]['att_id'] 			= $attendee->ID();
1019
									$this->_template_args['event_attendees'][$registration->ID()]['attendee'] 	= $attendee->full_name();
1020
									$this->_template_args['event_attendees'][$registration->ID()]['email'] 			= '<a href="mailto:' . $attendee->email() . '?subject=' . $event->get('EVT_name') . __(' Event', 'event_espresso') . '">' . $attendee->email() . '</a>';
1021
									$this->_template_args['event_attendees'][$registration->ID()]['address'] 		=  implode(',<br>', $attendee->full_address_as_array() );
1022
								} else {
1023
									$this->_template_args['event_attendees'][$registration->ID()]['att_id'] 			= '';
1024
									$this->_template_args['event_attendees'][$registration->ID()]['attendee'] 	= '';
1025
									$this->_template_args['event_attendees'][$registration->ID()]['email'] 			= '';
1026
									$this->_template_args['event_attendees'][$registration->ID()]['address'] 		= '';
1027
								}
1028
							}
1029
							break;
1030
1031
					}
1032
				}
1033
			}
1034
1035
			$this->_template_args['transaction_form_url'] = add_query_arg( array( 'action' => 'edit_transaction', 'process' => 'attendees'  ), TXN_ADMIN_URL );
1036
			echo EEH_Template::display_template( TXN_TEMPLATE_PATH . 'txn_admin_details_main_meta_box_attendees.template.php', $this->_template_args, TRUE );
1037
1038
		} else {
1039
			echo sprintf(
1040
				__( '%1$sFor some reason, there are no attendees registered for this transaction. Likely the registration was abandoned in process.%2$s', 'event_espresso' ),
1041
				'<p class="important-notice">',
1042
				'</p>'
1043
			);
1044
		}
1045
	}
1046
1047
1048
1049
	/**
1050
	 * txn_registrant_side_meta_box
1051
	 * generates HTML for the Edit Transaction side meta box
1052
	 *
1053
	 * @access public
1054
	 * @throws \EE_Error
1055
	 * @return void
1056
	 */
1057
	public function txn_registrant_side_meta_box() {
1058
		$primary_att = $this->_transaction->primary_registration() instanceof EE_Registration ? $this->_transaction->primary_registration()->get_first_related('Attendee') : null;
1059
		if ( ! $primary_att instanceof EE_Attendee ) {
1060
			$this->_template_args['no_attendee_message'] = __('There is no attached contact for this transaction.  The transaction either failed due to an error or was abandoned.', 'event_espresso');
1061
			$primary_att = EEM_Attendee::instance()->create_default_object();
1062
		}
1063
		$this->_template_args['ATT_ID'] 						= $primary_att->ID();
1064
		$this->_template_args['prime_reg_fname']		= $primary_att->fname();
0 ignored issues
show
Documentation Bug introduced by
The method fname does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1065
		$this->_template_args['prime_reg_lname']		= $primary_att->lname();
0 ignored issues
show
Documentation Bug introduced by
The method lname does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1066
		$this->_template_args['prime_reg_email'] 		= $primary_att->email();
0 ignored issues
show
Documentation Bug introduced by
The method email does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1067
		$this->_template_args['prime_reg_phone'] 	= $primary_att->phone();
0 ignored issues
show
Documentation Bug introduced by
The method phone does not exist on object<EE_Base_Class>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1068
		$this->_template_args['edit_attendee_url'] 	= EE_Admin_Page::add_query_args_and_nonce( array( 'action' => 'edit_attendee', 'post' => $primary_att->ID()  ), REG_ADMIN_URL );
1069
		// get formatted address for registrant
1070
		$this->_template_args[ 'formatted_address' ] = EEH_Address::format( $primary_att );
1071
		echo EEH_Template::display_template( TXN_TEMPLATE_PATH . 'txn_admin_details_side_meta_box_registrant.template.php', $this->_template_args, TRUE );
1072
	}
1073
1074
1075
1076
	/**
1077
	 * txn_billing_info_side_meta_box
1078
	 * 	generates HTML for the Edit Transaction side meta box
1079
	*
1080
	 * @access public
1081
	*	@return void
1082
	*/
1083
	public function txn_billing_info_side_meta_box() {
1084
1085
		$this->_template_args['billing_form'] = $this->_transaction->billing_info();
1086
		$this->_template_args['billing_form_url'] = add_query_arg(
1087
			array( 'action' => 'edit_transaction', 'process' => 'billing'  ),
1088
			TXN_ADMIN_URL
1089
		);
1090
1091
		$template_path = TXN_TEMPLATE_PATH . 'txn_admin_details_side_meta_box_billing_info.template.php';
1092
		echo EEH_Template::display_template( $template_path, $this->_template_args, TRUE );/**/
1093
	}
1094
1095
1096
1097
	/**
1098
	 * apply_payments_or_refunds
1099
	 * 	registers a payment or refund made towards a transaction
1100
	*
1101
	 * @access public
1102
	*	@return void
1103
	*/
1104
	public function apply_payments_or_refunds() {
1105
		$json_response_data = array( 'return_data' => FALSE );
1106
		$valid_data = $this->_validate_payment_request_data();
1107
		if ( ! empty( $valid_data ) ) {
1108
			$PAY_ID = $valid_data[ 'PAY_ID' ];
1109
			//save  the new payment
1110
			$payment = $this->_create_payment_from_request_data( $valid_data );
1111
			// get the TXN for this payment
1112
			$transaction = $payment->transaction();
1113
			// verify transaction
1114
			if ( $transaction instanceof EE_Transaction ) {
1115
				// calculate_total_payments_and_update_status
1116
				$this->_process_transaction_payments( $transaction );
1117
				$REG_IDs = $this->_get_REG_IDs_to_apply_payment_to( $payment );
1118
				$this->_remove_existing_registration_payments( $payment, $PAY_ID );
1119
				// apply payment to registrations (if applicable)
1120
				if ( ! empty( $REG_IDs ) ) {
1121
					$this->_update_registration_payments( $transaction, $payment, $REG_IDs );
1122
					$this->_maybe_send_notifications();
1123
					// now process status changes for the same registrations
1124
					$this->_process_registration_status_change( $transaction, $REG_IDs );
1125
				}
1126
				$this->_maybe_send_notifications( $payment );
1127
				//prepare to render page
1128
				$json_response_data[ 'return_data' ] = $this->_build_payment_json_response( $payment, $REG_IDs );
1129
				do_action( 'AHEE__Transactions_Admin_Page__apply_payments_or_refund__after_recording', $transaction, $payment );
1130
			} else {
1131
				EE_Error::add_error(
1132
					__( 'A valid Transaction for this payment could not be retrieved.', 'event_espresso' ),
1133
					__FILE__, __FUNCTION__, __LINE__
1134
				);
1135
			}
1136
		} else {
1137
			EE_Error::add_error( __( 'The payment form data could not be processed. Please try again.', 'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
1138
		}
1139
1140
		$notices = EE_Error::get_notices( false, false, false );
1141
		$this->_template_args = array(
1142
			'data' => $json_response_data,
1143
			'error' => $notices['errors'],
1144
			'success' => $notices['success']
1145
		);
1146
		$this->_return_json();
1147
	}
1148
1149
1150
1151
	/**
1152
	 * _validate_payment_request_data
1153
	 *
1154
	 * @return array
1155
	 */
1156
	protected function _validate_payment_request_data() {
1157
		if ( ! isset( $this->_req_data[ 'txn_admin_payment' ] ) ) {
1158
			return false;
1159
		}
1160
		$payment_form = $this->_generate_payment_form_section();
1161
		try {
1162
			if ( $payment_form->was_submitted() ) {
1163
				$payment_form->receive_form_submission();
1164
				if ( ! $payment_form->is_valid() ) {
1165
					$submission_error_messages = array();
1166
					foreach ( $payment_form->get_validation_errors_accumulated() as $validation_error ) {
1167 View Code Duplication
						if ( $validation_error instanceof EE_Validation_Error ) {
1168
							$submission_error_messages[] = sprintf(
1169
								_x( '%s : %s', 'Form Section Name : Form Validation Error', 'event_espresso' ),
1170
								$validation_error->get_form_section()->html_label_text(),
1171
								$validation_error->getMessage()
1172
							);
1173
						}
1174
					}
1175
					EE_Error::add_error( join( '<br />', $submission_error_messages ), __FILE__, __FUNCTION__, __LINE__ );
1176
					return array();
1177
				}
1178
			}
1179
		} catch ( EE_Error $e ) {
1180
			EE_Error::add_error( $e->getMessage(), __FILE__, __FUNCTION__, __LINE__ );
1181
			return array();
1182
		}
1183
		return $payment_form->valid_data();
1184
	}
1185
1186
1187
1188
	/**
1189
	 * _generate_payment_form_section
1190
	 *
1191
	 * @return EE_Form_Section_Proper
1192
	 */
1193
	protected function _generate_payment_form_section() {
1194
		return new EE_Form_Section_Proper(
1195
			array(
1196
				'name' => 'txn_admin_payment',
1197
				'subsections'     => array(
1198
					'PAY_ID' => new EE_Text_Input(
1199
						array(
1200
							'default' => 0,
1201
							'required' => false,
1202
							'html_label_text' => __( 'Payment ID', 'event_espresso' ),
1203
							'validation_strategies' => array( new EE_Int_Normalization() )
1204
						)
1205
					),
1206
					'TXN_ID' => new EE_Text_Input(
1207
						array(
1208
							'default' => 0,
1209
							'required' => true,
1210
							'html_label_text' => __( 'Transaction ID', 'event_espresso' ),
1211
							'validation_strategies' => array( new EE_Int_Normalization() )
1212
						)
1213
					),
1214
					'type' => new EE_Text_Input(
1215
						array(
1216
							'default' => 1,
1217
							'required' => true,
1218
							'html_label_text' => __( 'Payment or Refund', 'event_espresso' ),
1219
							'validation_strategies' => array( new EE_Int_Normalization() )
1220
						)
1221
					),
1222
					'amount' => new EE_Text_Input(
1223
						array(
1224
							'default' => 0,
1225
							'required' => true,
1226
							'html_label_text' => __( 'Payment amount', 'event_espresso' ),
1227
							'validation_strategies' => array( new EE_Float_Normalization() )
1228
						)
1229
					),
1230
					'status' => new EE_Text_Input(
1231
						array(
1232
							'default' => EEM_Payment::status_id_approved,
1233
							'required' => true,
1234
							'html_label_text' => __( 'Payment status', 'event_espresso' ),
1235
						)
1236
					),
1237
					'PMD_ID' => new EE_Text_Input(
1238
						array(
1239
							'default' => 2,
1240
							'required' => true,
1241
							'html_label_text' => __( 'Payment Method', 'event_espresso' ),
1242
							'validation_strategies' => array( new EE_Int_Normalization() )
1243
						)
1244
					),
1245
					'date' => new EE_Text_Input(
1246
						array(
1247
							'default' => time(),
1248
							'required' => true,
1249
							'html_label_text' => __( 'Payment date', 'event_espresso' ),
1250
						)
1251
					),
1252
					'txn_id_chq_nmbr' => new EE_Text_Input(
1253
						array(
1254
							'default' => '',
1255
							'required' => false,
1256
							'html_label_text' => __( 'Transaction or Cheque Number', 'event_espresso' ),
1257
                                                        'validation_strategies' => array(
1258
                                                            new EE_Max_Length_Validation_Strategy( __('Input too long', 'event_espresso'), 100 ),
1259
                                                        )
1260
						)
1261
					),
1262
					'po_number' => new EE_Text_Input(
1263
						array(
1264
							'default' => '',
1265
							'required' => false,
1266
							'html_label_text' => __( 'Purchase Order Number', 'event_espresso' ),
1267
                                                        'validation_strategies' => array(
1268
                                                            new EE_Max_Length_Validation_Strategy( __('Input too long', 'event_espresso'), 100 ),
1269
                                                        )
1270
						)
1271
					),
1272
					'accounting' => new EE_Text_Input(
1273
						array(
1274
							'default' => '',
1275
							'required' => false,
1276
							'html_label_text' => __( 'Extra Field for Accounting', 'event_espresso' ),
1277
                                                        'validation_strategies' => array(
1278
                                                            new EE_Max_Length_Validation_Strategy( __('Input too long', 'event_espresso'), 100 ),
1279
                                                        )
1280
						)
1281
					),
1282
				)
1283
			)
1284
		);
1285
	}
1286
1287
1288
1289
	/**
1290
	 * _create_payment_from_request_data
1291
	 *
1292
	 * @param array $valid_data
1293
	 * @return EE_Payment
1294
	 */
1295
	protected function _create_payment_from_request_data( $valid_data ) {
1296
		$PAY_ID = $valid_data[ 'PAY_ID' ];
1297
		// get payment amount
1298
		$amount = $valid_data[ 'amount' ] ? abs( $valid_data[ 'amount' ] ) : 0;
1299
		// payments have a type value of 1 and refunds have a type value of -1
1300
		// so multiplying amount by type will give a positive value for payments, and negative values for refunds
1301
		$amount = $valid_data[ 'type' ] < 0 ? $amount * -1 : $amount;
1302
		// for some reason the date string coming in has extra spaces between the date and time.  This fixes that.
1303
		$date = $valid_data['date'] ? preg_replace( '/\s+/', ' ', $valid_data['date'] ) : date( 'Y-m-d g:i a', current_time( 'timestamp' ) );
1304
		$payment = EE_Payment::new_instance(
1305
			array(
1306
				'TXN_ID' 								=> $valid_data[ 'TXN_ID' ],
1307
				'STS_ID' 								=> $valid_data[ 'status' ],
1308
				'PAY_timestamp' 				=> $date,
1309
				'PAY_source'           			=> EEM_Payment_Method::scope_admin,
1310
				'PMD_ID'               				=> $valid_data[ 'PMD_ID' ],
1311
				'PAY_amount'           			=> $amount,
1312
				'PAY_txn_id_chq_nmbr'  	=> $valid_data[ 'txn_id_chq_nmbr' ],
1313
				'PAY_po_number'        		=> $valid_data[ 'po_number' ],
1314
				'PAY_extra_accntng'    		=> $valid_data[ 'accounting' ],
1315
				'PAY_details'          				=> $valid_data,
1316
				'PAY_ID'               				=> $PAY_ID
1317
			),
1318
			'',
1319
			array( 'Y-m-d', 'g:i a' )
1320
		);
1321
1322
		if ( ! $payment->save() ) {
1323
			EE_Error::add_error(
1324
				sprintf(
1325
					__( 'Payment %1$d has not been successfully saved to the database.', 'event_espresso' ),
1326
					$payment->ID()
1327
				),
1328
				__FILE__, __FUNCTION__, __LINE__
1329
			);
1330
		}
1331
		return $payment;
1332
	}
1333
1334
1335
1336
	/**
1337
	 * _process_transaction_payments
1338
	 *
1339
	 * @param \EE_Transaction $transaction
1340
	 * @return array
1341
	 */
1342
	protected function _process_transaction_payments( EE_Transaction $transaction ) {
1343
		/** @type EE_Transaction_Payments $transaction_payments */
1344
		$transaction_payments = EE_Registry::instance()->load_class( 'Transaction_Payments' );
1345
		//update the transaction with this payment
1346
		if ( $transaction_payments->calculate_total_payments_and_update_status( $transaction ) ) {
1347
			EE_Error::add_success( __( 'The payment has been processed successfully.', 'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
1348
		} else {
1349
			EE_Error::add_error(
1350
				__( 'The payment was processed successfully but the amount paid for the transaction was not updated.', 'event_espresso' )
1351
				, __FILE__, __FUNCTION__, __LINE__
1352
			);
1353
		}
1354
	}
1355
1356
1357
1358
	/**
1359
	 * _get_REG_IDs_to_apply_payment_to
1360
	 *
1361
	 * returns a list of registration IDs that the payment will apply to
1362
	 *
1363
	 * @param \EE_Payment $payment
1364
	 * @return array
1365
	 */
1366
	protected function _get_REG_IDs_to_apply_payment_to( EE_Payment $payment ) {
1367
		$REG_IDs = array();
1368
		// grab array of IDs for specific registrations to apply changes to
1369
		if ( isset( $this->_req_data[ 'txn_admin_payment' ][ 'registrations' ] ) ) {
1370
			$REG_IDs = (array)$this->_req_data[ 'txn_admin_payment' ][ 'registrations' ];
1371
		}
1372
		//nothing specified ? then get all reg IDs
1373
		if ( empty( $REG_IDs ) ) {
1374
			$registrations = $payment->transaction()->registrations();
1375
			$REG_IDs = ! empty( $registrations ) ? array_keys( $registrations ) : $this->_get_existing_reg_payment_REG_IDs( $payment );
1376
		}
1377
		// ensure that REG_IDs are integers and NOT strings
1378
		return array_map( 'intval', $REG_IDs );
1379
	}
1380
1381
1382
1383
	/**
1384
	 * @return array
1385
	 */
1386
	public function existing_reg_payment_REG_IDs() {
1387
		return $this->_existing_reg_payment_REG_IDs;
1388
	}
1389
1390
1391
1392
	/**
1393
	 * @param array $existing_reg_payment_REG_IDs
1394
	 */
1395
	public function set_existing_reg_payment_REG_IDs( $existing_reg_payment_REG_IDs = null ) {
1396
		$this->_existing_reg_payment_REG_IDs = $existing_reg_payment_REG_IDs;
0 ignored issues
show
Documentation Bug introduced by
It seems like $existing_reg_payment_REG_IDs can be null. However, the property $_existing_reg_payment_REG_IDs is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
1397
	}
1398
1399
1400
1401
	/**
1402
	 * _get_existing_reg_payment_REG_IDs
1403
	 *
1404
	 * returns a list of registration IDs that the payment is currently related to
1405
	 * as recorded in the database
1406
	 *
1407
	 * @param \EE_Payment $payment
1408
	 * @return array
1409
	 */
1410
	protected function _get_existing_reg_payment_REG_IDs( EE_Payment $payment ) {
1411
		if ( $this->existing_reg_payment_REG_IDs() === null ) {
1412
			// let's get any existing reg payment records for this payment
1413
			$existing_reg_payment_REG_IDs = $payment->get_many_related( 'Registration' );
1414
			// but we only want the REG IDs, so grab the array keys
1415
			$existing_reg_payment_REG_IDs = ! empty( $existing_reg_payment_REG_IDs ) ? array_keys( $existing_reg_payment_REG_IDs ) : array();
1416
			$this->set_existing_reg_payment_REG_IDs( $existing_reg_payment_REG_IDs );
1417
		}
1418
		return $this->existing_reg_payment_REG_IDs();
1419
	}
1420
1421
1422
1423
	/**
1424
	 * _remove_existing_registration_payments
1425
	 *
1426
	 * this calculates the difference between existing relations
1427
	 * to the supplied payment and the new list registration IDs,
1428
	 * removes any related registrations that no longer apply,
1429
	 * and then updates the registration paid fields
1430
	 *
1431
	 * @param \EE_Payment $payment
1432
	 * @param int         $PAY_ID
1433
	 * @return bool;
0 ignored issues
show
Documentation introduced by
The doc-type bool; could not be parsed: Expected "|" or "end of type", but got ";" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1434
	 */
1435
	protected function _remove_existing_registration_payments( EE_Payment $payment, $PAY_ID = 0 ) {
1436
		// newly created payments will have nothing recorded for $PAY_ID
1437
		if ( $PAY_ID == 0 ) {
1438
			return false;
1439
		}
1440
		$existing_reg_payment_REG_IDs = $this->_get_existing_reg_payment_REG_IDs( $payment );
1441
		if ( empty( $existing_reg_payment_REG_IDs )) {
1442
			return false;
1443
		}
1444
		/** @type EE_Transaction_Payments $transaction_payments */
1445
		$transaction_payments = EE_Registry::instance()->load_class( 'Transaction_Payments' );
1446
		return $transaction_payments->delete_registration_payments_and_update_registrations(
1447
			$payment,
1448
			array(
1449
				array(
1450
					'PAY_ID' => $payment->ID(),
1451
					'REG_ID' => array( 'IN', $existing_reg_payment_REG_IDs ),
1452
				)
1453
			)
1454
		);
1455
	}
1456
1457
1458
1459
	/**
1460
	 * _update_registration_payments
1461
	 *
1462
	 * this applies the payments to the selected registrations
1463
	 * but only if they have not already been paid for
1464
	 *
1465
	 * @param  EE_Transaction $transaction
1466
	 * @param \EE_Payment $payment
1467
	 * @param array $REG_IDs
1468
	 * @return bool
1469
	 */
1470
	protected function _update_registration_payments( EE_Transaction $transaction, EE_Payment $payment, $REG_IDs = array() ) {
1471
		// we can pass our own custom set of registrations to EE_Payment_Processor::process_registration_payments()
1472
		// so let's do that using our set of REG_IDs from the form
1473
		$registration_query_where_params = array(
1474
			'REG_ID' => array( 'IN', $REG_IDs )
1475
		);
1476
		// but add in some conditions regarding payment,
1477
		// so that we don't apply payments to registrations that are free or have already been paid for
1478
		// but ONLY if the payment is NOT a refund ( ie: the payment amount is not negative )
1479
		if ( ! $payment->is_a_refund() ) {
1480
			$registration_query_where_params[ 'REG_final_price' ]  = array( '!=', 0 );
1481
			$registration_query_where_params[ 'REG_final_price*' ]  = array( '!=', 'REG_paid', true );
1482
		}
1483
		//EEH_Debug_Tools::printr( $registration_query_where_params, '$registration_query_where_params', __FILE__, __LINE__ );
1484
		$registrations = $transaction->registrations( array( $registration_query_where_params ) );
1485
		if ( ! empty( $registrations ) ) {
1486
			/** @type EE_Payment_Processor $payment_processor */
1487
			$payment_processor = EE_Registry::instance()->load_core( 'Payment_Processor' );
1488
			$payment_processor->process_registration_payments( $transaction, $payment, $registrations );
0 ignored issues
show
Documentation introduced by
$registrations is of type array<integer,object<EE_Base_Class>>, but the function expects a array<integer,object<EE_Registration>>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1489
		}
1490
	}
1491
1492
1493
1494
	/**
1495
	 * _process_registration_status_change
1496
	 *
1497
	 * This processes requested registration status changes for all the registrations
1498
	 * on a given transaction and (optionally) sends out notifications for the changes.
1499
	 *
1500
	 * @param  EE_Transaction $transaction
1501
	 * @param array $REG_IDs
1502
	 * @return bool
1503
	 */
1504
	protected function _process_registration_status_change( EE_Transaction $transaction, $REG_IDs = array() ) {
1505
		// first if there is no change in status then we get out.
1506
		if (
1507
			! isset( $this->_req_data['txn_reg_status_change'], $this->_req_data[ 'txn_reg_status_change' ][ 'reg_status' ] )
1508
			|| $this->_req_data['txn_reg_status_change']['reg_status'] == 'NAN'
1509
		) {
1510
			//no error message, no change requested, just nothing to do man.
1511
			return FALSE;
1512
		}
1513
		/** @type EE_Transaction_Processor $transaction_processor */
1514
		$transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1515
		// made it here dude?  Oh WOW.  K, let's take care of changing the statuses
1516
		return $transaction_processor->manually_update_registration_statuses(
1517
			$transaction,
1518
			sanitize_text_field( $this->_req_data[ 'txn_reg_status_change' ][ 'reg_status' ] ),
1519
			array( array( 'REG_ID' => array( 'IN', $REG_IDs ) ) )
1520
		);
1521
	}
1522
1523
1524
1525
	/**
1526
	 * _build_payment_json_response
1527
	 *
1528
	 * @access public
1529
	 * @param \EE_Payment $payment
1530
	 * @param array       $REG_IDs
1531
	 * @param bool | null        $delete_txn_reg_status_change
1532
	 * @return array
1533
	 */
1534
	protected function _build_payment_json_response( EE_Payment $payment, $REG_IDs = array(), $delete_txn_reg_status_change = null ) {
1535
		// was the payment deleted ?
1536
		if ( is_bool( $delete_txn_reg_status_change )) {
1537
			return array(
1538
				'PAY_ID' 				=> $payment->ID(),
1539
				'amount' 			=> $payment->amount(),
1540
				'total_paid' 			=> $payment->transaction()->paid(),
1541
				'txn_status' 			=> $payment->transaction()->status_ID(),
1542
				'pay_status' 		=> $payment->STS_ID(),
1543
				'registrations' 	=> $this->_registration_payment_data_array( $REG_IDs ),
1544
				'delete_txn_reg_status_change' => $delete_txn_reg_status_change,
1545
			);
1546
		} else {
1547
			$this->_get_payment_status_array();
1548
			return array(
1549
				'amount' 		=> $payment->amount(),
1550
				'total_paid' 		=> $payment->transaction()->paid(),
1551
				'txn_status' 		=> $payment->transaction()->status_ID(),
1552
				'pay_status' 	=> $payment->STS_ID(),
1553
				'PAY_ID'           => $payment->ID(),
1554
				'STS_ID' 			=> $payment->STS_ID(),
1555
				'status' 			=> self::$_pay_status[ $payment->STS_ID() ],
1556
				'date' 				=> $payment->timestamp( 'Y-m-d', 'h:i a' ),
1557
				'method' 		=> strtoupper( $payment->source() ),
1558
				'PM_ID' 			=> $payment->payment_method() ? $payment->payment_method()->ID() : 1,
1559
				'gateway' 		=> $payment->payment_method() ? $payment->payment_method()->admin_name() : __( "Unknown", 'event_espresso' ),
1560
				'gateway_response' 	=> $payment->gateway_response(),
1561
				'txn_id_chq_nmbr'  	=> $payment->txn_id_chq_nmbr(),
1562
				'po_number'        		=> $payment->po_number(),
1563
				'extra_accntng'    		=> $payment->extra_accntng(),
1564
				'registrations'    			=> $this->_registration_payment_data_array( $REG_IDs ),
1565
			);
1566
		}
1567
	}
1568
1569
1570
1571
	/**
1572
	 * delete_payment
1573
	 * 	delete a payment or refund made towards a transaction
1574
	*
1575
	 * @access public
1576
	*	@return void
1577
	*/
1578
	public function delete_payment() {
1579
		$json_response_data = array( 'return_data' => FALSE );
1580
		$PAY_ID = isset( $this->_req_data['delete_txn_admin_payment'], $this->_req_data['delete_txn_admin_payment']['PAY_ID'] ) ? absint( $this->_req_data['delete_txn_admin_payment']['PAY_ID'] ) : 0;
1581
		if ( $PAY_ID ) {
1582
			$delete_txn_reg_status_change = isset( $this->_req_data[ 'delete_txn_reg_status_change' ] ) ? $this->_req_data[ 'delete_txn_reg_status_change' ] : false;
1583
			$payment = EEM_Payment::instance()->get_one_by_ID( $PAY_ID );
1584
			if ( $payment instanceof EE_Payment ) {
1585
				$REG_IDs = $this->_get_existing_reg_payment_REG_IDs( $payment );
1586
				/** @type EE_Transaction_Payments $transaction_payments */
1587
				$transaction_payments = EE_Registry::instance()->load_class( 'Transaction_Payments' );
1588
				if ( $transaction_payments->delete_payment_and_update_transaction( $payment )) {
1589
					$json_response_data['return_data'] = $this->_build_payment_json_response( $payment, $REG_IDs, $delete_txn_reg_status_change );
1590
					if ( $delete_txn_reg_status_change ) {
1591
						$this->_req_data['txn_reg_status_change'] = $delete_txn_reg_status_change;
1592
						//MAKE sure we also add the delete_txn_req_status_change to the
1593
						//$_REQUEST global because that's how messages will be looking for it.
1594
						$_REQUEST['txn_reg_status_change'] = $delete_txn_reg_status_change;
1595
						$this->_maybe_send_notifications();
1596
						$this->_process_registration_status_change( $payment->transaction(), $REG_IDs );
0 ignored issues
show
Bug introduced by
It seems like $payment->transaction() can be null; however, _process_registration_status_change() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1597
					}
1598
				}
1599
			} else {
1600
				EE_Error::add_error(
1601
					__( 'Valid Payment data could not be retrieved from the database.', 'event_espresso' ),
1602
					__FILE__, __FUNCTION__, __LINE__
1603
				);
1604
			}
1605
		} else {
1606
			EE_Error::add_error(
1607
				__( 'A valid Payment ID was not received, therefore payment form data could not be loaded.', 'event_espresso' ),
1608
				__FILE__, __FUNCTION__, __LINE__
1609
			);
1610
		}
1611
		$notices = EE_Error::get_notices( false, false, false);
1612
		$this->_template_args = array(
1613
			'data' => $json_response_data,
1614
			'success' => $notices['success'],
1615
			'error' => $notices['errors'],
1616
			'attention' => $notices['attention']
1617
		);
1618
		$this->_return_json();
1619
	}
1620
1621
1622
1623
	/**
1624
	 * _registration_payment_data_array
1625
	 * adds info for 'owing' and 'paid' for each registration to the json response
1626
	 *
1627
	 * @access protected
1628
	 * @param array $REG_IDs
1629
	 * @return array
1630
	 */
1631
	protected function _registration_payment_data_array( $REG_IDs ) {
1632
		$registration_payment_data = array();
1633
		//if non empty reg_ids lets get an array of registrations and update the values for the apply_payment/refund rows.
1634
		if ( ! empty( $REG_IDs ) ) {
1635
			$registrations = EEM_Registration::instance()->get_all( array( array( 'REG_ID' => array( 'IN', $REG_IDs ) ) ) );
1636
			foreach ( $registrations as $registration ) {
1637
				if ( $registration instanceof EE_Registration ) {
1638
					$registration_payment_data[ $registration->ID() ] = array(
1639
						'paid' => $registration->pretty_paid(),
1640
						'owing' => EEH_Template::format_currency( $registration->final_price() - $registration->paid() ),
1641
					);
1642
				}
1643
			}
1644
		}
1645
		return $registration_payment_data;
1646
	}
1647
1648
1649
1650
	/**
1651
	 * _maybe_send_notifications
1652
	 *
1653
	 * determines whether or not the admin has indicated that notifications should be sent.
1654
	 * If so, will toggle a filter switch for delivering registration notices.
1655
	 * If passed an EE_Payment object, then it will trigger payment notifications instead.
1656
	 *
1657
	 * @access protected
1658
	 * @param \EE_Payment | null $payment
1659
	 */
1660
	protected function _maybe_send_notifications( $payment = null ) {
1661
		switch ( $payment instanceof EE_Payment ) {
1662
			// payment notifications
1663 View Code Duplication
			case true :
1664
				if (
1665
					isset(
1666
						$this->_req_data[ 'txn_payments' ],
1667
						$this->_req_data[ 'txn_payments' ][ 'send_notifications' ]
1668
					) &&
1669
					filter_var( $this->_req_data[ 'txn_payments' ][ 'send_notifications' ], FILTER_VALIDATE_BOOLEAN )
1670
				) {
1671
					$this->_process_payment_notification( $payment );
1672
				}
1673
				break;
1674
			// registration notifications
1675 View Code Duplication
			case false :
1676
				if (
1677
					isset(
1678
						$this->_req_data[ 'txn_reg_status_change' ],
1679
						$this->_req_data[ 'txn_reg_status_change' ][ 'send_notifications' ]
1680
					) &&
1681
					filter_var( $this->_req_data[ 'txn_reg_status_change' ][ 'send_notifications' ], FILTER_VALIDATE_BOOLEAN )
1682
				) {
1683
					add_filter( 'FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true' );
1684
				}
1685
				break;
1686
		}
1687
	}
1688
1689
1690
1691
	/**
1692
	 * _send_payment_reminder
1693
	 * 	generates HTML for the View Transaction Details Admin page
1694
	*
1695
	 * @access protected
1696
	*	@return void
1697
	*/
1698
	protected function _send_payment_reminder() {
1699
	    $TXN_ID = ( ! empty( $this->_req_data['TXN_ID'] )) ? absint( $this->_req_data['TXN_ID'] ) : FALSE;
1700
		$transaction = EEM_Transaction::instance()->get_one_by_ID( $TXN_ID );
1701
		$query_args = isset($this->_req_data['redirect_to'] ) ? array('action' => $this->_req_data['redirect_to'], 'TXN_ID' => $this->_req_data['TXN_ID'] ) : array();
1702
		do_action( 'AHEE__Transactions_Admin_Page___send_payment_reminder__process_admin_payment_reminder', $transaction );
1703
		$this->_redirect_after_action( FALSE, __('payment reminder', 'event_espresso'), __('sent', 'event_espresso'), $query_args, TRUE );
0 ignored issues
show
Documentation introduced by
FALSE is of type boolean, but the function expects a integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1704
	}
1705
1706
1707
1708
	/**
1709
	 *  get_transactions
1710
	 *    get transactions for given parameters (used by list table)
1711
	 *
1712
	 * @param  int     $perpage how many transactions displayed per page
1713
	 * @param  boolean $count return the count or objects
1714
	 * @param string   $view
1715
	 * @return mixed int = count || array of transaction objects
1716
	 */
1717
	public function get_transactions( $perpage, $count = FALSE, $view = '' ) {
1718
1719
		$TXN = EEM_Transaction::instance();
1720
1721
	    $start_date = isset( $this->_req_data['txn-filter-start-date'] ) ? wp_strip_all_tags( $this->_req_data['txn-filter-start-date'] ) : date( 'm/d/Y', strtotime( '-10 year' ));
1722
	    $end_date = isset( $this->_req_data['txn-filter-end-date'] ) ? wp_strip_all_tags( $this->_req_data['txn-filter-end-date'] ) : date( 'm/d/Y' );
1723
1724
	    //make sure our timestamps start and end right at the boundaries for each day
1725
	    $start_date = date( 'Y-m-d', strtotime( $start_date ) ) . ' 00:00:00';
1726
	    $end_date = date( 'Y-m-d', strtotime( $end_date ) ) . ' 23:59:59';
1727
1728
1729
	    //convert to timestamps
1730
	    $start_date = strtotime( $start_date );
1731
	    $end_date = strtotime( $end_date );
1732
1733
	    //makes sure start date is the lowest value and vice versa
1734
	    $start_date = min( $start_date, $end_date );
1735
	    $end_date = max( $start_date, $end_date );
1736
1737
	    //convert to correct format for query
1738
	$start_date = EEM_Transaction::instance()->convert_datetime_for_query( 'TXN_timestamp', date( 'Y-m-d H:i:s', $start_date ), 'Y-m-d H:i:s' );
1739
	$end_date = EEM_Transaction::instance()->convert_datetime_for_query( 'TXN_timestamp', date( 'Y-m-d H:i:s', $end_date ), 'Y-m-d H:i:s' );
1740
1741
1742
1743
	    //set orderby
1744
		$this->_req_data['orderby'] = ! empty($this->_req_data['orderby']) ? $this->_req_data['orderby'] : '';
1745
1746
		switch ( $this->_req_data['orderby'] ) {
1747
			case 'TXN_ID':
1748
				$orderby = 'TXN_ID';
1749
				break;
1750
			case 'ATT_fname':
1751
				$orderby = 'Registration.Attendee.ATT_fname';
1752
				break;
1753
			case 'event_name':
1754
				$orderby = 'Registration.Event.EVT_name';
1755
				break;
1756
			default: //'TXN_timestamp'
1757
				$orderby = 'TXN_timestamp';
1758
		}
1759
1760
		$sort = ( isset( $this->_req_data['order'] ) && ! empty( $this->_req_data['order'] )) ? $this->_req_data['order'] : 'DESC';
1761
		$current_page = isset( $this->_req_data['paged'] ) && !empty( $this->_req_data['paged'] ) ? $this->_req_data['paged'] : 1;
1762
		$per_page = isset( $perpage ) && !empty( $perpage ) ? $perpage : 10;
1763
		$per_page = isset( $this->_req_data['perpage'] ) && !empty( $this->_req_data['perpage'] ) ? $this->_req_data['perpage'] : $per_page;
1764
1765
		$offset = ($current_page-1)*$per_page;
1766
		$limit = array( $offset, $per_page );
1767
1768
		$_where = array(
1769
			'TXN_timestamp' => array('BETWEEN', array($start_date, $end_date) ),
1770
			'Registration.REG_count' => 1
1771
		);
1772
1773
		if ( isset( $this->_req_data['EVT_ID'] ) ) {
1774
			$_where['Registration.EVT_ID'] = $this->_req_data['EVT_ID'];
1775
		}
1776
1777
		if ( isset( $this->_req_data['s'] ) ) {
1778
			$search_string = '%' . $this->_req_data['s'] . '%';
1779
			$_where['OR'] = array(
1780
				'Registration.Event.EVT_name' => array( 'LIKE', $search_string ),
1781
				'Registration.Event.EVT_desc' => array( 'LIKE', $search_string ),
1782
				'Registration.Event.EVT_short_desc' => array( 'LIKE' , $search_string ),
1783
				'Registration.Attendee.ATT_full_name' => array( 'LIKE', $search_string ),
1784
				'Registration.Attendee.ATT_fname' => array( 'LIKE', $search_string ),
1785
				'Registration.Attendee.ATT_lname' => array( 'LIKE', $search_string ),
1786
				'Registration.Attendee.ATT_short_bio' => array( 'LIKE', $search_string ),
1787
				'Registration.Attendee.ATT_email' => array('LIKE', $search_string ),
1788
				'Registration.Attendee.ATT_address' => array( 'LIKE', $search_string ),
1789
				'Registration.Attendee.ATT_address2' => array( 'LIKE', $search_string ),
1790
				'Registration.Attendee.ATT_city' => array( 'LIKE', $search_string ),
1791
				'Registration.REG_final_price' => array( 'LIKE', $search_string ),
1792
				'Registration.REG_code' => array( 'LIKE', $search_string ),
1793
				'Registration.REG_count' => array( 'LIKE' , $search_string ),
1794
				'Registration.REG_group_size' => array( 'LIKE' , $search_string ),
1795
				'Registration.Ticket.TKT_name' => array( 'LIKE', $search_string ),
1796
				'Registration.Ticket.TKT_description' => array( 'LIKE', $search_string ),
1797
				'Payment.PAY_source' => array('LIKE', $search_string ),
1798
				'Payment.Payment_Method.PMD_name' => array('LIKE', $search_string ),
1799
				'TXN_session_data' => array( 'LIKE', $search_string ),
1800
				'Payment.PAY_txn_id_chq_nmbr' => array( 'LIKE', $search_string )
1801
				);
1802
		}
1803
1804
		//failed transactions
1805
		$failed = ( ! empty( $this->_req_data['status'] ) && $this->_req_data['status'] == 'failed' && ! $count ) || ( $count && $view == 'failed' ) ? TRUE: FALSE;
1806
		$abandoned = ( ! empty( $this->_req_data['status'] ) && $this->_req_data['status'] == 'abandoned' && ! $count ) || ( $count && $view == 'abandoned' ) ? TRUE: FALSE;
1807
1808
		if ( $failed ) {
1809
			$_where[ 'STS_ID' ] = EEM_Transaction::failed_status_code;
1810
		} else if ( $abandoned ) {
1811
				$_where['STS_ID'] = EEM_Transaction::abandoned_status_code;
1812
		} else {
1813
				$_where['STS_ID'] = array( '!=', EEM_Transaction::failed_status_code );
1814
				$_where['STS_ID*'] = array( '!=', EEM_Transaction::abandoned_status_code );
1815
		}
1816
1817
		$query_params = array( $_where, 'order_by' => array( $orderby => $sort ), 'limit' => $limit );
1818
1819
		$transactions = $count ? $TXN->count( array($_where), 'TXN_ID', TRUE ) : $TXN->get_all($query_params);
1820
1821
1822
		return $transactions;
1823
1824
	}
1825
1826
1827
1828
}
1829