Passed
Push — master ( a83567...99d634 )
by Stiofan
05:05
created

WPInv_EUVat::get_country_by_ip()   F

Complexity

Conditions 25
Paths 161

Size

Total Lines 47
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 29
c 0
b 0
f 0
nc 161
nop 1
dl 0
loc 47
rs 3.6583

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 = '' ) {
647
        global $wpinv_ip_address_country;
648
        
649
        if ( !empty( $wpinv_ip_address_country ) ) {
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 1818
  2. WPInv_EUVat::check_vat() is called
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1877
  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
                
1183
                if ( isset( $body->rates ) ) {
1184
                    $rates = array();
1185
                    
1186
                    foreach ( $body->rates as $country_code => $rate ) {
1187
                        $vat_rate = array();
1188
                        $vat_rate['country']        = $rate->country;
1189
                        $vat_rate['standard']       = (float)$rate->standard_rate;
1190
                        $vat_rate['reduced']        = (float)$rate->reduced_rate;
1191
                        $vat_rate['superreduced']   = (float)$rate->super_reduced_rate;
1192
                        $vat_rate['parking']        = (float)$rate->parking_rate;
1193
                        
1194
                        if ( $group !== '' && in_array( $group, array( 'standard', 'reduced', 'superreduced', 'parking' ) ) ) {
1195
                            $vat_rate_group = array();
1196
                            $vat_rate_group['country'] = $rate->country;
1197
                            $vat_rate_group[$group]    = $vat_rate[$group];
1198
                            
1199
                            $vat_rate = $vat_rate_group;
1200
                        }
1201
                        
1202
                        $rates[$country_code] = $vat_rate;
1203
                    }
1204
                    
1205
                    $response['success']    = true;                                
1206
                    $response['rates']      = apply_filters( 'wpinv_process_euvatrates', $rates, $api_response, $group );
1207
                } else {
1208
                    $response['error']      = __( 'No EU rates found!', 'invoicing' );
1209
                }
1210
            }
1211
        } catch ( Exception $e ) {
1212
            $response['error'] = __( $e->getMessage(), 'invoicing' );
1213
        }
1214
        
1215
        return apply_filters( 'wpinv_response_euvatrates', $response, $group );
1216
    }    
1217
    
1218
    public static function requires_vat( $requires_vat = false, $user_id = 0, $is_digital = null ) {
1219
        global $wpi_item_id, $wpi_country;
1220
        
1221
        if ( !empty( $_POST['wpinv_country'] ) ) {
1222
            $country_code = trim( $_POST['wpinv_country'] );
1223
        } else if ( !empty( $_POST['country'] ) ) {
1224
            $country_code = trim( $_POST['country'] );
1225
        } else if ( !empty( $wpi_country ) ) {
1226
            $country_code = $wpi_country;
1227
        } else {
1228
            $country_code = self::get_user_country( '', $user_id );
1229
        }
1230
        
1231
        if ( $is_digital === null && $wpi_item_id ) {
1232
            $is_digital = $wpi_item_id ? self::item_has_digital_rule( $wpi_item_id ) : self::allow_vat_rules();
1233
        }
1234
        
1235
        if ( !empty( $country_code ) ) {
1236
            $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...
1237
        }
1238
        
1239
        return apply_filters( 'wpinv_requires_vat', $requires_vat, $user_id );
1240
    }
1241
    
1242
    public static function tax_label( $label = '' ) {
1243
        global $wpi_requires_vat;
1244
        
1245
        if ( !( $wpi_requires_vat !== 0 && $wpi_requires_vat ) ) {
1246
            $wpi_requires_vat = self::requires_vat( 0, false );
1247
        }
1248
        
1249
        return $wpi_requires_vat ? __( self::get_vat_name(), 'invoicing' ) : ( $label ? $label : __( 'Tax', 'invoicing' ) );
1250
    }
1251
    
1252
    public static function standard_rates_label() {
1253
        return __( 'Standard Rates', 'invoicing' );
1254
    }
1255
    
1256
    public static function get_rate_classes( $with_desc = false ) {        
1257
        $rate_classes_option = get_option( '_wpinv_vat_rate_classes', true );
1258
        $classes = maybe_unserialize( $rate_classes_option );
1259
        
1260
        if ( empty( $classes ) || !is_array( $classes ) ) {
1261
            $classes = array();
1262
        }
1263
1264
        $rate_classes = array();
1265
        if ( !array_key_exists( '_standard', $classes ) ) {
1266
            if ( $with_desc ) {
1267
                $rate_classes['_standard'] = array( 'name' => self::standard_rates_label(), 'desc' => __( 'EU member states standard VAT rates', 'invoicing' ) );
1268
            } else {
1269
                $rate_classes['_standard'] = self::standard_rates_label();
1270
            }
1271
        }
1272
        
1273
        foreach ( $classes as $key => $class ) {
1274
            $name = !empty( $class['name'] ) ? __( $class['name'], 'invoicing' ) : $key;
1275
            $desc = !empty( $class['desc'] ) ? __( $class['desc'], 'invoicing' ) : '';
1276
            
1277
            if ( $with_desc ) {
1278
                $rate_classes[$key] = array( 'name' => $name, 'desc' => $desc );
1279
            } else {
1280
                $rate_classes[$key] = $name;
1281
            }
1282
        }
1283
1284
        return $rate_classes;
1285
    }
