Passed
Push — master ( 919089...f59267 )
by Stiofan
41s queued 11s
created

WPInv_EUVat::enqueue_vat_scripts()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
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();
0 ignored issues
show
Documentation Bug introduced by
It seems like new self() of type object<WPInv_EUVat> is incompatible with the declared type boolean of property $instance.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
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['checkoutNonce'] = wp_create_nonce( 'wpinv_checkout_nonce' );
131
        $vars['baseCountry'] = wpinv_get_default_country();
132
        $vars['disableVATSameCountry'] = ( self::same_country_rule() == 'no' ? true : false );
133
        $vars['disableVATSimpleCheck'] = wpinv_get_option( 'vat_offline_check' ) ? true : false;
134
135
        wp_enqueue_script( 'wpinv-vat-validation-script' );
136
        wp_enqueue_script( 'wpinv-vat-script' );
137
        wp_localize_script( 'wpinv-vat-script', 'WPInv_VAT_Vars', $vars );
138
    }
139
140
    public static function enqueue_admin_scripts() {
141
        if( isset( $_GET['page'] ) && 'wpinv-settings' == $_GET['page'] ) {
142
            self::load_vat_scripts();
143
        }
144
    }
145
    
146
    public static function section_vat_settings( $sections ) {
147
        if ( !empty( $sections ) ) {
148
            $sections['vat'] = __( 'EU VAT Settings', 'invoicing' );
149
            
150
            if ( self::allow_vat_classes() ) {
151
                $sections['vat_rates'] = __( 'EU VAT Rates', 'invoicing' );
152
            }
153
        }
154
        return $sections;
155
    }
156
    
157
    public static function vat_rates_settings() {
158
        $vat_classes = self::get_rate_classes();
159
        $vat_rates = array();
160
        $vat_class = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '_new';
161
        $current_url = remove_query_arg( 'wpi_sub' );
162
        
163
        $vat_rates['vat_rates_header'] = array(
164
            'id' => 'vat_rates_header',
165
            'name' => '<h3>' . __( 'Manage VAT Rates', 'invoicing' ) . '</h3>',
166
            'desc' => '',
167
            'type' => 'header',
168
            'size' => 'regular'
169
        );
170
        $vat_rates['vat_rates_class'] = array(
171
            'id'          => 'vat_rates_class',
172
            'name'        => __( 'Edit VAT Rates', 'invoicing' ),
173
            'desc'        => __( 'The standard rate will apply where no explicit rate is provided.', 'invoicing' ),
174
            'type'        => 'select',
175
            'options'     => array_merge( $vat_classes, array( '_new' => __( 'Add New Rate Class', 'invoicing' ) ) ),
176
            'placeholder' => __( 'Select a VAT Rate', 'invoicing' ),
177
            'selected'    => $vat_class,
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
            );
297
298
            $vat_settings['vat_checkout_title'] = array(
299
                'id' => 'vat_checkout_title',
300
                'name' => '<h3>' . __( 'Checkout Fields', 'invoicing' ) . '</h3>',
301
                'desc' => '',
302
                'type' => 'header',
303
                'size' => 'regular'
304
            );
305
306
            $vat_settings['vat_disable_fields'] = array(
307
                'id' => 'vat_disable_fields',
308
                'name' => __( 'Disable VAT Fields', 'invoicing' ),
309
                '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>',
310
                'type' => 'checkbox'
311
            );
312
313
            $vat_settings['vat_ip_lookup'] = array(
314
                'id'   => 'vat_ip_lookup',
315
                'name' => __( 'IP Country Look-up', 'invoicing' ),
316
                'type' => 'vat_ip_lookup',
317
                'size' => 'regular',
318
                'std' => 'default'
319
            );
320
321
            $vat_settings['hide_ip_address'] = array(
322
                'id' => 'hide_ip_address',
323
                'name' => __( 'Hide IP Info at Checkout', 'invoicing' ),
324
                'desc' => __( 'Hide the user IP info at checkout.', 'invoicing' ),
325
                'type' => 'checkbox'
326
            );
327
328
            $vat_settings['vat_ip_country_default'] = array(
329
                'id' => 'vat_ip_country_default',
330
                'name' => __( 'Enable IP Country as Default', 'invoicing' ),
331
                'desc' => __( 'Show the country of the users IP as the default country, otherwise the site default country will be used.', 'invoicing' ),
332
                'type' => 'checkbox'
333
            );
334
335
            $vat_settings['vies_validation_title'] = array(
336
                'id' => 'vies_validation_title',
337
                'name' => '<h3>' . __( 'VIES Validation', 'invoicing' ) . '</h3>',
338
                'desc' => '',
339
                'type' => 'header',
340
                'size' => 'regular'
341
            );
342
343
            $vat_settings['vat_vies_check'] = array(
344
                'id' => 'vat_vies_check',
345
                'name' => __( 'Disable VIES VAT ID Check', 'invoicing' ),
346
                '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>' ),
347
                'type' => 'checkbox'
348
            );
349
350
            $vat_settings['vat_disable_company_name_check'] = array(
351
                'id' => 'vat_disable_company_name_check',
352
                'name' => __( 'Disable VIES Name Check', 'invoicing' ),
353
                '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>' ),
354
                'type' => 'checkbox'
355
            );
356
357
            $vat_settings['vat_offline_check'] = array(
358
                'id' => 'vat_offline_check',
359
                'name' => __( 'Disable Basic Checks', 'invoicing' ),
360
                'desc' => __( 'Disable basic JS checks for correct format of VAT number. (Not Recommended)', 'invoicing' ),
361
                'type' => 'checkbox'
362
            );
363
            
364
365
            $settings['vat'] = $vat_settings;
366
            
367
            if ( self::allow_vat_classes() ) {
368
                $settings['vat_rates'] = self::vat_rates_settings();
369
            }
370
            
371
            $eu_fallback_rate = array(
372
                'id'   => 'eu_fallback_rate',
373
                'name' => '<h3>' . __( 'VAT rate for EU member states', 'invoicing' ) . '</h3>',
374
                'type' => 'eu_fallback_rate',
375
                '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' ),
376
                'std'  => '20',
377
                'size' => 'small'
378
            );
379
            $settings['rates']['eu_fallback_rate'] = $eu_fallback_rate;
380
        }
381
382
        return $settings;
383
    }
384
    // IP Geolocation
385
    public static function geoip2_download_database() {
386
        $upload_dir         = wp_upload_dir();
387
        
388
        $database_url       = 'http' . (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === 'on' ? 's' : '') . '://geolite.maxmind.com/download/geoip/database/';
389
        $destination_dir    = $upload_dir['basedir'] . '/invoicing';
390
        
391
        if ( !is_dir( $destination_dir ) ) { 
392
            mkdir( $destination_dir );
393
        }
394
        
395
        $database_files     = array(
396
            'country'   => array(
397
                'source'        => $database_url . 'GeoLite2-Country.mmdb.gz',
398
                'destination'   => $destination_dir . '/GeoLite2-Country.mmdb',
399
            ),
400
            'city'      => array(
401
                'source'        => $database_url . 'GeoLite2-City.mmdb.gz',
402
                'destination'   => $destination_dir . '/GeoLite2-City.mmdb',
403
            )
404
        );
405
406
        foreach( $database_files as $database => $files ) {
407
            $result = self::geoip2_download_file( $files['source'], $files['destination'] );
408
            
409
            if ( empty( $result['success'] ) ) {
410
                echo $result['message'];
411
                exit;
412
            }
413
            
414
            wpinv_update_option( 'wpinv_geoip2_date_updated', current_time( 'timestamp' ) );
415
            echo sprintf(__( 'GeoIP2 %s database updated successfully.', 'invoicing' ), $database ) . ' ';
416
        }
417
        
418
        exit;
419
    }
420
    
