Passed
Push — master ( 1a4515...55ed87 )
by Stiofan
02:32 queued 12s
created

wpinv-item-functions.php ➔ wpinv_get_cart_item_price()   C

Complexity

Conditions 12
Paths 32

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 32
nop 4
dl 0
loc 37
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
// Exit if accessed directly
3
if ( ! defined( 'ABSPATH' ) ) exit;
4
5
function wpinv_get_item_by( $field = '', $value = '', $type = '' ) {
6
    if( empty( $field ) || empty( $value ) ) {
7
        return false;
8
    }
9
    
10
    $posts = array();
0 ignored issues
show
Unused Code introduced by
$posts 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...
11
12
    switch( strtolower( $field ) ) {
13
        case 'id':
14
            $item = new WPInv_Item( $value );
0 ignored issues
show
Documentation introduced by
$value is of type string, 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...
15
16
            if ( !empty( $item ) && $item->post_type == 'wpi_item' ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
17
                return $item;
18
            }
19
            return false;
20
21
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
22
23
        case 'slug':
24
        case 'name':
25
            $posts = get_posts( array(
26
                'post_type'      => 'wpi_item',
27
                'name'           => $value,
28
                'posts_per_page' => 1,
29
                'post_status'    => 'any'
30
            ) );
31
32
            break;
33
        case 'custom_id':
34
            if ( empty( $value ) || empty( $type ) ) {
35
                return false;
36
            }
37
            
38
            $meta_query = array();
39
            $meta_query[] = array(
40
                'key'   => '_wpinv_type',
41
                'value' => $type,
42
            );
43
            $meta_query[] = array(
44
                'key'   => '_wpinv_custom_id',
45
                'value' => $value,
46
            );
47
            
48
            $args = array(
49
                'post_type'      => 'wpi_item',
50
                'posts_per_page' => 1,
51
                'post_status'    => 'any',
52
                'orderby'        => 'ID',
53
                'order'          => 'ASC',
54
                'meta_query'     => array( $meta_query )
55
            );
56
            
57
            $posts = get_posts( $args );
58
59
            break;
60
61
        default:
62
            return false;
63
    }
64
    
65
    if ( !empty( $posts[0] ) ) {
66
        $item = new WPInv_Item( $posts[0]->ID );
67
68
        if ( !empty( $item ) && $item->post_type == 'wpi_item' ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
69
            return $item;
70
        }
71
    }
72
73
    return false;
74
}
75
76
function wpinv_get_item( $item = 0 ) {
77
    if ( is_numeric( $item ) ) {
78
        $item = get_post( $item );
79
        if ( ! $item || 'wpi_item' !== $item->post_type )
80
            return null;
81
        return $item;
82
    }
83
84
    $args = array(
85
        'post_type'   => 'wpi_item',
86
        'name'        => $item,
87
        'numberposts' => 1
88
    );
89
90
    $item = get_posts($args);
91
92
    if ( $item ) {
93
        return $item[0];
94
    }
95
96
    return null;
97
}
98
99
function wpinv_is_free_item( $item_id = 0 ) {
100
    if( empty( $item_id ) ) {
101
        return false;
102
    }
103
104
    $item = new WPInv_Item( $item_id );
0 ignored issues
show
Documentation introduced by
$item_id 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...
105
    
106
    return $item->is_free();
107
}
108
109
function wpinv_item_is_editable( $item = 0 ) {
110
    if ( !empty( $item ) && is_a( $item, 'WP_Post' ) ) {
111
        $item = $item->ID;
112
    }
113
        
114
    if ( empty( $item ) ) {
115
        return true;
116
    }
117
118
    $item = new WPInv_Item( $item );
119
    
120
    return (bool) $item->is_editable();
121
}
122
123
function wpinv_get_item_price( $item_id = 0 ) {
124
    if( empty( $item_id ) ) {
125
        return false;
126
    }
127
128
    $item = new WPInv_Item( $item_id );
0 ignored issues
show
Documentation introduced by
$item_id 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...
129
    
130
    return $item->get_price();
131
}
132
133
function wpinv_is_recurring_item( $item_id = 0 ) {
134
    if( empty( $item_id ) ) {
135
        return false;
136
    }
137
138
    $item = new WPInv_Item( $item_id );
0 ignored issues
show
Documentation introduced by
$item_id 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...
139
    
140
    return $item->is_recurring();
141
}
142
143
function wpinv_item_price( $item_id = 0 ) {
144
    if( empty( $item_id ) ) {
145
        return false;
146
    }
147
148
    $price = wpinv_get_item_price( $item_id );
149
    $price = wpinv_price( wpinv_format_amount( $price ) );
150
    
151
    return apply_filters( 'wpinv_item_price', $price, $item_id );
152
}
153
154
function wpinv_item_show_price( $item_id = 0, $echo = true ) {
155
    if ( empty( $item_id ) ) {
156
        $item_id = get_the_ID();
157
    }
158
159
    $price = wpinv_item_price( $item_id );
160
161
    $price           = apply_filters( 'wpinv_item_price', wpinv_sanitize_amount( $price ), $item_id );
162
    $formatted_price = '<span class="wpinv_price" id="wpinv_item_' . $item_id . '">' . $price . '</span>';
163
    $formatted_price = apply_filters( 'wpinv_item_price_after_html', $formatted_price, $item_id, $price );
164
165
    if ( $echo ) {
166
        echo $formatted_price;
167
    } else {
168
        return $formatted_price;
169
    }
170
}
171
172
function wpinv_get_item_final_price( $item_id = 0, $amount_override = null ) {
173
    if ( is_null( $amount_override ) ) {
174
        $original_price = get_post_meta( $item_id, '_wpinv_price', true );
175
    } else {
176
        $original_price = $amount_override;
177
    }
178
    
179
    $price = $original_price;
180
181
    return apply_filters( 'wpinv_get_item_final_price', $price, $item_id );
182
}
183
184
function wpinv_item_custom_singular_name( $item_id ) {
185
    if( empty( $item_id ) ) {
186
        return false;
187
    }
188
189
    $item = new WPInv_Item( $item_id );
190
    
191
    return $item->get_custom_singular_name();
192
}
193
194
function wpinv_get_item_types() {
195
    $item_types = array(
196
            'custom'    => __( 'Standard', 'invoicing' ),
197
            'fee'       => __( 'Fee', 'invoicing' ),
198
        );
199
    return apply_filters( 'wpinv_get_item_types', $item_types );
200
}
201
202
function wpinv_item_types() {
203
    $item_types = wpinv_get_item_types();
204
    
205
    return ( !empty( $item_types ) ? array_keys( $item_types ) : array() );
206
}
207
208
function wpinv_get_item_type( $item_id ) {
209
    if( empty( $item_id ) ) {
210
        return false;
211
    }
212
213
    $item = new WPInv_Item( $item_id );
214
    
215
    return $item->get_type();
216
}
217
218
function wpinv_item_type( $item_id ) {
219
    $item_types = wpinv_get_item_types();
220
    
221
    $item_type = wpinv_get_item_type( $item_id );
222
    
223
    if ( empty( $item_type ) ) {
224
        $item_type = '-';
225
    }
226
    
227
    $item_type = isset( $item_types[$item_type] ) ? $item_types[$item_type] : __( $item_type, 'invoicing' );
228
229
    return apply_filters( 'wpinv_item_type', $item_type, $item_id );
230
}
231
232
function wpinv_record_item_in_log( $item_id = 0, $file_id, $user_info, $ip, $invoice_id ) {
233
    global $wpinv_logs;
234
    
235
    if ( empty( $wpinv_logs ) ) {
236
        return false;
237
    }
238
239
    $log_data = array(
240
        'post_parent'	=> $item_id,
241
        'log_type'		=> 'wpi_item'
242
    );
243
244
    $user_id = isset( $user_info['user_id'] ) ? $user_info['user_id'] : (int) -1;
245
246
    $log_meta = array(
247
        'user_info'	=> $user_info,
248
        'user_id'	=> $user_id,
249
        'file_id'	=> (int)$file_id,
250
        'ip'		=> $ip,
251
        'invoice_id'=> $invoice_id,
252
    );
253
254
    $wpinv_logs->insert_log( $log_data, $log_meta );
255
}
256
257
function wpinv_remove_item_logs_on_delete( $item_id = 0 ) {
258
    if ( 'wpi_item' !== get_post_type( $item_id ) )
259
        return;
260
261
    global $wpinv_logs;
262
    
263
    if ( empty( $wpinv_logs ) ) {
264
        return false;
265
    }
266
267
    // Remove all log entries related to this item
268
    $wpinv_logs->delete_logs( $item_id );
269
}
270
add_action( 'delete_post', 'wpinv_remove_item_logs_on_delete' );
271
272
function wpinv_get_random_item( $post_ids = true ) {
273
    wpinv_get_random_items( 1, $post_ids );
274
}
275
276
function wpinv_get_random_items( $num = 3, $post_ids = true ) {
277
    if ( $post_ids ) {
278
        $args = array( 'post_type' => 'wpi_item', 'orderby' => 'rand', 'post_count' => $num, 'fields' => 'ids' );
279
    } else {
280
        $args = array( 'post_type' => 'wpi_item', 'orderby' => 'rand', 'post_count' => $num );
281
    }
282
    
283
    $args  = apply_filters( 'wpinv_get_random_items', $args );
284
    
285
    return get_posts( $args );
286
}
287
288
function wpinv_get_item_token( $url = '' ) {
289
    $args    = array();
290
    $hash    = apply_filters( 'wpinv_get_url_token_algorithm', 'sha256' );
291
    $secret  = apply_filters( 'wpinv_get_url_token_secret', hash( $hash, wp_salt() ) );
292
293
    $parts   = parse_url( $url );
294
    $options = array();
295
296
    if ( isset( $parts['query'] ) ) {
297
        wp_parse_str( $parts['query'], $query_args );
0 ignored issues
show
Bug introduced by
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
298
299
        if ( ! empty( $query_args['o'] ) ) {
300
            $options = explode( ':', rawurldecode( $query_args['o'] ) );
301
302
            if ( in_array( 'ip', $options ) ) {
303
                $args['ip'] = wpinv_get_ip();
304
            }
305
306
            if ( in_array( 'ua', $options ) ) {
307
                $ua = wpinv_get_user_agent();
308
                $args['user_agent'] = rawurlencode( $ua );
309
            }
310
        }
311
    }
312
313
    $args = apply_filters( 'wpinv_get_url_token_args', $args, $url, $options );
314
315
    $args['secret'] = $secret;
316
    $args['token']  = false;
317
318
    $url   = add_query_arg( $args, $url );
319
    $parts = parse_url( $url );
320
321
    if ( ! isset( $parts['path'] ) ) {
322
        $parts['path'] = '';
323
    }
324
325
    $token = md5( $parts['path'] . '?' . $parts['query'] );
326
327
    return $token;
328
}
329
330
function wpinv_validate_url_token( $url = '' ) {
331
    $ret   = false;
332
    $parts = parse_url( $url );
333
334
    if ( isset( $parts['query'] ) ) {
335
        wp_parse_str( $parts['query'], $query_args );
0 ignored issues
show
Bug introduced by
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
336
337
        $allowed = apply_filters( 'wpinv_url_token_allowed_params', array(
338
            'item',
339
            'ttl',
340
            'token'
341
        ) );
342
343
        $remove = array();
344
345
        foreach( $query_args as $key => $value ) {
346
            if( false === in_array( $key, $allowed ) ) {
347
                $remove[] = $key;
348
            }
349
        }
350
351
        if( ! empty( $remove ) ) {
352
            $url = remove_query_arg( $remove, $url );
353
        }
354
355
        if ( isset( $query_args['ttl'] ) && current_time( 'timestamp' ) > $query_args['ttl'] ) {
356
            wp_die( apply_filters( 'wpinv_item_link_expired_text', __( 'Sorry but your item link has expired.', 'invoicing' ) ), __( 'Error', 'invoicing' ), array( 'response' => 403 ) );
357
        }
358
359
        if ( isset( $query_args['token'] ) && $query_args['token'] == wpinv_get_item_token( $url ) ) {
360
            $ret = true;
361
        }
362
363
    }
364
365
    return apply_filters( 'wpinv_validate_url_token', $ret, $url, $query_args );
366
}
367
368
function wpinv_item_in_cart( $item_id = 0, $options = array() ) {
369
    $cart_items = wpinv_get_cart_contents();
370
371
    $ret = false;
372
373
    if ( is_array( $cart_items ) ) {
374
        foreach ( $cart_items as $item ) {
375
            if ( $item['id'] == $item_id ) {
376
                $ret = true;
377
                break;
378
            }
379
        }
380
    }
381
382
    return (bool) apply_filters( 'wpinv_item_in_cart', $ret, $item_id, $options );
383
}
384
385
function wpinv_get_cart_item_tax( $item_id = 0, $subtotal = '', $options = array() ) {
386
    $tax = 0;
387
    if ( ! wpinv_item_is_tax_exclusive( $item_id ) ) {
388
        $country = !empty( $_POST['country'] ) ? $_POST['country'] : false;
389
        $state   = isset( $_POST['state'] ) ? $_POST['state'] : '';
390
391
        $tax = wpinv_calculate_tax( $subtotal, $country, $state, $item_id );
392
    }
393
394
    return apply_filters( 'wpinv_get_cart_item_tax', $tax, $item_id, $subtotal, $options );
395
}
396
397
function wpinv_cart_item_price( $item ) {
398
    $use_taxes  = wpinv_use_taxes();
0 ignored issues
show
Unused Code introduced by
$use_taxes 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...
399
    $item_id    = isset( $item['id'] ) ? $item['id'] : 0;
400
    $price      = isset( $item['item_price'] ) ? wpinv_round_amount( $item['item_price'] ) : 0;
401
    $options    = isset( $item['options'] ) ? $item['options'] : array();
402
    $price_id   = isset( $options['price_id'] ) ? $options['price_id'] : false;
403
    $tax        = wpinv_price( wpinv_format_amount( $item['tax'] ) );
404
    
405
    if ( !wpinv_is_free_item( $item_id, $price_id ) && !wpinv_item_is_tax_exclusive( $item_id ) ) {
0 ignored issues
show
Unused Code introduced by
The call to wpinv_is_free_item() has too many arguments starting with $price_id.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
406
        if ( wpinv_prices_show_tax_on_checkout() && !wpinv_prices_include_tax() ) {
407
            $price += $tax;
408
        }
409
        
410
        if( !wpinv_prices_show_tax_on_checkout() && wpinv_prices_include_tax() ) {
411
            $price -= $tax;
412
        }        
413
    }
414
415
    $price = wpinv_price( wpinv_format_amount( $price ) );
416
417
    return apply_filters( 'wpinv_cart_item_price_label', $price, $item );
418
}
419
420
function wpinv_cart_item_subtotal( $item ) {
421
    $subtotal   = isset( $item['subtotal'] ) ? $item['subtotal'] : 0;
422
    $subtotal   = wpinv_price( wpinv_format_amount( $subtotal ) );
423
424
    return apply_filters( 'wpinv_cart_item_subtotal_label', $subtotal, $item );
425
}
426
427
function wpinv_cart_item_tax( $item ) {
428
    $tax        = '';
429
    $tax_rate   = '';
430
    
431 View Code Duplication
    if ( isset( $item['tax'] ) && $item['tax'] > 0 && $item['subtotal'] > 0 ) {
432
        $tax      = wpinv_price( wpinv_format_amount( $item['tax'] ) );
433
        $tax_rate = !empty( $item['vat_rate'] ) ? $item['vat_rate'] : ( $item['tax'] / $item['subtotal'] ) * 100;
434
        $tax_rate = $tax_rate > 0 ? (float)wpinv_round_amount( $tax_rate, 4 ) : '';
435
        $tax_rate = $tax_rate != '' ? ' <small class="tax-rate normal small">(' . $tax_rate . '%)</small>' : '';
436
    }
437
    
438
    $tax        = $tax . $tax_rate;
439
    
440
    if ( $tax === '' ) {
441
        $tax = 0; // Zero tax
442
    }
443
444
    return apply_filters( 'wpinv_cart_item_tax_label', $tax, $item );
445
}
446
447
function wpinv_get_cart_item_price( $item_id = 0, $cart_item = array(), $options = array(), $remove_tax_from_inclusive = false ) {
448
    $price = 0;
449
    
450
    // Set custom price
451
    if ( isset( $cart_item['custom_price'] ) && $cart_item['custom_price'] !== '' ) {
452
        $price = $cart_item['custom_price'];
453
    } else {
454
        $variable_prices = wpinv_has_variable_prices( $item_id );
455
456
        if ( $variable_prices ) {
457
            $prices = wpinv_get_variable_prices( $item_id );
458
459
            if ( $prices ) {
460
                if( ! empty( $options ) ) {
461
                    $price = isset( $prices[ $options['price_id'] ] ) ? $prices[ $options['price_id'] ]['amount'] : false;
462
                } else {
463
                    $price = false;
464
                }
465
            }
466
        }
467
468
        if( ! $variable_prices || false === $price ) {
469
            if($cart_item['item_price'] > 0){
470
                $price = $cart_item['item_price'];
471
            } else {
472
                // Get the standard Item price if not using variable prices
473
                $price = wpinv_get_item_price( $item_id );
474
            }
475
        }
476
    }
477
478
    if ( $remove_tax_from_inclusive && wpinv_prices_include_tax() ) {
479
        $price -= wpinv_get_cart_item_tax( $item_id, $price, $options );
480
    }
481
482
    return apply_filters( 'wpinv_cart_item_price', $price, $item_id, $cart_item, $options, $remove_tax_from_inclusive );
483
}
484
485
function wpinv_get_cart_item_price_id( $item = array() ) {
486
    if( isset( $item['item_number'] ) ) {
487
        $price_id = isset( $item['item_number']['options']['price_id'] ) ? $item['item_number']['options']['price_id'] : null;
488
    } else {
489
        $price_id = isset( $item['options']['price_id'] ) ? $item['options']['price_id'] : null;
490
    }
491
    return $price_id;
492
}
493
494
function wpinv_get_cart_item_price_name( $item = array() ) {
495
    $price_id = (int)wpinv_get_cart_item_price_id( $item );
496
    $prices   = wpinv_get_variable_prices( $item['id'] );
497
    $name     = ! empty( $prices[ $price_id ] ) ? $prices[ $price_id ]['name'] : '';
498
    return apply_filters( 'wpinv_get_cart_item_price_name', $name, $item['id'], $price_id, $item );
499
}
500
501
function wpinv_get_cart_item_name( $item = array() ) {
502
    $item_title = !empty( $item['name'] ) ? $item['name'] : get_the_title( $item['id'] );
503
504
    if ( empty( $item_title ) ) {
505
        $item_title = $item['id'];
506
    }
507
508
    /*
509
    if ( wpinv_has_variable_prices( $item['id'] ) && false !== wpinv_get_cart_item_price_id( $item ) ) {
510
        $item_title .= ' - ' . wpinv_get_cart_item_price_name( $item );
511
    }
512
    */
513
514
    return apply_filters( 'wpinv_get_cart_item_name', $item_title, $item['id'], $item );
515
}
516
517
function wpinv_has_variable_prices( $item_id = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $item_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...
518
    return false;
519
}
520
521
function wpinv_get_item_position_in_cart( $item_id = 0, $options = array() ) {
522
    $cart_items = wpinv_get_cart_contents();
523
524
    if ( !is_array( $cart_items ) ) {
525
        return false; // Empty cart
526
    } else {
527
        foreach ( $cart_items as $position => $item ) {
528
            if ( $item['id'] == $item_id ) {
529
                if ( isset( $options['price_id'] ) && isset( $item['options']['price_id'] ) ) {
530
                    if ( (int) $options['price_id'] == (int) $item['options']['price_id'] ) {
531
                        return $position;
532
                    }
533
                } else {
534
                    return $position;
535
                }
536
            }
537
        }
538
    }
539
540
    return false; // Not found
541
}
542
543
function wpinv_get_cart_item_quantity( $item ) {
544
    if ( wpinv_item_quantities_enabled() ) {
545
        $quantity = !empty( $item['quantity'] ) && (int)$item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
546
    } else {
547
        $quantity = 1;
548
    }
549
    
550
    if ( $quantity < 1 ) {
551
        $quantity = 1;
552
    }
553
    
554
    return apply_filters( 'wpinv_get_cart_item_quantity', $quantity, $item );
555
}
556
557
function wpinv_get_item_suffix( $item, $html = true ) {
558
    if ( empty( $item ) ) {
559
        return NULL;
560
    }
561
    
562
    if ( is_int( $item ) ) {
563
        $item = new WPInv_Item( $item );
0 ignored issues
show
Documentation introduced by
$item 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...
564
    }
565
    
566
    if ( !( is_object( $item ) && is_a( $item, 'WPInv_Item' ) ) ) {
567
        return NULL;
568
    }
569
    
570
    $suffix = $item->is_recurring() ? ' <span class="wpi-suffix">' . __( '(r)', 'invoicing' ) . '</span>' : '';
571
    
572
    if ( !$html && $suffix ) {
573
        $suffix = strip_tags( $suffix );
574
    }
575
    
576
    return apply_filters( 'wpinv_get_item_suffix', $suffix, $item, $html );
577
}
578
579
function wpinv_remove_item( $item = 0, $force_delete = false ) {
580
    if ( empty( $item ) ) {
581
        return NULL;
582
    }
583
    
584
    if ( is_int( $item ) ) {
585
        $item = new WPInv_Item( $item );
0 ignored issues
show
Documentation introduced by
$item 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...
586
    }
587
    
588
    if ( !( is_object( $item ) && is_a( $item, 'WPInv_Item' ) ) ) {
589
        return NULL;
590
    }
591
    
592
    do_action( 'wpinv_pre_delete_item', $item );
593
594
    wp_delete_post( $item->ID, $force_delete );
595
596
    do_action( 'wpinv_post_delete_item', $item );
597
}
598
599
function wpinv_can_delete_item( $post_id ) {
600
    $return = current_user_can( 'manage_options' ) ? true : false;
601
    
602
    if ( $return && wpinv_item_in_use( $post_id ) ) {
603
        $return = false; // Don't delete item already use in invoices.
604
    }
605
    
606
    return apply_filters( 'wpinv_can_delete_item', $return, $post_id );
607
}
608
609
function wpinv_admin_action_delete() {
610
    $screen = get_current_screen();
611
    
612
    if ( !empty( $screen->post_type ) && $screen->post_type == 'wpi_item' && !empty( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] ) ) {
613
        $post_ids = array();
614
        
615
        foreach ( $_REQUEST['post'] as $post_id ) {
616
            if ( !wpinv_can_delete_item( $post_id ) ) {
617
                continue;
618
            }
619
            
620
            $post_ids[] = $post_id;
621
        }
622
        
623
        $_REQUEST['post'] = $post_ids;
624
    }
