Completed
Push — master ( d4bee7...20651b )
by Brian
20s queued 16s
created

WPInv_Ajax::add_invoice_item()   F

Complexity

Conditions 21
Paths 654

Size

Total Lines 108
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 70
nc 654
nop 0
dl 0
loc 108
rs 0.4805
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Contains functions related to Invoicing plugin.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
 
9
// MUST have WordPress.
10
if ( !defined( 'WPINC' ) ) {
11
    exit( 'Do NOT access this file directly: ' . basename( __FILE__ ) );
12
}
13
14
class WPInv_Ajax {
15
    public static function init() {
16
        add_action( 'init', array( __CLASS__, 'define_ajax' ), 0 );
17
        add_action( 'template_redirect', array( __CLASS__, 'do_wpinv_ajax' ), 0 );
18
        self::add_ajax_events();
19
    }
20
21
    public static function define_ajax() {
22
        if ( !empty( $_GET['wpinv-ajax'] ) ) {
23
            if ( ! defined( 'DOING_AJAX' ) ) {
24
                define( 'DOING_AJAX', true );
25
            }
26
            if ( ! defined( 'WC_DOING_AJAX' ) ) {
27
                define( 'WC_DOING_AJAX', true );
28
            }
29
            // Turn off display_errors during AJAX events to prevent malformed JSON
30
            if ( ! WP_DEBUG || ( WP_DEBUG && ! WP_DEBUG_DISPLAY ) ) {
31
                /** @scrutinizer ignore-unhandled */ @ini_set( 'display_errors', 0 );
32
            }
33
            $GLOBALS['wpdb']->hide_errors();
34
        }
35
    }
36
    
37
    public static function do_wpinv_ajax() {
38
        global $wp_query;
39
40
        if ( !empty( $_GET['wpinv-ajax'] ) ) {
41
            $wp_query->set( 'wpinv-ajax', sanitize_text_field( $_GET['wpinv-ajax'] ) );
42
        }
43
44
        if ( $action = $wp_query->get( 'wpinv-ajax' ) ) {
45
            self::wpinv_ajax_headers();
46
            do_action( 'wpinv_ajax_' . sanitize_text_field( $action ) );
47
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
48
        }
49
    }
50
    
51
    private static function wpinv_ajax_headers() {
52
        send_origin_headers();
53
        /** @scrutinizer ignore-unhandled */ @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
54
        /** @scrutinizer ignore-unhandled */ @header( 'X-Robots-Tag: noindex' );
55
        send_nosniff_header();
56
        nocache_headers();
57
        status_header( 200 );
58
    }
59
    
60
    public static function add_ajax_events() {
61
        $ajax_events = array(
62
            'add_note' => false,
63
            'delete_note' => false,
64
            'get_states_field' => true,
65
            'checkout' => false,
66
            'payment_form'     => true,
67
            'add_invoice_item' => false,
68
            'remove_invoice_item' => false,
69
            'create_invoice_item' => false,
70
            'get_billing_details' => false,
71
            'admin_recalculate_totals' => false,
72
            'admin_apply_discount' => false,
73
            'admin_remove_discount' => false,
74
            'check_email' => false,
75
            'run_tool' => false,
76
            'apply_discount' => true,
77
            'remove_discount' => true,
78
            'buy_items' => true,
79
        );
80
81
        foreach ( $ajax_events as $ajax_event => $nopriv ) {
82
            add_action( 'wp_ajax_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
83
            
84
            if ( !defined( 'WPI_AJAX_' . strtoupper( $nopriv ) ) ) {
85
                define( 'WPI_AJAX_' . strtoupper( $nopriv ), 1 );
86
            }
87
88
            if ( $nopriv ) {
89
                add_action( 'wp_ajax_nopriv_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
90
91
                add_action( 'wpinv_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
92
            }
93
        }
94
    }
95
    
96
    public static function add_note() {
97
        check_ajax_referer( 'add-invoice-note', '_nonce' );
98
99
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
100
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
101
        }
102
103
        $post_id   = absint( $_POST['post_id'] );
104
        $note      = wp_kses_post( trim( stripslashes( $_POST['note'] ) ) );
105
        $note_type = sanitize_text_field( $_POST['note_type'] );
106
107
        $is_customer_note = $note_type == 'customer' ? 1 : 0;
108
109
        if ( $post_id > 0 ) {
110
            $note_id = wpinv_insert_payment_note( $post_id, $note, $is_customer_note );
111
112
            if ( $note_id > 0 && !is_wp_error( $note_id ) ) {
113
                wpinv_get_invoice_note_line_item( $note_id );
114
            }
115
        }
116
117
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
118
    }
119
120
    public static function delete_note() {
121
        check_ajax_referer( 'delete-invoice-note', '_nonce' );
122
123
        if ( !wpinv_current_user_can_manage_invoicing() ) {
124
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
125
        }
126
127
        $note_id = (int)$_POST['note_id'];
128
129
        if ( $note_id > 0 ) {
130
            wp_delete_comment( $note_id, true );
131
        }
132
133
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
134
    }
135
    
136
    public static function get_states_field() {
137
        echo wpinv_get_states_field();
138
        
139
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
140
    }
141
    
142
    public static function checkout() {
143
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
144
            define( 'WPINV_CHECKOUT', true );
145
        }
146
147
        wpinv_process_checkout();
148
        die(0);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
149
    }
150
    
151
    public static function add_invoice_item() {
152
        global $wpi_userID, $wpinv_ip_address_country;
153
        check_ajax_referer( 'invoice-item', '_nonce' );
154
        if ( !wpinv_current_user_can_manage_invoicing() ) {
155
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
156
        }
157
        
158
        $item_id    = sanitize_text_field( $_POST['item_id'] );
159
        $invoice_id = absint( $_POST['invoice_id'] );
160
        
161
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
162
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
163
        }
164
        
165
        $invoice    = wpinv_get_invoice( $invoice_id );
166
        if ( empty( $invoice ) ) {
167
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
168
        }
169
        
170
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
171
            die(); // Don't allow modify items for paid invoice.
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
172
        }
173
        
174
        if ( !empty( $_POST['user_id'] ) ) {
175
            $wpi_userID = absint( $_POST['user_id'] ); 
176
        }
177
178
        $item = new WPInv_Item( $item_id );
179
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
180
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
181
        }
