Passed
Push — master ( 68e600...883b53 )
by Brian
04:15
created

wpinv_regex_validate_vat_number()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 16
rs 10
1
<?php
2
/**
3
 * Contains the tax functions.
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * Returns the tax class objet.
11
 * 
12
 * @return WPInv_EUVat
13
 */
14
function getpaid_tax() {
15
    return getpaid()->tax;
16
}
17
18
/**
19
 * Checks if a given country is an EU state.
20
 * 
21
 * @return bool
22
 */
23
function getpaid_is_eu_state( $country ) {
24
    return WPInv_EUVat::is_eu_state( $country );
25
}
26
27
/**
28
 * Checks if a given country is GST country.
29
 * 
30
 * @return bool
31
 */
32
function getpaid_is_gst_country( $country ) {
33
    return WPInv_EUVat::is_gst_country( $country );
34
}
35
36
/**
37
 * Returns the vat name.
38
 * 
39
 * @return string
40
 */
41
function getpaid_vat_name() {
42
    return getpaid_tax()->get_vat_name();
43
}
44
45
/**
46
 * Checks whether or not taxes are enabled.
47
 *
48
 * @return bool
49
 */
50
function wpinv_use_taxes() {
51
52
    $ret = wpinv_get_option( 'enable_taxes', false );
53
    return (bool) apply_filters( 'wpinv_use_taxes', ! empty( $ret ) );
54
55
}
56
57
/**
58
 * Checks whether or not an invoice is taxable.
59
 *
60
 * @param WPInv_Invoice $invoice
61
 * @return bool
62
 */
63
function wpinv_is_invoice_taxable( $invoice ) {
64
    return $invoice->is_taxable();
65
}
66
67
/**
68
 * Checks whether or not a given country is taxable.
69
 *
70
 * @param string $country
71
 * @return bool
72
 */
73
function wpinv_is_country_taxable( $country ) {
74
    $is_eu     = getpaid_is_eu_state( $country );
75
    $is_exempt = $is_eu && $country == wpinv_is_base_country( $country ) && wpinv_same_country_exempt_vat();
76
    
77
    return (bool) apply_filters( 'wpinv_is_country_taxable', ! $is_exempt, $country ); 
78
79
}
80
81
/**
82
 * Checks whether or not an item is taxable.
83
 *
84
 * @param WPInv_Item|GetPaid_Form_Item $item
85
 * @return bool
86
 */
87
function wpinv_is_item_taxable( $item ) {
88
    return '_exempt' != $item->get_vat_rule();
89
}
90
91
/**
92
 * Checks whether or not taxes are calculated based on the store address.
93
 *
94
 * @return bool
95
 */
96
function wpinv_use_store_address_as_tax_base() {
97
    $use_base = wpinv_get_option( 'tax_base', 'billing' ) == 'base';
98
    return (bool) apply_filters( 'wpinv_use_store_address_as_tax_base', $use_base );
99
}
100
101
/**
102
 * Checks whether or not prices include tax.
103
 *
104
 * @return bool
105
 */
106
function wpinv_prices_include_tax() {
107
    $is_inclusive = wpinv_get_option( 'prices_include_tax', 'no' ) == 'yes';
108
    return (bool) apply_filters( 'wpinv_prices_include_tax', $is_inclusive );
109
}
110
111
/**
112
 * Checks whether we should round per rate or per subtotal
113
 *
114
 * @return bool
115
 */
116
function wpinv_round_tax_per_tax_rate() {
117
    $subtotal_rounding = wpinv_get_option( 'tax_subtotal_rounding', 1 );
118
    return (bool) apply_filters( 'wpinv_round_tax_per_tax_rate', empty( $subtotal_rounding ) );
119
}
120
121
/**
122
 * Checks whether we should display individual tax rates.
123
 *
124
 * @return bool
125
 */
126
function wpinv_display_individual_tax_rates() {
127
    $individual = wpinv_get_option( 'tax_display_totals', 'single' ) == 'individual';
128
    return (bool) apply_filters( 'wpinv_display_individual_tax_rates', $individual );
129
}
130
131
/**
132
 * Retrieves the default tax rate.
133
 *
134
 * @return float
135
 */
136
function wpinv_get_default_tax_rate() {
137
    $rate = wpinv_get_option( 'tax_rate', false );
138
    return (float) apply_filters( 'wpinv_get_default_tax_rate', floatval( $rate ) );
139
}
140
141
/**
142
 * Checks if we should exempt same country vat.
143
 *
144
 * @return bool
145
 */
