Passed
Push — master ( 169dfd...f76276 )
by Stiofan
04:33
created

WPInv_Invoice::setup_total()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 6
nop 0
dl 0
loc 18
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Contains functions related to Invoicing plugin.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
 
9
// MUST have WordPress.
10
if ( !defined( 'WPINC' ) ) {
11
    exit( 'Do NOT access this file directly: ' . basename( __FILE__ ) );
12
}
13
14
final class WPInv_Invoice {
15
    public $ID  = 0;
16
    public $title;
17
    public $post_type;
18
    
19
    public $pending;
20
    public $items = array();
21
    public $user_info = array();
22
    public $payment_meta = array();
23
    
24
    public $new = false;
25
    public $number = '';
26
    public $mode = 'live';
27
    public $key = '';
28
    public $total = 0.00;
29
    public $subtotal = 0;
30
    public $tax = 0;
31
    public $fees = array();
32
    public $fees_total = 0;
33
    public $discounts = '';
34
    public $discount = 0;
35
    public $discount_code = 0;
36
    public $date = '';
37
    public $due_date = '';
38
    public $completed_date = '';
39
    public $status      = 'wpi-pending';
40
    public $post_status = 'wpi-pending';
41
    public $old_status = '';
42
    public $status_nicename = '';
43
    public $user_id = 0;
44
    public $first_name = '';
45
    public $last_name = '';
46
    public $email = '';
47
    public $phone = '';
48
    public $address = '';
49
    public $city = '';
50
    public $country = '';
51
    public $state = '';
52
    public $zip = '';
53
    public $transaction_id = '';
54
    public $ip = '';
55
    public $gateway = '';
56
    public $gateway_title = '';
57
    public $currency = '';
58
    public $cart_details = array();
59
    
60
    public $company = '';
61
    public $vat_number = '';
62
    public $vat_rate = '';
63
    public $adddress_confirmed = '';
64
    
65
    public $full_name = '';
66
    public $parent_invoice = 0;
67
    
68
    public function __construct( $invoice_id = false ) {
69
        if( empty( $invoice_id ) ) {
70
            return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
71
        }
72
73
        $this->setup_invoice( $invoice_id );
74
    }
75
76
    public function get( $key ) {
77
        if ( method_exists( $this, 'get_' . $key ) ) {
78
            $value = call_user_func( array( $this, 'get_' . $key ) );
79
        } else {
80
            $value = $this->$key;
81
        }
82
83
        return $value;
84
    }
85
86
    public function set( $key, $value ) {
87
        $ignore = array( 'items', 'cart_details', 'fees', '_ID' );
88
89
        if ( $key === 'status' ) {
90
            $this->old_status = $this->status;
91
        }
92
93
        if ( ! in_array( $key, $ignore ) ) {
94
            $this->pending[ $key ] = $value;
95
        }
96
97
        if( '_ID' !== $key ) {
98
            $this->$key = $value;
99
        }
100
    }
101
102
    public function _isset( $name ) {
103
        if ( property_exists( $this, $name) ) {
104
            return false === empty( $this->$name );
105
        } else {
106
            return null;
107
        }
108
    }
109
110
    private function setup_invoice( $invoice_id ) {
111
        $this->pending = array();
112
113
        if ( empty( $invoice_id ) ) {
114
            return false;
115
        }
116
117
        $invoice = get_post( $invoice_id );
118
119
        if( !$invoice || is_wp_error( $invoice ) ) {
120
            return false;
121
        }
122
123
        if( !('wpi_invoice' == $invoice->post_type OR 'wpi_quote' == $invoice->post_type) ) {
124
            return false;
125
        }
126
127
        do_action( 'wpinv_pre_setup_invoice', $this, $invoice_id );
128
        
129
        // Primary Identifier
130
        $this->ID              = absint( $invoice_id );
131
        $this->post_type       = $invoice->post_type;
132
        
133
        // We have a payment, get the generic payment_meta item to reduce calls to it
134
        $this->payment_meta    = $this->get_meta();
135
        $this->date            = $invoice->post_date;
136
        $this->due_date        = $this->setup_due_date();
137
        $this->completed_date  = $this->setup_completed_date();
138
        $this->status          = $invoice->post_status;
139
        $this->post_status     = $this->status;
140
        $this->mode            = $this->setup_mode();
141
        $this->parent_invoice  = $invoice->post_parent;
142
        $this->post_name       = $this->setup_post_name( $invoice );
0 ignored issues
show
Bug introduced by
The property post_name does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
Are you sure the assignment to $this->post_name is correct as $this->setup_post_name($invoice) (which targets WPInv_Invoice::setup_post_name()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
143
        $this->status_nicename = $this->setup_status_nicename($invoice->post_status);
144
145
        // Items
146
        $this->fees            = $this->setup_fees();
147
        $this->cart_details    = $this->setup_cart_details();
148
        $this->items           = $this->setup_items();
149
150
        // Currency Based
151
        $this->total           = $this->setup_total();
152
        $this->tax             = $this->setup_tax();
153
        $this->fees_total      = $this->get_fees_total();
154
        $this->subtotal        = $this->setup_subtotal();
155
        $this->currency        = $this->setup_currency();
156
        
157
        // Gateway based
158
        $this->gateway         = $this->setup_gateway();
159
        $this->gateway_title   = $this->setup_gateway_title();
160
        $this->transaction_id  = $this->setup_transaction_id();
161
        
162
        // User based
163
        $this->ip              = $this->setup_ip();
164
        $this->user_id         = !empty( $invoice->post_author ) ? $invoice->post_author : get_current_user_id();///$this->setup_user_id();
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
165
        $this->email           = get_the_author_meta( 'email', $this->user_id );
166
        
167
        $this->user_info       = $this->setup_user_info();
168
                
169
        $this->first_name      = $this->user_info['first_name'];
170
        $this->last_name       = $this->user_info['last_name'];
171
        $this->company         = $this->user_info['company'];
172
        $this->vat_number      = $this->user_info['vat_number'];
173
        $this->vat_rate        = $this->user_info['vat_rate'];
174
        $this->adddress_confirmed  = $this->user_info['adddress_confirmed'];
175
        $this->address         = $this->user_info['address'];
176
        $this->city            = $this->user_info['city'];
177
        $this->country         = $this->user_info['country'];
178
        $this->state           = $this->user_info['state'];
179
        $this->zip             = $this->user_info['zip'];
180
        $this->phone           = $this->user_info['phone'];
181
        
182
        $this->discounts       = $this->user_info['discount'];
183
            $this->discount        = $this->setup_discount();
184
            $this->discount_code   = $this->setup_discount_code();
185
186
        // Other Identifiers
187
        $this->key             = $this->setup_invoice_key();
188
        $this->number          = $this->setup_invoice_number();
189
        $this->title           = !empty( $invoice->post_title ) ? $invoice->post_title : $this->number;
190
        
191
        $this->full_name       = trim( $this->first_name . ' '. $this->last_name );
192
        
193
        // Allow extensions to add items to this object via hook
194
        do_action( 'wpinv_setup_invoice', $this, $invoice_id );
195
196
        return true;
197
    }
198
    
199
    private function setup_status_nicename($status) {
200
        $all_invoice_statuses  = wpinv_get_invoice_statuses();
201
        $status   = isset( $all_invoice_statuses[$status] ) ? $all_invoice_statuses[$status] : __( $status, 'invoicing' );
202
203
        return apply_filters( 'setup_status_nicename', $status );
204
    }
205
    
206
    private function setup_post_name( $post = NULL ) {
207
        global $wpdb;
208
        
209
        $post_name = '';
210
        
211
        if ( !empty( $post ) ) {
212
            if( !empty( $post->post_name ) ) {
213
                $post_name = $post->post_name;
214
            } else if ( !empty( $post->ID ) ) {
215
                $post_name = 'inv-' . $post->ID;
216
217
                $wpdb->update( $wpdb->posts, array( 'post_name' => 'inv-' . $post->ID ), array( 'ID' => $post->ID ) );
218
            }
219
        }
220
221
        $this->post_name = $post_name;
222
    }
223
    
224
    private function setup_due_date() {
225
        $due_date = $this->get_meta( '_wpinv_due_date' );
226
        
227
        if ( empty( $due_date ) ) {
228
            $overdue_time = strtotime( $this->date ) + ( DAY_IN_SECONDS * absint( wpinv_get_option( 'overdue_days' ) ) );
229
            $due_date = date_i18n( 'Y-m-d', $overdue_time );
230
        } else if ( $due_date == 'none' ) {
231
            $due_date = '';
232
        }
233
        
234
        return $due_date;
235
    }
236
    
237
    private function setup_completed_date() {
238
        $invoice = get_post( $this->ID );
239
240
        if ( 'wpi-pending' == $invoice->post_status || 'preapproved' == $invoice->post_status ) {
241
            return false; // This invoice was never paid
242
        }
243
244
        $date = ( $date = $this->get_meta( '_wpinv_completed_date', true ) ) ? $date : $invoice->modified_date;
245
246
        return $date;
247
    }
248
    
249
    private function setup_cart_details() {
250
        $cart_details = isset( $this->payment_meta['cart_details'] ) ? maybe_unserialize( $this->payment_meta['cart_details'] ) : array();
251
        return $cart_details;
252
    }
253
    
254
    public function array_convert() {
255
        return get_object_vars( $this );
256
    }
257
    
258
    private function setup_items() {
259
        $items = isset( $this->payment_meta['items'] ) ? maybe_unserialize( $this->payment_meta['items'] ) : array();
260
        return $items;
261
    }
262
    
263
    private function setup_fees() {
264
        $payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
265
        return $payment_fees;
266
    }
267
        
268
    private function setup_currency() {
269
        $currency = isset( $this->payment_meta['currency'] ) ? $this->payment_meta['currency'] : apply_filters( 'wpinv_currency_default', wpinv_get_currency(), $this );
270
        return $currency;
271
    }
272
    
273
    private function setup_discount() {
274
        //$discount = $this->get_meta( '_wpinv_discount', true );
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
275
        $discount = (float)$this->subtotal - ( (float)$this->total - (float)$this->tax - (float)$this->fees_total );
276
        if ( $discount < 0 ) {
277
            $discount = 0;
278
        }
279
        $discount = wpinv_round_amount( $discount );
280
        
281
        return $discount;
282
    }
283
    
284
    private function setup_discount_code() {
285
        $discount_code = !empty( $this->discounts ) ? $this->discounts : $this->get_meta( '_wpinv_discount_code', true );
286
        return $discount_code;
287
    }
288
    
289
    private function setup_tax() {
290
        $tax = $this->get_meta( '_wpinv_tax', true );
291
292
        // We don't have tax as it's own meta and no meta was passed
293
        if ( '' === $tax ) {            
294
            $tax = isset( $this->payment_meta['tax'] ) ? $this->payment_meta['tax'] : 0;
295
        }
296
297
        return $tax;
298
    }
299
300
    private function setup_subtotal() {
301
        $subtotal     = 0;
302
        $cart_details = $this->cart_details;
303
304
        if ( is_array( $cart_details ) ) {
305
            foreach ( $cart_details as $item ) {
306
                if ( isset( $item['subtotal'] ) ) {
307
                    $subtotal += $item['subtotal'];
308
                }
309
            }
310
        } else {
311
            $subtotal  = $this->total;
312
            $tax       = wpinv_use_taxes() ? $this->tax : 0;
313
            $subtotal -= $tax;
314
        }
315
316
        return $subtotal;
317
    }
318
    
319
    private function setup_discounts() {
320
        $discounts = ! empty( $this->payment_meta['user_info']['discount'] ) ? $this->payment_meta['user_info']['discount'] : array();
321
        return $discounts;
322
    }
323
    
324
    private function setup_total() {
325
        $amount = $this->get_meta( '_wpinv_total', true );
326
327
        if ( empty( $amount ) && '0.00' != $amount ) {
328
            $meta   = $this->get_meta( '_wpinv_payment_meta', true );
329
            $meta   = maybe_unserialize( $meta );
330
331
            if ( isset( $meta['amount'] ) ) {
332
                $amount = $meta['amount'];
333
            }
334
        }
335
336
        if($amount < 0){
337
            $amount = 0;
338
        }
339
340
        return $amount;
341
    }
342
    
343
    private function setup_mode() {
344
        return $this->get_meta( '_wpinv_mode' );
345
    }
346
347
    private function setup_gateway() {
348
        $gateway = $this->get_meta( '_wpinv_gateway' );
349
        
350
        if ( empty( $gateway ) && 'publish' === $this->status ) {
351
            $gateway = 'manual';
352
        }
353
        
354
        return $gateway;
355
    }
356
    
357
    private function setup_gateway_title() {
358
        $gateway_title = wpinv_get_gateway_checkout_label( $this->gateway );
359
        return $gateway_title;
360
    }
361
362
    private function setup_transaction_id() {
363
        $transaction_id = $this->get_meta( '_wpinv_transaction_id' );
364
365
        if ( empty( $transaction_id ) || (int) $transaction_id === (int) $this->ID ) {
366
            $gateway        = $this->gateway;
367
            $transaction_id = apply_filters( 'wpinv_get_invoice_transaction_id-' . $gateway, $this->ID );
368
        }
369
370
        return $transaction_id;
371
    }
372
373
    private function setup_ip() {
374
        $ip = $this->get_meta( '_wpinv_user_ip' );
375
        return $ip;
376
    }
377
378
    ///private function setup_user_id() {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
379
        ///$user_id = $this->get_meta( '_wpinv_user_id' );
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
380
        ///return $user_id;
381
    ///}
382
        
383
    private function setup_first_name() {
384
        $first_name = $this->get_meta( '_wpinv_first_name' );
385
        return $first_name;
386
    }
387
    
388
    private function setup_last_name() {
389
        $last_name = $this->get_meta( '_wpinv_last_name' );
390
        return $last_name;
391
    }
392
    
393
    private function setup_company() {
394
        $company = $this->get_meta( '_wpinv_company' );
395
        return $company;
396
    }
397
    
398
    private function setup_vat_number() {
399
        $vat_number = $this->get_meta( '_wpinv_vat_number' );
400
        return $vat_number;
401
    }
402
    
403
    private function setup_vat_rate() {
404
        $vat_rate = $this->get_meta( '_wpinv_vat_rate' );
405
        return $vat_rate;
406
    }
407
    
408
    private function setup_adddress_confirmed() {
409
        $adddress_confirmed = $this->get_meta( '_wpinv_adddress_confirmed' );
410
        return $adddress_confirmed;
411
    }
412
    
413
    private function setup_phone() {
414
        $phone = $this->get_meta( '_wpinv_phone' );
415
        return $phone;
416
    }
417
    
418
    private function setup_address() {
419
        $address = $this->get_meta( '_wpinv_address', true );
420
        return $address;
421
    }
422
    
423
    private function setup_city() {
424
        $city = $this->get_meta( '_wpinv_city', true );
425
        return $city;
426
    }
427
    
428
    private function setup_country() {
429
        $country = $this->get_meta( '_wpinv_country', true );
430
        return $country;
431
    }
432
    
433
    private function setup_state() {
434
        $state = $this->get_meta( '_wpinv_state', true );
435
        return $state;
436
    }
437
    
438
    private function setup_zip() {
439
        $zip = $this->get_meta( '_wpinv_zip', true );
440
        return $zip;
441
    }
442
443
    private function setup_user_info() {
444
        $defaults = array(
445
            'user_id'        => $this->user_id,
446
            'first_name'     => $this->first_name,
447
            'last_name'      => $this->last_name,
448
            'email'          => get_the_author_meta( 'email', $this->user_id ),
449
            'phone'          => $this->phone,
450
            'address'        => $this->address,
451
            'city'           => $this->city,
452
            'country'        => $this->country,
453
            'state'          => $this->state,
454
            'zip'            => $this->zip,
455
            'company'        => $this->company,
456
            'vat_number'     => $this->vat_number,
457
            'vat_rate'       => $this->vat_rate,
458
            'adddress_confirmed' => $this->adddress_confirmed,
459
            'discount'       => $this->discounts,
460
        );
461
        
462
        $user_info = array();
463
        if ( isset( $this->payment_meta['user_info'] ) ) {
464
            $user_info = maybe_unserialize( $this->payment_meta['user_info'] );
465
            
466
            if ( !empty( $user_info ) && isset( $user_info['user_id'] ) && $post = get_post( $this->ID ) ) {
467
                $this->user_id = $post->post_author;
468
                $this->email = get_the_author_meta( 'email', $this->user_id );
469
                
470
                $user_info['user_id'] = $this->user_id;
471
                $user_info['email'] = $this->email;
472
                $this->payment_meta['user_id'] = $this->user_id;
473
                $this->payment_meta['email'] = $this->email;
474
            }
475
        }
476
        
477
        $user_info    = wp_parse_args( $user_info, $defaults );
478
        
479
        // Get the user, but only if it's been created
480
        $user = get_userdata( $this->user_id );
481
        
482
        if ( !empty( $user ) && $user->ID > 0 ) {
483
            if ( empty( $user_info ) ) {
484
                $user_info = array(
485
                    'user_id'    => $user->ID,
486
                    'first_name' => $user->first_name,
487
                    'last_name'  => $user->last_name,
488
                    'email'      => $user->user_email,
489
                    'discount'   => '',
490
                );
491
            } else {
492
                foreach ( $user_info as $key => $value ) {
493
                    if ( ! empty( $value ) ) {
494
                        continue;
495
                    }
496
497
                    switch( $key ) {
498
                        case 'user_id':
499
                            $user_info[ $key ] = $user->ID;
500
                            break;
501
                        case 'first_name':
502
                            $user_info[ $key ] = $user->first_name;
503
                            break;
504
                        case 'last_name':
505
                            $user_info[ $key ] = $user->last_name;
506
                            break;
507
                        case 'email':
508
                            $user_info[ $key ] = $user->user_email;
509
                            break;
510
                    }
511
                }
512
            }
513
        }
514
515
        return $user_info;
516
    }
517
518
    private function setup_invoice_key() {
519
        $key = $this->get_meta( '_wpinv_key', true );
520
        
521
        return $key;
522
    }
523
524
    private function setup_invoice_number() {
525
        $number = $this->get_meta( '_wpinv_number', true );
526
527
        if ( !$number ) {
528
            $number = $this->ID;
529
530
            if ( $this->status == 'auto-draft' ) {
531
                if ( wpinv_get_option( 'sequential_invoice_number' ) ) {
532
                    $next_number = wpinv_get_next_invoice_number();
533
                    $number      = $next_number;
534
                }
535
            }
536
            
537
            $number = wpinv_format_invoice_number( $number );
538
        }
539
540
        return $number;
541
    }
542
    
543
    private function insert_invoice() {
544
        global $wpdb;
545
546
        $invoice_number = $this->ID;
547
        if ( $number = $this->get_meta( '_wpinv_number', true ) ) {
548
            $invoice_number = $number;
549
        }
550
551 View Code Duplication
        if ( empty( $this->key ) ) {
552
            $this->key = self::generate_key();
553
            $this->pending['key'] = $this->key;
554
        }
555
556
        if ( empty( $this->ip ) ) {
557
            $this->ip = wpinv_get_ip();
558
            $this->pending['ip'] = $this->ip;
559
        }
560
        
561
        $payment_data = array(
562
            'price'        => $this->total,
563
            'date'         => $this->date,
564
            'user_email'   => $this->email,
565
            'invoice_key'  => $this->key,
566
            'currency'     => $this->currency,
567
            'items'        => $this->items,
568
            'user_info' => array(
569
                'user_id'    => $this->user_id,
570
                'email'      => $this->email,
571
                'first_name' => $this->first_name,
572
                'last_name'  => $this->last_name,
573
                'address'    => $this->address,
574
                'phone'      => $this->phone,
575
                'city'       => $this->city,
576
                'country'    => $this->country,
577
                'state'      => $this->state,
578
                'zip'        => $this->zip,
579
                'company'    => $this->company,
580
                'vat_number' => $this->vat_number,
581
                'discount'   => $this->discounts,
582
            ),
583
            'cart_details' => $this->cart_details,
584
            'status'       => $this->status,
585
            'fees'         => $this->fees,
586
        );
587
588
        $post_data = array(
589
                        'post_title'    => $invoice_number,
590
                        'post_status'   => $this->status,
591
                        'post_author'   => $this->user_id,
592
                        'post_type'     => $this->post_type,
593
                        'post_date'     => ! empty( $this->date ) && $this->date != '0000-00-00 00:00:00' ? $this->date : current_time( 'mysql' ),
594
                        'post_date_gmt' => ! empty( $this->date ) && $this->date != '0000-00-00 00:00:00' ? get_gmt_from_date( $this->date ) : current_time( 'mysql', 1 ),
595
                        'post_parent'   => $this->parent_invoice,
596
                    );
597
        $args = apply_filters( 'wpinv_insert_invoice_args', $post_data, $this );
598
599
        // Create a blank invoice
600
        if ( !empty( $this->ID ) ) {
601
            $args['ID']         = $this->ID;
602
603
            $invoice_id = wp_update_post( $args, true );
604
        } else {
605
            $invoice_id = wp_insert_post( $args, true );
606
        }
607
608
        if ( is_wp_error( $invoice_id ) ) {
609
            return false;
610
        }
611
612
        if ( !empty( $invoice_id ) ) {
613
            $this->ID  = $invoice_id;
614
            $this->_ID = $invoice_id;
0 ignored issues
show
Bug introduced by
The property _ID does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
615
616
            $this->payment_meta = apply_filters( 'wpinv_payment_meta', $this->payment_meta, $payment_data );
617
            if ( ! empty( $this->payment_meta['fees'] ) ) {
618
                $this->fees = array_merge( $this->fees, $this->payment_meta['fees'] );
619
                foreach( $this->fees as $fee ) {
620
                    $this->increase_fees( $fee['amount'] );
621
                }
622
            }
623
624
            $this->update_meta( '_wpinv_payment_meta', $this->payment_meta );            
625
            $this->new = true;
626
        }
627
628
        return $this->ID;
629
    }
630
631
    public function save( $setup = false ) {
632
        global $wpi_session;
633
        
634
        $saved = false;
635
        if ( empty( $this->items ) ) {
636
            return $saved; // Don't save empty invoice.
637
        }
638
        
639 View Code Duplication
        if ( empty( $this->key ) ) {
640
            $this->key = self::generate_key();
641
            $this->pending['key'] = $this->key;
642
        }
643
        
644
        if ( empty( $this->ID ) ) {
645
            $invoice_id = $this->insert_invoice();
646
647
            if ( false === $invoice_id ) {
648
                $saved = false;
649
            } else {
650
                $this->ID = $invoice_id;
651
            }
652
        }
653
654
        // If we have something pending, let's save it
655
        if ( !empty( $this->pending ) ) {
656
            $total_increase = 0;
657
            $total_decrease = 0;
658
659
            foreach ( $this->pending as $key => $value ) {
660
                switch( $key ) {
661
                    case 'items':
662
                        // Update totals for pending items
663
                        foreach ( $this->pending[ $key ] as $item ) {
664
                            switch( $item['action'] ) {
665
                                case 'add':
666
                                    $price = $item['price'];
667
                                    $taxes = $item['tax'];
0 ignored issues
show
Unused Code introduced by
$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...
668
669
                                    if ( 'publish' === $this->status ) {
670
                                        $total_increase += $price;
671
                                    }
672
                                    break;
673
674
                                case 'remove':
675
                                    if ( 'publish' === $this->status ) {
676
                                        $total_decrease += $item['price'];
677
                                    }
678
                                    break;
679
                            }
680
                        }
681
                        break;
682
                    case 'fees':
683
                        if ( 'publish' !== $this->status ) {
684
                            break;
685
                        }
686
687
                        if ( empty( $this->pending[ $key ] ) ) {
688
                            break;
689
                        }
690
691
                        foreach ( $this->pending[ $key ] as $fee ) {
692
                            switch( $fee['action'] ) {
693
                                case 'add':
694
                                    $total_increase += $fee['amount'];
695
                                    break;
696
697
                                case 'remove':
698
                                    $total_decrease += $fee['amount'];
699
                                    break;
700
                            }
701
                        }
702
                        break;
703
                    case 'status':
704
                        $this->update_status( $this->status );
0 ignored issues
show
Documentation introduced by
$this->status 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...
705
                        break;
706
                    case 'gateway':
707
                        $this->update_meta( '_wpinv_gateway', $this->gateway );
708
                        break;
709
                    case 'mode':
710
                        $this->update_meta( '_wpinv_mode', $this->mode );
711
                        break;
712
                    case 'transaction_id':
713
                        $this->update_meta( '_wpinv_transaction_id', $this->transaction_id );
714
                        break;
715
                    case 'ip':
716
                        $this->update_meta( '_wpinv_user_ip', $this->ip );
717
                        break;
718
                    ///case 'user_id':
719
                        ///$this->update_meta( '_wpinv_user_id', $this->user_id );
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
720
                        ///$this->user_info['user_id'] = $this->user_id;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
721
                        ///break;
722
                    case 'first_name':
723
                        $this->update_meta( '_wpinv_first_name', $this->first_name );
724
                        $this->user_info['first_name'] = $this->first_name;
725
                        break;
726
                    case 'last_name':
727
                        $this->update_meta( '_wpinv_last_name', $this->last_name );
728
                        $this->user_info['last_name'] = $this->last_name;
729
                        break;
730
                    case 'phone':
731
                        $this->update_meta( '_wpinv_phone', $this->phone );
732
                        $this->user_info['phone'] = $this->phone;
733
                        break;
734
                    case 'address':
735
                        $this->update_meta( '_wpinv_address', $this->address );
736
                        $this->user_info['address'] = $this->address;
737
                        break;
738
                    case 'city':
739
                        $this->update_meta( '_wpinv_city', $this->city );
740
                        $this->user_info['city'] = $this->city;
741
                        break;
742
                    case 'country':
743
                        $this->update_meta( '_wpinv_country', $this->country );
744
                        $this->user_info['country'] = $this->country;
745
                        break;
746
                    case 'state':
747
                        $this->update_meta( '_wpinv_state', $this->state );
748
                        $this->user_info['state'] = $this->state;
749
                        break;
750
                    case 'zip':
751
                        $this->update_meta( '_wpinv_zip', $this->zip );
752
                        $this->user_info['zip'] = $this->zip;
753
                        break;
754
                    case 'company':
755
                        $this->update_meta( '_wpinv_company', $this->company );
756
                        $this->user_info['company'] = $this->company;
757
                        break;
758
                    case 'vat_number':
759
                        $this->update_meta( '_wpinv_vat_number', $this->vat_number );
760
                        $this->user_info['vat_number'] = $this->vat_number;
761
                        
762
                        $vat_info = $wpi_session->get( 'user_vat_data' );
763
                        if ( $this->vat_number && !empty( $vat_info ) && isset( $vat_info['number'] ) && isset( $vat_info['valid'] ) && $vat_info['number'] == $this->vat_number ) {
764
                            $adddress_confirmed = isset( $vat_info['adddress_confirmed'] ) ? $vat_info['adddress_confirmed'] : false;
765
                            $this->update_meta( '_wpinv_adddress_confirmed', (bool)$adddress_confirmed );
0 ignored issues
show
Documentation introduced by
(bool) $adddress_confirmed is of type boolean, but the function expects a string.

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...
766
                            $this->user_info['adddress_confirmed'] = (bool)$adddress_confirmed;
767
                        }
768
    
769
                        break;
770
                    case 'vat_rate':
771
                        $this->update_meta( '_wpinv_vat_rate', $this->vat_rate );
772
                        $this->user_info['vat_rate'] = $this->vat_rate;
773
                        break;
774
                    case 'adddress_confirmed':
775
                        $this->update_meta( '_wpinv_adddress_confirmed', $this->adddress_confirmed );
776
                        $this->user_info['adddress_confirmed'] = $this->adddress_confirmed;
777
                        break;
778
                    
779
                    case 'key':
780
                        $this->update_meta( '_wpinv_key', $this->key );
781
                        break;
782
                    case 'date':
783
                        $args = array(
784
                            'ID'        => $this->ID,
785
                            'post_date' => $this->date,
786
                            'edit_date' => true,
787
                        );
788
789
                        wp_update_post( $args );
790
                        break;
791
                    case 'due_date':
792
                        if ( empty( $this->due_date ) ) {
793
                            $this->due_date = 'none';
794
                        }
795
                        
796
                        $this->update_meta( '_wpinv_due_date', $this->due_date );
797
                        break;
798
                    case 'completed_date':
799
                        $this->update_meta( '_wpinv_completed_date', $this->completed_date );
800
                        break;
801
                    case 'discounts':
802
                        if ( ! is_array( $this->discounts ) ) {
803
                            $this->discounts = explode( ',', $this->discounts );
0 ignored issues
show
Documentation Bug introduced by
It seems like explode(',', $this->discounts) of type array is incompatible with the declared type string of property $discounts.

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...
804
                        }
805
806
                        $this->user_info['discount'] = implode( ',', $this->discounts );
807
                        break;
808
                    case 'discount':
809
                        $this->update_meta( '_wpinv_discount', wpinv_round_amount( $this->discount ) );
810
                        break;
811
                    case 'discount_code':
812
                        $this->update_meta( '_wpinv_discount_code', $this->discount_code );
813
                        break;
814
                    case 'parent_invoice':
815
                        $args = array(
816
                            'ID'          => $this->ID,
817
                            'post_parent' => $this->parent_invoice,
818
                        );
819
                        wp_update_post( $args );
820
                        break;
821
                    default:
822
                        do_action( 'wpinv_save', $this, $key );
823
                        break;
824
                }
825
            }
826
827
            $this->update_meta( '_wpinv_subtotal', wpinv_round_amount( $this->subtotal ) );
828
            $this->update_meta( '_wpinv_total', wpinv_round_amount( $this->total ) );
829
            $this->update_meta( '_wpinv_tax', wpinv_round_amount( $this->tax ) );
830
            
831
            $this->items    = array_values( $this->items );
832
            
833
            $new_meta = array(
834
                'items'         => $this->items,
835
                'cart_details'  => $this->cart_details,
836
                'fees'          => $this->fees,
837
                'currency'      => $this->currency,
838
                'user_info'     => $this->user_info,
839
            );
840
            
841
            $meta        = $this->get_meta();
842
            $merged_meta = array_merge( $meta, $new_meta );
843
844
            // Only save the payment meta if it's changed
845
            if ( md5( serialize( $meta ) ) !== md5( serialize( $merged_meta) ) ) {
846
                $updated     = $this->update_meta( '_wpinv_payment_meta', $merged_meta );
0 ignored issues
show
Documentation introduced by
$merged_meta is of type array<string,array|string>, but the function expects a string.

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...
847
                if ( false !== $updated ) {
848
                    $saved = true;
0 ignored issues
show
Unused Code introduced by
$saved 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...
849
                }
850
            }
851
852
            $this->pending = array();
853
            $saved         = true;
854
        } else {
855
            $this->update_meta( '_wpinv_subtotal', wpinv_round_amount( $this->subtotal ) );
856
            $this->update_meta( '_wpinv_total', wpinv_round_amount( $this->total ) );
857
            $this->update_meta( '_wpinv_tax', wpinv_round_amount( $this->tax ) );
858
        }
859
        
860
        do_action( 'wpinv_invoice_save', $this, $saved );
861
862
        if ( true === $saved || $setup ) {
863
            $this->setup_invoice( $this->ID );
864
        }
865
        
866
        $this->refresh_item_ids();
867
        
868
        return $saved;
869
    }
870
    
871
    public function add_fee( $args, $global = true ) {
0 ignored issues
show
Unused Code introduced by
The parameter $global 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...
872
        $default_args = array(
873
            'label'       => '',
874
            'amount'      => 0,
875
            'type'        => 'fee',
876
            'id'          => '',
877
            'no_tax'      => false,
878
            'item_id'     => 0,
879
        );
880
881
        $fee = wp_parse_args( $args, $default_args );
882
        
883
        if ( !empty( $fee['label'] ) ) {
884
            return false;
885
        }
886
        
887
        $fee['id']  = sanitize_title( $fee['label'] );
888
        
889
        $this->fees[]               = $fee;
890
        
891
        $added_fee               = $fee;
892
        $added_fee['action']     = 'add';
893
        $this->pending['fees'][] = $added_fee;
894
        reset( $this->fees );
895
896
        $this->increase_fees( $fee['amount'] );
897
        return true;
898
    }
899
900
    public function remove_fee( $key ) {
901
        $removed = false;
902
903
        if ( is_numeric( $key ) ) {
904
            $removed = $this->remove_fee_by( 'index', $key );
905
        }
906
907
        return $removed;
908
    }
909
910
    public function remove_fee_by( $key, $value, $global = false ) {
911
        $allowed_fee_keys = apply_filters( 'wpinv_fee_keys', array(
912
            'index', 'label', 'amount', 'type',
913
        ) );
914
915
        if ( ! in_array( $key, $allowed_fee_keys ) ) {
916
            return false;
917
        }
918
919
        $removed = false;
920
        if ( 'index' === $key && array_key_exists( $value, $this->fees ) ) {
921
            $removed_fee             = $this->fees[ $value ];
922
            $removed_fee['action']   = 'remove';
923
            $this->pending['fees'][] = $removed_fee;
924
925
            $this->decrease_fees( $removed_fee['amount'] );
926
927
            unset( $this->fees[ $value ] );
928
            $removed = true;
929
        } else if ( 'index' !== $key ) {
930
            foreach ( $this->fees as $index => $fee ) {
931
                if ( isset( $fee[ $key ] ) && $fee[ $key ] == $value ) {
932
                    $removed_fee             = $fee;
933
                    $removed_fee['action']   = 'remove';
934
                    $this->pending['fees'][] = $removed_fee;
935
936
                    $this->decrease_fees( $removed_fee['amount'] );
937
938
                    unset( $this->fees[ $index ] );
939
                    $removed = true;
940
941
                    if ( false === $global ) {
942
                        break;
943
                    }
944
                }
945
            }
946
        }
947
948
        if ( true === $removed ) {
949
            $this->fees = array_values( $this->fees );
950
        }
951
952
        return $removed;
953
    }
954
955
    
956
957
    public function add_note( $note = '', $customer_type = false, $added_by_user = false, $system = false ) {
958
        // Bail if no note specified
959
        if( !$note ) {
960
            return false;
961
        }
962
963
        if ( empty( $this->ID ) )
964
            return false;
965
        
966
        if ( ( ( is_user_logged_in() && current_user_can( 'manage_options' ) ) || $added_by_user ) && !$system ) {
967
            $user                 = get_user_by( 'id', get_current_user_id() );
968
            $comment_author       = $user->display_name;
969
            $comment_author_email = $user->user_email;
970
        } else {
971
            $comment_author       = __( 'System', 'invoicing' );
972
            $comment_author_email = strtolower( __( 'System', 'invoicing' ) ) . '@';
973
            $comment_author_email .= isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com';
974
            $comment_author_email = sanitize_email( $comment_author_email );
975
        }
976
977
        do_action( 'wpinv_pre_insert_invoice_note', $this->ID, $note, $customer_type );
978
979
        $note_id = wp_insert_comment( wp_filter_comment( array(
980
            'comment_post_ID'      => $this->ID,
981
            'comment_content'      => $note,
982
            'comment_agent'        => 'GeoDirectory',
983
            'user_id'              => is_admin() ? get_current_user_id() : 0,
984
            'comment_date'         => current_time( 'mysql' ),
985
            'comment_date_gmt'     => current_time( 'mysql', 1 ),
986
            'comment_approved'     => 1,
987
            'comment_parent'       => 0,
988
            'comment_author'       => $comment_author,
989
            'comment_author_IP'    => wpinv_get_ip(),
990
            'comment_author_url'   => '',
991
            'comment_author_email' => $comment_author_email,
992
            'comment_type'         => 'wpinv_note'
993
        ) ) );
994
995
        do_action( 'wpinv_insert_payment_note', $note_id, $this->ID, $note );
996
        
997
        if ( $customer_type ) {
998
            add_comment_meta( $note_id, '_wpi_customer_note', 1 );
999
1000
            do_action( 'wpinv_new_customer_note', array( 'invoice_id' => $this->ID, 'user_note' => $note ) );
1001
        }
1002
1003
        return $note_id;
1004
    }
1005
1006
    private function increase_subtotal( $amount = 0.00 ) {
1007
        $amount          = (float) $amount;
1008
        $this->subtotal += $amount;
1009
        $this->subtotal  = wpinv_round_amount( $this->subtotal );
1010
1011
        $this->recalculate_total();
1012
    }
1013
1014 View Code Duplication
    private function decrease_subtotal( $amount = 0.00 ) {
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...
1015
        $amount          = (float) $amount;
1016
        $this->subtotal -= $amount;
1017
        $this->subtotal  = wpinv_round_amount( $this->subtotal );
1018
1019
        if ( $this->subtotal < 0 ) {
1020
            $this->subtotal = 0;
1021
        }
1022
1023
        $this->recalculate_total();
1024
    }
1025
1026
    private function increase_fees( $amount = 0.00 ) {
1027
        $amount            = (float)$amount;
1028
        $this->fees_total += $amount;
1029
        $this->fees_total  = wpinv_round_amount( $this->fees_total );
1030
1031
        $this->recalculate_total();
1032
    }
1033
1034 View Code Duplication
    private function decrease_fees( $amount = 0.00 ) {
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...
1035
        $amount            = (float) $amount;
1036
        $this->fees_total -= $amount;
1037
        $this->fees_total  = wpinv_round_amount( $this->fees_total );
1038
1039
        if ( $this->fees_total < 0 ) {
1040
            $this->fees_total = 0;
1041
        }
1042
1043
        $this->recalculate_total();
1044
    }
1045
1046
    public function recalculate_total() {
1047
        global $wpi_nosave;
1048
        
1049
        $this->total = $this->subtotal + $this->tax + $this->fees_total;
1050
        $this->total = wpinv_round_amount( $this->total );
1051
        
1052
        do_action( 'wpinv_invoice_recalculate_total', $this, $wpi_nosave );
1053
    }
1054
    
1055
    public function increase_tax( $amount = 0.00 ) {
1056
        $amount       = (float) $amount;
1057
        $this->tax   += $amount;
1058
1059
        $this->recalculate_total();
1060
    }
1061
1062
    public function decrease_tax( $amount = 0.00 ) {
1063
        $amount     = (float) $amount;
1064
        $this->tax -= $amount;
1065
1066
        if ( $this->tax < 0 ) {
1067
            $this->tax = 0;
1068
        }
1069
1070
        $this->recalculate_total();
1071
    }
1072
1073
    public function update_status( $new_status = false, $note = '', $manual = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $note 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 $manual 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...
1074
        $old_status = ! empty( $this->old_status ) ? $this->old_status : get_post_status( $this->ID );
1075
        
1076
        if ( $old_status === $new_status && in_array( $new_status, array_keys( wpinv_get_invoice_statuses() ) ) ) {
1077
            return false; // Don't permit status changes that aren't changes
1078
        }
1079
1080
        $do_change = apply_filters( 'wpinv_should_update_invoice_status', true, $this->ID, $new_status, $old_status );
1081
        $updated = false;
1082
1083
        if ( $do_change ) {
1084
            do_action( 'wpinv_before_invoice_status_change', $this->ID, $new_status, $old_status );
1085
1086
            $update_post_data                   = array();
1087
            $update_post_data['ID']             = $this->ID;
1088
            $update_post_data['post_status']    = $new_status;
1089
            $update_post_data['edit_date']      = current_time( 'mysql', 0 );
1090
            $update_post_data['edit_date_gmt']  = current_time( 'mysql', 1 );
1091
            
1092
            $update_post_data = apply_filters( 'wpinv_update_invoice_status_fields', $update_post_data, $this->ID );
1093
1094
            $updated = wp_update_post( $update_post_data );     
1095
           
1096
            // Process any specific status functions
1097
            switch( $new_status ) {
1098
                case 'wpi-refunded':
1099
                    $this->process_refund();
1100
                    break;
1101
                case 'wpi-failed':
1102
                    $this->process_failure();
1103
                    break;
1104
                case 'wpi-pending':
1105
                    $this->process_pending();
1106
                    break;
1107
            }
1108
            
1109
            // Status was changed.
1110
            do_action( 'wpinv_status_' . $new_status, $this->ID, $old_status );
1111
            do_action( 'wpinv_status_' . $old_status . '_to_' . $new_status, $this->ID, $old_status );
1112
            do_action( 'wpinv_update_status', $this->ID, $new_status, $old_status );
1113
        }
1114
1115
        return $updated;
1116
    }
1117
1118
    public function refund() {
1119
        $this->old_status        = $this->status;
1120
        $this->status            = 'wpi-refunded';
1121
        $this->pending['status'] = $this->status;
1122
1123
        $this->save();
1124
    }
1125
1126
    public function update_meta( $meta_key = '', $meta_value = '', $prev_value = '' ) {
1127
        if ( empty( $meta_key ) ) {
1128
            return false;
1129
        }
1130
1131
        if ( $meta_key == 'key' || $meta_key == 'date' ) {
1132
            $current_meta = $this->get_meta();
1133
            $current_meta[ $meta_key ] = $meta_value;
1134
1135
            $meta_key     = '_wpinv_payment_meta';
1136
            $meta_value   = $current_meta;
1137
        }
1138
1139
        $meta_value = apply_filters( 'wpinv_update_payment_meta_' . $meta_key, $meta_value, $this->ID );
1140
        
1141
        if ( $meta_key == '_wpinv_completed_date' && !empty( $meta_value ) ) {
1142
            $args = array(
1143
                'ID'                => $this->ID,
1144
                'post_date'         => $meta_value,
1145
                'edit_date'         => true,
1146
                'post_date_gmt'     => get_gmt_from_date( $meta_value ),
1147
                'post_modified'     => $meta_value,
1148
                'post_modified_gmt' => get_gmt_from_date( $meta_value )
1149
            );
1150
            wp_update_post( $args );
1151
        }
1152
        
1153
        return update_post_meta( $this->ID, $meta_key, $meta_value, $prev_value );
1154
    }
1155
1156
    private function process_refund() {
1157
        $process_refund = true;
1158
1159
        // If the payment was not in publish, don't decrement stats as they were never incremented
1160
        if ( 'publish' != $this->old_status || 'wpi-refunded' != $this->status ) {
1161
            $process_refund = false;
1162
        }
1163
1164
        // Allow extensions to filter for their own payment types, Example: Recurring Payments
1165
        $process_refund = apply_filters( 'wpinv_should_process_refund', $process_refund, $this );
1166
1167
        if ( false === $process_refund ) {
1168
            return;
1169
        }
1170
1171
        do_action( 'wpinv_pre_refund_invoice', $this );
1172
        
1173
        $decrease_store_earnings = apply_filters( 'wpinv_decrease_store_earnings_on_refund', true, $this );
0 ignored issues
show
Unused Code introduced by
$decrease_store_earnings 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...
1174
        $decrease_customer_value = apply_filters( 'wpinv_decrease_customer_value_on_refund', true, $this );
0 ignored issues
show
Unused Code introduced by
$decrease_customer_value 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...
1175
        $decrease_purchase_count = apply_filters( 'wpinv_decrease_customer_purchase_count_on_refund', true, $this );
0 ignored issues
show
Unused Code introduced by
$decrease_purchase_count 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...
1176
        
1177
        do_action( 'wpinv_post_refund_invoice', $this );
1178
    }
1179
1180
    private function process_failure() {
1181
        $discounts = $this->discounts;
1182
        if ( empty( $discounts ) ) {
1183
            return;
1184
        }
1185
1186
        if ( ! is_array( $discounts ) ) {
1187
            $discounts = array_map( 'trim', explode( ',', $discounts ) );
1188
        }
1189
1190
        foreach ( $discounts as $discount ) {
1191
            wpinv_decrease_discount_usage( $discount );
1192
        }
1193
    }
1194
    
1195
    private function process_pending() {
1196
        $process_pending = true;
1197
1198
        // If the payment was not in publish or revoked status, don't decrement stats as they were never incremented
1199
        if ( ( 'publish' != $this->old_status && 'revoked' != $this->old_status ) || 'wpi-pending' != $this->status ) {
1200
            $process_pending = false;
1201
        }
1202
1203
        // Allow extensions to filter for their own payment types, Example: Recurring Payments
1204
        $process_pending = apply_filters( 'wpinv_should_process_pending', $process_pending, $this );
1205
1206
        if ( false === $process_pending ) {
1207
            return;
1208
        }
1209
1210
        $decrease_store_earnings = apply_filters( 'wpinv_decrease_store_earnings_on_pending', true, $this );
0 ignored issues
show
Unused Code introduced by
$decrease_store_earnings 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...
1211
        $decrease_customer_value = apply_filters( 'wpinv_decrease_customer_value_on_pending', true, $this );
0 ignored issues
show
Unused Code introduced by
$decrease_customer_value 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...
1212
        $decrease_purchase_count = apply_filters( 'wpinv_decrease_customer_purchase_count_on_pending', true, $this );
0 ignored issues
show
Unused Code introduced by
$decrease_purchase_count 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...
1213
1214
        $this->completed_date = '';
1215
        $this->update_meta( '_wpinv_completed_date', '' );
1216
    }
1217
    
1218
    // get data
1219
    public function get_meta( $meta_key = '_wpinv_payment_meta', $single = true ) {
1220
        $meta = get_post_meta( $this->ID, $meta_key, $single );
1221
1222
        if ( $meta_key === '_wpinv_payment_meta' ) {
1223
1224
            if(!is_array($meta)){$meta = array();} // we need this to be an array so make sure it is.
1225
1226
            if ( empty( $meta['key'] ) ) {
1227
                $meta['key'] = $this->setup_invoice_key();
1228
            }
1229
1230
            if ( empty( $meta['date'] ) ) {
1231
                $meta['date'] = get_post_field( 'post_date', $this->ID );
1232
            }
1233
        }
1234
1235
        $meta = apply_filters( 'wpinv_get_invoice_meta_' . $meta_key, $meta, $this->ID );
1236
1237
        return apply_filters( 'wpinv_get_invoice_meta', $meta, $this->ID, $meta_key );
1238
    }
1239
    
1240
    public function get_description() {
1241
        $post = get_post( $this->ID );
1242
        
1243
        $description = !empty( $post ) ? $post->post_content : '';
1244
        return apply_filters( 'wpinv_get_description', $description, $this->ID, $this );
1245
    }
1246
    
1247
    public function get_status( $nicename = false ) {
1248
        if ( !$nicename ) {
1249
            $status = $this->status;
1250
        } else {
1251
            $status = $this->status_nicename;
1252
        }
1253
        
1254
        return apply_filters( 'wpinv_get_status', $status, $nicename, $this->ID, $this );
1255
    }
1256
    
1257
    public function get_cart_details() {
1258
        return apply_filters( 'wpinv_cart_details', $this->cart_details, $this->ID, $this );
1259
    }
1260
    
1261 View Code Duplication
    public function get_subtotal( $currency = false ) {
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...
1262
        $subtotal = wpinv_round_amount( $this->subtotal );
1263
        
1264
        if ( $currency ) {
1265
            $subtotal = wpinv_price( wpinv_format_amount( $subtotal, NULL, !$currency ), $this->get_currency() );
1266
        }
1267
        
1268
        return apply_filters( 'wpinv_get_invoice_subtotal', $subtotal, $this->ID, $this, $currency );
1269
    }
1270
    
1271 View Code Duplication
    public function get_total( $currency = false ) {        
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...
1272
        if ( $this->is_free_trial() ) {
1273
            $total = wpinv_round_amount( 0 );
1274
        } else {
1275
            $total = wpinv_round_amount( $this->total );
1276
        }
1277
        if ( $currency ) {
1278
            $total = wpinv_price( wpinv_format_amount( $total, NULL, !$currency ), $this->get_currency() );
1279
        }
1280
        
1281
        return apply_filters( 'wpinv_get_invoice_total', $total, $this->ID, $this, $currency );
1282
    }
1283
    
1284
    public function get_recurring_details( $field = '', $currency = false ) {        
1285
        $data                 = array();
1286
        $data['cart_details'] = $this->cart_details;
1287
        $data['subtotal']     = $this->get_subtotal();
1288
        $data['discount']     = $this->get_discount();
1289
        $data['tax']          = $this->get_tax();
1290
        $data['total']        = $this->get_total();
1291
    
1292
        if ( !empty( $this->cart_details ) && ( $this->is_parent() || $this->is_renewal() ) ) {
1293
            $is_free_trial = $this->is_free_trial();
1294
            $discounts = $this->get_discounts( true );
1295
            
1296
            if ( $is_free_trial || !empty( $discounts ) ) {
1297
                $first_use_only = false;
1298
                
1299
                if ( !empty( $discounts ) ) {
1300
                    foreach ( $discounts as $key => $code ) {
1301
                        if ( wpinv_discount_is_recurring( $code, true ) ) {
1302
                            $first_use_only = true;
1303
                            break;
1304
                        }
1305
                    }
1306
                }
1307
                    
1308
                if ( !$first_use_only ) {
1309
                    $data['subtotal'] = wpinv_round_amount( $this->subtotal );
1310
                    $data['discount'] = wpinv_round_amount( $this->discount );
1311
                    $data['tax']      = wpinv_round_amount( $this->tax );
1312
                    $data['total']    = wpinv_round_amount( $this->total );
1313
                } else {
1314
                    $cart_subtotal   = 0;
1315
                    $cart_discount   = 0;
1316
                    $cart_tax        = 0;
1317
1318
                    foreach ( $this->cart_details as $key => $item ) {
1319
                        $item_quantity  = $item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
1320
                        $item_subtotal  = !empty( $item['subtotal'] ) ? $item['subtotal'] : $item['item_price'] * $item_quantity;
1321
                        $item_discount  = 0;
1322
                        $item_tax       = $item_subtotal > 0 && !empty( $item['vat_rate'] ) ? ( $item_subtotal * 0.01 * (float)$item['vat_rate'] ) : 0;
1323
                        
1324
                        if ( wpinv_prices_include_tax() ) {
1325
                            $item_subtotal -= wpinv_round_amount( $item_tax );
1326
                        }
1327
                        
1328
                        $item_total     = $item_subtotal - $item_discount + $item_tax;
1329
                        // Do not allow totals to go negative
1330
                        if ( $item_total < 0 ) {
1331
                            $item_total = 0;
1332
                        }
1333
                        
1334
                        $cart_subtotal  += (float)($item_subtotal);
1335
                        $cart_discount  += (float)($item_discount);
1336
                        $cart_tax       += (float)($item_tax);
1337
                        
1338
                        $data['cart_details'][$key]['discount']   = wpinv_round_amount( $item_discount );
1339
                        $data['cart_details'][$key]['tax']        = wpinv_round_amount( $item_tax );
1340
                        $data['cart_details'][$key]['price']      = wpinv_round_amount( $item_total );
1341
                    }
1342
                    
1343
                    $data['subtotal'] = wpinv_round_amount( $cart_subtotal );
1344
                    $data['discount'] = wpinv_round_amount( $cart_discount );
1345
                    $data['tax']      = wpinv_round_amount( $cart_tax );
1346
                    $data['total']    = wpinv_round_amount( $data['subtotal'] + $data['tax'] );
1347
                }
1348
            }
1349
        }
1350
        
1351
        $data = apply_filters( 'wpinv_get_invoice_recurring_details', $data, $this, $field, $currency );
1352
1353
        if ( isset( $data[$field] ) ) {
1354
            return ( $currency ? wpinv_price( $data[$field], $this->get_currency() ) : $data[$field] );
1355
        }
1356
        
1357
        return $data;
1358
    }
1359
    
1360
    public function get_final_tax( $currency = false ) {        
1361
        $final_total = wpinv_round_amount( $this->tax );
1362
        if ( $currency ) {
1363
            $final_total = wpinv_price( wpinv_format_amount( $final_total, NULL, !$currency ), $this->get_currency() );
1364
        }
1365
        
1366
        return apply_filters( 'wpinv_get_invoice_final_total', $final_total, $this, $currency );
1367
    }
1368
    
1369
    public function get_discounts( $array = false ) {
1370
        $discounts = $this->discounts;
1371
        if ( $array && $discounts ) {
1372
            $discounts = explode( ',', $discounts );
1373
        }
1374
        return apply_filters( 'wpinv_payment_discounts', $discounts, $this->ID, $this, $array );
1375
    }
1376
    
1377
    public function get_discount( $currency = false, $dash = false ) {
1378
        if ( !empty( $this->discounts ) ) {
1379
            global $ajax_cart_details;
1380
            $ajax_cart_details = $this->get_cart_details();
1381
            
1382
            if ( !empty( $ajax_cart_details ) && count( $ajax_cart_details ) == count( $this->items ) ) {
1383
                $cart_items = $ajax_cart_details;
1384
            } else {
1385
                $cart_items = $this->items;
1386
            }
1387
1388
            $this->discount = wpinv_get_cart_items_discount_amount( $cart_items , $this->discounts );
0 ignored issues
show
Documentation introduced by
$this->discounts 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...
1389
        }
1390
        $discount   = wpinv_round_amount( $this->discount );
1391
        $dash       = $dash && $discount > 0 ? '&ndash;' : '';
1392
        
1393
        if ( $currency ) {
1394
            $discount = wpinv_price( wpinv_format_amount( $discount, NULL, !$currency ), $this->get_currency() );
1395
        }
1396
        
1397
        $discount   = $dash . $discount;
1398
        
1399
        return apply_filters( 'wpinv_get_invoice_discount', $discount, $this->ID, $this, $currency, $dash );
1400
    }
1401
    
1402
    public function get_discount_code() {
1403
        return $this->discount_code;
1404
    }
1405
    
1406 View Code Duplication
    public function get_tax( $currency = false ) {
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...
1407
        $tax = wpinv_round_amount( $this->tax );
1408
        
1409
        if ( $currency ) {
1410
            $tax = wpinv_price( wpinv_format_amount( $tax, NULL, !$currency ), $this->get_currency() );
1411
        }
1412
        
1413
        return apply_filters( 'wpinv_get_invoice_tax', $tax, $this->ID, $this, $currency );
1414
    }
1415
    
1416
    public function get_fees( $type = 'all' ) {
1417
        $fees    = array();
1418
1419
        if ( ! empty( $this->fees ) && is_array( $this->fees ) ) {
1420
            foreach ( $this->fees as $fee ) {
1421 View Code Duplication
                if( 'all' != $type && ! empty( $fee['type'] ) && $type != $fee['type'] ) {
1422
                    continue;
1423
                }
1424
1425
                $fee['label'] = stripslashes( $fee['label'] );
1426
                $fee['amount_display'] = wpinv_price( $fee['amount'], $this->get_currency() );
1427
                $fees[]    = $fee;
1428
            }
1429
        }
1430
1431
        return apply_filters( 'wpinv_get_invoice_fees', $fees, $this->ID, $this );
1432
    }
1433
    
1434
    public function get_fees_total( $type = 'all' ) {
1435
        $fees_total = (float) 0.00;
1436
1437
        $payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
1438
        if ( ! empty( $payment_fees ) ) {
1439
            foreach ( $payment_fees as $fee ) {
1440
                $fees_total += (float) $fee['amount'];
1441
            }
1442
        }
1443
1444
        return apply_filters( 'wpinv_get_invoice_fees_total', $fees_total, $this->ID, $this );
1445
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1446
        $fees = $this->get_fees( $type );
1447
1448
        $fees_total = 0;
1449
        if ( ! empty( $fees ) && is_array( $fees ) ) {
1450
            foreach ( $fees as $fee_id => $fee ) {
1451
                if( 'all' != $type && !empty( $fee['type'] ) && $type != $fee['type'] ) {
1452
                    continue;
1453
                }
1454
1455
                $fees_total += $fee['amount'];
1456
            }
1457
        }
1458
1459
        return apply_filters( 'wpinv_get_invoice_fees_total', $fees_total, $this->ID, $this );
1460
        */
1461
    }
1462
1463
    public function get_user_id() {
1464
        return apply_filters( 'wpinv_user_id', $this->user_id, $this->ID, $this );
1465
    }
1466
    
1467
    public function get_first_name() {
1468
        return apply_filters( 'wpinv_first_name', $this->first_name, $this->ID, $this );
1469
    }
1470
    
1471
    public function get_last_name() {
1472
        return apply_filters( 'wpinv_last_name', $this->last_name, $this->ID, $this );
1473
    }
1474
    
1475
    public function get_user_full_name() {
1476
        return apply_filters( 'wpinv_user_full_name', $this->full_name, $this->ID, $this );
1477
    }
1478
    
1479
    public function get_user_info() {
1480
        return apply_filters( 'wpinv_user_info', $this->user_info, $this->ID, $this );
1481
    }
1482
    
1483
    public function get_email() {
1484
        return apply_filters( 'wpinv_user_email', $this->email, $this->ID, $this );
1485
    }
1486
    
1487
    public function get_address() {
1488
        return apply_filters( 'wpinv_address', $this->address, $this->ID, $this );
1489
    }
1490
    
1491
    public function get_phone() {
1492
        return apply_filters( 'wpinv_phone', $this->phone, $this->ID, $this );
1493
    }
1494
    
1495
    public function get_number() {
1496
        return apply_filters( 'wpinv_number', $this->number, $this->ID, $this );
1497
    }
1498
    
1499
    public function get_items() {
1500
        return apply_filters( 'wpinv_payment_meta_items', $this->items, $this->ID, $this );
1501
    }
1502
    
1503
    public function get_key() {
1504
        return apply_filters( 'wpinv_key', $this->key, $this->ID, $this );
1505
    }
1506
    
1507
    public function get_transaction_id() {
1508
        return apply_filters( 'wpinv_get_invoice_transaction_id', $this->transaction_id, $this->ID, $this );
1509
    }
1510
    
1511
    public function get_gateway() {
1512
        return apply_filters( 'wpinv_gateway', $this->gateway, $this->ID, $this );
1513
    }
1514
    
1515
    public function get_gateway_title() {
1516
        $this->gateway_title = !empty( $this->gateway_title ) ? $this->gateway_title : wpinv_get_gateway_checkout_label( $this->gateway );
1517
        
1518
        return apply_filters( 'wpinv_gateway_title', $this->gateway_title, $this->ID, $this );
1519
    }
1520
    
1521
    public function get_currency() {
1522
        return apply_filters( 'wpinv_currency_code', $this->currency, $this->ID, $this );
1523
    }
1524
    
1525
    public function get_created_date() {
1526
        return apply_filters( 'wpinv_created_date', $this->date, $this->ID, $this );
1527
    }
1528
    
1529
    public function get_due_date( $display = false ) {
1530
        $due_date = apply_filters( 'wpinv_due_date', $this->due_date, $this->ID, $this );
1531
        
1532
        if ( !$display || empty( $due_date ) ) {
1533
            return $due_date;
1534
        }
1535
        
1536
        return date_i18n( get_option( 'date_format' ), strtotime( $due_date ) );
1537
    }
1538
    
1539
    public function get_completed_date() {
1540
        return apply_filters( 'wpinv_completed_date', $this->completed_date, $this->ID, $this );
1541
    }
1542
    
1543
    public function get_invoice_date( $formatted = true ) {
1544
        $date_completed = $this->completed_date;
1545
        $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? $date_completed : '';
1546
        
1547
        if ( $invoice_date == '' ) {
1548
            $date_created   = $this->date;
1549
            $invoice_date   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? $date_created : '';
1550
        }
1551
        
1552
        if ( $formatted && $invoice_date ) {
1553
            $invoice_date   = date_i18n( get_option( 'date_format' ), strtotime( $invoice_date ) );
1554
        }
1555
1556
        return apply_filters( 'wpinv_get_invoice_date', $invoice_date, $formatted, $this->ID, $this );
1557
    }
1558
    
1559
    public function get_ip() {
1560
        return apply_filters( 'wpinv_user_ip', $this->ip, $this->ID, $this );
1561
    }
1562
        
1563
    public function has_status( $status ) {
1564
        return apply_filters( 'wpinv_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
1565
    }
1566
    
1567
    public function add_item( $item_id = 0, $args = array() ) {
1568
        global $wpi_current_id, $wpi_item_id;
1569
        
1570
        $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...
1571
1572
        // Bail if this post isn't a item
1573
        if( !$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...
1574
            return false;
1575
        }
1576
        
1577
        $has_quantities = wpinv_item_quantities_enabled();
1578
1579
        // Set some defaults
1580
        $defaults = array(
1581
            'quantity'      => 1,
1582
            'id'            => false,
1583
            'name'          => $item->get_name(),
1584
            'item_price'    => false,
1585
            'custom_price'  => '',
1586
            'discount'      => 0,
1587
            'tax'           => 0.00,
1588
            'meta'          => array(),
1589
            'fees'          => array()
1590
        );
1591
1592
        $args = wp_parse_args( apply_filters( 'wpinv_add_item_args', $args, $item->ID ), $defaults );
1593
        $args['quantity']   = $has_quantities && $args['quantity'] > 0 ? absint( $args['quantity'] ) : 1;
1594
1595
        $wpi_current_id         = $this->ID;
1596
        $wpi_item_id            = $item->ID;
1597
        $discounts              = $this->get_discounts();
0 ignored issues
show
Unused Code introduced by
$discounts 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...
1598
        
1599
        $_POST['wpinv_country'] = $this->country;
1600
        $_POST['wpinv_state']   = $this->state;
1601
        
1602
        $found_cart_key         = false;
1603
        
1604
        if ($has_quantities) {
1605
            $this->cart_details = !empty( $this->cart_details ) ? array_values( $this->cart_details ) : $this->cart_details;
1606
            
1607
            foreach ( $this->items as $key => $cart_item ) {
1608
                if ( (int)$item_id !== (int)$cart_item['id'] ) {
1609
                    continue;
1610
                }
1611
1612
                $this->items[ $key ]['quantity'] += $args['quantity'];
1613
                break;
1614
            }
1615
            
1616
            foreach ( $this->cart_details as $cart_key => $cart_item ) {
1617
                if ( $item_id != $cart_item['id'] ) {
1618
                    continue;
1619
                }
1620
1621
                $found_cart_key = $cart_key;
1622
                break;
1623
            }
1624
        }
1625
        
1626
        if ($has_quantities && $found_cart_key !== false) {
1627
            $cart_item          = $this->cart_details[$found_cart_key];
1628
            $item_price         = $cart_item['item_price'];
1629
            $quantity           = !empty( $cart_item['quantity'] ) ? $cart_item['quantity'] : 1;
1630
            $tax_rate           = !empty( $cart_item['vat_rate'] ) ? $cart_item['vat_rate'] : 0;
1631
            
1632
            $new_quantity       = $quantity + $args['quantity'];
1633
            $subtotal           = $item_price * $new_quantity;
1634
            
1635
            $args['quantity']   = $new_quantity;
1636
            $discount           = !empty( $args['discount'] ) ? $args['discount'] : 0;
1637
            $tax                = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1638
            
1639
            $discount_increased = $discount > 0 && $subtotal > 0 && $discount > (float)$cart_item['discount'] ? $discount - (float)$cart_item['discount'] : 0;
1640
            $tax_increased      = $tax > 0 && $subtotal > 0 && $tax > (float)$cart_item['tax'] ? $tax - (float)$cart_item['tax'] : 0;
1641
            // The total increase equals the number removed * the item_price
1642
            $total_increased    = wpinv_round_amount( $item_price );
1643
            
1644
            if ( wpinv_prices_include_tax() ) {
1645
                $subtotal -= wpinv_round_amount( $tax );
1646
            }
1647
1648
            $total              = $subtotal - $discount + $tax;
1649
1650
            // Do not allow totals to go negative
1651
            if( $total < 0 ) {
1652
                $total = 0;
1653
            }
1654
            
1655
            $cart_item['quantity']  = $new_quantity;
1656
            $cart_item['subtotal']  = $subtotal;
1657
            $cart_item['discount']  = $discount;
1658
            $cart_item['tax']       = $tax;
1659
            $cart_item['price']     = $total;
1660
            
1661
            $subtotal               = $total_increased - $discount_increased;
1662
            $tax                    = $tax_increased;
1663
            
1664
            $this->cart_details[$found_cart_key] = $cart_item;
1665
        } else {
1666
            // Set custom price.
1667
            if ( $args['custom_price'] !== '' ) {
1668
                $item_price = $args['custom_price'];
1669
            } else {
1670
                // Allow overriding the price
1671
                if ( false !== $args['item_price'] ) {
1672
                    $item_price = $args['item_price'];
1673
                } else {
1674
                    $item_price = wpinv_get_item_price( $item->ID );
1675
                }
1676
            }
1677
1678
            // Sanitizing the price here so we don't have a dozen calls later
1679
            $item_price = wpinv_sanitize_amount( $item_price );
1680
            $subtotal   = wpinv_round_amount( $item_price * $args['quantity'] );
1681
        
1682
            $discount   = !empty( $args['discount'] ) ? $args['discount'] : 0;
1683
            $tax_class  = !empty( $args['vat_class'] ) ? $args['vat_class'] : '';
1684
            $tax_rate   = !empty( $args['vat_rate'] ) ? $args['vat_rate'] : 0;
1685
            $tax        = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1686
1687
            // Setup the items meta item
1688
            $new_item = array(
1689
                'id'       => $item->ID,
1690
                'quantity' => $args['quantity'],
1691
            );
1692
1693
            $this->items[]  = $new_item;
1694
1695
            if ( wpinv_prices_include_tax() ) {
1696
                $subtotal -= wpinv_round_amount( $tax );
1697
            }
1698
1699
            $total      = $subtotal - $discount + $tax;
1700
1701
            // Do not allow totals to go negative
1702
            if( $total < 0 ) {
1703
                $total = 0;
1704
            }
1705
        
1706
            $this->cart_details[] = array(
1707
                'name'          => !empty($args['name']) ? $args['name'] : $item->get_name(),
1708
                'id'            => $item->ID,
1709
                'item_price'    => wpinv_round_amount( $item_price ),
1710
                'custom_price'  => ( $args['custom_price'] !== '' ? wpinv_round_amount( $args['custom_price'] ) : '' ),
1711
                'quantity'      => $args['quantity'],
1712
                'discount'      => $discount,
1713
                'subtotal'      => wpinv_round_amount( $subtotal ),
1714
                'tax'           => wpinv_round_amount( $tax ),
1715
                'price'         => wpinv_round_amount( $total ),
1716
                'vat_rate'      => $tax_rate,
1717
                'vat_class'     => $tax_class,
1718
                'meta'          => $args['meta'],
1719
                'fees'          => $args['fees'],
1720
            );
1721
                        
1722
            $subtotal = $subtotal - $discount;
1723
        }
1724
        
1725
        $added_item = end( $this->cart_details );
1726
        $added_item['action']  = 'add';
1727
        
1728
        $this->pending['items'][] = $added_item;
1729
        
1730
        $this->increase_subtotal( $subtotal );
1731
        $this->increase_tax( $tax );
1732
1733
        return true;
1734
    }
1735
    
1736
    public function remove_item( $item_id, $args = array() ) {
1737
        // Set some defaults
1738
        $defaults = array(
1739
            'quantity'      => 1,
1740
            'item_price'    => false,
1741
            'custom_price'  => '',
1742
            'cart_index'    => false,
1743
        );
1744
        $args = wp_parse_args( $args, $defaults );
1745
1746
        // Bail if this post isn't a item
1747
        if ( get_post_type( $item_id ) !== 'wpi_item' ) {
1748
            return false;
1749
        }
1750
        
1751
        $this->cart_details = !empty( $this->cart_details ) ? array_values( $this->cart_details ) : $this->cart_details;
1752
1753
        foreach ( $this->items as $key => $item ) {
1754
            if ( !empty($item['id']) && (int)$item_id !== (int)$item['id'] ) {
1755
                continue;
1756
            }
1757
1758
            if ( false !== $args['cart_index'] ) {
1759
                $cart_index = absint( $args['cart_index'] );
1760
                $cart_item  = ! empty( $this->cart_details[ $cart_index ] ) ? $this->cart_details[ $cart_index ] : false;
1761
1762
                if ( ! empty( $cart_item ) ) {
1763
                    // If the cart index item isn't the same item ID, don't remove it
1764
                    if ( !empty($cart_item['id']) && $cart_item['id'] != $item['id'] ) {
1765
                        continue;
1766
                    }
1767
                }
1768
            }
1769
1770
            $item_quantity = $this->items[ $key ]['quantity'];
1771
            if ( $item_quantity > $args['quantity'] ) {
1772
                $this->items[ $key ]['quantity'] -= $args['quantity'];
1773
                break;
1774
            } else {
1775
                unset( $this->items[ $key ] );
1776
                break;
1777
            }
1778
        }
1779
1780
        $found_cart_key = false;
1781
        if ( false === $args['cart_index'] ) {
1782
            foreach ( $this->cart_details as $cart_key => $item ) {
1783
                if ( $item_id != $item['id'] ) {
1784
                    continue;
1785
                }
1786
1787
                if ( false !== $args['item_price'] ) {
1788
                    if ( isset( $item['item_price'] ) && (float) $args['item_price'] != (float) $item['item_price'] ) {
1789
                        continue;
1790
                    }
1791
                }
1792
1793
                $found_cart_key = $cart_key;
1794
                break;
1795
            }
1796
        } else {
1797
            $cart_index = absint( $args['cart_index'] );
1798
1799
            if ( ! array_key_exists( $cart_index, $this->cart_details ) ) {
1800
                return false; // Invalid cart index passed.
1801
            }
1802
1803
            if ( (int) $this->cart_details[ $cart_index ]['id'] > 0 && (int) $this->cart_details[ $cart_index ]['id'] !== (int) $item_id ) {
1804
                return false; // We still need the proper Item ID to be sure.
1805
            }
1806
1807
            $found_cart_key = $cart_index;
1808
        }
1809
        
1810
        $cart_item  = $this->cart_details[$found_cart_key];
1811
        $quantity   = !empty( $cart_item['quantity'] ) ? $cart_item['quantity'] : 1;
1812
        
1813
        if ( count( $this->cart_details ) == 1 && ( $quantity - $args['quantity'] ) < 1 ) {
1814
            return false; // Invoice must contain at least one item.
1815
        }
1816
        
1817
        $discounts  = $this->get_discounts();
0 ignored issues
show
Unused Code introduced by
$discounts 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...
1818
        
1819
        if ( $quantity > $args['quantity'] ) {
1820
            $item_price         = $cart_item['item_price'];
1821
            $tax_rate           = !empty( $cart_item['vat_rate'] ) ? $cart_item['vat_rate'] : 0;
1822
            
1823
            $new_quantity       = max( $quantity - $args['quantity'], 1);
1824
            $subtotal           = $item_price * $new_quantity;
1825
            
1826
            $args['quantity']   = $new_quantity;
1827
            $discount           = !empty( $cart_item['discount'] ) ? $cart_item['discount'] : 0;
1828
            $tax                = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1829
            
1830
            $discount_decrease  = (float)$cart_item['discount'] > 0 && $quantity > 0 ? wpinv_round_amount( ( (float)$cart_item['discount'] / $quantity ) ) : 0;
1831
            $discount_decrease  = $discount > 0 && $subtotal > 0 && (float)$cart_item['discount'] > $discount ? (float)$cart_item['discount'] - $discount : $discount_decrease; 
1832
            $tax_decrease       = (float)$cart_item['tax'] > 0 && $quantity > 0 ? wpinv_round_amount( ( (float)$cart_item['tax'] / $quantity ) ) : 0;
1833
            $tax_decrease       = $tax > 0 && $subtotal > 0 && (float)$cart_item['tax'] > $tax ? (float)$cart_item['tax'] - $tax : $tax_decrease;
1834
            
1835
            // The total increase equals the number removed * the item_price
1836
            $total_decrease     = wpinv_round_amount( $item_price );
1837
            
1838
            if ( wpinv_prices_include_tax() ) {
1839
                $subtotal -= wpinv_round_amount( $tax );
1840
            }
1841
1842
            $total              = $subtotal - $discount + $tax;
1843
1844
            // Do not allow totals to go negative
1845
            if( $total < 0 ) {
1846
                $total = 0;
1847
            }
1848
            
1849
            $cart_item['quantity']  = $new_quantity;
1850
            $cart_item['subtotal']  = $subtotal;
1851
            $cart_item['discount']  = $discount;
1852
            $cart_item['tax']       = $tax;
1853
            $cart_item['price']     = $total;
1854
            
1855
            $added_item             = $cart_item;
1856
            $added_item['id']       = $item_id;
1857
            $added_item['price']    = $total_decrease;
1858
            $added_item['quantity'] = $args['quantity'];
1859
            
1860
            $subtotal_decrease      = $total_decrease - $discount_decrease;
1861
            
1862
            $this->cart_details[$found_cart_key] = $cart_item;
1863
            
1864
            $remove_item = end( $this->cart_details );
1865
        } else {
1866
            $item_price     = $cart_item['item_price'];
1867
            $discount       = !empty( $cart_item['discount'] ) ? $cart_item['discount'] : 0;
1868
            $tax            = !empty( $cart_item['tax'] ) ? $cart_item['tax'] : 0;
1869
        
1870
            $subtotal_decrease  = ( $item_price * $quantity ) - $discount;
1871
            $tax_decrease       = $tax;
1872
1873
            unset( $this->cart_details[$found_cart_key] );
1874
            
1875
            $remove_item             = $args;
1876
            $remove_item['id']       = $item_id;
1877
            $remove_item['price']    = $subtotal_decrease;
1878
            $remove_item['quantity'] = $args['quantity'];
1879
        }
1880
        
1881
        $remove_item['action']      = 'remove';
1882
        $this->pending['items'][]   = $remove_item;
1883
               
1884
        $this->decrease_subtotal( $subtotal_decrease );
1885
        $this->decrease_tax( $tax_decrease );
1886
        
1887
        return true;
1888
    }
1889
    
1890
    public function update_items($temp = false) {
1891
        global $wpinv_euvat, $wpi_current_id, $wpi_item_id, $wpi_nosave;
1892
        
1893
        if ( !empty( $this->cart_details ) ) {
1894
            $wpi_nosave             = $temp;
1895
            $cart_subtotal          = 0;
1896
            $cart_discount          = 0;
1897
            $cart_tax               = 0;
1898
            $cart_details           = array();
1899
            
1900
            $_POST['wpinv_country'] = $this->country;
1901
            $_POST['wpinv_state']   = $this->state;
1902
            
1903
            foreach ( $this->cart_details as $key => $item ) {
1904
                $item_price = $item['item_price'];
1905
                $quantity   = wpinv_item_quantities_enabled() && $item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
1906
                $amount     = wpinv_round_amount( $item_price * $quantity );
0 ignored issues
show
Unused Code introduced by
$amount 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...
1907
                $subtotal   = $item_price * $quantity;
1908
                
1909
                $wpi_current_id         = $this->ID;
1910
                $wpi_item_id            = $item['id'];
1911
                
1912
                $discount   = wpinv_get_cart_item_discount_amount( $item, $this->get_discounts() );
1913
                
1914
                $tax_rate   = wpinv_get_tax_rate( $this->country, $this->state, $wpi_item_id );
0 ignored issues
show
Documentation introduced by
$this->country 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...
Documentation introduced by
$this->state 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...
1915
                $tax_class  = $wpinv_euvat->get_item_class( $wpi_item_id );
1916
                $tax        = $item_price > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1917
1918
                if ( wpinv_prices_include_tax() ) {
1919
                    $subtotal -= wpinv_round_amount( $tax );
1920
                }
1921
1922
                $total      = $subtotal - $discount + $tax;
1923
1924
                // Do not allow totals to go negative
1925
                if( $total < 0 ) {
1926
                    $total = 0;
1927
                }
1928
1929
                $cart_details[] = array(
1930
                    'id'          => $item['id'],
1931
                    'name'        => $item['name'],
1932
                    'item_price'  => wpinv_round_amount( $item_price ),
1933
                    'custom_price'=> ( isset( $item['custom_price'] ) ? $item['custom_price'] : '' ),
1934
                    'quantity'    => $quantity,
1935
                    'discount'    => $discount,
1936
                    'subtotal'    => wpinv_round_amount( $subtotal ),
1937
                    'tax'         => wpinv_round_amount( $tax ),
1938
                    'price'       => wpinv_round_amount( $total ),
1939
                    'vat_rate'    => $tax_rate,
1940
                    'vat_class'   => $tax_class,
1941
                    'meta'        => isset($item['meta']) ? $item['meta'] : array(),
1942
                    'fees'        => isset($item['fees']) ? $item['fees'] : array(),
1943
                );
1944
                
1945
                $cart_subtotal  += (float)($subtotal - $discount); // TODO
1946
                $cart_discount  += (float)($discount);
1947
                $cart_tax       += (float)($tax);
1948
            }
1949
            $this->subtotal = wpinv_round_amount( $cart_subtotal );
1950
            $this->tax      = wpinv_round_amount( $cart_tax );
1951
            $this->discount = wpinv_round_amount( $cart_discount );
1952
            
1953
            $this->recalculate_total();
1954
            
1955
            $this->cart_details = $cart_details;
1956
        }
1957
1958
        return $this;
1959
    }
1960
    
1961
    public function recalculate_totals($temp = false) {        
1962
        $this->update_items($temp);
1963
        $this->save( true );
1964
        
1965
        return $this;
1966
    }
1967
    
1968
    public function needs_payment() {
1969
        $valid_invoice_statuses = apply_filters( 'wpinv_valid_invoice_statuses_for_payment', array( 'wpi-pending' ), $this );
1970
1971
        if ( $this->has_status( $valid_invoice_statuses ) && ( $this->get_total() > 0 || $this->is_free_trial() || $this->is_free() ) ) {
1972
            $needs_payment = true;
1973
        } else {
1974
            $needs_payment = false;
1975
        }
1976
1977
        return apply_filters( 'wpinv_needs_payment', $needs_payment, $this, $valid_invoice_statuses );
1978
    }
1979
    
1980
    public function get_checkout_payment_url( $with_key = false, $secret = false ) {
1981
        $pay_url = wpinv_get_checkout_uri();
1982
1983
        if ( is_ssl() ) {
1984
            $pay_url = str_replace( 'http:', 'https:', $pay_url );
1985
        }
1986
        
1987
        $key = $this->get_key();
1988
1989
        if ( $with_key ) {
1990
            $pay_url = add_query_arg( 'invoice_key', $key, $pay_url );
1991
        } else {
1992
            $pay_url = add_query_arg( array( 'wpi_action' => 'pay_for_invoice', 'invoice_key' => $key ), $pay_url );
1993
        }
1994
        
1995 View Code Duplication
        if ( $secret ) {
1996
            $pay_url = add_query_arg( array( '_wpipay' => md5( $this->get_user_id() . '::' . $this->get_email() . '::' . $key ) ), $pay_url );
1997
        }
1998
1999
        return apply_filters( 'wpinv_get_checkout_payment_url', $pay_url, $this, $with_key, $secret );
2000
    }
2001
    
2002
    public function get_view_url( $secret = false, $with_key = false ) {
2003
        $print_url = get_permalink( $this->ID );
2004
        
2005 View Code Duplication
        if ( $secret ) {
2006
            $print_url = add_query_arg( array( '_wpipay' => md5( $this->get_user_id() . '::' . $this->get_email() . '::' . $this->get_key() ) ), $print_url );
2007
        }
2008
        
2009
        if ( $with_key ) {
2010
            $print_url = add_query_arg( 'invoice_key', $this->get_key(), $print_url );
2011
        }
2012
2013
        return apply_filters( 'wpinv_get_view_url', $print_url, $this, $secret, $with_key );
2014
    }
2015
    
2016
    public function generate_key( $string = '' ) {
2017
        $auth_key  = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
2018
        return strtolower( md5( $string . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'wpinv', true ) ) );  // Unique key
2019
    }
2020
    
2021
    public function is_recurring() {
2022
        if ( empty( $this->cart_details ) ) {
2023
            return false;
2024
        }
2025
        
2026
        $has_subscription = false;
2027 View Code Duplication
        foreach( $this->cart_details as $cart_item ) {
2028
            if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
2029
                $has_subscription = true;
2030
                break;
2031
            }
2032
        }
2033
        
2034
        if ( count( $this->cart_details ) > 1 ) {
2035
            $has_subscription = false;
2036
        }
2037
2038
        return apply_filters( 'wpinv_invoice_has_recurring_item', $has_subscription, $this->cart_details );
2039
    }
2040
    
2041
    public function is_free_trial() {
2042
        $is_free_trial = false;
2043
        
2044
        if ( $this->is_parent() && $item = $this->get_recurring( true ) ) {
2045
            if ( !empty( $item ) && $item->has_free_trial() ) {
2046
                $is_free_trial = true;
2047
            }
2048
        }
2049
2050
        return apply_filters( 'wpinv_invoice_is_free_trial', $is_free_trial, $this->cart_details );
2051
    }
2052
    
2053
    public function get_recurring( $object = false ) {
2054
        $item = NULL;
2055
        
2056
        if ( empty( $this->cart_details ) ) {
2057
            return $item;
2058
        }
2059
        
2060 View Code Duplication
        foreach( $this->cart_details as $cart_item ) {
2061
            if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
2062
                $item = $cart_item['id'];
2063
                break;
2064
            }
2065
        }
2066
        
2067
        if ( $object ) {
2068
            $item = $item ? new WPInv_Item( $item ) : NULL;
2069
            
2070
            apply_filters( 'wpinv_invoice_get_recurring_item', $item, $this );
2071
        }
2072
2073
        return apply_filters( 'wpinv_invoice_get_recurring_item_id', $item, $this );
2074
    }