421
    public static function geoip2_download_file( $source_url, $destination_file ) {
422
        $success    = false;
423
        $message    = '';
424
        
425
        if ( !function_exists( 'download_url' ) ) {
426
            require_once( ABSPATH . 'wp-admin/includes/file.php' );
427
        }
428
429
        $temp_file  = download_url( $source_url );
430
        
431
        if ( is_wp_error( $temp_file ) ) {
432
            $message = sprintf( __( 'Error while downloading GeoIp2 database( %s ): %s', 'invoicing' ), $source_url, $temp_file->get_error_message() );
433
        } else {
434
            $handle = gzopen( $temp_file, 'rb' );
435
            
436
            if ( $handle ) {
437
                $fopen  = fopen( $destination_file, 'wb' );
438
                if ( $fopen ) {
439
                    while ( ( $data = gzread( $handle, 4096 ) ) != false ) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $data = gzread($handle, 4096) of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
440
                        fwrite( $fopen, $data );
441
                    }
442
443
                    gzclose( $handle );
444
                    fclose( $fopen );
445
                        
446
                    $success = true;
447
                } else {
448
                    gzclose( $handle );
449
                    $message = sprintf( __( 'Error could not open destination GeoIp2 database file for writing: %s', 'invoicing' ), $destination_file );
450
                }
451
            } else {
452
                $message = sprintf( __( 'Error could not open GeoIp2 database file for reading: %s', 'invoicing' ), $temp_file );
453
            }
454
            
455
            if ( file_exists( $temp_file ) ) {
456
                unlink( $temp_file );
457
            }
458
        }
459
        
460
        $return             = array();
461
        $return['success']  = $success;
462
        $return['message']  = $message;
463
464
        return $return;
465
    }
466
    
467
    public static function load_geoip2() {
468
        if ( defined( 'WPINV_GEOIP2_LODDED' ) ) {
469
            return;
470
        }
471
        
472
        if ( !class_exists( '\MaxMind\Db\Reader' ) ) {
473
            $maxmind_db_files = array(
474
                'Reader/Decoder.php',
475
                'Reader/InvalidDatabaseException.php',
476
                'Reader/Metadata.php',
477
                'Reader/Util.php',
478
                'Reader.php',
479
            );
480
            
481
            foreach ( $maxmind_db_files as $key => $file ) {
482
                require_once( WPINV_PLUGIN_DIR . 'includes/libraries/MaxMind/Db/' . $file );
483
            }
484
        }
485
        
486
        if ( !class_exists( '\GeoIp2\Database\Reader' ) ) {        
487
            $geoip2_files = array(
488
                'ProviderInterface.php',
489
                'Compat/JsonSerializable.php',
490
                'Database/Reader.php',
491
                'Exception/GeoIp2Exception.php',
492
                'Exception/AddressNotFoundException.php',
493
                'Exception/AuthenticationException.php',
494
                'Exception/HttpException.php',
495
                'Exception/InvalidRequestException.php',
496
                'Exception/OutOfQueriesException.php',
497
                'Model/AbstractModel.php',
498
                'Model/AnonymousIp.php',
499
                'Model/Country.php',
500
                'Model/City.php',
501
                'Model/ConnectionType.php',
502
                'Model/Domain.php',
503
                'Model/Enterprise.php',
504
                'Model/Insights.php',
505
                'Model/Isp.php',
506
                'Record/AbstractRecord.php',
507
                'Record/AbstractPlaceRecord.php',
508
                'Record/Country.php',
509
                'Record/City.php',
510
                'Record/Continent.php',
511
                'Record/Location.php',
512
                'Record/MaxMind.php',
513
                'Record/Postal.php',
514
                'Record/RepresentedCountry.php',
515
                'Record/Subdivision.php',
516
                'Record/Traits.php',
517
                'WebService/Client.php',
518
            );
519
            
520
            foreach ( $geoip2_files as $key => $file ) {
521
                require_once( WPINV_PLUGIN_DIR . 'includes/libraries/GeoIp2/' . $file );
522
            }
523
        }
524
525
        define( 'WPINV_GEOIP2_LODDED', true );
526
    }
527
528 View Code Duplication
    public static function geoip2_country_dbfile() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
529
        $upload_dir = wp_upload_dir();
530
531
        if ( !isset( $upload_dir['basedir'] ) ) {
532
            return false;
533
        }
534
535
        $filename = $upload_dir['basedir'] . '/invoicing/GeoLite2-Country.mmdb';
536
        if ( !file_exists( $filename ) ) {
537
            return false;
538
        }
539
        
540
        return $filename;
541
    }
542
543 View Code Duplication
    public static function geoip2_city_dbfile() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
544
        $upload_dir = wp_upload_dir();
545
546
        if ( !isset( $upload_dir['basedir'] ) ) {
547
            return false;
548
        }
549
550
        $filename = $upload_dir['basedir'] . '/invoicing/GeoLite2-City.mmdb';
551
        if ( !file_exists( $filename ) ) {
552
            return false;
553
        }
554
        
555
        return $filename;
556
    }
557
558 View Code Duplication
    public static function geoip2_country_reader() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
559
        try {
560
            self::load_geoip2();
561
562
            if ( $filename = self::geoip2_country_dbfile() ) {
563
                return new \GeoIp2\Database\Reader( $filename );
564
            }
565
        } catch( Exception $e ) {
566
            return false;
567
        }
568
        
569
        return false;
570
    }
571
572 View Code Duplication
    public static function geoip2_city_reader() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
573
        try {
574
            self::load_geoip2();
575
576
            if ( $filename = self::geoip2_city_dbfile() ) {
577
                return new \GeoIp2\Database\Reader( $filename );
578
            }
579
        } catch( Exception $e ) {
580
            return false;
581
        }
582
        
583
        return false;
584
    }
585
586 View Code Duplication
    public static function geoip2_country_record( $ip_address ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
587
        try {
588
            $reader = self::geoip2_country_reader();
589
590
            if ( $reader ) {
591
                $record = $reader->country( $ip_address );
592
                
593
                if ( !empty( $record->country->isoCode ) ) {
594
                    return $record;
595
                }
596
            }
597
        } catch(\InvalidArgumentException $e) {
598
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
599
            
600
            return false;
601
        } catch(\GeoIp2\Exception\AddressNotFoundException $e) {
602
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
603
            
604
            return false;
605
        } catch( Exception $e ) {
606
            return false;
607
        }
608
        
609
        return false;
610
    }
611
612 View Code Duplication
    public static function geoip2_city_record( $ip_address ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
613
        try {
614
            $reader = self::geoip2_city_reader();
615
616
            if ( $reader ) {
617
                $record = $reader->city( $ip_address );
618
                
619
                if ( !empty( $record->country->isoCode ) ) {
620
                    return $record;
621
                }
622
            }
623
        } catch(\InvalidArgumentException $e) {
624
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
625
            
626
            return false;
627
        } catch(\GeoIp2\Exception\AddressNotFoundException $e) {
628
            wpinv_error_log( $e->getMessage(), 'GeoIp2 Lookup( ' . $ip_address . ' )' );
629
            
630
            return false;
631
        } catch( Exception $e ) {
632
            return false;
633
        }
634
        
635
        return false;
636
    }
637
638
    public static function geoip2_country_code( $ip_address ) {
639
        $record = self::geoip2_country_record( $ip_address );
640
        return !empty( $record->country->isoCode ) ? $record->country->isoCode : wpinv_get_default_country();
641
    }
642
643
    // Find country by IP address.
