Passed
Push — master ( 55ce22...91e0d8 )
by Brian
04:53
created

WPInv_EUVat::get_country_by_ip()   D

Complexity

Conditions 25
Paths 1

Size

Total Lines 47
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 30
nc 1
nop 1
dl 0
loc 47
rs 4.1666
c 0
b 0
f 0

How to fix   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
// Exit if accessed directly.
3
if (!defined( 'ABSPATH' ) ) exit;
4
5
class WPInv_EUVat {
6
    private static $is_ajax = false;
7
    private static $default_country;
8
    private static $instance = false;
9
    
10
    public static function get_instance() {
11
        if ( !self::$instance ) {
12
            self::$instance = new self();
13
            self::$instance->actions();
14
        }
15
16
        return self::$instance;
17
    }
18
    
19
    public function __construct() {
20
        self::$is_ajax          = defined( 'DOING_AJAX' ) && DOING_AJAX;
21
        self::$default_country  = wpinv_get_default_country();
22
    }
23
    
24
    public static function actions() {
25
        if ( is_admin() ) {
26
            add_action( 'admin_enqueue_scripts', array( self::$instance, 'enqueue_admin_scripts' ) );
27
            add_action( 'wpinv_settings_sections_taxes', array( self::$instance, 'section_vat_settings' ) ); 
28
            add_action( 'wpinv_settings_taxes', array( self::$instance, 'vat_settings' ) );
29
            add_filter( 'wpinv_settings_taxes-vat_sanitize', array( self::$instance, 'sanitize_vat_settings' ) );
30
            add_filter( 'wpinv_settings_taxes-vat_rates_sanitize', array( self::$instance, 'sanitize_vat_rates' ) );
31
            add_action( 'wp_ajax_wpinv_add_vat_class', array( self::$instance, 'add_class' ) );
32
            add_action( 'wp_ajax_nopriv_wpinv_add_vat_class', array( self::$instance, 'add_class' ) );
33
            add_action( 'wp_ajax_wpinv_delete_vat_class', array( self::$instance, 'delete_class' ) );
34
            add_action( 'wp_ajax_nopriv_wpinv_delete_vat_class', array( self::$instance, 'delete_class' ) );
35
            add_action( 'wp_ajax_wpinv_update_vat_rates', array( self::$instance, 'update_eu_rates' ) );
36
            add_action( 'wp_ajax_nopriv_wpinv_update_vat_rates', array( self::$instance, 'update_eu_rates' ) );
37
            add_action( 'wp_ajax_wpinv_geoip2', array( self::$instance, 'geoip2_download_database' ) );
38
            add_action( 'wp_ajax_nopriv_wpinv_geoip2', array( self::$instance, 'geoip2_download_database' ) );
39
        }
40
        
41
        add_action( 'wp_enqueue_scripts', array( self::$instance, 'enqueue_vat_scripts' ) );
42
        add_filter( 'wpinv_default_billing_country', array( self::$instance, 'get_user_country' ), 10 );
43
        add_filter( 'wpinv_get_user_country', array( self::$instance, 'set_user_country' ), 10 );
44
        add_action( 'wp_ajax_wpinv_vat_validate', array( self::$instance, 'ajax_vat_validate' ) );
45
        add_action( 'wp_ajax_nopriv_wpinv_vat_validate', array( self::$instance, 'ajax_vat_validate' ) );
46
        add_action( 'wp_ajax_wpinv_vat_reset', array( self::$instance, 'ajax_vat_reset' ) );
47
        add_action( 'wp_ajax_nopriv_wpinv_vat_reset', array( self::$instance, 'ajax_vat_reset' ) );
48
        add_action( 'wpinv_invoice_print_after_line_items', array( self::$instance, 'show_vat_notice' ), 999, 1 );
49
        if ( wpinv_use_taxes() ) {
50
            add_action( 'wpinv_after_billing_fields', array( self::$instance, 'checkout_vat_fields' ) );
51
            if ( self::allow_vat_rules() ) {
52
                add_action( 'wpinv_checkout_error_checks', array( self::$instance, 'checkout_vat_validate' ), 10, 2 );
53
                add_filter( 'wpinv_tax_rate', array( self::$instance, 'get_rate' ), 10, 4 );
54
            }
55
        }
56
    }        
57
    
58
    public static function get_eu_states( $sort = true ) {
59
        $eu_states = array( 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GB', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE' );
60
        if ( $sort ) {
61
            $sort = sort( $eu_states );
62
        }
63
        
64
        return apply_filters( 'wpinv_get_eu_states', $eu_states, $sort );
65
    }
66
    
67
    public static function get_gst_countries( $sort = true ) {
68
        $gst_countries  = array( 'AU', 'NZ', 'CA', 'CN' );
69
        
70
        if ( $sort ) {
71
            $sort = sort( $gst_countries );
72
        }
73
        
74
        return apply_filters( 'wpinv_get_gst_countries', $gst_countries, $sort );
75
    }
76
    
77
    public static function is_eu_state( $country_code ) {
78
        $return = !empty( $country_code ) && in_array( strtoupper( $country_code ), self::get_eu_states() ) ? true : false;
79
                
80
        return apply_filters( 'wpinv_is_eu_state', $return, $country_code );
81
    }
82
    
83
    public static function is_gst_country( $country_code ) {
84
        $return = !empty( $country_code ) && in_array( strtoupper( $country_code ), self::get_gst_countries() ) ? true : false;
85
                
86
        return apply_filters( 'wpinv_is_gst_country', $return, $country_code );
87
    }
88
    
89
    public static function enqueue_vat_scripts() {
90
        if( wpinv_use_taxes() && wpinv_get_option( 'apply_vat_rules' ) ) {
91
            self::load_vat_scripts();
92
        }
93
    }
94
95
    public static function load_vat_scripts(){
96
        $suffix     = '';//defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
97
98
        wp_register_script( 'wpinv-vat-validation-script', WPINV_PLUGIN_URL . 'assets/js/jsvat' . $suffix . '.js', array( 'jquery' ),  WPINV_VERSION );
99
        wp_register_script( 'wpinv-vat-script', WPINV_PLUGIN_URL . 'assets/js/euvat' . $suffix . '.js', array( 'jquery' ),  WPINV_VERSION );
100
101
        $vat_name   = self::get_vat_name();
102
103
        $vars = array();
104
        $vars['UseTaxes'] = wpinv_use_taxes();
105
        $vars['EUStates'] = self::get_eu_states();
106
        $vars['NoRateSet'] = __( 'You have not set a rate. Do you want to continue?', 'invoicing' );
107
        $vars['EmptyCompany'] = __( 'Please enter your registered company name!', 'invoicing' );
108
        $vars['EmptyVAT'] = wp_sprintf( __( 'Please enter your %s number!', 'invoicing' ), $vat_name );
109
        $vars['TotalsRefreshed'] = wp_sprintf( __( 'The invoice totals will be refreshed to update the %s.', 'invoicing' ), $vat_name );
110
        $vars['ErrValidateVAT'] = wp_sprintf( __( 'Fail to validate the %s number!', 'invoicing' ), $vat_name );
111
        $vars['ErrResetVAT'] = wp_sprintf( __( 'Fail to reset the %s number!', 'invoicing' ), $vat_name );
112
        $vars['ErrInvalidVat'] = wp_sprintf( __( 'The %s number supplied does not have a valid format!', 'invoicing' ), $vat_name );
113
        $vars['ErrInvalidResponse'] = __( 'An invalid response has been received from the server!', 'invoicing' );
114
        $vars['ApplyVATRules'] = $vars['UseTaxes'] ? self::allow_vat_rules() : false;
115
        $vars['HideVatFields'] = $vars['ApplyVATRules'] ? self::hide_vat_fields() : true;
116
        $vars['ErrResponse'] = __( 'The request response is invalid!', 'invoicing' );
117
        $vars['ErrRateResponse'] = __( 'The get rate request response is invalid', 'invoicing' );
118
        $vars['PageRefresh'] = __( 'The page will be refreshed in 10 seconds to show the new options.', 'invoicing' );
119
        $vars['RequestResponseNotValidJSON'] = __( 'The get rate request response is not valid JSON', 'invoicing' );
120
        $vars['GetRateRequestFailed'] = __( 'The get rate request failed: ', 'invoicing' );
121
        $vars['NoRateInformationInResponse'] = __( 'The get rate request response does not contain any rate information', 'invoicing' );
122
        $vars['RatesUpdated'] = __( 'The rates have been updated. Press the save button to record these new rates.', 'invoicing' );
123
        $vars['IPAddressInformation'] = __( 'IP Address Information', 'invoicing' );
124
        $vars['VatValidating'] = wp_sprintf( __( 'Validating %s number...', 'invoicing' ), $vat_name );
125
        $vars['VatReseting'] = __( 'Reseting...', 'invoicing' );
126
        $vars['VatValidated'] = wp_sprintf( __( '%s number validated', 'invoicing' ), $vat_name );
127
        $vars['VatNotValidated'] = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
128
        $vars['ConfirmDeleteClass'] = __( 'Are you sure you wish to delete this rates class?', 'invoicing' );
129
        $vars['isFront'] = is_admin() ? false : true;
130
        $vars['baseCountry'] = wpinv_get_default_country();
131
        $vars['disableVATSameCountry'] = ( self::same_country_rule() == 'no' ? true : false );
132
        $vars['disableVATSimpleCheck'] = wpinv_get_option( 'vat_offline_check' ) ? true : false;
133
134
        wp_enqueue_script( 'wpinv-vat-validation-script' );
135
        wp_enqueue_script( 'wpinv-vat-script' );
136
        wp_localize_script( 'wpinv-vat-script', 'WPInv_VAT_Vars', $vars );
137
    }
138
139
    public static function enqueue_admin_scripts() {
140
        if( isset( $_GET['page'] ) && 'wpinv-settings' == $_GET['page'] ) {
141
            self::load_vat_scripts();
142
        }
143
    }
144
    
145
    public static function section_vat_settings( $sections ) {
146
        if ( !empty( $sections ) ) {
147
            $sections['vat'] = __( 'EU VAT Settings', 'invoicing' );
148
            
149
            if ( self::allow_vat_classes() ) {
150
                $sections['vat_rates'] = __( 'EU VAT Rates', 'invoicing' );
151
            }
152
        }
153
        return $sections;
154
    }
155
    
156
    public static function vat_rates_settings() {
157
        $vat_classes = self::get_rate_classes();
158
        $vat_rates = array();
159
        $vat_class = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '_new';
160
        $current_url = remove_query_arg( 'wpi_sub' );
161
        
162
        $vat_rates['vat_rates_header'] = array(
163
            'id' => 'vat_rates_header',
164
            'name' => '<h3>' . __( 'Manage VAT Rates', 'invoicing' ) . '</h3>',
165
            'desc' => '',
166
            'type' => 'header',
167
            'size' => 'regular'
168
        );
169
        $vat_rates['vat_rates_class'] = array(
170
            'id'          => 'vat_rates_class',
171
            'name'        => __( 'Edit VAT Rates', 'invoicing' ),
172
            'desc'        => __( 'The standard rate will apply where no explicit rate is provided.', 'invoicing' ),
173
            'type'        => 'select',
174
            'options'     => array_merge( $vat_classes, array( '_new' => __( 'Add New Rate Class', 'invoicing' ) ) ),
175
            'placeholder' => __( 'Select a VAT Rate', 'invoicing' ),
176
            'selected'    => $vat_class,
177
            'class'       => 'wpi_select2',
178
            'onchange'    => 'document.location.href="' . $current_url . '&wpi_sub=" + this.value;',
179
        );
180
        
181
        if ( $vat_class != '_standard' && $vat_class != '_new' ) {
182
            $vat_rates['vat_rate_delete'] = array(
183
                'id'   => 'vat_rate_delete',
184
                'type' => 'vat_rate_delete',
185
            );
186
        }
187
                    
188
        if ( $vat_class == '_new' ) {
189
            $vat_rates['vat_rates_settings'] = array(
190
                'id' => 'vat_rates_settings',
191
                'name' => '<h3>' . __( 'Add New Rate Class', 'invoicing' ) . '</h3>',
192
                'type' => 'header',
193
            );
194
            $vat_rates['vat_rate_name'] = array(
195
                'id'   => 'vat_rate_name',
196
                'name' => __( 'Name', 'invoicing' ),
197
                'desc' => __( 'A short name for the new VAT Rate class', 'invoicing' ),
198
                'type' => 'text',
199
                'size' => 'regular',
200
            );
201
            $vat_rates['vat_rate_desc'] = array(
202
                'id'   => 'vat_rate_desc',
203
                'name' => __( 'Description', 'invoicing' ),
204
                'desc' => __( 'Manage VAT Rate class', 'invoicing' ),
205
                'type' => 'text',
206
                'size' => 'regular',
207
            );
208
            $vat_rates['vat_rate_add'] = array(
209
                'id'   => 'vat_rate_add',
210
                'type' => 'vat_rate_add',
211
            );
212
        } else {
213
            $vat_rates['vat_rates'] = array(
214
                'id'   => 'vat_rates',
215
                'name' => '<h3>' . $vat_classes[$vat_class] . '</h3>',
216
                'desc' => self::get_class_desc( $vat_class ),
217
                'type' => 'vat_rates',
218
            );
219
        }
220
        
221
        return $vat_rates;
222
    }
223
    
224
    public static function vat_settings( $settings ) {
225
        if ( !empty( $settings ) ) {    
226
            $vat_settings = array();
227
            $vat_settings['vat_company_title'] = array(
228
                'id' => 'vat_company_title',
229
                'name' => '<h3>' . __( 'Your Company Details', 'invoicing' ) . '</h3>',
230
                'desc' => '',
231
                'type' => 'header',
232
                'size' => 'regular'
233
            );
234
            
235
            $vat_settings['vat_company_name'] = array(
236
                'id' => 'vat_company_name',
237
                'name' => __( 'Your Company Name', 'invoicing' ),
238
                'desc' => wp_sprintf(__( 'Your company name as it appears on your VAT return, you can verify it via your VAT ID on the %sEU VIES System.%s', 'invoicing' ), '<a href="http://ec.europa.eu/taxation_customs/vies/" target="_blank">', '</a>' ),
239
                'type' => 'text',
240
                'size' => 'regular',
241
            );
242
            
243
            $vat_settings['vat_number'] = array(
244
                'id'   => 'vat_number',
245
                'name' => __( 'Your VAT Number', 'invoicing' ),
246
                'type' => 'vat_number',
247
                'size' => 'regular',
248
            );
249
250
            $vat_settings['vat_settings_title'] = array(
251
                'id' => 'vat_settings_title',
252
                'name' => '<h3>' . __( 'Apply VAT Settings', 'invoicing' ) . '</h3>',
253
                'desc' => '',
254
                'type' => 'header',
255
                'size' => 'regular'
256
            );
257
258
            $vat_settings['apply_vat_rules'] = array(
259
                'id' => 'apply_vat_rules',
260
                'name' => __( 'Enable VAT Rules', 'invoicing' ),
261
                'desc' => __( 'Apply VAT to consumer sales from IP addresses within the EU, even if the billing address is outside the EU.', 'invoicing' ) . '<br><font style="color:red">' . __( 'Do not disable unless you know what you are doing.', 'invoicing' ) . '</font>',
262
                'type' => 'checkbox',
263
                'std' => '1'
264
            );
265
266
            /*
267
            $vat_settings['vat_allow_classes'] = array(
268
                'id' => 'vat_allow_classes',
269
                'name' => __( 'Allow the use of VAT rate classes', 'invoicing' ),
270
                'desc' =>  __( 'When enabled this option makes it possible to define alternative rate classes so rates for items that do not use the standard VAT rate in all member states can be defined.<br>A menu option will appear under the "Invoicing -> Settings -> Taxes -> EU VAT Rates" menu heading that will take you to a page on which new classes can be defined and rates entered. A meta-box will appear in the invoice page in which you are able to select one of the alternative classes you create so the rates associated with the class will be applied to invoice.<br>By default the standard rates class will be used just as they are when this option is not enabled.', 'invoicing' ),
271
                'type' => 'checkbox'
272
            );
273
            */
274
275
            $vat_settings['vat_prevent_b2c_purchase'] = array(
276
                'id' => 'vat_prevent_b2c_purchase',
277
                'name' => __( 'Prevent EU B2C Sales', 'invoicing' ),
278
                'desc' => __( 'Enable this option if you are not registered for VAT in the EU.', 'invoicing' ),
279
                'type' => 'checkbox'
280
            );
281
282
283
284
            $vat_settings['vat_same_country_rule'] = array(
285
                'id'          => 'vat_same_country_rule',
286
                'name'        => __( 'Same Country Rule', 'invoicing' ),
287
                'desc'        => __( 'Select how you want to handle VAT charge if sales are in the same country as the base country.', 'invoicing' ),
288
                'type'        => 'select',
289
                'options'     => array(
290
                    ''          => __( 'Normal', 'invoicing' ),
291
                    'no'        => __( 'No VAT', 'invoicing' ),
292
                    'always'    => __( 'Always apply VAT', 'invoicing' ),
293
                ),
294
                'placeholder' => __( 'Select an option', 'invoicing' ),
295
                'std'         => '',
296
                'class'   => 'wpi_select2',
297
            );
298
299
            $vat_settings['vat_checkout_title'] = array(
300
                'id' => 'vat_checkout_title',
301
                'name' => '<h3>' . __( 'Checkout Fields', 'invoicing' ) . '</h3>',
302
                'desc' => '',
303
                'type' => 'header',
304
                'size' => 'regular'
305
            );
306
307
            $vat_settings['vat_disable_fields'] = array(
308
                'id' => 'vat_disable_fields',
309
                'name' => __( 'Disable VAT Fields', 'invoicing' ),
310
                'desc' => __( 'Disable VAT fields if Invoicing is being used for GST.', 'invoicing' ) . '<br><font style="color:red">' . __( 'Do not disable if you have enabled Prevent EU B2C sales, otherwise Prevent EU B2C sales setting will not work.', 'invoicing' ) . '</font>',
311
                'type' => 'checkbox'
312
            );
313
314
            $vat_settings['vat_ip_lookup'] = array(
315
                'id'   => 'vat_ip_lookup',
316
                'name' => __( 'IP Country Look-up', 'invoicing' ),
317
                'type' => 'vat_ip_lookup',
318
                'size' => 'regular',
319
                'std' => 'default',
320
                'class'   => 'wpi_select2',
321
            );
322
323
            $vat_settings['hide_ip_address'] = array(
324
                'id' => 'hide_ip_address',
325
                'name' => __( 'Hide IP Info at Checkout', 'invoicing' ),
326
                'desc' => __( 'Hide the user IP info at checkout.', 'invoicing' ),
327
                'type' => 'checkbox'
328
            );
329
330
            $vat_settings['vat_ip_country_default'] = array(
331
                'id' => 'vat_ip_country_default',
332
                'name' => __( 'Enable IP Country as Default', 'invoicing' ),
333
                'desc' => __( 'Show the country of the users IP as the default country, otherwise the site default country will be used.', 'invoicing' ),
334
                'type' => 'checkbox'
335
            );
336
337
            $vat_settings['vies_validation_title'] = array(
338
                'id' => 'vies_validation_title',
339
                'name' => '<h3>' . __( 'VIES Validation', 'invoicing' ) . '</h3>',
340
                'desc' => '',
341
                'type' => 'header',
342
                'size' => 'regular'
343
            );
344
345
            $vat_settings['vat_vies_check'] = array(
346
                'id' => 'vat_vies_check',
347
                'name' => __( 'Disable VIES VAT ID Check', 'invoicing' ),
348
                'desc' => wp_sprintf( __( 'Prevent VAT numbers from being validated by the %sEU VIES System.%s', 'invoicing' ), '<a href="http://ec.europa.eu/taxation_customs/vies/" target="_blank">', '</a>' ),
349
                'type' => 'checkbox'
350
            );
351
352
            $vat_settings['vat_disable_company_name_check'] = array(
353
                'id' => 'vat_disable_company_name_check',
354
                'name' => __( 'Disable VIES Name Check', 'invoicing' ),
355
                'desc' => wp_sprintf( __( 'Prevent company name from being validated by the %sEU VIES System.%s', 'invoicing' ), '<a href="http://ec.europa.eu/taxation_customs/vies/" target="_blank">', '</a>' ),
356
                'type' => 'checkbox'
357
            );
358
359
            $vat_settings['vat_offline_check'] = array(
360
                'id' => 'vat_offline_check',
361
                'name' => __( 'Disable Basic Checks', 'invoicing' ),
362
                'desc' => __( 'Disable basic JS checks for correct format of VAT number. (Not Recommended)', 'invoicing' ),
363
                'type' => 'checkbox'
364
            );
365
            
366
367
            $settings['vat'] = $vat_settings;
368
            
369
            if ( self::allow_vat_classes() ) {
370
                $settings['vat_rates'] = self::vat_rates_settings();
371
            }
372
            
373
            $eu_fallback_rate = array(
374
                'id'   => 'eu_fallback_rate',
375
                'name' => '<h3>' . __( 'VAT rate for EU member states', 'invoicing' ) . '</h3>',
376
                'type' => 'eu_fallback_rate',
377
                'desc' => __( 'Enter the VAT rate to be charged for EU member states. You can edit the rates for each member state when a country rate has been set up by pressing this button.', 'invoicing' ),
378
                'std'  => '20',
379
                'size' => 'small'
380
            );
381
            $settings['rates']['eu_fallback_rate'] = $eu_fallback_rate;
382
        }
383
384
        return $settings;
385
    }
386
    // IP Geolocation
387
    public static function geoip2_download_database() {
388
        $upload_dir         = wp_upload_dir();
389
        
390
        $database_url       = 'http' . (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === 'on' ? 's' : '') . '://geolite.maxmind.com/download/geoip/database/';
391
        $destination_dir    = $upload_dir['basedir'] . '/invoicing';
392
        
393
        if ( !is_dir( $destination_dir ) ) { 
394
            mkdir( $destination_dir );
395
        }
396
        
397
        $database_files     = array(
398
            'country'   => array(
399
                'source'        => $database_url . 'GeoLite2-Country.mmdb.gz',
400
                'destination'   => $destination_dir . '/GeoLite2-Country.mmdb',
401
            ),
402
            'city'      => array(
403
                'source'        => $database_url . 'GeoLite2-City.mmdb.gz',
404
                'destination'   => $destination_dir . '/GeoLite2-City.mmdb',
405
            )
406
        );
407
408
        foreach( $database_files as $database => $files ) {
409
            $result = self::geoip2_download_file( $files['source'], $files['destination'] );
410
            
411
            if ( empty( $result['success'] ) ) {
412
                echo $result['message'];
413
                exit;
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...
414
            }
415
            
416
            wpinv_update_option( 'wpinv_geoip2_date_updated', current_time( 'timestamp' ) );
417
            echo sprintf(__( 'GeoIP2 %s database updated successfully.', 'invoicing' ), $database ) . ' ';
418
        }
419
        
420
        exit;
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...
421
    }
422
    
423
    public static function geoip2_download_file( $source_url, $destination_file ) {
424
        $success    = false;
425
        $message    = '';
426
        
427
        if ( !function_exists( 'download_url' ) ) {
428
            require_once( ABSPATH . 'wp-admin/includes/file.php' );
429
        }
430
431
        $temp_file  = download_url( $source_url );
432
        
433
        if ( is_wp_error( $temp_file ) ) {
434
            $message = sprintf( __( 'Error while downloading GeoIp2 database( %s ): %s', 'invoicing' ), $source_url, $temp_file->get_error_message() );
435
        } else {
436
            $handle = gzopen( $temp_file, 'rb' );
0 ignored issues
show
Bug introduced by
$temp_file of type WP_Error is incompatible with the type string expected by parameter $filename of gzopen(). ( Ignorable by Annotation )

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

436
            $handle = gzopen( /** @scrutinizer ignore-type */ $temp_file, 'rb' );
Loading history...
437
            
438
            if ( $handle ) {
0 ignored issues
show
introduced by
$handle is of type resource, thus it always evaluated to false.
Loading history...
439
                $fopen  = fopen( $destination_file, 'wb' );
440
                if ( $fopen ) {
441
                    while ( ( $data = gzread( $handle, 4096 ) ) != false ) {
442
                        fwrite( $fopen, $data );
443
                    }
444
445
                    gzclose( $handle );
446
                    fclose( $fopen );
447
                        
448
                    $success = true;
449
                } else {
450
                    gzclose( $handle );
451
                    $message = sprintf( __( 'Error could not open destination GeoIp2 database file for writing: %s', 'invoicing' ), $destination_file );
452
                }
453
            } else {
454
                $message = sprintf( __( 'Error could not open GeoIp2 database file for reading: %s', 'invoicing' ), $temp_file );
0 ignored issues
show
Bug introduced by
$temp_file of type WP_Error is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

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

454
                $message = sprintf( __( 'Error could not open GeoIp2 database file for reading: %s', 'invoicing' ), /** @scrutinizer ignore-type */ $temp_file );
Loading history...
455
            }
456
            
457
            if ( file_exists( $temp_file ) ) {
0 ignored issues
show
Bug introduced by
$temp_file of type WP_Error is incompatible with the type string expected by parameter $filename of file_exists(). ( Ignorable by Annotation )

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

457
            if ( file_exists( /** @scrutinizer ignore-type */ $temp_file ) ) {
Loading history...
458
                unlink( $temp_file );
0 ignored issues
show
Bug introduced by
$temp_file of type WP_Error is incompatible with the type string expected by parameter $filename of unlink(). ( Ignorable by Annotation )

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

458
                unlink( /** @scrutinizer ignore-type */ $temp_file );
Loading history...
459
            }
460
        }
461
        
462
        $return             = array();
463
        $return['success']  = $success;
464
        $return['message']  = $message;
465
466
        return $return;
467
    }
468
    
469
    public static function load_geoip2() {
470
        if ( defined( 'WPINV_GEOIP2_LODDED' ) ) {
471
            return;
472
        }
473
        
474
        if ( !class_exists( '\MaxMind\Db\Reader' ) ) {
475
            $maxmind_db_files = array(
476
                'Reader/Decoder.php',
477
                'Reader/InvalidDatabaseException.php',
478
                'Reader/Metadata.php',
479
                'Reader/Util.php',
480
                'Reader.php',
481
            );
482
            
483
            foreach ( $maxmind_db_files as $key => $file ) {
484
                require_once( WPINV_PLUGIN_DIR . 'includes/libraries/MaxMind/Db/' . $file );
485
            }
486
        }
487
        
488
        if ( !class_exists( '\GeoIp2\Database\Reader' ) ) {        
489
            $geoip2_files = array(
490
                'ProviderInterface.php',
491
                'Compat/JsonSerializable.php',
492
                'Database/Reader.php',
493
                'Exception/GeoIp2Exception.php',
494
                'Exception/AddressNotFoundException.php',
495
                'Exception/AuthenticationException.php',
496
                'Exception/HttpException.php',
497
                'Exception/InvalidRequestException.php',
498
                'Exception/OutOfQueriesException.php',
499
                'Model/AbstractModel.php',
500
                'Model/AnonymousIp.php',
501
                'Model/Country.php',
502
                'Model/City.php',
503
                'Model/ConnectionType.php',
504
                'Model/Domain.php',
505
                'Model/Enterprise.php',
506
                'Model/Insights.php',
507
                'Model/Isp.php',
508
                'Record/AbstractRecord.php',
509
                'Record/AbstractPlaceRecord.php',
510
                'Record/Country.php',
511
                'Record/City.php',
512
                'Record/Continent.php',
513
                'Record/Location.php',
514
                'Record/MaxMind.php',
515
                'Record/Postal.php',
516
                'Record/RepresentedCountry.php',
517
                'Record/Subdivision.php',
518
                'Record/Traits.php',
519
                'WebService/Client.php',
520
            );
521
            
522
            foreach ( $geoip2_files as $key => $file ) {
523
                require_once( WPINV_PLUGIN_DIR . 'includes/libraries/GeoIp2/' . $file );
524
            }
525
        }
526
527
        define( 'WPINV_GEOIP2_LODDED', true );
528
    }
529
530
    public static function geoip2_country_dbfile() {
531
        $upload_dir = wp_upload_dir();
532
533
        if ( !isset( $upload_dir['basedir'] ) ) {
534
            return false;
535
        }
536
537
        $filename = $upload_dir['basedir'] . '/invoicing/GeoLite2-Country.mmdb';
538
        if ( !file_exists( $filename ) ) {
539
            return false;
540
        }
541
        
542
        return $filename;
543
    }
544
545
    public static function geoip2_city_dbfile() {
546
        $upload_dir = wp_upload_dir();
547
548
        if ( !isset( $upload_dir['basedir'] ) ) {
549
            return false;
550
        }
551
552
        $filename = $upload_dir['basedir'] . '/invoicing/GeoLite2-City.mmdb';
553
        if ( !file_exists( $filename ) ) {
554
            return false;
555
        }
556
        
557
        return $filename;
558
    }
559
560
    public static function geoip2_country_reader() {
561
        try {
562
            self::load_geoip2();
563
564
            if ( $filename = self::geoip2_country_dbfile() ) {
565
                return new \GeoIp2\Database\Reader( $filename );
566
            }
567
        } catch( Exception $e ) {
568
            return false;
569
        }
570
        
571
        return false;
572
    }
573
574
    public static function geoip2_city_reader() {
575
        try {
576
            self::load_geoip2();
577
578
            if ( $filename = self::geoip2_city_dbfile() ) {
579
                return new \GeoIp2\Database\Reader( $filename );
580
            }
581
        } catch( Exception $e ) {
582
            return false;
583
        }
584
        
585
        return false;
586
    }
587
588
    public static function geoip2_country_record( $ip_address ) {
589
        try {
590
            $reader = self::geoip2_country_reader();
591
592
            if ( $reader ) {
593
                $record = $reader->country( $ip_address );
594
                
595
                if ( !empty( $record->country->isoCode ) ) {
596
                    return $record;
597
                }
598
            }
599
        } catch(\InvalidArgumentException $e) {
600
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
601
            
602
            return false;
603
        } catch(\GeoIp2\Exception\AddressNotFoundException $e) {
604
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
605
            
606
            return false;
607
        } catch( Exception $e ) {
608
            return false;
609
        }
610
        
611
        return false;
612
    }
613
614
    public static function geoip2_city_record( $ip_address ) {
615
        try {
616
            $reader = self::geoip2_city_reader();
617
618
            if ( $reader ) {
619
                $record = $reader->city( $ip_address );
620
                
621
                if ( !empty( $record->country->isoCode ) ) {
622
                    return $record;
623
                }
624
            }
625
        } catch(\InvalidArgumentException $e) {
626
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
627
            
628
            return false;
629
        } catch(\GeoIp2\Exception\AddressNotFoundException $e) {
630
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
631
            
632
            return false;
633
        } catch( Exception $e ) {
634
            return false;
635
        }
636
        
637
        return false;
638
    }
639
640
    public static function geoip2_country_code( $ip_address ) {
641
        $record = self::geoip2_country_record( $ip_address );
642
        return !empty( $record->country->isoCode ) ? $record->country->isoCode : wpinv_get_default_country();
643
    }
644
645
    // Find country by IP address.
646
    public static function get_country_by_ip( $ip = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $ip is not used and could be removed. ( Ignorable by Annotation )

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

646
    public static function get_country_by_ip( /** @scrutinizer ignore-unused */ $ip = '' ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
647
        global $wpinv_ip_address_country;
648
        return '';
649
        if ( !empty( $wpinv_ip_address_country ) ) {
0 ignored issues
show
Unused Code introduced by
IfNode is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
650
            return $wpinv_ip_address_country;
651
        }
652
        
653
        if ( empty( $ip ) ) {
654
            $ip = wpinv_get_ip();
655
        }
656
657
        $ip_country_service = wpinv_get_option( 'vat_ip_lookup' );
658
        $is_default         = empty( $ip_country_service ) || $ip_country_service === 'default' ? true : false;
659
660
        if ( !empty( $ip ) && $ip !== '127.0.0.1' ) { // For 127.0.0.1(localhost) use default country.
661
            if ( function_exists( 'geoip_country_code_by_name') && ( $ip_country_service === 'geoip' || $is_default ) ) {
662
                try {
663
                    $wpinv_ip_address_country = geoip_country_code_by_name( $ip );
664
                } catch( Exception $e ) {
665
                    wpinv_error_log( $e->getMessage(), 'GeoIP Lookup( ' . $ip . ' )' );
666
                }
667
            } else if ( self::geoip2_country_dbfile() && ( $ip_country_service === 'geoip2' || $is_default ) ) {
668
                $wpinv_ip_address_country = self::geoip2_country_code( $ip );
669
            } else if ( function_exists( 'simplexml_load_file' ) && ini_get('allow_url_fopen') && ( $ip_country_service === 'geoplugin' || $is_default ) ) {
670
                $load_xml = simplexml_load_file( 'http://www.geoplugin.net/xml.gp?ip=' . $ip );
671
                
672
                if ( !empty( $load_xml ) && !empty( $load_xml->geoplugin_countryCode ) ) {
673
                    $wpinv_ip_address_country = (string)$load_xml->geoplugin_countryCode;
674
                }
675
            }elseif(!empty( $ip )){
676
                $url = 'http://ip-api.com/json/' . $ip;
677
                $response = wp_remote_get($url);
678
679
                if ( is_array( $response ) && wp_remote_retrieve_response_code( $response ) == '200' ) {
680
                    $data = json_decode(wp_remote_retrieve_body( $response ),true);
681
                    if(!empty($data['countryCode'])){
682
                        $wpinv_ip_address_country = (string)$data['countryCode'];
683
                    }
684
                }
685
            }
686
        }
687
688
        if ( empty( $wpinv_ip_address_country ) ) {
689
            $wpinv_ip_address_country = wpinv_get_default_country();
690
        }
691
692
        return $wpinv_ip_address_country;
693
    }
694
    
695
    public static function sanitize_vat_settings( $input ) {
696
        global $wpinv_options;
697
        
698
        $valid      = false;
699
        $message    = '';
700
        
701
        if ( !empty( $wpinv_options['vat_vies_check'] ) ) {
702
            if ( empty( $wpinv_options['vat_offline_check'] ) ) {
703
                $valid = self::offline_check( $input['vat_number'] );
704
            } else {
705
                $valid = true;
706
            }
707
            
708
            $message = $valid ? '' : __( 'VAT number not validated', 'invoicing' );
709
        } else {
710
            $result = self::check_vat( $input['vat_number'] );
711
            
712
            if ( empty( $result['valid'] ) ) {
713
                $valid      = false;
714
                $message    = $result['message'];
715
            } else {
716
                $valid      = ( isset( $result['company'] ) && ( $result['company'] == '---' || ( strcasecmp( trim( $result['company'] ), trim( $input['vat_company_name'] ) ) == 0 ) ) ) || !empty( $wpinv_options['vat_disable_company_name_check'] );
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $valid = (IssetNode && $..._company_name_check'])), Probably Intended Meaning: $valid = IssetNode && ($..._company_name_check']))
Loading history...
717
                $message    = $valid ? '' : __( 'The company name associated with the VAT number provided is not the same as the company name provided.', 'invoicing' );
718
            }
719
        }
720
721
        if ( $message && self::is_vat_validated() != $valid ) {
722
            add_settings_error( 'wpinv-notices', '', $message, ( $valid ? 'updated' : 'error' ) );
723
        }
724
725
        $input['vat_valid'] = $valid;
726
        return $input;
727
    }
728
    
729
    public static function sanitize_vat_rates( $input ) {
730
        if( !wpinv_current_user_can_manage_invoicing() ) {
731
            add_settings_error( 'wpinv-notices', '', __( 'Your account does not have permission to add rate classes.', 'invoicing' ), 'error' );
732
            return $input;
733
        }
734
        
735
        $vat_classes = self::get_rate_classes();
736
        $vat_class = !empty( $_REQUEST['wpi_vat_class'] ) && isset( $vat_classes[$_REQUEST['wpi_vat_class']] )? sanitize_text_field( $_REQUEST['wpi_vat_class'] ) : '';
737
        
738
        if ( empty( $vat_class ) ) {
739
            add_settings_error( 'wpinv-notices', '', __( 'No valid VAT rates class contained in the request to save rates.', 'invoicing' ), 'error' );
740
            
741
            return $input;
742
        }
743
744
        $new_rates = ! empty( $_POST['vat_rates'] ) ? array_values( $_POST['vat_rates'] ) : array();
745
746
        if ( $vat_class === '_standard' ) {
747
            // Save the active rates in the invoice settings
748
            update_option( 'wpinv_tax_rates', $new_rates );
749
        } else {
750
            // Get the existing set of rates
751
            $rates = self::get_non_standard_rates();
752
            $rates[$vat_class] = $new_rates;
753
754
            update_option( 'wpinv_vat_rates', $rates );
755
        }
756
        
757
        return $input;
758
    }
759
    
760
    public static function add_class() {        
761
        $response = array();
762
        $response['success'] = false;
763
        
764
        if ( !wpinv_current_user_can_manage_invoicing() ) {
765
            $response['error'] = __( 'Invalid access!', 'invoicing' );
766
            wp_send_json( $response );
767
        }
768
        
769
        $vat_class_name = !empty( $_POST['name'] ) ? sanitize_text_field( $_POST['name'] ) : false;
770
        $vat_class_desc = !empty( $_POST['desc'] ) ? sanitize_text_field( $_POST['desc'] ) : false;
771
        
772
        if ( empty( $vat_class_name ) ) {
773
            $response['error'] = __( 'Select the VAT rate name', 'invoicing' );
774
            wp_send_json( $response );
775
        }
776
        
777
        $vat_classes = (array)self::get_rate_classes();
778
779
        if ( !empty( $vat_classes ) && in_array( strtolower( $vat_class_name ), array_map( 'strtolower', array_values( $vat_classes ) ) ) ) {
780
            $response['error'] = wp_sprintf( __( 'A VAT Rate name "%s" already exists', 'invoicing' ), $vat_class_name );
781
            wp_send_json( $response );
782
        }
783
        
784
        $rate_class_key = normalize_whitespace( 'wpi-' . $vat_class_name );
785
        $rate_class_key = sanitize_key( str_replace( " ", "-", $rate_class_key ) );
786
        
787
        $vat_classes = (array)self::get_rate_classes( true );
788
        $vat_classes[$rate_class_key] = array( 'name' => $vat_class_name, 'desc' => $vat_class_desc );
789
        
790
        update_option( '_wpinv_vat_rate_classes', $vat_classes );
791
        
792
        $response['success'] = true;
793
        $response['redirect'] = admin_url( 'admin.php?page=wpinv-settings&tab=taxes&section=vat_rates&wpi_sub=' . $rate_class_key );
794
        
795
        wp_send_json( $response );
796
    }
797
    
798
    public static function delete_class() {
799
        $response = array();
800
        $response['success'] = false;
801
        
802
        if ( !wpinv_current_user_can_manage_invoicing() || !isset( $_POST['class'] ) ) {
803
            $response['error'] = __( 'Invalid access!', 'invoicing' );
804
            wp_send_json( $response );
805
        }
806
        
807
        $vat_class = isset( $_POST['class'] ) && $_POST['class'] !== '' ? sanitize_text_field( $_POST['class'] ) : false;
808
        $vat_classes = (array)self::get_rate_classes();
809
810
        if ( !isset( $vat_classes[$vat_class] ) ) {
811
            $response['error'] = __( 'Requested class does not exists', 'invoicing' );
812
            wp_send_json( $response );
813
        }
814
        
815
        if ( $vat_class == '_new' || $vat_class == '_standard' ) {
816
            $response['error'] = __( 'You can not delete standard rates class', 'invoicing' );
817
            wp_send_json( $response );
818
        }
819
            
820
        $vat_classes = (array)self::get_rate_classes( true );
821
        unset( $vat_classes[$vat_class] );
822
        
823
        update_option( '_wpinv_vat_rate_classes', $vat_classes );
824
        
825
        $response['success'] = true;
826
        $response['redirect'] = admin_url( 'admin.php?page=wpinv-settings&tab=taxes&section=vat_rates&wpi_sub=_new' );
827
        
828
        wp_send_json( $response );
829
    }
830
    
831
    public static function update_eu_rates() {        
832
        $response               = array();
833
        $response['success']    = false;
834
        $response['error']      = null;
835
        $response['data']       = null;
836
        
837
        if ( !wpinv_current_user_can_manage_invoicing() ) {
838
            $response['error'] = __( 'Invalid access!', 'invoicing' );
839
            wp_send_json( $response );
840
        }
841
        
842
        $group      = !empty( $_POST['group'] ) ? sanitize_text_field( $_POST['group'] ) : '';
843
        $euvatrates = self::request_euvatrates( $group );
844
        
845
        if ( !empty( $euvatrates ) ) {
846
            if ( !empty( $euvatrates['success'] ) && !empty( $euvatrates['rates'] ) ) {
847
                $response['success']        = true;
848
                $response['data']['rates']  = $euvatrates['rates'];
849
            } else if ( !empty( $euvatrates['error'] ) ) {
850
                $response['error']          = $euvatrates['error'];
851
            }
852
        }
853
            
854
        wp_send_json( $response );
855
    }
856
    
857
    public static function hide_vat_fields() {
858
        $hide = wpinv_get_option( 'vat_disable_fields' );
859
        
860
        return apply_filters( 'wpinv_hide_vat_fields', $hide );
861
    }
862
    
863
    public static function same_country_rule() {
864
        $same_country_rule = wpinv_get_option( 'vat_same_country_rule' );
865
        
866
        return apply_filters( 'wpinv_vat_same_country_rule', $same_country_rule );
867
    }
868
    
869
    public static function get_vat_name() {
870
        $vat_name   = wpinv_get_option( 'vat_name' );
871
        $vat_name   = !empty( $vat_name ) ? $vat_name : 'VAT';
872
        
873
        return apply_filters( 'wpinv_get_owner_vat_name', $vat_name );
874
    }
875
    
876
    public static function get_company_name() {
877
        $company_name = wpinv_get_option( 'vat_company_name' );
878
        
879
        return apply_filters( 'wpinv_get_owner_company_name', $company_name );
880
    }
881
    
882
    public static function get_vat_number() {
883
        $vat_number = wpinv_get_option( 'vat_number' );
884
        
885
        return apply_filters( 'wpinv_get_owner_vat_number', $vat_number );
886
    }
887
    
888
    public static function is_vat_validated() {
889
        $validated = self::get_vat_number() && wpinv_get_option( 'vat_valid' );
890
        
891
        return apply_filters( 'wpinv_is_owner_vat_validated', $validated );
892
    }
893
    
894
    public static function sanitize_vat( $vat_number, $country_code = '' ) {        
895
        $vat_number = str_replace( array(' ', '.', '-', '_', ',' ), '', strtoupper( trim( $vat_number ) ) );
896
        
897
        if ( empty( $country_code ) ) {
898
            $country_code = substr( $vat_number, 0, 2 );
899
        }
900
        
901
        if ( strpos( $vat_number , $country_code ) === 0 ) {
902
            $vat = str_replace( $country_code, '', $vat_number );
903
        } else {
904
            $vat = $country_code . $vat_number;
905
        }
906
        
907
        $return                 = array();
908
        $return['vat']          = $vat;
909
        $return['iso']          = $country_code;
910
        $return['vat_number']   = $country_code . $vat;
911
        
912
        return $return;
913
    }
914
    
915
    public static function offline_check( $vat_number, $country_code = '', $formatted = false ) {
916
        $vat            = self::sanitize_vat( $vat_number, $country_code );
917
        $vat_number     = $vat['vat_number'];
918
        $country_code   = $vat['iso'];
919
        $regex          = array();
920
        
921
        switch ( $country_code ) {
922
            case 'AT':
923
                $regex[] = '/^(AT)U(\d{8})$/';                           // Austria
924
                break;
925
            case 'BE':
926
                $regex[] = '/^(BE)(0?\d{9})$/';                          // Belgium
927
                break;
928
            case 'BG':
929
                $regex[] = '/^(BG)(\d{9,10})$/';                         // Bulgaria
930
                break;
931
            case 'CH':
932
            case 'CHE':
933
                $regex[] = '/^(CHE)(\d{9})MWST$/';                       // Switzerland (Not EU)
934
                break;
935
            case 'CY':
936
                $regex[] = '/^(CY)([0-5|9]\d{7}[A-Z])$/';                // Cyprus
937
                break;
938
            case 'CZ':
939
                $regex[] = '/^(CZ)(\d{8,13})$/';                         // Czech Republic
940
                break;
941
            case 'DE':
942
                $regex[] = '/^(DE)([1-9]\d{8})$/';                       // Germany
943
                break;
944
            case 'DK':
945
                $regex[] = '/^(DK)(\d{8})$/';                            // Denmark
946
                break;
947
            case 'EE':
948
                $regex[] = '/^(EE)(10\d{7})$/';                          // Estonia
949
                break;
950
            case 'EL':
951
                $regex[] = '/^(EL)(\d{9})$/';                            // Greece
952
                break;
953
            case 'ES':
954
                $regex[] = '/^(ES)([A-Z]\d{8})$/';                       // Spain (National juridical entities)
955
                $regex[] = '/^(ES)([A-H|N-S|W]\d{7}[A-J])$/';            // Spain (Other juridical entities)
956
                $regex[] = '/^(ES)([0-9|Y|Z]\d{7}[A-Z])$/';              // Spain (Personal entities type 1)
957
                $regex[] = '/^(ES)([K|L|M|X]\d{7}[A-Z])$/';              // Spain (Personal entities type 2)
958
                break;
959
            case 'EU':
960
                $regex[] = '/^(EU)(\d{9})$/';                            // EU-type
961
                break;
962
            case 'FI':
963
                $regex[] = '/^(FI)(\d{8})$/';                            // Finland
964
                break;
965
            case 'FR':
966
                $regex[] = '/^(FR)(\d{11})$/';                           // France (1)
967
                $regex[] = '/^(FR)[(A-H)|(J-N)|(P-Z)](\d{10})$/';        // France (2)
968
                $regex[] = '/^(FR)\d[(A-H)|(J-N)|(P-Z)](\d{9})$/';       // France (3)
969
                $regex[] = '/^(FR)[(A-H)|(J-N)|(P-Z)]{2}(\d{9})$/';      // France (4)
970
                break;
971
            case 'GB':
972
                $regex[] = '/^(GB)?(\d{9})$/';                           // UK (Standard)
973
                $regex[] = '/^(GB)?(\d{12})$/';                          // UK (Branches)
974
                $regex[] = '/^(GB)?(GD\d{3})$/';                         // UK (Government)
975
                $regex[] = '/^(GB)?(HA\d{3})$/';                         // UK (Health authority)
976
                break;
977
            case 'GR':
978
                $regex[] = '/^(GR)(\d{8,9})$/';                          // Greece
979
                break;
980
            case 'HR':
981
                $regex[] = '/^(HR)(\d{11})$/';                           // Croatia
982
                break;
983
            case 'HU':
984
                $regex[] = '/^(HU)(\d{8})$/';                            // Hungary
985
                break;
986
            case 'IE':
987
                $regex[] = '/^(IE)(\d{7}[A-W])$/';                       // Ireland (1)
988
                $regex[] = '/^(IE)([7-9][A-Z\*\+)]\d{5}[A-W])$/';        // Ireland (2)
989
                $regex[] = '/^(IE)(\d{7}[A-Z][AH])$/';                   // Ireland (3) (new format from 1 Jan 2013)
990
                break;
991
            case 'IT':
992
                $regex[] = '/^(IT)(\d{11})$/';                           // Italy
993
                break;
994
            case 'LV':
995
                $regex[] = '/^(LV)(\d{11})$/';                           // Latvia
996
                break;
997
            case 'LT':
998
                $regex[] = '/^(LT)(\d{9}|\d{12})$/';                     // Lithuania
999
                break;
1000
            case 'LU':
1001
                $regex[] = '/^(LU)(\d{8})$/';                            // Luxembourg
1002
                break;
1003
            case 'MT':
1004
                $regex[] = '/^(MT)([1-9]\d{7})$/';                       // Malta
1005
                break;
1006
            case 'NL':
1007
                $regex[] = '/^(NL)(\d{9})B\d{2}$/';                      // Netherlands
1008
                break;
1009
            case 'NO':
1010
                $regex[] = '/^(NO)(\d{9})$/';                            // Norway (Not EU)
1011
                break;
1012
            case 'PL':
1013
                $regex[] = '/^(PL)(\d{10})$/';                           // Poland
1014
                break;
1015
            case 'PT':
1016
                $regex[] = '/^(PT)(\d{9})$/';                            // Portugal
1017
                break;
1018
            case 'RO':
1019
                $regex[] = '/^(RO)([1-9]\d{1,9})$/';                     // Romania
1020
                break;
1021
            case 'RS':
1022
                $regex[] = '/^(RS)(\d{9})$/';                            // Serbia (Not EU)
1023
                break;
1024
            case 'SI':
1025
                $regex[] = '/^(SI)([1-9]\d{7})$/';                       // Slovenia
1026
                break;
1027
            case 'SK':
1028
                $regex[] = '/^(SK)([1-9]\d[(2-4)|(6-9)]\d{7})$/';        // Slovakia Republic
1029
                break;
1030
            case 'SE':
1031
                $regex[] = '/^(SE)(\d{10}01)$/';                         // Sweden
1032
                break;
1033
            default:
1034
                $regex = array();
1035
            break;
1036
        }
1037
        
1038
        if ( empty( $regex ) ) {
1039
            return false;
1040
        }
1041
        
1042
        foreach ( $regex as $pattern ) {
1043
            $matches = null;
1044
            preg_match_all( $pattern, $vat_number, $matches );
1045
            
1046
            if ( !empty( $matches[1][0] ) && !empty( $matches[2][0] ) ) {
1047
                if ( $formatted ) {
1048
                    return array( 'code' => $matches[1][0], 'number' => $matches[2][0] );
1049
                } else {
1050
                    return true;
1051
                }
1052
            }
1053
        }
1054
        
1055
        return false;
1056
    }
1057
    
1058
    public static function vies_check( $vat_number, $country_code = '', $result = false ) {
1059
        $vat            = self::sanitize_vat( $vat_number, $country_code );
1060
        $vat_number     = $vat['vat'];
1061
        $iso            = $vat['iso'];
1062
        
1063
        $url = 'http://ec.europa.eu/taxation_customs/vies/viesquer.do?ms=' . urlencode( $iso ) . '&iso=' . urlencode( $iso ) . '&vat=' . urlencode( $vat_number );
1064
        
1065
        if ( ini_get( 'allow_url_fopen' ) ) {
1066
            $response = file_get_contents( $url );
1067
        } else if ( function_exists( 'curl_init' ) ) {
1068
            $ch = curl_init();
1069
            
1070
            curl_setopt( $ch, CURLOPT_URL, $url );
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, 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

1070
            curl_setopt( /** @scrutinizer ignore-type */ $ch, CURLOPT_URL, $url );
Loading history...
Security Header Injection introduced by
$url can contain request data and is used in request header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and ! empty($_POST['wpinv_vat_number']) ? $_POST['wpinv_vat_number'] : null is assigned to $vat_number
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1822
  2. WPInv_EUVat::check_vat() is called
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1881
  3. Enters via parameter $vat_number
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1138
  4. WPInv_EUVat::vies_check() is called
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1149
  5. Enters via parameter $vat_number
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1058
  6. Data is passed through sanitize_vat(), and self::sanitize_vat($vat_number, $country_code) is assigned to $vat
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1059
  7. $vat['iso'] is assigned to $iso
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1061
  8. Data is passed through urlencode(), and 'http://ec.europa.eu/taxation_customs/vies/viesquer.do?ms=' . urlencode($iso) . '&iso=' . urlencode($iso) . '&vat=' . urlencode($vat_number) is assigned to $url
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1063

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
1071
            curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 30 );
1072
            curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1073
            curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
1074
            curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
1075
            
1076
            $response = curl_exec( $ch );
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, 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

1076
            $response = curl_exec( /** @scrutinizer ignore-type */ $ch );
Loading history...
1077
            
1078
            if ( curl_errno( $ch ) ) {
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_errno() does only seem to accept resource, 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

1078
            if ( curl_errno( /** @scrutinizer ignore-type */ $ch ) ) {
Loading history...
1079
                wpinv_error_log( curl_error( $ch ), 'VIES CHECK ERROR' );
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_error() does only seem to accept resource, 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

1079
                wpinv_error_log( curl_error( /** @scrutinizer ignore-type */ $ch ), 'VIES CHECK ERROR' );
Loading history...
1080
                $response = '';
1081
            }
1082
            
1083
            curl_close( $ch );
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, 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

1083
            curl_close( /** @scrutinizer ignore-type */ $ch );
Loading history...
1084
        } else {
1085
            wpinv_error_log( 'To use VIES CHECK you must have allow_url_fopen is ON or cURL installed & active on your server.', 'VIES CHECK ERROR' );
1086
        }
1087
        
1088
        if ( empty( $response ) ) {
1089
            return $result;
1090
        }
1091
1092
        if ( preg_match( '/invalid VAT number/i', $response ) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $response does not seem to be defined for all execution paths leading up to this point.
Loading history...
1093
            return false;
1094
        } else if ( preg_match( '/valid VAT number/i', $response, $matches ) ) {
1095
            $content = explode( "valid VAT number", htmlentities( $response ) );
1096
            
1097
            if ( !empty( $content[1] ) ) {
1098
                preg_match_all( '/<tr>(.*?)<td.*?>(.*?)<\/td>(.*?)<\/tr>/si', html_entity_decode( $content[1] ), $matches );
1099
                
1100
                if ( !empty( $matches[2] ) && $matches[3] ) {
1101
                    $return = array();
1102
                    
1103
                    foreach ( $matches[2] as $key => $label ) {
1104
                        $label = trim( $label );
1105
                        
1106
                        switch ( strtolower( $label ) ) {
1107
                            case 'member state':
1108
                                $return['state'] = trim( strip_tags( $matches[3][$key] ) );
1109
                            break;
1110
                            case 'vat number':
1111
                                $return['number'] = trim( strip_tags( $matches[3][$key] ) );
1112
                            break;
1113
                            case 'name':
1114
                                $return['company'] = trim( strip_tags( $matches[3][$key] ) );
1115
                            break;
1116
                            case 'address':
1117
                                $address           = str_replace( array( "<br><br>", "<br /><br />", "<br/><br/>" ), "<br>", html_entity_decode( trim( $matches[3][$key] ) ) );
1118
                                $return['address'] = trim( strip_tags( $address, '<br>' ) );
1119
                            break;
1120
                            case 'consultation number':
1121
                                $return['consultation'] = trim( strip_tags( $matches[3][$key] ) );
1122
                            break;
1123
                        }
1124
                    }
1125
                    
1126
                    if ( !empty( $return ) ) {
1127
                        return $return;
1128
                    }
1129
                }
1130
            }
1131
            
1132
            return true;
1133
        } else {
1134
            return $result;
1135
        }
1136
    }
1137
    
1138
    public static function check_vat( $vat_number, $country_code = '' ) {        
1139
        $vat_name           = self::get_vat_name();
1140
        
1141
        $return             = array();
1142
        $return['valid']    = false;
1143
        $return['message']  = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
1144
                
1145
        if ( !wpinv_get_option( 'vat_offline_check' ) && !self::offline_check( $vat_number, $country_code ) ) {
1146
            return $return;
1147
        }
1148
            
1149
        $response = self::vies_check( $vat_number, $country_code );
1150
        
1151
        if ( $response ) {
1152
            $return['valid']    = true;
1153
            
1154
            if ( is_array( $response ) ) {
1155
                $return['company'] = isset( $response['company'] ) ? $response['company'] : '';
1156
                $return['address'] = isset( $response['address'] ) ? $response['address'] : '';
1157
                $return['message'] = $return['company'] . '<br/>' . $return['address'];
1158
            }
1159
        } else {
1160
            $return['valid']    = false;
1161
            $return['message']  = wp_sprintf( __( 'Fail to validate the %s number: EU Commission VAT server (VIES) check fails.', 'invoicing' ), $vat_name );
1162
        }
1163
        
1164
        return $return;
1165
    }
1166
1167
    public static function request_euvatrates( $group ) {
1168
        $response               = array();
1169
        $response['success']    = false;
1170
        $response['error']      = null;
1171
        $response['eurates']    = null;
1172
        
1173
        $euvatrates_url = 'https://euvatrates.com/rates.json';
1174
        $euvatrates_url = apply_filters( 'wpinv_euvatrates_url', $euvatrates_url );
1175
        $api_response   = wp_remote_get( $euvatrates_url );
1176
1177
        try {
1178
            if ( is_wp_error( $api_response ) ) {
1179
                $response['error']      = __( $api_response->get_error_message(), 'invoicing' );
1180
            } else {
1181
                $body = json_decode( $api_response['body'] );
1182
                if ( isset( $body->rates ) ) {
1183
                    $rates = array();
1184
                    
1185
                    foreach ( $body->rates as $country_code => $rate ) {
1186
                        $vat_rate = array();
1187
                        $vat_rate['country']        = $rate->country;
1188
                        $vat_rate['standard']       = (float)$rate->standard_rate;
1189
                        $vat_rate['reduced']        = (float)$rate->reduced_rate;
1190
                        $vat_rate['superreduced']   = (float)$rate->super_reduced_rate;
1191
                        $vat_rate['parking']        = (float)$rate->parking_rate;
1192
                        
1193
                        if ( $group !== '' && in_array( $group, array( 'standard', 'reduced', 'superreduced', 'parking' ) ) ) {
1194
                            $vat_rate_group = array();
1195
                            $vat_rate_group['country'] = $rate->country;
1196
                            $vat_rate_group[$group]    = $vat_rate[$group];
1197
                            
1198
                            $vat_rate = $vat_rate_group;
1199
                        }
1200
                        
1201
                        $rates[$country_code] = $vat_rate;
1202
                    }
1203
                    
1204
                    $response['success']    = true;                                
1205
                    $response['rates']      = apply_filters( 'wpinv_process_euvatrates', $rates, $api_response, $group );
1206
                } else {
1207
                    $response['error']      = __( 'No EU rates found!', 'invoicing' );
1208
                }
1209
            }
1210
        } catch ( Exception $e ) {
1211
            $response['error'] = __( $e->getMessage(), 'invoicing' );
1212
        }
1213
        
1214
        return apply_filters( 'wpinv_response_euvatrates', $response, $group );
1215
    }    
1216
    
1217
    public static function requires_vat( $requires_vat = false, $user_id = 0, $is_digital = null ) {
1218
        global $wpi_item_id, $wpi_country;
1219
        
1220
        if ( !empty( $_POST['wpinv_country'] ) ) {
1221
            $country_code = trim( $_POST['wpinv_country'] );
1222
        } else if ( !empty( $_POST['country'] ) ) {
1223
            $country_code = trim( $_POST['country'] );
1224
        } else if ( !empty( $wpi_country ) ) {
1225
            $country_code = $wpi_country;
1226
        } else {
1227
            $country_code = self::get_user_country( '', $user_id );
1228
        }
1229
        
1230
        if ( $is_digital === null && $wpi_item_id ) {
1231
            $is_digital = $wpi_item_id ? self::item_has_digital_rule( $wpi_item_id ) : self::allow_vat_rules();
1232
        }
1233
        
1234
        if ( !empty( $country_code ) ) {
1235
            $requires_vat = ( self::is_eu_state( $country_code ) && ( self::is_eu_state( self::$default_country ) || $is_digital ) ) || ( self::is_gst_country( $country_code ) && self::is_gst_country( self::$default_country ) );
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $requires_vat = (self::i...self::default_country)), Probably Intended Meaning: $requires_vat = self::is...self::default_country))
Loading history...
1236
        }