2075
    
2076
    public function get_subscription_name() {
2077
        $item = $this->get_recurring( true );
2078
        
2079
        if ( empty( $item ) ) {
2080
            return NULL;
2081
        }
2082
        
2083
        if ( !($name = $item->get_name()) ) {
2084
            $name = $item->post_name;
2085
        }
2086
2087
        return apply_filters( 'wpinv_invoice_get_subscription_name', $name, $this );
2088
    }
2089
        
2090
    public function get_expiration() {
2091
        $expiration = $this->get_meta( '_wpinv_subscr_expiration', true );
2092
        return $expiration;
2093
    }
2094
    
2095
    public function get_cancelled_date( $formatted = true ) {
2096
        $cancelled_date = $this->get_subscription_status() == 'cancelled' ? $this->get_meta( '_wpinv_subscr_cancelled_on', true ) : '';
2097
        
2098
        if ( $formatted && $cancelled_date ) {
2099
            $cancelled_date = date_i18n( get_option( 'date_format' ), strtotime( $cancelled_date ) );
2100
        }
2101
        
2102
        return $cancelled_date;
2103
    }
2104
    
2105
    public function get_trial_end_date( $formatted = true ) {
2106
        if ( !$this->is_free_trial() || !$this->is_paid() ) {
2107
            return NULL;
2108
        }
2109
        
2110
        $trial_end_date = $this->get_subscription_status() == 'trialing' ? $this->get_meta( '_wpinv_subscr_trial_end', true ) : '';
2111
        
2112
        if ( empty( $trial_end_date ) ) {
2113
            $trial_start_time = strtotime( $this->get_subscription_start() );
2114
            $trial_start_time += ( wpinv_period_in_days( $this->get_subscription_trial_interval(), $this->get_subscription_trial_period() ) * DAY_IN_SECONDS ) ;
2115
            
2116
            $trial_end_date = date_i18n( 'Y-m-d H:i:s', $trial_start_time );
2117
        }
2118
        
2119
        if ( $formatted && $trial_end_date ) {
2120
            $trial_end_date = date_i18n( get_option( 'date_format' ), strtotime( $trial_end_date ) );
2121
        }
2122
        
2123
        return $trial_end_date;
2124
    }