644
    public static function get_country_by_ip( $ip = '' ) {
645
        global $wpinv_ip_address_country;
646
        
647
        if ( !empty( $wpinv_ip_address_country ) ) {
648
            return $wpinv_ip_address_country;
649
        }
650
        
651
        if ( empty( $ip ) ) {
652
            $ip = wpinv_get_ip();
653
        }
654
655
        $ip_country_service = wpinv_get_option( 'vat_ip_lookup' );
656
        $is_default         = empty( $ip_country_service ) || $ip_country_service === 'default' ? true : false;
657
658
        if ( !empty( $ip ) && $ip !== '127.0.0.1' ) { // For 127.0.0.1(localhost) use default country.
659
            if ( function_exists( 'geoip_country_code_by_name') && ( $ip_country_service === 'geoip' || $is_default ) ) {
660
                try {
661
                    $wpinv_ip_address_country = geoip_country_code_by_name( $ip );
662
                } catch( Exception $e ) {
663
                    wpinv_error_log( $e->getMessage(), 'GeoIP Lookup( ' . $ip . ' )' );
664
                }
665
            } else if ( self::geoip2_country_dbfile() && ( $ip_country_service === 'geoip2' || $is_default ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::geoip2_country_dbfile() of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
666
                $wpinv_ip_address_country = self::geoip2_country_code( $ip );
667
            } else if ( function_exists( 'simplexml_load_file' ) && ( $ip_country_service === 'geoplugin' || $is_default ) ) {
668
                $load_xml = simplexml_load_file( 'http://www.geoplugin.net/xml.gp?ip=' . $ip );
669
                
670
                if ( !empty( $load_xml ) && !empty( $load_xml->geoplugin_countryCode ) ) {
671
                    $wpinv_ip_address_country = (string)$load_xml->geoplugin_countryCode;
672
                }
673
            }
674
        }
675
676
        if ( empty( $wpinv_ip_address_country ) ) {
677
            $wpinv_ip_address_country = wpinv_get_default_country();
678
        }
679
680
        return $wpinv_ip_address_country;
681
    }
682
    
683
    public static function sanitize_vat_settings( $input ) {
684
        global $wpinv_options;
685
        
686
        $valid      = false;
0 ignored issues
show
Unused Code introduced by
$valid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
687
        $message    = '';
0 ignored issues
show
Unused Code introduced by
$message is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
688
        
689
        if ( !empty( $wpinv_options['vat_vies_check'] ) ) {
690 View Code Duplication
            if ( empty( $wpinv_options['vat_offline_check'] ) ) {
691
                $valid = self::offline_check( $input['vat_number'] );
692
            } else {
693
                $valid = true;
694
            }
695
            
696
            $message = $valid ? '' : __( 'VAT number not validated', 'invoicing' );
697
        } else {
698
            $result = self::check_vat( $input['vat_number'] );
699
            
700
            if ( empty( $result['valid'] ) ) {
701
                $valid      = false;
702
                $message    = $result['message'];
703
            } else {
704
                $valid      = ( isset( $result['company'] ) && ( $result['company'] == '---' || ( strcasecmp( trim( $result['company'] ), trim( $input['vat_company_name'] ) ) == 0 ) ) ) || !empty( $wpinv_options['vat_disable_company_name_check'] );
705
                $message    = $valid ? '' : __( 'The company name associated with the VAT number provided is not the same as the company name provided.', 'invoicing' );
706
            }
707
        }
708
709
        if ( $message && self::is_vat_validated() != $valid ) {
710
            add_settings_error( 'wpinv-notices', '', $message, ( $valid ? 'updated' : 'error' ) );
711
        }
712
713
        $input['vat_valid'] = $valid;
714
        return $input;
715
    }
716
    
717
    public static function sanitize_vat_rates( $input ) {
718
        if( !current_user_can( 'manage_options' ) ) {
719
            add_settings_error( 'wpinv-notices', '', __( 'Your account does not have permission to add rate classes.', 'invoicing' ), 'error' );
720
            return $input;
721
        }
722
        
723
        $vat_classes = self::get_rate_classes();
724
        $vat_class = !empty( $_REQUEST['wpi_vat_class'] ) && isset( $vat_classes[$_REQUEST['wpi_vat_class']] )? sanitize_text_field( $_REQUEST['wpi_vat_class'] ) : '';
725
        
726
        if ( empty( $vat_class ) ) {
727
            add_settings_error( 'wpinv-notices', '', __( 'No valid VAT rates class contained in the request to save rates.', 'invoicing' ), 'error' );
728
            
729
            return $input;
730
        }
731
732
        $new_rates = ! empty( $_POST['vat_rates'] ) ? array_values( $_POST['vat_rates'] ) : array();
733
734
        if ( $vat_class === '_standard' ) {
735
            // Save the active rates in the invoice settings
736
            update_option( 'wpinv_tax_rates', $new_rates );
737
        } else {
738
            // Get the existing set of rates
739
            $rates = self::get_non_standard_rates();
740
            $rates[$vat_class] = $new_rates;
741
742
            update_option( 'wpinv_vat_rates', $rates );
743
        }
744
        
745
        return $input;
746
    }
747
    
748
    public static function add_class() {        
749
        $response = array();
750
        $response['success'] = false;
751
        
752 View Code Duplication
        if ( !current_user_can( 'manage_options' ) ) {
753
            $response['error'] = __( 'Invalid access!', 'invoicing' );
754
            wp_send_json( $response );
755
        }
756
        
757
        $vat_class_name = !empty( $_POST['name'] ) ? sanitize_text_field( $_POST['name'] ) : false;
758
        $vat_class_desc = !empty( $_POST['desc'] ) ? sanitize_text_field( $_POST['desc'] ) : false;
759
        
760
        if ( empty( $vat_class_name ) ) {
761
            $response['error'] = __( 'Select the VAT rate name', 'invoicing' );
762
            wp_send_json( $response );
763
        }
764
        
765
        $vat_classes = (array)self::get_rate_classes();
766
767 View Code Duplication
        if ( !empty( $vat_classes ) && in_array( strtolower( $vat_class_name ), array_map( 'strtolower', array_values( $vat_classes ) ) ) ) {
768
            $response['error'] = wp_sprintf( __( 'A VAT Rate name "%s" already exists', 'invoicing' ), $vat_class_name );
769
            wp_send_json( $response );
770
        }
771
        
772
        $rate_class_key = normalize_whitespace( 'wpi-' . $vat_class_name );
773
        $rate_class_key = sanitize_key( str_replace( " ", "-", $rate_class_key ) );
774
        
775
        $vat_classes = (array)self::get_rate_classes( true );
776
        $vat_classes[$rate_class_key] = array( 'name' => $vat_class_name, 'desc' => $vat_class_desc );
777
        
778
        update_option( '_wpinv_vat_rate_classes', $vat_classes );
779
        
780
        $response['success'] = true;
781
        $response['redirect'] = admin_url( 'admin.php?page=wpinv-settings&tab=taxes&section=vat_rates&wpi_sub=' . $rate_class_key );
782
        
783
        wp_send_json( $response );
784
    }
785
    
786
    public static function delete_class() {
787
        $response = array();
788
        $response['success'] = false;
789
        
790 View Code Duplication
        if ( !current_user_can( 'manage_options' ) || !isset( $_POST['class'] ) ) {
791
            $response['error'] = __( 'Invalid access!', 'invoicing' );
792
            wp_send_json( $response );
793
        }
794
        
795
        $vat_class = isset( $_POST['class'] ) && $_POST['class'] !== '' ? sanitize_text_field( $_POST['class'] ) : false;
796
        $vat_classes = (array)self::get_rate_classes();
797
798
        if ( !isset( $vat_classes[$vat_class] ) ) {
799
            $response['error'] = __( 'Requested class does not exists', 'invoicing' );
800
            wp_send_json( $response );
801
        }
802
        
803
        if ( $vat_class == '_new' || $vat_class == '_standard' ) {
804
            $response['error'] = __( 'You can not delete standard rates class', 'invoicing' );
805
            wp_send_json( $response );
806
        }
807
            
808
        $vat_classes = (array)self::get_rate_classes( true );
809
        unset( $vat_classes[$vat_class] );
810
        
811
        update_option( '_wpinv_vat_rate_classes', $vat_classes );
812
        
813
        $response['success'] = true;
814
        $response['redirect'] = admin_url( 'admin.php?page=wpinv-settings&tab=taxes&section=vat_rates&wpi_sub=_new' );
815
        
816
        wp_send_json( $response );
817
    }
818
    
819
    public static function update_eu_rates() {        
820
        $response               = array();
821
        $response['success']    = false;
822
        $response['error']      = null;
823
        $response['data']       = null;
824
        
825 View Code Duplication
        if ( !current_user_can( 'manage_options' ) ) {
826
            $response['error'] = __( 'Invalid access!', 'invoicing' );
827
            wp_send_json( $response );
828
        }
829
        
830
        $group      = !empty( $_POST['group'] ) ? sanitize_text_field( $_POST['group'] ) : '';
831
        $euvatrates = self::request_euvatrates( $group );
832
        
833
        if ( !empty( $euvatrates ) ) {
834
            if ( !empty( $euvatrates['success'] ) && !empty( $euvatrates['rates'] ) ) {
835
                $response['success']        = true;
836
                $response['data']['rates']  = $euvatrates['rates'];
837
            } else if ( !empty( $euvatrates['error'] ) ) {
838
                $response['error']          = $euvatrates['error'];
839
            }
840
        }
841
            
842
        wp_send_json( $response );
843
    }
844
    
845
    public static function hide_vat_fields() {
846
        $hide = wpinv_get_option( 'vat_disable_fields' );
847
        
848
        return apply_filters( 'wpinv_hide_vat_fields', $hide );
849
    }
850
    
851
    public static function same_country_rule() {
852
        $same_country_rule = wpinv_get_option( 'vat_same_country_rule' );
853
        
854
        return apply_filters( 'wpinv_vat_same_country_rule', $same_country_rule );
855
    }
856
    
857
    public static function get_vat_name() {
858
        $vat_name   = wpinv_get_option( 'vat_name' );
859
        $vat_name   = !empty( $vat_name ) ? $vat_name : 'VAT';
860
        
861
        return apply_filters( 'wpinv_get_owner_vat_name', $vat_name );
862
    }
863
    
864
    public static function get_company_name() {
865
        $company_name = wpinv_get_option( 'vat_company_name' );
866
        
867
        return apply_filters( 'wpinv_get_owner_company_name', $company_name );
868
    }
869
    
870
    public static function get_vat_number() {
871
        $vat_number = wpinv_get_option( 'vat_number' );
872
        
873
        return apply_filters( 'wpinv_get_owner_vat_number', $vat_number );
874
    }
875
    
876
    public static function is_vat_validated() {
877
        $validated = self::get_vat_number() && wpinv_get_option( 'vat_valid' );
878
        
879
        return apply_filters( 'wpinv_is_owner_vat_validated', $validated );
880
    }
881
    
882
    public static function sanitize_vat( $vat_number, $country_code = '' ) {        
883
        $vat_number = str_replace( array(' ', '.', '-', '_', ',' ), '', strtoupper( trim( $vat_number ) ) );
884
        
885
        if ( empty( $country_code ) ) {
886
            $country_code = substr( $vat_number, 0, 2 );
887
        }
888
        
889
        if ( strpos( $vat_number , $country_code ) === 0 ) {
890
            $vat = str_replace( $country_code, '', $vat_number );
891
        } else {
892
            $vat = $country_code . $vat_number;
893
        }
894
        
895
        $return                 = array();
896
        $return['vat']          = $vat;
897
        $return['iso']          = $country_code;
898
        $return['vat_number']   = $country_code . $vat;
899
        
900
        return $return;
901
    }
902
    
903
    public static function offline_check( $vat_number, $country_code = '', $formatted = false ) {
904
        $vat            = self::sanitize_vat( $vat_number, $country_code );
905
        $vat_number     = $vat['vat_number'];
906
        $country_code   = $vat['iso'];
907
        $regex          = array();
908
        
909
        switch ( $country_code ) {
910
            case 'AT':
911
                $regex[] = '/^(AT)U(\d{8})$/';                           // Austria
912
                break;
913
            case 'BE':
914
                $regex[] = '/^(BE)(0?\d{9})$/';                          // Belgium
915
                break;
916
            case 'BG':
917
                $regex[] = '/^(BG)(\d{9,10})$/';                         // Bulgaria
918
                break;
919
            case 'CH':
920
            case 'CHE':
921
                $regex[] = '/^(CHE)(\d{9})MWST$/';                       // Switzerland (Not EU)
922
                break;
923
            case 'CY':
924
                $regex[] = '/^(CY)([0-5|9]\d{7}[A-Z])$/';                // Cyprus
925
                break;
926
            case 'CZ':
927
                $regex[] = '/^(CZ)(\d{8,13})$/';                         // Czech Republic
928
                break;
929
            case 'DE':
930
                $regex[] = '/^(DE)([1-9]\d{8})$/';                       // Germany
931
                break;
932
            case 'DK':
933
                $regex[] = '/^(DK)(\d{8})$/';                            // Denmark
934
                break;
935
            case 'EE':
936
                $regex[] = '/^(EE)(10\d{7})$/';                          // Estonia
937
                break;
938
            case 'EL':
939
                $regex[] = '/^(EL)(\d{9})$/';                            // Greece
940
                break;
941
            case 'ES':
942
                $regex[] = '/^(ES)([A-Z]\d{8})$/';                       // Spain (National juridical entities)
943
                $regex[] = '/^(ES)([A-H|N-S|W]\d{7}[A-J])$/';            // Spain (Other juridical entities)
944
                $regex[] = '/^(ES)([0-9|Y|Z]\d{7}[A-Z])$/';              // Spain (Personal entities type 1)
945
                $regex[] = '/^(ES)([K|L|M|X]\d{7}[A-Z])$/';              // Spain (Personal entities type 2)
946
                break;
947
            case 'EU':
948
                $regex[] = '/^(EU)(\d{9})$/';                            // EU-type
949
                break;
950
            case 'FI':
951
                $regex[] = '/^(FI)(\d{8})$/';                            // Finland
952
                break;
953
            case 'FR':
954
                $regex[] = '/^(FR)(\d{11})$/';                           // France (1)
955
                $regex[] = '/^(FR)[(A-H)|(J-N)|(P-Z)](\d{10})$/';        // France (2)
956
                $regex[] = '/^(FR)\d[(A-H)|(J-N)|(P-Z)](\d{9})$/';       // France (3)
957
                $regex[] = '/^(FR)[(A-H)|(J-N)|(P-Z)]{2}(\d{9})$/';      // France (4)
958
                break;
959
            case 'GB':
960
                $regex[] = '/^(GB)?(\d{9})$/';                           // UK (Standard)
961
                $regex[] = '/^(GB)?(\d{12})$/';                          // UK (Branches)
962
                $regex[] = '/^(GB)?(GD\d{3})$/';                         // UK (Government)
963
                $regex[] = '/^(GB)?(HA\d{3})$/';                         // UK (Health authority)
964
                break;
965
            case 'GR':
966
                $regex[] = '/^(GR)(\d{8,9})$/';                          // Greece
967
                break;
968
            case 'HR':
969
                $regex[] = '/^(HR)(\d{11})$/';                           // Croatia
970
                break;
971
            case 'HU':
972
                $regex[] = '/^(HU)(\d{8})$/';                            // Hungary
973
                break;
974
            case 'IE':
975
                $regex[] = '/^(IE)(\d{7}[A-W])$/';                       // Ireland (1)
976
                $regex[] = '/^(IE)([7-9][A-Z\*\+)]\d{5}[A-W])$/';        // Ireland (2)
977
                $regex[] = '/^(IE)(\d{7}[A-Z][AH])$/';                   // Ireland (3) (new format from 1 Jan 2013)
978
                break;
979
            case 'IT':
980
                $regex[] = '/^(IT)(\d{11})$/';                           // Italy
981
                break;
982
            case 'LV':
983
                $regex[] = '/^(LV)(\d{11})$/';                           // Latvia
984
                break;
985
            case 'LT':
986
                $regex[] = '/^(LT)(\d{9}|\d{12})$/';                     // Lithuania
987
                break;
988
            case 'LU':
989
                $regex[] = '/^(LU)(\d{8})$/';                            // Luxembourg
990
                break;
991
            case 'MT':
992
                $regex[] = '/^(MT)([1-9]\d{7})$/';                       // Malta
993
                break;
994
            case 'NL':
995
                $regex[] = '/^(NL)(\d{9})B\d{2}$/';                      // Netherlands
996
                break;
997
            case 'NO':
998
                $regex[] = '/^(NO)(\d{9})$/';                            // Norway (Not EU)
999
                break;
1000
            case 'PL':
1001
                $regex[] = '/^(PL)(\d{10})$/';                           // Poland
1002
                break;
1003
            case 'PT':
1004
                $regex[] = '/^(PT)(\d{9})$/';                            // Portugal
1005
                break;
1006
            case 'RO':
1007
                $regex[] = '/^(RO)([1-9]\d{1,9})$/';                     // Romania
1008
                break;
1009
            case 'RS':
1010
                $regex[] = '/^(RS)(\d{9})$/';                            // Serbia (Not EU)
1011
                break;
1012
            case 'SI':
1013
                $regex[] = '/^(SI)([1-9]\d{7})$/';                       // Slovenia
1014
                break;
1015
            case 'SK':
1016
                $regex[] = '/^(SK)([1-9]\d[(2-4)|(6-9)]\d{7})$/';        // Slovakia Republic
1017
                break;
1018
            case 'SE':
1019
                $regex[] = '/^(SE)(\d{10}01)$/';                         // Sweden
1020
                break;
1021
            default:
1022
                $regex = array();
1023
            break;
1024
        }
1025
        
1026
        if ( empty( $regex ) ) {
1027
            return false;
1028
        }
1029
        
1030
        foreach ( $regex as $pattern ) {
1031
            $matches = null;
1032
            preg_match_all( $pattern, $vat_number, $matches );
1033
            
1034
            if ( !empty( $matches[1][0] ) && !empty( $matches[2][0] ) ) {
1035
                if ( $formatted ) {
1036
                    return array( 'code' => $matches[1][0], 'number' => $matches[2][0] );
1037
                } else {
1038
                    return true;
1039
                }
1040
            }
1041
        }
1042
        
1043
        return false;
1044
    }
1045
    
1046
    public static function vies_check( $vat_number, $country_code = '', $result = false ) {
1047
        $vat            = self::sanitize_vat( $vat_number, $country_code );
1048
        $vat_number     = $vat['vat'];
1049
        $iso            = $vat['iso'];
1050
        
1051
        $url = 'http://ec.europa.eu/taxation_customs/vies/viesquer.do?ms=' . urlencode( $iso ) . '&iso=' . urlencode( $iso ) . '&vat=' . urlencode( $vat_number );
1052
        
1053
        if ( ini_get( 'allow_url_fopen' ) ) {
1054
            $response = file_get_contents( $url );
1055
        } else if ( function_exists( 'curl_init' ) ) {
1056
            $ch = curl_init();
1057
            
1058
            curl_setopt( $ch, CURLOPT_URL, $url );
1059
            curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 30 );
1060
            curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1061
            curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
1062
            curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
1063
            
1064
            $response = curl_exec( $ch );
1065
            
1066
            if ( curl_errno( $ch ) ) {
1067
                wpinv_error_log( curl_error( $ch ), 'VIES CHECK ERROR' );
1068
                $response = '';
1069
            }
1070
            
1071
            curl_close( $ch );
1072
        } else {
1073
            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' );
1074
        }
1075
        
1076
        if ( empty( $response ) ) {
1077
            return $result;
1078
        }
1079
1080
        if ( preg_match( '/invalid VAT number/i', $response ) ) {
1081
            return false;
1082
        } else if ( preg_match( '/valid VAT number/i', $response, $matches ) ) {
1083
            $content = explode( "valid VAT number", htmlentities( $response ) );
1084
            
1085
            if ( !empty( $content[1] ) ) {
1086
                preg_match_all( '/<tr>(.*?)<td.*?>(.*?)<\/td>(.*?)<\/tr>/si', html_entity_decode( $content[1] ), $matches );
1087
                
1088
                if ( !empty( $matches[2] ) && $matches[3] ) {
1089
                    $return = array();
1090
                    
1091
                    foreach ( $matches[2] as $key => $label ) {
1092
                        $label = trim( $label );
1093
                        
1094
                        switch ( strtolower( $label ) ) {
1095
                            case 'member state':
1096
                                $return['state'] = trim( strip_tags( $matches[3][$key] ) );
1097
                            break;
1098
                            case 'vat number':
1099
                                $return['number'] = trim( strip_tags( $matches[3][$key] ) );
1100
                            break;
1101
                            case 'name':
1102
                                $return['company'] = trim( strip_tags( $matches[3][$key] ) );
1103
                            break;
1104
                            case 'address':
1105
                                $address           = str_replace( array( "<br><br>", "<br /><br />", "<br/><br/>" ), "<br>", html_entity_decode( trim( $matches[3][$key] ) ) );
1106
                                $return['address'] = trim( strip_tags( $address, '<br>' ) );
1107
                            break;
1108
                            case 'consultation number':
1109
                                $return['consultation'] = trim( strip_tags( $matches[3][$key] ) );
1110
                            break;
1111
                        }
1112
                    }
1113
                    
1114
                    if ( !empty( $return ) ) {
1115
                        return $return;
1116
                    }
1117
                }
1118
            }
1119
            
1120
            return true;
1121
        } else {
1122
            return $result;
1123
        }
1124
    }