1237
        
1238
        return apply_filters( 'wpinv_requires_vat', $requires_vat, $user_id );
1239
    }
1240
    
1241
    public static function tax_label( $label = '' ) {
1242
        global $wpi_requires_vat;
1243
        
1244
        if ( !( $wpi_requires_vat !== 0 && $wpi_requires_vat ) ) {
1245
            $wpi_requires_vat = self::requires_vat( 0, false );
1246
        }
1247
        
1248
        return $wpi_requires_vat ? __( self::get_vat_name(), 'invoicing' ) : ( $label ? $label : __( 'Tax', 'invoicing' ) );
1249
    }
1250
    
1251
    public static function standard_rates_label() {
1252
        return __( 'Standard Rates', 'invoicing' );
1253
    }
1254
    
1255
    public static function get_rate_classes( $with_desc = false ) {        
1256
        $rate_classes_option = get_option( '_wpinv_vat_rate_classes', true );
1257
        $classes = maybe_unserialize( $rate_classes_option );
1258
1259
        if ( empty( $classes ) || !is_array( $classes ) ) {
1260
            $classes = array();
1261
        }
1262
1263
        $rate_classes = array();
1264
        if ( !array_key_exists( '_standard', $classes ) ) {
1265
            if ( $with_desc ) {
1266
                $rate_classes['_standard'] = array( 'name' => self::standard_rates_label(), 'desc' => __( 'EU member states standard VAT rates', 'invoicing' ) );
1267
            } else {
1268
                $rate_classes['_standard'] = self::standard_rates_label();
1269
            }
1270
        }
1271
        
1272
        foreach ( $classes as $key => $class ) {
1273
            $name = !empty( $class['name'] ) ? __( $class['name'], 'invoicing' ) : $key;
1274
            $desc = !empty( $class['desc'] ) ? __( $class['desc'], 'invoicing' ) : '';
1275
            
1276
            if ( $with_desc ) {
1277
                $rate_classes[$key] = array( 'name' => $name, 'desc' => $desc );
1278
            } else {
1279
                $rate_classes[$key] = $name;
1280
            }
1281
        }
1282
1283
        return $rate_classes;
1284
    }
