Completed
Push — master ( 9700bf...c850cb )
by Stiofan
11s
created

WPInv_Invoice::get_subscription_end()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 20
nc 8
nop 1
dl 0
loc 34
rs 5.3846
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 = wpinv_generate_post_name( $post->ID );
216
217
                $wpdb->update( $wpdb->posts, array( 'post_name' => $post_name ), 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
        if ( $tax < 0 ) {
298
            $tax = 0;
299
        }
300
301
        return $tax;
302
    }
303
304
    private function setup_subtotal() {
305
        $subtotal     = 0;
306
        $cart_details = $this->cart_details;
307
308
        if ( is_array( $cart_details ) ) {
309
            foreach ( $cart_details as $item ) {
310
                if ( isset( $item['subtotal'] ) ) {
311
                    $subtotal += $item['subtotal'];
312
                }
313
            }
314
        } else {
315
            $subtotal  = $this->total;
316
            $tax       = wpinv_use_taxes() ? $this->tax : 0;
317
            $subtotal -= $tax;
318
        }
319
320
        return $subtotal;
321
    }
322
    
323
    private function setup_discounts() {
324
        $discounts = ! empty( $this->payment_meta['user_info']['discount'] ) ? $this->payment_meta['user_info']['discount'] : array();
325
        return $discounts;
326
    }
327
    
328
    private function setup_total() {
329
        $amount = $this->get_meta( '_wpinv_total', true );
330
331
        if ( empty( $amount ) && '0.00' != $amount ) {
332
            $meta   = $this->get_meta( '_wpinv_payment_meta', true );
333
            $meta   = maybe_unserialize( $meta );
334
335
            if ( isset( $meta['amount'] ) ) {
336
                $amount = $meta['amount'];
337
            }
338
        }
339
340
        if($amount < 0){
341
            $amount = 0;
342
        }
343
344
        return $amount;
345
    }
346
    
347
    private function setup_mode() {
348
        return $this->get_meta( '_wpinv_mode' );
349
    }
350
351
    private function setup_gateway() {
352
        $gateway = $this->get_meta( '_wpinv_gateway' );
353
        
354
        if ( empty( $gateway ) && 'publish' === $this->status ) {
355
            $gateway = 'manual';
356
        }
357
        
358
        return $gateway;
359
    }
360
    
361
    private function setup_gateway_title() {
362
        $gateway_title = wpinv_get_gateway_checkout_label( $this->gateway );
363
        return $gateway_title;
364
    }
365
366
    private function setup_transaction_id() {
367
        $transaction_id = $this->get_meta( '_wpinv_transaction_id' );
368
369
        if ( empty( $transaction_id ) || (int) $transaction_id === (int) $this->ID ) {
370
            $gateway        = $this->gateway;
371
            $transaction_id = apply_filters( 'wpinv_get_invoice_transaction_id-' . $gateway, $this->ID );
372
        }
373
374
        return $transaction_id;
375
    }
376
377
    private function setup_ip() {
378
        $ip = $this->get_meta( '_wpinv_user_ip' );
379
        return $ip;
380
    }
381
382
    ///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...
383
        ///$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...
384
        ///return $user_id;
385
    ///}
386
        
387
    private function setup_first_name() {
388
        $first_name = $this->get_meta( '_wpinv_first_name' );
389
        return $first_name;
390
    }
391
    
392
    private function setup_last_name() {
393
        $last_name = $this->get_meta( '_wpinv_last_name' );
394
        return $last_name;
395
    }
396
    
397
    private function setup_company() {
398
        $company = $this->get_meta( '_wpinv_company' );
399
        return $company;
400
    }
401
    
402
    private function setup_vat_number() {
403
        $vat_number = $this->get_meta( '_wpinv_vat_number' );
404
        return $vat_number;
405
    }
406
    
407
    private function setup_vat_rate() {
408
        $vat_rate = $this->get_meta( '_wpinv_vat_rate' );
409
        return $vat_rate;
410
    }
411
    
412
    private function setup_adddress_confirmed() {
413
        $adddress_confirmed = $this->get_meta( '_wpinv_adddress_confirmed' );
414
        return $adddress_confirmed;
415
    }
416
    
417
    private function setup_phone() {
418
        $phone = $this->get_meta( '_wpinv_phone' );
419
        return $phone;
420
    }
421
    
422
    private function setup_address() {
423
        $address = $this->get_meta( '_wpinv_address', true );
424
        return $address;
425
    }
426
    
427
    private function setup_city() {
428
        $city = $this->get_meta( '_wpinv_city', true );
429
        return $city;
430
    }
431
    
432
    private function setup_country() {
433
        $country = $this->get_meta( '_wpinv_country', true );
434
        return $country;
435
    }
436
    
437
    private function setup_state() {
438
        $state = $this->get_meta( '_wpinv_state', true );
439
        return $state;
440
    }
441
    
442
    private function setup_zip() {
443
        $zip = $this->get_meta( '_wpinv_zip', true );
444
        return $zip;
445
    }
446
447
    private function setup_user_info() {
448
        $defaults = array(
449
            'user_id'        => $this->user_id,
450
            'first_name'     => $this->first_name,
451
            'last_name'      => $this->last_name,
452
            'email'          => get_the_author_meta( 'email', $this->user_id ),
453
            'phone'          => $this->phone,
454
            'address'        => $this->address,
455
            'city'           => $this->city,
456
            'country'        => $this->country,
457
            'state'          => $this->state,
458
            'zip'            => $this->zip,
459
            'company'        => $this->company,
460
            'vat_number'     => $this->vat_number,
461
            'vat_rate'       => $this->vat_rate,
462
            'adddress_confirmed' => $this->adddress_confirmed,
463
            'discount'       => $this->discounts,
464
        );
465
        
466
        $user_info = array();
467
        if ( isset( $this->payment_meta['user_info'] ) ) {
468
            $user_info = maybe_unserialize( $this->payment_meta['user_info'] );
469
            
470
            if ( !empty( $user_info ) && isset( $user_info['user_id'] ) && $post = get_post( $this->ID ) ) {
471
                $this->user_id = $post->post_author;
472
                $this->email = get_the_author_meta( 'email', $this->user_id );
473
                
474
                $user_info['user_id'] = $this->user_id;
475
                $user_info['email'] = $this->email;
476
                $this->payment_meta['user_id'] = $this->user_id;
477
                $this->payment_meta['email'] = $this->email;
478
            }
479
        }
480
        
481
        $user_info    = wp_parse_args( $user_info, $defaults );
482
        
483
        // Get the user, but only if it's been created
484
        $user = get_userdata( $this->user_id );
485
        
486
        if ( !empty( $user ) && $user->ID > 0 ) {
487
            if ( empty( $user_info ) ) {
488
                $user_info = array(
489
                    'user_id'    => $user->ID,
490
                    'first_name' => $user->first_name,
491
                    'last_name'  => $user->last_name,
492
                    'email'      => $user->user_email,
493
                    'discount'   => '',
494
                );
495
            } else {
496
                foreach ( $user_info as $key => $value ) {
497
                    if ( ! empty( $value ) ) {
498
                        continue;
499
                    }
500
501
                    switch( $key ) {
502
                        case 'user_id':
503
                            $user_info[ $key ] = $user->ID;
504
                            break;
505
                        case 'first_name':
506
                            $user_info[ $key ] = $user->first_name;
507
                            break;
508
                        case 'last_name':
509
                            $user_info[ $key ] = $user->last_name;
510
                            break;
511
                        case 'email':
512
                            $user_info[ $key ] = $user->user_email;
513
                            break;
514
                    }
515
                }
516
            }
517
        }
518
519
        return $user_info;
520
    }
521
522
    private function setup_invoice_key() {
523
        $key = $this->get_meta( '_wpinv_key', true );
524
        
525
        return $key;
526
    }
527
528
    private function setup_invoice_number() {
529
        $number = $this->get_meta( '_wpinv_number', true );
530
531
        if ( !$number ) {
532
            $number = $this->ID;
533
534
            if ( $this->status == 'auto-draft' ) {
535
                if ( wpinv_sequential_number_active( $this->post_type ) ) {
536
                    $next_number = wpinv_get_next_invoice_number( $this->post_type );
537
                    $number      = $next_number;
538
                }
539
            }
540
            
541
            $number = wpinv_format_invoice_number( $number, $this->post_type );
542
        }
543
544
        return $number;
545
    }
546
    
547
    private function insert_invoice() {
548
        global $wpdb;
549
550
        if ( empty( $this->post_type ) ) {
551
            if ( !empty( $this->ID ) && $post_type = get_post_type( $this->ID ) ) {
552
                $this->post_type = $post_type;
553
            } else if ( !empty( $this->parent_invoice ) && $post_type = get_post_type( $this->parent_invoice ) ) {
554
                $this->post_type = $post_type;
555
            } else {
556
                $this->post_type = 'wpi_invoice';
557
            }
558
        }
559
560
        $invoice_number = $this->ID;
561
        if ( $number = $this->get_meta( '_wpinv_number', true ) ) {
562
            $invoice_number = $number;
563
        }
564
565 View Code Duplication
        if ( empty( $this->key ) ) {
566
            $this->key = self::generate_key();
567
            $this->pending['key'] = $this->key;
568
        }
569
570
        if ( empty( $this->ip ) ) {
571
            $this->ip = wpinv_get_ip();
572
            $this->pending['ip'] = $this->ip;
573
        }
574
        
575
        $payment_data = array(
576
            'price'        => $this->total,
577
            'date'         => $this->date,
578
            'user_email'   => $this->email,
579
            'invoice_key'  => $this->key,
580
            'currency'     => $this->currency,
581
            'items'        => $this->items,
582
            'user_info' => array(
583
                'user_id'    => $this->user_id,
584
                'email'      => $this->email,
585
                'first_name' => $this->first_name,
586
                'last_name'  => $this->last_name,
587
                'address'    => $this->address,
588
                'phone'      => $this->phone,
589
                'city'       => $this->city,
590
                'country'    => $this->country,
591
                'state'      => $this->state,
592
                'zip'        => $this->zip,
593
                'company'    => $this->company,
594
                'vat_number' => $this->vat_number,
595
                'discount'   => $this->discounts,
596
            ),
597
            'cart_details' => $this->cart_details,
598
            'status'       => $this->status,
599
            'fees'         => $this->fees,
600
        );
601
602
        $post_data = array(
603
                        'post_title'    => $invoice_number,
604
                        'post_status'   => $this->status,
605
                        'post_author'   => $this->user_id,
606
                        'post_type'     => $this->post_type,
607
                        'post_date'     => ! empty( $this->date ) && $this->date != '0000-00-00 00:00:00' ? $this->date : current_time( 'mysql' ),
608
                        'post_date_gmt' => ! empty( $this->date ) && $this->date != '0000-00-00 00:00:00' ? get_gmt_from_date( $this->date ) : current_time( 'mysql', 1 ),
609
                        'post_parent'   => $this->parent_invoice,
610
                    );
611
        $args = apply_filters( 'wpinv_insert_invoice_args', $post_data, $this );
612
613
        // Create a blank invoice
614
        if ( !empty( $this->ID ) ) {
615
            $args['ID']         = $this->ID;
616
617
            $invoice_id = wp_update_post( $args, true );
618
        } else {
619
            $invoice_id = wp_insert_post( $args, true );
620
        }
621
622
        if ( is_wp_error( $invoice_id ) ) {
623
            return false;
624
        }
625
626
        if ( !empty( $invoice_id ) ) {
627
            $this->ID  = $invoice_id;
628
            $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...
629
630
            $this->payment_meta = apply_filters( 'wpinv_payment_meta', $this->payment_meta, $payment_data );
631
            if ( ! empty( $this->payment_meta['fees'] ) ) {
632
                $this->fees = array_merge( $this->fees, $this->payment_meta['fees'] );
633
                foreach( $this->fees as $fee ) {
634
                    $this->increase_fees( $fee['amount'] );
635
                }
636
            }
637
638
            $this->update_meta( '_wpinv_payment_meta', $this->payment_meta );            
639
            $this->new = true;
640
        }
641
642
        return $this->ID;
643
    }