1125
    
1126
    public static function check_vat( $vat_number, $country_code = '' ) {        
1127
        $vat_name           = self::get_vat_name();
1128
        
1129
        $return             = array();
1130
        $return['valid']    = false;
1131
        $return['message']  = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
1132
                
1133
        if ( !wpinv_get_option( 'vat_offline_check' ) && !self::offline_check( $vat_number, $country_code ) ) {
1134
            return $return;
1135
        }
1136
            
1137
        $response = self::vies_check( $vat_number, $country_code );
1138
        
1139
        if ( $response ) {
1140
            $return['valid']    = true;
1141
            
1142
            if ( is_array( $response ) ) {
1143
                $return['company'] = isset( $response['company'] ) ? $response['company'] : '';
1144
                $return['address'] = isset( $response['address'] ) ? $response['address'] : '';
1145
                $return['message'] = $return['company'] . '<br/>' . $return['address'];
1146
            }
1147
        } else {
1148
            $return['valid']    = false;
1149
            $return['message']  = wp_sprintf( __( 'Fail to validate the %s number: EU Commission VAT server (VIES) check fails.', 'invoicing' ), $vat_name );
1150
        }
1151
        
1152
        return $return;
1153
    }
1154
    
1155
    public static function request_euvatrates( $group ) {
1156
        $response               = array();
1157
        $response['success']    = false;
1158
        $response['error']      = null;
1159
        $response['eurates']    = null;
1160
        
1161
        $euvatrates_url = 'https://euvatrates.com/rates.json';
1162
        $euvatrates_url = apply_filters( 'wpinv_euvatrates_url', $euvatrates_url );
1163
        $api_response   = wp_remote_get( $euvatrates_url );
1164
    
1165
        try {
1166
            if ( is_wp_error( $api_response ) ) {
1167
                $response['error']      = __( $api_response->get_error_message(), 'invoicing' );
1168
            } else {
1169
                $body = json_decode( $api_response['body'] );
1170
                
1171
                if ( isset( $body->rates ) ) {
1172
                    $rates = array();
1173
                    
1174
                    foreach ( $body->rates as $country_code => $rate ) {
1175
                        $vat_rate = array();
1176
                        $vat_rate['country']        = $rate->country;
1177
                        $vat_rate['standard']       = (float)$rate->standard_rate;
1178
                        $vat_rate['reduced']        = (float)$rate->reduced_rate;
1179
                        $vat_rate['superreduced']   = (float)$rate->super_reduced_rate;
1180
                        $vat_rate['parking']        = (float)$rate->parking_rate;
1181
                        
1182
                        if ( $group !== '' && in_array( $group, array( 'standard', 'reduced', 'superreduced', 'parking' ) ) ) {
1183
                            $vat_rate_group = array();
1184
                            $vat_rate_group['country'] = $rate->country;
1185
                            $vat_rate_group[$group]    = $vat_rate[$group];
1186
                            
1187
                            $vat_rate = $vat_rate_group;
1188
                        }
1189
                        
1190
                        $rates[$country_code] = $vat_rate;
1191
                    }
1192
                    
1193
                    $response['success']    = true;                                
1194
                    $response['rates']      = apply_filters( 'wpinv_process_euvatrates', $rates, $api_response, $group );
1195
                } else {
1196
                    $response['error']      = __( 'No EU rates found!', 'invoicing' );
1197
                }
1198
            }
1199
        } catch ( Exception $e ) {
1200
            $response['error'] = __( $e->getMessage(), 'invoicing' );
1201
        }
1202
        
1203
        return apply_filters( 'wpinv_response_euvatrates', $response, $group );
1204
    }    
