Passed
Branch master (50908e)
by Stiofan
07:01
created

WPInv_Ajax   F

Complexity

Total Complexity 155

Size/Duplication

Total Lines 827
Duplicated Lines 4.84 %

Coupling/Cohesion

Components 0
Dependencies 2

Importance

Changes 0
Metric Value
dl 40
loc 827
rs 1.773
c 0
b 0
f 0
wmc 155
lcom 0
cbo 2

21 Methods

Rating   Name   Duplication   Size   Complexity  
A apply_discount() 0 47 5
A wpinv_ajax_headers() 0 7 1
A init() 0 4 1
A run_tool() 0 12 3
F add_invoice_item() 0 108 21
B define_ajax() 0 13 7
B admin_apply_discount() 0 48 10
A add_note() 0 22 6
B check_email() 0 31 8
B admin_recalculate_totals() 0 56 7
B admin_remove_discount() 0 38 8
C create_invoice_item() 0 49 13
F buy_items() 0 140 30
A get_billing_details() 0 24 4
A checkout() 0 7 2
A remove_discount() 0 24 3
A delete_note() 0 14 3
A do_wpinv_ajax() 0 11 3
A get_states_field() 0 4 1
C remove_invoice_item() 0 83 15
A add_ajax_events() 0 31 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complex Class

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

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

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

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

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
                @ini_set( 'display_errors', 0 );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

31
                /** @scrutinizer ignore-unhandled */ @ini_set( 'display_errors', 0 );

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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
        @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for header(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

53
        /** @scrutinizer ignore-unhandled */ @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
54
        @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
            'add_invoice_item' => false,
67
            'remove_invoice_item' => false,
68
            'create_invoice_item' => false,
69
            'get_billing_details' => false,
70
            'admin_recalculate_totals' => false,
71
            'admin_apply_discount' => false,
72
            'admin_remove_discount' => false,
73
            'check_email' => false,
74
            'run_tool' => false,
75
            'apply_discount' => true,
76
            'remove_discount' => true,
77
            'buy_items' => true,
78
        );
79
80
        foreach ( $ajax_events as $ajax_event => $nopriv ) {
81
            add_action( 'wp_ajax_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
82
            
83
            if ( !defined( 'WPI_AJAX_' . strtoupper( $nopriv ) ) ) {
84
                define( 'WPI_AJAX_' . strtoupper( $nopriv ), 1 );
85
            }
86
87
            if ( $nopriv ) {
88
                add_action( 'wp_ajax_nopriv_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
89
90
                add_action( 'wpinv_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
91
            }
92
        }
93
    }
94
    
95
    public static function add_note() {
96
        check_ajax_referer( 'add-invoice-note', '_nonce' );
97
98
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
99
            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...
100
        }
101
102
        $post_id   = absint( $_POST['post_id'] );
103
        $note      = wp_kses_post( trim( stripslashes( $_POST['note'] ) ) );
104
        $note_type = sanitize_text_field( $_POST['note_type'] );
105
106
        $is_customer_note = $note_type == 'customer' ? 1 : 0;
107
108
        if ( $post_id > 0 ) {
109
            $note_id = wpinv_insert_payment_note( $post_id, $note, $is_customer_note );
110
111
            if ( $note_id > 0 && !is_wp_error( $note_id ) ) {
112
                wpinv_get_invoice_note_line_item( $note_id );
113
            }
114
        }
115
116
        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...
117
    }
118
119
    public static function delete_note() {
120
        check_ajax_referer( 'delete-invoice-note', '_nonce' );
121
122
        if ( !wpinv_current_user_can_manage_invoicing() ) {
123
            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...
124
        }
125
126
        $note_id = (int)$_POST['note_id'];
127
128
        if ( $note_id > 0 ) {
129
            wp_delete_comment( $note_id, true );
130
        }
131
132
        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...
133
    }
134
    
135
    public static function get_states_field() {
136
        echo wpinv_get_states_field();
137
        
138
        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...
139
    }
140
    
141
    public static function checkout() {
142
        if ( ! defined( 'WPINV_CHECKOUT' ) ) {
143
            define( 'WPINV_CHECKOUT', true );
144
        }
145
146
        wpinv_process_checkout();
147
        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...
148
    }
149
    
150
    public static function add_invoice_item() {
151
        global $wpi_userID, $wpinv_ip_address_country;
152
        check_ajax_referer( 'invoice-item', '_nonce' );
153
        if ( !wpinv_current_user_can_manage_invoicing() ) {
154
            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...
155
        }
156
        
157
        $item_id    = sanitize_text_field( $_POST['item_id'] );
158
        $invoice_id = absint( $_POST['invoice_id'] );
159
        
160
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
161
            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...
162
        }
163
        
164
        $invoice    = wpinv_get_invoice( $invoice_id );
165
        if ( empty( $invoice ) ) {
166
            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...
167
        }
168
        
169
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
170
            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...
171
        }