644
645
    public function save( $setup = false ) {
646
        global $wpi_session;
647
        
648
        $saved = false;
649
        if ( empty( $this->items ) ) {
650
            return $saved; // Don't save empty invoice.
651
        }
652
        
653 View Code Duplication
        if ( empty( $this->key ) ) {
654
            $this->key = self::generate_key();
655
            $this->pending['key'] = $this->key;
656
        }
657
        
658
        if ( empty( $this->ID ) ) {
659
            $invoice_id = $this->insert_invoice();
660
661
            if ( false === $invoice_id ) {
662
                $saved = false;
663
            } else {
664
                $this->ID = $invoice_id;
665
            }
666
        }
667
668
        // If we have something pending, let's save it
669
        if ( !empty( $this->pending ) ) {
670
            $total_increase = 0;
671
            $total_decrease = 0;
672
673
            foreach ( $this->pending as $key => $value ) {
674
                switch( $key ) {
675
                    case 'items':
676
                        // Update totals for pending items
677
                        foreach ( $this->pending[ $key ] as $item ) {
678
                            switch( $item['action'] ) {
679
                                case 'add':
680
                                    $price = $item['price'];
681
                                    $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...
682
683
                                    if ( 'publish' === $this->status ) {
684
                                        $total_increase += $price;
685
                                    }
686
                                    break;
687
688
                                case 'remove':
689
                                    if ( 'publish' === $this->status ) {
690
                                        $total_decrease += $item['price'];
691
                                    }
692
                                    break;
693
                            }
694
                        }
695
                        break;
696
                    case 'fees':
697
                        if ( 'publish' !== $this->status ) {
698
                            break;
699
                        }
700
701
                        if ( empty( $this->pending[ $key ] ) ) {
702
                            break;
703
                        }
704
705
                        foreach ( $this->pending[ $key ] as $fee ) {
706
                            switch( $fee['action'] ) {
707
                                case 'add':
708
                                    $total_increase += $fee['amount'];
709
                                    break;
710
711
                                case 'remove':
712
                                    $total_decrease += $fee['amount'];
713
                                    break;
714
                            }
715
                        }
716
                        break;
717
                    case 'status':
718
                        $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...
719
                        break;
720
                    case 'gateway':
721
                        $this->update_meta( '_wpinv_gateway', $this->gateway );
722
                        break;
723
                    case 'mode':
724
                        $this->update_meta( '_wpinv_mode', $this->mode );
725
                        break;
726
                    case 'transaction_id':
727
                        $this->update_meta( '_wpinv_transaction_id', $this->transaction_id );
728
                        break;
729
                    case 'ip':
730
                        $this->update_meta( '_wpinv_user_ip', $this->ip );
731
                        break;
732
                    ///case 'user_id':
733
                        ///$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...
734
                        ///$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...
735
                        ///break;
736
                    case 'first_name':
737
                        $this->update_meta( '_wpinv_first_name', $this->first_name );
738
                        $this->user_info['first_name'] = $this->first_name;
739
                        break;
740
                    case 'last_name':
741
                        $this->update_meta( '_wpinv_last_name', $this->last_name );
742
                        $this->user_info['last_name'] = $this->last_name;
743
                        break;
744
                    case 'phone':
745
                        $this->update_meta( '_wpinv_phone', $this->phone );
746
                        $this->user_info['phone'] = $this->phone;
747
                        break;
748
                    case 'address':
749
                        $this->update_meta( '_wpinv_address', $this->address );
750
                        $this->user_info['address'] = $this->address;
751
                        break;
752
                    case 'city':
753
                        $this->update_meta( '_wpinv_city', $this->city );
754
                        $this->user_info['city'] = $this->city;
755
                        break;
756
                    case 'country':
757
                        $this->update_meta( '_wpinv_country', $this->country );
758
                        $this->user_info['country'] = $this->country;
759
                        break;
760
                    case 'state':
761
                        $this->update_meta( '_wpinv_state', $this->state );
762
                        $this->user_info['state'] = $this->state;
763
                        break;
764
                    case 'zip':
765
                        $this->update_meta( '_wpinv_zip', $this->zip );
766
                        $this->user_info['zip'] = $this->zip;
767
                        break;
768
                    case 'company':
769
                        $this->update_meta( '_wpinv_company', $this->company );
770
                        $this->user_info['company'] = $this->company;
771
                        break;
772
                    case 'vat_number':
773
                        $this->update_meta( '_wpinv_vat_number', $this->vat_number );
774
                        $this->user_info['vat_number'] = $this->vat_number;
775
                        
776
                        $vat_info = $wpi_session->get( 'user_vat_data' );
777
                        if ( $this->vat_number && !empty( $vat_info ) && isset( $vat_info['number'] ) && isset( $vat_info['valid'] ) && $vat_info['number'] == $this->vat_number ) {
778
                            $adddress_confirmed = isset( $vat_info['adddress_confirmed'] ) ? $vat_info['adddress_confirmed'] : false;
779
                            $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...
780
                            $this->user_info['adddress_confirmed'] = (bool)$adddress_confirmed;
781
                        }
782
    
783
                        break;
784
                    case 'vat_rate':
785
                        $this->update_meta( '_wpinv_vat_rate', $this->vat_rate );
786
                        $this->user_info['vat_rate'] = $this->vat_rate;
787
                        break;
788
                    case 'adddress_confirmed':
789
                        $this->update_meta( '_wpinv_adddress_confirmed', $this->adddress_confirmed );
790
                        $this->user_info['adddress_confirmed'] = $this->adddress_confirmed;
791
                        break;
792
                    
793
                    case 'key':
794
                        $this->update_meta( '_wpinv_key', $this->key );
795
                        break;
796
                    case 'date':
797
                        $args = array(
798
                            'ID'        => $this->ID,
799
                            'post_date' => $this->date,
800
                            'edit_date' => true,
801
                        );
802
803
                        wp_update_post( $args );
804
                        break;
805
                    case 'due_date':
806
                        if ( empty( $this->due_date ) ) {
807
                            $this->due_date = 'none';
808
                        }
809
                        
810
                        $this->update_meta( '_wpinv_due_date', $this->due_date );
811
                        break;
812
                    case 'completed_date':
813
                        $this->update_meta( '_wpinv_completed_date', $this->completed_date );
814
                        break;
815
                    case 'discounts':
816
                        if ( ! is_array( $this->discounts ) ) {
817
                            $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...
818
                        }
819
820
                        $this->user_info['discount'] = implode( ',', $this->discounts );
821
                        break;
822
                    case 'discount':
823
                        $this->update_meta( '_wpinv_discount', wpinv_round_amount( $this->discount ) );
824
                        break;
825
                    case 'discount_code':
826
                        $this->update_meta( '_wpinv_discount_code', $this->discount_code );
827
                        break;
828
                    case 'parent_invoice':
829
                        $args = array(
830
                            'ID'          => $this->ID,
831
                            'post_parent' => $this->parent_invoice,
832
                        );
833
                        wp_update_post( $args );
834
                        break;
835
                    default:
836
                        do_action( 'wpinv_save', $this, $key );
837
                        break;
838
                }
839
            }
840
841
            $this->update_meta( '_wpinv_subtotal', wpinv_round_amount( $this->subtotal ) );
842
            $this->update_meta( '_wpinv_total', wpinv_round_amount( $this->total ) );
843
            $this->update_meta( '_wpinv_tax', wpinv_round_amount( $this->tax ) );
844
            
845
            $this->items    = array_values( $this->items );
846
            
847
            $new_meta = array(
848
                'items'         => $this->items,
849
                'cart_details'  => $this->cart_details,
850
                'fees'          => $this->fees,
851
                'currency'      => $this->currency,
852
                'user_info'     => $this->user_info,
853
            );
854
            
855
            $meta        = $this->get_meta();
856
            $merged_meta = array_merge( $meta, $new_meta );
857
858
            // Only save the payment meta if it's changed
859
            if ( md5( serialize( $meta ) ) !== md5( serialize( $merged_meta) ) ) {
860
                $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...
861
                if ( false !== $updated ) {
862
                    $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...
863
                }
864
            }
865
866
            $this->pending = array();
867
            $saved         = true;
868
        } else {
869
            $this->update_meta( '_wpinv_subtotal', wpinv_round_amount( $this->subtotal ) );
870
            $this->update_meta( '_wpinv_total', wpinv_round_amount( $this->total ) );
871
            $this->update_meta( '_wpinv_tax', wpinv_round_amount( $this->tax ) );
872
        }
873
        
874
        do_action( 'wpinv_invoice_save', $this, $saved );
875
876
        if ( true === $saved || $setup ) {
877
            $this->setup_invoice( $this->ID );
878
        }
879
        
880
        $this->refresh_item_ids();
881
        
882
        return $saved;
883
    }
884
    
885
    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...
886
        $default_args = array(
887
            'label'       => '',
888
            'amount'      => 0,
889
            'type'        => 'fee',
890
            'id'          => '',
891
            'no_tax'      => false,
892
            'item_id'     => 0,
893
        );
894
895
        $fee = wp_parse_args( $args, $default_args );
896
        
897
        if ( !empty( $fee['label'] ) ) {
898
            return false;
899
        }
900
        
901
        $fee['id']  = sanitize_title( $fee['label'] );
902
        
903
        $this->fees[]               = $fee;
904
        
905
        $added_fee               = $fee;
906
        $added_fee['action']     = 'add';
907
        $this->pending['fees'][] = $added_fee;
908
        reset( $this->fees );
909
910
        $this->increase_fees( $fee['amount'] );
911
        return true;
912
    }
913
914
    public function remove_fee( $key ) {
915
        $removed = false;
916
917
        if ( is_numeric( $key ) ) {
918
            $removed = $this->remove_fee_by( 'index', $key );
919
        }
920
921
        return $removed;
922
    }
923
924
    public function remove_fee_by( $key, $value, $global = false ) {
925
        $allowed_fee_keys = apply_filters( 'wpinv_fee_keys', array(
926
            'index', 'label', 'amount', 'type',
927
        ) );
928
929
        if ( ! in_array( $key, $allowed_fee_keys ) ) {
930
            return false;
931
        }
932
933
        $removed = false;
934
        if ( 'index' === $key && array_key_exists( $value, $this->fees ) ) {
935
            $removed_fee             = $this->fees[ $value ];
936
            $removed_fee['action']   = 'remove';
937
            $this->pending['fees'][] = $removed_fee;
938
939
            $this->decrease_fees( $removed_fee['amount'] );
940
941
            unset( $this->fees[ $value ] );
942
            $removed = true;
943
        } else if ( 'index' !== $key ) {
944
            foreach ( $this->fees as $index => $fee ) {
945
                if ( isset( $fee[ $key ] ) && $fee[ $key ] == $value ) {
946
                    $removed_fee             = $fee;
947
                    $removed_fee['action']   = 'remove';
948
                    $this->pending['fees'][] = $removed_fee;
949
950
                    $this->decrease_fees( $removed_fee['amount'] );
951
952
                    unset( $this->fees[ $index ] );
953
                    $removed = true;
954
955
                    if ( false === $global ) {
956
                        break;
957
                    }
958
                }
959
            }
960
        }
961
962
        if ( true === $removed ) {
963
            $this->fees = array_values( $this->fees );
964
        }
965
966
        return $removed;
967
    }
968
969
    
970
971
    public function add_note( $note = '', $customer_type = false, $added_by_user = false, $system = false ) {
972
        // Bail if no note specified
973
        if( !$note ) {
974
            return false;
975
        }
976
977
        if ( empty( $this->ID ) )
978
            return false;
979
        
980
        if ( ( ( is_user_logged_in() && current_user_can( 'manage_options' ) ) || $added_by_user ) && !$system ) {
981
            $user                 = get_user_by( 'id', get_current_user_id() );
982
            $comment_author       = $user->display_name;
983
            $comment_author_email = $user->user_email;
984
        } else {
985
            $comment_author       = __( 'System', 'invoicing' );
986
            $comment_author_email = strtolower( __( 'System', 'invoicing' ) ) . '@';
987
            $comment_author_email .= isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com';
988
            $comment_author_email = sanitize_email( $comment_author_email );
989
        }
990
991
        do_action( 'wpinv_pre_insert_invoice_note', $this->ID, $note, $customer_type );
992
993
        $note_id = wp_insert_comment( wp_filter_comment( array(
994
            'comment_post_ID'      => $this->ID,
995
            'comment_content'      => $note,
996
            'comment_agent'        => 'WPInvoicing',
997
            'user_id'              => is_admin() ? get_current_user_id() : 0,
998
            'comment_date'         => current_time( 'mysql' ),
999
            'comment_date_gmt'     => current_time( 'mysql', 1 ),
1000
            'comment_approved'     => 1,
1001
            'comment_parent'       => 0,
1002
            'comment_author'       => $comment_author,
1003
            'comment_author_IP'    => wpinv_get_ip(),
1004
            'comment_author_url'   => '',
1005
            'comment_author_email' => $comment_author_email,
1006
            'comment_type'         => 'wpinv_note'
1007
        ) ) );
1008
1009
        do_action( 'wpinv_insert_payment_note', $note_id, $this->ID, $note );
1010
        
1011
        if ( $customer_type ) {
1012
            add_comment_meta( $note_id, '_wpi_customer_note', 1 );
1013
1014
            do_action( 'wpinv_new_customer_note', array( 'invoice_id' => $this->ID, 'user_note' => $note ) );
1015
        }
1016
1017
        return $note_id;
1018
    }