1205
    
1206
    public static function requires_vat( $requires_vat = false, $user_id = 0, $is_digital = null ) {
1207
        global $wpi_item_id, $wpi_country;
1208
        
1209
        if ( !empty( $_POST['wpinv_country'] ) ) {
1210
            $country_code = trim( $_POST['wpinv_country'] );
1211
        } else if ( !empty( $_POST['country'] ) ) {
1212
            $country_code = trim( $_POST['country'] );
1213
        } else if ( !empty( $wpi_country ) ) {
1214
            $country_code = $wpi_country;
1215
        } else {
1216
            $country_code = self::get_user_country( '', $user_id );
1217
        }
1218
        
1219
        if ( $is_digital === null && $wpi_item_id ) {
1220
            $is_digital = $wpi_item_id ? self::item_has_digital_rule( $wpi_item_id ) : self::allow_vat_rules();
1221
        }
1222
        
1223
        if ( !empty( $country_code ) ) {
1224
            $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 ) );
1225
        }
1226
        
1227
        return apply_filters( 'wpinv_requires_vat', $requires_vat, $user_id );
1228
    }
1229
    
1230
    public static function tax_label( $label = '' ) {
1231
        global $wpi_requires_vat;
1232
        
1233
        if ( !( $wpi_requires_vat !== 0 && $wpi_requires_vat ) ) {
1234
            $wpi_requires_vat = self::requires_vat( 0, false );
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
false is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1235
        }
1236
        
1237
        return $wpi_requires_vat ? __( self::get_vat_name(), 'invoicing' ) : ( $label ? $label : __( 'Tax', 'invoicing' ) );
1238
    }