172
        
173
        if ( !empty( $_POST['user_id'] ) ) {
174
            $wpi_userID = absint( $_POST['user_id'] ); 
175
        }
176
177
        $item = new WPInv_Item( $item_id );
178
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
179
            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...
180
        }
181
        
182
        // Validate item before adding to invoice because recurring item must be paid individually.
183
        if ( !empty( $invoice->cart_details ) ) {
184
            $valid = true;
185
            
186
            if ( $recurring_item = $invoice->get_recurring() ) {
187
                if ( $recurring_item != $item_id ) {
188
                    $valid = false;
189
                }
190
            } else if ( wpinv_is_recurring_item( $item_id ) ) {
191
                $valid = false;
192
            }
193
            
194
            if ( !$valid ) {
195
                $response               = array();
196
                $response['success']    = false;
197
                $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
198
                wp_send_json( $response );
199
            }
200
        }
201
        
202
        $checkout_session = wpinv_get_checkout_session();
203
        
204
        $data                   = array();
205
        $data['invoice_id']     = $invoice_id;
206
        $data['cart_discounts'] = $invoice->get_discounts( true );
207
        
208
        wpinv_set_checkout_session( $data );
209
        
210
        $quantity = wpinv_item_quantities_enabled() && !empty($_POST['qty']) && (int)$_POST['qty'] > 0 ? (int)$_POST['qty'] : 1;
211
212
        $args = array(
213
            'id'            => $item_id,
214
            'quantity'      => $quantity,
215
            'item_price'    => $item->get_price(),
216
            'custom_price'  => '',
217
            'tax'           => 0.00,
218
            'discount'      => 0,
219
            'meta'          => array(),
220
            'fees'          => array()
221
        );
222
223
        $invoice->add_item( $item_id, $args );
224
        $invoice->save();
225
        
226
        if ( empty( $_POST['country'] ) ) {
227
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
228
        }
229
        if ( empty( $_POST['state'] ) ) {
230
            $_POST['state'] = $invoice->state;
231
        }
232
         
233
        $invoice->country   = sanitize_text_field( $_POST['country'] );
234
        $invoice->state     = sanitize_text_field( $_POST['state'] );
235
        
236
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
237
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
238
        
239
        $wpinv_ip_address_country = $invoice->country;
240
241
        $invoice->recalculate_totals(true);
242
        
243
        $response                       = array();
244
        $response['success']            = true;
245
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
246
        $response['data']['subtotal']   = $invoice->get_subtotal();
247
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
248
        $response['data']['tax']        = $invoice->get_tax();
249
        $response['data']['taxf']       = $invoice->get_tax(true);
250
        $response['data']['discount']   = $invoice->get_discount();
251
        $response['data']['discountf']  = $invoice->get_discount(true);
252
        $response['data']['total']      = $invoice->get_total();
253
        $response['data']['totalf']     = $invoice->get_total(true);
254
        
255
        wpinv_set_checkout_session($checkout_session);
256
        
257
        wp_send_json( $response );
258
    }
259
    