1019
1020
    private function increase_subtotal( $amount = 0.00 ) {
1021
        $amount          = (float) $amount;
1022
        $this->subtotal += $amount;
1023
        $this->subtotal  = wpinv_round_amount( $this->subtotal );
1024
1025
        $this->recalculate_total();
1026
    }
1027
1028 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...
1029
        $amount          = (float) $amount;
1030
        $this->subtotal -= $amount;
1031
        $this->subtotal  = wpinv_round_amount( $this->subtotal );
1032
1033
        if ( $this->subtotal < 0 ) {
1034
            $this->subtotal = 0;
1035
        }
1036
1037
        $this->recalculate_total();
1038
    }
1039
1040
    private function increase_fees( $amount = 0.00 ) {
1041
        $amount            = (float)$amount;
1042
        $this->fees_total += $amount;
1043
        $this->fees_total  = wpinv_round_amount( $this->fees_total );
1044
1045
        $this->recalculate_total();
1046
    }
1047
1048 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...
1049
        $amount            = (float) $amount;
1050
        $this->fees_total -= $amount;
1051
        $this->fees_total  = wpinv_round_amount( $this->fees_total );
1052
1053
        if ( $this->fees_total < 0 ) {
1054
            $this->fees_total = 0;
1055
        }
1056
1057
        $this->recalculate_total();
1058
    }
1059
1060
    public function recalculate_total() {
1061
        global $wpi_nosave;
1062
        
1063
        $this->total = $this->subtotal + $this->tax + $this->fees_total;
1064
        $this->total = wpinv_round_amount( $this->total );
1065
        
1066
        do_action( 'wpinv_invoice_recalculate_total', $this, $wpi_nosave );
1067
    }
1068
    
1069
    public function increase_tax( $amount = 0.00 ) {
1070
        $amount       = (float) $amount;
1071
        $this->tax   += $amount;
1072
1073
        $this->recalculate_total();
1074
    }
1075
1076
    public function decrease_tax( $amount = 0.00 ) {
1077
        $amount     = (float) $amount;
1078
        $this->tax -= $amount;
1079
1080
        if ( $this->tax < 0 ) {
1081
            $this->tax = 0;
1082
        }
1083
1084
        $this->recalculate_total();
1085
    }
1086
1087
    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...
1088
        $old_status = ! empty( $this->old_status ) ? $this->old_status : get_post_status( $this->ID );
1089
        
1090
        if ( $old_status === $new_status && in_array( $new_status, array_keys( wpinv_get_invoice_statuses() ) ) ) {
1091
            return false; // Don't permit status changes that aren't changes
1092
        }
1093
1094
        $do_change = apply_filters( 'wpinv_should_update_invoice_status', true, $this->ID, $new_status, $old_status );
1095
        $updated = false;
1096
1097
        if ( $do_change ) {
1098
            do_action( 'wpinv_before_invoice_status_change', $this->ID, $new_status, $old_status );
1099
1100
            $update_post_data                   = array();
1101
            $update_post_data['ID']             = $this->ID;
1102
            $update_post_data['post_status']    = $new_status;
1103
            $update_post_data['edit_date']      = current_time( 'mysql', 0 );
1104
            $update_post_data['edit_date_gmt']  = current_time( 'mysql', 1 );
1105
            
1106
            $update_post_data = apply_filters( 'wpinv_update_invoice_status_fields', $update_post_data, $this->ID );
1107
1108
            $updated = wp_update_post( $update_post_data );     
1109
           
1110
            // Process any specific status functions
1111
            switch( $new_status ) {
1112
                case 'wpi-refunded':
1113
                    $this->process_refund();
1114
                    break;
1115
                case 'wpi-failed':
1116
                    $this->process_failure();
1117
                    break;
1118
                case 'wpi-pending':
1119
                    $this->process_pending();
1120
                    break;
1121
            }
1122
            
1123
            // Status was changed.
1124
            do_action( 'wpinv_status_' . $new_status, $this->ID, $old_status );
1125
            do_action( 'wpinv_status_' . $old_status . '_to_' . $new_status, $this->ID, $old_status );
1126
            do_action( 'wpinv_update_status', $this->ID, $new_status, $old_status );
1127
        }
1128
1129
        return $updated;
1130
    }
1131
1132
    public function refund() {
1133
        $this->old_status        = $this->status;
1134
        $this->status            = 'wpi-refunded';
1135
        $this->pending['status'] = $this->status;
1136
1137
        $this->save();
1138
    }
1139
1140
    public function update_meta( $meta_key = '', $meta_value = '', $prev_value = '' ) {
1141
        if ( empty( $meta_key ) ) {
1142
            return false;
1143
        }
1144
1145
        if ( $meta_key == 'key' || $meta_key == 'date' ) {
1146
            $current_meta = $this->get_meta();
1147
            $current_meta[ $meta_key ] = $meta_value;
1148
1149
            $meta_key     = '_wpinv_payment_meta';
1150
            $meta_value   = $current_meta;
1151
        }
1152
1153
        $meta_value = apply_filters( 'wpinv_update_payment_meta_' . $meta_key, $meta_value, $this->ID );
1154
        
1155
        if ( $meta_key == '_wpinv_completed_date' && !empty( $meta_value ) ) {
1156
            $args = array(
1157
                'ID'                => $this->ID,
1158
                'post_date'         => $meta_value,
1159
                'edit_date'         => true,
1160
                'post_date_gmt'     => get_gmt_from_date( $meta_value ),
1161
                'post_modified'     => $meta_value,
1162
                'post_modified_gmt' => get_gmt_from_date( $meta_value )
1163
            );
1164
            wp_update_post( $args );
1165
        }
1166
        
1167
        return update_post_meta( $this->ID, $meta_key, $meta_value, $prev_value );
1168
    }
1169
1170
    private function process_refund() {
1171
        $process_refund = true;
1172
1173
        // If the payment was not in publish, don't decrement stats as they were never incremented
1174
        if ( 'publish' != $this->old_status || 'wpi-refunded' != $this->status ) {
1175
            $process_refund = false;
1176
        }
1177
1178
        // Allow extensions to filter for their own payment types, Example: Recurring Payments
1179
        $process_refund = apply_filters( 'wpinv_should_process_refund', $process_refund, $this );
1180
1181
        if ( false === $process_refund ) {
1182
            return;
1183
        }
1184
1185
        do_action( 'wpinv_pre_refund_invoice', $this );
1186
        
1187
        $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...
1188
        $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...
1189
        $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...
1190
        
1191
        do_action( 'wpinv_post_refund_invoice', $this );
1192
    }
1193
1194
    private function process_failure() {
1195
        $discounts = $this->discounts;
1196
        if ( empty( $discounts ) ) {
1197
            return;
1198
        }
1199
1200
        if ( ! is_array( $discounts ) ) {
1201
            $discounts = array_map( 'trim', explode( ',', $discounts ) );
1202
        }
1203
1204
        foreach ( $discounts as $discount ) {
1205
            wpinv_decrease_discount_usage( $discount );
1206
        }
1207
    }
1208
    
1209
    private function process_pending() {
1210
        $process_pending = true;
1211
1212
        // If the payment was not in publish or revoked status, don't decrement stats as they were never incremented
1213
        if ( ( 'publish' != $this->old_status && 'revoked' != $this->old_status ) || 'wpi-pending' != $this->status ) {
1214
            $process_pending = false;
1215
        }
1216
1217
        // Allow extensions to filter for their own payment types, Example: Recurring Payments
1218
        $process_pending = apply_filters( 'wpinv_should_process_pending', $process_pending, $this );
1219
1220
        if ( false === $process_pending ) {
1221
            return;
1222
        }
1223
1224
        $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...
1225
        $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...
1226
        $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...
1227
1228
        $this->completed_date = '';
1229
        $this->update_meta( '_wpinv_completed_date', '' );
1230
    }
1231
    
1232
    // get data
1233
    public function get_meta( $meta_key = '_wpinv_payment_meta', $single = true ) {
1234
        $meta = get_post_meta( $this->ID, $meta_key, $single );
1235
1236
        if ( $meta_key === '_wpinv_payment_meta' ) {
1237
1238
            if(!is_array($meta)){$meta = array();} // we need this to be an array so make sure it is.
1239
1240
            if ( empty( $meta['key'] ) ) {
1241
                $meta['key'] = $this->setup_invoice_key();
1242
            }
1243
1244
            if ( empty( $meta['date'] ) ) {
1245
                $meta['date'] = get_post_field( 'post_date', $this->ID );
1246
            }
1247
        }
1248
1249
        $meta = apply_filters( 'wpinv_get_invoice_meta_' . $meta_key, $meta, $this->ID );
1250
1251
        return apply_filters( 'wpinv_get_invoice_meta', $meta, $this->ID, $meta_key );
1252
    }
1253
    
1254
    public function get_description() {
1255
        $post = get_post( $this->ID );
1256
        
1257
        $description = !empty( $post ) ? $post->post_content : '';
1258
        return apply_filters( 'wpinv_get_description', $description, $this->ID, $this );
1259
    }
1260
    
1261
    public function get_status( $nicename = false ) {
1262
        if ( !$nicename ) {
1263
            $status = $this->status;
1264
        } else {
1265
            $status = $this->status_nicename;
1266
        }
1267
        
1268
        return apply_filters( 'wpinv_get_status', $status, $nicename, $this->ID, $this );
1269
    }
1270
    
1271
    public function get_cart_details() {
1272
        return apply_filters( 'wpinv_cart_details', $this->cart_details, $this->ID, $this );
1273
    }
1274
    
1275 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...
1276
        $subtotal = wpinv_round_amount( $this->subtotal );
1277
        
1278
        if ( $currency ) {
1279
            $subtotal = wpinv_price( wpinv_format_amount( $subtotal, NULL, !$currency ), $this->get_currency() );
1280
        }
1281
        
1282
        return apply_filters( 'wpinv_get_invoice_subtotal', $subtotal, $this->ID, $this, $currency );
1283
    }
1284
    
1285 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...
1286
        if ( $this->is_free_trial() ) {
1287
            $total = wpinv_round_amount( 0 );
1288
        } else {
1289
            $total = wpinv_round_amount( $this->total );
1290
        }
1291
        if ( $currency ) {
1292
            $total = wpinv_price( wpinv_format_amount( $total, NULL, !$currency ), $this->get_currency() );
1293
        }
1294
        
1295
        return apply_filters( 'wpinv_get_invoice_total', $total, $this->ID, $this, $currency );
1296
    }
1297
    