1285
    
1286
    public static function get_all_classes() {        
1287
        $classes            = self::get_rate_classes();
1288
        $classes['_exempt'] = __( 'Exempt (0%)', 'invoicing' );
1289
        
1290
        return apply_filters( 'wpinv_vat_get_all_classes', $classes );
1291
    }
1292
    
1293
    public static function get_class_desc( $rate_class ) {        
1294
        $rate_classes = self::get_rate_classes( true );
1295
1296
        if ( !empty( $rate_classes ) && isset( $rate_classes[$rate_class] ) && isset( $rate_classes[$rate_class]['desc'] ) ) {
1297
            return $rate_classes[$rate_class]['desc'];
1298
        }
1299
        
1300
        return '';
1301
    }
1302
    
1303
    public static function get_vat_groups() {
1304
        $vat_groups = array(
1305
            'standard'      => 'Standard',
1306
            'reduced'       => 'Reduced',
1307
            'superreduced'  => 'Super Reduced',
1308
            'parking'       => 'Parking',
1309
            'increased'     => 'Increased'
1310
        );
1311
        
1312
        return apply_filters( 'wpinv_get_vat_groups', $vat_groups );
1313
    }
1314
1315
    public static function get_rules() {
1316
        $vat_rules = array(
1317
            'digital' => __( 'Digital Product', 'invoicing' ),
1318
            'physical' => __( 'Physical Product', 'invoicing' ),    
1319
            '_exempt' => __( 'Tax-Free Product', 'invoicing' ),
1320
        );
1321
        return apply_filters( 'wpinv_get_vat_rules', $vat_rules );
1322
    }
