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

WPInv_EUVat::checkout_vat_fields()   F

Complexity

Conditions 25
Paths > 20000

Size

Total Lines 109
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 69
c 0
b 0
f 0
nc 138240
nop 1
dl 0
loc 109
rs 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

1060
            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 1808
  2. WPInv_EUVat::check_vat() is called
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1867
  3. Enters via parameter $vat_number
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1128
  4. WPInv_EUVat::vies_check() is called
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1139
  5. Enters via parameter $vat_number
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1048
  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 1049
  7. $vat['iso'] is assigned to $iso
    in includes/libraries/wpinv-euvat/class-wpinv-euvat.php on line 1051
  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 1053

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...
1061
            curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 30 );
1062
            curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1063
            curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
1064
            curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
1065
            
1066
            $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

1066
            $response = curl_exec( /** @scrutinizer ignore-type */ $ch );
Loading history...
1067
            
1068
            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

1068
            if ( curl_errno( /** @scrutinizer ignore-type */ $ch ) ) {
Loading history...
1069
                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

1069
                wpinv_error_log( curl_error( /** @scrutinizer ignore-type */ $ch ), 'VIES CHECK ERROR' );
Loading history...
1070
                $response = '';
1071
            }
1072
            
1073
            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

1073
            curl_close( /** @scrutinizer ignore-type */ $ch );
Loading history...
1074
        } else {
1075
            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' );
1076
        }
1077
        
1078
        if ( empty( $response ) ) {
1079
            return $result;
1080
        }
1081
1082
        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...
1083
            return false;
1084
        } else if ( preg_match( '/valid VAT number/i', $response, $matches ) ) {
1085
            $content = explode( "valid VAT number", htmlentities( $response ) );
1086
            
1087
            if ( !empty( $content[1] ) ) {
1088
                preg_match_all( '/<tr>(.*?)<td.*?>(.*?)<\/td>(.*?)<\/tr>/si', html_entity_decode( $content[1] ), $matches );
1089
                
1090
                if ( !empty( $matches[2] ) && $matches[3] ) {
1091
                    $return = array();
1092
                    
1093
                    foreach ( $matches[2] as $key => $label ) {
1094
                        $label = trim( $label );
1095
                        
1096
                        switch ( strtolower( $label ) ) {
1097
                            case 'member state':
1098
                                $return['state'] = trim( strip_tags( $matches[3][$key] ) );
1099
                            break;
1100
                            case 'vat number':
1101
                                $return['number'] = trim( strip_tags( $matches[3][$key] ) );
1102
                            break;
1103
                            case 'name':
1104
                                $return['company'] = trim( strip_tags( $matches[3][$key] ) );
1105
                            break;
1106
                            case 'address':
1107
                                $address           = str_replace( array( "<br><br>", "<br /><br />", "<br/><br/>" ), "<br>", html_entity_decode( trim( $matches[3][$key] ) ) );
1108
                                $return['address'] = trim( strip_tags( $address, '<br>' ) );
1109
                            break;
1110
                            case 'consultation number':
1111
                                $return['consultation'] = trim( strip_tags( $matches[3][$key] ) );
1112
                            break;
1113
                        }
1114
                    }
1115
                    
1116
                    if ( !empty( $return ) ) {
1117
                        return $return;
1118
                    }
1119
                }
1120
            }
1121
            
1122
            return true;
1123
        } else {
1124
            return $result;
1125
        }
1126
    }
1127
    
1128
    public static function check_vat( $vat_number, $country_code = '' ) {        
1129
        $vat_name           = self::get_vat_name();
1130
        
1131
        $return             = array();
1132
        $return['valid']    = false;
1133
        $return['message']  = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
1134
                
1135
        if ( !wpinv_get_option( 'vat_offline_check' ) && !self::offline_check( $vat_number, $country_code ) ) {
1136
            return $return;
1137
        }
1138
            
1139
        $response = self::vies_check( $vat_number, $country_code );
1140
        
1141
        if ( $response ) {
1142
            $return['valid']    = true;
1143
            
1144
            if ( is_array( $response ) ) {
1145
                $return['company'] = isset( $response['company'] ) ? $response['company'] : '';
1146
                $return['address'] = isset( $response['address'] ) ? $response['address'] : '';
1147
                $return['message'] = $return['company'] . '<br/>' . $return['address'];
1148
            }
1149
        } else {
1150
            $return['valid']    = false;
1151
            $return['message']  = wp_sprintf( __( 'Fail to validate the %s number: EU Commission VAT server (VIES) check fails.', 'invoicing' ), $vat_name );
1152
        }
1153
        
1154
        return $return;
1155
    }
1156
    