260
    public static function remove_invoice_item() {
261
        global $wpi_userID, $wpinv_ip_address_country;
262
        
263
        check_ajax_referer( 'invoice-item', '_nonce' );
264
        if ( !wpinv_current_user_can_manage_invoicing() ) {
265
            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...
266
        }
267
        
268
        $item_id    = sanitize_text_field( $_POST['item_id'] );
269
        $invoice_id = absint( $_POST['invoice_id'] );
270
        $cart_index = isset( $_POST['index'] ) && $_POST['index'] >= 0 ? $_POST['index'] : false;
271
        
272
        if ( !is_numeric( $invoice_id ) || !is_numeric( $item_id ) ) {
273
            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...
274
        }
275
276
        $invoice    = wpinv_get_invoice( $invoice_id );
277
        if ( empty( $invoice ) ) {
278
            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...
279
        }
280
        
281
        if ( $invoice->is_paid() || $invoice->is_refunded() ) {
282
            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...
283
        }
284
        
285
        if ( !empty( $_POST['user_id'] ) ) {
286
            $wpi_userID = absint( $_POST['user_id'] ); 
287
        }
288
289
        $item       = new WPInv_Item( $item_id );
290
        if ( !( !empty( $item ) && $item->post_type == 'wpi_item' ) ) {
291
            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...
292
        }
293
        
294
        $checkout_session = wpinv_get_checkout_session();
295
        
296
        $data                   = array();
297
        $data['invoice_id']     = $invoice_id;
298
        $data['cart_discounts'] = $invoice->get_discounts( true );
299
        
300
        wpinv_set_checkout_session( $data );
301
302
        $args = array(
303
            'id'         => $item_id,
304
            'quantity'   => 1,
305
            'cart_index' => $cart_index
306
        );
307
308
        $invoice->remove_item( $item_id, $args );
309
        $invoice->save();
310
        
311
        if ( empty( $_POST['country'] ) ) {
312
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
313
        }
314
        if ( empty( $_POST['state'] ) ) {
315
            $_POST['state'] = $invoice->state;
316
        }
317
         
318
        $invoice->country   = sanitize_text_field( $_POST['country'] );
319
        $invoice->state     = sanitize_text_field( $_POST['state'] );
320
        
321
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
322
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
323
        
324
        $wpinv_ip_address_country = $invoice->country;
325
        
326
        $invoice->recalculate_totals(true);
327
        
328
        $response                       = array();
329
        $response['success']            = true;
330
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
331
        $response['data']['subtotal']   = $invoice->get_subtotal();
332
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
333
        $response['data']['tax']        = $invoice->get_tax();
334
        $response['data']['taxf']       = $invoice->get_tax(true);
335
        $response['data']['discount']   = $invoice->get_discount();
336
        $response['data']['discountf']  = $invoice->get_discount(true);
337
        $response['data']['total']      = $invoice->get_total();
338
        $response['data']['totalf']     = $invoice->get_total(true);
339
        
340
        wpinv_set_checkout_session($checkout_session);
341
        
342
        wp_send_json( $response );
343
    }
344
    
345
    public static function create_invoice_item() {
346
        check_ajax_referer( 'invoice-item', '_nonce' );
347
        if ( !wpinv_current_user_can_manage_invoicing() ) {
348
            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...
349
        }
350
        
351
        $invoice_id = absint( $_POST['invoice_id'] );
352
353
        // Find the item
354
        if ( !is_numeric( $invoice_id ) ) {
355
            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...
356
        }        
357
        
358
        $invoice     = wpinv_get_invoice( $invoice_id );
359
        if ( empty( $invoice ) ) {
360
            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...
361
        }
362
        
363
        // Validate item before adding to invoice because recurring item must be paid individually.
364
        if ( !empty( $invoice->cart_details ) && $invoice->get_recurring() ) {
365
            $response               = array();
366
            $response['success']    = false;
367
            $response['msg']        = __( 'You can not add item because recurring item must be paid individually!', 'invoicing' );
368
            wp_send_json( $response );
369
        }        
370
        
371
        $save_item = $_POST['_wpinv_quick'];
372
        
373
        $meta               = array();
374
        $meta['type']       = !empty($save_item['type']) ? sanitize_text_field($save_item['type']) : 'custom';
375
        $meta['price']      = !empty($save_item['price']) ? wpinv_sanitize_amount( $save_item['price'] ) : 0;
376
        $meta['vat_rule']   = !empty($save_item['vat_rule']) ? sanitize_text_field($save_item['vat_rule']) : 'digital';
377
        $meta['vat_class']  = !empty($save_item['vat_class']) ? sanitize_text_field($save_item['vat_class']) : '_standard';
378
        
379
        $data                   = array();
380
        $data['post_title']     = sanitize_text_field($save_item['name']);
381
        $data['post_status']    = 'publish';
382
        $data['meta']           = $meta;
383
        
384
        $item = new WPInv_Item();
385
        $item->create( $data );
386
        
387
        if ( !empty( $item ) ) {
388
            $_POST['item_id']   = $item->ID;
389
            $_POST['qty']       = !empty($save_item['qty']) && $save_item['qty'] > 0 ? (int)$save_item['qty'] : 1;
390
            
391
            self::add_invoice_item();
392
        }
393
        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...
394
    }