1323
1324
    public static function get_vat_rates( $class ) {
1325
        if ( $class === '_standard' ) {
1326
            return wpinv_get_tax_rates();
1327
        }
1328
1329
        $rates = self::get_non_standard_rates();
1330
1331
        return array_key_exists( $class, $rates ) ? $rates[$class] : array();
1332
    }
1333
1334
    public static function get_non_standard_rates() {
1335
        $option = get_option( 'wpinv_vat_rates', array());
1336
        return is_array( $option ) ? $option : array();
1337
    }
1338
    
1339
    public static function allow_vat_rules() {
1340
        return ( wpinv_use_taxes() && wpinv_get_option( 'apply_vat_rules' ) ? true : false );
1341
    }
1342
    
1343
    public static function allow_vat_classes() {
1344
        return false; // TODO
1345
        return ( wpinv_get_option( 'vat_allow_classes' ) ? true : false );
0 ignored issues
show
Unused Code introduced by
return wpinv_get_option(...lasses') ? true : false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1346
    }
1347
    
1348
    public static function get_item_class( $postID ) {
1349
        $class = get_post_meta( $postID, '_wpinv_vat_class', true );
1350
1351
        if ( empty( $class ) ) {
1352
            $class = '_standard';
1353
        }
1354
        
1355
        return apply_filters( 'wpinv_get_item_vat_class', $class, $postID );
1356
    }
1357
    
1358
    public static function item_class_label( $postID ) {        
1359
        $vat_classes = self::get_all_classes();
1360
        
1361
        $class = self::get_item_class( $postID );
1362
        $class = isset( $vat_classes[$class] ) ? $vat_classes[$class] : __( $class, 'invoicing' );
1363
        
1364
        return apply_filters( 'wpinv_item_class_label', $class, $postID );
1365
    }
1366
    
1367
    public static function get_item_rule( $postID ) {        
1368
        $rule_type = get_post_meta( $postID, '_wpinv_vat_rule', true );
1369
1370
        if ( empty( $rule_type ) ) {        
1371
            $rule_type = self::allow_vat_rules() ? 'digital' : 'physical';
1372
        }
1373
        
1374
        return apply_filters( 'wpinv_item_get_vat_rule', $rule_type, $postID );
1375
    }
1376
    
1377
    public static function item_rule_label( $postID ) {
1378
        $vat_rules  = self::get_rules();
1379
        $vat_rule   = self::get_item_rule( $postID );
1380
        $vat_rule   = isset( $vat_rules[$vat_rule] ) ? $vat_rules[$vat_rule] : $vat_rule;
1381
        
1382
        return apply_filters( 'wpinv_item_rule_label', $vat_rule, $postID );
1383
    }
1384
    
1385
    public static function item_has_digital_rule( $item_id = 0 ) {        
1386
        return self::get_item_rule( $item_id ) == 'digital' ? true : false;
1387
    }
1388
1389
    public static function invoice_has_digital_rule( $invoice = 0 ) {        
1390
        if ( !self::allow_vat_rules() ) {
1391
            return false;
1392
        }
1393
        
1394
        if ( empty( $invoice ) ) {
1395
            return true;
1396
        }
1397
        
1398
        if ( is_int( $invoice ) ) {
1399
            $invoice = new WPInv_Invoice( $invoice );
1400
        }
1401
        
1402
        if ( !( is_object( $invoice ) && is_a( $invoice, 'WPInv_Invoice' ) ) ) {
1403
            return true;
1404
        }
1405
        
1406
        $cart_items  = $invoice->get_cart_details();
1407
        
1408
        if ( !empty( $cart_items ) ) {
1409
            $has_digital_rule = false;
1410
            
1411
            foreach ( $cart_items as $key => $item ) {
1412
                if ( self::item_has_digital_rule( $item['id'] ) ) {
1413
                    $has_digital_rule = true;
1414
                    break;
1415
                }
1416
            }
1417
        } else {
1418
            $has_digital_rule = true;
1419
        }
1420
        
1421
        return $has_digital_rule;
1422
    }
1423
    
1424
    public static function item_is_taxable( $item_id = 0, $country = false, $state = false ) {        
1425
        if ( !wpinv_use_taxes() ) {
1426
            return false;
1427
        }
1428
1429
        $is_taxable = true;
1430
1431
        if ( !empty( $item_id ) && self::get_item_class( $item_id ) == '_exempt' ) {
1432
            $is_taxable = false;
1433
        }
1434
1435
        if ( !empty( $item_id ) && self::get_item_rule( $item_id ) == '_exempt' ) {
1436
            $is_taxable = false;
1437
        }
1438
1439
        return apply_filters( 'wpinv_item_is_taxable', $is_taxable, $item_id, $country , $state );
1440
    }
1441
    
1442
    public static function find_rate( $country, $state, $rate, $class ) {
1443
        global $wpi_zero_tax;
1444
1445
        if ( $class === '_exempt' || $wpi_zero_tax ) {
1446
            return 0;
1447
        }
1448
1449
        $tax_rates   = wpinv_get_tax_rates();
1450
        
1451
        if ( $class !== '_standard' ) {
1452
            $class_rates = self::get_vat_rates( $class );
1453
            
1454
            if ( is_array( $class_rates ) ) {
1455
                $indexed_class_rates = array();
1456
                
1457
                foreach ( $class_rates as $key => $cr ) {
1458
                    $indexed_class_rates[$cr['country']] = $cr;
1459
                }
1460
1461
                $tax_rates = array_map( function( $tr ) use( $indexed_class_rates ) {
1462
                    $tr_country = $tr['country'];
1463
                    if ( !isset( $indexed_class_rates[$tr_country] ) ) {
1464
                        return $tr;
1465
                    }
1466
                    $icr = $indexed_class_rates[$tr_country];
1467
                    return ( empty( $icr['rate'] ) && $icr['rate'] !== '0' ) ? $tr : $icr;
1468
1469
                }, $tax_rates, $class_rates );
0 ignored issues
show
Bug introduced by
It seems like $tax_rates can also be of type false; however, parameter $arr1 of array_map() does only seem to accept array, 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

1469
                }, /** @scrutinizer ignore-type */ $tax_rates, $class_rates );
Loading history...
1470
            }
1471
        }