1298
    public function get_recurring_details( $field = '', $currency = false ) {        
1299
        $data                 = array();
1300
        $data['cart_details'] = $this->cart_details;
1301
        $data['subtotal']     = $this->get_subtotal();
1302
        $data['discount']     = $this->get_discount();
1303
        $data['tax']          = $this->get_tax();
1304
        $data['total']        = $this->get_total();
1305
    
1306
        if ( !empty( $this->cart_details ) && ( $this->is_parent() || $this->is_renewal() ) ) {
1307
            $is_free_trial = $this->is_free_trial();
1308
            $discounts = $this->get_discounts( true );
1309
            
1310
            if ( $is_free_trial || !empty( $discounts ) ) {
1311
                $first_use_only = false;
1312
                
1313
                if ( !empty( $discounts ) ) {
1314
                    foreach ( $discounts as $key => $code ) {
1315
                        if ( wpinv_discount_is_recurring( $code, true ) ) {
1316
                            $first_use_only = true;
1317
                            break;
1318
                        }
1319
                    }
1320
                }
1321
                    
1322
                if ( !$first_use_only ) {
1323
                    $data['subtotal'] = wpinv_round_amount( $this->subtotal );
1324
                    $data['discount'] = wpinv_round_amount( $this->discount );
1325
                    $data['tax']      = wpinv_round_amount( $this->tax );
1326
                    $data['total']    = wpinv_round_amount( $this->total );
1327
                } else {
1328
                    $cart_subtotal   = 0;
1329
                    $cart_discount   = 0;
1330
                    $cart_tax        = 0;
1331
1332
                    foreach ( $this->cart_details as $key => $item ) {
1333
                        $item_quantity  = $item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
1334
                        $item_subtotal  = !empty( $item['subtotal'] ) ? $item['subtotal'] : $item['item_price'] * $item_quantity;
1335
                        $item_discount  = 0;
1336
                        $item_tax       = $item_subtotal > 0 && !empty( $item['vat_rate'] ) ? ( $item_subtotal * 0.01 * (float)$item['vat_rate'] ) : 0;
1337
                        
1338
                        if ( wpinv_prices_include_tax() ) {
1339
                            $item_subtotal -= wpinv_round_amount( $item_tax );
1340
                        }
1341
                        
1342
                        $item_total     = $item_subtotal - $item_discount + $item_tax;
1343
                        // Do not allow totals to go negative
1344
                        if ( $item_total < 0 ) {
1345
                            $item_total = 0;
1346
                        }
1347
                        
1348
                        $cart_subtotal  += (float)($item_subtotal);
1349
                        $cart_discount  += (float)($item_discount);
1350
                        $cart_tax       += (float)($item_tax);
1351
                        
1352
                        $data['cart_details'][$key]['discount']   = wpinv_round_amount( $item_discount );
1353
                        $data['cart_details'][$key]['tax']        = wpinv_round_amount( $item_tax );
1354
                        $data['cart_details'][$key]['price']      = wpinv_round_amount( $item_total );
1355
                    }
1356
                    
1357
                    $data['subtotal'] = wpinv_round_amount( $cart_subtotal );
1358
                    $data['discount'] = wpinv_round_amount( $cart_discount );
1359
                    $data['tax']      = wpinv_round_amount( $cart_tax );
1360
                    $data['total']    = wpinv_round_amount( $data['subtotal'] + $data['tax'] );
1361
                }
1362
            }
1363
        }
1364
        
1365
        $data = apply_filters( 'wpinv_get_invoice_recurring_details', $data, $this, $field, $currency );
1366
1367
        if ( isset( $data[$field] ) ) {
1368
            return ( $currency ? wpinv_price( $data[$field], $this->get_currency() ) : $data[$field] );
1369
        }
1370
        
1371
        return $data;
1372
    }
1373
    
1374
    public function get_final_tax( $currency = false ) {        
1375
        $final_total = wpinv_round_amount( $this->tax );
1376
        if ( $currency ) {
1377
            $final_total = wpinv_price( wpinv_format_amount( $final_total, NULL, !$currency ), $this->get_currency() );
1378
        }
1379
        
1380
        return apply_filters( 'wpinv_get_invoice_final_total', $final_total, $this, $currency );
1381
    }
1382
    
1383
    public function get_discounts( $array = false ) {
1384
        $discounts = $this->discounts;
1385
        if ( $array && $discounts ) {
1386
            $discounts = explode( ',', $discounts );
1387
        }
1388
        return apply_filters( 'wpinv_payment_discounts', $discounts, $this->ID, $this, $array );
1389
    }
1390
    
1391
    public function get_discount( $currency = false, $dash = false ) {
1392
        if ( !empty( $this->discounts ) ) {
1393
            global $ajax_cart_details;
1394
            $ajax_cart_details = $this->get_cart_details();
1395
            
1396
            if ( !empty( $ajax_cart_details ) && count( $ajax_cart_details ) == count( $this->items ) ) {
1397
                $cart_items = $ajax_cart_details;
1398
            } else {
1399
                $cart_items = $this->items;
1400
            }
1401
1402
            $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...
1403
        }
1404
        $discount   = wpinv_round_amount( $this->discount );
1405
        $dash       = $dash && $discount > 0 ? '&ndash;' : '';
1406
        
1407
        if ( $currency ) {
1408
            $discount = wpinv_price( wpinv_format_amount( $discount, NULL, !$currency ), $this->get_currency() );
1409
        }
1410
        
1411
        $discount   = $dash . $discount;
1412
        
1413
        return apply_filters( 'wpinv_get_invoice_discount', $discount, $this->ID, $this, $currency, $dash );
1414
    }
1415
    
1416
    public function get_discount_code() {
1417
        return $this->discount_code;
1418
    }
1419
    
1420 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...
1421
        $tax = wpinv_round_amount( $this->tax );
1422
        
1423
        if ( $currency ) {
1424
            $tax = wpinv_price( wpinv_format_amount( $tax, NULL, !$currency ), $this->get_currency() );
1425
        }
1426
        
1427
        return apply_filters( 'wpinv_get_invoice_tax', $tax, $this->ID, $this, $currency );
1428
    }
1429
    
1430
    public function get_fees( $type = 'all' ) {
1431
        $fees    = array();
1432
1433
        if ( ! empty( $this->fees ) && is_array( $this->fees ) ) {
1434
            foreach ( $this->fees as $fee ) {
1435 View Code Duplication
                if( 'all' != $type && ! empty( $fee['type'] ) && $type != $fee['type'] ) {
1436
                    continue;
1437
                }
1438
1439
                $fee['label'] = stripslashes( $fee['label'] );
1440
                $fee['amount_display'] = wpinv_price( $fee['amount'], $this->get_currency() );
1441
                $fees[]    = $fee;
1442
            }
1443
        }
1444
1445
        return apply_filters( 'wpinv_get_invoice_fees', $fees, $this->ID, $this );
1446
    }
1447
    
1448
    public function get_fees_total( $type = 'all' ) {
1449
        $fees_total = (float) 0.00;
1450
1451
        $payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
1452
        if ( ! empty( $payment_fees ) ) {
1453
            foreach ( $payment_fees as $fee ) {
1454
                $fees_total += (float) $fee['amount'];
1455
            }
1456
        }
1457
1458
        return apply_filters( 'wpinv_get_invoice_fees_total', $fees_total, $this->ID, $this );
1459
        /*
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...
1460
        $fees = $this->get_fees( $type );
1461
1462
        $fees_total = 0;
1463
        if ( ! empty( $fees ) && is_array( $fees ) ) {
1464
            foreach ( $fees as $fee_id => $fee ) {
1465
                if( 'all' != $type && !empty( $fee['type'] ) && $type != $fee['type'] ) {
1466
                    continue;
1467
                }
1468
1469
                $fees_total += $fee['amount'];
1470
            }
1471
        }
1472
1473
        return apply_filters( 'wpinv_get_invoice_fees_total', $fees_total, $this->ID, $this );
1474
        */
1475
    }
1476
1477
    public function get_user_id() {
1478
        return apply_filters( 'wpinv_user_id', $this->user_id, $this->ID, $this );
1479
    }
1480
    
1481
    public function get_first_name() {
1482
        return apply_filters( 'wpinv_first_name', $this->first_name, $this->ID, $this );
1483
    }
1484
    
1485
    public function get_last_name() {
1486
        return apply_filters( 'wpinv_last_name', $this->last_name, $this->ID, $this );
1487
    }
1488
    
1489
    public function get_user_full_name() {
1490
        return apply_filters( 'wpinv_user_full_name', $this->full_name, $this->ID, $this );
1491
    }
1492
    
1493
    public function get_user_info() {
1494
        return apply_filters( 'wpinv_user_info', $this->user_info, $this->ID, $this );
1495
    }
1496
    
1497
    public function get_email() {
1498
        return apply_filters( 'wpinv_user_email', $this->email, $this->ID, $this );
1499
    }
1500
    
1501
    public function get_address() {
1502
        return apply_filters( 'wpinv_address', $this->address, $this->ID, $this );
1503
    }
1504
    
1505
    public function get_phone() {
1506
        return apply_filters( 'wpinv_phone', $this->phone, $this->ID, $this );
1507
    }
1508
    
1509
    public function get_number() {
1510
        return apply_filters( 'wpinv_number', $this->number, $this->ID, $this );
1511
    }
1512
    
1513
    public function get_items() {
1514
        return apply_filters( 'wpinv_payment_meta_items', $this->items, $this->ID, $this );
1515
    }
1516
    
1517
    public function get_key() {
1518
        return apply_filters( 'wpinv_key', $this->key, $this->ID, $this );
1519
    }
1520
    
1521
    public function get_transaction_id() {
1522
        return apply_filters( 'wpinv_get_invoice_transaction_id', $this->transaction_id, $this->ID, $this );
1523
    }
1524
    
1525
    public function get_gateway() {
1526
        return apply_filters( 'wpinv_gateway', $this->gateway, $this->ID, $this );
1527
    }
1528
    
1529
    public function get_gateway_title() {
1530
        $this->gateway_title = !empty( $this->gateway_title ) ? $this->gateway_title : wpinv_get_gateway_checkout_label( $this->gateway );
1531
        
1532
        return apply_filters( 'wpinv_gateway_title', $this->gateway_title, $this->ID, $this );
1533
    }
1534
    
1535
    public function get_currency() {
1536
        return apply_filters( 'wpinv_currency_code', $this->currency, $this->ID, $this );
1537
    }
1538
    
1539
    public function get_created_date() {
1540
        return apply_filters( 'wpinv_created_date', $this->date, $this->ID, $this );
1541
    }
1542
    
1543
    public function get_due_date( $display = false ) {
1544
        $due_date = apply_filters( 'wpinv_due_date', $this->due_date, $this->ID, $this );
1545
        
1546
        if ( !$display || empty( $due_date ) ) {
1547
            return $due_date;
1548
        }
1549
        
1550
        return date_i18n( get_option( 'date_format' ), strtotime( $due_date ) );
1551
    }
1552
    
1553
    public function get_completed_date() {
1554
        return apply_filters( 'wpinv_completed_date', $this->completed_date, $this->ID, $this );
1555
    }
1556
    
1557
    public function get_invoice_date( $formatted = true ) {
1558
        $date_completed = $this->completed_date;
1559
        $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? $date_completed : '';
1560
        
1561
        if ( $invoice_date == '' ) {
1562
            $date_created   = $this->date;
1563
            $invoice_date   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? $date_created : '';
1564
        }
1565
        
1566
        if ( $formatted && $invoice_date ) {
1567
            $invoice_date   = date_i18n( get_option( 'date_format' ), strtotime( $invoice_date ) );
1568
        }
1569
1570
        return apply_filters( 'wpinv_get_invoice_date', $invoice_date, $formatted, $this->ID, $this );
1571
    }
1572
    
1573
    public function get_ip() {
1574
        return apply_filters( 'wpinv_user_ip', $this->ip, $this->ID, $this );
1575
    }
1576
        