1239
    
1240
    public static function standard_rates_label() {
1241
        return __( 'Standard Rates', 'invoicing' );
1242
    }
1243
    
1244
    public static function get_rate_classes( $with_desc = false ) {        
1245
        $rate_classes_option = get_option( '_wpinv_vat_rate_classes', true );
1246
        $classes = maybe_unserialize( $rate_classes_option );
1247
        
1248
        if ( empty( $classes ) || !is_array( $classes ) ) {
1249
            $classes = array();
1250
        }
1251
1252
        $rate_classes = array();
1253
        if ( !array_key_exists( '_standard', $classes ) ) {
1254
            if ( $with_desc ) {
1255
                $rate_classes['_standard'] = array( 'name' => self::standard_rates_label(), 'desc' => __( 'EU member states standard VAT rates', 'invoicing' ) );
1256
            } else {
1257
                $rate_classes['_standard'] = self::standard_rates_label();
1258
            }
1259
        }
1260
        
1261
        foreach ( $classes as $key => $class ) {
1262
            $name = !empty( $class['name'] ) ? __( $class['name'], 'invoicing' ) : $key;
1263
            $desc = !empty( $class['desc'] ) ? __( $class['desc'], 'invoicing' ) : '';
1264
            
1265
            if ( $with_desc ) {
1266
                $rate_classes[$key] = array( 'name' => $name, 'desc' => $desc );
1267
            } else {
1268
                $rate_classes[$key] = $name;
1269
            }
1270
        }
1271
1272
        return $rate_classes;
1273
    }
1274
    
1275
    public static function get_all_classes() {        
1276
        $classes            = self::get_rate_classes();
1277
        $classes['_exempt'] = __( 'Exempt (0%)', 'invoicing' );
1278
        
1279
        return apply_filters( 'wpinv_vat_get_all_classes', $classes );
1280
    }
1281
    
1282
    public static function get_class_desc( $rate_class ) {        
1283
        $rate_classes = self::get_rate_classes( true );
1284
1285
        if ( !empty( $rate_classes ) && isset( $rate_classes[$rate_class] ) && isset( $rate_classes[$rate_class]['desc'] ) ) {
1286
            return $rate_classes[$rate_class]['desc'];
1287
        }
1288
        
1289
        return '';
1290
    }
1291
    
1292
    public static function get_vat_groups() {
1293
        $vat_groups = array(
1294
            'standard'      => 'Standard',
1295
            'reduced'       => 'Reduced',
1296
            'superreduced'  => 'Super Reduced',
1297
            'parking'       => 'Parking',
1298
            'increased'     => 'Increased'
1299
        );
1300
        
1301
        return apply_filters( 'wpinv_get_vat_groups', $vat_groups );
1302
    }
1303
1304
    public static function get_rules() {
1305
        $vat_rules = array(
1306
            'digital' => __( 'Digital Product', 'invoicing' ),
1307
            'physical' => __( 'Physical Product', 'invoicing' )
1308
        );
1309
        return apply_filters( 'wpinv_get_vat_rules', $vat_rules );
1310
    }
1311
1312
    public static function get_vat_rates( $class ) {
1313
        if ( $class === '_standard' ) {
1314
            return wpinv_get_tax_rates();
1315
        }
1316
1317
        $rates = self::get_non_standard_rates();
1318
1319
        return array_key_exists( $class, $rates ) ? $rates[$class] : array();
1320
    }
1321
1322
    public static function get_non_standard_rates() {
1323
        $option = get_option( 'wpinv_vat_rates', array());
1324
        return is_array( $option ) ? $option : array();
1325
    }
1326
    
1327
    public static function allow_vat_rules() {
1328
        return ( wpinv_use_taxes() && wpinv_get_option( 'apply_vat_rules' ) ? true : false );
1329
    }
1330
    
