Completed
Branch BUG-10220-spco-permalinks (9ba45e)
by
unknown
14:48 queued 11s
created

Transactions_Admin_Page::admin_init()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 17
nc 2
nop 0
dl 0
loc 26
rs 8.8571
c 0
b 0
f 0
1
<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2
    exit('No direct script access allowed');
3
}
4
5
/**
6
 * Event Espresso
7
 *
8
 * Event Registration and Management Plugin for WordPress
9
 *
10
 * @ package            Event Espresso
11
 * @ author                Seth Shoultes
12
 * @ copyright        (c) 2008-2011 Event Espresso  All Rights Reserved.
13
 * @ license            {@link http://eventespresso.com/support/terms-conditions/}   * see Plugin Licensing *
14
 * @ link                    {@link http://www.eventespresso.com}
15
 * @ since                4.0
16
 *
17
 * ------------------------------------------------------------------------
18
 *
19
 * EE_Admin_Transactions class
20
 *
21
 * @package               Event Espresso
22
 * @subpackage            includes/core/admin/transactions/Transactions_Admin_Page.core.php
23
 * @author                Brent Christensen
24
 *
25
 * ------------------------------------------------------------------------
26
 */
27
class Transactions_Admin_Page extends EE_Admin_Page
28
{
29
    
30
    /**
31
     * @var EE_Transaction
32
     */
33
    private $_transaction;
34
    
35
    /**
36
     * @var EE_Session
37
     */
38
    private $_session;
39
    
40
    /**
41
     * @var array $_txn_status
42
     */
43
    private static $_txn_status;
44
    
45
    /**
46
     * @var array $_pay_status
47
     */
48
    private static $_pay_status;
49
    
50
    /**
51
     * @var array $_existing_reg_payment_REG_IDs
52
     */
53
    protected $_existing_reg_payment_REG_IDs = null;
54
    
55
    
56
    /**
57
     * @Constructor
58
     * @access public
59
     *
60
     * @param bool $routing
61
     *
62
     * @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...
63
     */
64
    public function __construct($routing = true)
65
    {
66
        parent::__construct($routing);
67
    }
68
    
69
    
70
    /**
71
     *    _init_page_props
72
     * @return void
73
     */
74 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...
75
    {
76
        $this->page_slug        = TXN_PG_SLUG;
77
        $this->page_label       = esc_html__('Transactions', 'event_espresso');
78
        $this->_admin_base_url  = TXN_ADMIN_URL;
79
        $this->_admin_base_path = TXN_ADMIN;
80
    }
81
    
82
    
83
    /**
84
     *    _ajax_hooks
85
     * @return void
86
     */
87
    protected function _ajax_hooks()
88
    {
89
        add_action('wp_ajax_espresso_apply_payment', array($this, 'apply_payments_or_refunds'));
90
        add_action('wp_ajax_espresso_apply_refund', array($this, 'apply_payments_or_refunds'));
91
        add_action('wp_ajax_espresso_delete_payment', array($this, 'delete_payment'));
92
    }
93
    
94
    
95
    /**
96
     *    _define_page_props
97
     * @return void
98
     */
99 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...
100
    {
101
        $this->_admin_page_title = $this->page_label;
102
        $this->_labels           = array(
103
            'buttons' => array(
104
                'add'    => esc_html__('Add New Transaction', 'event_espresso'),
105
                'edit'   => esc_html__('Edit Transaction', 'event_espresso'),
106
                'delete' => esc_html__('Delete Transaction', 'event_espresso'),
107
            )
108
        );
109
    }
110
    
111
    
112
    /**
113
     *        grab url requests and route them
114
     * @access private
115
     * @return void
116
     */
117
    public function _set_page_routes()
118
    {
119
        
120
        $this->_set_transaction_status_array();
121
        
122
        $txn_id = ! empty($this->_req_data['TXN_ID']) && ! is_array($this->_req_data['TXN_ID']) ? $this->_req_data['TXN_ID'] : 0;
123
        
124
        $this->_page_routes = array(
125
            
126
            'default' => array(
127
                'func'       => '_transactions_overview_list_table',
128
                'capability' => 'ee_read_transactions'
129
            ),
130
            
131
            'view_transaction' => array(
132
                'func'       => '_transaction_details',
133
                'capability' => 'ee_read_transaction',
134
                'obj_id'     => $txn_id
135
            ),
136
            
137
            'send_payment_reminder' => array(
138
                'func'       => '_send_payment_reminder',
139
                'noheader'   => true,
140
                'capability' => 'ee_send_message'
141
            ),
142
            
143
            'espresso_apply_payment' => array(
144
                'func'       => 'apply_payments_or_refunds',
145
                'noheader'   => true,
146
                'capability' => 'ee_edit_payments'
147
            ),
148
            
149
            'espresso_apply_refund' => array(
150
                'func'       => 'apply_payments_or_refunds',
151
                'noheader'   => true,
152
                'capability' => 'ee_edit_payments'
153
            ),
154
            
155
            'espresso_delete_payment' => array(
156
                'func'       => 'delete_payment',
157
                'noheader'   => true,
158
                'capability' => 'ee_delete_payments'
159
            ),
160
        
161
        );
162
        
163
    }
164
    
165
    
166
    protected function _set_page_config()
167
    {
168
        $this->_page_config = array(
169
            'default'          => array(
170
                'nav'           => array(
171
                    'label' => esc_html__('Overview', 'event_espresso'),
172
                    'order' => 10
173
                ),
174
                'list_table'    => 'EE_Admin_Transactions_List_Table',
175
                'help_tabs'     => array(
176
                    'transactions_overview_help_tab'                       => array(
177
                        'title'    => esc_html__('Transactions Overview', 'event_espresso'),
178
                        'filename' => 'transactions_overview'
179
                    ),
180
                    'transactions_overview_table_column_headings_help_tab' => array(
181
                        'title'    => esc_html__('Transactions Table Column Headings', 'event_espresso'),
182
                        'filename' => 'transactions_overview_table_column_headings'
183
                    ),
184
                    'transactions_overview_views_filters_help_tab'         => array(
185
                        'title'    => esc_html__('Transaction Views & Filters & Search', 'event_espresso'),
186
                        'filename' => 'transactions_overview_views_filters_search'
187
                    ),
188
                ),
189
                'help_tour'     => array('Transactions_Overview_Help_Tour'),
190
                /**
191
                 * commented out because currently we are not displaying tips for transaction list table status but this
192
                 * may change in a later iteration so want to keep the code for then.
193
                 */
194
                //'qtips' => array( 'Transactions_List_Table_Tips' ),
195
                'require_nonce' => false
196
            ),
197
            'view_transaction' => array(
198
                'nav'       => array(
199
                    'label'      => esc_html__('View Transaction', 'event_espresso'),
200
                    'order'      => 5,
201
                    'url'        => isset($this->_req_data['TXN_ID']) ? add_query_arg(array('TXN_ID' => $this->_req_data['TXN_ID']),
202
                        $this->_current_page_view_url) : $this->_admin_base_url,
203
                    'persistent' => false
204
                ),
205
                'help_tabs' => array(
206
                    'transactions_view_transaction_help_tab'                                              => array(
207
                        'title'    => esc_html__('View Transaction', 'event_espresso'),
208
                        'filename' => 'transactions_view_transaction'
209
                    ),
210
                    'transactions_view_transaction_transaction_details_table_help_tab'                    => array(
211
                        'title'    => esc_html__('Transaction Details Table', 'event_espresso'),
212
                        'filename' => 'transactions_view_transaction_transaction_details_table'
213
                    ),
214
                    'transactions_view_transaction_attendees_registered_help_tab'                         => array(
215
                        'title'    => esc_html__('Attendees Registered', 'event_espresso'),
216
                        'filename' => 'transactions_view_transaction_attendees_registered'
217
                    ),
218
                    'transactions_view_transaction_views_primary_registrant_billing_information_help_tab' => array(
219
                        'title'    => esc_html__('Primary Registrant & Billing Information', 'event_espresso'),
220
                        'filename' => 'transactions_view_transaction_primary_registrant_billing_information'
221
                    ),
222
                ),
223
                'qtips'     => array('Transaction_Details_Tips'),
224
                'help_tour' => array('Transaction_Details_Help_Tour'),
225
                'metaboxes' => array('_transaction_details_metaboxes'),
226
                
227
                'require_nonce' => false
228
            )
229
        );
230
    }
231
    
232
    
233
    /**
234
     * The below methods aren't used by this class currently
235
     */
236
    protected function _add_screen_options()
237
    {
238
    }
239
    
240
    protected function _add_feature_pointers()
241
    {
242
    }
243
    
244
    public function admin_init()
245
    {
246
        // IF a registration was JUST added via the admin...
247
        if (
248
        isset(
249
            $this->_req_data['redirect_from'],
250
            $this->_req_data['EVT_ID'],
251
            $this->_req_data['event_name']
252
        )
253
        ) {
254
            // then set a cookie so that we can block any attempts to use
255
            // the back button as a way to enter another registration.
256
            setcookie('ee_registration_added', $this->_req_data['EVT_ID'], time() + WEEK_IN_SECONDS, '/');
257
            // and update the global
258
            $_COOKIE['ee_registration_added'] = $this->_req_data['EVT_ID'];
259
        }
260
        EE_Registry::$i18n_js_strings['invalid_server_response'] = esc_html__('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.',
261
            'event_espresso');
262
        EE_Registry::$i18n_js_strings['error_occurred']          = esc_html__('An error occurred! Please refresh the page and try again.',
263
            'event_espresso');
264
        EE_Registry::$i18n_js_strings['txn_status_array']        = self::$_txn_status;
265
        EE_Registry::$i18n_js_strings['pay_status_array']        = self::$_pay_status;
266
        EE_Registry::$i18n_js_strings['payments_total']          = esc_html__('Payments Total', 'event_espresso');
267
        EE_Registry::$i18n_js_strings['transaction_overpaid']    = esc_html__('This transaction has been overpaid ! Payments Total',
268
            'event_espresso');
269
    }
270
    
271
    public function admin_notices()
272
    {
273
    }
274
    
275
    public function admin_footer_scripts()
276
    {
277
    }
278
    
279
    
280
    /**
281
     * _set_transaction_status_array
282
     * sets list of transaction statuses
283
     *
284
     * @access private
285
     * @return void
286
     */
287
    private function _set_transaction_status_array()
288
    {
289
        self::$_txn_status = EEM_Transaction::instance()->status_array(true);
290
    }
291
    
292
    
293
    /**
294
     * get_transaction_status_array
295
     * return the transaction status array for wp_list_table
296
     *
297
     * @access public
298
     * @return array
299
     */
300
    public function get_transaction_status_array()
301
    {
302
        return self::$_txn_status;
303
    }
304
    
305
    
306
    /**
307
     *    get list of payment statuses
308
     *
309
     * @access private
310
     * @return void
311
     */
312
    private function _get_payment_status_array()
313
    {
314
        self::$_pay_status                      = EEM_Payment::instance()->status_array(true);
315
        $this->_template_args['payment_status'] = self::$_pay_status;
316
        
317
    }
318
    
319
    
320
    /**
321
     *    _add_screen_options_default
322
     *
323
     * @access protected
324
     * @return void
325
     */
326
    protected function _add_screen_options_default()
327
    {
328
        $this->_per_page_screen_option();
329
    }
330
    
331
    
332
    /**
333
     * load_scripts_styles
334
     *
335
     * @access public
336
     * @return void
337
     */
338
    public function load_scripts_styles()
339
    {
340
        //enqueue style
341
        wp_register_style('espresso_txn', TXN_ASSETS_URL . 'espresso_transactions_admin.css', array(),
342
            EVENT_ESPRESSO_VERSION);
343
        wp_enqueue_style('espresso_txn');
344
        
345
        //scripts
346
        add_filter('FHEE_load_accounting_js', '__return_true');
347
        
348
        //scripts
349
        wp_register_script('espresso_txn', TXN_ASSETS_URL . 'espresso_transactions_admin.js', array(
350
            'ee_admin_js',
351
            'ee-datepicker',
352
            'jquery-ui-datepicker',
353
            'jquery-ui-draggable',
354
            'ee-dialog',
355
            'ee-accounting',
356
            'ee-serialize-full-array'
357
        ), EVENT_ESPRESSO_VERSION, true);
358
        wp_enqueue_script('espresso_txn');
359
        
360
    }
361
    
362
    
363
    /**
364
     *    load_scripts_styles_view_transaction
365
     *
366
     * @access public
367
     * @return void
368
     */
369
    public function load_scripts_styles_view_transaction()
370
    {
371
        //styles
372
        wp_enqueue_style('espresso-ui-theme');
373
    }
374
    
375
    
376
    /**
377
     *    load_scripts_styles_default
378
     *
379
     * @access public
380
     * @return void
381
     */
382
    public function load_scripts_styles_default()
383
    {
384
        //styles
385
        wp_enqueue_style('espresso-ui-theme');
386
    }
387
    
388
    
389
    /**
390
     *    _set_list_table_views_default
391
     *
392
     * @access protected
393
     * @return void
394
     */
395
    protected function _set_list_table_views_default()
396
    {
397
        $this->_views = array(
398
            'all'       => array(
399
                'slug'  => 'all',
400
                'label' => esc_html__('View All Transactions', 'event_espresso'),
401
                'count' => 0
402
            ),
403
            'abandoned' => array(
404
                'slug'  => 'abandoned',
405
                'label' => esc_html__('Abandoned Transactions', 'event_espresso'),
406
                'count' => 0
407
            ),
408
            'failed'    => array(
409
                'slug'  => 'failed',
410
                'label' => esc_html__('Failed Transactions', 'event_espresso'),
411
                'count' => 0
412
            )
413
        );
414
    }
415
    
416
    
417
    /**
418
     * _set_transaction_object
419
     * This sets the _transaction property for the transaction details screen
420
     *
421
     * @access private
422
     * @return void
423
     */
424
    private function _set_transaction_object()
425
    {
426
        if (is_object($this->_transaction)) {
427
            return;
428
        } //get out we've already set the object
429
        
430
        $TXN = EEM_Transaction::instance();
431
        
432
        $TXN_ID = ( ! empty($this->_req_data['TXN_ID'])) ? absint($this->_req_data['TXN_ID']) : false;
433
        
434
        //get transaction object
435
        $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...
436
        $this->_session     = ! empty($this->_transaction) ? $this->_transaction->get('TXN_session_data') : null;
437
        $this->_transaction->verify_abandoned_transaction_status();
0 ignored issues
show
Documentation Bug introduced by
The method verify_abandoned_transaction_status 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...
438
        
439
        if (empty($this->_transaction)) {
440
            $error_msg = esc_html__('An error occurred and the details for Transaction ID #',
441
                    'event_espresso') . $TXN_ID . esc_html__(' could not be retrieved.', 'event_espresso');
442
            EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
443
        }
444
    }
445
    
446
    
447
    /**
448
     *    _transaction_legend_items
449
     *
450
     * @access protected
451
     * @return array
452
     */
453
    protected function _transaction_legend_items()
454
    {
455
        EE_Registry::instance()->load_helper('MSG_Template');
456
        $items = array();
457
        
458 View Code Duplication
        if (EE_Registry::instance()->CAP->current_user_can('ee_read_global_messages', 'view_filtered_messages')) {
459
            $related_for_icon = EEH_MSG_Template::get_message_action_icon('see_notifications_for');
460
            if (isset($related_for_icon['css_class']) && isset($related_for_icon['label'])) {
461
                $items['view_related_messages'] = array(
462
                    'class' => $related_for_icon['css_class'],
463
                    'desc'  => $related_for_icon['label'],
464
                );
465
            }
466
        }
467
        
468
        $items = apply_filters(
469
            'FHEE__Transactions_Admin_Page___transaction_legend_items__items',
470
            array_merge($items,
471
                array(
472
                    'view_details'      => array(
473
                        'class' => 'dashicons dashicons-cart',
474
                        'desc'  => esc_html__('View Transaction Details', 'event_espresso')
475
                    ),
476
                    'view_invoice'      => array(
477
                        'class' => 'dashicons dashicons-media-spreadsheet',
478
                        'desc'  => esc_html__('View Transaction Invoice', 'event_espresso')
479
                    ),
480
                    'view_receipt'      => array(
481
                        'class' => 'dashicons dashicons-media-default',
482
                        'desc'  => esc_html__('View Transaction Receipt', 'event_espresso')
483
                    ),
484
                    'view_registration' => array(
485
                        'class' => 'dashicons dashicons-clipboard',
486
                        'desc'  => esc_html__('View Registration Details', 'event_espresso')
487
                    )
488
                )
489
            )
490
        );
491
        
492
        if (EE_Registry::instance()->CAP->current_user_can('ee_send_message',
493
            'espresso_transactions_send_payment_reminder')
494
        ) {
495
            if (EEH_MSG_Template::is_mt_active('payment_reminder')) {
496
                $items['send_payment_reminder'] = array(
497
                    'class' => 'dashicons dashicons-email-alt',
498
                    'desc'  => esc_html__('Send Payment Reminder', 'event_espresso')
499
                );
500
            } else {
501
                $items['blank*'] = array(
502
                    'class' => '',
503
                    'desc'  => ''
504
                );
505
            }
506
        } else {
507
            $items['blank*'] = array(
508
                'class' => '',
509
                'desc'  => ''
510
            );
511
        }
512
        $more_items = apply_filters(
513
            'FHEE__Transactions_Admin_Page___transaction_legend_items__more_items',
514
            array(
515
                'overpaid'   => array(
516
                    'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::overpaid_status_code,
517
                    'desc'  => EEH_Template::pretty_status(EEM_Transaction::overpaid_status_code, false, 'sentence')
518
                ),
519
                'complete'   => array(
520
                    'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::complete_status_code,
521
                    'desc'  => EEH_Template::pretty_status(EEM_Transaction::complete_status_code, false, 'sentence')
522
                ),
523
                'incomplete' => array(
524
                    'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::incomplete_status_code,
525
                    'desc'  => EEH_Template::pretty_status(EEM_Transaction::incomplete_status_code, false, 'sentence')
526
                ),
527
                'abandoned'  => array(
528
                    'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::abandoned_status_code,
529
                    'desc'  => EEH_Template::pretty_status(EEM_Transaction::abandoned_status_code, false, 'sentence')
530
                ),
531
                'failed'     => array(
532
                    'class' => 'ee-status-legend ee-status-legend-' . EEM_Transaction::failed_status_code,
533
                    'desc'  => EEH_Template::pretty_status(EEM_Transaction::failed_status_code, false, 'sentence')
534
                )
535
            )
536
        );
537
        
538
        return array_merge($items, $more_items);
539
    }
540
    
541
    
542
    /**
543
     *    _transactions_overview_list_table
544
     *
545
     * @access protected
546
     * @return void
547
     */
548
    protected function _transactions_overview_list_table()
549
    {
550
        $this->_admin_page_title                   = esc_html__('Transactions', 'event_espresso');
551
        $event                                     = isset($this->_req_data['EVT_ID']) ? EEM_Event::instance()->get_one_by_ID($this->_req_data['EVT_ID']) : null;
552
        $this->_template_args['admin_page_header'] = $event instanceof EE_Event ? sprintf(esc_html__('%sViewing Transactions for the Event: %s%s',
553
            'event_espresso'), '<h3>',
554
            '<a href="' . EE_Admin_Page::add_query_args_and_nonce(array('action' => 'edit', 'post' => $event->ID()),
555
                EVENTS_ADMIN_URL) . '" title="' . esc_attr__('Click to Edit event',
556
                'event_espresso') . '">' . $event->get('EVT_name') . '</a>', '</h3>') : '';
557
        $this->_template_args['after_list_table']  = $this->_display_legend($this->_transaction_legend_items());
558
        $this->display_admin_list_table_page_with_no_sidebar();
559
    }
560
    
561
    
562
    /**
563
     *    _transaction_details
564
     * generates HTML for the View Transaction Details Admin page
565
     *
566
     * @access protected
567
     * @return void
568
     */
569
    protected function _transaction_details()
570
    {
571
        do_action('AHEE__Transactions_Admin_Page__transaction_details__start', $this->_transaction);
572
        
573
        $this->_set_transaction_status_array();
574
        
575
        $this->_template_args                      = array();
576
        $this->_template_args['transactions_page'] = $this->_wp_page_slug;
577
        
578
        $this->_set_transaction_object();
579
        
580
        $primary_registration = $this->_transaction->primary_registration();
581
        $attendee             = $primary_registration instanceof EE_Registration ? $primary_registration->attendee() : null;
582
        
583
        $this->_template_args['txn_nmbr']['value'] = $this->_transaction->ID();
584
        $this->_template_args['txn_nmbr']['label'] = esc_html__('Transaction Number', 'event_espresso');
585
        
586
        $this->_template_args['txn_datetime']['value'] = $this->_transaction->get_i18n_datetime('TXN_timestamp');
587
        $this->_template_args['txn_datetime']['label'] = esc_html__('Date', 'event_espresso');
588
        
589
        $this->_template_args['txn_status']['value'] = self::$_txn_status[$this->_transaction->get('STS_ID')];
590
        $this->_template_args['txn_status']['label'] = esc_html__('Transaction Status', 'event_espresso');
591
        $this->_template_args['txn_status']['class'] = 'status-' . $this->_transaction->get('STS_ID');
592
        
593
        $this->_template_args['grand_total'] = $this->_transaction->get('TXN_total');
594
        $this->_template_args['total_paid']  = $this->_transaction->get('TXN_paid');
595
        
596
        if (
597
            $attendee instanceof EE_Attendee
598
            && EE_Registry::instance()->CAP->current_user_can(
599
                'ee_send_message',
600
                'espresso_transactions_send_payment_reminder'
601
            )
602
        ) {
603
            $this->_template_args['send_payment_reminder_button'] =
604
                EEH_MSG_Template::is_mt_active('payment_reminder')
605
                && $this->_transaction->get('STS_ID') != EEM_Transaction::complete_status_code
606
                && $this->_transaction->get('STS_ID') != EEM_Transaction::overpaid_status_code
607
                    ? EEH_Template::get_button_or_link(
608
                    EE_Admin_Page::add_query_args_and_nonce(
609
                        array(
610
                            'action'      => 'send_payment_reminder',
611
                            'TXN_ID'      => $this->_transaction->ID(),
612
                            'redirect_to' => 'view_transaction'
613
                        ),
614
                        TXN_ADMIN_URL
615
                    ),
616
                    __(' Send Payment Reminder', 'event_espresso'),
617
                    'button secondary-button right',
618
                    'dashicons dashicons-email-alt'
619
                )
620
                    : '';
621
        } else {
622
            $this->_template_args['send_payment_reminder_button'] = '';
623
        }
624
        
625
        $amount_due                         = $this->_transaction->get('TXN_total') - $this->_transaction->get('TXN_paid');
626
        $this->_template_args['amount_due'] = EEH_Template::format_currency($amount_due, true);
627
        if (EE_Registry::instance()->CFG->currency->sign_b4) {
628
            $this->_template_args['amount_due'] = EE_Registry::instance()->CFG->currency->sign . $this->_template_args['amount_due'];
629
        } else {
630
            $this->_template_args['amount_due'] = $this->_template_args['amount_due'] . EE_Registry::instance()->CFG->currency->sign;
631
        }
632
        $this->_template_args['amount_due_class'] = '';
633
        
634
        if ($this->_transaction->get('TXN_paid') == $this->_transaction->get('TXN_total')) {
635
            // paid in full
636
            $this->_template_args['amount_due'] = false;
637 View Code Duplication
        } elseif ($this->_transaction->get('TXN_paid') > $this->_transaction->get('TXN_total')) {
638
            // overpaid
639
            $this->_template_args['amount_due_class'] = 'txn-overview-no-payment-spn';
640
        } elseif (($this->_transaction->get('TXN_total') > 0) && ($this->_transaction->get('TXN_paid') > 0)) {
641
            // monies owing
642
            $this->_template_args['amount_due_class'] = 'txn-overview-part-payment-spn';
643 View Code Duplication
        } elseif (($this->_transaction->get('TXN_total') > 0) && ($this->_transaction->get('TXN_paid') == 0)) {
644
            // no payments made yet
645
            $this->_template_args['amount_due_class'] = 'txn-overview-no-payment-spn';
646
        } elseif ($this->_transaction->get('TXN_total') == 0) {
647
            // free event
648
            $this->_template_args['amount_due'] = false;
649
        }
650
        
651
        $payment_method = $this->_transaction->payment_method();
652
        
653
        $this->_template_args['method_of_payment_name'] = $payment_method instanceof EE_Payment_Method
654
            ? $payment_method->admin_name()
655
            : esc_html__('Unknown', 'event_espresso');
656
        
657
        $this->_template_args['currency_sign'] = EE_Registry::instance()->CFG->currency->sign;
658
        // link back to overview
659
        $this->_template_args['txn_overview_url'] = ! empty ($_SERVER['HTTP_REFERER'])
660
            ? $_SERVER['HTTP_REFERER']
661
            : TXN_ADMIN_URL;
662
        
663
        
664
        // next link
665
        $next_txn                                 = $this->_transaction->next(
666
            null,
667
            array(array('STS_ID' => array('!=', EEM_Transaction::failed_status_code))),
668
            'TXN_ID'
669
        );
670
        $this->_template_args['next_transaction'] = $next_txn
671
            ? $this->_next_link(
672
                EE_Admin_Page::add_query_args_and_nonce(
673
                    array('action' => 'view_transaction', 'TXN_ID' => $next_txn['TXN_ID']),
674
                    TXN_ADMIN_URL
675
                ),
676
                'dashicons dashicons-arrow-right ee-icon-size-22'
677
            )
678
            : '';
679
        // previous link
680
        $previous_txn                                 = $this->_transaction->previous(
681
            null,
682
            array(array('STS_ID' => array('!=', EEM_Transaction::failed_status_code))),
683
            'TXN_ID'
684
        );
685
        $this->_template_args['previous_transaction'] = $previous_txn
686
            ? $this->_previous_link(
687
                EE_Admin_Page::add_query_args_and_nonce(
688
                    array('action' => 'view_transaction', 'TXN_ID' => $previous_txn['TXN_ID']),
689
                    TXN_ADMIN_URL
690
                ),
691
                'dashicons dashicons-arrow-left ee-icon-size-22'
692
            )
693
            : '';
694
        
695
        // were we just redirected here after adding a new registration ???
696
        if (
697
        isset(
698
            $this->_req_data['redirect_from'],
699
            $this->_req_data['EVT_ID'],
700
            $this->_req_data['event_name']
701
        )
702
        ) {
703
            if (
704
            EE_Registry::instance()->CAP->current_user_can(
705
                'ee_edit_registrations',
706
                'espresso_registrations_new_registration',
707
                $this->_req_data['EVT_ID']
708
            )
709
            ) {
710
                $this->_admin_page_title .= '<a id="add-new-registration" class="add-new-h2 button-primary" href="';
711
                $this->_admin_page_title .= EE_Admin_Page::add_query_args_and_nonce(
712
                    array(
713
                        'page'     => 'espresso_registrations',
714
                        'action'   => 'new_registration',
715
                        'return'   => 'default',
716
                        'TXN_ID'   => $this->_transaction->ID(),
717
                        'event_id' => $this->_req_data['EVT_ID'],
718
                    ),
719
                    REG_ADMIN_URL
720
                );
721
                $this->_admin_page_title .= '">';
722
                
723
                $this->_admin_page_title .= sprintf(
724
                    esc_html__('Add Another New Registration to Event: "%1$s" ?', 'event_espresso'),
725
                    htmlentities(urldecode($this->_req_data['event_name']), ENT_QUOTES, 'UTF-8')
726
                );
727
                $this->_admin_page_title .= '</a>';
728
            }
729
            EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
730
        }
731
        // grab messages at the last second
732
        $this->_template_args['notices'] = EE_Error::get_notices();
733
        // path to template
734
        $template_path                             = TXN_TEMPLATE_PATH . 'txn_admin_details_header.template.php';
735
        $this->_template_args['admin_page_header'] = EEH_Template::display_template($template_path,
736
            $this->_template_args, true);
737
        
738
        // the details template wrapper
739
        $this->display_admin_page_with_sidebar();
740
        
741
    }
742
    
743
    
744
    /**
745
     *        _transaction_details_metaboxes
746
     *
747
     * @access protected
748
     * @return void
749
     */
750
    protected function _transaction_details_metaboxes()
751
    {
752
        
753
        $this->_set_transaction_object();
754
        
755
        add_meta_box('edit-txn-details-mbox', esc_html__('Transaction Details', 'event_espresso'),
756
            array($this, 'txn_details_meta_box'), $this->_wp_page_slug, 'normal', 'high');
757
        add_meta_box(
758
            'edit-txn-attendees-mbox',
759
            esc_html__('Attendees Registered in this Transaction', 'event_espresso'),
760
            array($this, 'txn_attendees_meta_box'),
761
            $this->_wp_page_slug,
762
            'normal',
763
            'high',
764
            array('TXN_ID' => $this->_transaction->ID())
765
        );
766
        add_meta_box('edit-txn-registrant-mbox', esc_html__('Primary Contact', 'event_espresso'),
767
            array($this, 'txn_registrant_side_meta_box'), $this->_wp_page_slug, 'side', 'high');
768
        add_meta_box('edit-txn-billing-info-mbox', esc_html__('Billing Information', 'event_espresso'),
769
            array($this, 'txn_billing_info_side_meta_box'), $this->_wp_page_slug, 'side', 'high');
770
        
771
    }
772
    
773
    
774
    /**
775
     * txn_details_meta_box
776
     * generates HTML for the Transaction main meta box
777
     *
778
     * @access public
779
     * @return void
780
     */
781
    public function txn_details_meta_box()
782
    {
783
        
784
        $this->_set_transaction_object();
785
        $this->_template_args['TXN_ID']   = $this->_transaction->ID();
786
        $this->_template_args['attendee'] = $this->_transaction->primary_registration() instanceof EE_Registration ? $this->_transaction->primary_registration()->attendee() : null;
787
        
788
        //get line table
789
        EEH_Autoloader::register_line_item_display_autoloaders();
790
        $Line_Item_Display                       = new EE_Line_Item_Display('admin_table',
791
            'EE_Admin_Table_Line_Item_Display_Strategy');
792
        $this->_template_args['line_item_table'] = $Line_Item_Display->display_line_item($this->_transaction->total_line_item());
793
        $this->_template_args['REG_code']        = $this->_transaction->get_first_related('Registration')->get('REG_code');
794
        
795
        // process taxes
796
        $taxes                         = $this->_transaction->get_many_related('Line_Item',
797
            array(array('LIN_type' => EEM_Line_Item::type_tax)));
798
        $this->_template_args['taxes'] = ! empty($taxes) ? $taxes : false;
799
        
800
        $this->_template_args['grand_total']     = EEH_Template::format_currency($this->_transaction->get('TXN_total'),
801
            false, false);
802
        $this->_template_args['grand_raw_total'] = $this->_transaction->get('TXN_total');
803
        $this->_template_args['TXN_status']      = $this->_transaction->get('STS_ID');
804
805
//		$txn_status_class = 'status-' . $this->_transaction->get('STS_ID');
806
        
807
        // process payment details
808
        $payments = $this->_transaction->get_many_related('Payment');
809
        if ( ! empty($payments)) {
810
            $this->_template_args['payments']              = $payments;
811
            $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...
812
        } else {
813
            $this->_template_args['payments']              = false;
814
            $this->_template_args['existing_reg_payments'] = array();
815
        }
816
        
817
        $this->_template_args['edit_payment_url']   = add_query_arg(array('action' => 'edit_payment'), TXN_ADMIN_URL);
818
        $this->_template_args['delete_payment_url'] = add_query_arg(array('action' => 'espresso_delete_payment'),
819
            TXN_ADMIN_URL);
820
        
821
        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...
822
            $this->_template_args['txn_details']['invoice_number']['value'] = $this->_template_args['REG_code'];
823
            $this->_template_args['txn_details']['invoice_number']['label'] = esc_html__('Invoice Number',
824
                'event_espresso');
825
        }
826
        
827
        $this->_template_args['txn_details']['registration_session']['value'] = $this->_transaction->get_first_related('Registration')->get('REG_session');
828
        $this->_template_args['txn_details']['registration_session']['label'] = esc_html__('Registration Session',
829
            'event_espresso');
830
        
831
        $this->_template_args['txn_details']['ip_address']['value'] = isset($this->_session['ip_address']) ? $this->_session['ip_address'] : '';
832
        $this->_template_args['txn_details']['ip_address']['label'] = esc_html__('Transaction placed from IP',
833
            'event_espresso');
834
        
835
        $this->_template_args['txn_details']['user_agent']['value'] = isset($this->_session['user_agent']) ? $this->_session['user_agent'] : '';
836
        $this->_template_args['txn_details']['user_agent']['label'] = esc_html__('Registrant User Agent',
837
            'event_espresso');
838
        
839
        $reg_steps = '<ul>';
840
        foreach ($this->_transaction->reg_steps() as $reg_step => $reg_step_status) {
841
            if ($reg_step_status === true) {
842
                $reg_steps .= '<li style="color:#70cc50">' . sprintf(esc_html__('%1$s : Completed', 'event_espresso'),
843
                        ucwords(str_replace('_', ' ', $reg_step))) . '</li>';
844
            } else if (is_numeric($reg_step_status) && $reg_step_status !== false) {
845
                $reg_steps .= '<li style="color:#2EA2CC">' . sprintf(
846
                        esc_html__('%1$s : Initiated %2$s', 'event_espresso'),
847
                        ucwords(str_replace('_', ' ', $reg_step)),
848
                        date(get_option('date_format') . ' ' . get_option('time_format'),
849
                            ($reg_step_status + (get_option('gmt_offset') * HOUR_IN_SECONDS)))
850
                    ) . '</li>';
851
            } else {
852
                $reg_steps .= '<li style="color:#E76700">' . sprintf(esc_html__('%1$s : Never Initiated',
853
                        'event_espresso'), ucwords(str_replace('_', ' ', $reg_step))) . '</li>';
854
            }
855
        }
856
        $reg_steps .= '</ul>';
857
        $this->_template_args['txn_details']['reg_steps']['value'] = $reg_steps;
858
        $this->_template_args['txn_details']['reg_steps']['label'] = esc_html__('Registration Step Progress',
859
            'event_espresso');
860
        
861
        
862
        $this->_get_registrations_to_apply_payment_to();
863
        $this->_get_payment_methods($payments);
864
        $this->_get_payment_status_array();
865
        $this->_get_reg_status_selection(); //sets up the template args for the reg status array for the transaction.
866
        
867
        $this->_template_args['transaction_form_url']    = add_query_arg(array(
868
            'action'  => 'edit_transaction',
869
            'process' => 'transaction'
870
        ), TXN_ADMIN_URL);
871
        $this->_template_args['apply_payment_form_url']  = add_query_arg(array(
872
            'page'   => 'espresso_transactions',
873
            'action' => 'espresso_apply_payment'
874
        ), WP_AJAX_URL);
875
        $this->_template_args['delete_payment_form_url'] = add_query_arg(array(
876
            'page'   => 'espresso_transactions',
877
            'action' => 'espresso_delete_payment'
878
        ), WP_AJAX_URL);
879
        
880
        // 'espresso_delete_payment_nonce'
881
        
882
        $template_path = TXN_TEMPLATE_PATH . 'txn_admin_details_main_meta_box_txn_details.template.php';
883
        echo EEH_Template::display_template($template_path, $this->_template_args, true);
884
        
885
    }