146
function wpinv_same_country_exempt_vat() {
147
    return 'no' == wpinv_get_option( 'vat_same_country_rule' );
148
}
149
150
/**
151
 * Retrieves an array of all tax rates.
152
 *
153
 * @return array
154
 */
155
function wpinv_get_tax_rates() {
156
    return GetPaid_Tax::get_all_tax_rates();
157
}
158
159
/**
160
 * Retrieves an item's tax rates.
161
 *
162
 * @param WPInv_Item|GetPaid_Form_Item $item
163
 * @param string $country
164
 * @param string $state
165
 * @return array
166
 */
167
function getpaid_get_item_tax_rates( $item, $country = '', $state = '' ) {
168
169
    // Abort if the item is not taxable.
170
    if ( ! wpinv_is_item_taxable( $item ) ) {
171
        return array();
172
    }
173
174
    // Maybe use the store address.
175
    if ( wpinv_use_store_address_as_tax_base() ) {
176
        $country = wpinv_get_default_country();
177
        $state   = wpinv_get_default_state();
178
    }
179
180
    // Retrieve tax rates.
181
    $tax_rates = GetPaid_Tax::get_address_tax_rates( $country, $state );
182
183
    // Fallback to the default tax rates if non were found.
184
    if ( empty( $tax_rates ) ) {
185
        $tax_rates = GetPaid_Tax::get_default_tax_rates();
186
    }
187
188
    return apply_filters( 'getpaid_get_item_tax_rates', $tax_rates, $item, $country, $state );
189
}
190
191
/**
192
 * Retrieves an item's taxes.
193
 *
194
 * @param float $amount
195
 * @param array $rates
196
 * @return array
197
 */
198
function getpaid_calculate_item_taxes( $amount, $rates ) {
199
200
    $is_inclusive = wpinv_prices_include_tax();
201
    $taxes        = GetPaid_Tax::calc_tax( $amount, $rates, $is_inclusive );
202
203
    return apply_filters( 'getpaid_calculate_taxes', $taxes, $amount, $rates );
204
}
205
206
/**
207
 * Prepares an item's tax.
208
 *
209
 * @param WPInv_Item|GetPaid_Form_Item $item
210
 * @param string $tax_name
211
 * @param float $tax_amount
212
 * @param float $recurring_tax_amount
213
 * @return array
214
 */
215
function getpaid_prepare_item_tax( $item, $tax_name, $tax_amount, $recurring_tax_amount ) {
216
217
    $initial_tax   = $tax_amount;
218
	$recurring_tax = 0;
219
220
    if ( $item->is_recurring() ) {
221
		$recurring_tax = $recurring_tax_amount;
222
	}
223
224
	return array(
225
		'name'          => sanitize_text_field( $tax_name ),
226
		'initial_tax'   => $initial_tax,
227
		'recurring_tax' => $recurring_tax,
228
    );
229
230
}
231
232
/**
233
 * Sanitizes a VAT number.
234
 *
235
 * @param string $vat_number
236
 * @return string
237
 */
238
function wpinv_sanitize_vat_number( $vat_number ) {
239
    return str_replace( array(' ', '.', '-', '_', ',' ), '', strtoupper( trim( $vat_number ) ) );
240
}
241
242
/**
243
 * Validates a vat number via a REGEX.
244
 *
245
 * @param string $vat_number
246
 * @return bool
247
 */
248
function wpinv_regex_validate_vat_number( $vat_number ) {
249
250
    $country    = substr( $vat_number, 0, 2 );
251
    $vatin      = substr( $vat_number, 2 );
252
    $regexes    = wpinv_get_data( 'vat-number-regexes' );
253
254
    if ( isset( $regexes[ $country ] ) ) {
255
256
        $regex = $regexes[ $country ];
257
        $regex = '/^(?:' . $regex . ')$/';
258
        return 1 === preg_match( $regex, $vatin );
259
260
    }
261
262
    // Not an EU state, use filters to validate the number.
263
    return apply_filters( 'wpinv_regex_validate_vat_number', true, $vat_number );
264
}
265
266
/**
267
 * Validates a vat number via a VIES.
268
 *
269
 * @param string $vat_number
270
 * @return bool
271
 */