1472
1473
        if ( !empty( $tax_rates ) ) {
1474
            foreach ( $tax_rates as $key => $tax_rate ) {
1475
                if ( $country != $tax_rate['country'] )
1476
                    continue;
1477
1478
                if ( !empty( $tax_rate['global'] ) ) {
1479
                    if ( 0 !== $tax_rate['rate'] || !empty( $tax_rate['rate'] ) ) {
1480
                        $rate = number_format( $tax_rate['rate'], 4 );
1481
                    }
1482
                } else {
1483
                    if ( empty( $tax_rate['state'] ) || strtolower( $state ) != strtolower( $tax_rate['state'] ) )
1484
                        continue;
1485
1486
                    $state_rate = $tax_rate['rate'];
1487
                    if ( 0 !== $state_rate || !empty( $state_rate ) ) {
1488
                        $rate = number_format( $state_rate, 4 );
1489
                    }
1490
                }
1491
            }
1492
        }
1493
        
1494
        return $rate;
1495
    }
1496
    
1497
    public static function get_rate( $rate = 1, $country = '', $state = '', $item_id = 0 ) {
1498
        global $wpinv_options, $wpi_session, $wpi_item_id, $wpi_zero_tax;
1499
        
1500
        $item_id = $item_id > 0 ? $item_id : $wpi_item_id;
1501
        $allow_vat_classes = self::allow_vat_classes();
1502
        $class = $item_id ? self::get_item_class( $item_id ) : '_standard';
1503
1504
        if ( $class === '_exempt' || $wpi_zero_tax ) {
1505
            return 0;
1506
        } else if ( !$allow_vat_classes ) {
0 ignored issues
show
introduced by
The condition $allow_vat_classes is always false.
Loading history...
1507
            $class = '_standard';
1508
        }
1509
1510
        if( !empty( $_POST['wpinv_country'] ) ) {
1511
            $post_country = $_POST['wpinv_country'];
1512
        } elseif( !empty( $_POST['wpinv_country'] ) ) {
1513
            $post_country = $_POST['wpinv_country'];
1514
        } elseif( !empty( $_POST['country'] ) ) {
1515
            $post_country = $_POST['country'];
1516
        } else {
1517
            $post_country = '';
1518
        }
1519
1520
        $country        = !empty( $post_country ) ? $post_country : wpinv_default_billing_country( $country );
1521
        $base_country   = wpinv_is_base_country( $country );
1522
        
1523
        $requires_vat   = self::requires_vat( 0, false );
1524
        $is_digital     = self::get_item_rule( $item_id ) == 'digital' ;
1525
        $rate           = $requires_vat && isset( $wpinv_options['eu_fallback_rate'] ) ? $wpinv_options['eu_fallback_rate'] : $rate;
1526
          
1527
        if ( self::same_country_rule() == 'no' && $base_country ) { // Disable VAT to same country
1528
            $rate = 0;
1529
        } else if ( $requires_vat ) {
1530
            $vat_number = self::get_user_vat_number( '', 0, true );
1531
            $vat_info   = self::current_vat_data();
1532
            
1533
            if ( is_array( $vat_info ) ) {
1534
                $vat_number = isset( $vat_info['number'] ) && !empty( $vat_info['valid'] ) ? $vat_info['number'] : "";
1535
            }
1536
            
1537
            if ( $country == 'UK' ) {
1538
                $country = 'GB';
1539
            }
1540
1541
            if ( !empty( $vat_number ) ) {
1542
                $rate = 0;
1543
            } else {
1544
                $rate = self::find_rate( $country, $state, $rate, $class ); // Fix if there are no tax rated and you try to pay an invoice it does not add the fallback tax rate
1545
            }
1546
1547
            if ( empty( $vat_number ) && !$is_digital ) {
1548
                if ( $base_country ) {
1549
                    $rate = self::find_rate( $country, null, $rate, $class );
1550
                } else {
1551
                    if ( empty( $country ) && isset( $wpinv_options['eu_fallback_rate'] ) ) {
1552
                        $rate = $wpinv_options['eu_fallback_rate'];
1553
                    } else if( !empty( $country ) ) {
1554
                        $rate = self::find_rate( $country, $state, $rate, $class );
1555
                    }
1556
                }
1557
            } else if ( empty( $vat_number ) || ( self::same_country_rule() == 'always' && $base_country ) ) {
1558
                if ( empty( $country ) && isset( $wpinv_options['eu_fallback_rate'] ) ) {
1559
                    $rate = $wpinv_options['eu_fallback_rate'];
1560
                } else if( !empty( $country ) ) {
1561
                    $rate = self::find_rate( $country, $state, $rate, $class );
1562
                }
1563
            }
1564
        } else {
1565
            if ( $is_digital ) {
1566
                $ip_country_code = self::get_country_by_ip();
1567
                
1568
                if ( $ip_country_code && self::is_eu_state( $ip_country_code ) ) {
1569
                    $rate = self::find_rate( $ip_country_code, '', 0, $class );
1570
                } else {
1571
                    $rate = self::find_rate( $country, $state, $rate, $class );
1572
                }
1573
            } else {
1574
                $rate = self::find_rate( $country, $state, $rate, $class );
1575
            }
1576
        }
1577
1578
        return $rate;
1579
    }