886
    
887
    
888
    /**
889
     * _get_registration_payment_IDs
890
     *
891
     *    generates an array of Payment IDs and their corresponding Registration IDs
892
     *
893
     * @access protected
894
     *
895
     * @param EE_Payment[] $payments
896
     *
897
     * @return array
898
     */
899
    protected function _get_registration_payment_IDs($payments = array())
900
    {
901
        $existing_reg_payments = array();
902
        // get all reg payments for these payments
903
        $reg_payments = EEM_Registration_Payment::instance()->get_all(array(
904
            array(
905
                'PAY_ID' => array(
906
                    'IN',
907
                    array_keys($payments)
908
                )
909
            )
910
        ));
911
        if ( ! empty($reg_payments)) {
912
            foreach ($payments as $payment) {
913
                if ( ! $payment instanceof EE_Payment) {
914
                    continue;
915
                } else if ( ! isset($existing_reg_payments[$payment->ID()])) {
916
                    $existing_reg_payments[$payment->ID()] = array();
917
                }
918
                foreach ($reg_payments as $reg_payment) {
919
                    if ($reg_payment instanceof EE_Registration_Payment && $reg_payment->payment_ID() === $payment->ID()) {
920
                        $existing_reg_payments[$payment->ID()][] = $reg_payment->registration_ID();
921
                    }
922
                }
923
            }
924
        }
925
        
926
        return $existing_reg_payments;
927
    }