395
    
396
    public static function get_billing_details() {
397
        check_ajax_referer( 'get-billing-details', '_nonce' );
398
        
399
        if ( !wpinv_current_user_can_manage_invoicing() ) {
400
            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...
401
        }
402
403
        $user_id            = (int)$_POST['user_id'];
404
        $billing_details    = wpinv_get_user_address($user_id);
405
        $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
406
        
407
        if (isset($billing_details['user_id'])) {
408
            unset($billing_details['user_id']);
409
        }
410
        
411
        if (isset($billing_details['email'])) {
412
            unset($billing_details['email']);
413
        }
414
415
        $response                               = array();
416
        $response['success']                    = true;
417
        $response['data']['billing_details']    = $billing_details;
418
        
419
        wp_send_json( $response );
420
    }
421
    
422
    public static function admin_recalculate_totals() {
423
        global $wpi_userID, $wpinv_ip_address_country;
424
        
425
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
426
        if ( !wpinv_current_user_can_manage_invoicing() ) {
427
            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...
428
        }
429
        
430
        $invoice_id = absint( $_POST['invoice_id'] );        
431
        $invoice    = wpinv_get_invoice( $invoice_id );
432
        if ( empty( $invoice ) ) {
433
            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...
434
        }
435
        
436
        $checkout_session = wpinv_get_checkout_session();
437
        
438
        $data                   = array();
439
        $data['invoice_id']     = $invoice_id;
440
        $data['cart_discounts'] = $invoice->get_discounts( true );
441
        
442
        wpinv_set_checkout_session( $data );
443
        
444
        if ( !empty( $_POST['user_id'] ) ) {
445
            $wpi_userID = absint( $_POST['user_id'] ); 
446
        }
447
        
448
        if ( empty( $_POST['country'] ) ) {
449
            $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
450
        }
451
            
452
        $invoice->country = sanitize_text_field( $_POST['country'] );
453
        $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
454
        if ( isset( $_POST['state'] ) ) {
455
            $invoice->state = sanitize_text_field( $_POST['state'] );
456
            $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
457
        }
458
        
459
        $wpinv_ip_address_country = $invoice->country;
460
        
461
        $invoice = $invoice->recalculate_totals(true);
462
        
463
        $response                       = array();
464
        $response['success']            = true;
465
        $response['data']['items']      = wpinv_admin_get_line_items( $invoice );
466
        $response['data']['subtotal']   = $invoice->get_subtotal();
467
        $response['data']['subtotalf']  = $invoice->get_subtotal(true);
468
        $response['data']['tax']        = $invoice->get_tax();
469
        $response['data']['taxf']       = $invoice->get_tax(true);
470
        $response['data']['discount']   = $invoice->get_discount();
471
        $response['data']['discountf']  = $invoice->get_discount(true);
472
        $response['data']['total']      = $invoice->get_total();
473
        $response['data']['totalf']     = $invoice->get_total(true);
474
        
475
        wpinv_set_checkout_session($checkout_session);
476
        
477
        wp_send_json( $response );
478
    }
479
    
480
    public static function admin_apply_discount() {
481
        global $wpi_userID;
482
        
483
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
484
        if ( !wpinv_current_user_can_manage_invoicing() ) {
485
            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...
486
        }
487
        
488
        $invoice_id = absint( $_POST['invoice_id'] );
489
        $discount_code = sanitize_text_field( $_POST['code'] );
490
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
491
            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...
492
        }
493
        
494
        $invoice = wpinv_get_invoice( $invoice_id );
495
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
496
            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...
497
        }