1577
    public function has_status( $status ) {
1578
        return apply_filters( 'wpinv_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
1579
    }
1580
    
1581
    public function add_item( $item_id = 0, $args = array() ) {
1582
        global $wpi_current_id, $wpi_item_id;
1583
        
1584
        $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...
1585
1586
        // Bail if this post isn't a item
1587
        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...
1588
            return false;
1589
        }
1590
        
1591
        $has_quantities = wpinv_item_quantities_enabled();
1592
1593
        // Set some defaults
1594
        $defaults = array(
1595
            'quantity'      => 1,
1596
            'id'            => false,
1597
            'name'          => $item->get_name(),
1598
            'item_price'    => false,
1599
            'custom_price'  => '',
1600
            'discount'      => 0,
1601
            'tax'           => 0.00,
1602
            'meta'          => array(),
1603
            'fees'          => array()
1604
        );
1605
1606
        $args = wp_parse_args( apply_filters( 'wpinv_add_item_args', $args, $item->ID ), $defaults );
1607
        $args['quantity']   = $has_quantities && $args['quantity'] > 0 ? absint( $args['quantity'] ) : 1;
1608
1609
        $wpi_current_id         = $this->ID;
1610
        $wpi_item_id            = $item->ID;
1611
        $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...
1612
        
1613
        $_POST['wpinv_country'] = $this->country;
1614
        $_POST['wpinv_state']   = $this->state;
1615
        
1616
        $found_cart_key         = false;
1617
        
1618
        if ($has_quantities) {
1619
            $this->cart_details = !empty( $this->cart_details ) ? array_values( $this->cart_details ) : $this->cart_details;
1620
            
1621
            foreach ( $this->items as $key => $cart_item ) {
1622
                if ( (int)$item_id !== (int)$cart_item['id'] ) {
1623
                    continue;
1624
                }
1625
1626
                $this->items[ $key ]['quantity'] += $args['quantity'];
1627
                break;
1628
            }
1629
            
1630
            foreach ( $this->cart_details as $cart_key => $cart_item ) {
1631
                if ( $item_id != $cart_item['id'] ) {
1632
                    continue;
1633
                }
1634
1635
                $found_cart_key = $cart_key;
1636
                break;
1637
            }
1638
        }
1639
        
1640
        if ($has_quantities && $found_cart_key !== false) {
1641
            $cart_item          = $this->cart_details[$found_cart_key];
1642
            $item_price         = $cart_item['item_price'];
1643
            $quantity           = !empty( $cart_item['quantity'] ) ? $cart_item['quantity'] : 1;
1644
            $tax_rate           = !empty( $cart_item['vat_rate'] ) ? $cart_item['vat_rate'] : 0;
1645
            
1646
            $new_quantity       = $quantity + $args['quantity'];
1647
            $subtotal           = $item_price * $new_quantity;
1648
            
1649
            $args['quantity']   = $new_quantity;
1650
            $discount           = !empty( $args['discount'] ) ? $args['discount'] : 0;
1651
            $tax                = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1652
            
1653
            $discount_increased = $discount > 0 && $subtotal > 0 && $discount > (float)$cart_item['discount'] ? $discount - (float)$cart_item['discount'] : 0;
1654
            $tax_increased      = $tax > 0 && $subtotal > 0 && $tax > (float)$cart_item['tax'] ? $tax - (float)$cart_item['tax'] : 0;
1655
            // The total increase equals the number removed * the item_price
1656
            $total_increased    = wpinv_round_amount( $item_price );
1657
            
1658
            if ( wpinv_prices_include_tax() ) {
1659
                $subtotal -= wpinv_round_amount( $tax );
1660
            }
1661
1662
            $total              = $subtotal - $discount + $tax;
1663
1664
            // Do not allow totals to go negative
1665
            if( $total < 0 ) {
1666
                $total = 0;
1667
            }
1668
            
1669
            $cart_item['quantity']  = $new_quantity;
1670
            $cart_item['subtotal']  = $subtotal;
1671
            $cart_item['discount']  = $discount;
1672
            $cart_item['tax']       = $tax;
1673
            $cart_item['price']     = $total;
1674
            
1675
            $subtotal               = $total_increased - $discount_increased;
1676
            $tax                    = $tax_increased;
1677
            
1678
            $this->cart_details[$found_cart_key] = $cart_item;
1679
        } else {
1680
            // Set custom price.
1681
            if ( $args['custom_price'] !== '' ) {
1682
                $item_price = $args['custom_price'];
1683
            } else {
1684
                // Allow overriding the price
1685
                if ( false !== $args['item_price'] ) {
1686
                    $item_price = $args['item_price'];
1687
                } else {
1688
                    $item_price = wpinv_get_item_price( $item->ID );
1689
                }
1690
            }
1691
1692
            // Sanitizing the price here so we don't have a dozen calls later
1693
            $item_price = wpinv_sanitize_amount( $item_price );
1694
            $subtotal   = wpinv_round_amount( $item_price * $args['quantity'] );
1695
        
1696
            $discount   = !empty( $args['discount'] ) ? $args['discount'] : 0;
1697
            $tax_class  = !empty( $args['vat_class'] ) ? $args['vat_class'] : '';
1698
            $tax_rate   = !empty( $args['vat_rate'] ) ? $args['vat_rate'] : 0;
1699
            $tax        = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1700
1701
            // Setup the items meta item
1702
            $new_item = array(
1703
                'id'       => $item->ID,
1704
                'quantity' => $args['quantity'],
1705
            );
1706
1707
            $this->items[]  = $new_item;
1708
1709
            if ( wpinv_prices_include_tax() ) {
1710
                $subtotal -= wpinv_round_amount( $tax );
1711
            }
1712
1713
            $total      = $subtotal - $discount + $tax;
1714
1715
            // Do not allow totals to go negative
1716
            if( $total < 0 ) {
1717
                $total = 0;
1718
            }
1719
        
1720
            $this->cart_details[] = array(
1721
                'name'          => !empty($args['name']) ? $args['name'] : $item->get_name(),
1722
                'id'            => $item->ID,
1723
                'item_price'    => wpinv_round_amount( $item_price ),
1724
                'custom_price'  => ( $args['custom_price'] !== '' ? wpinv_round_amount( $args['custom_price'] ) : '' ),
1725
                'quantity'      => $args['quantity'],
1726
                'discount'      => $discount,
1727
                'subtotal'      => wpinv_round_amount( $subtotal ),
1728
                'tax'           => wpinv_round_amount( $tax ),
1729
                'price'         => wpinv_round_amount( $total ),
1730
                'vat_rate'      => $tax_rate,
1731
                'vat_class'     => $tax_class,
1732
                'meta'          => $args['meta'],
1733
                'fees'          => $args['fees'],
1734
            );
1735
                        
1736
            $subtotal = $subtotal - $discount;
1737
        }
1738
        
1739
        $added_item = end( $this->cart_details );
1740
        $added_item['action']  = 'add';
1741
        
1742
        $this->pending['items'][] = $added_item;
1743
        
1744
        $this->increase_subtotal( $subtotal );
1745
        $this->increase_tax( $tax );
1746
1747
        return true;
1748
    }
1749
    
1750
    public function remove_item( $item_id, $args = array() ) {
1751
        // Set some defaults
1752
        $defaults = array(
1753
            'quantity'      => 1,
1754
            'item_price'    => false,
1755
            'custom_price'  => '',
1756
            'cart_index'    => false,
1757
        );
1758
        $args = wp_parse_args( $args, $defaults );
1759
1760
        // Bail if this post isn't a item
1761
        if ( get_post_type( $item_id ) !== 'wpi_item' ) {
1762
            return false;
1763
        }
1764
        
1765
        $this->cart_details = !empty( $this->cart_details ) ? array_values( $this->cart_details ) : $this->cart_details;
1766
1767
        foreach ( $this->items as $key => $item ) {
1768
            if ( !empty($item['id']) && (int)$item_id !== (int)$item['id'] ) {
1769
                continue;
1770
            }
1771
1772
            if ( false !== $args['cart_index'] ) {
1773
                $cart_index = absint( $args['cart_index'] );
1774
                $cart_item  = ! empty( $this->cart_details[ $cart_index ] ) ? $this->cart_details[ $cart_index ] : false;
1775
1776
                if ( ! empty( $cart_item ) ) {
1777
                    // If the cart index item isn't the same item ID, don't remove it
1778
                    if ( !empty($cart_item['id']) && $cart_item['id'] != $item['id'] ) {
1779
                        continue;
1780
                    }
1781
                }
1782
            }
1783
1784
            $item_quantity = $this->items[ $key ]['quantity'];
1785
            if ( $item_quantity > $args['quantity'] ) {
1786
                $this->items[ $key ]['quantity'] -= $args['quantity'];
1787
                break;
1788
            } else {
1789
                unset( $this->items[ $key ] );
1790
                break;
1791
            }
1792
        }
1793
1794
        $found_cart_key = false;
1795
        if ( false === $args['cart_index'] ) {
1796
            foreach ( $this->cart_details as $cart_key => $item ) {
1797
                if ( $item_id != $item['id'] ) {
1798
                    continue;
1799
                }
1800
1801
                if ( false !== $args['item_price'] ) {
1802
                    if ( isset( $item['item_price'] ) && (float) $args['item_price'] != (float) $item['item_price'] ) {
1803
                        continue;
1804
                    }
1805
                }
1806
1807
                $found_cart_key = $cart_key;
1808
                break;
1809
            }
1810
        } else {
1811
            $cart_index = absint( $args['cart_index'] );
1812
1813
            if ( ! array_key_exists( $cart_index, $this->cart_details ) ) {
1814
                return false; // Invalid cart index passed.
1815
            }
1816
1817
            if ( (int) $this->cart_details[ $cart_index ]['id'] > 0 && (int) $this->cart_details[ $cart_index ]['id'] !== (int) $item_id ) {
1818
                return false; // We still need the proper Item ID to be sure.
1819
            }
1820
1821
            $found_cart_key = $cart_index;
1822
        }
1823
        
1824
        $cart_item  = $this->cart_details[$found_cart_key];
1825
        $quantity   = !empty( $cart_item['quantity'] ) ? $cart_item['quantity'] : 1;
1826
        
1827
        if ( count( $this->cart_details ) == 1 && ( $quantity - $args['quantity'] ) < 1 ) {
1828
            return false; // Invoice must contain at least one item.
1829
        }
1830
        
1831
        $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...
1832
        
1833
        if ( $quantity > $args['quantity'] ) {
1834
            $item_price         = $cart_item['item_price'];
1835
            $tax_rate           = !empty( $cart_item['vat_rate'] ) ? $cart_item['vat_rate'] : 0;
1836
            
1837
            $new_quantity       = max( $quantity - $args['quantity'], 1);
1838
            $subtotal           = $item_price * $new_quantity;
1839
            
1840
            $args['quantity']   = $new_quantity;
1841
            $discount           = !empty( $cart_item['discount'] ) ? $cart_item['discount'] : 0;
1842
            $tax                = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1843
            
1844
            $discount_decrease  = (float)$cart_item['discount'] > 0 && $quantity > 0 ? wpinv_round_amount( ( (float)$cart_item['discount'] / $quantity ) ) : 0;
1845
            $discount_decrease  = $discount > 0 && $subtotal > 0 && (float)$cart_item['discount'] > $discount ? (float)$cart_item['discount'] - $discount : $discount_decrease; 
1846
            $tax_decrease       = (float)$cart_item['tax'] > 0 && $quantity > 0 ? wpinv_round_amount( ( (float)$cart_item['tax'] / $quantity ) ) : 0;
1847
            $tax_decrease       = $tax > 0 && $subtotal > 0 && (float)$cart_item['tax'] > $tax ? (float)$cart_item['tax'] - $tax : $tax_decrease;
1848
            
1849
            // The total increase equals the number removed * the item_price
1850
            $total_decrease     = wpinv_round_amount( $item_price );
1851
            
1852
            if ( wpinv_prices_include_tax() ) {
1853
                $subtotal -= wpinv_round_amount( $tax );
1854
            }
1855
1856
            $total              = $subtotal - $discount + $tax;
1857
1858
            // Do not allow totals to go negative
1859
            if( $total < 0 ) {
1860
                $total = 0;
1861
            }
1862
            
1863
            $cart_item['quantity']  = $new_quantity;
1864
            $cart_item['subtotal']  = $subtotal;
1865
            $cart_item['discount']  = $discount;
1866
            $cart_item['tax']       = $tax;
1867
            $cart_item['price']     = $total;
1868
            
1869
            $added_item             = $cart_item;
1870
            $added_item['id']       = $item_id;
1871
            $added_item['price']    = $total_decrease;
1872
            $added_item['quantity'] = $args['quantity'];
1873
            
1874
            $subtotal_decrease      = $total_decrease - $discount_decrease;
1875
            
1876
            $this->cart_details[$found_cart_key] = $cart_item;
1877
            
1878
            $remove_item = end( $this->cart_details );
1879
        } else {
1880
            $item_price     = $cart_item['item_price'];
1881
            $discount       = !empty( $cart_item['discount'] ) ? $cart_item['discount'] : 0;
1882
            $tax            = !empty( $cart_item['tax'] ) ? $cart_item['tax'] : 0;
1883
        
1884
            $subtotal_decrease  = ( $item_price * $quantity ) - $discount;
1885
            $tax_decrease       = $tax;
1886
1887
            unset( $this->cart_details[$found_cart_key] );
1888
            
1889
            $remove_item             = $args;
1890
            $remove_item['id']       = $item_id;
1891
            $remove_item['price']    = $subtotal_decrease;
1892
            $remove_item['quantity'] = $args['quantity'];
1893
        }
1894
        
1895
        $remove_item['action']      = 'remove';
1896
        $this->pending['items'][]   = $remove_item;
1897
               
1898
        $this->decrease_subtotal( $subtotal_decrease );
1899
        $this->decrease_tax( $tax_decrease );
1900
        
1901
        return true;
1902
    }