928
    
929
    
930
    /**
931
     * _get_registrations_to_apply_payment_to
932
     *    generates HTML for displaying a series of checkboxes in the admin payment modal window
933
     * which allows the admin to only apply the payment to the specific registrations
934
     *
935
     * @access protected
936
     * @return void
937
     * @throws \EE_Error
938
     */
939
    protected function _get_registrations_to_apply_payment_to()
940
    {
941
        // we want any registration with an active status (ie: not deleted or cancelled)
942
        $query_params                      = array(
943
            array(
944
                'STS_ID' => array(
945
                    'IN',
946
                    array(
947
                        EEM_Registration::status_id_approved,
948
                        EEM_Registration::status_id_pending_payment,
949
                        EEM_Registration::status_id_not_approved,
950
                    )
951
                )
952
            )
953
        );
954
        $registrations_to_apply_payment_to = EEH_HTML::br() . EEH_HTML::div(
955
                '', 'txn-admin-apply-payment-to-registrations-dv', '', 'clear: both; margin: 1.5em 0 0; display: none;'
956
            );
957
        $registrations_to_apply_payment_to .= EEH_HTML::br() . EEH_HTML::div('', '', 'admin-primary-mbox-tbl-wrap');
958
        $registrations_to_apply_payment_to .= EEH_HTML::table('', '', 'admin-primary-mbox-tbl');
959
        $registrations_to_apply_payment_to .= EEH_HTML::thead(
960
            EEH_HTML::tr(
961
                EEH_HTML::th(esc_html__('ID', 'event_espresso')) .
962
                EEH_HTML::th(esc_html__('Registrant', 'event_espresso')) .
963
                EEH_HTML::th(esc_html__('Ticket', 'event_espresso')) .
964
                EEH_HTML::th(esc_html__('Event', 'event_espresso')) .
965
                EEH_HTML::th(esc_html__('Paid', 'event_espresso'), '', 'txn-admin-payment-paid-td jst-cntr') .
966
                EEH_HTML::th(esc_html__('Owing', 'event_espresso'), '', 'txn-admin-payment-owing-td jst-cntr') .
967
                EEH_HTML::th(esc_html__('Apply', 'event_espresso'), '', 'jst-cntr')
968
            )
969
        );
970
        $registrations_to_apply_payment_to .= EEH_HTML::tbody();
971
        // get registrations for TXN
972
        $registrations = $this->_transaction->registrations($query_params);
973
        foreach ($registrations as $registration) {
974
            if ($registration instanceof EE_Registration) {
975
                $attendee_name = $registration->attendee() instanceof EE_Attendee
976
                    ? $registration->attendee()->full_name()
977
                    : esc_html__('Unknown Attendee', 'event_espresso');
978
                $owing         = $registration->final_price() - $registration->paid();
979
                $taxable       = $registration->ticket()->taxable()
980
                    ? ' <span class="smaller-text lt-grey-text"> ' . esc_html__('+ tax', 'event_espresso') . '</span>'
981
                    : '';
982
                $checked       = empty($existing_reg_payments) || in_array($registration->ID(), $existing_reg_payments)
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...
983
                    ? ' checked="checked"'
984
                    : '';
985
                $disabled      = $registration->final_price() > 0 ? '' : ' disabled';
986
                $registrations_to_apply_payment_to .= EEH_HTML::tr(
987
                    EEH_HTML::td($registration->ID()) .
988
                    EEH_HTML::td($attendee_name) .
989
                    EEH_HTML::td(
990
                        $registration->ticket()->name() . ' : ' . $registration->ticket()->pretty_price() . $taxable
991
                    ) .
992
                    EEH_HTML::td($registration->event_name()) .
0 ignored issues
show
Documentation introduced by
$registration->event_name() is of type boolean|null, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
993
                    EEH_HTML::td($registration->pretty_paid(), '', 'txn-admin-payment-paid-td jst-cntr') .
994
                    EEH_HTML::td(EEH_Template::format_currency($owing), '', 'txn-admin-payment-owing-td jst-cntr') .
995
                    EEH_HTML::td(
996
                        '<input type="checkbox" value="' . $registration->ID()
997
                        . '" name="txn_admin_payment[registrations]"'
998
                        . $checked . $disabled . '>',
999
                        '', 'jst-cntr'
1000
                    ),
1001
                    'apply-payment-registration-row-' . $registration->ID()
1002
                );
1003
            }
1004
        }
1005
        $registrations_to_apply_payment_to .= EEH_HTML::tbodyx();
1006
        $registrations_to_apply_payment_to .= EEH_HTML::tablex();
1007
        $registrations_to_apply_payment_to .= EEH_HTML::divx();
1008
        $registrations_to_apply_payment_to .= EEH_HTML::p(
1009
            esc_html__(
1010
                '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.',
1011
                'event_espresso'
1012
            ),
1013
            '', 'clear description'
1014
        );
1015
        $registrations_to_apply_payment_to .= EEH_HTML::divx();
1016
        $this->_template_args['registrations_to_apply_payment_to'] = $registrations_to_apply_payment_to;
1017
    }