182
        
183
        // Validate item before adding to invoice because recurring item must be paid individually.
184
        if ( !empty( $invoice->cart_details ) ) {
185
            $valid = true;
186
            
187
            if ( $recurring_item = $invoice->get_recurring() ) {
188
                if ( $recurring_item != $item_id ) {
189
                    $valid = false;
190
                }
191
            } else if ( wpinv_is_recurring_item( $item_id ) ) {
192
                $valid = false;
193
            }
194
            
195
            if ( !$valid ) {
196
                $response               = array();
197
                $response['success']    = false;
198
                $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
199
                wp_send_json( $response );
200
            }
201
        }
202
        
203
        $checkout_session = wpinv_get_checkout_session();
204
        
205
        $data                   = array();
206
        $data['invoice_id']     = $invoice_id;
207
        $data['cart_discounts'] = $invoice->get_discounts( true );
208
        
209
        wpinv_set_checkout_session( $data );
210
        
211
        $quantity = wpinv_item_quantities_enabled() && !empty($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : 1;
212
213
        $args = array(
214
            'id'            => $item_id,
215
            'quantity'      => $quantity,
216
            'item_price'    => $item->get_price(),
217
            'custom_price'  => '',
218
            'tax'           => 0.00,
219
            'discount'      => 0,
220
            'meta'          => array(),
221
            'fees'          => array()
222
        );
223
224
        $invoice->add_item( $item_id, $args );
225
        $invoice->save();
226
        
227
        if ( empty( $_POST['country'] ) ) {
228
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
229
        }
230
        if ( empty( $_POST['state'] ) ) {
231
            $_POST['state'] = $invoice->state;
232
        }
233
         
234
        $invoice->country   = sanitize_text_field( $_POST['country'] );
235
        $invoice->state     = sanitize_text_field( $_POST['state'] );
236
        
237
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
238
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
239
        
240
        $wpinv_ip_address_country = $invoice->country;
241
242
        $invoice->recalculate_totals(true);
243
        
244
        $response                       = array();
245
        $response['success']            = true;
246
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
247
        $response['data']['subtotal']   = $invoice->get_subtotal();
248
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
249
        $response['data']['tax']        = $invoice->get_tax();
250
        $response['data']['taxf']       = $invoice->get_tax(true);
251
        $response['data']['discount']   = $invoice->get_discount();
252
        $response['data']['discountf']  = $invoice->get_discount(true);
253
        $response['data']['total']      = $invoice->get_total();
254
        $response['data']['totalf']     = $invoice->get_total(true);
255
        
256
        wpinv_set_checkout_session($checkout_session);
257
        
258
        wp_send_json( $response );
259
    }