1903
    
1904
    public function update_items($temp = false) {
1905
        global $wpinv_euvat, $wpi_current_id, $wpi_item_id, $wpi_nosave;
1906
        
1907
        if ( !empty( $this->cart_details ) ) {
1908
            $wpi_nosave             = $temp;
1909
            $cart_subtotal          = 0;
1910
            $cart_discount          = 0;
1911
            $cart_tax               = 0;
1912
            $cart_details           = array();
1913
            
1914
            $_POST['wpinv_country'] = $this->country;
1915
            $_POST['wpinv_state']   = $this->state;
1916
            
1917
            foreach ( $this->cart_details as $key => $item ) {
1918
                $item_price = $item['item_price'];
1919
                $quantity   = wpinv_item_quantities_enabled() && $item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
1920
                $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...
1921
                $subtotal   = $item_price * $quantity;
1922
                
1923
                $wpi_current_id         = $this->ID;
1924
                $wpi_item_id            = $item['id'];
1925
                
1926
                $discount   = wpinv_get_cart_item_discount_amount( $item, $this->get_discounts() );
1927
                
1928
                $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...
1929
                $tax_class  = $wpinv_euvat->get_item_class( $wpi_item_id );
1930
                $tax        = $item_price > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1931
1932
                if ( wpinv_prices_include_tax() ) {
1933
                    $subtotal -= wpinv_round_amount( $tax );
1934
                }
1935
1936
                $total      = $subtotal - $discount + $tax;
1937
1938
                // Do not allow totals to go negative
1939
                if( $total < 0 ) {
1940
                    $total = 0;
1941
                }
1942
1943
                $cart_details[] = array(
1944
                    'id'          => $item['id'],
1945
                    'name'        => $item['name'],
1946
                    'item_price'  => wpinv_round_amount( $item_price ),
1947
                    'custom_price'=> ( isset( $item['custom_price'] ) ? $item['custom_price'] : '' ),
1948
                    'quantity'    => $quantity,
1949
                    'discount'    => $discount,
1950
                    'subtotal'    => wpinv_round_amount( $subtotal ),
1951
                    'tax'         => wpinv_round_amount( $tax ),
1952
                    'price'       => wpinv_round_amount( $total ),
1953
                    'vat_rate'    => $tax_rate,
1954
                    'vat_class'   => $tax_class,
1955
                    'meta'        => isset($item['meta']) ? $item['meta'] : array(),
1956
                    'fees'        => isset($item['fees']) ? $item['fees'] : array(),
1957
                );
1958
                
1959
                $cart_subtotal  += (float)($subtotal - $discount); // TODO
1960
                $cart_discount  += (float)($discount);
1961
                $cart_tax       += (float)($tax);
1962
            }
1963
            if ( $cart_subtotal < 0 ) {
1964
                $cart_subtotal = 0;
1965
            }
1966
            if ( $cart_tax < 0 ) {
1967
                $cart_tax = 0;
1968
            }
1969
            $this->subtotal = wpinv_round_amount( $cart_subtotal );
1970
            $this->tax      = wpinv_round_amount( $cart_tax );
1971
            $this->discount = wpinv_round_amount( $cart_discount );
1972
            
1973
            $this->recalculate_total();
1974
            
1975
            $this->cart_details = $cart_details;
1976
        }
1977
1978
        return $this;
1979
    }
1980
    
1981
    public function recalculate_totals($temp = false) {        
1982
        $this->update_items($temp);
1983
        $this->save( true );
1984
        
1985
        return $this;
1986
    }
1987
    
1988
    public function needs_payment() {
1989
        $valid_invoice_statuses = apply_filters( 'wpinv_valid_invoice_statuses_for_payment', array( 'wpi-pending' ), $this );
1990
1991
        if ( $this->has_status( $valid_invoice_statuses ) && ( $this->get_total() > 0 || $this->is_free_trial() || $this->is_free() ) ) {
1992
            $needs_payment = true;
1993
        } else {
1994
            $needs_payment = false;
1995
        }
1996
1997
        return apply_filters( 'wpinv_needs_payment', $needs_payment, $this, $valid_invoice_statuses );
1998
    }
1999
    
2000
    public function get_checkout_payment_url( $with_key = false, $secret = false ) {
2001
        $pay_url = wpinv_get_checkout_uri();
2002
2003
        if ( is_ssl() ) {
2004
            $pay_url = str_replace( 'http:', 'https:', $pay_url );
2005
        }
2006
        
2007
        $key = $this->get_key();
2008
2009
        if ( $with_key ) {
2010
            $pay_url = add_query_arg( 'invoice_key', $key, $pay_url );
2011
        } else {
2012
            $pay_url = add_query_arg( array( 'wpi_action' => 'pay_for_invoice', 'invoice_key' => $key ), $pay_url );
2013
        }
2014
        
2015
        if ( $secret ) {
2016
            $pay_url = add_query_arg( array( '_wpipay' => md5( $this->get_user_id() . '::' . $this->get_email() . '::' . $key ) ), $pay_url );
2017
        }
2018
2019
        return apply_filters( 'wpinv_get_checkout_payment_url', $pay_url, $this, $with_key, $secret );
2020
    }
2021
    
2022
    public function get_view_url( $with_key = false ) {
2023
        $invoice_url = get_permalink( $this->ID );
2024
2025
        if ( $with_key ) {
2026
            $invoice_url = add_query_arg( 'invoice_key', $this->get_key(), $invoice_url );
2027
        }
2028
2029
        return apply_filters( 'wpinv_get_view_url', $invoice_url, $this, $with_key );
2030
    }
2031
    
2032
    public function generate_key( $string = '' ) {
2033
        $auth_key  = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
2034
        return strtolower( md5( $string . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'wpinv', true ) ) );  // Unique key
2035
    }
2036
    
2037
    public function is_recurring() {
2038
        if ( empty( $this->cart_details ) ) {
2039
            return false;
2040
        }
2041
        
2042
        $has_subscription = false;
2043 View Code Duplication
        foreach( $this->cart_details as $cart_item ) {
2044
            if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
2045
                $has_subscription = true;
2046
                break;
2047
            }
2048
        }
2049
        
2050
        if ( count( $this->cart_details ) > 1 ) {
2051
            $has_subscription = false;
2052
        }
2053
2054
        return apply_filters( 'wpinv_invoice_has_recurring_item', $has_subscription, $this->cart_details );
2055
    }
2056
    
2057
    public function is_free_trial() {
2058
        $is_free_trial = false;
2059
        
2060
        if ( $this->is_parent() && $item = $this->get_recurring( true ) ) {
2061
            if ( !empty( $item ) && $item->has_free_trial() ) {
2062
                $is_free_trial = true;
2063
            }
2064
        }
2065
2066
        return apply_filters( 'wpinv_invoice_is_free_trial', $is_free_trial, $this->cart_details );
2067
    }
2068
    
2069
    public function get_recurring( $object = false ) {
2070
        $item = NULL;
2071
        
2072
        if ( empty( $this->cart_details ) ) {
2073
            return $item;
2074
        }
2075
        
2076 View Code Duplication
        foreach( $this->cart_details as $cart_item ) {
2077
            if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
2078
                $item = $cart_item['id'];
2079
                break;
2080
            }
2081
        }
2082
        
2083
        if ( $object ) {
2084
            $item = $item ? new WPInv_Item( $item ) : NULL;
2085
            
2086
            apply_filters( 'wpinv_invoice_get_recurring_item', $item, $this );
2087
        }
2088
2089
        return apply_filters( 'wpinv_invoice_get_recurring_item_id', $item, $this );
2090
    }
2091
    
2092
    public function get_subscription_name() {
2093
        $item = $this->get_recurring( true );
2094
        
2095
        if ( empty( $item ) ) {
2096
            return NULL;
2097
        }
2098
        
2099
        if ( !($name = $item->get_name()) ) {
2100
            $name = $item->post_name;
2101
        }
2102
2103
        return apply_filters( 'wpinv_invoice_get_subscription_name', $name, $this );
2104
    }
2105
        
2106
    public function get_expiration() {
2107
        $expiration = $this->get_meta( '_wpinv_subscr_expiration', true );
2108
        return $expiration;
2109
    }
2110
    
2111
    public function get_cancelled_date( $formatted = true ) {
2112
        $cancelled_date = $this->get_subscription_status() == 'cancelled' ? $this->get_meta( '_wpinv_subscr_cancelled_on', true ) : '';
2113
        
2114
        if ( $formatted && $cancelled_date ) {
2115
            $cancelled_date = date_i18n( get_option( 'date_format' ), strtotime( $cancelled_date ) );
2116
        }
2117
        
2118
        return $cancelled_date;
2119
    }
2120
    
2121
    public function get_trial_end_date( $formatted = true ) {
2122
        if ( !$this->is_free_trial() || ! ( $this->is_paid() || $this->is_refunded() ) ) {
2123
            return NULL;
2124
        }
2125
        
2126
        $trial_end_date = $this->get_subscription_status() == 'trialing' ? $this->get_meta( '_wpinv_subscr_trial_end', true ) : '';
2127
        
2128
        if ( empty( $trial_end_date ) ) {
2129
            $trial_start_time = strtotime( $this->get_subscription_start() );
2130
            $trial_start_time += ( wpinv_period_in_days( $this->get_subscription_trial_interval(), $this->get_subscription_trial_period() ) * DAY_IN_SECONDS ) ;
2131
            
2132
            $trial_end_date = date_i18n( 'Y-m-d H:i:s', $trial_start_time );
2133
        }
2134
        
2135
        if ( $formatted && $trial_end_date ) {
2136
            $trial_end_date = date_i18n( get_option( 'date_format' ), strtotime( $trial_end_date ) );
2137
        }
2138
        
2139
        return $trial_end_date;
2140
    }
2141
    
2142
    public function get_subscription_created( $default = true ) {
2143
        $created = $this->get_meta( '_wpinv_subscr_created', true );
2144
        
2145
        if ( empty( $created ) && $default ) {
2146
            $created = $this->date;
2147
        }
2148
        return $created;
2149
    }
2150
    
2151
    public function get_subscription_start( $formatted = true ) {
2152
        if ( ! ( $this->is_paid() || $this->is_refunded() ) ) {
2153
            return '-';
2154
        }
2155
        $start   = $this->get_subscription_created();
2156
        
2157
        if ( $formatted ) {
2158
            $date = date_i18n( get_option( 'date_format' ), strtotime( $start ) );
2159
        } else {
2160
            $date = date_i18n( 'Y-m-d H:i:s', strtotime( $start ) );
2161
        }
2162
2163
        return $date;
2164
    }
2165
    
2166
    public function get_subscription_end( $formatted = true ) {
2167
        if ( ! ( $this->is_paid() || $this->is_refunded() ) ) {
2168
            return '-';
2169
        }
2170
2171
        if ( $this->get_subscription_status() == 'cancelled' ) {
2172
            return $this->get_cancelled_date( $formatted );
2173
        }
2174
2175
        $start          = $this->get_subscription_created();
2176
        $interval       = $this->get_subscription_interval();
2177
        $period         = $this->get_subscription_period( true );
2178
        $bill_times     = (int)$this->get_bill_times();
2179
        
2180
        if ( $bill_times == 0 ) {
2181
            return $formatted ? __( 'Until cancelled', 'invoicing' ) : $bill_times;
2182
        }
2183
        
2184
        $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...
2185
        
2186
        $end_time = strtotime( $start . '+' . ( $interval * $bill_times ) . ' ' . $period );
2187
        
2188
        if ( $this->is_free_trial() ) {
2189
            $end_time += ( wpinv_period_in_days( $this->get_subscription_trial_interval(), $this->get_subscription_trial_period() ) * DAY_IN_SECONDS ) ;
2190
        }
2191
        
2192
        if ( $formatted ) {
2193
            $date = date_i18n( get_option( 'date_format' ), $end_time );
2194
        } else {
2195
            $date = date_i18n( 'Y-m-d H:i:s', $end_time );
2196
        }
2197
2198
        return $date;
2199
    }