1018
    
1019
    
1020
    /**
1021
     * _get_reg_status_selection
1022
     *
1023
     * @todo   this will need to be adjusted either once MER comes along OR we move default reg status to tickets
1024
     *         instead of events.
1025
     * @access protected
1026
     * @return void
1027
     */
1028
    protected function _get_reg_status_selection()
1029
    {
1030
        //first get all possible statuses
1031
        $statuses = EEM_Registration::reg_status_array(array(), true);
1032
        //let's add a "don't change" option.
1033
        $status_array['NAN']                                 = esc_html__('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...
1034
        $status_array                                        = array_merge($status_array, $statuses);
1035
        $this->_template_args['status_change_select']        = EEH_Form_Fields::select_input('txn_reg_status_change[reg_status]',
1036
            $status_array, 'NAN', 'id="txn-admin-payment-reg-status-inp"', 'txn-reg-status-change-reg-status');
1037
        $this->_template_args['delete_status_change_select'] = EEH_Form_Fields::select_input('delete_txn_reg_status_change[reg_status]',
1038
            $status_array, 'NAN', 'delete-txn-admin-payment-reg-status-inp', 'delete-txn-reg-status-change-reg-status');
1039
        
1040
    }
1041
    
1042
    
1043
    /**
1044
     *    _get_payment_methods
1045
     * Gets all the payment methods available generally, or the ones that are already
1046
     * selected on these payments (in case their payment methods are no longer active).
1047
     * Has the side-effect of updating the template args' payment_methods item
1048
     * @access private
1049
     *
1050
     * @param EE_Payment[] to show on this page
1051
     *
1052
     * @return void
1053
     */
1054
    private function _get_payment_methods($payments = array())
1055
    {
1056
        $payment_methods_of_payments = array();
1057
        foreach ($payments as $payment) {
1058
            if ($payment instanceof EE_Payment) {
1059
                $payment_methods_of_payments[] = $payment->get('PMD_ID');
1060
            }
1061
        }
1062
        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...
1063
            $query_args = array(
1064
                array(
1065
                    'OR*payment_method_for_payment' => array(
1066
                        'PMD_ID'    => array('IN', $payment_methods_of_payments),
1067
                        'PMD_scope' => array('LIKE', '%' . EEM_Payment_Method::scope_admin . '%')
1068
                    )
1069
                )
1070
            );
1071
        } else {
1072
            $query_args = array(array('PMD_scope' => array('LIKE', '%' . EEM_Payment_Method::scope_admin . '%')));
1073
        }
1074
        $this->_template_args['payment_methods'] = EEM_Payment_Method::instance()->get_all($query_args);
1075
    }