260
261
262
    public static function remove_invoice_item() {
263
        global $wpi_userID, $wpinv_ip_address_country;
264
        
265
        check_ajax_referer( 'invoice-item', '_nonce' );
266
        if ( !wpinv_current_user_can_manage_invoicing() ) {
267
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
268
        }
269
        
270
        $item_id    = sanitize_text_field( $_POST['item_id'] );
271
        $invoice_id = absint( $_POST['invoice_id'] );
272
        $cart_index = isset( $_POST['index'] ) && $_POST['index'] >= 0 ? $_POST['index'] : false;
273
        
274
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
275
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
276
        }
277
278
        $invoice    = wpinv_get_invoice( $invoice_id );
279
        if ( empty( $invoice ) ) {
280
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
281
        }
282
        
283
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
284
            die(); // Don't allow modify items for paid invoice.
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
285
        }
286
        
287
        if ( !empty( $_POST['user_id'] ) ) {
288
            $wpi_userID = absint( $_POST['user_id'] ); 
289
        }
290
291
        $item       = new WPInv_Item( $item_id );
292
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
293
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
294
        }
295
        
296
        $checkout_session = wpinv_get_checkout_session();
297
        
298
        $data                   = array();
299
        $data['invoice_id']     = $invoice_id;
300
        $data['cart_discounts'] = $invoice->get_discounts( true );
301
        
302
        wpinv_set_checkout_session( $data );
303
304
        $args = array(
305
            'id'         => $item_id,
306
            'quantity'   => 1,
307
            'cart_index' => $cart_index
308
        );
309
310
        $invoice->remove_item( $item_id, $args );
311
        $invoice->save();
312
        
313
        if ( empty( $_POST['country'] ) ) {
314
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
315
        }
316
        if ( empty( $_POST['state'] ) ) {
317
            $_POST['state'] = $invoice->state;
318
        }
319
         
320
        $invoice->country   = sanitize_text_field( $_POST['country'] );
321
        $invoice->state     = sanitize_text_field( $_POST['state'] );
322
        
323
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
324
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
325
        
326
        $wpinv_ip_address_country = $invoice->country;
327
        
328
        $invoice->recalculate_totals(true);
329
        
330
        $response                       = array();
331
        $response['success']            = true;
332
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
333
        $response['data']['subtotal']   = $invoice->get_subtotal();
334
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
335
        $response['data']['tax']        = $invoice->get_tax();
336
        $response['data']['taxf']       = $invoice->get_tax(true);
337
        $response['data']['discount']   = $invoice->get_discount();
338
        $response['data']['discountf']  = $invoice->get_discount(true);
339
        $response['data']['total']      = $invoice->get_total();
340
        $response['data']['totalf']     = $invoice->get_total(true);
341
        
342
        wpinv_set_checkout_session($checkout_session);
343
        
344
        wp_send_json( $response );
345
    }
346
    
347
    public static function create_invoice_item() {
348
        check_ajax_referer( 'invoice-item', '_nonce' );
349
        if ( !wpinv_current_user_can_manage_invoicing() ) {
350
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
351
        }
352
        
353
        $invoice_id = absint( $_POST['invoice_id'] );
354
355
        // Find the item
356
        if ( !is_numeric( $invoice_id ) ) {
357
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
358
        }        
359
        
360
        $invoice     = wpinv_get_invoice( $invoice_id );
361
        if ( empty( $invoice ) ) {
362
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
363
        }
364
        
365
        // Validate item before adding to invoice because recurring item must be paid individually.
366
        if ( !empty( $invoice->cart_details ) && $invoice->get_recurring() ) {
367
            $response               = array();
368
            $response['success']    = false;
369
            $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
370
            wp_send_json( $response );
371
        }        
372
        
373
        $save_item = wp_unslash( $_POST['_wpinv_quick'] );
374
        
375
        $meta               = array();
376
        $meta['type']       = !empty($save_item['type']) ? sanitize_text_field($save_item['type']) : 'custom';
377
        $meta['price']      = !empty($save_item['price']) ? wpinv_sanitize_amount( $save_item['price'] ) : 0;
378
        $meta['vat_rule']   = !empty($save_item['vat_rule']) ? sanitize_text_field($save_item['vat_rule']) : 'digital';
379
        $meta['vat_class']  = !empty($save_item['vat_class']) ? sanitize_text_field($save_item['vat_class']) : '_standard';
380
        
381
        $data                   = array();
382
        $data['post_title']     = sanitize_text_field($save_item['name']);
383
        $data['post_status']    = 'publish';
384
        $data['post_excerpt']   = ! empty( $save_item['excerpt'] ) ? wp_kses_post( $save_item['excerpt'] ) : '';
385
        $data['meta']           = $meta;
386
        
387
        $item = new WPInv_Item();
388
        $item->create( $data );
389
        
390
        if ( !empty( $item ) ) {
391
            $_POST['item_id']   = $item->ID;
392
            $_POST['qty']       = !empty($save_item['qty']) && $save_item['qty'] > 0 ? (int)$save_item['qty'] : 1;
393
            
394
            self::add_invoice_item();
395
        }
396
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
397
    }