1286
    
1287
    public static function get_all_classes() {        
1288
        $classes            = self::get_rate_classes();
1289
        $classes['_exempt'] = __( 'Exempt (0%)', 'invoicing' );
1290
        
1291
        return apply_filters( 'wpinv_vat_get_all_classes', $classes );
1292
    }
1293
    
1294
    public static function get_class_desc( $rate_class ) {        
1295
        $rate_classes = self::get_rate_classes( true );
1296
1297
        if ( !empty( $rate_classes ) && isset( $rate_classes[$rate_class] ) && isset( $rate_classes[$rate_class]['desc'] ) ) {
1298
            return $rate_classes[$rate_class]['desc'];
1299
        }
1300
        
1301
        return '';
1302
    }
1303
    
1304
    public static function get_vat_groups() {
1305
        $vat_groups = array(
1306
            'standard'      => 'Standard',
1307
            'reduced'       => 'Reduced',
1308
            'superreduced'  => 'Super Reduced',
1309
            'parking'       => 'Parking',
1310
            'increased'     => 'Increased'
1311
        );
1312
        
1313
        return apply_filters( 'wpinv_get_vat_groups', $vat_groups );
1314
    }
1315
1316
    public static function get_rules() {
1317
        $vat_rules = array(
1318
            'digital' => __( 'Digital Product', 'invoicing' ),
1319
            'physical' => __( 'Physical 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
        return apply_filters( 'wpinv_item_is_taxable', $is_taxable, $item_id, $country , $state );
1436
    }
1437
    
1438
    public static function find_rate( $country, $state, $rate, $class ) {
1439
        global $wpi_zero_tax;
1440
1441
        if ( $class === '_exempt' || $wpi_zero_tax ) {
1442
            return 0;
1443
        }
1444
1445
        $tax_rates   = wpinv_get_tax_rates();
1446
        
1447
        if ( $class !== '_standard' ) {
1448
            $class_rates = self::get_vat_rates( $class );
1449
            
1450
            if ( is_array( $class_rates ) ) {
1451
                $indexed_class_rates = array();
1452
                
1453
                foreach ( $class_rates as $key => $cr ) {
1454
                    $indexed_class_rates[$cr['country']] = $cr;
1455
                }
1456
1457
                $tax_rates = array_map( function( $tr ) use( $indexed_class_rates ) {
1458
                    $tr_country = $tr['country'];
1459
                    if ( !isset( $indexed_class_rates[$tr_country] ) ) {
1460
                        return $tr;
1461
                    }
1462
                    $icr = $indexed_class_rates[$tr_country];
1463
                    return ( empty( $icr['rate'] ) && $icr['rate'] !== '0' ) ? $tr : $icr;
1464
1465
                }, $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

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

1600
    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...
1601
        global $wpi_userID;
1602
        
1603
        if ( empty($country) && !empty($wpi_userID) && get_current_user_id() != $wpi_userID ) {
1604
            $country = wpinv_get_default_country();
1605
        }
1606
        
1607
        return $country;
1608
    }
1609
    
1610
    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

1610
    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...
1611
        global $wpi_current_id, $wpi_userID;
1612
        
1613
        if ( !empty( $_POST['new_user'] ) ) {
1614
            return '';
1615
        }
1616
        
1617
        if ( empty( $user_id ) ) {
1618
            $user_id = !empty( $wpi_userID ) ? $wpi_userID : ( $wpi_current_id ? wpinv_get_user_id( $wpi_current_id ) : get_current_user_id() );
1619
        }
1620
1621
        $vat_number = empty( $user_id ) ? '' : get_user_meta( $user_id, '_wpinv_vat_number', true );
1622
        
1623
        /* TODO
1624
        if ( $is_valid && $vat_number ) {
1625
            $adddress_confirmed = empty( $user_id ) ? false : get_user_meta( $user_id, '_wpinv_adddress_confirmed', true );
1626
            if ( !$adddress_confirmed ) {
1627
                $vat_number = '';
1628
            }
1629
        }
1630
        */
1631
1632
        return apply_filters('wpinv_get_user_vat_number', $vat_number, $user_id, $is_valid );
1633
    }
1634
    
1635
    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

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

1799
    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...
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

1799
    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...
1800
        global $wpinv_options, $wpi_session;
1801
        
1802
        $vat_name  = __( self::get_vat_name(), 'invoicing' );
1803
        
1804
        if ( !isset( $_POST['_wpi_nonce'] ) || !wp_verify_nonce( $_POST['_wpi_nonce'], 'vat_validation' ) ) {
1805
            wpinv_set_error( 'vat_validation', wp_sprintf( __( "Invalid %s validation request.", 'invoicing' ), $vat_name ) );
1806
            return;
1807
        }
1808
        
1809
        $vat_saved = $wpi_session->get( 'user_vat_data' );
1810
        $wpi_session->set( 'user_vat_data', null );
1811
        
1812
        $invoice        = wpinv_get_invoice_cart();
1813
        $amount         = $invoice->get_total();
1814
        $is_digital     = self::invoice_has_digital_rule( $invoice );
1815
        $no_vat         = !self::requires_vat( 0, false, $is_digital );
1816
        
1817
        $company        = !empty( $_POST['wpinv_company'] ) ? $_POST['wpinv_company'] : null;
1818
        $vat_number     = !empty( $_POST['wpinv_vat_number'] ) ? $_POST['wpinv_vat_number'] : null;
1819
        $country        = !empty( $_POST['wpinv_country'] ) ? $_POST['wpinv_country'] : $invoice->country;
1820
        if ( empty( $country ) ) {
1821
            $country = wpinv_default_billing_country();
1822
        }
1823
        
1824
        if ( !$is_digital && $no_vat ) {
1825
            return;
1826
        }
1827
            
1828
        $vat_data           = array( 'company' => '', 'number' => '', 'valid' => false );
1829
        
1830
        $ip_country_code    = self::get_country_by_ip();
1831
        $is_eu_state        = self::is_eu_state( $country );
1832
        $is_eu_state_ip     = self::is_eu_state( $ip_country_code );
1833
        $is_non_eu_user     = !$is_eu_state && !$is_eu_state_ip;
1834
        
1835
        if ( $is_digital && !$is_non_eu_user && empty( $vat_number ) && apply_filters( 'wpinv_checkout_requires_country', true, $amount ) ) {
1836
            $vat_data['adddress_confirmed'] = false;
1837
            
1838
            if ( !isset( $_POST['wpinv_adddress_confirmed'] ) ) {
1839
                if ( $ip_country_code != $country ) {
1840
                    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>' ) );
1841
                }
1842
            } else {
1843
                $vat_data['adddress_confirmed'] = true;
1844
            }
1845
        }
1846
        
1847
        if ( !empty( $wpinv_options['vat_prevent_b2c_purchase'] ) && !$is_non_eu_user && ( empty( $vat_number ) || $no_vat ) ) {
1848
            if ( $is_eu_state ) {
1849
                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 ) );
1850
            } else if ( $is_digital && $is_eu_state_ip ) {
1851
                wpinv_set_error( 'vat_validation', wp_sprintf( __( 'Sales to non-EU countries cannot be completed because %s must be applied.', 'invoicing' ), $vat_name ) );
1852
            }
1853
        }
1854
        
1855
        if ( !$is_eu_state || $no_vat || empty( $vat_number ) ) {
1856
            return;
1857
        }
1858
1859
        if ( !empty( $vat_saved ) && isset( $vat_saved['valid'] ) ) {
1860
            $vat_data['valid']  = $vat_saved['valid'];
1861
        }
1862
            
1863
        if ( $company !== null ) {
1864
            $vat_data['company'] = $company;
1865
        }
1866
1867
        $message = '';
1868
        if ( $vat_number !== null ) {
1869
            $vat_data['number'] = $vat_number;
1870
            
1871
            if ( !$vat_data['valid'] || ( $vat_saved['number'] !== $vat_data['number'] ) || ( $vat_saved['company'] !== $vat_data['company'] ) ) {
1872
                if ( !empty( $wpinv_options['vat_vies_check'] ) ) {            
1873
                    if ( empty( $wpinv_options['vat_offline_check'] ) && !self::offline_check( $vat_number ) ) {
1874
                        $vat_data['valid'] = false;
1875
                    }
1876
                } else {
1877
                    $result = self::check_vat( $vat_number );
1878
                    
1879
                    if ( !empty( $result['valid'] ) ) {                
1880
                        $vat_data['valid'] = true;
1881
                        $vies_company = !empty( $result['company'] ) ? $result['company'] : '';
1882
                        $vies_company = apply_filters( 'wpinv_vies_company_name', $vies_company );
1883
                    
1884
                        $valid_company = $vies_company && $company && ( $vies_company == '---' || strcasecmp( trim( $vies_company ), trim( $company ) ) == 0 ) ? true : false;
1885
1886
                        if ( !( !empty( $wpinv_options['vat_disable_company_name_check'] ) || $valid_company ) ) {         
1887
                            $vat_data['valid'] = false;
1888
                            
1889
                            $message = wp_sprintf( __( 'The company name associated with the %s number provided is not the same as the company name provided.', 'invoicing' ), $vat_name );
1890
                        }
1891
                    } else {
1892
                        $message = wp_sprintf( __( 'Fail to validate the %s number: EU Commission VAT server (VIES) check fails.', 'invoicing' ), $vat_name );
1893
                    }
1894
                }
1895
                
1896
                if ( !$vat_data['valid'] ) {
1897
                    $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 . ' )' : '' );
1898
                    wpinv_set_error( 'vat_validation', $error );
1899
                }
1900
            }
1901
        }
1902
1903
        $wpi_session->set( 'user_vat_data', $vat_data );
1904
    }
1905
    
1906
    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

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