498
        
499
        $checkout_session = wpinv_get_checkout_session();
500
        
501
        $data                   = array();
502
        $data['invoice_id']     = $invoice_id;
503
        $data['cart_discounts'] = $invoice->get_discounts( true );
504
        
505
        wpinv_set_checkout_session( $data );
506
        
507
        $response               = array();
508
        $response['success']    = false;
509
        $response['msg']        = __( 'This discount is invalid.', 'invoicing' );
510
        $response['data']['code'] = $discount_code;
511
        
512
        if ( wpinv_is_discount_valid( $discount_code, $invoice->get_user_id() ) ) {
513
            $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...
514
            
515
            $response['success'] = true;
516
            $response['msg'] = __( 'Discount has been applied successfully.', 'invoicing' );
517
        }  else {
518
            $errors = wpinv_get_errors();
519
            if ( !empty( $errors['wpinv-discount-error'] ) ) {
520
                $response['msg'] = $errors['wpinv-discount-error'];
521
            }
522
            wpinv_unset_error( 'wpinv-discount-error' );
523
        }
524
        
525
        wpinv_set_checkout_session($checkout_session);
526
        
527
        wp_send_json( $response );
528
    }
529
    
530
    public static function admin_remove_discount() {
531
        global $wpi_userID;
532
        
533
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
534
        if ( !wpinv_current_user_can_manage_invoicing() ) {
535
            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...
536
        }
537
        
538
        $invoice_id = absint( $_POST['invoice_id'] );
539
        $discount_code = sanitize_text_field( $_POST['code'] );
540
        if ( empty( $invoice_id ) || empty( $discount_code ) ) {
541
            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...
542
        }
543
        
544
        $invoice = wpinv_get_invoice( $invoice_id );
545
        if ( empty( $invoice ) || ( !empty( $invoice ) && ( $invoice->is_paid() || $invoice->is_refunded() ) ) ) {
546
            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...
547
        }
548
        
549
        $checkout_session = wpinv_get_checkout_session();
550
        
551
        $data                   = array();
552
        $data['invoice_id']     = $invoice_id;
553
        $data['cart_discounts'] = $invoice->get_discounts( true );
554
        
555
        wpinv_set_checkout_session( $data );
556
        
557
        $response               = array();
558
        $response['success']    = false;
559
        $response['msg']        = NULL;
560
        
561
        $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...
562
        $response['success'] = true;
563
        $response['msg'] = __( 'Discount has been removed successfully.', 'invoicing' );
564
        
565
        wpinv_set_checkout_session($checkout_session);
566
        
567
        wp_send_json( $response );
568
    }
569
    
570
    public static function check_email() {
571
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
572
        if ( !wpinv_current_user_can_manage_invoicing() ) {
573
            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...
574
        }
575
        
576
        $email = sanitize_text_field( $_POST['email'] );
577
        
578
        $response = array();
579
        if ( is_email( $email ) && email_exists( $email ) && $user_data = get_user_by( 'email', $email ) ) {
580
            $user_id            = $user_data->ID;
581
            $user_login         = $user_data->user_login;
582
            $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...
583
            $billing_details    = wpinv_get_user_address($user_id);
584
            $billing_details    = apply_filters( 'wpinv_fill_billing_details', $billing_details, $user_id );
585
            
586
            if (isset($billing_details['user_id'])) {
587
                unset($billing_details['user_id']);
588
            }
589
            
590
            if (isset($billing_details['email'])) {
591
                unset($billing_details['email']);
592
            }
593
            
594
            $response['success']                    = true;
595
            $response['data']['id']                 = $user_data->ID;
596
            $response['data']['name']               = $user_data->user_email;
597
            $response['data']['billing_details']    = $billing_details;
598
        }
599
        
600
        wp_send_json( $response );
601
    }
602
    
603
    public static function run_tool() {
604
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
605
        if ( !wpinv_current_user_can_manage_invoicing() ) {
606
            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...
607
        }
608
        
609
        $tool = sanitize_text_field( $_POST['tool'] );
610
        
611
        do_action( 'wpinv_run_tool' );
612
        
613
        if ( !empty( $tool ) ) {
614
            do_action( 'wpinv_tool_' . $tool );
615
        }
616
    }