2125
    
2126
    public function get_subscription_created( $default = true ) {
2127
        $created = $this->get_meta( '_wpinv_subscr_created', true );
2128
        
2129
        if ( empty( $created ) && $default ) {
2130
            $created = $this->date;
2131
        }
2132
        return $created;
2133
    }
2134
    
2135
    public function get_subscription_start( $formatted = true ) {
2136
        if ( !$this->is_paid() ) {
2137
            return '-';
2138
        }
2139
        $start   = $this->get_subscription_created();
2140
        
2141
        if ( $formatted ) {
2142
            $date = date_i18n( get_option( 'date_format' ), strtotime( $start ) );
2143
        } else {
2144
            $date = date_i18n( 'Y-m-d H:i:s', strtotime( $start ) );
2145
        }
2146
2147
        return $date;
2148
    }
2149
    
2150
    public function get_subscription_end( $formatted = true ) {
2151
        if ( !$this->is_paid() ) {
2152
            return '-';
2153
        }
2154
        $start          = $this->get_subscription_created();
2155
        $interval       = $this->get_subscription_interval();
2156
        $period         = $this->get_subscription_period( true );
2157
        $bill_times     = (int)$this->get_bill_times();
2158
        
2159
        if ( $bill_times == 0 ) {
2160
            return $formatted ? __( 'Until cancelled', 'invoicing' ) : $bill_times;
2161
        }
2162
        
2163
        $total_period = $start . '+' . ( $interval * $bill_times ) . ' ' . $period;
0 ignored issues
show
Unused Code introduced by
$total_period 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...
2164
        
2165
        $end_time = strtotime( $start . '+' . ( $interval * $bill_times ) . ' ' . $period );
2166
        
2167
        if ( $this->is_free_trial() ) {
2168
            $end_time += ( wpinv_period_in_days( $this->get_subscription_trial_interval(), $this->get_subscription_trial_period() ) * DAY_IN_SECONDS ) ;
2169
        }
2170
        
2171
        if ( $formatted ) {
2172
            $date = date_i18n( get_option( 'date_format' ), $end_time );
2173
        } else {
2174
            $date = date_i18n( 'Y-m-d H:i:s', $end_time );
2175
        }
2176
2177
        return $date;
2178
    }