1076
    
1077
    
1078
    /**
1079
     * txn_attendees_meta_box
1080
     *    generates HTML for the Attendees Transaction main meta box
1081
     *
1082
     * @access public
1083
     *
1084
     * @param WP_Post $post
1085
     * @param array   $metabox
1086
     *
1087
     * @return void
1088
     */
1089
    public function txn_attendees_meta_box($post, $metabox = array('args' => array()))
1090
    {
1091
        
1092
        extract($metabox['args']);
1093
        $this->_template_args['post']            = $post;
1094
        $this->_template_args['event_attendees'] = array();
1095
        // process items in cart
1096
        $line_items = $this->_transaction->get_many_related('Line_Item', array(array('LIN_type' => 'line-item')));
1097
        if ( ! empty($line_items)) {
1098
            foreach ($line_items as $item) {
1099
                if ($item instanceof EE_Line_Item) {
1100
                    switch ($item->OBJ_type()) {
1101
                        
1102
                        case 'Event' :
1103
                            break;
1104
                        
1105
                        case 'Ticket' :
1106
                            $ticket = $item->ticket();
1107
                            //right now we're only handling tickets here.  Cause its expected that only tickets will have attendees right?
1108
                            if ( ! $ticket instanceof EE_Ticket) {
1109
                                continue;
1110
                            }
1111
                            try {
1112
                                $event_name = $ticket->get_event_name();
1113
                            } catch (Exception $e) {
1114
                                EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
1115
                                $event_name = esc_html__('Unknown Event', 'event_espresso');
1116
                            }
1117
                            $event_name .= ' - ' . $item->get('LIN_name');
1118
                            $ticket_price = EEH_Template::format_currency($item->get('LIN_unit_price'));
1119
                            // now get all of the registrations for this transaction that use this ticket
1120
                            $registrations = $ticket->get_many_related('Registration',
1121
                                array(array('TXN_ID' => $this->_transaction->ID())));
1122
                            foreach ($registrations as $registration) {
1123
                                if ( ! $registration instanceof EE_Registration) {
1124
                                    continue;
1125
                                }
1126
                                $this->_template_args['event_attendees'][$registration->ID()]['STS_ID']            = $registration->status_ID();
1127
                                $this->_template_args['event_attendees'][$registration->ID()]['att_num']           = $registration->count();
1128
                                $this->_template_args['event_attendees'][$registration->ID()]['event_ticket_name'] = $event_name;
1129
                                $this->_template_args['event_attendees'][$registration->ID()]['ticket_price']      = $ticket_price;
1130
                                // attendee info
1131
                                $attendee = $registration->get_first_related('Attendee');
1132
                                if ($attendee instanceof EE_Attendee) {
1133
                                    $this->_template_args['event_attendees'][$registration->ID()]['att_id']   = $attendee->ID();
1134
                                    $this->_template_args['event_attendees'][$registration->ID()]['attendee'] = $attendee->full_name();
1135
                                    $this->_template_args['event_attendees'][$registration->ID()]['email']    = '<a href="mailto:' . $attendee->email() . '?subject=' . $event_name . esc_html__(' Event',
1136
                                            'event_espresso') . '">' . $attendee->email() . '</a>';
1137
                                    $this->_template_args['event_attendees'][$registration->ID()]['address']  = EEH_Address::format($attendee,
1138
                                        'inline', false, false);
1139
                                } else {
1140
                                    $this->_template_args['event_attendees'][$registration->ID()]['att_id']   = '';
1141
                                    $this->_template_args['event_attendees'][$registration->ID()]['attendee'] = '';
1142
                                    $this->_template_args['event_attendees'][$registration->ID()]['email']    = '';
1143
                                    $this->_template_args['event_attendees'][$registration->ID()]['address']  = '';
1144
                                }
1145
                            }
1146
                            break;
1147
                        
1148
                    }
1149
                }
1150
            }
1151
            
1152
            $this->_template_args['transaction_form_url'] = add_query_arg(array(
1153
                'action'  => 'edit_transaction',
1154
                'process' => 'attendees'
1155
            ), TXN_ADMIN_URL);
1156
            echo EEH_Template::display_template(TXN_TEMPLATE_PATH . 'txn_admin_details_main_meta_box_attendees.template.php',
1157
                $this->_template_args, true);
1158
            
1159
        } else {
1160
            echo sprintf(
1161
                esc_html__('%1$sFor some reason, there are no attendees registered for this transaction. Likely the registration was abandoned in process.%2$s',
1162
                    'event_espresso'),
1163
                '<p class="important-notice">',
1164
                '</p>'
1165
            );
1166
        }
1167
    }
1168
    
1169
    
1170
    /**
1171
     * txn_registrant_side_meta_box
1172
     * generates HTML for the Edit Transaction side meta box
1173
     *
1174
     * @access public
1175
     * @throws \EE_Error
1176
     * @return void
1177
     */
1178
    public function txn_registrant_side_meta_box()
1179
    {
1180
        $primary_att = $this->_transaction->primary_registration() instanceof EE_Registration ? $this->_transaction->primary_registration()->get_first_related('Attendee') : null;
1181
        if ( ! $primary_att instanceof EE_Attendee) {
1182
            $this->_template_args['no_attendee_message'] = esc_html__('There is no attached contact for this transaction.  The transaction either failed due to an error or was abandoned.',
1183
                'event_espresso');
1184
            $primary_att                                 = EEM_Attendee::instance()->create_default_object();
1185
        }
1186
        $this->_template_args['ATT_ID']            = $primary_att->ID();
1187
        $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...
1188
        $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...
1189
        $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...
1190
        $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...
1191
        $this->_template_args['edit_attendee_url'] = EE_Admin_Page::add_query_args_and_nonce(array(
1192
            'action' => 'edit_attendee',
1193
            'post'   => $primary_att->ID()
1194
        ), REG_ADMIN_URL);
1195
        // get formatted address for registrant
1196
        $this->_template_args['formatted_address'] = EEH_Address::format($primary_att);
1197
        echo EEH_Template::display_template(TXN_TEMPLATE_PATH . 'txn_admin_details_side_meta_box_registrant.template.php',
1198
            $this->_template_args, true);
1199
    }
1200
    
1201
    
1202
    /**
1203
     * txn_billing_info_side_meta_box
1204
     *    generates HTML for the Edit Transaction side meta box
1205
     *
1206
     * @access public
1207
     * @return void
1208
     */
1209
    public function txn_billing_info_side_meta_box()
1210
    {
1211
        
1212
        $this->_template_args['billing_form']     = $this->_transaction->billing_info();
1213
        $this->_template_args['billing_form_url'] = add_query_arg(
1214
            array('action' => 'edit_transaction', 'process' => 'billing'),
1215
            TXN_ADMIN_URL
1216
        );
1217
        
1218
        $template_path = TXN_TEMPLATE_PATH . 'txn_admin_details_side_meta_box_billing_info.template.php';
1219
        echo EEH_Template::display_template($template_path, $this->_template_args, true);/**/
1220
    }
1221
    
1222
    
1223
    /**
1224
     * apply_payments_or_refunds
1225
     *    registers a payment or refund made towards a transaction
1226
     *
1227
     * @access public
1228
     * @return void
1229
     */
1230
    public function apply_payments_or_refunds()