2200
    
2201
    public function get_expiration_time() {
2202
        return strtotime( $this->get_expiration(), current_time( 'timestamp' ) );
2203
    }
2204
    
2205
    public function get_original_invoice_id() {        
2206
        return $this->parent_invoice;
2207
    }
2208
    
2209
    public function get_bill_times() {
2210
        $subscription_data = $this->get_subscription_data();
2211
        return $subscription_data['bill_times'];
2212
    }
2213
2214
    public function get_child_payments( $self = false ) {
2215
        $invoices = get_posts( array(
2216
            'post_type'         => $this->post_type,
2217
            'post_parent'       => (int)$this->ID,
2218
            'posts_per_page'    => '999',
2219
            'post_status'       => array( 'publish', 'wpi-processing', 'wpi-renewal' ),
2220
            'orderby'           => 'ID',
2221
            'order'             => 'DESC',
2222
            'fields'            => 'ids'
2223
        ) );
2224
        
2225
        if ( $this->is_free_trial() ) {
2226
            $self = false;
2227
        }
2228
        
2229
        if ( $self && $this->is_paid() ) {
2230
            if ( !empty( $invoices ) ) {
2231
                $invoices[] = (int)$this->ID;
2232
            } else {
2233
                $invoices = array( $this->ID );
2234
            }
2235
            
2236
            $invoices = array_unique( $invoices );
2237
        }
2238
2239
        return $invoices;
2240
    }
2241
2242
    public function get_total_payments( $self = true ) {
2243
        return count( $this->get_child_payments( $self ) );
2244
    }
2245
    
2246
    public function get_subscriptions( $limit = -1 ) {
2247
        $subscriptions = wpinv_get_subscriptions( array( 'parent_invoice_id' => $this->ID, 'numberposts' => $limit ) );
2248
2249
        return $subscriptions;
2250
    }
2251
    
2252
    public function get_subscription_id() {
2253
        $subscription_id = $this->get_meta( '_wpinv_subscr_profile_id', true );
2254
        
2255
        if ( empty( $subscription_id ) && !empty( $this->parent_invoice ) ) {
2256
            $parent_invoice = wpinv_get_invoice( $this->parent_invoice );
2257
            
2258
            $subscription_id = $parent_invoice->get_meta( '_wpinv_subscr_profile_id', true );
2259
        }
2260
        
2261
        return $subscription_id;
2262
    }
2263
    
2264
    public function get_subscription_status() {
2265
        $subscription_status = $this->get_meta( '_wpinv_subscr_status', true );
2266
2267
        if ( empty( $subscription_status ) ) {
2268
            $status = 'pending';
2269
            
2270
            if ( $this->is_paid() ) {        
2271
                $bill_times   = (int)$this->get_bill_times();
2272
                $times_billed = (int)$this->get_total_payments();
2273
                $expiration = $this->get_subscription_end( false );
2274
                $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;
2275
                
2276
                if ( (int)$bill_times == 0 ) {
2277
                    $status = $expired ? 'expired' : 'active';
2278
                } else if ( $bill_times > 0 && $times_billed >= $bill_times ) {
2279
                    $status = 'completed';
2280
                } else if ( $expired ) {
2281
                    $status = 'expired';
2282
                } else if ( $bill_times > 0 ) {
2283
                    $status = 'active';
2284
                } else {
2285
                    $status = 'pending';
2286
                }
2287
            }
2288
            
2289
            if ( $status && $status != $subscription_status ) {
2290
                $subscription_status = $status;
2291
                
2292
                $this->update_meta( '_wpinv_subscr_status', $status );
2293
            }
2294
        }
2295
        
2296
        return $subscription_status;
2297
    }
2298
    
2299
    public function get_subscription_status_label( $status = '' ) {
2300
        $status = !empty( $status ) ? $status : $this->get_subscription_status();
2301
2302
        switch( $status ) {
2303
            case 'active' :
2304
                $status_label = __( 'Active', 'invoicing' );
2305
                break;
2306
2307
            case 'cancelled' :
2308
                $status_label = __( 'Cancelled', 'invoicing' );
2309
                break;
2310
                
2311
            case 'completed' :
2312
                $status_label = __( 'Completed', 'invoicing' );
2313
                break;
2314
2315
            case 'expired' :
2316
                $status_label = __( 'Expired', 'invoicing' );
2317
                break;
2318
2319
            case 'pending' :
2320
                $status_label = __( 'Pending', 'invoicing' );
2321
                break;
2322
2323
            case 'failing' :
2324
                $status_label = __( 'Failing', 'invoicing' );
2325
                break;
2326
                
2327
            case 'stopped' :
2328
                $status_label = __( 'Stopped', 'invoicing' );
2329
                break;
2330
                
2331
            case 'trialing' :
2332
                $status_label = __( 'Trialing', 'invoicing' );
2333
                break;
2334
2335
            default:
2336
                $status_label = $status;
2337
                break;
2338
        }
2339
2340
        return $status_label;
2341
    }
2342
    
2343 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...
2344
        $period = $this->get_meta( '_wpinv_subscr_period', true );
2345
        
2346
        // Fix period for old invoices
2347
        if ( $period == 'day' ) {
2348
            $period = 'D';
2349
        } else if ( $period == 'week' ) {
2350
            $period = 'W';
2351
        } else if ( $period == 'month' ) {
2352
            $period = 'M';
2353
        } else if ( $period == 'year' ) {
2354
            $period = 'Y';
2355
        }
2356
        
2357
        if ( !in_array( $period, array( 'D', 'W', 'M', 'Y' ) ) ) {
2358
            $period = 'D';
2359
        }
2360
        
2361
        if ( $full ) {
2362
            switch( $period ) {
2363
                case 'D':
2364
                    $period = 'day';
2365
                break;
2366
                case 'W':
2367
                    $period = 'week';
2368
                break;
2369
                case 'M':
2370
                    $period = 'month';
2371
                break;
2372
                case 'Y':
2373
                    $period = 'year';
2374
                break;
2375
            }
2376
        }
2377
        
2378
        return $period;
2379
    }
2380
    
2381
    public function get_subscription_interval() {
2382
        $interval = (int)$this->get_meta( '_wpinv_subscr_interval', true );
2383
        
2384
        if ( !$interval > 0 ) {
2385
            $interval = 1;
2386
        }
2387
        
2388
        return $interval;
2389
    }
2390
    
2391 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...
2392
        if ( !$this->is_free_trial() ) {
2393
            return '';
2394
        }
2395
        
2396
        $period = $this->get_meta( '_wpinv_subscr_trial_period', true );
2397
        
2398
        // Fix period for old invoices
2399
        if ( $period == 'day' ) {
2400
            $period = 'D';
2401
        } else if ( $period == 'week' ) {
2402
            $period = 'W';
2403
        } else if ( $period == 'month' ) {
2404
            $period = 'M';
2405
        } else if ( $period == 'year' ) {
2406
            $period = 'Y';
2407
        }
2408
        
2409
        if ( !in_array( $period, array( 'D', 'W', 'M', 'Y' ) ) ) {
2410
            $period = 'D';
2411
        }
2412
        
2413
        if ( $full ) {
2414
            switch( $period ) {
2415
                case 'D':
2416
                    $period = 'day';
2417
                break;
2418
                case 'W':
2419
                    $period = 'week';
2420
                break;
2421
                case 'M':
2422
                    $period = 'month';
2423
                break;
2424
                case 'Y':
2425
                    $period = 'year';
2426
                break;
2427
            }
2428
        }
2429
        
2430
        return $period;
2431
    }
2432
    
2433
    public function get_subscription_trial_interval() {
2434
        if ( !$this->is_free_trial() ) {
2435
            return 0;
2436
        }
2437
        
2438
        $interval = (int)$this->get_meta( '_wpinv_subscr_trial_interval', true );
2439
        
2440
        if ( !$interval > 0 ) {
2441
            $interval = 1;
2442
        }
2443
        
2444
        return $interval;
2445
    }
2446
    
2447 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...
2448
        $args = array(
2449
            'status' => 'failing'
2450
        );
2451
2452
        if ( $this->update_subscription( $args ) ) {
2453
            do_action( 'wpinv_subscription_failing', $this->ID, $this );
2454
            return true;
2455
        }
2456
2457
        return false;
2458
    }
2459
    
2460 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...
2461
        $args = array(
2462
            'status' => 'stopped'
2463
        );
2464
2465
        if ( $this->update_subscription( $args ) ) {
2466
            do_action( 'wpinv_subscription_stopped', $this->ID, $this );
2467
            return true;
2468
        }
2469
2470
        return false;
2471
    }
2472
    
2473 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...
2474
        $args = array(
2475
            'status' => 'active'
2476
        );
2477
2478
        if ( $this->update_subscription( $args ) ) {
2479
            do_action( 'wpinv_subscription_restarted', $this->ID, $this );
2480
            return true;
2481
        }
2482
2483
        return false;
2484
    }
2485
2486
    public function cancel_subscription() {
2487
        $args = array(
2488
            'status' => 'cancelled'
2489
        );
2490
2491
        if ( $this->update_subscription( $args ) ) {
2492
            if ( is_user_logged_in() ) {
2493
                $userdata = get_userdata( get_current_user_id() );
2494
                $user     = $userdata->user_login;
2495
            } else {
2496
                $user = __( 'gateway', 'invoicing' );
2497
            }
2498
            
2499
            $subscription_id = $this->get_subscription_id();
2500
            if ( !$subscription_id ) {
2501
                $subscription_id = $this->ID;
2502
            }
2503
2504
            $note = sprintf( __( 'Subscription %s has been cancelled by %s', 'invoicing' ), $subscription_id, $user );
2505
            $this->add_note( $note );
2506
2507
            do_action( 'wpinv_subscription_cancelled', $this->ID, $this );
2508
            return true;
2509
        }
2510
2511
        return false;
2512
    }
2513
2514
    public function can_cancel() {
2515
        return apply_filters( 'wpinv_subscription_can_cancel', false, $this );
2516
    }
2517
    
2518
    public function add_subscription( $data = array() ) {
2519
        if ( empty( $this->ID ) ) {
2520
            return false;
2521
        }
2522
2523
        $defaults = array(
2524
            'period'            => '',
2525
            'initial_amount'    => '',
2526
            'recurring_amount'  => '',
2527
            'interval'          => 0,
2528
            'trial_interval'    => 0,
2529
            'trial_period'      => '',
2530
            'bill_times'        => 0,
2531
            'item_id'           => 0,
2532
            'created'           => '',
2533
            'expiration'        => '',
2534
            'status'            => '',
2535
            'profile_id'        => '',
2536
        );
2537
2538
        $args = wp_parse_args( $data, $defaults );
2539
2540
        if ( $args['expiration'] && strtotime( 'NOW', current_time( 'timestamp' ) ) > strtotime( $args['expiration'], current_time( 'timestamp' ) ) ) {
2541
            if ( 'active' == $args['status'] || $args['status'] == 'trialing' ) {
2542
                $args['status'] = 'expired';
2543
            }
2544
        }
2545
2546
        do_action( 'wpinv_subscription_pre_create', $args, $data, $this );
2547
        
2548
        if ( !empty( $args ) ) {
2549
            foreach ( $args as $key => $value ) {
2550
                $this->update_meta( '_wpinv_subscr_' . $key, $value );
2551
            }
2552
        }
2553
2554
        do_action( 'wpinv_subscription_post_create', $args, $data, $this );
2555
2556
        return true;
2557
    }
2558
    
2559
    public function update_subscription( $args = array() ) {
2560
        if ( empty( $this->ID ) ) {
2561
            return false;
2562
        }
2563
2564
        if ( !empty( $args['expiration'] ) && $args['expiration'] && strtotime( 'NOW', current_time( 'timestamp' ) ) > strtotime( $args['expiration'], current_time( 'timestamp' ) ) ) {
2565
            if ( !isset( $args['status'] ) || ( isset( $args['status'] ) && ( 'active' == $args['status'] || $args['status'] == 'trialing' ) ) ) {
2566
                $args['status'] = 'expired';
2567
            }
2568
        }
2569
2570
        if ( isset( $args['status'] ) && $args['status'] == 'cancelled' && empty( $args['cancelled_on'] ) ) {
2571
            $args['cancelled_on'] = date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) );