398
    
399
    public static function get_billing_details() {
400
        check_ajax_referer( 'get-billing-details', '_nonce' );
401
        
402
        if ( !wpinv_current_user_can_manage_invoicing() ) {
403
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
404
        }
405
406
        $user_id            = (int)$_POST['user_id'];
407
        $billing_details    = wpinv_get_user_address($user_id);
408
        $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
409
        
410
        if (isset($billing_details['user_id'])) {
411
            unset($billing_details['user_id']);
412
        }
413
        
414
        if (isset($billing_details['email'])) {
415
            unset($billing_details['email']);
416
        }
417
418
        $response                               = array();
419
        $response['success']                    = true;
420
        $response['data']['billing_details']    = $billing_details;
421
        
422
        wp_send_json( $response );
423
    }
424
    
425
    public static function admin_recalculate_totals() {
426
        global $wpi_userID, $wpinv_ip_address_country;
427
        
428
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
429
        if ( !wpinv_current_user_can_manage_invoicing() ) {
430
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
431
        }
432
        
433
        $invoice_id = absint( $_POST['invoice_id'] );        
434
        $invoice    = wpinv_get_invoice( $invoice_id );
435
        if ( empty( $invoice ) ) {
436
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
437
        }
438
        
439
        $checkout_session = wpinv_get_checkout_session();
440
        
441
        $data                   = array();
442
        $data['invoice_id']     = $invoice_id;
443
        $data['cart_discounts'] = $invoice->get_discounts( true );
444
        
445
        wpinv_set_checkout_session( $data );
446
        
447
        if ( !empty( $_POST['user_id'] ) ) {
448
            $wpi_userID = absint( $_POST['user_id'] ); 
449
        }
450
        
451
        if ( empty( $_POST['country'] ) ) {
452
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
453
        }
454
            
455
        $invoice->country = sanitize_text_field( $_POST['country'] );
456
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
457
        if ( isset( $_POST['state'] ) ) {
458
            $invoice->state = sanitize_text_field( $_POST['state'] );
459
            $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
460
        }
461
        
462
        $wpinv_ip_address_country = $invoice->country;
463
        
464
        $invoice = $invoice->recalculate_totals(true);
465
        
466
        $response                       = array();
467
        $response['success']            = true;
468
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
469
        $response['data']['subtotal']   = $invoice->get_subtotal();
470
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
471
        $response['data']['tax']        = $invoice->get_tax();
472
        $response['data']['taxf']       = $invoice->get_tax(true);
473
        $response['data']['discount']   = $invoice->get_discount();
474
        $response['data']['discountf']  = $invoice->get_discount(true);
475
        $response['data']['total']      = $invoice->get_total();
476
        $response['data']['totalf']     = $invoice->get_total(true);
477
        
478
        wpinv_set_checkout_session($checkout_session);
479
        
480
        wp_send_json( $response );
481
    }
482
    
483
    public static function admin_apply_discount() {
484
        global $wpi_userID;
485
        
486
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
487
        if ( !wpinv_current_user_can_manage_invoicing() ) {
488
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
489
        }
490
        
491
        $invoice_id = absint( $_POST['invoice_id'] );
492
        $discount_code = sanitize_text_field( $_POST['code'] );
493
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
494
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
495
        }
496
        
497
        $invoice = wpinv_get_invoice( $invoice_id );
498
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
499
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
500
        }
501
        
502
        $checkout_session = wpinv_get_checkout_session();
503
        
504
        $data                   = array();
505
        $data['invoice_id']     = $invoice_id;
506
        $data['cart_discounts'] = $invoice->get_discounts( true );
507
        
508
        wpinv_set_checkout_session( $data );
509
        
510
        $response               = array();
511
        $response['success']    = false;
512
        $response['msg']        = __( 'This discount is invalid.', 'invoicing' );
513
        $response['data']['code'] = $discount_code;
514
        