625
}
626
add_action( 'admin_action_trash', 'wpinv_admin_action_delete', -10 );
627
add_action( 'admin_action_delete', 'wpinv_admin_action_delete', -10 );
628
629
function wpinv_check_delete_item( $check, $post, $force_delete ) {
630
    if ( $post->post_type == 'wpi_item' ) {
631
        if ( $force_delete && !wpinv_can_delete_item( $post->ID ) ) {
632
            return true;
633
        }
634
    }
635
    
636
    return $check;
637
}
638
add_filter( 'pre_delete_post', 'wpinv_check_delete_item', 10, 3 );
639
640
function wpinv_item_in_use( $item_id ) {
641
    global $wpdb, $wpi_items_in_use;
642
    
643
    if ( !$item_id > 0 ) {
644
        return false;
645
    }
646
    
647
    if ( !empty( $wpi_items_in_use ) ) {
648
        if ( isset( $wpi_items_in_use[$item_id] ) ) {
649
            return $wpi_items_in_use[$item_id];
650
        }
651
    } else {
652
        $wpi_items_in_use = array();
653
    }
654
    
655
    $statuses   = array_keys( wpinv_get_invoice_statuses( true, true ) );
656
    
657
    $query  = "SELECT p.ID FROM " . $wpdb->posts . " AS p INNER JOIN " . $wpdb->postmeta . " AS pm ON p.ID = pm.post_id WHERE p.post_type = 'wpi_invoice' AND p.post_status IN( '" . implode( "','", $statuses ) . "' ) AND pm.meta_key = '_wpinv_item_ids' AND FIND_IN_SET( '" . (int)$item_id . "', pm.meta_value )";
658
    $in_use = $wpdb->get_var( $query ) > 0 ? true : false;
659
    
660
    $wpi_items_in_use[$item_id] = $in_use;
661
    
662
    return $in_use;
663
}
664
665
function wpinv_create_item( $args = array(), $wp_error = false, $force_update = false ) {
666
    // Set some defaults
667
    $defaults = array(
668
        'type'                 => 'custom',                                                // Optional. Item type. Default 'custom'.
669
        'title'                => '',                                                      // Required. Item title.
670
        'custom_id'            => 0,                                                       // Optional. Any integer or non numeric id. Must be unique within item type.
671
        'price'                => '0.00',                                                  // Optional. Item price. Default '0.00'.
672
        'status'               => 'pending',                                               // Optional. pending, publish
673
        'custom_name'          => '',                                                      // Optional. Plural sub title for item.
674
        'custom_singular_name' => '',                                                      // Optional. Singular sub title for item.
675
        'vat_rule'             => 'digital',                                               // Optional. digital => Digital item, physical => Physical item
676
        'editable'             => true,                                                    // Optional. Item editable from Items list page? Default true.
677
        'excerpt'              => '',                                                      // Optional. Item short description
678
        /* Recurring item fields */
679
        'is_recurring'         => 0,                                                       // Optional. 1 => Allow recurring or 0 => Don't allow recurring
680
        'recurring_period'     => 'M',                                                     // Optional. D => Daily, W => Weekly, M => Monthly, Y => Yearly
681
        'recurring_interval'   => 0,                                                       // Optional. Integer value between 1 - 90.
682
        'recurring_limit'      => 0,                                                       // Optional. Any integer number. 0 for recurring forever until cancelled.
683
        'free_trial'           => 0,                                                       // Optional. 1 => Allow free trial or 0 => Don't free trial
684
        'trial_period'         => 'M',                                                     // Optional. D => Daily, W => Weekly, M => Monthly, Y => Yearly
685
        'trial_interval'       => 0,                                                       // Optional. Any integer number.
686
    );
687
688
    $data = wp_parse_args( $args, $defaults );
689
690
    if ( empty( $data['type'] ) ) {
691
        $data['type'] = 'custom';
692
    }
693
694
    if ( !empty( $data['custom_id'] ) ) {
695
        $item = wpinv_get_item_by( 'custom_id', $data['custom_id'], $data['type'] );
696
    } else {
697
        $item = NULL;
698
    }
699
700
    if ( !empty( $item ) ) {
701
        if ( $force_update ) {
702
            if ( empty( $args['ID'] ) ) {
703
                $args['ID'] = $item->ID;
704
            }
705
            return wpinv_update_item( $args, $wp_error );
706
        }
707
708
        return $item;
709
    }
710
711
    $meta                           = array();
712
    $meta['type']                   = $data['type'];
713
    $meta['custom_id']              = $data['custom_id'];
714
    $meta['custom_singular_name']   = $data['custom_singular_name'];
715
    $meta['custom_name']            = $data['custom_name'];
716
    $meta['price']                  = wpinv_round_amount( $data['price'] );
717
    $meta['editable']               = (int)$data['editable'];
718
    $meta['vat_rule']               = $data['vat_rule'];
719
    $meta['vat_class']              = '_standard';
720
    
721
    if ( !empty( $data['is_recurring'] ) ) {
722
        $meta['is_recurring']       = $data['is_recurring'];
723
        $meta['recurring_period']   = $data['recurring_period'];
724
        $meta['recurring_interval'] = absint( $data['recurring_interval'] );
725
        $meta['recurring_limit']    = absint( $data['recurring_limit'] );
726
        $meta['free_trial']         = $data['free_trial'];
727
        $meta['trial_period']       = $data['trial_period'];
728
        $meta['trial_interval']     = absint( $data['trial_interval'] );
729
    } else {
730
        $meta['is_recurring']       = 0;
731
        $meta['recurring_period']   = '';
732
        $meta['recurring_interval'] = '';
733
        $meta['recurring_limit']    = '';
734
        $meta['free_trial']         = 0;
735
        $meta['trial_period']       = '';
736
        $meta['trial_interval']     = '';
737
    }
738
    
739
    $post_data  = array( 
740
        'post_title'    => $data['title'],
741
        'post_excerpt'  => $data['excerpt'],
742
        'post_status'   => $data['status'],
743
        'meta'          => $meta
744
    );
745
746
    $item = new WPInv_Item();
747
    $return = $item->create( $post_data, $wp_error );
748
749
    if ( $return && !empty( $item ) && !is_wp_error( $return ) ) {
750
        return $item;
751
    }
752
753
    if ( $wp_error && is_wp_error( $return ) ) {
754
        return $return;
755
    }
756
    return 0;
757
}
758
759
function wpinv_update_item( $args = array(), $wp_error = false ) {
760
    $item = !empty( $args['ID'] ) ? new WPInv_Item( $args['ID'] ) : NULL;
761
762 View Code Duplication
    if ( empty( $item ) || !( !empty( $item->post_type ) && $item->post_type == 'wpi_item' ) ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
763
        if ( $wp_error ) {
764
            return new WP_Error( 'wpinv_invalid_item', __( 'Invalid item.', 'invoicing' ) );
765
        }
766
        return 0;
767
    }
768
    
769
    if ( !empty( $args['custom_id'] ) ) {
770
        $item_exists = wpinv_get_item_by( 'custom_id', $args['custom_id'], ( !empty( $args['type'] ) ? $args['type'] : $item->type ) );
0 ignored issues
show
Documentation introduced by
The property $type is declared private in WPInv_Item. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
771
        
772 View Code Duplication
        if ( !empty( $item_exists ) && $item_exists->ID != $args['ID'] ) {
773
            if ( $wp_error ) {
774
                return new WP_Error( 'wpinv_invalid_custom_id', __( 'Item with custom id already exists.', 'invoicing' ) );
775
            }
776
            return 0;
777
        }
778
    }
779
780
    $meta_fields = array( 'type', 'custom_id', 'custom_singular_name', 'custom_name', 'price', 'editable', 'vat_rule', 'vat_class', 'is_recurring', 'recurring_period', 'recurring_interval', 'recurring_limit', 'free_trial', 'trial_period', 'trial_interval' );
781
782
    $post_data = array();
783
    if ( isset( $args['title'] ) ) { 
784
        $post_data['post_title'] = $args['title'];
785
    }
786
    if ( isset( $args['excerpt'] ) ) { 
787
        $post_data['post_excerpt'] = $args['excerpt'];
788
    }
789
    if ( isset( $args['status'] ) ) { 
790
        $post_data['post_status'] = $args['status'];
791
    }
792
    
793
    foreach ( $meta_fields as $meta_field ) {
794
        if ( isset( $args[ $meta_field ] ) ) { 
795
            $value = $args[ $meta_field ];
796
797
            switch ( $meta_field ) {
798
                case 'price':
799
                    $value = wpinv_round_amount( $value );
800
                break;
801
                case 'recurring_interval':
802
                case 'recurring_limit':
803
                case 'trial_interval':
804
                    $value = absint( $value );
805
                break;
806
				case 'editable':
807
                    $value = (int) $value;
808
                break;
809
            }
810
811
            $post_data['meta'][ $meta_field ] = $value;
812
        };
813
    }
814
815
    if ( empty( $post_data ) ) {
816
        if ( $wp_error ) {
817
            return new WP_Error( 'wpinv_invalid_item_data', __( 'Invalid item data.', 'invoicing' ) );
818
        }
819
        return 0;
820
    }
821
    $post_data['ID'] = $args['ID'];
822
823
    $return = $item->update( $post_data, $wp_error );
824
825
    if ( $return && !empty( $item ) && !is_wp_error( $return ) ) {
826
        return $item;
827
    }
828
829
    if ( $wp_error && is_wp_error( $return ) ) {
830
        return $return;
831
    }
832
    return 0;
833
}