Passed
Push — master ( ed459e...214561 )
by Brian
09:30
created

getpaid_get_taxable_amount()   A

Complexity

Conditions 6
Paths 3

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 7
c 1
b 0
f 1
nc 3
nop 4
dl 0
loc 17
rs 9.2222
1
<?php
2
/**
3
 * Contains the tax functions.
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * Returns an array of eu states.
11
 * 
12
 * @return array
13
 */
14
function getpaid_get_eu_states() {
15
    return wpinv_get_data( 'eu-states' );
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 ! empty( $country ) && in_array( strtoupper( $country ), getpaid_get_eu_states() ) ? true : false;
25
}
26
27
/**
28
 * Returns an array of gst states.
29
 * 
30
 * @return array
31
 */
32
function getpaid_get_gst_states() {
33
    return array( 'AU', 'NZ', 'CA', 'CN' );
34
}
35
36
/**
37
 * Checks if a given country is GST country.
38
 * 
39
 * @return bool
40
 */
41
function getpaid_is_gst_country( $country ) {
42
    return ! empty( $country ) && in_array( strtoupper( $country ), getpaid_get_gst_states() ) ? true : false;
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', 20 );
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', 'always' );
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
 * Filters an item's tax rate.
193
 *
194
 * @param WPInv_Item|GetPaid_Form_Item $item
195
 * @param array $rates
196
 * @return array
197
 */
198
function getpaid_filter_item_tax_rates( $item, $rates ) {
199
200
    $tax_class = $item->get_vat_class();
201
202
    foreach ( $rates as $i => $rate ) {
203
204
        if ( $tax_class == '_reduced' ) {
205
            $rates[ $i ]['rate'] = empty( $rate['reduced_rate'] ) ? 0 : $rate['reduced_rate'];
206
        }
207
208
        if ( $tax_class == '_exempt' ) {
209
            $rates[ $i ]['rate'] = 0;
210
        }
211
212
    }
213
214
    return apply_filters( 'getpaid_filter_item_tax_rates', $rates, $item );
215
}
216
217
/**
218
 * Retrieves an item's taxes.
219
 *
220
 * @param float $amount
221
 * @param array $rates
222
 * @return array
223
 */
224
function getpaid_calculate_item_taxes( $amount, $rates ) {
225
226
    $is_inclusive = wpinv_prices_include_tax();
227
    $taxes        = GetPaid_Tax::calc_tax( $amount, $rates, $is_inclusive );
228
229
    return apply_filters( 'getpaid_calculate_taxes', $taxes, $amount, $rates );
230
}
231
232
/**
233
 * Prepares an item's tax.
234
 *
235
 * @param WPInv_Item|GetPaid_Form_Item $item
236
 * @param string $tax_name
237
 * @param float $tax_amount
238
 * @param float $recurring_tax_amount
239
 * @return array
240
 */
241
function getpaid_prepare_item_tax( $item, $tax_name, $tax_amount, $recurring_tax_amount ) {
242
243
    $initial_tax   = $tax_amount;
244
	$recurring_tax = 0;
245
246
    if ( $item->is_recurring() ) {
247
		$recurring_tax = $recurring_tax_amount;
248
	}
249
250
	return array(
251
		'name'          => sanitize_text_field( $tax_name ),
252
		'initial_tax'   => $initial_tax,
253
		'recurring_tax' => $recurring_tax,
254
    );
255
256
}
257
258
/**
259
 * Sanitizes a VAT number.
260
 *
261
 * @param string $vat_number
262
 * @return string
263
 */
264
function wpinv_sanitize_vat_number( $vat_number ) {
265
    return str_replace( array(' ', '.', '-', '_', ',' ), '', strtoupper( trim( $vat_number ) ) );
266
}
267
268
/**
269
 * Validates a vat number via a REGEX.
270
 *
271
 * @param string $vat_number
272
 * @return bool
273
 */
274
function wpinv_regex_validate_vat_number( $vat_number ) {
275
276
    $country    = substr( $vat_number, 0, 2 );
277
    $vatin      = substr( $vat_number, 2 );
278
    $regexes    = wpinv_get_data( 'vat-number-regexes' );
279
280
    if ( isset( $regexes[ $country ] ) ) {
281
282
        $regex = $regexes[ $country ];
283
        $regex = '/^(?:' . $regex . ')$/';
284
        return 1 === preg_match( $regex, $vatin );
285
286
    }
287
288
    // Not an EU state, use filters to validate the number.
289
    return apply_filters( 'wpinv_regex_validate_vat_number', true, $vat_number );
290
}
291
292
/**
293
 * Validates a vat number via a VIES.
294
 *
295
 * @param string $vat_number
296
 * @return bool
297
 */
298
function wpinv_vies_validate_vat_number( $vat_number ) {
299
300
    $country    = substr( $vat_number, 0, 2 );
301
    $vatin      = substr( $vat_number, 2 );
302
303
    $url        = add_query_arg(
304
        array(
305
            'ms'  => urlencode( $country ),
306
            'iso' => urlencode( $country ),
307
            'vat' => urlencode( $vatin ),
308
        ),
309
        'http://ec.europa.eu/taxation_customs/vies/viesquer.do'
310
    );
311
312
    $response   = wp_remote_get( $url );
313
    $response   = wp_remote_retrieve_body( $response );
314
315
    // Fallback gracefully if the VIES website is down.
316
    if ( empty( $response ) ) {
317
        return true;
318
    }
319
320
    return 1 !== preg_match( '/invalid VAT number/i', $response );
321
322
}
323
324
/**
325
 * Validates a vat number.
326
 *
327
 * @param string $vat_number
328
 * @param string $country
329
 * @return bool
330
 */
331
function wpinv_validate_vat_number( $vat_number, $country ) {
332
333
    // In case the vat number does not have a country code...
334
    $vat_number = wpinv_sanitize_vat_number( $vat_number );
335
    $_country   = substr( $vat_number, 0, 2 );
336
    $_country   = $_country == wpinv_country_name( $_country );
337
338
    if ( $_country ) {
339
        $vat_number = strtoupper( $country ) . $vat_number;
340
    }
341
342
    return wpinv_regex_validate_vat_number( $vat_number ) && wpinv_vies_validate_vat_number( $vat_number );
343
}
344
345
/**
346
 * Checks whether or not we should validate vat numbers.
347
 *
348
 * @return bool
349
 */
350
function wpinv_should_validate_vat_number() {
351
    $validate = wpinv_get_option( 'validate_vat_number' );
352
	return ! empty( $validate );
353
}
354
355
function wpinv_sales_tax_for_year( $year = null ) {
356
    return wpinv_price( wpinv_get_sales_tax_for_year( $year ) );
357
}
358
359
function wpinv_get_sales_tax_for_year( $year = null ) {
360
    global $wpdb;
361
362
    // Start at zero
363
    $tax = 0;
364
365
    if ( ! empty( $year ) ) {
366
        $args = array(
367
            'post_type'      => 'wpi_invoice',
368
            'post_status'    => array( 'publish' ),
369
            'posts_per_page' => -1,
370
            'year'           => $year,
371
            'fields'         => 'ids'
372
        );
373
374
        $payments    = get_posts( $args );
375
        $payment_ids = implode( ',', $payments );
376
377
        if ( count( $payments ) > 0 ) {
378
            $sql = "SELECT SUM( meta_value ) FROM $wpdb->postmeta WHERE meta_key = '_wpinv_tax' AND post_id IN( $payment_ids )";
379
            $tax = $wpdb->get_var( $sql );
380
        }
381
382
    }
383
384
    return apply_filters( 'wpinv_get_sales_tax_for_year', $tax, $year );
385
}
386
387
function wpinv_is_cart_taxed() {
388
    return wpinv_use_taxes();
389
}
390
391
function wpinv_prices_show_tax_on_checkout() {
392
    return false; // TODO
393
    $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...
394
395
    return apply_filters( 'wpinv_taxes_on_prices_on_checkout', $ret );
396
}
397
398
function wpinv_display_tax_rate() {
399
    $ret = wpinv_use_taxes() && wpinv_get_option( 'display_tax_rate', false );
400
401
    return apply_filters( 'wpinv_display_tax_rate', $ret );
402
}
403
404
function wpinv_cart_needs_tax_address_fields() {
405
    if( !wpinv_is_cart_taxed() )
406
        return false;
407
408
    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

408
    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...
409
}
410
411
function wpinv_item_is_tax_exclusive( $item_id = 0 ) {
412
    $ret = (bool)get_post_meta( $item_id, '_wpinv_tax_exclusive', false );
413
    return apply_filters( 'wpinv_is_tax_exclusive', $ret, $item_id );
414
}
415
416
function wpinv_currency_decimal_filter( $decimals = 2 ) {
417
    $currency = wpinv_get_currency();
418
419
    switch ( $currency ) {
420
        case 'RIAL' :
421
        case 'JPY' :
422
        case 'TWD' :
423
        case 'HUF' :
424
            $decimals = 0;
425
            break;
426
    }
427
428
    return apply_filters( 'wpinv_currency_decimal_count', $decimals, $currency );
429
}
430
431
function wpinv_tax_amount() {
432
    $output = 0.00;
433
    
434
    return apply_filters( 'wpinv_tax_amount', $output );
435
}
436
437
/**
438
 * Filters the VAT rules to ensure that each item has a VAT rule.
439
 * 
440
 * @param string|bool|null $vat_rule
441
 */
442
function getpaid_filter_vat_rule( $vat_rule ) {
443
444
    if ( empty( $vat_rule ) ) {        
445
        return 'digital';
446
    }
447
448
    return $vat_rule;
449
}
450
add_filter( 'wpinv_get_item_vat_rule', 'getpaid_filter_vat_rule' );
451
452
/**
453
 * Filters the VAT class to ensure that each item has a VAT class.
454
 * 
455
 * @param string|bool|null $vat_rule
456
 */
457
function getpaid_filter_vat_class( $vat_class ) {
458
    return empty( $vat_class ) ? '_standard' : $vat_class;
459
}
460
add_filter( 'wpinv_get_item_vat_class', 'getpaid_filter_vat_class' );
461
462
/**
463
 * Returns a list of all tax classes.
464
 * 
465
 * @return array
466
 */
467
function getpaid_get_tax_classes() {
468
469
    return apply_filters(
470
        'getpaid_tax_classes',
471
        array(
472
            '_standard' => __( 'Standard Tax Rate', 'invoicing' ),
473
            '_reduced'  => __( 'Reduced Tax Rate', 'invoicing' ),
474
            '_exempt'   => __( 'Tax Exempt', 'invoicing' ),
475
        )
476
    );
477
478
}
479
480
/**
481
 * Returns a list of all tax rules.
482
 * 
483
 * @return array
484
 */
485
function getpaid_get_tax_rules() {
486
487
    return apply_filters(
488
        'getpaid_tax_rules',
489
        array(
490
            'physical' => __( 'Physical Item', 'invoicing' ),
491
            'digital'  => __( 'Digital Item', 'invoicing' ),
492
        )
493
    );
494
495
}
496
497
/**
498
 * Returns the label of a tax class.
499
 * 
500
 * @param string $tax_class
501
 * @return string
502
 */
503
function getpaid_get_tax_class_label( $tax_class ) {
504
505
    $classes = getpaid_get_tax_classes();
506
507
    if ( isset( $classes[ $tax_class ] ) ) {
508
        return sanitize_text_field( $classes[ $tax_class ] );
509
    }
510
511
    return sanitize_text_field( $tax_class );
512
513
}
514
515
/**
516
 * Returns the label of a tax rule.
517
 * 
518
 * @param string $tax_rule
519
 * @return string
520
 */
521
function getpaid_get_tax_rule_label( $tax_rule ) {
522
523
    $rules = getpaid_get_tax_rules();
524
525
    if ( isset( $rules[ $tax_rule ] ) ) {
526
        return sanitize_text_field( $rules[ $tax_rule ] );
527
    }
528
529
    return sanitize_text_field( $tax_rule );
530
531
}
532
533
/**
534
 * Returns the taxable amount
535
 *
536
 * @param int $item_id
537
 * @param float $item_total
538
 * @param string $discount_code
539
 * @param string $recurring
540
 * @return string
541
 */
542
function getpaid_get_taxable_amount( $item_id, $item_total, $discount_code, $recurring = false ) {
543
544
    $taxable_amount = $item_total;
545
546
    // Do we have a $discount_code?
547
    if ( ! empty( $discount_code ) ) {
548
549
        $discount = new WPInv_Discount( $discount_code );
550
551
        if ( $discount->exists() && $discount->is_valid_for_items( $item_id ) && ( ! $recurring || $discount->is_recurring() ) ) {
552
            $taxable_amount = $item_total - $discount->get_discounted_amount( $item_total );
553
        }
554
555
    }
556
557
    $taxable_amount = max( 0, $taxable_amount );
558
    return apply_filters( 'getpaid_taxable_amount', $taxable_amount, $item_id, $item_total, $discount_code, $recurring );
559
}
560