617
    
618
    public static function apply_discount() {
619
        global $wpi_userID;
620
        
621
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
622
        
623
        $response = array();
624
        
625
        if ( isset( $_POST['code'] ) ) {
626
            $discount_code = sanitize_text_field( $_POST['code'] );
627
628
            $response['success']        = false;
629
            $response['msg']            = '';
630
            $response['data']['code']   = $discount_code;
631
            
632
            $invoice = wpinv_get_invoice_cart();
633
            if ( empty( $invoice->ID ) ) {
634
                $response['msg'] = __( 'Invalid checkout request.', 'invoicing' );
635
                wp_send_json( $response );
636
            }
637
638
            $wpi_userID = $invoice->get_user_id();
639
640
            if ( wpinv_is_discount_valid( $discount_code, $wpi_userID ) ) {
641
                $discount       = wpinv_get_discount_by_code( $discount_code );
642
                $discounts      = wpinv_set_cart_discount( $discount_code );
643
                $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...
644
                $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...
645
                $cart_totals    = wpinv_recalculate_tax( true );
646
            
647
                if ( !empty( $cart_totals ) ) {
648
                    $response['success']        = true;
649
                    $response['data']           = $cart_totals;
650
                    $response['data']['code']   = $discount_code;
651
                } else {
652
                    $response['success']        = false;
653
                }
654
            } else {
655
                $errors = wpinv_get_errors();
656
                $response['msg']  = $errors['wpinv-discount-error'];
657
                wpinv_unset_error( 'wpinv-discount-error' );
658
            }
659
660
            // Allow for custom discount code handling
661
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
662
        }
663
        
664
        wp_send_json( $response );
665
    }
666
    
667
    public static function remove_discount() {
668
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
669
        
670
        $response = array();
671
        
672
        if ( isset( $_POST['code'] ) ) {
673
            $discount_code  = sanitize_text_field( $_POST['code'] );
674
            $discounts      = wpinv_unset_cart_discount( $discount_code );
675
            $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...
676
            $cart_totals    = wpinv_recalculate_tax( true );
677
            
678
            if ( !empty( $cart_totals ) ) {
679
                $response['success']        = true;
680
                $response['data']           = $cart_totals;
681
                $response['data']['code']   = $discount_code;
682
            } else {
683
                $response['success']        = false;
684
            }
685
            
686
            // Allow for custom discount code handling
687
            $response = apply_filters( 'wpinv_ajax_discount_response', $response );
688
        }
689
        
690
        wp_send_json( $response );
691
    }
692
693
694
    /**
695
     * Lets users buy items via ajax.
696
     *
697
     * @since 1.0.0
698
     */