2572
        }
2573
2574
        do_action( 'wpinv_subscription_pre_update', $args, $this );
2575
        
2576
        if ( !empty( $args ) ) {
2577
            foreach ( $args as $key => $value ) {
2578
                $this->update_meta( '_wpinv_subscr_' . $key, $value );
2579
            }
2580
        }
2581
2582
        do_action( 'wpinv_subscription_post_update', $args, $this );
2583
2584
        return true;
2585
    }
2586
    
2587
    public function renew_subscription() {
2588
        $parent_invoice = $this->get_parent_payment();
2589
        $parent_invoice = empty( $parent_invoice ) ? $this : $parent_invoice;
2590
        
2591
        $current_time   = current_time( 'timestamp' );
2592
        $start          = $this->get_subscription_created();
2593
        $start          = $start ? strtotime( $start ) : $current_time;
2594
        $expires        = $this->get_expiration_time();
2595
        
2596
        if ( !$expires ) {
2597
            $expires    = strtotime( '+' . $parent_invoice->get_subscription_interval() . ' ' . $parent_invoice->get_subscription_period( true ), $start );
2598
        }
2599
        
2600
        $expiration     = date_i18n( 'Y-m-d 23:59:59', $expires );
2601
        $expiration     = apply_filters( 'wpinv_subscription_renewal_expiration', $expiration, $this->ID, $this );
2602
        $bill_times     = $parent_invoice->get_bill_times();
2603
        $times_billed   = $parent_invoice->get_total_payments();
2604
        
2605
        if ( $parent_invoice->get_subscription_status() == 'trialing' && ( $times_billed > 0 || strtotime( date_i18n( 'Y-m-d' ) ) < strtotime( $parent_invoice->get_trial_end_date( false ) ) ) ) {
2606
            $args = array(
2607
                'status'     => 'active',
2608
            );
2609
2610
            $parent_invoice->update_subscription( $args );
2611
        }
2612
        
2613
        do_action( 'wpinv_subscription_pre_renew', $this->ID, $expiration, $this );
2614
2615
        $status       = 'active';
2616
        if ( $bill_times > 0 && $times_billed >= $bill_times ) {
2617
            $this->complete_subscription();
2618
            $status = 'completed';
2619
        }
2620
2621
        $args = array(
2622
            'expiration' => $expiration,
2623
            'status'     => $status,
2624
        );
2625
2626
        $this->update_subscription( $args );
2627
2628
        do_action( 'wpinv_subscription_post_renew', $this->ID, $expiration, $this );
2629
        do_action( 'wpinv_recurring_set_subscription_status', $this->ID, $status, $this );
2630
    }
2631
    
2632
    public function complete_subscription() {
2633
        $args = array(
2634
            'status' => 'completed'
2635
        );
2636
2637
        if ( $this->update_subscription( $args ) ) {
2638
            do_action( 'wpinv_subscription_completed', $this->ID, $this );
2639
        }
2640
    }
2641
    
2642
    public function expire_subscription() {
2643
        $args = array(
2644
            'status' => 'expired'
2645
        );
2646
2647
        if ( $this->update_subscription( $args ) ) {
2648
            do_action( 'wpinv_subscription_expired', $this->ID, $this );
2649
        }
2650
    }
2651
2652
    public function get_cancel_url() {
2653
        $url = wp_nonce_url( add_query_arg( array( 'wpi_action' => 'cancel_subscription', 'sub_id' => $this->ID ) ), 'wpinv-recurring-cancel' );
2654
2655
        return apply_filters( 'wpinv_subscription_cancel_url', $url, $this );
2656
    }
2657
2658
    public function can_update() {
2659
        return apply_filters( 'wpinv_subscription_can_update', false, $this );
2660
    }
2661
2662
    public function get_update_url() {
2663
        $url = add_query_arg( array( 'action' => 'update', 'sub_id' => $this->ID ) );
2664
2665
        return apply_filters( 'wpinv_subscription_update_url', $url, $this );
2666
    }
2667
2668
    public function is_parent() {
2669
        $is_parent = empty( $this->parent_invoice ) ? true : false;
2670
2671
        return apply_filters( 'wpinv_invoice_is_parent', $is_parent, $this );
2672
    }
2673
    
2674
    public function is_renewal() {
2675
        $is_renewal = $this->parent_invoice && $this->parent_invoice != $this->ID ? true : false;
2676
2677
        return apply_filters( 'wpinv_invoice_is_renewal', $is_renewal, $this );
2678
    }
2679
    
2680
    public function get_parent_payment() {
2681
        $parent_payment = NULL;
2682
        
2683
        if ( $this->is_renewal() ) {
2684
            $parent_payment = wpinv_get_invoice( $this->parent_invoice );
2685
        }
2686
        
2687
        return $parent_payment;
2688
    }
2689
    
2690
    public function is_subscription_active() {
2691
        $ret = false;
2692
        
2693
        $subscription_status = $this->get_subscription_status();
2694
2695
        if( ! $this->is_subscription_expired() && ( $subscription_status == 'active' || $subscription_status == 'cancelled' || $subscription_status == 'trialing' ) ) {
2696
            $ret = true;
2697
        }
2698
2699
        return apply_filters( 'wpinv_subscription_is_active', $ret, $this->ID, $this );
2700
    }
2701
2702
    public function is_subscription_expired() {
2703
        $ret = false;
2704
        $subscription_status = $this->get_subscription_status();
2705
2706
        if ( $subscription_status == 'expired' ) {
2707
            $ret = true;
2708
        } else if ( 'active' === $subscription_status || 'cancelled' === $subscription_status || 'trialing' == $subscription_status ) {
2709
            $ret        = false;
2710
            $expiration = $this->get_expiration_time();
2711
2712
            if ( $expiration && strtotime( 'NOW', current_time( 'timestamp' ) ) > $expiration ) {
2713
                $ret = true;
2714
2715
                if ( 'active' === $subscription_status || 'trialing' === $subscription_status ) {
2716
                    $this->expire_subscription();
2717
                }
2718
            }
2719
        }
2720
2721
        return apply_filters( 'wpinv_subscription_is_expired', $ret, $this->ID, $this );
2722
    }
2723
    
2724
    public function get_new_expiration( $item_id = 0, $trial = true ) {
2725
        $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...
2726
        $interval = $item->get_recurring_interval();
2727
        $period = $item->get_recurring_period( true );
2728
        
2729
        $expiration_time = strtotime( '+' . $interval . ' ' . $period );
2730
        
2731
        if ( $trial && $this->is_free_trial() && $item->has_free_trial() ) {
2732
            $expiration_time += ( wpinv_period_in_days( $item->get_trial_interval(), $item->get_trial_period() ) * DAY_IN_SECONDS ) ;
2733
        }
2734
2735
        return date_i18n( 'Y-m-d 23:59:59', $expiration_time );
2736
    }
2737
    
2738
    public function get_subscription_data( $filed = '' ) {
2739
        $fields = array( 'item_id', 'status', 'period', 'initial_amount', 'recurring_amount', 'interval', 'bill_times', 'trial_period', 'trial_interval', 'expiration', 'profile_id', 'created', 'cancelled_on' );
2740
        
2741
        $subscription_meta = array();
2742
        foreach ( $fields as $field ) {
2743
            $subscription_meta[ $field ] = $this->get_meta( '_wpinv_subscr_' . $field );
2744
        }
2745
        
2746
        $item = $this->get_recurring( true );
2747
        
2748
        if ( !empty( $item ) ) {
2749
            if ( empty( $subscription_meta['item_id'] ) ) {
2750
                $subscription_meta['item_id'] = $item->ID;
2751
            }
2752
            if ( empty( $subscription_meta['period'] ) ) {
2753
                $subscription_meta['period'] = $item->get_recurring_period();
2754
            }
2755
            if ( empty( $subscription_meta['interval'] ) ) {
2756
                $subscription_meta['interval'] = $item->get_recurring_interval();
2757
            }
2758
            if ( $item->has_free_trial() ) {
2759
                if ( empty( $subscription_meta['trial_period'] ) ) {
2760
                    $subscription_meta['trial_period'] = $item->get_trial_period();
2761
                }
2762
                if ( empty( $subscription_meta['trial_interval'] ) ) {
2763
                    $subscription_meta['trial_interval'] = $item->get_trial_interval();
2764
                }
2765
            } else {
2766
                $subscription_meta['trial_period']      = '';
2767
                $subscription_meta['trial_interval']    = 0;
2768
            }
2769
            if ( !$subscription_meta['bill_times'] && $subscription_meta['bill_times'] !== 0 ) {
2770
                $subscription_meta['bill_times'] = $item->get_recurring_limit();
2771
            }
2772
            if ( $subscription_meta['initial_amount'] === '' || $subscription_meta['recurring_amount'] === '' ) {
2773
                $subscription_meta['initial_amount']    = wpinv_round_amount( $this->get_total() );
2774
                $subscription_meta['recurring_amount']  = wpinv_round_amount( $this->get_recurring_details( 'total' ) );
2775
            }
2776
        }
2777
        
2778
        if ( $filed === '' ) {
2779
            return apply_filters( 'wpinv_get_invoice_subscription_data', $subscription_meta, $this );
2780
        }
2781
        
2782
        $value = isset( $subscription_meta[$filed] ) ? $subscription_meta[$filed] : '';
2783
        
2784
        return apply_filters( 'wpinv_invoice_subscription_data_value', $value, $subscription_meta, $this );
2785
    }
2786
    
2787
    public function is_paid() {
2788
        if ( $this->has_status( array( 'publish', 'wpi-processing', 'wpi-renewal' ) ) ) {
2789
            return true;
2790
        }
2791
        
2792
        return false;
2793
    }
2794
    
2795
    public function is_refunded() {
2796
        $is_refunded = $this->has_status( array( 'wpi-refunded' ) );
2797
2798
        return apply_filters( 'wpinv_invoice_is_refunded', $is_refunded, $this );
2799
    }
2800
    
2801
    public function is_free() {
2802
        $is_free = false;
2803
        
2804
        if ( !( (float)wpinv_round_amount( $this->get_total() ) > 0 ) ) {
2805
            if ( $this->is_parent() && $this->is_recurring() ) {
2806
                $is_free = (float)wpinv_round_amount( $this->get_recurring_details( 'total' ) ) > 0 ? false : true;
2807
            } else {
2808
                $is_free = true;
2809
            }
2810
        }
2811
        
2812
        return apply_filters( 'wpinv_invoice_is_free', $is_free, $this );
2813
    }
2814
    
2815
    public function has_vat() {
2816
        global $wpinv_euvat, $wpi_country;
2817
        
2818
        $requires_vat = false;
2819
        
2820
        if ( $this->country ) {
2821
            $wpi_country        = $this->country;
2822
            
2823
            $requires_vat       = $wpinv_euvat->requires_vat( $requires_vat, $this->get_user_id(), $wpinv_euvat->invoice_has_digital_rule( $this ) );
2824
        }
2825
        
2826
        return apply_filters( 'wpinv_invoice_has_vat', $requires_vat, $this );
2827
    }
2828
    
2829
    public function refresh_item_ids() {
2830
        $item_ids = array();
2831
        
2832
        if ( !empty( $this->cart_details ) ) {
2833
            foreach ( $this->cart_details as $key => $item ) {
2834
                if ( !empty( $item['id'] ) ) {
2835
                    $item_ids[] = $item['id'];
2836
                }
2837
            }
2838
        }
2839
        
2840
        $item_ids = !empty( $item_ids ) ? implode( ',', array_unique( $item_ids ) ) : '';
2841
        
2842
        update_post_meta( $this->ID, '_wpinv_item_ids', $item_ids );
2843
    }
2844
    
2845
    public function get_invoice_quote_type( $post_id ) {
2846
        if ( empty( $post_id ) ) {
2847
            return '';
2848
        }
2849
2850
        $type = get_post_type( $post_id );
2851
2852
        if ( 'wpi_invoice' === $type ) {
2853
            $post_type = __('Invoice', 'invoicing');
2854
        } else{
2855
            $post_type = __('Quote', 'invoicing');
2856
        }
2857
2858
        return $post_type;
2859
    }
2860
}
2861