2179
    
2180
    public function get_expiration_time() {
2181
        return strtotime( $this->get_expiration(), current_time( 'timestamp' ) );
2182
    }
2183
    
2184
    public function get_original_invoice_id() {        
2185
        return $this->parent_invoice;
2186
    }
2187
    
2188
    public function get_bill_times() {
2189
        $subscription_data = $this->get_subscription_data();
2190
        return $subscription_data['bill_times'];
2191
    }
2192
2193
    public function get_child_payments( $self = false ) {
2194
        $invoices = get_posts( array(
2195
            'post_type'         => $this->post_type,
2196
            'post_parent'       => (int)$this->ID,
2197
            'posts_per_page'    => '999',
2198
            'post_status'       => array( 'publish', 'wpi-processing', 'wpi-renewal' ),
2199
            'orderby'           => 'ID',
2200
            'order'             => 'DESC',
2201
            'fields'            => 'ids'
2202
        ) );
2203
        
2204
        if ( $this->is_free_trial() ) {
2205
            $self = false;
2206
        }
2207
        
2208
        if ( $self && $this->is_paid() ) {
2209
            if ( !empty( $invoices ) ) {
2210
                $invoices[] = (int)$this->ID;
2211
            } else {
2212
                $invoices = array( $this->ID );
2213
            }
2214
            
2215
            $invoices = array_unique( $invoices );
2216
        }
2217
2218
        return $invoices;
2219
    }