515
        if ( wpinv_is_discount_valid( $discount_code, $invoice->get_user_id() ) ) {
516
            $discounts = wpinv_set_cart_discount( $discount_code );
0 ignored issues
show
Unused Code introduced by
The assignment to $discounts is dead and can be removed.
Loading history...
517
            
518
            $response['success'] = true;
519
            $response['msg'] = __( 'Discount has been applied successfully.', 'invoicing' );
520
        }  else {
521
            $errors = wpinv_get_errors();
522
            if ( !empty( $errors['wpinv-discount-error'] ) ) {
523
                $response['msg'] = $errors['wpinv-discount-error'];
524
            }
525
            wpinv_unset_error( 'wpinv-discount-error' );
526
        }
527
        
528
        wpinv_set_checkout_session($checkout_session);
529
        
530
        wp_send_json( $response );
531
    }
532
    
533
    public static function admin_remove_discount() {
534
        global $wpi_userID;
535
        
536
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
537
        if ( !wpinv_current_user_can_manage_invoicing() ) {
538
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
539
        }
540
        
541
        $invoice_id = absint( $_POST['invoice_id'] );
542
        $discount_code = sanitize_text_field( $_POST['code'] );
543
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
544
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
545
        }
546
        
547
        $invoice = wpinv_get_invoice( $invoice_id );
548
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
549
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
550
        }
551
        
552
        $checkout_session = wpinv_get_checkout_session();
553
        
554
        $data                   = array();
555
        $data['invoice_id']     = $invoice_id;
556
        $data['cart_discounts'] = $invoice->get_discounts( true );
557
        
558
        wpinv_set_checkout_session( $data );
559
        
560
        $response               = array();
561
        $response['success']    = false;
562
        $response['msg']        = NULL;
563
        
564
        $discounts  = wpinv_unset_cart_discount( $discount_code );
0 ignored issues
show
Unused Code introduced by
The assignment to $discounts is dead and can be removed.
Loading history...
565
        $response['success'] = true;
566
        $response['msg'] = __( 'Discount has been removed successfully.', 'invoicing' );
567
        
568
        wpinv_set_checkout_session($checkout_session);
569
        
570
        wp_send_json( $response );
571
    }
572
    
573
    public static function check_email() {
574
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
575
        if ( !wpinv_current_user_can_manage_invoicing() ) {
576
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
577
        }
578
        
579
        $email = sanitize_text_field( $_POST['email'] );
580
        
581
        $response = array();
582
        if ( is_email( $email ) && email_exists( $email ) && $user_data = get_user_by( 'email', $email ) ) {
583
            $user_id            = $user_data->ID;
584
            $user_login         = $user_data->user_login;
585
            $display_name       = $user_data->display_name ? $user_data->display_name : $user_login;
0 ignored issues
show
Unused Code introduced by
The assignment to $display_name is dead and can be removed.
Loading history...
586
            $billing_details    = wpinv_get_user_address($user_id);
587
            $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
588
            
589
            if (isset($billing_details['user_id'])) {
590
                unset($billing_details['user_id']);
591
            }
592
            
593
            if (isset($billing_details['email'])) {
594
                unset($billing_details['email']);
595
            }
596
            
597
            $response['success']                    = true;
598
            $response['data']['id']                 = $user_data->ID;
599
            $response['data']['name']               = $user_data->user_email;
600
            $response['data']['billing_details']    = $billing_details;
601
        }
602
        
603
        wp_send_json( $response );
604
    }
605
    
606
    public static function run_tool() {
607
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
608
        if ( !wpinv_current_user_can_manage_invoicing() ) {
609
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
610
        }
611
        
612
        $tool = sanitize_text_field( $_POST['tool'] );
613
        
614
        do_action( 'wpinv_run_tool' );
615
        
616
        if ( !empty( $tool ) ) {
617
            do_action( 'wpinv_tool_' . $tool );
618
        }
619
    }
620
    