1231
    {
1232
        $json_response_data = array('return_data' => false);
1233
        $valid_data         = $this->_validate_payment_request_data();
1234
        if ( ! empty($valid_data)) {
1235
            $PAY_ID = $valid_data['PAY_ID'];
1236
            //save  the new payment
1237
            $payment = $this->_create_payment_from_request_data($valid_data);
1238
            // get the TXN for this payment
1239
            $transaction = $payment->transaction();
1240
            // verify transaction
1241
            if ($transaction instanceof EE_Transaction) {
1242
                // calculate_total_payments_and_update_status
1243
                $this->_process_transaction_payments($transaction);
1244
                $REG_IDs = $this->_get_REG_IDs_to_apply_payment_to($payment);
1245
                $this->_remove_existing_registration_payments($payment, $PAY_ID);
1246
                // apply payment to registrations (if applicable)
1247
                if ( ! empty($REG_IDs)) {
1248
                    $this->_update_registration_payments($transaction, $payment, $REG_IDs);
1249
                    $this->_maybe_send_notifications();
1250
                    // now process status changes for the same registrations
1251
                    $this->_process_registration_status_change($transaction, $REG_IDs);
1252
                }
1253
                $this->_maybe_send_notifications($payment);
1254
                //prepare to render page
1255
                $json_response_data['return_data'] = $this->_build_payment_json_response($payment, $REG_IDs);
1256
                do_action('AHEE__Transactions_Admin_Page__apply_payments_or_refund__after_recording', $transaction,
1257
                    $payment);
1258
            } else {
1259
                EE_Error::add_error(
1260
                    esc_html__('A valid Transaction for this payment could not be retrieved.', 'event_espresso'),
1261
                    __FILE__, __FUNCTION__, __LINE__
1262
                );
1263
            }
1264
        } else {
1265
            EE_Error::add_error(esc_html__('The payment form data could not be processed. Please try again.',
1266
                'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
1267
        }
1268
        
1269
        $notices              = EE_Error::get_notices(false, false, false);
1270
        $this->_template_args = array(
1271
            'data'    => $json_response_data,
1272
            'error'   => $notices['errors'],
1273
            'success' => $notices['success']
1274
        );
1275
        $this->_return_json();
1276
    }
1277
    
1278
    
1279
    /**
1280
     * _validate_payment_request_data
1281
     *
1282
     * @return array
1283
     */
1284
    protected function _validate_payment_request_data()
1285
    {
1286
        if ( ! isset($this->_req_data['txn_admin_payment'])) {
1287
            return false;
1288
        }
1289
        $payment_form = $this->_generate_payment_form_section();
1290
        try {
1291
            if ($payment_form->was_submitted()) {
1292
                $payment_form->receive_form_submission();
1293
                if ( ! $payment_form->is_valid()) {
1294
                    $submission_error_messages = array();
1295
                    foreach ($payment_form->get_validation_errors_accumulated() as $validation_error) {
1296 View Code Duplication
                        if ($validation_error instanceof EE_Validation_Error) {
1297
                            $submission_error_messages[] = sprintf(
1298
                                _x('%s : %s', 'Form Section Name : Form Validation Error', 'event_espresso'),
1299
                                $validation_error->get_form_section()->html_label_text(),
1300
                                $validation_error->getMessage()
1301
                            );
1302
                        }
1303
                    }
1304
                    EE_Error::add_error(join('<br />', $submission_error_messages), __FILE__, __FUNCTION__, __LINE__);
1305
                    
1306
                    return array();
1307
                }
1308
            }
1309
        } catch (EE_Error $e) {
1310
            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
1311
            
1312
            return array();
1313
        }
1314
        
1315
        return $payment_form->valid_data();
1316
    }
1317
    
1318
    
1319
    /**
1320
     * _generate_payment_form_section
1321
     *
1322
     * @return EE_Form_Section_Proper
1323
     */
1324
    protected function _generate_payment_form_section()
1325
    {
1326
        return new EE_Form_Section_Proper(
1327
            array(
1328
                'name'        => 'txn_admin_payment',
1329
                'subsections' => array(
1330
                    'PAY_ID'          => new EE_Text_Input(
1331
                        array(
1332
                            'default'               => 0,
1333
                            'required'              => false,
1334
                            'html_label_text'       => esc_html__('Payment ID', 'event_espresso'),
1335
                            'validation_strategies' => array(new EE_Int_Normalization())
1336
                        )
1337
                    ),
1338
                    'TXN_ID'          => new EE_Text_Input(
1339
                        array(
1340
                            'default'               => 0,
1341
                            'required'              => true,
1342
                            'html_label_text'       => esc_html__('Transaction ID', 'event_espresso'),
1343
                            'validation_strategies' => array(new EE_Int_Normalization())
1344
                        )
1345
                    ),
1346
                    'type'            => new EE_Text_Input(
1347
                        array(
1348
                            'default'               => 1,
1349
                            'required'              => true,
1350
                            'html_label_text'       => esc_html__('Payment or Refund', 'event_espresso'),
1351
                            'validation_strategies' => array(new EE_Int_Normalization())
1352
                        )
1353
                    ),
1354
                    'amount'          => new EE_Text_Input(
1355
                        array(
1356
                            'default'               => 0,
1357
                            'required'              => true,
1358
                            'html_label_text'       => esc_html__('Payment amount', 'event_espresso'),
1359
                            'validation_strategies' => array(new EE_Float_Normalization())
1360
                        )
1361
                    ),
1362
                    'status'          => new EE_Text_Input(
1363
                        array(
1364
                            'default'         => EEM_Payment::status_id_approved,
1365
                            'required'        => true,
1366
                            'html_label_text' => esc_html__('Payment status', 'event_espresso'),
1367
                        )
1368
                    ),
1369
                    'PMD_ID'          => new EE_Text_Input(
1370
                        array(
1371
                            'default'               => 2,
1372
                            'required'              => true,
1373
                            'html_label_text'       => esc_html__('Payment Method', 'event_espresso'),
1374
                            'validation_strategies' => array(new EE_Int_Normalization())
1375
                        )
1376
                    ),
1377
                    'date'            => new EE_Text_Input(
1378
                        array(
1379
                            'default'         => time(),
1380
                            'required'        => true,
1381
                            'html_label_text' => esc_html__('Payment date', 'event_espresso'),
1382
                        )
1383
                    ),
1384
                    'txn_id_chq_nmbr' => new EE_Text_Input(
1385
                        array(
1386
                            'default'               => '',
1387
                            'required'              => false,
1388
                            'html_label_text'       => esc_html__('Transaction or Cheque Number', 'event_espresso'),
1389
                            'validation_strategies' => array(
1390
                                new EE_Max_Length_Validation_Strategy(esc_html__('Input too long', 'event_espresso'),
1391
                                    100),
1392
                            )
1393
                        )
1394
                    ),
1395
                    'po_number'       => new EE_Text_Input(
1396
                        array(
1397
                            'default'               => '',
1398
                            'required'              => false,
1399
                            'html_label_text'       => esc_html__('Purchase Order Number', 'event_espresso'),
1400
                            'validation_strategies' => array(
1401
                                new EE_Max_Length_Validation_Strategy(esc_html__('Input too long', 'event_espresso'),
1402
                                    100),
1403
                            )
1404
                        )
1405
                    ),
1406
                    'accounting'      => new EE_Text_Input(
1407
                        array(
1408
                            'default'               => '',
1409
                            'required'              => false,
1410
                            'html_label_text'       => esc_html__('Extra Field for Accounting', 'event_espresso'),
1411
                            'validation_strategies' => array(
1412
                                new EE_Max_Length_Validation_Strategy(esc_html__('Input too long', 'event_espresso'),
1413
                                    100),
1414
                            )
1415
                        )
1416
                    ),
1417
                )
1418
            )
1419
        );
1420
    }
1421
    
1422
    
1423
    /**
1424
     * _create_payment_from_request_data
1425
     *
1426
     * @param array $valid_data
1427
     *
1428
     * @return EE_Payment
1429
     */
1430
    protected function _create_payment_from_request_data($valid_data)
1431
    {
1432
        $PAY_ID = $valid_data['PAY_ID'];
1433
        // get payment amount
1434
        $amount = $valid_data['amount'] ? abs($valid_data['amount']) : 0;
1435
        // payments have a type value of 1 and refunds have a type value of -1
1436
        // so multiplying amount by type will give a positive value for payments, and negative values for refunds
1437
        $amount = $valid_data['type'] < 0 ? $amount * -1 : $amount;
1438
        // for some reason the date string coming in has extra spaces between the date and time.  This fixes that.
1439
        $date    = $valid_data['date'] ? preg_replace('/\s+/', ' ', $valid_data['date']) : date('Y-m-d g:i a',
1440
            current_time('timestamp'));
1441
        $payment = EE_Payment::new_instance(
1442
            array(
1443
                'TXN_ID'              => $valid_data['TXN_ID'],
1444
                'STS_ID'              => $valid_data['status'],
1445
                'PAY_timestamp'       => $date,
1446
                'PAY_source'          => EEM_Payment_Method::scope_admin,
1447
                'PMD_ID'              => $valid_data['PMD_ID'],
1448
                'PAY_amount'          => $amount,
1449
                'PAY_txn_id_chq_nmbr' => $valid_data['txn_id_chq_nmbr'],
1450
                'PAY_po_number'       => $valid_data['po_number'],
1451
                'PAY_extra_accntng'   => $valid_data['accounting'],
1452
                'PAY_details'         => $valid_data,
1453
                'PAY_ID'              => $PAY_ID
1454
            ),
1455
            '',
1456
            array('Y-m-d', 'g:i a')
1457
        );
1458
        
1459
        if ( ! $payment->save()) {
1460
            EE_Error::add_error(
1461
                sprintf(
1462
                    esc_html__('Payment %1$d has not been successfully saved to the database.', 'event_espresso'),
1463
                    $payment->ID()
1464
                ),
1465
                __FILE__, __FUNCTION__, __LINE__
1466
            );
1467
        }
1468
        
1469
        return $payment;
1470
    }
1471
    
1472
    
1473
    /**
1474
     * _process_transaction_payments
1475
     *
1476
     * @param \EE_Transaction $transaction
1477
     *
1478
     * @return array
1479
     */
1480
    protected function _process_transaction_payments(EE_Transaction $transaction)
1481
    {
1482
        /** @type EE_Transaction_Payments $transaction_payments */
1483
        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
1484
        //update the transaction with this payment
1485
        if ($transaction_payments->calculate_total_payments_and_update_status($transaction)) {
1486
            EE_Error::add_success(esc_html__('The payment has been processed successfully.', 'event_espresso'),
1487
                __FILE__, __FUNCTION__, __LINE__);
1488
        } else {
1489
            EE_Error::add_error(
1490
                esc_html__('The payment was processed successfully but the amount paid for the transaction was not updated.',
1491
                    'event_espresso')
1492
                , __FILE__, __FUNCTION__, __LINE__
1493
            );
1494
        }
1495
    }
1496
    
1497
    
1498
    /**
1499
     * _get_REG_IDs_to_apply_payment_to
1500
     *
1501
     * returns a list of registration IDs that the payment will apply to
1502
     *
1503
     * @param \EE_Payment $payment
1504
     *
1505
     * @return array
1506
     */
1507
    protected function _get_REG_IDs_to_apply_payment_to(EE_Payment $payment)
1508
    {
1509
        $REG_IDs = array();
1510
        // grab array of IDs for specific registrations to apply changes to
1511
        if (isset($this->_req_data['txn_admin_payment']['registrations'])) {
1512
            $REG_IDs = (array)$this->_req_data['txn_admin_payment']['registrations'];
1513
        }
1514
        //nothing specified ? then get all reg IDs
1515
        if (empty($REG_IDs)) {
1516
            $registrations = $payment->transaction()->registrations();
1517
            $REG_IDs       = ! empty($registrations) ? array_keys($registrations) : $this->_get_existing_reg_payment_REG_IDs($payment);
1518
        }
1519
        
1520
        // ensure that REG_IDs are integers and NOT strings
1521
        return array_map('intval', $REG_IDs);
1522
    }
1523
    
1524
    
1525
    /**
1526
     * @return array
1527
     */
1528
    public function existing_reg_payment_REG_IDs()
1529
    {
1530
        return $this->_existing_reg_payment_REG_IDs;
1531
    }
1532
    
1533
    
1534
    /**
1535
     * @param array $existing_reg_payment_REG_IDs
1536
     */
1537
    public function set_existing_reg_payment_REG_IDs($existing_reg_payment_REG_IDs = null)
1538
    {
1539
        $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...
1540
    }
1541
    
1542
    
1543
    /**
1544
     * _get_existing_reg_payment_REG_IDs
1545
     *
1546
     * returns a list of registration IDs that the payment is currently related to
1547
     * as recorded in the database
1548
     *
1549
     * @param \EE_Payment $payment
1550
     *
1551
     * @return array
1552
     */
1553
    protected function _get_existing_reg_payment_REG_IDs(EE_Payment $payment)
1554
    {
1555
        if ($this->existing_reg_payment_REG_IDs() === null) {
1556
            // let's get any existing reg payment records for this payment
1557
            $existing_reg_payment_REG_IDs = $payment->get_many_related('Registration');
1558
            // but we only want the REG IDs, so grab the array keys
1559
            $existing_reg_payment_REG_IDs = ! empty($existing_reg_payment_REG_IDs) ? array_keys($existing_reg_payment_REG_IDs) : array();
1560
            $this->set_existing_reg_payment_REG_IDs($existing_reg_payment_REG_IDs);
1561
        }
1562
        
1563
        return $this->existing_reg_payment_REG_IDs();
1564
    }
1565
    
1566
    
1567
    /**
1568
     * _remove_existing_registration_payments
1569
     *
1570
     * this calculates the difference between existing relations
1571
     * to the supplied payment and the new list registration IDs,
1572
     * removes any related registrations that no longer apply,
1573
     * and then updates the registration paid fields
1574
     *
1575
     * @param \EE_Payment $payment
1576
     * @param int         $PAY_ID
1577
     *
1578
     * @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...
1579
     */
1580
    protected function _remove_existing_registration_payments(EE_Payment $payment, $PAY_ID = 0)
1581
    {
1582
        // newly created payments will have nothing recorded for $PAY_ID
1583
        if ($PAY_ID == 0) {
1584
            return false;
1585
        }
1586
        $existing_reg_payment_REG_IDs = $this->_get_existing_reg_payment_REG_IDs($payment);
1587
        if (empty($existing_reg_payment_REG_IDs)) {
1588
            return false;
1589
        }
1590
        /** @type EE_Transaction_Payments $transaction_payments */
1591
        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
1592
        
1593
        return $transaction_payments->delete_registration_payments_and_update_registrations(
1594
            $payment,
1595
            array(
1596
                array(
1597
                    'PAY_ID' => $payment->ID(),
1598
                    'REG_ID' => array('IN', $existing_reg_payment_REG_IDs),
1599
                )
1600
            )
1601
        );
1602
    }
1603
    
1604
    
1605
    /**
1606
     * _update_registration_payments
1607
     *
1608
     * this applies the payments to the selected registrations
1609
     * but only if they have not already been paid for
1610
     *
1611
     * @param  EE_Transaction $transaction
1612
     * @param \EE_Payment     $payment
1613
     * @param array           $REG_IDs
1614
     *
1615
     * @return bool
1616
     */
1617
    protected function _update_registration_payments(
1618
        EE_Transaction $transaction,
1619
        EE_Payment $payment,
1620
        $REG_IDs = array()
1621
    ) {
1622
        // we can pass our own custom set of registrations to EE_Payment_Processor::process_registration_payments()
1623
        // so let's do that using our set of REG_IDs from the form
1624
        $registration_query_where_params = array(
1625
            'REG_ID' => array('IN', $REG_IDs)
1626
        );
1627
        // but add in some conditions regarding payment,
1628
        // so that we don't apply payments to registrations that are free or have already been paid for
1629
        // but ONLY if the payment is NOT a refund ( ie: the payment amount is not negative )
1630
        if ( ! $payment->is_a_refund()) {
1631
            $registration_query_where_params['REG_final_price']  = array('!=', 0);
1632
            $registration_query_where_params['REG_final_price*'] = array('!=', 'REG_paid', true);
1633
        }
1634
        //EEH_Debug_Tools::printr( $registration_query_where_params, '$registration_query_where_params', __FILE__, __LINE__ );
1635
        $registrations = $transaction->registrations(array($registration_query_where_params));
1636
        if ( ! empty($registrations)) {
1637
            /** @type EE_Payment_Processor $payment_processor */
1638
            $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
1639
            $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...
1640
        }
1641
    }
1642
    
1643
    
1644
    /**
1645
     * _process_registration_status_change
1646
     *
1647
     * This processes requested registration status changes for all the registrations
1648
     * on a given transaction and (optionally) sends out notifications for the changes.
1649
     *
1650
     * @param  EE_Transaction $transaction
1651
     * @param array           $REG_IDs
1652
     *
1653
     * @return bool
1654
     */
1655
    protected function _process_registration_status_change(EE_Transaction $transaction, $REG_IDs = array())
1656
    {
1657
        // first if there is no change in status then we get out.
1658
        if (
1659
            ! isset($this->_req_data['txn_reg_status_change'], $this->_req_data['txn_reg_status_change']['reg_status'])
1660
            || $this->_req_data['txn_reg_status_change']['reg_status'] == 'NAN'
1661
        ) {
1662
            //no error message, no change requested, just nothing to do man.
1663
            return false;
1664
        }
1665
        /** @type EE_Transaction_Processor $transaction_processor */
1666
        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1667
        
1668
        // made it here dude?  Oh WOW.  K, let's take care of changing the statuses
1669
        return $transaction_processor->manually_update_registration_statuses(
1670
            $transaction,
1671
            sanitize_text_field($this->_req_data['txn_reg_status_change']['reg_status']),
1672
            array(array('REG_ID' => array('IN', $REG_IDs)))
1673
        );
1674
    }
1675
    
1676
    
1677
    /**
1678
     * _build_payment_json_response
1679
     *
1680
     * @access public
1681
     *
1682
     * @param \EE_Payment $payment
1683
     * @param array       $REG_IDs
1684
     * @param bool | null $delete_txn_reg_status_change
1685
     *
1686
     * @return array
1687
     */
1688
    protected function _build_payment_json_response(
1689
        EE_Payment $payment,
1690
        $REG_IDs = array(),
1691
        $delete_txn_reg_status_change = null
1692
    ) {
1693
        // was the payment deleted ?
1694
        if (is_bool($delete_txn_reg_status_change)) {
1695
            return array(
1696
                'PAY_ID'                       => $payment->ID(),
1697
                'amount'                       => $payment->amount(),
1698
                'total_paid'                   => $payment->transaction()->paid(),
1699
                'txn_status'                   => $payment->transaction()->status_ID(),
1700
                'pay_status'                   => $payment->STS_ID(),
1701
                'registrations'                => $this->_registration_payment_data_array($REG_IDs),
1702
                'delete_txn_reg_status_change' => $delete_txn_reg_status_change,
1703
            );
1704
        } else {
1705
            $this->_get_payment_status_array();
1706
            
1707
            return array(
1708
                'amount'           => $payment->amount(),
1709
                'total_paid'       => $payment->transaction()->paid(),
1710
                'txn_status'       => $payment->transaction()->status_ID(),
1711
                'pay_status'       => $payment->STS_ID(),
1712
                'PAY_ID'           => $payment->ID(),
1713
                'STS_ID'           => $payment->STS_ID(),
1714
                'status'           => self::$_pay_status[$payment->STS_ID()],
1715
                'date'             => $payment->timestamp('Y-m-d', 'h:i a'),
1716
                'method'           => strtoupper($payment->source()),
1717
                'PM_ID'            => $payment->payment_method() ? $payment->payment_method()->ID() : 1,
1718
                'gateway'          => $payment->payment_method() ? $payment->payment_method()->admin_name() : esc_html__("Unknown",
1719
                    'event_espresso'),
1720
                'gateway_response' => $payment->gateway_response(),
1721
                'txn_id_chq_nmbr'  => $payment->txn_id_chq_nmbr(),
1722
                'po_number'        => $payment->po_number(),
1723
                'extra_accntng'    => $payment->extra_accntng(),
1724
                'registrations'    => $this->_registration_payment_data_array($REG_IDs),
1725
            );
1726
        }
1727
    }
1728
    
1729
    
1730
    /**
1731
     * delete_payment
1732
     *    delete a payment or refund made towards a transaction
1733
     *
1734
     * @access public
1735
     * @return void
1736
     */
1737
    public function delete_payment()
1738
    {
1739
        $json_response_data = array('return_data' => false);
1740
        $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;
1741
        if ($PAY_ID) {
1742
            $delete_txn_reg_status_change = isset($this->_req_data['delete_txn_reg_status_change']) ? $this->_req_data['delete_txn_reg_status_change'] : false;
1743
            $payment                      = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
1744
            if ($payment instanceof EE_Payment) {
1745
                $REG_IDs = $this->_get_existing_reg_payment_REG_IDs($payment);
1746
                /** @type EE_Transaction_Payments $transaction_payments */
1747
                $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
1748
                if ($transaction_payments->delete_payment_and_update_transaction($payment)) {
1749
                    $json_response_data['return_data'] = $this->_build_payment_json_response($payment, $REG_IDs,
1750
                        $delete_txn_reg_status_change);
1751
                    if ($delete_txn_reg_status_change) {
1752
                        $this->_req_data['txn_reg_status_change'] = $delete_txn_reg_status_change;
1753
                        //MAKE sure we also add the delete_txn_req_status_change to the
1754
                        //$_REQUEST global because that's how messages will be looking for it.
1755
                        $_REQUEST['txn_reg_status_change'] = $delete_txn_reg_status_change;
1756
                        $this->_maybe_send_notifications();
1757
                        $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...
1758
                    }
1759
                }
1760
            } else {
1761
                EE_Error::add_error(
1762
                    esc_html__('Valid Payment data could not be retrieved from the database.', 'event_espresso'),
1763
                    __FILE__, __FUNCTION__, __LINE__
1764
                );
1765
            }
1766
        } else {
1767
            EE_Error::add_error(
1768
                esc_html__('A valid Payment ID was not received, therefore payment form data could not be loaded.',
1769
                    'event_espresso'),
1770
                __FILE__, __FUNCTION__, __LINE__
1771
            );
1772
        }
1773
        $notices              = EE_Error::get_notices(false, false, false);
1774
        $this->_template_args = array(
1775
            'data'      => $json_response_data,
1776
            'success'   => $notices['success'],
1777
            'error'     => $notices['errors'],
1778
            'attention' => $notices['attention']
1779
        );
1780
        $this->_return_json();
1781
    }
1782
    
1783
    
1784
    /**
1785
     * _registration_payment_data_array
1786
     * adds info for 'owing' and 'paid' for each registration to the json response
1787
     *
1788
     * @access protected
1789
     *
1790
     * @param array $REG_IDs
1791
     *
1792
     * @return array
1793
     */
1794
    protected function _registration_payment_data_array($REG_IDs)
1795
    {
1796
        $registration_payment_data = array();
1797
        //if non empty reg_ids lets get an array of registrations and update the values for the apply_payment/refund rows.
1798
        if ( ! empty($REG_IDs)) {
1799
            $registrations = EEM_Registration::instance()->get_all(array(array('REG_ID' => array('IN', $REG_IDs))));
1800
            foreach ($registrations as $registration) {
1801
                if ($registration instanceof EE_Registration) {
1802
                    $registration_payment_data[$registration->ID()] = array(
1803
                        'paid'  => $registration->pretty_paid(),
1804
                        'owing' => EEH_Template::format_currency($registration->final_price() - $registration->paid()),
1805
                    );
1806
                }
1807
            }
1808
        }
1809
        
1810
        return $registration_payment_data;
1811
    }
1812
    
1813
    
1814
    /**
1815
     * _maybe_send_notifications
1816
     *
1817
     * determines whether or not the admin has indicated that notifications should be sent.
1818
     * If so, will toggle a filter switch for delivering registration notices.
1819
     * If passed an EE_Payment object, then it will trigger payment notifications instead.
1820
     *
1821
     * @access protected
1822
     *
1823
     * @param \EE_Payment | null $payment
1824
     */
1825
    protected function _maybe_send_notifications($payment = null)
1826
    {
1827
        switch ($payment instanceof EE_Payment) {
1828
            // payment notifications
1829 View Code Duplication
            case true :
1830
                if (
1831
                    isset(
1832
                        $this->_req_data['txn_payments'],
1833
                        $this->_req_data['txn_payments']['send_notifications']
1834
                    ) &&
1835
                    filter_var($this->_req_data['txn_payments']['send_notifications'], FILTER_VALIDATE_BOOLEAN)
1836
                ) {
1837
                    $this->_process_payment_notification($payment);
1838
                }
1839
                break;
1840
            // registration notifications
1841 View Code Duplication
            case false :
1842
                if (
1843
                    isset(
1844
                        $this->_req_data['txn_reg_status_change'],
1845
                        $this->_req_data['txn_reg_status_change']['send_notifications']
1846
                    ) &&
1847
                    filter_var($this->_req_data['txn_reg_status_change']['send_notifications'], FILTER_VALIDATE_BOOLEAN)
1848
                ) {
1849
                    add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
1850
                }
1851
                break;
1852
        }
1853
    }
1854
    
1855
    
1856
    /**
1857
     * _send_payment_reminder
1858
     *    generates HTML for the View Transaction Details Admin page
1859
     *
1860
     * @access protected
1861
     * @return void
1862
     */
1863
    protected function _send_payment_reminder()
1864
    {
1865
        $TXN_ID      = ( ! empty($this->_req_data['TXN_ID'])) ? absint($this->_req_data['TXN_ID']) : false;
1866
        $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
1867
        $query_args  = isset($this->_req_data['redirect_to']) ? array(
1868
            'action' => $this->_req_data['redirect_to'],
1869
            'TXN_ID' => $this->_req_data['TXN_ID']
1870
        ) : array();
1871
        do_action('AHEE__Transactions_Admin_Page___send_payment_reminder__process_admin_payment_reminder',
1872
            $transaction);
1873
        $this->_redirect_after_action(false, esc_html__('payment reminder', 'event_espresso'),
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...
1874
            esc_html__('sent', 'event_espresso'), $query_args, true);
1875
    }
1876
    
1877
    
1878
    /**
1879
     *  get_transactions
1880
     *    get transactions for given parameters (used by list table)
1881
     *
1882
     * @param  int     $perpage how many transactions displayed per page
1883
     * @param  boolean $count   return the count or objects
1884
     * @param string   $view
1885
     *
1886
     * @return mixed int = count || array of transaction objects
1887
     */
1888
    public function get_transactions($perpage, $count = false, $view = '')
1889
    {
1890
        
1891
        $TXN = EEM_Transaction::instance();
1892
        
1893
        $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',
1894
            strtotime('-10 year'));
1895
        $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');
1896
        
1897
        //make sure our timestamps start and end right at the boundaries for each day
1898
        $start_date = date('Y-m-d', strtotime($start_date)) . ' 00:00:00';
1899
        $end_date   = date('Y-m-d', strtotime($end_date)) . ' 23:59:59';
1900
        
1901
        
1902
        //convert to timestamps
1903
        $start_date = strtotime($start_date);
1904
        $end_date   = strtotime($end_date);
1905
        
1906
        //makes sure start date is the lowest value and vice versa
1907
        $start_date = min($start_date, $end_date);
1908
        $end_date   = max($start_date, $end_date);
1909
        
1910
        //convert to correct format for query
1911
        $start_date = EEM_Transaction::instance()->convert_datetime_for_query('TXN_timestamp',
1912
            date('Y-m-d H:i:s', $start_date), 'Y-m-d H:i:s');
1913
        $end_date   = EEM_Transaction::instance()->convert_datetime_for_query('TXN_timestamp',
1914
            date('Y-m-d H:i:s', $end_date), 'Y-m-d H:i:s');
1915
        
1916
        
1917
        //set orderby
1918
        $this->_req_data['orderby'] = ! empty($this->_req_data['orderby']) ? $this->_req_data['orderby'] : '';
1919
        
1920
        switch ($this->_req_data['orderby']) {
1921
            case 'TXN_ID':
1922
                $orderby = 'TXN_ID';
1923
                break;
1924
            case 'ATT_fname':
1925
                $orderby = 'Registration.Attendee.ATT_fname';
1926
                break;
1927
            case 'event_name':
1928
                $orderby = 'Registration.Event.EVT_name';
1929
                break;
1930
            default: //'TXN_timestamp'
1931
                $orderby = 'TXN_timestamp';
1932
        }
1933
        
1934
        $sort         = (isset($this->_req_data['order']) && ! empty($this->_req_data['order'])) ? $this->_req_data['order'] : 'DESC';
1935
        $current_page = isset($this->_req_data['paged']) && ! empty($this->_req_data['paged']) ? $this->_req_data['paged'] : 1;
1936
        $per_page     = isset($perpage) && ! empty($perpage) ? $perpage : 10;
1937
        $per_page     = isset($this->_req_data['perpage']) && ! empty($this->_req_data['perpage']) ? $this->_req_data['perpage'] : $per_page;
1938
        
1939
        $offset = ($current_page - 1) * $per_page;
1940
        $limit  = array($offset, $per_page);
1941
        
1942
        $_where = array(
1943
            'TXN_timestamp'          => array('BETWEEN', array($start_date, $end_date)),
1944
            'Registration.REG_count' => 1
1945
        );
1946
        
1947
        if (isset($this->_req_data['EVT_ID'])) {
1948
            $_where['Registration.EVT_ID'] = $this->_req_data['EVT_ID'];
1949
        }
1950
        
1951
        if (isset($this->_req_data['s'])) {
1952
            $search_string = '%' . $this->_req_data['s'] . '%';
1953
            $_where['OR']  = array(
1954
                'Registration.Event.EVT_name'         => array('LIKE', $search_string),
1955
                'Registration.Event.EVT_desc'         => array('LIKE', $search_string),
1956
                'Registration.Event.EVT_short_desc'   => array('LIKE', $search_string),
1957
                'Registration.Attendee.ATT_full_name' => array('LIKE', $search_string),
1958
                'Registration.Attendee.ATT_fname'     => array('LIKE', $search_string),
1959
                'Registration.Attendee.ATT_lname'     => array('LIKE', $search_string),
1960
                'Registration.Attendee.ATT_short_bio' => array('LIKE', $search_string),
1961
                'Registration.Attendee.ATT_email'     => array('LIKE', $search_string),
1962
                'Registration.Attendee.ATT_address'   => array('LIKE', $search_string),
1963
                'Registration.Attendee.ATT_address2'  => array('LIKE', $search_string),
1964
                'Registration.Attendee.ATT_city'      => array('LIKE', $search_string),
1965
                'Registration.REG_final_price'        => array('LIKE', $search_string),
1966
                'Registration.REG_code'               => array('LIKE', $search_string),
1967
                'Registration.REG_count'              => array('LIKE', $search_string),
1968
                'Registration.REG_group_size'         => array('LIKE', $search_string),
1969
                'Registration.Ticket.TKT_name'        => array('LIKE', $search_string),
1970
                'Registration.Ticket.TKT_description' => array('LIKE', $search_string),
1971
                'Payment.PAY_source'                  => array('LIKE', $search_string),
1972
                'Payment.Payment_Method.PMD_name'     => array('LIKE', $search_string),
1973
                'TXN_session_data'                    => array('LIKE', $search_string),
1974
                'Payment.PAY_txn_id_chq_nmbr'         => array('LIKE', $search_string)
1975
            );
1976
        }
1977
        
1978
        //failed transactions
1979
        $failed    = ( ! empty($this->_req_data['status']) && $this->_req_data['status'] == 'failed' && ! $count) || ($count && $view == 'failed') ? true : false;
1980
        $abandoned = ( ! empty($this->_req_data['status']) && $this->_req_data['status'] == 'abandoned' && ! $count) || ($count && $view == 'abandoned') ? true : false;
1981
        
1982
        if ($failed) {
1983
            $_where['STS_ID'] = EEM_Transaction::failed_status_code;
1984
        } else if ($abandoned) {
1985
            $_where['STS_ID'] = EEM_Transaction::abandoned_status_code;
1986
        } else {
1987
            $_where['STS_ID']  = array('!=', EEM_Transaction::failed_status_code);
1988
            $_where['STS_ID*'] = array('!=', EEM_Transaction::abandoned_status_code);
1989
        }
1990
        
1991
        $query_params = array($_where, 'order_by' => array($orderby => $sort), 'limit' => $limit);
1992
        
1993
        $transactions = $count ? $TXN->count(array($_where), 'TXN_ID', true) : $TXN->get_all($query_params);
1994
        
1995
        
1996
        return $transactions;
1997
        
1998
    }
1999
    
2000
    
2001
}
2002