1580
    
1581
    public static function current_vat_data() {
1582
        global $wpi_session;
1583
        
1584
        return $wpi_session->get( 'user_vat_data' );
1585
    }
1586
    
1587
    public static function get_user_country( $country = '', $user_id = 0 ) {
1588
        $user_address = wpinv_get_user_address( $user_id, false );
1589
        
1590
        if ( wpinv_get_option( 'vat_ip_country_default' ) ) {
1591
            $country = '';
1592
        }
1593
        
1594
        $country    = empty( $user_address ) || !isset( $user_address['country'] ) || empty( $user_address['country'] ) ? $country : $user_address['country'];
1595
        $result     = apply_filters( 'wpinv_get_user_country', $country, $user_id );
1596
1597
        if ( empty( $result ) ) {
1598
            $result = self::get_country_by_ip();
1599
        }
1600
1601
        return $result;
1602
    }
1603
    
1604
    public static function set_user_country( $country = '', $user_id = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $user_id is not used and could be removed. ( Ignorable by Annotation )

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

1604
    public static function set_user_country( $country = '', /** @scrutinizer ignore-unused */ $user_id = 0 ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1605
        global $wpi_userID;
1606
        
1607
        if ( empty($country) && !empty($wpi_userID) && get_current_user_id() != $wpi_userID ) {
1608
            $country = wpinv_get_default_country();
1609
        }
1610
        
1611
        return $country;
1612
    }
1613
    
1614
    public static function get_user_vat_number( $vat_number = '', $user_id = 0, $is_valid = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $vat_number is not used and could be removed. ( Ignorable by Annotation )

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

1614
    public static function get_user_vat_number( /** @scrutinizer ignore-unused */ $vat_number = '', $user_id = 0, $is_valid = false ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1615
        global $wpi_current_id, $wpi_userID;
1616
        
1617
        if ( !empty( $_POST['new_user'] ) ) {
1618
            return '';
1619
        }
1620
        
1621
        if ( empty( $user_id ) ) {
1622
            $user_id = !empty( $wpi_userID ) ? $wpi_userID : ( $wpi_current_id ? wpinv_get_user_id( $wpi_current_id ) : get_current_user_id() );
1623
        }
1624
1625
        $vat_number = empty( $user_id ) ? '' : get_user_meta( $user_id, '_wpinv_vat_number', true );
1626
        
1627
        /* TODO
1628
        if ( $is_valid && $vat_number ) {
1629
            $adddress_confirmed = empty( $user_id ) ? false : get_user_meta( $user_id, '_wpinv_adddress_confirmed', true );
1630
            if ( !$adddress_confirmed ) {
1631
                $vat_number = '';
1632
            }
1633
        }
1634
        */
1635
1636
        return apply_filters('wpinv_get_user_vat_number', $vat_number, $user_id, $is_valid );
1637
    }
1638
    
1639
    public static function get_user_company( $company = '', $user_id = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $company is not used and could be removed. ( Ignorable by Annotation )

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

1639
    public static function get_user_company( /** @scrutinizer ignore-unused */ $company = '', $user_id = 0 ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1640
        global $wpi_current_id, $wpi_userID;
1641
        
1642
        if ( empty( $user_id ) ) {
1643
            $user_id = !empty( $wpi_userID ) ? $wpi_userID : ( $wpi_current_id ? wpinv_get_user_id( $wpi_current_id ) : get_current_user_id() );
1644
        }
1645
1646
        $company = empty( $user_id ) ? "" : get_user_meta( $user_id, '_wpinv_company', true );
1647
1648
        return apply_filters( 'wpinv_user_company', $company, $user_id );
1649
    }
1650
    
1651
    public static function save_user_vat_details( $company = '', $vat_number = '' ) {
1652
        $save = apply_filters( 'wpinv_allow_save_user_vat_details', true );
1653
1654
        if ( is_user_logged_in() && $save ) {
1655
            $user_id = get_current_user_id();
1656
1657
            if ( !empty( $vat_number ) ) {
1658
                update_user_meta( $user_id, '_wpinv_vat_number', $vat_number );
1659
            } else {
1660
                delete_user_meta( $user_id, '_wpinv_vat_number');
1661
            }
1662
1663
            if ( !empty( $company ) ) {
1664
                update_user_meta( $user_id, '_wpinv_company', $company );
1665
            } else {
1666
                delete_user_meta( $user_id, '_wpinv_company');
1667
                delete_user_meta( $user_id, '_wpinv_vat_number');
1668
            }
1669
        }
1670
1671
        do_action('wpinv_save_user_vat_details', $company, $vat_number);
1672
    }
1673
    
1674
    public static function ajax_vat_validate() {
1675
        global $wpinv_options, $wpi_session;
1676
        
1677
        $is_checkout            = ( !empty( $_POST['source'] ) && $_POST['source'] == 'checkout' ) ? true : false;
1678
        $response               = array();
1679
        $response['success']    = false;
1680
        
1681
        if ( empty( $_REQUEST['_wpi_nonce'] ) || ( !empty( $_REQUEST['_wpi_nonce'] ) && !wp_verify_nonce( $_REQUEST['_wpi_nonce'], 'vat_validation' ) ) ) {
1682
            $response['error'] = __( 'Invalid security nonce', 'invoicing' );
1683
            wp_send_json( $response );
1684
        }
1685
        
1686
        $vat_name   = self::get_vat_name();
1687
        
1688
        if ( $is_checkout ) {
1689
            $invoice = wpinv_get_invoice_cart();
1690
            
1691
            if ( !self::requires_vat( false, 0, self::invoice_has_digital_rule( $invoice ) ) ) {
1692
                $vat_info = array();
1693
                $wpi_session->set( 'user_vat_data', $vat_info );
1694
1695
                self::save_user_vat_details();
1696
                
1697
                $response['success'] = true;
1698
                $response['message'] = wp_sprintf( __( 'Ignore %s', 'invoicing' ), $vat_name );
1699
                wp_send_json( $response );
1700
            }
1701
        }
1702
        
1703
        $company    = !empty( $_POST['company'] ) ? sanitize_text_field( $_POST['company'] ) : '';
1704
        $vat_number = !empty( $_POST['number'] ) ? sanitize_text_field( $_POST['number'] ) : '';
1705
        
1706
        $vat_info = $wpi_session->get( 'user_vat_data' );
1707
        if ( !is_array( $vat_info ) || empty( $vat_info ) ) {
1708
            $vat_info = array( 'company'=> $company, 'number' => '', 'valid' => true );
1709
        }
1710
        
1711
        if ( empty( $vat_number ) ) {
1712
            if ( $is_checkout ) {
1713
                $response['success'] = true;
1714
                $response['message'] = wp_sprintf( __( 'No %s number has been applied. %s will be added to invoice totals', 'invoicing' ), $vat_name, $vat_name );
1715
                
1716
                $vat_info = $wpi_session->get( 'user_vat_data' );
1717
                $vat_info['number'] = "";
1718
                $vat_info['valid'] = true;
1719
                
1720
                self::save_user_vat_details( $company );
1721
            } else {
1722
                $response['error'] = wp_sprintf( __( 'Please enter your %s number!', 'invoicing' ), $vat_name );
1723
                
1724
                $vat_info['valid'] = false;
1725
            }
1726
1727
            $wpi_session->set( 'user_vat_data', $vat_info );
1728
            wp_send_json( $response );
1729
        }
1730
        
1731
        if ( empty( $company ) ) {
1732
            $vat_info['valid'] = false;
1733
            $wpi_session->set( 'user_vat_data', $vat_info );
1734
            
1735
            $response['error'] = __( 'Please enter your registered company name!', 'invoicing' );
1736
            wp_send_json( $response );
1737
        }
1738
        
1739
        if ( !empty( $wpinv_options['vat_vies_check'] ) ) {
1740
            if ( empty( $wpinv_options['vat_offline_check'] ) && !self::offline_check( $vat_number ) ) {
1741
                $vat_info['valid'] = false;
1742
                $wpi_session->set( 'user_vat_data', $vat_info );
1743
                
1744
                $response['error'] = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
1745
                wp_send_json( $response );
1746
            }
1747
            
1748
            $response['success'] = true;
1749
            $response['message'] = wp_sprintf( __( '%s number validated', 'invoicing' ), $vat_name );
1750
        } else {
1751
            $result = self::check_vat( $vat_number );
1752
            
1753
            if ( empty( $result['valid'] ) ) {
1754
                $response['error'] = $result['message'];
1755
                wp_send_json( $response );
1756
            }
1757
            
1758
            $vies_company = !empty( $result['company'] ) ? $result['company'] : '';
1759
            $vies_company = apply_filters( 'wpinv_vies_company_name', $vies_company );
1760
            
1761
            $valid_company = $vies_company && $company && ( $vies_company == '---' || strcasecmp( trim( $vies_company ), trim( $company ) ) == 0 ) ? true : false;
1762
1763
            if ( !empty( $wpinv_options['vat_disable_company_name_check'] ) || $valid_company ) {
1764
                $response['success'] = true;
1765
                $response['message'] = wp_sprintf( __( '%s number validated', 'invoicing' ), $vat_name );
1766
            } else {           
1767
                $vat_info['valid'] = false;
1768
                $wpi_session->set( 'user_vat_data', $vat_info );
1769
                
1770
                $response['success'] = false;
1771
                $response['message'] = wp_sprintf( __( 'The company name associated with the %s number provided is not the same as the company name provided.', 'invoicing' ), $vat_name );
1772
                wp_send_json( $response );
1773
            }
1774
        }
1775
        
1776
        if ( $is_checkout ) {
1777
            self::save_user_vat_details( $company, $vat_number );
1778
1779
            $vat_info = array('company' => $company, 'number' => $vat_number, 'valid' => true );
1780
            $wpi_session->set( 'user_vat_data', $vat_info );
1781
        }
1782
1783
        wp_send_json( $response );
1784
    }
1785
    
1786
    public static function ajax_vat_reset() {
1787
        global $wpi_session;
1788
        
1789
        $company    = is_user_logged_in() ? self::get_user_company() : '';
1790
        $vat_number = self::get_user_vat_number();
1791
        
1792
        $vat_info   = array('company' => $company, 'number' => $vat_number, 'valid' => false );
1793
        $wpi_session->set( 'user_vat_data', $vat_info );
1794
        
1795
        $response                       = array();
1796
        $response['success']            = true;
1797
        $response['data']['company']    = $company;
1798
        $response['data']['number']     = $vat_number;
1799
        
1800
        wp_send_json( $response );
1801
    }
1802
    
1803
    public static function checkout_vat_validate( $valid_data, $post ) {
0 ignored issues
show
Unused Code introduced by
The parameter $valid_data is not used and could be removed. ( Ignorable by Annotation )

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

1803
    public static function checkout_vat_validate( /** @scrutinizer ignore-unused */ $valid_data, $post ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $post is not used and could be removed. ( Ignorable by Annotation )

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

1803
    public static function checkout_vat_validate( $valid_data, /** @scrutinizer ignore-unused */ $post ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1804
        global $wpinv_options, $wpi_session;
1805
1806
        $vat_name  = __( self::get_vat_name(), 'invoicing' );
1807
1808
        if ( !isset( $_POST['_wpi_nonce'] ) || !wp_verify_nonce( $_POST['_wpi_nonce'], 'vat_validation' ) ) {
1809
            wpinv_set_error( 'vat_validation', wp_sprintf( __( "Invalid %s validation request.", 'invoicing' ), $vat_name ) );
1810
            return;
1811
        }
1812
1813
        $vat_saved = $wpi_session->get( 'user_vat_data' );
1814
        $wpi_session->set( 'user_vat_data', null );
1815
        
1816
        $invoice        = wpinv_get_invoice_cart();
1817
        $amount         = $invoice->get_total();
1818
        $is_digital     = self::invoice_has_digital_rule( $invoice );
1819
        $no_vat         = !self::requires_vat( 0, false, $is_digital );
1820
        
1821
        $company        = !empty( $_POST['wpinv_company'] ) ? $_POST['wpinv_company'] : null;
1822
        $vat_number     = !empty( $_POST['wpinv_vat_number'] ) ? $_POST['wpinv_vat_number'] : null;
1823
        $country        = !empty( $_POST['wpinv_country'] ) ? $_POST['wpinv_country'] : $invoice->country;
1824
        if ( empty( $country ) ) {
1825
            $country = wpinv_default_billing_country();
1826
        }
1827
        
1828
        if ( !$is_digital && $no_vat ) {
1829
            return;
1830
        }
1831
            
1832
        $vat_data           = array( 'company' => '', 'number' => '', 'valid' => false );
1833
        
1834
        $ip_country_code    = self::get_country_by_ip();
1835
        $is_eu_state        = self::is_eu_state( $country );
1836
        $is_eu_state_ip     = self::is_eu_state( $ip_country_code );
1837
        $is_non_eu_user     = !$is_eu_state && !$is_eu_state_ip;
1838
1839
        if ( $is_digital && !$is_non_eu_user && empty( $vat_number ) && apply_filters( 'wpinv_checkout_requires_country', true, $amount ) ) {
1840
            $vat_data['adddress_confirmed'] = false;
1841
            
1842
            if ( !isset( $_POST['wpinv_adddress_confirmed'] ) ) {
1843
                if ( $ip_country_code != $country ) {
1844
                    wpinv_set_error( 'vat_validation', sprintf( __( 'The country of your current location must be the same as the country of your billing location or you must %s confirm %s the billing address is your home country.', 'invoicing' ), '<a href="#wpinv_adddress_confirm">', '</a>' ) );
1845
                }
1846
            } else {
1847
                $vat_data['adddress_confirmed'] = true;
1848
            }
1849
        }
1850
1851
        if ( !empty( $wpinv_options['vat_prevent_b2c_purchase'] ) && !$is_non_eu_user && ( empty( $vat_number ) || $no_vat ) ) {
1852
            if ( $is_eu_state ) {
1853
                wpinv_set_error( 'vat_validation', wp_sprintf( __( 'Please enter and validate your %s number to verify your purchase is by an EU business.', 'invoicing' ), $vat_name ) );
1854
            } else if ( $is_digital && $is_eu_state_ip ) {
1855
                wpinv_set_error( 'vat_validation', wp_sprintf( __( 'Sales to non-EU countries cannot be completed because %s must be applied.', 'invoicing' ), $vat_name ) );
1856
            }
1857
        }
1858
1859
        if ( !$is_eu_state || $no_vat || empty( $vat_number ) ) {
1860
            return;
1861
        }
1862
1863
        if ( !empty( $vat_saved ) && isset( $vat_saved['valid'] ) ) {
1864
            $vat_data['valid']  = $vat_saved['valid'];
1865
        }
1866
            
1867
        if ( $company !== null ) {
1868
            $vat_data['company'] = $company;
1869
        }
1870
1871
        $message = '';
1872
        if ( $vat_number !== null ) {
1873
            $vat_data['number'] = $vat_number;
1874
            
1875
            if ( !$vat_data['valid'] || ( $vat_saved['number'] !== $vat_data['number'] ) || ( $vat_saved['company'] !== $vat_data['company'] ) ) {
1876
                if ( !empty( $wpinv_options['vat_vies_check'] ) ) {            
1877
                    if ( empty( $wpinv_options['vat_offline_check'] ) && !self::offline_check( $vat_number ) ) {
1878
                        $vat_data['valid'] = false;
1879
                    }
1880
                } else {
1881
                    $result = self::check_vat( $vat_number );
1882
                    
1883
                    if ( !empty( $result['valid'] ) ) {                
1884
                        $vat_data['valid'] = true;
1885
                        $vies_company = !empty( $result['company'] ) ? $result['company'] : '';
1886
                        $vies_company = apply_filters( 'wpinv_vies_company_name', $vies_company );
1887
                    
1888
                        $valid_company = $vies_company && $company && ( $vies_company == '---' || strcasecmp( trim( $vies_company ), trim( $company ) ) == 0 ) ? true : false;
1889
1890
                        if ( !( !empty( $wpinv_options['vat_disable_company_name_check'] ) || $valid_company ) ) {         
1891
                            $vat_data['valid'] = false;
1892
                            
1893
                            $message = wp_sprintf( __( 'The company name associated with the %s number provided is not the same as the company name provided.', 'invoicing' ), $vat_name );
1894
                        }
1895
                    } else {
1896
                        $message = wp_sprintf( __( 'Fail to validate the %s number: EU Commission VAT server (VIES) check fails.', 'invoicing' ), $vat_name );
1897
                    }
1898
                }
1899
                
1900
                if ( !$vat_data['valid'] ) {
1901
                    $error = wp_sprintf( __( 'The %s %s number %s you have entered has not been validated', 'invoicing' ), '<a href="#wpi-vat-details">', $vat_name, '</a>' ) . ( $message ? ' ( ' . $message . ' )' : '' );
1902
                    wpinv_set_error( 'vat_validation', $error );
1903
                }
1904
            }
1905
        }
1906
1907
        $wpi_session->set( 'user_vat_data', $vat_data );
1908
    }
1909
    
1910
    public static function checkout_vat_fields( $billing_details ) {
0 ignored issues
show
Unused Code introduced by
The parameter $billing_details is not used and could be removed. ( Ignorable by Annotation )

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

1910
    public static function checkout_vat_fields( /** @scrutinizer ignore-unused */ $billing_details ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1911
        global $wpi_session, $wpinv_options, $wpi_country, $wpi_requires_vat;
1912
        
1913
        $ip_address         = wpinv_get_ip();
1914
        $ip_country_code    = self::get_country_by_ip();
1915
        
1916
        $tax_label          = __( self::get_vat_name(), 'invoicing' );
1917
        $invoice            = wpinv_get_invoice_cart();
1918
        $is_digital         = self::invoice_has_digital_rule( $invoice );
1919
        $wpi_country        = $invoice->country;
1920
        
1921
        $requires_vat       = !self::hide_vat_fields() && !$invoice->is_free() && self::requires_vat( 0, false, $is_digital );
1922
        $wpi_requires_vat   = $requires_vat;
1923
        
1924
        $company            = self::get_user_company();
1925
        $vat_number         = self::get_user_vat_number();
1926
        
1927
        $validated          = $vat_number ? self::get_user_vat_number( '', 0, true ) : 1;
1928
        $vat_info           = $wpi_session->get( 'user_vat_data' );
1929
1930
        if ( is_array( $vat_info ) ) {
1931
            $company    = isset( $vat_info['company'] ) ? $vat_info['company'] : '';
1932
            $vat_number = isset( $vat_info['number'] ) ? $vat_info['number'] : '';
1933
            $validated  = isset( $vat_info['valid'] ) ? $vat_info['valid'] : false;
1934
        }
1935
        
1936
        $selected_country = $invoice->country ? $invoice->country : wpinv_default_billing_country();
1937
1938
        if ( $ip_country_code == 'UK' ) {
1939
            $ip_country_code = 'GB';
1940
        }
1941
        
1942
        if ( $selected_country == 'UK' ) {
1943
            $selected_country = 'GB';
1944
        }
1945
        
1946
        if ( $requires_vat && ( self::same_country_rule() == 'no' && wpinv_is_base_country( $selected_country ) || !self::allow_vat_rules() ) ) {
1947
            $requires_vat = false;
1948
        }
1949
1950
        $display_vat_details    = $requires_vat ? 'block' : 'none';
1951
        $display_validate_btn   = 'none';
1952
        $display_reset_btn      = 'none';
1953
        
1954
        if ( !empty( $vat_number ) && $validated ) {
1955
            $vat_vailidated_text    = wp_sprintf( __( '%s number validated', 'invoicing' ), $tax_label );
1956
            $vat_vailidated_class   = 'wpinv-vat-stat-1';
1957
            $display_reset_btn      = 'block';
1958
        } else {
1959
            $vat_vailidated_text    = empty( $vat_number ) ? '' : wp_sprintf( __( '%s number not validated', 'invoicing' ), $tax_label );
1960
            $vat_vailidated_class   = empty( $vat_number ) ? '' : 'wpinv-vat-stat-0';
1961
            $display_validate_btn   = 'block';
1962
        }
1963
        
1964
        $show_ip_country        = $is_digital && ( empty( $vat_number ) || !$requires_vat ) && $ip_country_code != $selected_country ? 'block' : 'none';
1965
        ?>
1966
        <div id="wpi-vat-details" class="wpi-vat-details clearfix" style="display:<?php echo $display_vat_details; ?>">
1967
            <div id="wpi_vat_info" class="clearfix panel panel-default">
1968
                <div class="panel-heading"><h3 class="panel-title"><?php echo wp_sprintf( __( '%s Details', 'invoicing' ), $tax_label );?></h3></div>
1969
                <div id="wpinv-fields-box" class="panel-body">
1970
                    <p id="wpi_show_vat_note">
1971
                        <?php echo wp_sprintf( __( 'Validate your registered %s number to exclude tax.', 'invoicing' ), $tax_label ); ?>
1972
                    </p>
1973
                    <div id="wpi_vat_fields" class="wpi_vat_info">
1974
                        <p class="wpi-cart-field wpi-col2 wpi-colf">
1975
                            <label for="wpinv_company" class="wpi-label"><?php _e( 'Company Name', 'invoicing' );?></label>
1976
                            <?php
1977
                            echo wpinv_html_text( array(
1978
                                    'id'            => 'wpinv_company',
1979
                                    'name'          => 'wpinv_company',
1980
                                    'value'         => $company,
1981
                                    'class'         => 'wpi-input form-control',
1982
                                    'placeholder'   => __( 'Company name', 'invoicing' ),
1983
                                ) );
1984
                            ?>
1985
                        </p>
1986
                        <p class="wpi-cart-field wpi-col2 wpi-coll wpi-cart-field-vat">
1987
                            <label for="wpinv_vat_number" class="wpi-label"><?php echo wp_sprintf( __( '%s Number', 'invoicing' ), $tax_label );?></label>
1988
                            <span id="wpinv_vat_number-wrap">
1989
                                <label for="wpinv_vat_number" class="wpinv-label"></label>
1990
                                <input type="text" class="wpi-input form-control" placeholder="<?php echo esc_attr( wp_sprintf( __( '%s number', 'invoicing' ), $tax_label ) );?>" value="<?php esc_attr_e( $vat_number );?>" id="wpinv_vat_number" name="wpinv_vat_number">
1991
                                <span class="wpinv-vat-stat <?php echo $vat_vailidated_class;?>"><i class="fa"></i>&nbsp;<font><?php echo $vat_vailidated_text;?></font></span>
1992
                            </span>
1993
                        </p>
1994
                        <p class="wpi-cart-field wpi-col wpi-colf wpi-cart-field-actions">
1995
                            <button class="btn btn-success btn-sm wpinv-vat-validate" type="button" id="wpinv_vat_validate" style="display:<?php echo $display_validate_btn; ?>"><?php echo wp_sprintf( __("Validate %s Number", 'invoicing'), $tax_label ); ?></button>
1996
                            <button class="btn btn-danger btn-sm wpinv-vat-reset" type="button" id="wpinv_vat_reset" style="display:<?php echo $display_reset_btn; ?>"><?php echo wp_sprintf( __("Reset %s", 'invoicing'), $tax_label ); ?></button>
1997
                            <span class="wpi-vat-box wpi-vat-box-info"><span id="text"></span></span>
1998
                            <span class="wpi-vat-box wpi-vat-box-error"><span id="text"></span></span>
1999
                            <input type="hidden" name="_wpi_nonce" value="<?php echo wp_create_nonce( 'vat_validation' ) ?>" />
2000
                        </p>
2001
                    </div>
2002
                </div>
2003
            </div>
2004
        </div>
2005
        <div id="wpinv_adddress_confirm" class="wpi-vat-info clearfix panel panel-info" value="<?php echo $ip_country_code; ?>" style="display:<?php echo $show_ip_country; ?>;">
2006
            <div id="wpinv-fields-box" class="panel-body">
2007
                <span id="wpinv_adddress_confirmed-wrap">
2008
                    <input type="checkbox" id="wpinv_adddress_confirmed" name="wpinv_adddress_confirmed" value="1">
2009
                    <label for="wpinv_adddress_confirmed"><?php _e('The country of your current location must be the same as the country of your billing location or you must confirm the billing address is your home country.', 'invoicing'); ?></label>
2010
                </span>
2011
            </div>
2012
        </div>
2013
        <?php if ( empty( $wpinv_options['hide_ip_address'] ) ) { 
2014
            $ip_link = '<a title="' . esc_attr( __( 'View more details on map', 'invoicing' ) ) . '" target="_blank" href="' . esc_url( admin_url( 'admin-ajax.php?action=wpinv_ip_geolocation&ip=' . $ip_address ) ) . '" class="wpi-ip-address-link">' . $ip_address . '&nbsp;&nbsp;<i class="fa fa-external-link-square" aria-hidden="true"></i></a>';
2015
        ?>
2016
        <div class="wpi-ip-info clearfix panel panel-info">
2017
            <div id="wpinv-fields-box" class="panel-body">
2018
                <span><?php echo wp_sprintf( __( "Your IP address is: %s", 'invoicing' ), $ip_link ); ?></span>
2019
            </div>
2020
        </div>
2021
        <?php }
2022
    }
2023
    
2024
    public static function show_vat_notice( $invoice ) {
2025
        if ( empty( $invoice ) ) {
2026
            return NULL;
2027
        }
2028
        
2029
        $label      = wpinv_get_option( 'vat_invoice_notice_label' );
2030
        $notice     = wpinv_get_option( 'vat_invoice_notice' );
2031
        if ( $label || $notice ) {
2032
        ?>
2033
        <div class="row wpinv-vat-notice">
2034
            <div class="col-sm-12">
2035
                <?php if ( $label ) { ?>
2036
                <strong><?php _e( $label, 'invoicing' ); ?></strong>
2037
                <?php } if ( $notice ) { ?>
2038
                <?php echo wpautop( wptexturize( __( $notice, 'invoicing' ) ) ) ?>
2039
                <?php } ?>
2040
            </div>
2041
        </div>
2042
        <?php
2043
        }
2044
    }
2045
}
2046
2047
global $wpinv_euvat;
2048
$wpinv_euvat = WPInv_EUVat::get_instance();