1331
    public static function allow_vat_classes() {
1332
        return false; // TODO
1333
        return ( wpinv_get_option( 'vat_allow_classes' ) ? true : false );
0 ignored issues
show
Unused Code introduced by
return wpinv_get_option(...asses') ? true : false; does not seem to be 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...
1334
    }
1335
    
1336
    public static function get_item_class( $postID ) {
1337
        $class = get_post_meta( $postID, '_wpinv_vat_class', true );
1338
1339
        if ( empty( $class ) ) {
1340
            $class = '_standard';
1341
        }
1342
        
1343
        return apply_filters( 'wpinv_get_item_vat_class', $class, $postID );
1344
    }
1345
    
1346
    public static function item_class_label( $postID ) {        
1347
        $vat_classes = self::get_all_classes();
1348
        
1349
        $class = self::get_item_class( $postID );
1350
        $class = isset( $vat_classes[$class] ) ? $vat_classes[$class] : __( $class, 'invoicing' );
1351
        
1352
        return apply_filters( 'wpinv_item_class_label', $class, $postID );
1353
    }
1354
    
1355
    public static function get_item_rule( $postID ) {        
1356
        $rule_type = get_post_meta( $postID, '_wpinv_vat_rule', true );
1357
        
1358
        if ( empty( $rule_type ) ) {        
1359
            $rule_type = self::allow_vat_rules() ? 'digital' : 'physical';
1360
        }
1361
        
1362
        return apply_filters( 'wpinv_item_get_vat_rule', $rule_type, $postID );
1363
    }
1364
    
1365
    public static function item_rule_label( $postID ) {
1366
        $vat_rules  = self::get_rules();
1367
        $vat_rule   = self::get_item_rule( $postID );
1368
        $vat_rule   = isset( $vat_rules[$vat_rule] ) ? $vat_rules[$vat_rule] : $vat_rule;
1369
        
1370
        return apply_filters( 'wpinv_item_rule_label', $vat_rule, $postID );
1371
    }
1372
    
1373
    public static function item_has_digital_rule( $item_id = 0 ) {        
1374
        return self::get_item_rule( $item_id ) == 'digital' ? true : false;
1375
    }
1376
    
1377
    public static function invoice_has_digital_rule( $invoice = 0 ) {        
1378
        if ( !self::allow_vat_rules() ) {
1379
            return false;
1380
        }
1381
        
1382
        if ( empty( $invoice ) ) {
1383
            return true;
1384
        }
1385
        
1386
        if ( is_int( $invoice ) ) {
1387
            $invoice = new WPInv_Invoice( $invoice );
0 ignored issues
show
Documentation introduced by
$invoice is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1388
        }
1389
        
1390
        if ( !( is_object( $invoice ) && is_a( $invoice, 'WPInv_Invoice' ) ) ) {
1391
            return true;
1392
        }
1393
        
1394
        $cart_items  = $invoice->get_cart_details();
1395
        
1396
        if ( !empty( $cart_items ) ) {
1397
            $has_digital_rule = false;
1398
            
1399
            foreach ( $cart_items as $key => $item ) {
1400
                if ( self::item_has_digital_rule( $item['id'] ) ) {
1401
                    $has_digital_rule = true;
1402
                    break;
1403
                }
1404
            }
1405
        } else {
1406
            $has_digital_rule = true;
1407
        }
1408
        
1409
        return $has_digital_rule;
1410
    }
1411
    
1412
    public static function item_is_taxable( $item_id = 0, $country = false, $state = false ) {        
1413
        if ( !wpinv_use_taxes() ) {
1414
            return false;
1415
        }
1416
        
1417
        $is_taxable = true;
1418
1419
        if ( !empty( $item_id ) && self::get_item_class( $item_id ) == '_exempt' ) {
1420
            $is_taxable = false;
1421
        }
1422
        
1423
        return apply_filters( 'wpinv_item_is_taxable', $is_taxable, $item_id, $country , $state );
1424
    }
1425
    
1426
    public static function find_rate( $country, $state, $rate, $class ) {
1427
        global $wpi_zero_tax;
1428
1429
        if ( $class === '_exempt' || $wpi_zero_tax ) {
1430
            return 0;
1431
        }
1432
1433
        $tax_rates   = wpinv_get_tax_rates();
1434
        
1435
        if ( $class !== '_standard' ) {
1436
            $class_rates = self::get_vat_rates( $class );
1437
            
1438
            if ( is_array( $class_rates ) ) {
1439
                $indexed_class_rates = array();
1440
                
1441
                foreach ( $class_rates as $key => $cr ) {
1442
                    $indexed_class_rates[$cr['country']] = $cr;
1443
                }
1444
1445
                $tax_rates = array_map( function( $tr ) use( $indexed_class_rates ) {
1446
                    $tr_country = $tr['country'];
1447
                    if ( !isset( $indexed_class_rates[$tr_country] ) ) {
1448
                        return $tr;
1449
                    }
1450
                    $icr = $indexed_class_rates[$tr_country];
1451
                    return ( empty( $icr['rate'] ) && $icr['rate'] !== '0' ) ? $tr : $icr;
1452
1453
                }, $tax_rates, $class_rates );
1454
            }
1455
        }
1456
1457 View Code Duplication
        if ( !empty( $tax_rates ) ) {
1458
            foreach ( $tax_rates as $key => $tax_rate ) {
1459
                if ( $country != $tax_rate['country'] )
1460
                    continue;
1461
1462
                if ( !empty( $tax_rate['global'] ) ) {
1463
                    if ( 0 !== $tax_rate['rate'] || !empty( $tax_rate['rate'] ) ) {
1464
                        $rate = number_format( $tax_rate['rate'], 4 );
1465
                    }
1466
                } else {
1467
                    if ( empty( $tax_rate['state'] ) || strtolower( $state ) != strtolower( $tax_rate['state'] ) )
1468
                        continue;
1469
1470
                    $state_rate = $tax_rate['rate'];
1471
                    if ( 0 !== $state_rate || !empty( $state_rate ) ) {
1472
                        $rate = number_format( $state_rate, 4 );
1473
                    }
1474
                }
1475
            }
1476
        }
1477
        
1478
        return $rate;
1479
    }
1480
    
1481
    public static function get_rate( $rate = 1, $country = '', $state = '', $item_id = 0 ) {
1482
        global $wpinv_options, $wpi_session, $wpi_item_id, $wpi_zero_tax;
1483
        
1484
        $item_id = $item_id > 0 ? $item_id : $wpi_item_id;
1485
        $allow_vat_classes = self::allow_vat_classes();
1486
        $class = $item_id ? self::get_item_class( $item_id ) : '_standard';
1487
1488
        if ( $class === '_exempt' || $wpi_zero_tax ) {
1489
            return 0;
1490
        } else if ( !$allow_vat_classes ) {
1491
            $class = '_standard';
1492
        }
1493
1494
        if( !empty( $_POST['wpinv_country'] ) ) {
1495
            $post_country = $_POST['wpinv_country'];
1496
        } elseif( !empty( $_POST['wpinv_country'] ) ) {
1497
            $post_country = $_POST['wpinv_country'];
1498
        } elseif( !empty( $_POST['country'] ) ) {
1499
            $post_country = $_POST['country'];
1500
        } else {
1501
            $post_country = '';
1502
        }
1503
1504
        $country        = !empty( $post_country ) ? $post_country : wpinv_default_billing_country( $country );
1505
        $base_country   = wpinv_is_base_country( $country );
1506
        
1507
        $requires_vat   = self::requires_vat( 0, false );
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
false is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1508
        $is_digital     = self::get_item_rule( $item_id ) == 'digital' ;
1509
        $rate           = $requires_vat && isset( $wpinv_options['eu_fallback_rate'] ) ? $wpinv_options['eu_fallback_rate'] : $rate;
1510
          
1511
        if ( self::same_country_rule() == 'no' && $base_country ) { // Disable VAT to same country
1512
            $rate = 0;
1513
        } else if ( $requires_vat ) {
1514
            $vat_number = self::get_user_vat_number( '', 0, true );
1515
            $vat_info   = self::current_vat_data();
1516
            
1517
            if ( is_array( $vat_info ) ) {
1518
                $vat_number = isset( $vat_info['number'] ) && !empty( $vat_info['valid'] ) ? $vat_info['number'] : "";
1519
            }
1520
            
1521
            if ( $country == 'UK' ) {
1522
                $country = 'GB';
1523
            }
1524
1525
            if ( !empty( $vat_number ) ) {
1526
                $rate = 0;
1527
            } else {
1528
                $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
1529
            }
1530
1531
            if ( empty( $vat_number ) && !$is_digital ) {
1532
                if ( $base_country ) {
1533
                    $rate = self::find_rate( $country, null, $rate, $class );
1534 View Code Duplication
                } else {
1535
                    if ( empty( $country ) && isset( $wpinv_options['eu_fallback_rate'] ) ) {
1536
                        $rate = $wpinv_options['eu_fallback_rate'];
1537
                    } else if( !empty( $country ) ) {
1538
                        $rate = self::find_rate( $country, $state, $rate, $class );
1539
                    }
1540
                }
1541
            } else if ( empty( $vat_number ) || ( self::same_country_rule() == 'always' && $base_country ) ) {
1542 View Code Duplication
                if ( empty( $country ) && isset( $wpinv_options['eu_fallback_rate'] ) ) {
1543
                    $rate = $wpinv_options['eu_fallback_rate'];
1544
                } else if( !empty( $country ) ) {
1545
                    $rate = self::find_rate( $country, $state, $rate, $class );
1546
                }
1547
            }
1548
        } else {
1549
            if ( $is_digital ) {
1550
                $ip_country_code = self::get_country_by_ip();
1551
                
1552
                if ( $ip_country_code && self::is_eu_state( $ip_country_code ) ) {
1553
                    $rate = self::find_rate( $ip_country_code, '', 0, $class );
1554
                } else {
1555
                    $rate = self::find_rate( $country, $state, $rate, $class );
1556
                }
1557
            } else {
1558
                $rate = self::find_rate( $country, $state, $rate, $class );
1559
            }
1560
        }
1561
1562
        return $rate;
1563
    }
1564
    
1565
    public static function current_vat_data() {
1566
        global $wpi_session;
1567
        
1568
        return $wpi_session->get( 'user_vat_data' );
1569
    }
1570
    
1571
    public static function get_user_country( $country = '', $user_id = 0 ) {
1572
        $user_address = wpinv_get_user_address( $user_id, false );
1573
        
1574
        if ( wpinv_get_option( 'vat_ip_country_default' ) ) {
1575
            $country = '';
1576
        }
1577
        
1578
        $country    = empty( $user_address ) || !isset( $user_address['country'] ) || empty( $user_address['country'] ) ? $country : $user_address['country'];
1579
        $result     = apply_filters( 'wpinv_get_user_country', $country, $user_id );
1580
1581
        if ( empty( $result ) ) {
1582
            $result = self::get_country_by_ip();
1583
        }
1584
1585
        return $result;
1586
    }
1587
    
1588
    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.

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

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

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

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

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

Loading history...
1624
        global $wpi_current_id, $wpi_userID;
1625
        
1626 View Code Duplication
        if ( empty( $user_id ) ) {
1627
            $user_id = !empty( $wpi_userID ) ? $wpi_userID : ( $wpi_current_id ? wpinv_get_user_id( $wpi_current_id ) : get_current_user_id() );
1628
        }
1629
1630
        $company = empty( $user_id ) ? "" : get_user_meta( $user_id, '_wpinv_company', true );
1631
1632
        return apply_filters( 'wpinv_user_company', $company, $user_id );
1633
    }
1634
    
1635
    public static function save_user_vat_details( $company = '', $vat_number = '' ) {
1636
        $save = apply_filters( 'wpinv_allow_save_user_vat_details', true );
1637
1638
        if ( is_user_logged_in() && $save ) {
1639
            $user_id = get_current_user_id();
1640
1641
            if ( !empty( $vat_number ) ) {
1642
                update_user_meta( $user_id, '_wpinv_vat_number', $vat_number );
1643
            } else {
1644
                delete_user_meta( $user_id, '_wpinv_vat_number');
1645
            }
1646
1647
            if ( !empty( $company ) ) {
1648
                update_user_meta( $user_id, '_wpinv_company', $company );
1649
            } else {
1650
                delete_user_meta( $user_id, '_wpinv_company');
1651
                delete_user_meta( $user_id, '_wpinv_vat_number');
1652
            }
1653
        }
1654
1655
        do_action('wpinv_save_user_vat_details', $company, $vat_number);
1656
    }