621
    public static function apply_discount() {
622
        global $wpi_userID;
623
        
624
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
625
        
626
        $response = array();
627
        
628
        if ( isset( $_POST['code'] ) ) {
629
            $discount_code = sanitize_text_field( $_POST['code'] );
630
631
            $response['success']        = false;
632
            $response['msg']            = '';
633
            $response['data']['code']   = $discount_code;
634
            
635
            $invoice = wpinv_get_invoice_cart();
636
            if ( empty( $invoice->ID ) ) {
637
                $response['msg'] = __( 'Invalid checkout request.', 'invoicing' );
638
                wp_send_json( $response );
639
            }
640
641
            $wpi_userID = $invoice->get_user_id();
642
643
            if ( wpinv_is_discount_valid( $discount_code, $wpi_userID ) ) {
644
                $discount       = wpinv_get_discount_by_code( $discount_code );
645
                $discounts      = wpinv_set_cart_discount( $discount_code );
646
                $amount         = wpinv_format_discount_rate( wpinv_get_discount_type( $discount->ID ), wpinv_get_discount_amount( $discount->ID ) );
0 ignored issues
show
Unused Code introduced by
The assignment to $amount is dead and can be removed.
Loading history...
647
                $total          = wpinv_get_cart_total( null, $discounts );
0 ignored issues
show
Unused Code introduced by
The assignment to $total is dead and can be removed.
Loading history...
648
                $cart_totals    = wpinv_recalculate_tax( true );
649
            
650
                if ( !empty( $cart_totals ) ) {
651
                    $response['success']        = true;
652
                    $response['data']           = $cart_totals;
653
                    $response['data']['code']   = $discount_code;
654
                } else {
655
                    $response['success']        = false;
656
                }
657
            } else {
658
                $errors = wpinv_get_errors();
659
                $response['msg']  = $errors['wpinv-discount-error'];
660
                wpinv_unset_error( 'wpinv-discount-error' );
661
            }
662
663
            // Allow for custom discount code handling
664
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
665
        }
666
        
667
        wp_send_json( $response );
668
    }
669
    
670
    public static function remove_discount() {
671
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
672
        
673
        $response = array();
674
        
675
        if ( isset( $_POST['code'] ) ) {
676
            $discount_code  = sanitize_text_field( $_POST['code'] );
677
            $discounts      = wpinv_unset_cart_discount( $discount_code );
678
            $total          = wpinv_get_cart_total( null, $discounts );
0 ignored issues
show
Unused Code introduced by
The assignment to $total is dead and can be removed.
Loading history...
679
            $cart_totals    = wpinv_recalculate_tax( true );
680
            
681
            if ( !empty( $cart_totals ) ) {
682
                $response['success']        = true;
683
                $response['data']           = $cart_totals;
684
                $response['data']['code']   = $discount_code;
685
            } else {
686
                $response['success']        = false;
687
            }
688
            
689
            // Allow for custom discount code handling
690
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
691
        }
692
        
693
        wp_send_json( $response );
694
    }
695
696
    /**
697
     * Payment forms.
698
     *
699
     * @since 1.0.18
700
     */