699
    public static function buy_items() {
700
        $user_id = get_current_user_id();
701
702
        if ( empty( $user_id ) ) { // If not logged in then lets redirect to the login page
703
            wp_send_json( array(
704
                '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

704
                'success' => wp_login_url( /** @scrutinizer ignore-type */ wp_get_referer() )
Loading history...
705
            ) );
706
        } else {
707
            // Only check nonce if logged in as it could be cached when logged out.
708
            if ( ! isset( $_POST['wpinv_buy_nonce'] ) || ! wp_verify_nonce( $_POST['wpinv_buy_nonce'], 'wpinv_buy_items' ) ) {
709
                wp_send_json( array(
710
                    'error' => __( 'Security checks failed.', 'invoicing' )
711
                ) );
712
                wp_die();
713
            }
714
715
            // allow to set a custom price through post_id
716
            $items = $_POST['items'];
717
            $related_post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : 0;
718
            $custom_item_price = $related_post_id ? abs( get_post_meta( $related_post_id, '_wpi_custom_price', true ) ) : 0;
719
720
            $cart_items = array();
721
            if ( $items ) {
722
                $items = explode( ',', $items );
723
724
                foreach( $items as $item ) {
725
                    $item_id = $item;
726
                    $quantity = 1;
727
728
                    if ( strpos( $item, '|' ) !== false ) {
729
                        $item_parts = explode( '|', $item );
730
                        $item_id = $item_parts[0];
731
                        $quantity = $item_parts[1];
732
                    }
733
734
                    if ( $item_id && $quantity ) {
735
                        $cart_items_arr = array(
736
                            'id'            => (int)$item_id,
737
                            'quantity'      => (int)$quantity
738
                        );
739
740
                        // If there is a related post id then add it to meta
741
                        if ( $related_post_id ) {
742
                            $cart_items_arr['meta'] = array(
743
                                'post_id'   => $related_post_id
744
                            );
745
                        }
746
747
                        // If there is a custom price then set it.
748
                        if ( $custom_item_price ) {
749
                            $cart_items_arr['custom_price'] = $custom_item_price;
750
                        }
751
752
                        $cart_items[] = $cart_items_arr;
753
                    }
754
                }
755
            }
756
757
            /**
758
             * Filter the wpinv_buy shortcode cart items on the fly.
759
             *
760
             * @param array $cart_items The cart items array.
761
             * @param int $related_post_id The related post id if any.
762
             * @since 1.0.0
763
             */
764
            $cart_items = apply_filters( 'wpinv_buy_cart_items', $cart_items, $related_post_id );
765
766
            // Make sure its not in the cart already, if it is then redirect to checkout.
767
            $cart_invoice = wpinv_get_invoice_cart();
768
769
            if ( isset( $cart_invoice->items ) && !empty( $cart_invoice->items ) && !empty( $cart_items ) && serialize( $cart_invoice->items ) == serialize( $cart_items ) ) {
770
                wp_send_json( array(
771
                    'success' =>  $cart_invoice->get_checkout_payment_url()
772
                ) );
773
                wp_die();
774
            }
775
776
            // Check if user has invoice with same items waiting to be paid.
777
            $user_invoices = wpinv_get_users_invoices( $user_id , 10 , false , 'wpi-pending' );
778
            if ( !empty( $user_invoices ) ) {
779
                foreach( $user_invoices as $user_invoice ) {
780
                    $user_cart_details = array();
781
                    $invoice  = wpinv_get_invoice( $user_invoice->ID );
782
                    $cart_details = $invoice->get_cart_details();
783
784
                    if ( !empty( $cart_details ) ) {
785
                        foreach ( $cart_details as $invoice_item ) {
786
                            $ii_arr = array();
787
                            $ii_arr['id'] = (int)$invoice_item['id'];
788
                            $ii_arr['quantity'] = (int)$invoice_item['quantity'];
789
790
                            if (isset( $invoice_item['meta'] ) && !empty( $invoice_item['meta'] ) ) {
791
                                $ii_arr['meta'] = $invoice_item['meta'];
792
                            }
793
794
                            if ( isset( $invoice_item['custom_price'] ) && !empty( $invoice_item['custom_price'] ) ) {
795
                                $ii_arr['custom_price'] = $invoice_item['custom_price'];
796
                            }
797
798
                            $user_cart_details[] = $ii_arr;
799
                        }
800
                    }
801
802
                    if ( !empty( $user_cart_details ) && serialize( $cart_items ) == serialize( $user_cart_details ) ) {
803
                        wp_send_json( array(
804
                            'success' =>  $invoice->get_checkout_payment_url()
805
                        ) );
806
                        wp_die();
807
                    }
808
                }
809
            }
810
811
            // Create invoice and send user to checkout
812
            if ( !empty( $cart_items ) ) {
813
                $invoice_data = array(
814
                    'status'        =>  'wpi-pending',
815
                    'created_via'   =>  'wpi',
816
                    'user_id'       =>  $user_id,
817
                    'cart_details'  =>  $cart_items,
818
                );
819
820
                $invoice = wpinv_insert_invoice( $invoice_data, true );
821
822
                if ( !empty( $invoice ) && isset( $invoice->ID ) ) {
823
                    wp_send_json( array(
824
                        '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

824
                        '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...
825
                    ) );
826
                } else {
827
                    wp_send_json( array(
828
                        'error' => __( 'Invoice failed to create', 'invoicing' )
829
                    ) );
830
                }
831
            } else {
832
                wp_send_json( array(
833
                    'error' => __( 'Items not valid.', 'invoicing' )
834
                ) );
835
            }
836
        }
837
838
        wp_die();
839
    }
840
}
841
842
WPInv_Ajax::init();