1157
    public static function request_euvatrates( $group ) {
1158
        $response               = array();
1159
        $response['success']    = false;
1160
        $response['error']      = null;
1161
        $response['eurates']    = null;
1162
        
1163
        $euvatrates_url = 'https://euvatrates.com/rates.json';
1164
        $euvatrates_url = apply_filters( 'wpinv_euvatrates_url', $euvatrates_url );
1165
        $api_response   = wp_remote_get( $euvatrates_url );
1166
    
1167
        try {
1168
            if ( is_wp_error( $api_response ) ) {
1169
                $response['error']      = __( $api_response->get_error_message(), 'invoicing' );
1170
            } else {
1171
                $body = json_decode( $api_response['body'] );
1172
                
1173
                if ( isset( $body->rates ) ) {
1174
                    $rates = array();
1175
                    
1176
                    foreach ( $body->rates as $country_code => $rate ) {
1177
                        $vat_rate = array();
1178
                        $vat_rate['country']        = $rate->country;
1179
                        $vat_rate['standard']       = (float)$rate->standard_rate;
1180
                        $vat_rate['reduced']        = (float)$rate->reduced_rate;
1181
                        $vat_rate['superreduced']   = (float)$rate->super_reduced_rate;
1182
                        $vat_rate['parking']        = (float)$rate->parking_rate;
1183
                        
1184
                        if ( $group !== '' && in_array( $group, array( 'standard', 'reduced', 'superreduced', 'parking' ) ) ) {
1185
                            $vat_rate_group = array();
1186
                            $vat_rate_group['country'] = $rate->country;
1187
                            $vat_rate_group[$group]    = $vat_rate[$group];
1188
                            
1189
                            $vat_rate = $vat_rate_group;
1190
                        }
1191
                        
1192
                        $rates[$country_code] = $vat_rate;
1193
                    }
1194
                    
1195
                    $response['success']    = true;                                
1196
                    $response['rates']      = apply_filters( 'wpinv_process_euvatrates', $rates, $api_response, $group );
1197
                } else {
1198
                    $response['error']      = __( 'No EU rates found!', 'invoicing' );
1199
                }
1200
            }
1201
        } catch ( Exception $e ) {
1202
            $response['error'] = __( $e->getMessage(), 'invoicing' );
1203
        }
1204
        
1205
        return apply_filters( 'wpinv_response_euvatrates', $response, $group );
1206
    }    
1207
    
1208
    public static function requires_vat( $requires_vat = false, $user_id = 0, $is_digital = null ) {
1209
        global $wpi_item_id, $wpi_country;
1210
        
1211
        if ( !empty( $_POST['wpinv_country'] ) ) {
1212
            $country_code = trim( $_POST['wpinv_country'] );
1213
        } else if ( !empty( $_POST['country'] ) ) {
1214
            $country_code = trim( $_POST['country'] );
1215
        } else if ( !empty( $wpi_country ) ) {
1216
            $country_code = $wpi_country;
1217
        } else {
1218
            $country_code = self::get_user_country( '', $user_id );
1219
        }
1220
        
1221
        if ( $is_digital === null && $wpi_item_id ) {
1222
            $is_digital = $wpi_item_id ? self::item_has_digital_rule( $wpi_item_id ) : self::allow_vat_rules();
1223
        }
1224
        
1225
        if ( !empty( $country_code ) ) {
1226
            $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...
1227
        }
1228
        
1229
        return apply_filters( 'wpinv_requires_vat', $requires_vat, $user_id );
1230
    }
1231
    
1232
    public static function tax_label( $label = '' ) {
1233
        global $wpi_requires_vat;
1234
        
1235
        if ( !( $wpi_requires_vat !== 0 && $wpi_requires_vat ) ) {
1236
            $wpi_requires_vat = self::requires_vat( 0, false );
1237
        }
1238
        
1239
        return $wpi_requires_vat ? __( self::get_vat_name(), 'invoicing' ) : ( $label ? $label : __( 'Tax', 'invoicing' ) );
1240
    }
1241
    
1242
    public static function standard_rates_label() {
1243
        return __( 'Standard Rates', 'invoicing' );
1244
    }
1245
    