272
function wpinv_vies_validate_vat_number( $vat_number ) {
273
274
    $country    = substr( $vat_number, 0, 2 );
275
    $vatin      = substr( $vat_number, 2 );
276
277
    $url        = add_query_arg(
278
        array(
279
            'ms'  => urlencode( $country ),
280
            'iso' => urlencode( $country ),
281
            'vat' => urlencode( $vatin ),
282
        ),
283
        'http://ec.europa.eu/taxation_customs/vies/viesquer.do'
284
    );
285
286
    $response   = wp_remote_get( $url );
287
    $response   = wp_remote_retrieve_body( $response );
288
289
    // Fallback gracefully if the VIES website is down.
290
    if ( empty( $response ) ) {
291
        return true;
292
    }
293
294
    return 1 !== preg_match( '/invalid VAT number/i', $response );
295
296
}
297
298
/**
299
 * Validates a vat number.
300
 *
301
 * @param string $vat_number
302
 * @param string $country
303
 * @return bool
304
 */
305
function wpinv_validate_vat_number( $vat_number, $country ) {
306
    
307
    // Abort if we are not validating this.
308
    if ( ! wpinv_should_validate_vat_number() || empty( $vat_number ) ) {
309
        return true;
310
    }
311
312
    // In case the vat number does not have a country code...
313
    $vat_number = wpinv_sanitize_vat_number( $vat_number );
314
    $_country   = substr( $vat_number, 0, 2 );
315
    $_country   = $_country == wpinv_country_name( $_country );
316
317
    if ( $_country ) {
318
        $vat_number = strtoupper( $country ) . $vat_number;
319
    }
320
321
    return wpinv_regex_validate_vat_number( $vat_number ) && wpinv_vies_validate_vat_number( $vat_number );
322
}
323
324
/**
325
 * Checks whether or not we should validate vat numbers.
326
 *
327
 * @return bool
328
 */