2220
2221
    public function get_total_payments( $self = true ) {
2222
        return count( $this->get_child_payments( $self ) );
2223
    }
2224
    
2225
    public function get_subscriptions( $limit = -1 ) {
2226
        $subscriptions = wpinv_get_subscriptions( array( 'parent_invoice_id' => $this->ID, 'numberposts' => $limit ) );
2227
2228
        return $subscriptions;
2229
    }
2230
    
2231
    public function get_subscription_id() {
2232
        $subscription_id = $this->get_meta( '_wpinv_subscr_profile_id', true );
2233
        
2234
        if ( empty( $subscription_id ) && !empty( $this->parent_invoice ) ) {
2235
            $parent_invoice = wpinv_get_invoice( $this->parent_invoice );
2236
            
2237
            $subscription_id = $parent_invoice->get_meta( '_wpinv_subscr_profile_id', true );
2238
        }
2239
        
2240
        return $subscription_id;
2241
    }
2242
    
2243
    public function get_subscription_status() {
2244
        $subscription_status = $this->get_meta( '_wpinv_subscr_status', true );
2245
2246
        if ( empty( $subscription_status ) ) {
2247
            $status = 'pending';
2248
            
2249
            if ( $this->is_paid() ) {        
2250
                $bill_times   = (int)$this->get_bill_times();
2251
                $times_billed = (int)$this->get_total_payments();
2252
                $expiration = $this->get_subscription_end( false );
2253
                $expired = $bill_times != 0 && $expiration != '' && $expiration != '-' && strtotime( date_i18n( 'Y-m-d', strtotime( $expiration ) ) ) < strtotime( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ) ? true : false;
2254
                
2255
                if ( (int)$bill_times == 0 ) {
2256
                    $status = $expired ? 'expired' : 'active';
2257
                } else if ( $bill_times > 0 && $times_billed >= $bill_times ) {
2258
                    $status = 'completed';
2259
                } else if ( $expired ) {
2260
                    $status = 'expired';
2261
                } else if ( $bill_times > 0 ) {
2262
                    $status = 'active';
2263
                } else {
2264
                    $status = 'pending';
2265
                }
2266
            }
2267
            
2268
            if ( $status && $status != $subscription_status ) {
2269
                $subscription_status = $status;
2270
                
2271
                $this->update_meta( '_wpinv_subscr_status', $status );
2272
            }
2273
        }
2274
        
2275
        return $subscription_status;
2276
    }