1246
    public static function get_rate_classes( $with_desc = false ) {        
1247
        $rate_classes_option = get_option( '_wpinv_vat_rate_classes', true );
1248
        $classes = maybe_unserialize( $rate_classes_option );
1249
        
1250
        if ( empty( $classes ) || !is_array( $classes ) ) {
1251
            $classes = array();
1252
        }
1253
1254
        $rate_classes = array();
1255
        if ( !array_key_exists( '_standard', $classes ) ) {
1256
            if ( $with_desc ) {
1257
                $rate_classes['_standard'] = array( 'name' => self::standard_rates_label(), 'desc' => __( 'EU member states standard VAT rates', 'invoicing' ) );
1258
            } else {
1259
                $rate_classes['_standard'] = self::standard_rates_label();
1260
            }
1261
        }
1262
        
1263
        foreach ( $classes as $key => $class ) {
1264
            $name = !empty( $class['name'] ) ? __( $class['name'], 'invoicing' ) : $key;
1265
            $desc = !empty( $class['desc'] ) ? __( $class['desc'], 'invoicing' ) : '';
1266
            
1267
            if ( $with_desc ) {
1268
                $rate_classes[$key] = array( 'name' => $name, 'desc' => $desc );
1269
            } else {
1270
                $rate_classes[$key] = $name;
1271
            }
1272
        }
1273
1274
        return $rate_classes;
1275
    }
1276
    
1277
    public static function get_all_classes() {        
1278
        $classes            = self::get_rate_classes();
1279
        $classes['_exempt'] = __( 'Exempt (0%)', 'invoicing' );
1280
        
1281
        return apply_filters( 'wpinv_vat_get_all_classes', $classes );
1282
    }
1283
    
1284
    public static function get_class_desc( $rate_class ) {        
1285
        $rate_classes = self::get_rate_classes( true );
1286
1287
        if ( !empty( $rate_classes ) && isset( $rate_classes[$rate_class] ) && isset( $rate_classes[$rate_class]['desc'] ) ) {
1288
            return $rate_classes[$rate_class]['desc'];
1289
        }
1290
        
1291
        return '';
1292
    }
1293
    
1294
    public static function get_vat_groups() {
1295
        $vat_groups = array(
1296
            'standard'      => 'Standard',
1297
            'reduced'       => 'Reduced',
1298
            'superreduced'  => 'Super Reduced',
1299
            'parking'       => 'Parking',
1300
            'increased'     => 'Increased'
1301
        );
1302
        
1303
        return apply_filters( 'wpinv_get_vat_groups', $vat_groups );
1304
    }
1305
1306
    public static function get_rules() {
1307
        $vat_rules = array(
1308
            'digital' => __( 'Digital Product', 'invoicing' ),
1309
            'physical' => __( 'Physical Product', 'invoicing' )
1310
        );
1311
        return apply_filters( 'wpinv_get_vat_rules', $vat_rules );
1312
    }
1313
1314
    public static function get_vat_rates( $class ) {
1315
        if ( $class === '_standard' ) {
1316
            return wpinv_get_tax_rates();
1317
        }
1318
1319
        $rates = self::get_non_standard_rates();
1320
1321
        return array_key_exists( $class, $rates ) ? $rates[$class] : array();
1322
    }
1323
1324
    public static function get_non_standard_rates() {
1325
        $option = get_option( 'wpinv_vat_rates', array());
1326
        return is_array( $option ) ? $option : array();
1327
    }
1328
    
1329
    public static function allow_vat_rules() {
1330
        return ( wpinv_use_taxes() && wpinv_get_option( 'apply_vat_rules' ) ? true : false );
1331
    }
1332
    
1333
    public static function allow_vat_classes() {
1334
        return false; // TODO
1335
        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...
1336
    }
1337
    
1338
    public static function get_item_class( $postID ) {
1339
        $class = get_post_meta( $postID, '_wpinv_vat_class', true );
1340
1341
        if ( empty( $class ) ) {
1342
            $class = '_standard';
1343
        }
1344
        
1345
        return apply_filters( 'wpinv_get_item_vat_class', $class, $postID );
1346
    }
1347
    
1348
    public static function item_class_label( $postID ) {        
1349
        $vat_classes = self::get_all_classes();
1350
        
1351
        $class = self::get_item_class( $postID );
1352
        $class = isset( $vat_classes[$class] ) ? $vat_classes[$class] : __( $class, 'invoicing' );
1353
        
1354
        return apply_filters( 'wpinv_item_class_label', $class, $postID );
1355
    }
1356
    
1357
    public static function get_item_rule( $postID ) {        
1358
        $rule_type = get_post_meta( $postID, '_wpinv_vat_rule', true );
1359
        
1360
        if ( empty( $rule_type ) ) {        
1361
            $rule_type = self::allow_vat_rules() ? 'digital' : 'physical';
1362
        }
1363
        
1364
        return apply_filters( 'wpinv_item_get_vat_rule', $rule_type, $postID );
1365
    }
1366
    
1367
    public static function item_rule_label( $postID ) {
1368
        $vat_rules  = self::get_rules();
1369
        $vat_rule   = self::get_item_rule( $postID );
1370
        $vat_rule   = isset( $vat_rules[$vat_rule] ) ? $vat_rules[$vat_rule] : $vat_rule;
1371
        
1372
        return apply_filters( 'wpinv_item_rule_label', $vat_rule, $postID );
1373
    }