701
    public static function payment_form() {
702
        global $invoicing;
703
704
        // Check nonce.
705
        if ( ! isset( $_POST['wpinv_payment_form'] ) || ! wp_verify_nonce( $_POST['wpinv_payment_form'], 'wpinv_payment_form' ) ) {
706
            wp_send_json_error( __( 'Security checks failed.', 'invoicing' ) );
707
        }
708
709
        // Prepare submitted data...
710
        $data     = wp_unslash( $_POST );
711
712
        // ... form fields...
713
        if ( empty( $data['form_id'] ) || 'publish' != get_post_status( $data['form_id'] ) ) {
714
            wp_send_json_error( __( 'This payment form is no longer active.', 'invoicing' ) );
715
        }
716
717
        if ( empty( $data['billing_email'] ) || ! is_email( $data['billing_email'] ) ) {
718
            wp_send_json_error( __( 'Provide a valid billing email.', 'invoicing' ) );
719
        }
720
721
        $prepared = array(
722
            'billing_email'                    => sanitize_email( $data['billing_email'] ),
723
            __( 'Billing Email', 'invoicing' ) => sanitize_email( $data['billing_email'] ),
724
            __( 'Form Id', 'invoicing' )       => absint( $data['form_id'] ),
725
        );
726
727
        $prepared['billing_email'] = sanitize_email( $data['billing_email'] );
728
729
        $fields = $invoicing->form_elements->get_form_elements( $data['form_id'] );
730
731
        // ... and form items.
732
        $items          = $invoicing->form_elements->get_form_items( $data['form_id'] );
733
        $prepared_items = array();
734
735
        if ( ! empty( $data['payment-form-items'] ) ) {
736
737
            $selected_items   = wpinv_parse_list( $data['payment-form-items'] );
738
739
            foreach( $items as $item ) {
740
741
                if ( ! in_array( $item['id'], $selected_items ) ) {
742
                    continue;
743
                }
744
745
                $prepared_items[] = array(
746
                    'id'           => $item['id'],
747
                    'item_price'   => wpinv_sanitize_amount( $item['price'] ),
748
                    'custom_price' => wpinv_sanitize_amount( $item['price'] ),
749
                    'name'         => $item['title'],
750
                );
751
752
            }
753
754
        } else {
755
756
            foreach( $items as $item ) {
757
                $prepared_items[] = array(
758
                    'id'           => $item['id'],
759
                    'item_price'   => wpinv_sanitize_amount( $item['price'] ),
760
                    'custom_price' => wpinv_sanitize_amount( $item['price'] ),
761
                    'name'         => $item['title'],
762
                );
763
            }
764
765
        }
766
767
        // Are all required fields provided?
768
        foreach ( $fields as $field ) {
769
770
            if ( ! empty( $field['premade'] ) ) {
771
                continue;
772
            }
773
774
            if ( ! empty( $field['required'] ) && empty( $data[ $field['id'] ] ) ) {
775
                wp_send_json_error( __( 'Some required fields have not been filled.', 'invoicing' ) );
776
            }
777
778
            if ( isset( $data[ $field['id'] ] ) ) {
779
                $label = $field['id'];
780
781
                if ( isset( $field['label'] ) ) {
782
                    $label = $field['label'];
783
                }
784
785
                $prepared[ wpinv_clean( $label ) ] = wpinv_clean( $data[ $field['id'] ] );
786
            }
787
788
        }
789
        
790
        $user = get_user_by( 'email', $prepared['billing_email'] );
791
792
        if ( empty( $user ) ) {
793
            $user = wpinv_create_user( $prepared['billing_email'] );
794
        }
795
796
        if ( is_wp_error( $user ) ) {
797
            wp_send_json_error( $user->get_error_message() );
798
        }
799
800
        if ( is_numeric( $user ) ) {
801
            $user = get_user_by( 'id', $user );
802
        }
803
804
        // Create the invoice.
805
        $created = wpinv_insert_invoice(
806
            array(
807
                'status'        =>  'wpi-pending',
808
                'created_via'   =>  'wpi',
809
                'user_id'       =>  $user->ID,
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on WP_Error.
Loading history...
810
                'cart_details'  =>  $prepared_items,
811
            ),
812
            true
813
        );
814
815
        if ( is_wp_error( $created ) ) {
816
            wp_send_json_error( $created->get_error_message() );
0 ignored issues
show
Bug introduced by
The method get_error_message() does not exist on WPInv_Invoice. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

816
            wp_send_json_error( $created->/** @scrutinizer ignore-call */ get_error_message() );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
817
        }
818
819
        if ( empty( $created ) ) {
820
            wp_send_json_error( __( 'Could not create your invoice.', 'invoicing' ) );
821
        }
822
823
        unset( $prepared['billing_email'] );
824
        update_post_meta( $created->ID, 'payment_form_data', $prepared );
825
826
        wp_send_json_success( $created->get_view_url( true ) );
0 ignored issues
show
Bug introduced by
The method get_view_url() does not exist on WP_Error. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

826
        wp_send_json_success( $created->/** @scrutinizer ignore-call */ get_view_url( true ) );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
827
828
    }
829
830
    /**
831
     * Lets users buy items via ajax.
832
     *
833
     * @since 1.0.0
834
     */