2277
    
2278
    public function get_subscription_status_label( $status = '' ) {
2279
        $status = !empty( $status ) ? $status : $this->get_subscription_status();
2280
2281
        switch( $status ) {
2282
            case 'active' :
2283
                $status_label = __( 'Active', 'invoicing' );
2284
                break;
2285
2286
            case 'cancelled' :
2287
                $status_label = __( 'Cancelled', 'invoicing' );
2288
                break;
2289
                
2290
            case 'completed' :
2291
                $status_label = __( 'Completed', 'invoicing' );
2292
                break;
2293
2294
            case 'expired' :
2295
                $status_label = __( 'Expired', 'invoicing' );
2296
                break;
2297
2298
            case 'pending' :
2299
                $status_label = __( 'Pending', 'invoicing' );
2300
                break;
2301
2302
            case 'failing' :
2303
                $status_label = __( 'Failing', 'invoicing' );
2304
                break;
2305
                
2306
            case 'stopped' :
2307
                $status_label = __( 'Stopped', 'invoicing' );
2308
                break;
2309
                
2310
            case 'trialing' :
2311
                $status_label = __( 'Trialing', 'invoicing' );
2312
                break;
2313
2314
            default:
2315
                $status_label = $status;
2316
                break;
2317
        }
2318
2319
        return $status_label;
2320
    }