329
function wpinv_should_validate_vat_number() {
330
    $validate = wpinv_get_option( 'validate_vat_number' );
331
	return ! empty( $validate );
332
}
333
334
function wpinv_sales_tax_for_year( $year = null ) {
335
    return wpinv_price( wpinv_format_amount( wpinv_get_sales_tax_for_year( $year ) ) );
0 ignored issues
show
Bug introduced by
wpinv_format_amount(wpin...es_tax_for_year($year)) of type string is incompatible with the type double expected by parameter $amount of wpinv_price(). ( Ignorable by Annotation )

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

335
    return wpinv_price( /** @scrutinizer ignore-type */ wpinv_format_amount( wpinv_get_sales_tax_for_year( $year ) ) );
Loading history...
336
}
337
338
function wpinv_get_sales_tax_for_year( $year = null ) {
339
    global $wpdb;
340
341
    // Start at zero
342
    $tax = 0;
343
344
    if ( ! empty( $year ) ) {
345
        $args = array(
346
            'post_type'      => 'wpi_invoice',
347
            'post_status'    => array( 'publish' ),
348
            'posts_per_page' => -1,
349
            'year'           => $year,
350
            'fields'         => 'ids'
351
        );
352
353
        $payments    = get_posts( $args );
354
        $payment_ids = implode( ',', $payments );
355
356
        if ( count( $payments ) > 0 ) {
357
            $sql = "SELECT SUM( meta_value ) FROM $wpdb->postmeta WHERE meta_key = '_wpinv_tax' AND post_id IN( $payment_ids )";
358
            $tax = $wpdb->get_var( $sql );
359
        }
360
361
    }
362
363
    return apply_filters( 'wpinv_get_sales_tax_for_year', $tax, $year );
364
}
365
366
function wpinv_is_cart_taxed() {
367
    return wpinv_use_taxes();
368
}
369
370
function wpinv_prices_show_tax_on_checkout() {
371
    return false; // TODO
372
    $ret = ( wpinv_get_option( 'checkout_include_tax', false ) == 'yes' && wpinv_use_taxes() );
0 ignored issues
show
Unused Code introduced by
$ret = wpinv_get_option(...s' && wpinv_use_taxes() is not reachable.

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

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

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

    return false;
}

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

Loading history...
373
374
    return apply_filters( 'wpinv_taxes_on_prices_on_checkout', $ret );
375
}
376
377
function wpinv_display_tax_rate() {
378
    $ret = wpinv_use_taxes() && wpinv_get_option( 'display_tax_rate', false );
379
380
    return apply_filters( 'wpinv_display_tax_rate', $ret );
381
}
382
383
function wpinv_cart_needs_tax_address_fields() {
384
    if( !wpinv_is_cart_taxed() )
385
        return false;
386
387
    return ! did_action( 'wpinv_after_cc_fields', 'wpinv_default_cc_address_fields' );
0 ignored issues
show
Unused Code introduced by
The call to did_action() has too many arguments starting with 'wpinv_default_cc_address_fields'. ( Ignorable by Annotation )

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

387
    return ! /** @scrutinizer ignore-call */ did_action( 'wpinv_after_cc_fields', 'wpinv_default_cc_address_fields' );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
388
}
389
390
function wpinv_item_is_tax_exclusive( $item_id = 0 ) {
391
    $ret = (bool)get_post_meta( $item_id, '_wpinv_tax_exclusive', false );
392
    return apply_filters( 'wpinv_is_tax_exclusive', $ret, $item_id );
393
}
394
395
function wpinv_currency_decimal_filter( $decimals = 2 ) {
396
    $currency = wpinv_get_currency();
397
398
    switch ( $currency ) {
399
        case 'RIAL' :
400
        case 'JPY' :
401
        case 'TWD' :
402
        case 'HUF' :
403
            $decimals = 0;
404
            break;
405
    }
406
407
    return apply_filters( 'wpinv_currency_decimal_count', $decimals, $currency );
408
}
409
410
function wpinv_tax_amount() {
411
    $output = 0.00;
412
    
413
    return apply_filters( 'wpinv_tax_amount', $output );
414
}
415
416
// VAT Settings
417
function wpinv_vat_rate_add_callback( $args ) {
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed. ( Ignorable by Annotation )

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

417
function wpinv_vat_rate_add_callback( /** @scrutinizer ignore-unused */ $args ) {

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

Loading history...
418
    ?>
419
    <p class="wpi-vat-rate-actions"><input id="wpi_vat_rate_add" type="button" value="<?php esc_attr_e( 'Add', 'invoicing' );?>" class="button button-primary" />&nbsp;&nbsp;<i style="display:none;" class="fa fa-refresh fa-spin"></i></p>
420
    <?php
421
}
422
423
function wpinv_vat_rate_delete_callback( $args ) {
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed. ( Ignorable by Annotation )

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

423
function wpinv_vat_rate_delete_callback( /** @scrutinizer ignore-unused */ $args ) {

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

Loading history...
424
    global $wpinv_euvat;
425
    
426
    $vat_classes = $wpinv_euvat->get_rate_classes();
427
    $vat_class = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '';
428
    if ( isset( $vat_classes[$vat_class] ) ) {
429
    ?>
430
    <p class="wpi-vat-rate-actions"><input id="wpi_vat_rate_delete" type="button" value="<?php echo wp_sprintf( esc_attr__( 'Delete class "%s"', 'invoicing' ), $vat_classes[$vat_class] );?>" class="button button-primary" />&nbsp;&nbsp;<i style="display:none;" class="fa fa-refresh fa-spin"></i></p>
431
    <?php
432
    }
433
}
434
435
function wpinv_vat_rates_callback( $args ) {
436
    global $wpinv_euvat;
437
    
438
    $vat_classes    = $wpinv_euvat->get_rate_classes();
439
    $vat_class      = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '_standard';
440
    
441
    $eu_states      = $wpinv_euvat->get_eu_states();
442
    $countries      = wpinv_get_country_list();
443
    $vat_groups     = $wpinv_euvat->get_vat_groups();
444
    $rates          = $wpinv_euvat->get_vat_rates( $vat_class );
445
    ob_start();
446
?>
447
</td><tr>
448
    <td colspan="2" class="wpinv_vat_tdbox">
449
    <input type="hidden" name="wpi_vat_class" value="<?php echo $vat_class;?>" />
450
    <p><?php echo ( isset( $args['desc'] ) ? $args['desc'] : '' ); ?></p>
451
    <table id="wpinv_vat_rates" class="wp-list-table widefat fixed posts">
452
        <colgroup>
453
            <col width="50px" />
454
            <col width="auto" />
455
            <col width="auto" />
456
            <col width="auto" />
457
            <col width="auto" />
458
            <col width="auto" />
459
        </colgroup>
460
        <thead>
461
            <tr>
462
                <th scope="col" colspan="2" class="wpinv_vat_country_name"><?php _e( 'Country', 'invoicing' ); ?></th>
463
                <th scope="col" class="wpinv_vat_global" title="<?php esc_attr_e( 'Apply rate to whole country', 'invoicing' ); ?>"><?php _e( 'Country Wide', 'invoicing' ); ?></th>
464
                <th scope="col" class="wpinv_vat_rate"><?php _e( 'Rate %', 'invoicing' ); ?></th> 
465
                <th scope="col" class="wpinv_vat_name"><?php _e( 'VAT Name', 'invoicing' ); ?></th>
466
                <th scope="col" class="wpinv_vat_group"><?php _e( 'Tax Group', 'invoicing' ); ?></th>
467
            </tr>
468
        </thead>
469
        <tbody>
470
        <?php if( !empty( $eu_states ) ) { ?>
471
        <?php 
472
        foreach ( $eu_states as $state ) { 
473
            $country_name = isset( $countries[$state] ) ? $countries[$state] : '';
474
            
475
            // Filter the rate for each country
476
            $country_rate = array_filter( $rates, function( $rate ) use( $state ) { return $rate['country'] === $state; } );
477
            
478
            // If one does not exist create a default
479
            $country_rate = is_array( $country_rate ) && count( $country_rate ) > 0 ? reset( $country_rate ) : array();
480
            
481
            $vat_global = isset( $country_rate['global'] ) ? !empty( $country_rate['global'] ) : true;
482
            $vat_rate = isset( $country_rate['rate'] ) ? $country_rate['rate'] : '';
483
            $vat_name = !empty( $country_rate['name'] ) ? esc_attr( stripslashes( $country_rate['name'] ) ) : '';
484
            $vat_group = !empty( $country_rate['group'] ) ? $country_rate['group'] : ( $vat_class === '_standard' ? 'standard' : 'reduced' );
485
        ?>
486
        <tr>
487
            <td class="wpinv_vat_country"><?php echo $state; ?><input type="hidden" name="vat_rates[<?php echo $state; ?>][country]" value="<?php echo $state; ?>" /><input type="hidden" name="vat_rates[<?php echo $state; ?>][state]" value="" /></td>
488
            <td class="wpinv_vat_country_name"><?php echo $country_name; ?></td>
489
            <td class="wpinv_vat_global">
490
                <input type="checkbox" name="vat_rates[<?php echo $state;?>][global]" id="vat_rates[<?php echo $state;?>][global]" value="1" <?php checked( true, $vat_global );?> disabled="disabled" />
491
                <label for="tax_rates[<?php echo $state;?>][global]"><?php _e( 'Apply to whole country', 'invoicing' ); ?></label>
492
                <input type="hidden" name="vat_rates[<?php echo $state;?>][global]" value="1" checked="checked" />
493
            </td>
494
            <td class="wpinv_vat_rate"><input type="number" class="small-text" step="any" min="0" max="99" name="vat_rates[<?php echo $state;?>][rate]" value="<?php echo $vat_rate; ?>" /></td>
495
            <td class="wpinv_vat_name"><input type="text" class="regular-text" name="vat_rates[<?php echo $state;?>][name]" value="<?php echo $vat_name; ?>" /></td>
496
            <td class="wpinv_vat_group">
497
            <?php
498
            echo wpinv_html_select( array(
499
                                        'name'             => 'vat_rates[' . $state . '][group]',
500
                                        'selected'         => $vat_group,
501
                                        'id'               => 'vat_rates[' . $state . '][group]',
502
                                        'class'            => 'wpi_select2',
503
                                        'options'          => $vat_groups,
504
                                        'multiple'         => false,
505
                                        'show_option_all'  => false,
506
                                        'show_option_none' => false
507
                                    ) );
508
            ?>
509
            </td>
510
        </tr>
511
        <?php } ?>
512
        <tr>
513
            <td colspan="6" style="background-color:#fafafa;">
514
                <span><input id="wpi_vat_get_rates_group" type="button" class="button-secondary" value="<?php esc_attr_e( 'Update EU VAT Rates', 'invoicing' ); ?>" />&nbsp;&nbsp;<i style="display:none" class="fa fa-refresh fa-spin"></i></span><span id="wpinv-rates-error-wrap" class="wpinv_errors" style="display:none;"></span>
515
            </td>
516
        </tr>
517
        <?php } ?>
518
        </tbody>
519
    </table>
520
    <?php
521
    $content = ob_get_clean();
522
    
523
    echo $content;
524
}
525
526
/**
527
 * Filters the VAT rules to ensure that each item has a VAT rule.
528
 * 
529
 * @param string|bool|null $vat_rule
530
 */
531
function getpaid_filter_vat_rule( $vat_rule ) {
532
533
    if ( empty( $vat_rule ) ) {        
534
        return 'digital';
535
    }
536
537
    return $vat_rule;
538
}
539
add_filter( 'wpinv_get_item_vat_rule', 'getpaid_filter_vat_rule' );
540
541
/**
542
 * Filters the VAT class to ensure that each item has a VAT class.
543
 * 
544
 * @param string|bool|null $vat_rule
545
 */
546
function getpaid_filter_vat_class( $vat_class ) {
547
    return empty( $vat_class ) ? '_standard' : $vat_class;
548
}
549
add_filter( 'wpinv_get_item_vat_class', 'getpaid_filter_vat_class' );
550