835
    public static function buy_items() {
836
        $user_id = get_current_user_id();
837
838
        if ( empty( $user_id ) ) { // If not logged in then lets redirect to the login page
839
            wp_send_json( array(
840
                'success' => wp_login_url( wp_get_referer() )
0 ignored issues
show
Bug introduced by
It seems like wp_get_referer() can also be of type false; however, parameter $redirect of wp_login_url() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

840
                'success' => wp_login_url( /** @scrutinizer ignore-type */ wp_get_referer() )
Loading history...
841
            ) );
842
        } else {
843
            // Only check nonce if logged in as it could be cached when logged out.
844
            if ( ! isset( $_POST['wpinv_buy_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_buy_nonce'], 'wpinv_buy_items' ) ) {
845
                wp_send_json( array(
846
                    'error' => __( 'Security checks failed.', 'invoicing' )
847
                ) );
848
                wp_die();
849
            }
850
851
            // allow to set a custom price through post_id
852
            $items = $_POST['items'];
853
            $related_post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : 0;
854
            $custom_item_price = $related_post_id ? abs( get_post_meta( $related_post_id, '_wpi_custom_price', true ) ) : 0;
855
856
            $cart_items = array();
857
            if ( $items ) {
858
                $items = explode( ',', $items );
859
860
                foreach( $items as $item ) {
861
                    $item_id = $item;
862
                    $quantity = 1;
863
864
                    if ( strpos( $item, '|' ) !== false ) {
865
                        $item_parts = explode( '|', $item );
866
                        $item_id = $item_parts[0];
867
                        $quantity = $item_parts[1];
868
                    }
869
870
                    if ( $item_id && $quantity ) {
871
                        $cart_items_arr = array(
872
                            'id'            => (int)$item_id,
873
                            'quantity'      => (int)$quantity
874
                        );
875
876
                        // If there is a related post id then add it to meta
877
                        if ( $related_post_id ) {
878
                            $cart_items_arr['meta'] = array(
879
                                'post_id'   => $related_post_id
880
                            );
881
                        }
882
883
                        // If there is a custom price then set it.
884
                        if ( $custom_item_price ) {
885
                            $cart_items_arr['custom_price'] = $custom_item_price;
886
                        }
887
888
                        $cart_items[] = $cart_items_arr;
889
                    }
890
                }
891
            }
892
893
            /**
894
             * Filter the wpinv_buy shortcode cart items on the fly.
895
             *
896
             * @param array $cart_items The cart items array.
897
             * @param int $related_post_id The related post id if any.
898
             * @since 1.0.0
899
             */
900
            $cart_items = apply_filters( 'wpinv_buy_cart_items', $cart_items, $related_post_id );
901
902
            // Make sure its not in the cart already, if it is then redirect to checkout.
903
            $cart_invoice = wpinv_get_invoice_cart();
904
905
            if ( isset( $cart_invoice->items ) && !empty( $cart_invoice->items ) && !empty( $cart_items ) && serialize( $cart_invoice->items ) == serialize( $cart_items ) ) {
906
                wp_send_json( array(
907
                    'success' =>  $cart_invoice->get_checkout_payment_url()
908
                ) );
909
                wp_die();
910
            }
911
912
            // Check if user has invoice with same items waiting to be paid.
913
            $user_invoices = wpinv_get_users_invoices( $user_id , 10 , false , 'wpi-pending' );
914
            if ( !empty( $user_invoices ) ) {
915
                foreach( $user_invoices as $user_invoice ) {
916
                    $user_cart_details = array();
917
                    $invoice  = wpinv_get_invoice( $user_invoice->ID );
918
                    $cart_details = $invoice->get_cart_details();
919
920
                    if ( !empty( $cart_details ) ) {
921
                        foreach ( $cart_details as $invoice_item ) {
922
                            $ii_arr = array();
923
                            $ii_arr['id'] = (int)$invoice_item['id'];
924
                            $ii_arr['quantity'] = (int)$invoice_item['quantity'];
925
926
                            if (isset( $invoice_item['meta'] ) && !empty( $invoice_item['meta'] ) ) {
927
                                $ii_arr['meta'] = $invoice_item['meta'];
928
                            }
929
930
                            if ( isset( $invoice_item['custom_price'] ) && !empty( $invoice_item['custom_price'] ) ) {
931
                                $ii_arr['custom_price'] = $invoice_item['custom_price'];
932
                            }
933
934
                            $user_cart_details[] = $ii_arr;
935
                        }
936
                    }
937
938
                    if ( !empty( $user_cart_details ) && serialize( $cart_items ) == serialize( $user_cart_details ) ) {
939
                        wp_send_json( array(
940
                            'success' =>  $invoice->get_checkout_payment_url()
941
                        ) );
942
                        wp_die();
943
                    }
944
                }
945
            }
946
947
            // Create invoice and send user to checkout
948
            if ( !empty( $cart_items ) ) {
949
                $invoice_data = array(
950
                    'status'        =>  'wpi-pending',
951
                    'created_via'   =>  'wpi',
952
                    'user_id'       =>  $user_id,
953
                    'cart_details'  =>  $cart_items,
954
                );
955
956
                $invoice = wpinv_insert_invoice( $invoice_data, true );
957
958
                if ( !empty( $invoice ) && isset( $invoice->ID ) ) {
959
                    wp_send_json( array(
960
                        'success' =>  $invoice->get_checkout_payment_url()
0 ignored issues
show
Bug introduced by
The method get_checkout_payment_url() does not exist on WP_Error. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

960
                        'success' =>  $invoice->/** @scrutinizer ignore-call */ get_checkout_payment_url()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
961
                    ) );
962
                } else {
963
                    wp_send_json( array(
964
                        'error' => __( 'Invoice failed to create', 'invoicing' )
965
                    ) );
966
                }
967
            } else {
968
                wp_send_json( array(
969
                    'error' => __( 'Items not valid.', 'invoicing' )
970
                ) );
971
            }
972
        }
973
974
        wp_die();
975
    }
976
}
977
978
WPInv_Ajax::init();