2321
    
2322 View Code Duplication
    public function get_subscription_period( $full = false ) {
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...
2323
        $period = $this->get_meta( '_wpinv_subscr_period', true );
2324
        
2325
        // Fix period for old invoices
2326
        if ( $period == 'day' ) {
2327
            $period = 'D';
2328
        } else if ( $period == 'week' ) {
2329
            $period = 'W';
2330
        } else if ( $period == 'month' ) {
2331
            $period = 'M';
2332
        } else if ( $period == 'year' ) {
2333
            $period = 'Y';
2334
        }
2335
        
2336
        if ( !in_array( $period, array( 'D', 'W', 'M', 'Y' ) ) ) {
2337
            $period = 'D';
2338
        }
2339
        
2340
        if ( $full ) {
2341
            switch( $period ) {
2342
                case 'D':
2343
                    $period = 'day';
2344
                break;
2345
                case 'W':
2346
                    $period = 'week';
2347
                break;
2348
                case 'M':
2349
                    $period = 'month';
2350
                break;
2351
                case 'Y':
2352
                    $period = 'year';
2353
                break;
2354
            }
2355
        }
2356
        
2357
        return $period;
2358
    }
2359
    
2360
    public function get_subscription_interval() {
2361
        $interval = (int)$this->get_meta( '_wpinv_subscr_interval', true );
2362
        
2363
        if ( !$interval > 0 ) {
2364
            $interval = 1;
2365
        }
2366
        
2367
        return $interval;
2368
    }
2369
    
2370 View Code Duplication
    public function get_subscription_trial_period( $full = false ) {
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...
2371
        if ( !$this->is_free_trial() ) {
2372
            return '';
2373
        }
2374
        
2375
        $period = $this->get_meta( '_wpinv_subscr_trial_period', true );
2376
        
2377
        // Fix period for old invoices
2378
        if ( $period == 'day' ) {
2379
            $period = 'D';
2380
        } else if ( $period == 'week' ) {
2381
            $period = 'W';
2382
        } else if ( $period == 'month' ) {
2383
            $period = 'M';
2384
        } else if ( $period == 'year' ) {
2385
            $period = 'Y';
2386
        }
2387
        
2388
        if ( !in_array( $period, array( 'D', 'W', 'M', 'Y' ) ) ) {
2389
            $period = 'D';
2390
        }
2391
        
2392
        if ( $full ) {
2393
            switch( $period ) {
2394
                case 'D':
2395
                    $period = 'day';
2396
                break;
2397
                case 'W':
2398
                    $period = 'week';
2399
                break;
2400
                case 'M':
2401
                    $period = 'month';
2402
                break;
2403
                case 'Y':
2404
                    $period = 'year';
2405
                break;
2406
            }
2407
        }
2408
        
2409
        return $period;
2410
    }
2411
    
2412
    public function get_subscription_trial_interval() {
2413
        if ( !$this->is_free_trial() ) {
2414
            return 0;
2415
        }
2416
        
2417
        $interval = (int)$this->get_meta( '_wpinv_subscr_trial_interval', true );
2418
        
2419
        if ( !$interval > 0 ) {
2420
            $interval = 1;
2421
        }
2422
        
2423
        return $interval;
2424
    }
2425
    
2426 View Code Duplication
    public function failing_subscription() {
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...
2427
        $args = array(
2428
            'status' => 'failing'
2429
        );
2430
2431
        if ( $this->update_subscription( $args ) ) {
2432
            do_action( 'wpinv_subscription_failing', $this->ID, $this );
2433
            return true;
2434
        }
2435
2436
        return false;
2437
    }
2438
    
2439 View Code Duplication
    public function stop_subscription() {
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...
2440
        $args = array(
2441
            'status' => 'stopped'
2442
        );
2443
2444
        if ( $this->update_subscription( $args ) ) {
2445
            do_action( 'wpinv_subscription_stopped', $this->ID, $this );
2446
            return true;
2447
        }
2448
2449
        return false;
2450
    }
2451
    
2452 View Code Duplication
    public function restart_subscription() {
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...
2453
        $args = array(
2454
            'status' => 'active'
2455
        );
2456
2457
        if ( $this->update_subscription( $args ) ) {
2458
            do_action( 'wpinv_subscription_restarted', $this->ID, $this );
2459
            return true;
2460
        }
2461
2462
        return false;
2463
    }
2464
2465
    public function cancel_subscription() {
2466
        $args = array(
2467
            'status' => 'cancelled'
2468
        );
2469
2470
        if ( $this->update_subscription( $args ) ) {
2471
            if ( is_user_logged_in() ) {
2472
                $userdata = get_userdata( get_current_user_id() );
2473
                $user     = $userdata->user_login;
2474
            } else {
2475
                $user = __( 'gateway', 'invoicing' );
2476
            }
2477
            
2478
            $subscription_id = $this->get_subscription_id();
2479
            if ( !$subscription_id ) {
2480
                $subscription_id = $this->ID;
2481
            }
2482
2483
            $note = sprintf( __( 'Subscription %s has been cancelled by %s', 'invoicing' ), $subscription_id, $user );
2484
            $this->add_note( $note );
2485
2486
            do_action( 'wpinv_subscription_cancelled', $this->ID, $this );
2487
            return true;
2488
        }
2489
2490
        return false;
2491
    }
2492
2493
    public function can_cancel() {
2494
        return apply_filters( 'wpinv_subscription_can_cancel', false, $this );
2495
    }
2496
    