1374
    
1375
    public static function item_has_digital_rule( $item_id = 0 ) {        
1376
        return self::get_item_rule( $item_id ) == 'digital' ? true : false;
1377
    }
1378
    
1379
    public static function invoice_has_digital_rule( $invoice = 0 ) {        
1380
        if ( !self::allow_vat_rules() ) {
1381
            return false;
1382
        }
1383
        
1384
        if ( empty( $invoice ) ) {
1385
            return true;
1386
        }
1387
        
1388
        if ( is_int( $invoice ) ) {
1389
            $invoice = new WPInv_Invoice( $invoice );
1390
        }
1391
        
1392
        if ( !( is_object( $invoice ) && is_a( $invoice, 'WPInv_Invoice' ) ) ) {
1393
            return true;
1394
        }
1395
        
1396
        $cart_items  = $invoice->get_cart_details();
1397
        
1398
        if ( !empty( $cart_items ) ) {
1399
            $has_digital_rule = false;
1400
            
1401
            foreach ( $cart_items as $key => $item ) {
1402
                if ( self::item_has_digital_rule( $item['id'] ) ) {
1403
                    $has_digital_rule = true;
1404
                    break;
1405
                }
1406
            }
1407
        } else {
1408
            $has_digital_rule = true;
1409
        }
1410
        
1411
        return $has_digital_rule;
1412
    }
1413
    
1414
    public static function item_is_taxable( $item_id = 0, $country = false, $state = false ) {        
1415
        if ( !wpinv_use_taxes() ) {
1416
            return false;
1417
        }
1418
        
1419
        $is_taxable = true;
1420
1421
        if ( !empty( $item_id ) && self::get_item_class( $item_id ) == '_exempt' ) {
1422
            $is_taxable = false;
1423
        }
1424
        
1425
        return apply_filters( 'wpinv_item_is_taxable', $is_taxable, $item_id, $country , $state );
1426
    }
1427
    
1428
    public static function find_rate( $country, $state, $rate, $class ) {
1429
        global $wpi_zero_tax;
1430
1431
        if ( $class === '_exempt' || $wpi_zero_tax ) {
1432
            return 0;
1433
        }
1434
1435
        $tax_rates   = wpinv_get_tax_rates();
1436
        
1437
        if ( $class !== '_standard' ) {
1438
            $class_rates = self::get_vat_rates( $class );
1439
            
1440
            if ( is_array( $class_rates ) ) {
1441
                $indexed_class_rates = array();
1442
                
1443
                foreach ( $class_rates as $key => $cr ) {
1444
                    $indexed_class_rates[$cr['country']] = $cr;
1445
                }
1446
1447
                $tax_rates = array_map( function( $tr ) use( $indexed_class_rates ) {
1448
                    $tr_country = $tr['country'];
1449
                    if ( !isset( $indexed_class_rates[$tr_country] ) ) {
1450
                        return $tr;
1451
                    }
1452
                    $icr = $indexed_class_rates[$tr_country];
1453
                    return ( empty( $icr['rate'] ) && $icr['rate'] !== '0' ) ? $tr : $icr;
1454
1455
                }, $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

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

1590
    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...
1591
        global $wpi_userID;
1592
        
1593
        if ( empty($country) && !empty($wpi_userID) && get_current_user_id() != $wpi_userID ) {
1594
            $country = wpinv_get_default_country();
1595
        }
1596
        
1597
        return $country;
1598
    }
1599
    
1600
    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

1600
    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...
1601
        global $wpi_current_id, $wpi_userID;
1602
        
1603
        if ( !empty( $_POST['new_user'] ) ) {
1604
            return '';
1605
        }
1606
        
1607
        if ( empty( $user_id ) ) {
1608
            $user_id = !empty( $wpi_userID ) ? $wpi_userID : ( $wpi_current_id ? wpinv_get_user_id( $wpi_current_id ) : get_current_user_id() );
1609
        }
1610
1611
        $vat_number = empty( $user_id ) ? '' : get_user_meta( $user_id, '_wpinv_vat_number', true );
1612
        
1613
        /* TODO
1614
        if ( $is_valid && $vat_number ) {
1615
            $adddress_confirmed = empty( $user_id ) ? false : get_user_meta( $user_id, '_wpinv_adddress_confirmed', true );
1616
            if ( !$adddress_confirmed ) {
1617
                $vat_number = '';
1618
            }
1619
        }
1620
        */
1621
1622
        return apply_filters('wpinv_get_user_vat_number', $vat_number, $user_id, $is_valid );
1623
    }
1624
    
1625
    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

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

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

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

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

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

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

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

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