Passed
Pull Request — master (#43)
by Kiran
03:49
created

WPInv_Invoice::needs_payment()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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