2497
    public function add_subscription( $data = array() ) {
2498
        if ( empty( $this->ID ) ) {
2499
            return false;
2500
        }
2501
2502
        $defaults = array(
2503
            'period'            => '',
2504
            'initial_amount'    => '',
2505
            'recurring_amount'  => '',
2506
            'interval'          => 0,
2507
            'trial_interval'    => 0,
2508
            'trial_period'      => '',
2509
            'bill_times'        => 0,
2510
            'item_id'           => 0,
2511
            'created'           => '',
2512
            'expiration'        => '',
2513
            'status'            => '',
2514
            'profile_id'        => '',
2515
        );
2516
2517
        $args = wp_parse_args( $data, $defaults );
2518
2519
        if ( $args['expiration'] && strtotime( 'NOW', current_time( 'timestamp' ) ) > strtotime( $args['expiration'], current_time( 'timestamp' ) ) ) {
2520
            if ( 'active' == $args['status'] || $args['status'] == 'trialing' ) {
2521
                $args['status'] = 'expired';
2522
            }
2523
        }
2524
2525
        do_action( 'wpinv_subscription_pre_create', $args, $data, $this );
2526
        
2527
        if ( !empty( $args ) ) {
2528
            foreach ( $args as $key => $value ) {
2529
                $this->update_meta( '_wpinv_subscr_' . $key, $value );
2530
            }
2531
        }
2532
2533
        do_action( 'wpinv_subscription_post_create', $args, $data, $this );
2534
2535
        return true;
2536
    }
2537
    
2538
    public function update_subscription( $args = array() ) {
2539
        if ( empty( $this->ID ) ) {
2540
            return false;
2541
        }
2542
2543
        if ( !empty( $args['expiration'] ) && $args['expiration'] && strtotime( 'NOW', current_time( 'timestamp' ) ) > strtotime( $args['expiration'], current_time( 'timestamp' ) ) ) {
2544
            if ( !isset( $args['status'] ) || ( isset( $args['status'] ) && ( 'active' == $args['status'] || $args['status'] == 'trialing' ) ) ) {
2545
                $args['status'] = 'expired';
2546
            }
2547
        }
2548
2549
        if ( isset( $args['status'] ) && $args['status'] == 'cancelled' && empty( $args['cancelled_on'] ) ) {
2550
            $args['cancelled_on'] = date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) );
2551
        }
2552
2553
        do_action( 'wpinv_subscription_pre_update', $args, $this );
2554
        
2555
        if ( !empty( $args ) ) {
2556
            foreach ( $args as $key => $value ) {
2557
                $this->update_meta( '_wpinv_subscr_' . $key, $value );
2558
            }
2559
        }
2560
2561
        do_action( 'wpinv_subscription_post_update', $args, $this );
2562
2563
        return true;
2564
    }
2565
    
2566
    public function renew_subscription() {
2567
        $parent_invoice = $this->get_parent_payment();
2568
        $parent_invoice = empty( $parent_invoice ) ? $this : $parent_invoice;
2569
        
2570
        $current_time   = current_time( 'timestamp' );
2571
        $start          = $this->get_subscription_created();
2572
        $start          = $start ? strtotime( $start ) : $current_time;
2573
        $expires        = $this->get_expiration_time();
2574
        
2575
        if ( !$expires ) {
2576
            $expires    = strtotime( '+' . $parent_invoice->get_subscription_interval() . ' ' . $parent_invoice->get_subscription_period( true ), $start );
2577
        }
2578
        
2579
        $expiration     = date_i18n( 'Y-m-d 23:59:59', $expires );
2580
        $expiration     = apply_filters( 'wpinv_subscription_renewal_expiration', $expiration, $this->ID, $this );
2581
        $bill_times     = $parent_invoice->get_bill_times();
2582
        $times_billed   = $parent_invoice->get_total_payments();
2583
        
2584
        if ( $parent_invoice->get_subscription_status() == 'trialing' && ( $times_billed > 0 || strtotime( date_i18n( 'Y-m-d' ) ) < strtotime( $parent_invoice->get_trial_end_date( false ) ) ) ) {
2585
            $args = array(
2586
                'status'     => 'active',
2587
            );
2588
2589
            $parent_invoice->update_subscription( $args );
2590
        }
2591
        
2592
        do_action( 'wpinv_subscription_pre_renew', $this->ID, $expiration, $this );
2593
2594
        $status       = 'active';
2595
        if ( $bill_times > 0 && $times_billed >= $bill_times ) {
2596
            $this->complete_subscription();
2597
            $status = 'completed';
2598
        }
2599
2600
        $args = array(
2601
            'expiration' => $expiration,
2602
            'status'     => $status,
2603
        );
2604
2605
        $this->update_subscription( $args );
2606
2607
        do_action( 'wpinv_subscription_post_renew', $this->ID, $expiration, $this );
2608
        do_action( 'wpinv_recurring_set_subscription_status', $this->ID, $status, $this );
2609
    }
2610
    
2611
    public function complete_subscription() {
2612
        $args = array(
2613
            'status' => 'completed'
2614
        );
2615
2616
        if ( $this->update_subscription( $args ) ) {
2617
            do_action( 'wpinv_subscription_completed', $this->ID, $this );
2618
        }
2619
    }
2620
    
2621
    public function expire_subscription() {
2622
        $args = array(
2623
            'status' => 'expired'
2624
        );
2625
2626
        if ( $this->update_subscription( $args ) ) {
2627
            do_action( 'wpinv_subscription_expired', $this->ID, $this );
2628
        }
2629
    }
2630
2631
    public function get_cancel_url() {
2632
        $url = wp_nonce_url( add_query_arg( array( 'wpi_action' => 'cancel_subscription', 'sub_id' => $this->ID ) ), 'wpinv-recurring-cancel' );
2633
2634
        return apply_filters( 'wpinv_subscription_cancel_url', $url, $this );
2635
    }
2636
2637
    public function can_update() {
2638
        return apply_filters( 'wpinv_subscription_can_update', false, $this );
2639
    }
2640
2641
    public function get_update_url() {
2642
        $url = add_query_arg( array( 'action' => 'update', 'sub_id' => $this->ID ) );
2643
2644
        return apply_filters( 'wpinv_subscription_update_url', $url, $this );
2645
    }
2646
2647
    public function is_parent() {
2648
        $is_parent = empty( $this->parent_invoice ) ? true : false;
2649
2650
        return apply_filters( 'wpinv_invoice_is_parent', $is_parent, $this );
2651
    }
2652
    
2653
    public function is_renewal() {
2654
        $is_renewal = $this->parent_invoice && $this->parent_invoice != $this->ID ? true : false;
2655
2656
        return apply_filters( 'wpinv_invoice_is_renewal', $is_renewal, $this );
2657
    }
2658
    
2659
    public function get_parent_payment() {
2660
        $parent_payment = NULL;
2661
        
2662
        if ( $this->is_renewal() ) {
2663
            $parent_payment = wpinv_get_invoice( $this->parent_invoice );
2664
        }
2665
        
2666
        return $parent_payment;
2667
    }
2668
    
2669
    public function is_subscription_active() {
2670
        $ret = false;
2671
        
2672
        $subscription_status = $this->get_subscription_status();
2673
2674
        if( ! $this->is_subscription_expired() && ( $subscription_status == 'active' || $subscription_status == 'cancelled' || $subscription_status == 'trialing' ) ) {
2675
            $ret = true;
2676
        }
2677
2678
        return apply_filters( 'wpinv_subscription_is_active', $ret, $this->ID, $this );
2679
    }
2680
2681
    public function is_subscription_expired() {
2682
        $ret = false;
2683
        $subscription_status = $this->get_subscription_status();
2684
2685
        if ( $subscription_status == 'expired' ) {
2686
            $ret = true;
2687
        } else if ( 'active' === $subscription_status || 'cancelled' === $subscription_status || 'trialing' == $subscription_status ) {
2688
            $ret        = false;
2689
            $expiration = $this->get_expiration_time();
2690
2691
            if ( $expiration && strtotime( 'NOW', current_time( 'timestamp' ) ) > $expiration ) {
2692
                $ret = true;
2693
2694
                if ( 'active' === $subscription_status || 'trialing' === $subscription_status ) {
2695
                    $this->expire_subscription();
2696
                }
2697
            }
2698
        }
2699
2700
        return apply_filters( 'wpinv_subscription_is_expired', $ret, $this->ID, $this );
2701
    }
2702
    
2703
    public function get_new_expiration( $item_id = 0, $trial = true ) {
2704
        $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...
2705
        $interval = $item->get_recurring_interval();
2706
        $period = $item->get_recurring_period( true );
2707
        
2708
        $expiration_time = strtotime( '+' . $interval . ' ' . $period );
2709
        
2710
        if ( $trial && $this->is_free_trial() && $item->has_free_trial() ) {
2711
            $expiration_time += ( wpinv_period_in_days( $item->get_trial_interval(), $item->get_trial_period() ) * DAY_IN_SECONDS ) ;
2712
        }
2713
2714
        return date_i18n( 'Y-m-d 23:59:59', $expiration_time );
2715
    }
2716
    
2717
    public function get_subscription_data( $filed = '' ) {
2718
        $fields = array( 'item_id', 'status', 'period', 'initial_amount', 'recurring_amount', 'interval', 'bill_times', 'trial_period', 'trial_interval', 'expiration', 'profile_id', 'created', 'cancelled_on' );
2719
        
2720
        $subscription_meta = array();
2721
        foreach ( $fields as $field ) {
2722
            $subscription_meta[ $field ] = $this->get_meta( '_wpinv_subscr_' . $field );
2723
        }
2724
        
2725
        $item = $this->get_recurring( true );
2726
        
2727
        if ( !empty( $item ) ) {
2728
            if ( empty( $subscription_meta['item_id'] ) ) {
2729
                $subscription_meta['item_id'] = $item->ID;
2730
            }
2731
            if ( empty( $subscription_meta['period'] ) ) {
2732
                $subscription_meta['period'] = $item->get_recurring_period();
2733
            }
2734
            if ( empty( $subscription_meta['interval'] ) ) {
2735
                $subscription_meta['interval'] = $item->get_recurring_interval();
2736
            }
2737
            if ( $item->has_free_trial() ) {
2738
                if ( empty( $subscription_meta['trial_period'] ) ) {
2739
                    $subscription_meta['trial_period'] = $item->get_trial_period();
2740
                }
2741
                if ( empty( $subscription_meta['trial_interval'] ) ) {
2742
                    $subscription_meta['trial_interval'] = $item->get_trial_interval();
2743
                }
2744
            } else {
2745
                $subscription_meta['trial_period']      = '';
2746
                $subscription_meta['trial_interval']    = 0;
2747
            }
2748
            if ( !$subscription_meta['bill_times'] && $subscription_meta['bill_times'] !== 0 ) {
2749
                $subscription_meta['bill_times'] = $item->get_recurring_limit();
2750
            }
2751
            if ( $subscription_meta['initial_amount'] === '' || $subscription_meta['recurring_amount'] === '' ) {
2752
                $subscription_meta['initial_amount']    = wpinv_round_amount( $this->get_total() );
2753
                $subscription_meta['recurring_amount']  = wpinv_round_amount( $this->get_recurring_details( 'total' ) );
2754
            }
2755
        }
2756
        
2757
        if ( $filed === '' ) {
2758
            return apply_filters( 'wpinv_get_invoice_subscription_data', $subscription_meta, $this );
2759
        }
2760
        
2761
        $value = isset( $subscription_meta[$filed] ) ? $subscription_meta[$filed] : '';
2762
        
2763
        return apply_filters( 'wpinv_invoice_subscription_data_value', $value, $subscription_meta, $this );
2764
    }
2765
    
2766
    public function is_paid() {
2767
        if ( $this->has_status( array( 'publish', 'wpi-processing', 'wpi-renewal' ) ) ) {
2768
            return true;
2769
        }
2770
        
2771
        return false;
2772
    }
2773
    
2774
    public function is_refunded() {
2775
        $is_refunded = $this->has_status( array( 'wpi-refunded' ) );
2776
2777
        return apply_filters( 'wpinv_invoice_is_refunded', $is_refunded, $this );
2778
    }
2779
    
2780
    public function is_free() {
2781
        $is_free = false;
2782
        
2783
        if ( !( (float)wpinv_round_amount( $this->get_total() ) > 0 ) ) {
2784
            if ( $this->is_parent() && $this->is_recurring() ) {
2785
                $is_free = (float)wpinv_round_amount( $this->get_recurring_details( 'total' ) ) > 0 ? false : true;
2786
            } else {
2787
                $is_free = true;
2788
            }
2789
        }
2790
        
2791
        return apply_filters( 'wpinv_invoice_is_free', $is_free, $this );
2792
    }
2793
    
2794
    public function has_vat() {
2795
        global $wpinv_euvat, $wpi_country;
2796
        
2797
        $requires_vat = false;
2798
        
2799
        if ( $this->country ) {
2800
            $wpi_country        = $this->country;
2801
            
2802
            $requires_vat       = $wpinv_euvat->requires_vat( $requires_vat, $this->get_user_id(), $wpinv_euvat->invoice_has_digital_rule( $this ) );
2803
        }
2804
        
2805
        return apply_filters( 'wpinv_invoice_has_vat', $requires_vat, $this );
2806
    }
2807
    
2808
    public function refresh_item_ids() {
2809
        $item_ids = array();
2810
        
2811
        if ( !empty( $this->cart_details ) ) {
2812
            foreach ( $this->cart_details as $key => $item ) {
2813
                if ( !empty( $item['id'] ) ) {
2814
                    $item_ids[] = $item['id'];
2815
                }
2816
            }
2817
        }
2818
        
2819
        $item_ids = !empty( $item_ids ) ? implode( ',', array_unique( $item_ids ) ) : '';
2820
        
2821
        update_post_meta( $this->ID, '_wpinv_item_ids', $item_ids );
2822
    }
2823
    
2824
    public function get_invoice_quote_type( $post_id ) {
2825
        if ( empty( $post_id ) ) {
2826
            return '';
2827
        }
2828
2829
        $type = get_post_type( $post_id );
2830
2831
        if ( 'wpi_invoice' === $type ) {
2832
            $post_type = __('Invoice', 'invoicing');
2833
        } else{
2834
            $post_type = __('Quote', 'invoicing');
2835
        }
2836
2837
        return $post_type;
2838
    }
2839
}
2840