1657
    
1658
    public static function ajax_vat_validate() {
1659
        global $wpinv_options, $wpi_session;
1660
        
1661
        $is_checkout            = ( !empty( $_POST['source'] ) && $_POST['source'] == 'checkout' ) ? true : false;
1662
        $response               = array();
1663
        $response['success']    = false;
1664
        
1665 View Code Duplication
        if ( empty( $_REQUEST['_wpi_nonce'] ) || ( !empty( $_REQUEST['_wpi_nonce'] ) && !wp_verify_nonce( $_REQUEST['_wpi_nonce'], 'vat_validation' ) ) ) {
1666
            $response['error'] = __( 'Invalid security nonce', 'invoicing' );
1667
            wp_send_json( $response );
1668
        }
1669
        
1670
        $vat_name   = self::get_vat_name();
1671
        
1672
        if ( $is_checkout ) {
1673
            $invoice = wpinv_get_invoice_cart();
1674
            
1675
            if ( !self::requires_vat( false, 0, self::invoice_has_digital_rule( $invoice ) ) ) {
0 ignored issues
show
Documentation introduced by
$invoice is of type object<WPInv_Invoice>|null, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1676
                $vat_info = array();
1677
                $wpi_session->set( 'user_vat_data', $vat_info );
1678
1679
                self::save_user_vat_details();
1680
                
1681
                $response['success'] = true;
1682
                $response['message'] = wp_sprintf( __( 'Ignore %s', 'invoicing' ), $vat_name );
1683
                wp_send_json( $response );
1684
            }
1685
        }
1686
        
1687
        $company    = !empty( $_POST['company'] ) ? sanitize_text_field( $_POST['company'] ) : '';
1688
        $vat_number = !empty( $_POST['number'] ) ? sanitize_text_field( $_POST['number'] ) : '';
1689
        
1690
        $vat_info = $wpi_session->get( 'user_vat_data' );
1691
        if ( !is_array( $vat_info ) || empty( $vat_info ) ) {
1692
            $vat_info = array( 'company'=> $company, 'number' => '', 'valid' => true );
1693
        }
1694
        
1695
        if ( empty( $vat_number ) ) {
1696
            if ( $is_checkout ) {
1697
                $response['success'] = true;
1698
                $response['message'] = wp_sprintf( __( 'No %s number has been applied. %s will be added to invoice totals', 'invoicing' ), $vat_name, $vat_name );
1699
                
1700
                $vat_info = $wpi_session->get( 'user_vat_data' );
1701
                $vat_info['number'] = "";
1702
                $vat_info['valid'] = true;
1703
                
1704
                self::save_user_vat_details( $company );
1705
            } else {
1706
                $response['error'] = wp_sprintf( __( 'Please enter your %s number!', 'invoicing' ), $vat_name );
1707
                
1708
                $vat_info['valid'] = false;
1709
            }
1710
1711
            $wpi_session->set( 'user_vat_data', $vat_info );
1712
            wp_send_json( $response );
1713
        }
1714
        
1715 View Code Duplication
        if ( empty( $company ) ) {
1716
            $vat_info['valid'] = false;
1717
            $wpi_session->set( 'user_vat_data', $vat_info );
1718
            
1719
            $response['error'] = __( 'Please enter your registered company name!', 'invoicing' );
1720
            wp_send_json( $response );
1721
        }
1722
        
1723
        if ( !empty( $wpinv_options['vat_vies_check'] ) ) {
1724 View Code Duplication
            if ( empty( $wpinv_options['vat_offline_check'] ) && !self::offline_check( $vat_number ) ) {
1725
                $vat_info['valid'] = false;
1726
                $wpi_session->set( 'user_vat_data', $vat_info );
1727
                
1728
                $response['error'] = wp_sprintf( __( '%s number not validated', 'invoicing' ), $vat_name );
1729
                wp_send_json( $response );
1730
            }
1731
            
1732
            $response['success'] = true;
1733
            $response['message'] = wp_sprintf( __( '%s number validated', 'invoicing' ), $vat_name );
1734
        } else {
1735
            $result = self::check_vat( $vat_number );
1736
            
1737
            if ( empty( $result['valid'] ) ) {
1738
                $response['error'] = $result['message'];
1739
                wp_send_json( $response );
1740
            }
1741
            
1742
            $vies_company = !empty( $result['company'] ) ? $result['company'] : '';
1743
            $vies_company = apply_filters( 'wpinv_vies_company_name', $vies_company );
1744
            
1745
            $valid_company = $vies_company && $company && ( $vies_company == '---' || strcasecmp( trim( $vies_company ), trim( $company ) ) == 0 ) ? true : false;
1746
1747
            if ( !empty( $wpinv_options['vat_disable_company_name_check'] ) || $valid_company ) {
1748
                $response['success'] = true;
1749
                $response['message'] = wp_sprintf( __( '%s number validated', 'invoicing' ), $vat_name );
1750 View Code Duplication
            } else {           
1751
                $vat_info['valid'] = false;
1752
                $wpi_session->set( 'user_vat_data', $vat_info );
1753
                
1754
                $response['success'] = false;
1755
                $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 );
1756
                wp_send_json( $response );
1757
            }
1758
        }
1759
        
1760
        if ( $is_checkout ) {
1761
            self::save_user_vat_details( $company, $vat_number );
1762
1763
            $vat_info = array('company' => $company, 'number' => $vat_number, 'valid' => true );
1764
            $wpi_session->set( 'user_vat_data', $vat_info );
1765
        }
1766
1767
        wp_send_json( $response );
1768
    }
1769
    
1770
    public static function ajax_vat_reset() {
1771
        global $wpi_session;
1772
        
1773
        $company    = is_user_logged_in() ? self::get_user_company() : '';
1774
        $vat_number = self::get_user_vat_number();
1775
        
1776
        $vat_info   = array('company' => $company, 'number' => $vat_number, 'valid' => false );
1777
        $wpi_session->set( 'user_vat_data', $vat_info );
1778
        
1779
        $response                       = array();
1780
        $response['success']            = true;
1781
        $response['data']['company']    = $company;
1782
        $response['data']['number']     = $vat_number;
1783
        
1784
        wp_send_json( $response );
1785
    }
1786
    
1787
    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.

This check looks from 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.

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

Loading history...
1788
        global $wpinv_options, $wpi_session;
1789
        
1790
        $vat_name  = __( self::get_vat_name(), 'invoicing' );
1791
        
1792
        if ( !isset( $_POST['_wpi_nonce'] ) || !wp_verify_nonce( $_POST['_wpi_nonce'], 'vat_validation' ) ) {
1793
            wpinv_set_error( 'vat_validation', wp_sprintf( __( "Invalid %s validation request.", 'invoicing' ), $vat_name ) );
1794
            return;
1795
        }
1796
        
1797
        $vat_saved = $wpi_session->get( 'user_vat_data' );
1798
        $wpi_session->set( 'user_vat_data', null );
1799
        
1800
        $invoice        = wpinv_get_invoice_cart();
1801
        $amount         = $invoice->get_total();
1802
        $is_digital     = self::invoice_has_digital_rule( $invoice );
0 ignored issues
show
Documentation introduced by
$invoice is of type object<WPInv_Invoice>|null, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1803
        $no_vat         = !self::requires_vat( 0, false, $is_digital );
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
false is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

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

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

Loading history...
1895
        global $wpi_session, $wpinv_options, $wpi_country, $wpi_requires_vat;
1896
        
1897
        $ip_address         = wpinv_get_ip();
1898
        $ip_country_code    = self::get_country_by_ip();
1899
        
1900
        $tax_label          = __( self::get_vat_name(), 'invoicing' );
1901
        $invoice            = wpinv_get_invoice_cart();
1902
        $is_digital         = self::invoice_has_digital_rule( $invoice );
0 ignored issues
show
Documentation introduced by
$invoice is of type object<WPInv_Invoice>|null, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1903
        $wpi_country        = $invoice->country;
1904
        
1905
        $requires_vat       = !self::hide_vat_fields() && !$invoice->is_free() && self::requires_vat( 0, false, $is_digital );
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
false is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

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