Passed
Push — master ( f7b81c...169dfd )
by Stiofan
04:09
created

WPInv_Invoice::is_refunded()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

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

class MyClass { }

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

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

class MyClass {
    public $foo;
}

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

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

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

}

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

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

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

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

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

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

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

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

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

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

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

Loading history...
275
        $discount = (float)$this->subtotal - ( (float)$this->total - (float)$this->tax - (float)$this->fees_total );
276
        if ( $discount < 0 ) {
277
            $discount = 0;
278
        }
279
        $discount = wpinv_round_amount( $discount );
280
        
281
        return $discount;
282
    }
283
    
284
    private function setup_discount_code() {
285
        $discount_code = !empty( $this->discounts ) ? $this->discounts : $this->get_meta( '_wpinv_discount_code', true );
286
        return $discount_code;
287
    }
288
    
289
    private function setup_tax() {
290
        $tax = $this->get_meta( '_wpinv_tax', true );
291
292
        // We don't have tax as it's own meta and no meta was passed
293
        if ( '' === $tax ) {            
294
            $tax = isset( $this->payment_meta['tax'] ) ? $this->payment_meta['tax'] : 0;
295
        }
296
297
        return $tax;
298
    }
299
300
    private function setup_subtotal() {
301
        $subtotal     = 0;
302
        $cart_details = $this->cart_details;
303
304
        if ( is_array( $cart_details ) ) {
305
            foreach ( $cart_details as $item ) {
306
                if ( isset( $item['subtotal'] ) ) {
307
                    $subtotal += $item['subtotal'];
308
                }
309
            }
310
        } else {
311
            $subtotal  = $this->total;
312
            $tax       = wpinv_use_taxes() ? $this->tax : 0;
313
            $subtotal -= $tax;
314
        }
315
316
        return $subtotal;
317
    }
318
    
319
    private function setup_discounts() {
320
        $discounts = ! empty( $this->payment_meta['user_info']['discount'] ) ? $this->payment_meta['user_info']['discount'] : array();
321
        return $discounts;
322
    }
323
    
324
    private function setup_total() {
325
        $amount = $this->get_meta( '_wpinv_total', true );
326
327
        if ( empty( $amount ) && '0.00' != $amount ) {
328
            $meta   = $this->get_meta( '_wpinv_payment_meta', true );
329
            $meta   = maybe_unserialize( $meta );
330
331
            if ( isset( $meta['amount'] ) ) {
332
                $amount = $meta['amount'];
333
            }
334
        }
335
336
        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 = $this->ID;
525
526
            if ( $this->status == 'auto-draft' ) {
527
                if ( wpinv_get_option( 'sequential_invoice_number' ) ) {
528
                    $next_number = wpinv_get_next_invoice_number();
529
                    $number      = $next_number;
530
                }
531
            }
532
            
533
            $number = wpinv_format_invoice_number( $number );
534
        }
535
536
        return $number;
537
    }
538
    
539
    private function insert_invoice() {
540
        global $wpdb;
541
542
        $invoice_number = $this->ID;
543
        if ( $number = $this->get_meta( '_wpinv_number', true ) ) {
544
            $invoice_number = $number;
545
        }
546
547 View Code Duplication
        if ( empty( $this->key ) ) {
548
            $this->key = self::generate_key();
549
            $this->pending['key'] = $this->key;
550
        }
551
552
        if ( empty( $this->ip ) ) {
553
            $this->ip = wpinv_get_ip();
554
            $this->pending['ip'] = $this->ip;
555
        }
556
        
557
        $payment_data = array(
558
            'price'        => $this->total,
559
            'date'         => $this->date,
560
            'user_email'   => $this->email,
561
            'invoice_key'  => $this->key,
562
            'currency'     => $this->currency,
563
            'items'        => $this->items,
564
            'user_info' => array(
565
                'user_id'    => $this->user_id,
566
                'email'      => $this->email,
567
                'first_name' => $this->first_name,
568
                'last_name'  => $this->last_name,
569
                'address'    => $this->address,
570
                'phone'      => $this->phone,
571
                'city'       => $this->city,
572
                'country'    => $this->country,
573
                'state'      => $this->state,
574
                'zip'        => $this->zip,
575
                'company'    => $this->company,
576
                'vat_number' => $this->vat_number,
577
                'discount'   => $this->discounts,
578
            ),
579
            'cart_details' => $this->cart_details,
580
            'status'       => $this->status,
581
            'fees'         => $this->fees,
582
        );
583
584
        $post_data = array(
585
                        'post_title'    => $invoice_number,
586
                        'post_status'   => $this->status,
587
                        'post_author'   => $this->user_id,
588
                        'post_type'     => $this->post_type,
589
                        'post_date'     => ! empty( $this->date ) && $this->date != '0000-00-00 00:00:00' ? $this->date : current_time( 'mysql' ),
590
                        'post_date_gmt' => ! empty( $this->date ) && $this->date != '0000-00-00 00:00:00' ? get_gmt_from_date( $this->date ) : current_time( 'mysql', 1 ),
591
                        'post_parent'   => $this->parent_invoice,
592
                    );
593
        $args = apply_filters( 'wpinv_insert_invoice_args', $post_data, $this );
594
595
        // Create a blank invoice
596
        if ( !empty( $this->ID ) ) {
597
            $args['ID']         = $this->ID;
598
599
            $invoice_id = wp_update_post( $args, true );
600
        } else {
601
            $invoice_id = wp_insert_post( $args, true );
602
        }
603
604
        if ( is_wp_error( $invoice_id ) ) {
605
            return false;
606
        }
607
608
        if ( !empty( $invoice_id ) ) {
609
            $this->ID  = $invoice_id;
610
            $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...
611
612
            $this->payment_meta = apply_filters( 'wpinv_payment_meta', $this->payment_meta, $payment_data );
613
            if ( ! empty( $this->payment_meta['fees'] ) ) {
614
                $this->fees = array_merge( $this->fees, $this->payment_meta['fees'] );
615
                foreach( $this->fees as $fee ) {
616
                    $this->increase_fees( $fee['amount'] );
617
                }
618
            }
619
620
            $this->update_meta( '_wpinv_payment_meta', $this->payment_meta );            
621
            $this->new = true;
622
        }
623
624
        return $this->ID;
625
    }
626
627
    public function save( $setup = false ) {
628
        global $wpi_session;
629
        
630
        $saved = false;
631
        if ( empty( $this->items ) ) {
632
            return $saved; // Don't save empty invoice.
633
        }
634
        
635 View Code Duplication
        if ( empty( $this->key ) ) {
636
            $this->key = self::generate_key();
637
            $this->pending['key'] = $this->key;
638
        }
639
        
640
        if ( empty( $this->ID ) ) {
641
            $invoice_id = $this->insert_invoice();
642
643
            if ( false === $invoice_id ) {
644
                $saved = false;
645
            } else {
646
                $this->ID = $invoice_id;
647
            }
648
        }
649
650
        // If we have something pending, let's save it
651
        if ( !empty( $this->pending ) ) {
652
            $total_increase = 0;
653
            $total_decrease = 0;
654
655
            foreach ( $this->pending as $key => $value ) {
656
                switch( $key ) {
657
                    case 'items':
658
                        // Update totals for pending items
659
                        foreach ( $this->pending[ $key ] as $item ) {
660
                            switch( $item['action'] ) {
661
                                case 'add':
662
                                    $price = $item['price'];
663
                                    $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...
664
665
                                    if ( 'publish' === $this->status ) {
666
                                        $total_increase += $price;
667
                                    }
668
                                    break;
669
670
                                case 'remove':
671
                                    if ( 'publish' === $this->status ) {
672
                                        $total_decrease += $item['price'];
673
                                    }
674
                                    break;
675
                            }
676
                        }
677
                        break;
678
                    case 'fees':
679
                        if ( 'publish' !== $this->status ) {
680
                            break;
681
                        }
682
683
                        if ( empty( $this->pending[ $key ] ) ) {
684
                            break;
685
                        }
686
687
                        foreach ( $this->pending[ $key ] as $fee ) {
688
                            switch( $fee['action'] ) {
689
                                case 'add':
690
                                    $total_increase += $fee['amount'];
691
                                    break;
692
693
                                case 'remove':
694
                                    $total_decrease += $fee['amount'];
695
                                    break;
696
                            }
697
                        }
698
                        break;
699
                    case 'status':
700
                        $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...
701
                        break;
702
                    case 'gateway':
703
                        $this->update_meta( '_wpinv_gateway', $this->gateway );
704
                        break;
705
                    case 'mode':
706
                        $this->update_meta( '_wpinv_mode', $this->mode );
707
                        break;
708
                    case 'transaction_id':
709
                        $this->update_meta( '_wpinv_transaction_id', $this->transaction_id );
710
                        break;
711
                    case 'ip':
712
                        $this->update_meta( '_wpinv_user_ip', $this->ip );
713
                        break;
714
                    ///case 'user_id':
715
                        ///$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...
716
                        ///$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...
717
                        ///break;
718
                    case 'first_name':
719
                        $this->update_meta( '_wpinv_first_name', $this->first_name );
720
                        $this->user_info['first_name'] = $this->first_name;
721
                        break;
722
                    case 'last_name':
723
                        $this->update_meta( '_wpinv_last_name', $this->last_name );
724
                        $this->user_info['last_name'] = $this->last_name;
725
                        break;
726
                    case 'phone':
727
                        $this->update_meta( '_wpinv_phone', $this->phone );
728
                        $this->user_info['phone'] = $this->phone;
729
                        break;
730
                    case 'address':
731
                        $this->update_meta( '_wpinv_address', $this->address );
732
                        $this->user_info['address'] = $this->address;
733
                        break;
734
                    case 'city':
735
                        $this->update_meta( '_wpinv_city', $this->city );
736
                        $this->user_info['city'] = $this->city;
737
                        break;
738
                    case 'country':
739
                        $this->update_meta( '_wpinv_country', $this->country );
740
                        $this->user_info['country'] = $this->country;
741
                        break;
742
                    case 'state':
743
                        $this->update_meta( '_wpinv_state', $this->state );
744
                        $this->user_info['state'] = $this->state;
745
                        break;
746
                    case 'zip':
747
                        $this->update_meta( '_wpinv_zip', $this->zip );
748
                        $this->user_info['zip'] = $this->zip;
749
                        break;
750
                    case 'company':
751
                        $this->update_meta( '_wpinv_company', $this->company );
752
                        $this->user_info['company'] = $this->company;
753
                        break;
754
                    case 'vat_number':
755
                        $this->update_meta( '_wpinv_vat_number', $this->vat_number );
756
                        $this->user_info['vat_number'] = $this->vat_number;
757
                        
758
                        $vat_info = $wpi_session->get( 'user_vat_data' );
759
                        if ( $this->vat_number && !empty( $vat_info ) && isset( $vat_info['number'] ) && isset( $vat_info['valid'] ) && $vat_info['number'] == $this->vat_number ) {
760
                            $adddress_confirmed = isset( $vat_info['adddress_confirmed'] ) ? $vat_info['adddress_confirmed'] : false;
761
                            $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...
762
                            $this->user_info['adddress_confirmed'] = (bool)$adddress_confirmed;
763
                        }
764
    
765
                        break;
766
                    case 'vat_rate':
767
                        $this->update_meta( '_wpinv_vat_rate', $this->vat_rate );
768
                        $this->user_info['vat_rate'] = $this->vat_rate;
769
                        break;
770
                    case 'adddress_confirmed':
771
                        $this->update_meta( '_wpinv_adddress_confirmed', $this->adddress_confirmed );
772
                        $this->user_info['adddress_confirmed'] = $this->adddress_confirmed;
773
                        break;
774
                    
775
                    case 'key':
776
                        $this->update_meta( '_wpinv_key', $this->key );
777
                        break;
778
                    case 'date':
779
                        $args = array(
780
                            'ID'        => $this->ID,
781
                            'post_date' => $this->date,
782
                            'edit_date' => true,
783
                        );
784
785
                        wp_update_post( $args );
786
                        break;
787
                    case 'due_date':
788
                        if ( empty( $this->due_date ) ) {
789
                            $this->due_date = 'none';
790
                        }
791
                        
792
                        $this->update_meta( '_wpinv_due_date', $this->due_date );
793
                        break;
794
                    case 'completed_date':
795
                        $this->update_meta( '_wpinv_completed_date', $this->completed_date );
796
                        break;
797
                    case 'discounts':
798
                        if ( ! is_array( $this->discounts ) ) {
799
                            $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...
800
                        }
801
802
                        $this->user_info['discount'] = implode( ',', $this->discounts );
803
                        break;
804
                    case 'discount':
805
                        $this->update_meta( '_wpinv_discount', wpinv_round_amount( $this->discount ) );
806
                        break;
807
                    case 'discount_code':
808
                        $this->update_meta( '_wpinv_discount_code', $this->discount_code );
809
                        break;
810
                    case 'parent_invoice':
811
                        $args = array(
812
                            'ID'          => $this->ID,
813
                            'post_parent' => $this->parent_invoice,
814
                        );
815
                        wp_update_post( $args );
816
                        break;
817
                    default:
818
                        do_action( 'wpinv_save', $this, $key );
819
                        break;
820
                }
821
            }
822
823
            $this->update_meta( '_wpinv_subtotal', wpinv_round_amount( $this->subtotal ) );
824
            $this->update_meta( '_wpinv_total', wpinv_round_amount( $this->total ) );
825
            $this->update_meta( '_wpinv_tax', wpinv_round_amount( $this->tax ) );
826
            
827
            $this->items    = array_values( $this->items );
828
            
829
            $new_meta = array(
830
                'items'         => $this->items,
831
                'cart_details'  => $this->cart_details,
832
                'fees'          => $this->fees,
833
                'currency'      => $this->currency,
834
                'user_info'     => $this->user_info,
835
            );
836
            
837
            $meta        = $this->get_meta();
838
            $merged_meta = array_merge( $meta, $new_meta );
839
840
            // Only save the payment meta if it's changed
841
            if ( md5( serialize( $meta ) ) !== md5( serialize( $merged_meta) ) ) {
842
                $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...
843
                if ( false !== $updated ) {
844
                    $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...
845
                }
846
            }
847
848
            $this->pending = array();
849
            $saved         = true;
850
        } else {
851
            $this->update_meta( '_wpinv_subtotal', wpinv_round_amount( $this->subtotal ) );
852
            $this->update_meta( '_wpinv_total', wpinv_round_amount( $this->total ) );
853
            $this->update_meta( '_wpinv_tax', wpinv_round_amount( $this->tax ) );
854
        }
855
        
856
        do_action( 'wpinv_invoice_save', $this, $saved );
857
858
        if ( true === $saved || $setup ) {
859
            $this->setup_invoice( $this->ID );
860
        }
861
        
862
        $this->refresh_item_ids();
863
        
864
        return $saved;
865
    }
866
    
867
    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...
868
        $default_args = array(
869
            'label'       => '',
870
            'amount'      => 0,
871
            'type'        => 'fee',
872
            'id'          => '',
873
            'no_tax'      => false,
874
            'item_id'     => 0,
875
        );
876
877
        $fee = wp_parse_args( $args, $default_args );
878
        
879
        if ( !empty( $fee['label'] ) ) {
880
            return false;
881
        }
882
        
883
        $fee['id']  = sanitize_title( $fee['label'] );
884
        
885
        $this->fees[]               = $fee;
886
        
887
        $added_fee               = $fee;
888
        $added_fee['action']     = 'add';
889
        $this->pending['fees'][] = $added_fee;
890
        reset( $this->fees );
891
892
        $this->increase_fees( $fee['amount'] );
893
        return true;
894
    }
895
896
    public function remove_fee( $key ) {
897
        $removed = false;
898
899
        if ( is_numeric( $key ) ) {
900
            $removed = $this->remove_fee_by( 'index', $key );
901
        }
902
903
        return $removed;
904
    }
905
906
    public function remove_fee_by( $key, $value, $global = false ) {
907
        $allowed_fee_keys = apply_filters( 'wpinv_fee_keys', array(
908
            'index', 'label', 'amount', 'type',
909
        ) );
910
911
        if ( ! in_array( $key, $allowed_fee_keys ) ) {
912
            return false;
913
        }
914
915
        $removed = false;
916
        if ( 'index' === $key && array_key_exists( $value, $this->fees ) ) {
917
            $removed_fee             = $this->fees[ $value ];
918
            $removed_fee['action']   = 'remove';
919
            $this->pending['fees'][] = $removed_fee;
920
921
            $this->decrease_fees( $removed_fee['amount'] );
922
923
            unset( $this->fees[ $value ] );
924
            $removed = true;
925
        } else if ( 'index' !== $key ) {
926
            foreach ( $this->fees as $index => $fee ) {
927
                if ( isset( $fee[ $key ] ) && $fee[ $key ] == $value ) {
928
                    $removed_fee             = $fee;
929
                    $removed_fee['action']   = 'remove';
930
                    $this->pending['fees'][] = $removed_fee;
931
932
                    $this->decrease_fees( $removed_fee['amount'] );
933
934
                    unset( $this->fees[ $index ] );
935
                    $removed = true;
936
937
                    if ( false === $global ) {
938
                        break;
939
                    }
940
                }
941
            }
942
        }
943
944
        if ( true === $removed ) {
945
            $this->fees = array_values( $this->fees );
946
        }
947
948
        return $removed;
949
    }
950
951
    
952
953
    public function add_note( $note = '', $customer_type = false, $added_by_user = false, $system = false ) {
954
        // Bail if no note specified
955
        if( !$note ) {
956
            return false;
957
        }
958
959
        if ( empty( $this->ID ) )
960
            return false;
961
        
962
        if ( ( ( is_user_logged_in() && current_user_can( 'manage_options' ) ) || $added_by_user ) && !$system ) {
963
            $user                 = get_user_by( 'id', get_current_user_id() );
964
            $comment_author       = $user->display_name;
965
            $comment_author_email = $user->user_email;
966
        } else {
967
            $comment_author       = __( 'System', 'invoicing' );
968
            $comment_author_email = strtolower( __( 'System', 'invoicing' ) ) . '@';
969
            $comment_author_email .= isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com';
970
            $comment_author_email = sanitize_email( $comment_author_email );
971
        }
972
973
        do_action( 'wpinv_pre_insert_invoice_note', $this->ID, $note, $customer_type );
974
975
        $note_id = wp_insert_comment( wp_filter_comment( array(
976
            'comment_post_ID'      => $this->ID,
977
            'comment_content'      => $note,
978
            'comment_agent'        => 'GeoDirectory',
979
            'user_id'              => is_admin() ? get_current_user_id() : 0,
980
            'comment_date'         => current_time( 'mysql' ),
981
            'comment_date_gmt'     => current_time( 'mysql', 1 ),
982
            'comment_approved'     => 1,
983
            'comment_parent'       => 0,
984
            'comment_author'       => $comment_author,
985
            'comment_author_IP'    => wpinv_get_ip(),
986
            'comment_author_url'   => '',
987
            'comment_author_email' => $comment_author_email,
988
            'comment_type'         => 'wpinv_note'
989
        ) ) );
990
991
        do_action( 'wpinv_insert_payment_note', $note_id, $this->ID, $note );
992
        
993
        if ( $customer_type ) {
994
            add_comment_meta( $note_id, '_wpi_customer_note', 1 );
995
996
            do_action( 'wpinv_new_customer_note', array( 'invoice_id' => $this->ID, 'user_note' => $note ) );
997
        }
998
999
        return $note_id;
1000
    }
1001
1002
    private function increase_subtotal( $amount = 0.00 ) {
1003
        $amount          = (float) $amount;
1004
        $this->subtotal += $amount;
1005
        $this->subtotal  = wpinv_round_amount( $this->subtotal );
1006
1007
        $this->recalculate_total();
1008
    }
1009
1010 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...
1011
        $amount          = (float) $amount;
1012
        $this->subtotal -= $amount;
1013
        $this->subtotal  = wpinv_round_amount( $this->subtotal );
1014
1015
        if ( $this->subtotal < 0 ) {
1016
            $this->subtotal = 0;
1017
        }
1018
1019
        $this->recalculate_total();
1020
    }
1021
1022
    private function increase_fees( $amount = 0.00 ) {
1023
        $amount            = (float)$amount;
1024
        $this->fees_total += $amount;
1025
        $this->fees_total  = wpinv_round_amount( $this->fees_total );
1026
1027
        $this->recalculate_total();
1028
    }
1029
1030 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...
1031
        $amount            = (float) $amount;
1032
        $this->fees_total -= $amount;
1033
        $this->fees_total  = wpinv_round_amount( $this->fees_total );
1034
1035
        if ( $this->fees_total < 0 ) {
1036
            $this->fees_total = 0;
1037
        }
1038
1039
        $this->recalculate_total();
1040
    }
1041
1042
    public function recalculate_total() {
1043
        global $wpi_nosave;
1044
        
1045
        $this->total = $this->subtotal + $this->tax + $this->fees_total;
1046
        $this->total = wpinv_round_amount( $this->total );
1047
        
1048
        do_action( 'wpinv_invoice_recalculate_total', $this, $wpi_nosave );
1049
    }
1050
    
1051
    public function increase_tax( $amount = 0.00 ) {
1052
        $amount       = (float) $amount;
1053
        $this->tax   += $amount;
1054
1055
        $this->recalculate_total();
1056
    }
1057
1058
    public function decrease_tax( $amount = 0.00 ) {
1059
        $amount     = (float) $amount;
1060
        $this->tax -= $amount;
1061
1062
        if ( $this->tax < 0 ) {
1063
            $this->tax = 0;
1064
        }
1065
1066
        $this->recalculate_total();
1067
    }
1068
1069
    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...
1070
        $old_status = ! empty( $this->old_status ) ? $this->old_status : get_post_status( $this->ID );
1071
        
1072
        if ( $old_status === $new_status && in_array( $new_status, array_keys( wpinv_get_invoice_statuses() ) ) ) {
1073
            return false; // Don't permit status changes that aren't changes
1074
        }
1075
1076
        $do_change = apply_filters( 'wpinv_should_update_invoice_status', true, $this->ID, $new_status, $old_status );
1077
        $updated = false;
1078
1079
        if ( $do_change ) {
1080
            do_action( 'wpinv_before_invoice_status_change', $this->ID, $new_status, $old_status );
1081
1082
            $update_post_data                   = array();
1083
            $update_post_data['ID']             = $this->ID;
1084
            $update_post_data['post_status']    = $new_status;
1085
            $update_post_data['edit_date']      = current_time( 'mysql', 0 );
1086
            $update_post_data['edit_date_gmt']  = current_time( 'mysql', 1 );
1087
            
1088
            $update_post_data = apply_filters( 'wpinv_update_invoice_status_fields', $update_post_data, $this->ID );
1089
1090
            $updated = wp_update_post( $update_post_data );     
1091
           
1092
            // Process any specific status functions
1093
            switch( $new_status ) {
1094
                case 'wpi-refunded':
1095
                    $this->process_refund();
1096
                    break;
1097
                case 'wpi-failed':
1098
                    $this->process_failure();
1099
                    break;
1100
                case 'wpi-pending':
1101
                    $this->process_pending();
1102
                    break;
1103
            }
1104
            
1105
            // Status was changed.
1106
            do_action( 'wpinv_status_' . $new_status, $this->ID, $old_status );
1107
            do_action( 'wpinv_status_' . $old_status . '_to_' . $new_status, $this->ID, $old_status );
1108
            do_action( 'wpinv_update_status', $this->ID, $new_status, $old_status );
1109
        }
1110
1111
        return $updated;
1112
    }
1113
1114
    public function refund() {
1115
        $this->old_status        = $this->status;
1116
        $this->status            = 'wpi-refunded';
1117
        $this->pending['status'] = $this->status;
1118
1119
        $this->save();
1120
    }
1121
1122
    public function update_meta( $meta_key = '', $meta_value = '', $prev_value = '' ) {
1123
        if ( empty( $meta_key ) ) {
1124
            return false;
1125
        }
1126
1127
        if ( $meta_key == 'key' || $meta_key == 'date' ) {
1128
            $current_meta = $this->get_meta();
1129
            $current_meta[ $meta_key ] = $meta_value;
1130
1131
            $meta_key     = '_wpinv_payment_meta';
1132
            $meta_value   = $current_meta;
1133
        }
1134
1135
        $meta_value = apply_filters( 'wpinv_update_payment_meta_' . $meta_key, $meta_value, $this->ID );
1136
        
1137
        if ( $meta_key == '_wpinv_completed_date' && !empty( $meta_value ) ) {
1138
            $args = array(
1139
                'ID'                => $this->ID,
1140
                'post_date'         => $meta_value,
1141
                'edit_date'         => true,
1142
                'post_date_gmt'     => get_gmt_from_date( $meta_value ),
1143
                'post_modified'     => $meta_value,
1144
                'post_modified_gmt' => get_gmt_from_date( $meta_value )
1145
            );
1146
            wp_update_post( $args );
1147
        }
1148
        
1149
        return update_post_meta( $this->ID, $meta_key, $meta_value, $prev_value );
1150
    }
1151
1152
    private function process_refund() {
1153
        $process_refund = true;
1154
1155
        // If the payment was not in publish, don't decrement stats as they were never incremented
1156
        if ( 'publish' != $this->old_status || 'wpi-refunded' != $this->status ) {
1157
            $process_refund = false;
1158
        }
1159
1160
        // Allow extensions to filter for their own payment types, Example: Recurring Payments
1161
        $process_refund = apply_filters( 'wpinv_should_process_refund', $process_refund, $this );
1162
1163
        if ( false === $process_refund ) {
1164
            return;
1165
        }
1166
1167
        do_action( 'wpinv_pre_refund_invoice', $this );
1168
        
1169
        $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...
1170
        $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...
1171
        $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...
1172
        
1173
        do_action( 'wpinv_post_refund_invoice', $this );
1174
    }
1175
1176
    private function process_failure() {
1177
        $discounts = $this->discounts;
1178
        if ( empty( $discounts ) ) {
1179
            return;
1180
        }
1181
1182
        if ( ! is_array( $discounts ) ) {
1183
            $discounts = array_map( 'trim', explode( ',', $discounts ) );
1184
        }
1185
1186
        foreach ( $discounts as $discount ) {
1187
            wpinv_decrease_discount_usage( $discount );
1188
        }
1189
    }
1190
    
1191
    private function process_pending() {
1192
        $process_pending = true;
1193
1194
        // If the payment was not in publish or revoked status, don't decrement stats as they were never incremented
1195
        if ( ( 'publish' != $this->old_status && 'revoked' != $this->old_status ) || 'wpi-pending' != $this->status ) {
1196
            $process_pending = false;
1197
        }
1198
1199
        // Allow extensions to filter for their own payment types, Example: Recurring Payments
1200
        $process_pending = apply_filters( 'wpinv_should_process_pending', $process_pending, $this );
1201
1202
        if ( false === $process_pending ) {
1203
            return;
1204
        }
1205
1206
        $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...
1207
        $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...
1208
        $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...
1209
1210
        $this->completed_date = '';
1211
        $this->update_meta( '_wpinv_completed_date', '' );
1212
    }
1213
    
1214
    // get data
1215
    public function get_meta( $meta_key = '_wpinv_payment_meta', $single = true ) {
1216
        $meta = get_post_meta( $this->ID, $meta_key, $single );
1217
1218
        if ( $meta_key === '_wpinv_payment_meta' ) {
1219
1220
            if(!is_array($meta)){$meta = array();} // we need this to be an array so make sure it is.
1221
1222
            if ( empty( $meta['key'] ) ) {
1223
                $meta['key'] = $this->setup_invoice_key();
1224
            }
1225
1226
            if ( empty( $meta['date'] ) ) {
1227
                $meta['date'] = get_post_field( 'post_date', $this->ID );
1228
            }
1229
        }
1230
1231
        $meta = apply_filters( 'wpinv_get_invoice_meta_' . $meta_key, $meta, $this->ID );
1232
1233
        return apply_filters( 'wpinv_get_invoice_meta', $meta, $this->ID, $meta_key );
1234
    }
1235
    
1236
    public function get_description() {
1237
        $post = get_post( $this->ID );
1238
        
1239
        $description = !empty( $post ) ? $post->post_content : '';
1240
        return apply_filters( 'wpinv_get_description', $description, $this->ID, $this );
1241
    }
1242
    
1243
    public function get_status( $nicename = false ) {
1244
        if ( !$nicename ) {
1245
            $status = $this->status;
1246
        } else {
1247
            $status = $this->status_nicename;
1248
        }
1249
        
1250
        return apply_filters( 'wpinv_get_status', $status, $nicename, $this->ID, $this );
1251
    }
1252
    
1253
    public function get_cart_details() {
1254
        return apply_filters( 'wpinv_cart_details', $this->cart_details, $this->ID, $this );
1255
    }
1256
    
1257 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...
1258
        $subtotal = wpinv_round_amount( $this->subtotal );
1259
        
1260
        if ( $currency ) {
1261
            $subtotal = wpinv_price( wpinv_format_amount( $subtotal, NULL, !$currency ), $this->get_currency() );
1262
        }
1263
        
1264
        return apply_filters( 'wpinv_get_invoice_subtotal', $subtotal, $this->ID, $this, $currency );
1265
    }
1266
    
1267 View Code Duplication
    public function get_total( $currency = false ) {        
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1268
        if ( $this->is_free_trial() ) {
1269
            $total = wpinv_round_amount( 0 );
1270
        } else {
1271
            $total = wpinv_round_amount( $this->total );
1272
        }
1273
        if ( $currency ) {
1274
            $total = wpinv_price( wpinv_format_amount( $total, NULL, !$currency ), $this->get_currency() );
1275
        }
1276
        
1277
        return apply_filters( 'wpinv_get_invoice_total', $total, $this->ID, $this, $currency );
1278
    }
1279
    
1280
    public function get_recurring_details( $field = '', $currency = false ) {        
1281
        $data                 = array();
1282
        $data['cart_details'] = $this->cart_details;
1283
        $data['subtotal']     = $this->get_subtotal();
1284
        $data['discount']     = $this->get_discount();
1285
        $data['tax']          = $this->get_tax();
1286
        $data['total']        = $this->get_total();
1287
    
1288
        if ( !empty( $this->cart_details ) && ( $this->is_parent() || $this->is_renewal() ) ) {
1289
            $is_free_trial = $this->is_free_trial();
1290
            $discounts = $this->get_discounts( true );
1291
            
1292
            if ( $is_free_trial || !empty( $discounts ) ) {
1293
                $first_use_only = false;
1294
                
1295
                if ( !empty( $discounts ) ) {
1296
                    foreach ( $discounts as $key => $code ) {
1297
                        if ( wpinv_discount_is_recurring( $code, true ) ) {
1298
                            $first_use_only = true;
1299
                            break;
1300
                        }
1301
                    }
1302
                }
1303
                    
1304
                if ( !$first_use_only ) {
1305
                    $data['subtotal'] = wpinv_round_amount( $this->subtotal );
1306
                    $data['discount'] = wpinv_round_amount( $this->discount );
1307
                    $data['tax']      = wpinv_round_amount( $this->tax );
1308
                    $data['total']    = wpinv_round_amount( $this->total );
1309
                } else {
1310
                    $cart_subtotal   = 0;
1311
                    $cart_discount   = 0;
1312
                    $cart_tax        = 0;
1313
1314
                    foreach ( $this->cart_details as $key => $item ) {
1315
                        $item_quantity  = $item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
1316
                        $item_subtotal  = !empty( $item['subtotal'] ) ? $item['subtotal'] : $item['item_price'] * $item_quantity;
1317
                        $item_discount  = 0;
1318
                        $item_tax       = $item_subtotal > 0 && !empty( $item['vat_rate'] ) ? ( $item_subtotal * 0.01 * (float)$item['vat_rate'] ) : 0;
1319
                        
1320
                        if ( wpinv_prices_include_tax() ) {
1321
                            $item_subtotal -= wpinv_round_amount( $item_tax );
1322
                        }
1323
                        
1324
                        $item_total     = $item_subtotal - $item_discount + $item_tax;
1325
                        // Do not allow totals to go negative
1326
                        if ( $item_total < 0 ) {
1327
                            $item_total = 0;
1328
                        }
1329
                        
1330
                        $cart_subtotal  += (float)($item_subtotal);
1331
                        $cart_discount  += (float)($item_discount);
1332
                        $cart_tax       += (float)($item_tax);
1333
                        
1334
                        $data['cart_details'][$key]['discount']   = wpinv_round_amount( $item_discount );
1335
                        $data['cart_details'][$key]['tax']        = wpinv_round_amount( $item_tax );
1336
                        $data['cart_details'][$key]['price']      = wpinv_round_amount( $item_total );
1337
                    }
1338
                    
1339
                    $data['subtotal'] = wpinv_round_amount( $cart_subtotal );
1340
                    $data['discount'] = wpinv_round_amount( $cart_discount );
1341
                    $data['tax']      = wpinv_round_amount( $cart_tax );
1342
                    $data['total']    = wpinv_round_amount( $data['subtotal'] + $data['tax'] );
1343
                }
1344
            }
1345
        }
1346
        
1347
        $data = apply_filters( 'wpinv_get_invoice_recurring_details', $data, $this, $field, $currency );
1348
1349
        if ( isset( $data[$field] ) ) {
1350
            return ( $currency ? wpinv_price( $data[$field], $this->get_currency() ) : $data[$field] );
1351
        }
1352
        
1353
        return $data;
1354
    }
1355
    
1356
    public function get_final_tax( $currency = false ) {        
1357
        $final_total = wpinv_round_amount( $this->tax );
1358
        if ( $currency ) {
1359
            $final_total = wpinv_price( wpinv_format_amount( $final_total, NULL, !$currency ), $this->get_currency() );
1360
        }
1361
        
1362
        return apply_filters( 'wpinv_get_invoice_final_total', $final_total, $this, $currency );
1363
    }
1364
    
1365
    public function get_discounts( $array = false ) {
1366
        $discounts = $this->discounts;
1367
        if ( $array && $discounts ) {
1368
            $discounts = explode( ',', $discounts );
1369
        }
1370
        return apply_filters( 'wpinv_payment_discounts', $discounts, $this->ID, $this, $array );
1371
    }
1372
    
1373
    public function get_discount( $currency = false, $dash = false ) {
1374
        if ( !empty( $this->discounts ) ) {
1375
            global $ajax_cart_details;
1376
            $ajax_cart_details = $this->get_cart_details();
1377
            
1378
            if ( !empty( $ajax_cart_details ) && count( $ajax_cart_details ) == count( $this->items ) ) {
1379
                $cart_items = $ajax_cart_details;
1380
            } else {
1381
                $cart_items = $this->items;
1382
            }
1383
1384
            $this->discount = wpinv_get_cart_items_discount_amount( $cart_items , $this->discounts );
0 ignored issues
show
Documentation introduced by
$this->discounts is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1385
        }
1386
        $discount   = wpinv_round_amount( $this->discount );
1387
        $dash       = $dash && $discount > 0 ? '&ndash;' : '';
1388
        
1389
        if ( $currency ) {
1390
            $discount = wpinv_price( wpinv_format_amount( $discount, NULL, !$currency ), $this->get_currency() );
1391
        }
1392
        
1393
        $discount   = $dash . $discount;
1394
        
1395
        return apply_filters( 'wpinv_get_invoice_discount', $discount, $this->ID, $this, $currency, $dash );
1396
    }
1397
    
1398
    public function get_discount_code() {
1399
        return $this->discount_code;
1400
    }
1401
    
1402 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...
1403
        $tax = wpinv_round_amount( $this->tax );
1404
        
1405
        if ( $currency ) {
1406
            $tax = wpinv_price( wpinv_format_amount( $tax, NULL, !$currency ), $this->get_currency() );
1407
        }
1408
        
1409
        return apply_filters( 'wpinv_get_invoice_tax', $tax, $this->ID, $this, $currency );
1410
    }
1411
    
1412
    public function get_fees( $type = 'all' ) {
1413
        $fees    = array();
1414
1415
        if ( ! empty( $this->fees ) && is_array( $this->fees ) ) {
1416
            foreach ( $this->fees as $fee ) {
1417 View Code Duplication
                if( 'all' != $type && ! empty( $fee['type'] ) && $type != $fee['type'] ) {
1418
                    continue;
1419
                }
1420
1421
                $fee['label'] = stripslashes( $fee['label'] );
1422
                $fee['amount_display'] = wpinv_price( $fee['amount'], $this->get_currency() );
1423
                $fees[]    = $fee;
1424
            }
1425
        }
1426
1427
        return apply_filters( 'wpinv_get_invoice_fees', $fees, $this->ID, $this );
1428
    }
1429
    
1430
    public function get_fees_total( $type = 'all' ) {
1431
        $fees_total = (float) 0.00;
1432
1433
        $payment_fees = isset( $this->payment_meta['fees'] ) ? $this->payment_meta['fees'] : array();
1434
        if ( ! empty( $payment_fees ) ) {
1435
            foreach ( $payment_fees as $fee ) {
1436
                $fees_total += (float) $fee['amount'];
1437
            }
1438
        }
1439
1440
        return apply_filters( 'wpinv_get_invoice_fees_total', $fees_total, $this->ID, $this );
1441
        /*
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...
1442
        $fees = $this->get_fees( $type );
1443
1444
        $fees_total = 0;
1445
        if ( ! empty( $fees ) && is_array( $fees ) ) {
1446
            foreach ( $fees as $fee_id => $fee ) {
1447
                if( 'all' != $type && !empty( $fee['type'] ) && $type != $fee['type'] ) {
1448
                    continue;
1449
                }
1450
1451
                $fees_total += $fee['amount'];
1452
            }
1453
        }
1454
1455
        return apply_filters( 'wpinv_get_invoice_fees_total', $fees_total, $this->ID, $this );
1456
        */
1457
    }
1458
1459
    public function get_user_id() {
1460
        return apply_filters( 'wpinv_user_id', $this->user_id, $this->ID, $this );
1461
    }
1462
    
1463
    public function get_first_name() {
1464
        return apply_filters( 'wpinv_first_name', $this->first_name, $this->ID, $this );
1465
    }
1466
    
1467
    public function get_last_name() {
1468
        return apply_filters( 'wpinv_last_name', $this->last_name, $this->ID, $this );
1469
    }
1470
    
1471
    public function get_user_full_name() {
1472
        return apply_filters( 'wpinv_user_full_name', $this->full_name, $this->ID, $this );
1473
    }
1474
    
1475
    public function get_user_info() {
1476
        return apply_filters( 'wpinv_user_info', $this->user_info, $this->ID, $this );
1477
    }
1478
    
1479
    public function get_email() {
1480
        return apply_filters( 'wpinv_user_email', $this->email, $this->ID, $this );
1481
    }
1482
    
1483
    public function get_address() {
1484
        return apply_filters( 'wpinv_address', $this->address, $this->ID, $this );
1485
    }
1486
    
1487
    public function get_phone() {
1488
        return apply_filters( 'wpinv_phone', $this->phone, $this->ID, $this );
1489
    }
1490
    
1491
    public function get_number() {
1492
        return apply_filters( 'wpinv_number', $this->number, $this->ID, $this );
1493
    }
1494
    
1495
    public function get_items() {
1496
        return apply_filters( 'wpinv_payment_meta_items', $this->items, $this->ID, $this );
1497
    }
1498
    
1499
    public function get_key() {
1500
        return apply_filters( 'wpinv_key', $this->key, $this->ID, $this );
1501
    }
1502
    
1503
    public function get_transaction_id() {
1504
        return apply_filters( 'wpinv_get_invoice_transaction_id', $this->transaction_id, $this->ID, $this );
1505
    }
1506
    
1507
    public function get_gateway() {
1508
        return apply_filters( 'wpinv_gateway', $this->gateway, $this->ID, $this );
1509
    }
1510
    
1511
    public function get_gateway_title() {
1512
        $this->gateway_title = !empty( $this->gateway_title ) ? $this->gateway_title : wpinv_get_gateway_checkout_label( $this->gateway );
1513
        
1514
        return apply_filters( 'wpinv_gateway_title', $this->gateway_title, $this->ID, $this );
1515
    }
1516
    
1517
    public function get_currency() {
1518
        return apply_filters( 'wpinv_currency_code', $this->currency, $this->ID, $this );
1519
    }
1520
    
1521
    public function get_created_date() {
1522
        return apply_filters( 'wpinv_created_date', $this->date, $this->ID, $this );
1523
    }
1524
    
1525
    public function get_due_date( $display = false ) {
1526
        $due_date = apply_filters( 'wpinv_due_date', $this->due_date, $this->ID, $this );
1527
        
1528
        if ( !$display || empty( $due_date ) ) {
1529
            return $due_date;
1530
        }
1531
        
1532
        return date_i18n( get_option( 'date_format' ), strtotime( $due_date ) );
1533
    }
1534
    
1535
    public function get_completed_date() {
1536
        return apply_filters( 'wpinv_completed_date', $this->completed_date, $this->ID, $this );
1537
    }
1538
    
1539
    public function get_invoice_date( $formatted = true ) {
1540
        $date_completed = $this->completed_date;
1541
        $invoice_date   = $date_completed != '' && $date_completed != '0000-00-00 00:00:00' ? $date_completed : '';
1542
        
1543
        if ( $invoice_date == '' ) {
1544
            $date_created   = $this->date;
1545
            $invoice_date   = $date_created != '' && $date_created != '0000-00-00 00:00:00' ? $date_created : '';
1546
        }
1547
        
1548
        if ( $formatted && $invoice_date ) {
1549
            $invoice_date   = date_i18n( get_option( 'date_format' ), strtotime( $invoice_date ) );
1550
        }
1551
1552
        return apply_filters( 'wpinv_get_invoice_date', $invoice_date, $formatted, $this->ID, $this );
1553
    }
1554
    
1555
    public function get_ip() {
1556
        return apply_filters( 'wpinv_user_ip', $this->ip, $this->ID, $this );
1557
    }
1558
        
1559
    public function has_status( $status ) {
1560
        return apply_filters( 'wpinv_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
1561
    }
1562
    
1563
    public function add_item( $item_id = 0, $args = array() ) {
1564
        global $wpi_current_id, $wpi_item_id;
1565
        
1566
        $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...
1567
1568
        // Bail if this post isn't a item
1569
        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...
1570
            return false;
1571
        }
1572
        
1573
        $has_quantities = wpinv_item_quantities_enabled();
1574
1575
        // Set some defaults
1576
        $defaults = array(
1577
            'quantity'      => 1,
1578
            'id'            => false,
1579
            'name'          => $item->get_name(),
1580
            'item_price'    => false,
1581
            'custom_price'  => '',
1582
            'discount'      => 0,
1583
            'tax'           => 0.00,
1584
            'meta'          => array(),
1585
            'fees'          => array()
1586
        );
1587
1588
        $args = wp_parse_args( apply_filters( 'wpinv_add_item_args', $args, $item->ID ), $defaults );
1589
        $args['quantity']   = $has_quantities && $args['quantity'] > 0 ? absint( $args['quantity'] ) : 1;
1590
1591
        $wpi_current_id         = $this->ID;
1592
        $wpi_item_id            = $item->ID;
1593
        $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...
1594
        
1595
        $_POST['wpinv_country'] = $this->country;
1596
        $_POST['wpinv_state']   = $this->state;
1597
        
1598
        $found_cart_key         = false;
1599
        
1600
        if ($has_quantities) {
1601
            $this->cart_details = !empty( $this->cart_details ) ? array_values( $this->cart_details ) : $this->cart_details;
1602
            
1603
            foreach ( $this->items as $key => $cart_item ) {
1604
                if ( (int)$item_id !== (int)$cart_item['id'] ) {
1605
                    continue;
1606
                }
1607
1608
                $this->items[ $key ]['quantity'] += $args['quantity'];
1609
                break;
1610
            }
1611
            
1612
            foreach ( $this->cart_details as $cart_key => $cart_item ) {
1613
                if ( $item_id != $cart_item['id'] ) {
1614
                    continue;
1615
                }
1616
1617
                $found_cart_key = $cart_key;
1618
                break;
1619
            }
1620
        }
1621
        
1622
        if ($has_quantities && $found_cart_key !== false) {
1623
            $cart_item          = $this->cart_details[$found_cart_key];
1624
            $item_price         = $cart_item['item_price'];
1625
            $quantity           = !empty( $cart_item['quantity'] ) ? $cart_item['quantity'] : 1;
1626
            $tax_rate           = !empty( $cart_item['vat_rate'] ) ? $cart_item['vat_rate'] : 0;
1627
            
1628
            $new_quantity       = $quantity + $args['quantity'];
1629
            $subtotal           = $item_price * $new_quantity;
1630
            
1631
            $args['quantity']   = $new_quantity;
1632
            $discount           = !empty( $args['discount'] ) ? $args['discount'] : 0;
1633
            $tax                = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1634
            
1635
            $discount_increased = $discount > 0 && $subtotal > 0 && $discount > (float)$cart_item['discount'] ? $discount - (float)$cart_item['discount'] : 0;
1636
            $tax_increased      = $tax > 0 && $subtotal > 0 && $tax > (float)$cart_item['tax'] ? $tax - (float)$cart_item['tax'] : 0;
1637
            // The total increase equals the number removed * the item_price
1638
            $total_increased    = wpinv_round_amount( $item_price );
1639
            
1640
            if ( wpinv_prices_include_tax() ) {
1641
                $subtotal -= wpinv_round_amount( $tax );
1642
            }
1643
1644
            $total              = $subtotal - $discount + $tax;
1645
1646
            // Do not allow totals to go negative
1647
            if( $total < 0 ) {
1648
                $total = 0;
1649
            }
1650
            
1651
            $cart_item['quantity']  = $new_quantity;
1652
            $cart_item['subtotal']  = $subtotal;
1653
            $cart_item['discount']  = $discount;
1654
            $cart_item['tax']       = $tax;
1655
            $cart_item['price']     = $total;
1656
            
1657
            $subtotal               = $total_increased - $discount_increased;
1658
            $tax                    = $tax_increased;
1659
            
1660
            $this->cart_details[$found_cart_key] = $cart_item;
1661
        } else {
1662
            // Set custom price.
1663
            if ( $args['custom_price'] !== '' ) {
1664
                $item_price = $args['custom_price'];
1665
            } else {
1666
                // Allow overriding the price
1667
                if ( false !== $args['item_price'] ) {
1668
                    $item_price = $args['item_price'];
1669
                } else {
1670
                    $item_price = wpinv_get_item_price( $item->ID );
1671
                }
1672
            }
1673
1674
            // Sanitizing the price here so we don't have a dozen calls later
1675
            $item_price = wpinv_sanitize_amount( $item_price );
1676
            $subtotal   = wpinv_round_amount( $item_price * $args['quantity'] );
1677
        
1678
            $discount   = !empty( $args['discount'] ) ? $args['discount'] : 0;
1679
            $tax_class  = !empty( $args['vat_class'] ) ? $args['vat_class'] : '';
1680
            $tax_rate   = !empty( $args['vat_rate'] ) ? $args['vat_rate'] : 0;
1681
            $tax        = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1682
1683
            // Setup the items meta item
1684
            $new_item = array(
1685
                'id'       => $item->ID,
1686
                'quantity' => $args['quantity'],
1687
            );
1688
1689
            $this->items[]  = $new_item;
1690
1691
            if ( wpinv_prices_include_tax() ) {
1692
                $subtotal -= wpinv_round_amount( $tax );
1693
            }
1694
1695
            $total      = $subtotal - $discount + $tax;
1696
1697
            // Do not allow totals to go negative
1698
            if( $total < 0 ) {
1699
                $total = 0;
1700
            }
1701
        
1702
            $this->cart_details[] = array(
1703
                'name'          => !empty($args['name']) ? $args['name'] : $item->get_name(),
1704
                'id'            => $item->ID,
1705
                'item_price'    => wpinv_round_amount( $item_price ),
1706
                'custom_price'  => ( $args['custom_price'] !== '' ? wpinv_round_amount( $args['custom_price'] ) : '' ),
1707
                'quantity'      => $args['quantity'],
1708
                'discount'      => $discount,
1709
                'subtotal'      => wpinv_round_amount( $subtotal ),
1710
                'tax'           => wpinv_round_amount( $tax ),
1711
                'price'         => wpinv_round_amount( $total ),
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
            'custom_price'  => '',
1738
            'cart_index'    => false,
1739
        );
1740
        $args = wp_parse_args( $args, $defaults );
1741
1742
        // Bail if this post isn't a item
1743
        if ( get_post_type( $item_id ) !== 'wpi_item' ) {
1744
            return false;
1745
        }
1746
        
1747
        $this->cart_details = !empty( $this->cart_details ) ? array_values( $this->cart_details ) : $this->cart_details;
1748
1749
        foreach ( $this->items as $key => $item ) {
1750
            if ( !empty($item['id']) && (int)$item_id !== (int)$item['id'] ) {
1751
                continue;
1752
            }
1753
1754
            if ( false !== $args['cart_index'] ) {
1755
                $cart_index = absint( $args['cart_index'] );
1756
                $cart_item  = ! empty( $this->cart_details[ $cart_index ] ) ? $this->cart_details[ $cart_index ] : false;
1757
1758
                if ( ! empty( $cart_item ) ) {
1759
                    // If the cart index item isn't the same item ID, don't remove it
1760
                    if ( !empty($cart_item['id']) && $cart_item['id'] != $item['id'] ) {
1761
                        continue;
1762
                    }
1763
                }
1764
            }
1765
1766
            $item_quantity = $this->items[ $key ]['quantity'];
1767
            if ( $item_quantity > $args['quantity'] ) {
1768
                $this->items[ $key ]['quantity'] -= $args['quantity'];
1769
                break;
1770
            } else {
1771
                unset( $this->items[ $key ] );
1772
                break;
1773
            }
1774
        }
1775
1776
        $found_cart_key = false;
1777
        if ( false === $args['cart_index'] ) {
1778
            foreach ( $this->cart_details as $cart_key => $item ) {
1779
                if ( $item_id != $item['id'] ) {
1780
                    continue;
1781
                }
1782
1783
                if ( false !== $args['item_price'] ) {
1784
                    if ( isset( $item['item_price'] ) && (float) $args['item_price'] != (float) $item['item_price'] ) {
1785
                        continue;
1786
                    }
1787
                }
1788
1789
                $found_cart_key = $cart_key;
1790
                break;
1791
            }
1792
        } else {
1793
            $cart_index = absint( $args['cart_index'] );
1794
1795
            if ( ! array_key_exists( $cart_index, $this->cart_details ) ) {
1796
                return false; // Invalid cart index passed.
1797
            }
1798
1799
            if ( (int) $this->cart_details[ $cart_index ]['id'] > 0 && (int) $this->cart_details[ $cart_index ]['id'] !== (int) $item_id ) {
1800
                return false; // We still need the proper Item ID to be sure.
1801
            }
1802
1803
            $found_cart_key = $cart_index;
1804
        }
1805
        
1806
        $cart_item  = $this->cart_details[$found_cart_key];
1807
        $quantity   = !empty( $cart_item['quantity'] ) ? $cart_item['quantity'] : 1;
1808
        
1809
        if ( count( $this->cart_details ) == 1 && ( $quantity - $args['quantity'] ) < 1 ) {
1810
            return false; // Invoice must contain at least one item.
1811
        }
1812
        
1813
        $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...
1814
        
1815
        if ( $quantity > $args['quantity'] ) {
1816
            $item_price         = $cart_item['item_price'];
1817
            $tax_rate           = !empty( $cart_item['vat_rate'] ) ? $cart_item['vat_rate'] : 0;
1818
            
1819
            $new_quantity       = max( $quantity - $args['quantity'], 1);
1820
            $subtotal           = $item_price * $new_quantity;
1821
            
1822
            $args['quantity']   = $new_quantity;
1823
            $discount           = !empty( $cart_item['discount'] ) ? $cart_item['discount'] : 0;
1824
            $tax                = $subtotal > 0 && $tax_rate > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1825
            
1826
            $discount_decrease  = (float)$cart_item['discount'] > 0 && $quantity > 0 ? wpinv_round_amount( ( (float)$cart_item['discount'] / $quantity ) ) : 0;
1827
            $discount_decrease  = $discount > 0 && $subtotal > 0 && (float)$cart_item['discount'] > $discount ? (float)$cart_item['discount'] - $discount : $discount_decrease; 
1828
            $tax_decrease       = (float)$cart_item['tax'] > 0 && $quantity > 0 ? wpinv_round_amount( ( (float)$cart_item['tax'] / $quantity ) ) : 0;
1829
            $tax_decrease       = $tax > 0 && $subtotal > 0 && (float)$cart_item['tax'] > $tax ? (float)$cart_item['tax'] - $tax : $tax_decrease;
1830
            
1831
            // The total increase equals the number removed * the item_price
1832
            $total_decrease     = wpinv_round_amount( $item_price );
1833
            
1834
            if ( wpinv_prices_include_tax() ) {
1835
                $subtotal -= wpinv_round_amount( $tax );
1836
            }
1837
1838
            $total              = $subtotal - $discount + $tax;
1839
1840
            // Do not allow totals to go negative
1841
            if( $total < 0 ) {
1842
                $total = 0;
1843
            }
1844
            
1845
            $cart_item['quantity']  = $new_quantity;
1846
            $cart_item['subtotal']  = $subtotal;
1847
            $cart_item['discount']  = $discount;
1848
            $cart_item['tax']       = $tax;
1849
            $cart_item['price']     = $total;
1850
            
1851
            $added_item             = $cart_item;
1852
            $added_item['id']       = $item_id;
1853
            $added_item['price']    = $total_decrease;
1854
            $added_item['quantity'] = $args['quantity'];
1855
            
1856
            $subtotal_decrease      = $total_decrease - $discount_decrease;
1857
            
1858
            $this->cart_details[$found_cart_key] = $cart_item;
1859
            
1860
            $remove_item = end( $this->cart_details );
1861
        } else {
1862
            $item_price     = $cart_item['item_price'];
1863
            $discount       = !empty( $cart_item['discount'] ) ? $cart_item['discount'] : 0;
1864
            $tax            = !empty( $cart_item['tax'] ) ? $cart_item['tax'] : 0;
1865
        
1866
            $subtotal_decrease  = ( $item_price * $quantity ) - $discount;
1867
            $tax_decrease       = $tax;
1868
1869
            unset( $this->cart_details[$found_cart_key] );
1870
            
1871
            $remove_item             = $args;
1872
            $remove_item['id']       = $item_id;
1873
            $remove_item['price']    = $subtotal_decrease;
1874
            $remove_item['quantity'] = $args['quantity'];
1875
        }
1876
        
1877
        $remove_item['action']      = 'remove';
1878
        $this->pending['items'][]   = $remove_item;
1879
               
1880
        $this->decrease_subtotal( $subtotal_decrease );
1881
        $this->decrease_tax( $tax_decrease );
1882
        
1883
        return true;
1884
    }
1885
    
1886
    public function update_items($temp = false) {
1887
        global $wpinv_euvat, $wpi_current_id, $wpi_item_id, $wpi_nosave;
1888
        
1889
        if ( !empty( $this->cart_details ) ) {
1890
            $wpi_nosave             = $temp;
1891
            $cart_subtotal          = 0;
1892
            $cart_discount          = 0;
1893
            $cart_tax               = 0;
1894
            $cart_details           = array();
1895
            
1896
            $_POST['wpinv_country'] = $this->country;
1897
            $_POST['wpinv_state']   = $this->state;
1898
            
1899
            foreach ( $this->cart_details as $key => $item ) {
1900
                $item_price = $item['item_price'];
1901
                $quantity   = wpinv_item_quantities_enabled() && $item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
1902
                $amount     = wpinv_round_amount( $item_price * $quantity );
0 ignored issues
show
Unused Code introduced by
$amount is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1903
                $subtotal   = $item_price * $quantity;
1904
                
1905
                $wpi_current_id         = $this->ID;
1906
                $wpi_item_id            = $item['id'];
1907
                
1908
                $discount   = wpinv_get_cart_item_discount_amount( $item, $this->get_discounts() );
1909
                
1910
                $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...
1911
                $tax_class  = $wpinv_euvat->get_item_class( $wpi_item_id );
1912
                $tax        = $item_price > 0 ? ( ( $subtotal - $discount ) * 0.01 * (float)$tax_rate ) : 0;
1913
1914
                if ( wpinv_prices_include_tax() ) {
1915
                    $subtotal -= wpinv_round_amount( $tax );
1916
                }
1917
1918
                $total      = $subtotal - $discount + $tax;
1919
1920
                // Do not allow totals to go negative
1921
                if( $total < 0 ) {
1922
                    $total = 0;
1923
                }
1924
1925
                $cart_details[] = array(
1926
                    'id'          => $item['id'],
1927
                    'name'        => $item['name'],
1928
                    'item_price'  => wpinv_round_amount( $item_price ),
1929
                    'custom_price'=> ( isset( $item['custom_price'] ) ? $item['custom_price'] : '' ),
1930
                    'quantity'    => $quantity,
1931
                    'discount'    => $discount,
1932
                    'subtotal'    => wpinv_round_amount( $subtotal ),
1933
                    'tax'         => wpinv_round_amount( $tax ),
1934
                    'price'       => wpinv_round_amount( $total ),
1935
                    'vat_rate'    => $tax_rate,
1936
                    'vat_class'   => $tax_class,
1937
                    'meta'        => isset($item['meta']) ? $item['meta'] : array(),
1938
                    'fees'        => isset($item['fees']) ? $item['fees'] : array(),
1939
                );
1940
                
1941
                $cart_subtotal  += (float)($subtotal - $discount); // TODO
1942
                $cart_discount  += (float)($discount);
1943
                $cart_tax       += (float)($tax);
1944
            }
1945
            $this->subtotal = wpinv_round_amount( $cart_subtotal );
1946
            $this->tax      = wpinv_round_amount( $cart_tax );
1947
            $this->discount = wpinv_round_amount( $cart_discount );
1948
            
1949
            $this->recalculate_total();
1950
            
1951
            $this->cart_details = $cart_details;
1952
        }
1953
1954
        return $this;
1955
    }
1956
    
1957
    public function recalculate_totals($temp = false) {        
1958
        $this->update_items($temp);
1959
        $this->save( true );
1960
        
1961
        return $this;
1962
    }
1963
    
1964
    public function needs_payment() {
1965
        $valid_invoice_statuses = apply_filters( 'wpinv_valid_invoice_statuses_for_payment', array( 'wpi-pending' ), $this );
1966
1967
        if ( $this->has_status( $valid_invoice_statuses ) && ( $this->get_total() > 0 || $this->is_free_trial() || $this->is_free() ) ) {
1968
            $needs_payment = true;
1969
        } else {
1970
            $needs_payment = false;
1971
        }
1972
1973
        return apply_filters( 'wpinv_needs_payment', $needs_payment, $this, $valid_invoice_statuses );
1974
    }
1975
    
1976
    public function get_checkout_payment_url( $with_key = false, $secret = false ) {
1977
        $pay_url = wpinv_get_checkout_uri();
1978
1979
        if ( is_ssl() ) {
1980
            $pay_url = str_replace( 'http:', 'https:', $pay_url );
1981
        }
1982
        
1983
        $key = $this->get_key();
1984
1985
        if ( $with_key ) {
1986
            $pay_url = add_query_arg( 'invoice_key', $key, $pay_url );
1987
        } else {
1988
            $pay_url = add_query_arg( array( 'wpi_action' => 'pay_for_invoice', 'invoice_key' => $key ), $pay_url );
1989
        }
1990
        
1991 View Code Duplication
        if ( $secret ) {
1992
            $pay_url = add_query_arg( array( '_wpipay' => md5( $this->get_user_id() . '::' . $this->get_email() . '::' . $key ) ), $pay_url );
1993
        }
1994
1995
        return apply_filters( 'wpinv_get_checkout_payment_url', $pay_url, $this, $with_key, $secret );
1996
    }
1997
    
1998
    public function get_view_url( $secret = false, $with_key = false ) {
1999
        $print_url = get_permalink( $this->ID );
2000
        
2001 View Code Duplication
        if ( $secret ) {
2002
            $print_url = add_query_arg( array( '_wpipay' => md5( $this->get_user_id() . '::' . $this->get_email() . '::' . $this->get_key() ) ), $print_url );
2003
        }
2004
        
2005
        if ( $with_key ) {
2006
            $print_url = add_query_arg( 'invoice_key', $this->get_key(), $print_url );
2007
        }
2008
2009
        return apply_filters( 'wpinv_get_view_url', $print_url, $this, $secret, $with_key );
2010
    }
2011
    
2012
    public function generate_key( $string = '' ) {
2013
        $auth_key  = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
2014
        return strtolower( md5( $string . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'wpinv', true ) ) );  // Unique key
2015
    }
2016
    
2017
    public function is_recurring() {
2018
        if ( empty( $this->cart_details ) ) {
2019
            return false;
2020
        }
2021
        
2022
        $has_subscription = false;
2023 View Code Duplication
        foreach( $this->cart_details as $cart_item ) {
2024
            if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
2025
                $has_subscription = true;
2026
                break;
2027
            }
2028
        }
2029
        
2030
        if ( count( $this->cart_details ) > 1 ) {
2031
            $has_subscription = false;
2032
        }
2033
2034
        return apply_filters( 'wpinv_invoice_has_recurring_item', $has_subscription, $this->cart_details );
2035
    }
2036
    
2037
    public function is_free_trial() {
2038
        $is_free_trial = false;
2039
        
2040
        if ( $this->is_parent() && $item = $this->get_recurring( true ) ) {
2041
            if ( !empty( $item ) && $item->has_free_trial() ) {
2042
                $is_free_trial = true;
2043
            }
2044
        }
2045
2046
        return apply_filters( 'wpinv_invoice_is_free_trial', $is_free_trial, $this->cart_details );
2047
    }
2048
    
2049
    public function get_recurring( $object = false ) {
2050
        $item = NULL;
2051
        
2052
        if ( empty( $this->cart_details ) ) {
2053
            return $item;
2054
        }
2055
        
2056 View Code Duplication
        foreach( $this->cart_details as $cart_item ) {
2057
            if ( !empty( $cart_item['id'] ) && wpinv_is_recurring_item( $cart_item['id'] )  ) {
2058
                $item = $cart_item['id'];
2059
                break;
2060
            }
2061
        }
2062
        
2063
        if ( $object ) {
2064
            $item = $item ? new WPInv_Item( $item ) : NULL;
2065
            
2066
            apply_filters( 'wpinv_invoice_get_recurring_item', $item, $this );
2067
        }
2068
2069
        return apply_filters( 'wpinv_invoice_get_recurring_item_id', $item, $this );
2070
    }
2071
    
2072
    public function get_subscription_name() {
2073
        $item = $this->get_recurring( true );
2074
        
2075
        if ( empty( $item ) ) {
2076
            return NULL;
2077
        }
2078
        
2079
        if ( !($name = $item->get_name()) ) {
2080
            $name = $item->post_name;
2081
        }
2082
2083
        return apply_filters( 'wpinv_invoice_get_subscription_name', $name, $this );
2084
    }
2085
        
2086
    public function get_expiration() {
2087
        $expiration = $this->get_meta( '_wpinv_subscr_expiration', true );
2088
        return $expiration;
2089
    }
2090
    
2091
    public function get_cancelled_date( $formatted = true ) {
2092
        $cancelled_date = $this->get_subscription_status() == 'cancelled' ? $this->get_meta( '_wpinv_subscr_cancelled_on', true ) : '';
2093
        
2094
        if ( $formatted && $cancelled_date ) {
2095
            $cancelled_date = date_i18n( get_option( 'date_format' ), strtotime( $cancelled_date ) );
2096
        }
2097
        
2098
        return $cancelled_date;
2099
    }
2100
    
2101
    public function get_trial_end_date( $formatted = true ) {
2102
        if ( !$this->is_free_trial() || !$this->is_paid() ) {
2103
            return NULL;
2104
        }
2105
        
2106
        $trial_end_date = $this->get_subscription_status() == 'trialing' ? $this->get_meta( '_wpinv_subscr_trial_end', true ) : '';
2107
        
2108
        if ( empty( $trial_end_date ) ) {
2109
            $trial_start_time = strtotime( $this->get_subscription_start() );
2110
            $trial_start_time += ( wpinv_period_in_days( $this->get_subscription_trial_interval(), $this->get_subscription_trial_period() ) * DAY_IN_SECONDS ) ;
2111
            
2112
            $trial_end_date = date_i18n( 'Y-m-d H:i:s', $trial_start_time );
2113
        }
2114
        
2115
        if ( $formatted && $trial_end_date ) {
2116
            $trial_end_date = date_i18n( get_option( 'date_format' ), strtotime( $trial_end_date ) );
2117
        }
2118
        
2119
        return $trial_end_date;
2120
    }
2121
    
2122
    public function get_subscription_created( $default = true ) {
2123
        $created = $this->get_meta( '_wpinv_subscr_created', true );
2124
        
2125
        if ( empty( $created ) && $default ) {
2126
            $created = $this->date;
2127
        }
2128
        return $created;
2129
    }
2130
    
2131
    public function get_subscription_start( $formatted = true ) {
2132
        if ( !$this->is_paid() ) {
2133
            return '-';
2134
        }
2135
        $start   = $this->get_subscription_created();
2136
        
2137
        if ( $formatted ) {
2138
            $date = date_i18n( get_option( 'date_format' ), strtotime( $start ) );
2139
        } else {
2140
            $date = date_i18n( 'Y-m-d H:i:s', strtotime( $start ) );
2141
        }
2142
2143
        return $date;
2144
    }
2145
    
2146
    public function get_subscription_end( $formatted = true ) {
2147
        if ( !$this->is_paid() ) {
2148
            return '-';
2149
        }
2150
        $start          = $this->get_subscription_created();
2151
        $interval       = $this->get_subscription_interval();
2152
        $period         = $this->get_subscription_period( true );
2153
        $bill_times     = (int)$this->get_bill_times();
2154
        
2155
        if ( $bill_times == 0 ) {
2156
            return $formatted ? __( 'Until cancelled', 'invoicing' ) : $bill_times;
2157
        }
2158
        
2159
        $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...
2160
        
2161
        $end_time = strtotime( $start . '+' . ( $interval * $bill_times ) . ' ' . $period );
2162
        
2163
        if ( $this->is_free_trial() ) {
2164
            $end_time += ( wpinv_period_in_days( $this->get_subscription_trial_interval(), $this->get_subscription_trial_period() ) * DAY_IN_SECONDS ) ;
2165
        }
2166
        
2167
        if ( $formatted ) {
2168
            $date = date_i18n( get_option( 'date_format' ), $end_time );
2169
        } else {
2170
            $date = date_i18n( 'Y-m-d H:i:s', $end_time );
2171
        }
2172
2173
        return $date;
2174
    }
2175
    
2176
    public function get_expiration_time() {
2177
        return strtotime( $this->get_expiration(), current_time( 'timestamp' ) );
2178
    }
2179
    
2180
    public function get_original_invoice_id() {        
2181
        return $this->parent_invoice;
2182
    }
2183
    
2184
    public function get_bill_times() {
2185
        $subscription_data = $this->get_subscription_data();
2186
        return $subscription_data['bill_times'];
2187
    }
2188
2189
    public function get_child_payments( $self = false ) {
2190
        $invoices = get_posts( array(
2191
            'post_type'         => $this->post_type,
2192
            'post_parent'       => (int)$this->ID,
2193
            'posts_per_page'    => '999',
2194
            'post_status'       => array( 'publish', 'wpi-processing', 'wpi-renewal' ),
2195
            'orderby'           => 'ID',
2196
            'order'             => 'DESC',
2197
            'fields'            => 'ids'
2198
        ) );
2199
        
2200
        if ( $this->is_free_trial() ) {
2201
            $self = false;
2202
        }
2203
        
2204
        if ( $self && $this->is_paid() ) {
2205
            if ( !empty( $invoices ) ) {
2206
                $invoices[] = (int)$this->ID;
2207
            } else {
2208
                $invoices = array( $this->ID );
2209
            }
2210
            
2211
            $invoices = array_unique( $invoices );
2212
        }
2213
2214
        return $invoices;
2215
    }
2216
2217
    public function get_total_payments( $self = true ) {
2218
        return count( $this->get_child_payments( $self ) );
2219
    }
2220
    
2221
    public function get_subscriptions( $limit = -1 ) {
2222
        $subscriptions = wpinv_get_subscriptions( array( 'parent_invoice_id' => $this->ID, 'numberposts' => $limit ) );
2223
2224
        return $subscriptions;
2225
    }
2226
    
2227
    public function get_subscription_id() {
2228
        $subscription_id = $this->get_meta( '_wpinv_subscr_profile_id', true );
2229
        
2230
        if ( empty( $subscription_id ) && !empty( $this->parent_invoice ) ) {
2231
            $parent_invoice = wpinv_get_invoice( $this->parent_invoice );
2232
            
2233
            $subscription_id = $parent_invoice->get_meta( '_wpinv_subscr_profile_id', true );
2234
        }
2235
        
2236
        return $subscription_id;
2237
    }
2238
    
2239
    public function get_subscription_status() {
2240
        $subscription_status = $this->get_meta( '_wpinv_subscr_status', true );
2241
2242
        if ( empty( $subscription_status ) ) {
2243
            $status = 'pending';
2244
            
2245
            if ( $this->is_paid() ) {        
2246
                $bill_times   = (int)$this->get_bill_times();
2247
                $times_billed = (int)$this->get_total_payments();
2248
                $expiration = $this->get_subscription_end( false );
2249
                $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;
2250
                
2251
                if ( (int)$bill_times == 0 ) {
2252
                    $status = $expired ? 'expired' : 'active';
2253
                } else if ( $bill_times > 0 && $times_billed >= $bill_times ) {
2254
                    $status = 'completed';
2255
                } else if ( $expired ) {
2256
                    $status = 'expired';
2257
                } else if ( $bill_times > 0 ) {
2258
                    $status = 'active';
2259
                } else {
2260
                    $status = 'pending';
2261
                }
2262
            }
2263
            
2264
            if ( $status && $status != $subscription_status ) {
2265
                $subscription_status = $status;
2266
                
2267
                $this->update_meta( '_wpinv_subscr_status', $status );
2268
            }
2269
        }
2270
        
2271
        return $subscription_status;
2272
    }
2273
    
2274
    public function get_subscription_status_label( $status = '' ) {
2275
        $status = !empty( $status ) ? $status : $this->get_subscription_status();
2276
2277
        switch( $status ) {
2278
            case 'active' :
2279
                $status_label = __( 'Active', 'invoicing' );
2280
                break;
2281
2282
            case 'cancelled' :
2283
                $status_label = __( 'Cancelled', 'invoicing' );
2284
                break;
2285
                
2286
            case 'completed' :
2287
                $status_label = __( 'Completed', 'invoicing' );
2288
                break;
2289
2290
            case 'expired' :
2291
                $status_label = __( 'Expired', 'invoicing' );
2292
                break;
2293
2294
            case 'pending' :
2295
                $status_label = __( 'Pending', 'invoicing' );
2296
                break;
2297
2298
            case 'failing' :
2299
                $status_label = __( 'Failing', 'invoicing' );
2300
                break;
2301
                
2302
            case 'stopped' :
2303
                $status_label = __( 'Stopped', 'invoicing' );
2304
                break;
2305
                
2306
            case 'trialing' :
2307
                $status_label = __( 'Trialing', 'invoicing' );
2308
                break;
2309
2310
            default:
2311
                $status_label = $status;
2312
                break;
2313
        }
2314
2315
        return $status_label;
2316
    }
2317
    
2318 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...
2319
        $period = $this->get_meta( '_wpinv_subscr_period', true );
2320
        
2321
        // Fix period for old invoices
2322
        if ( $period == 'day' ) {
2323
            $period = 'D';
2324
        } else if ( $period == 'week' ) {
2325
            $period = 'W';
2326
        } else if ( $period == 'month' ) {
2327
            $period = 'M';
2328
        } else if ( $period == 'year' ) {
2329
            $period = 'Y';
2330
        }
2331
        
2332
        if ( !in_array( $period, array( 'D', 'W', 'M', 'Y' ) ) ) {
2333
            $period = 'D';
2334
        }
2335
        
2336
        if ( $full ) {
2337
            switch( $period ) {
2338
                case 'D':
2339
                    $period = 'day';
2340
                break;
2341
                case 'W':
2342
                    $period = 'week';
2343
                break;
2344
                case 'M':
2345
                    $period = 'month';
2346
                break;
2347
                case 'Y':
2348
                    $period = 'year';
2349
                break;
2350
            }
2351
        }
2352
        
2353
        return $period;
2354
    }
2355
    
2356
    public function get_subscription_interval() {
2357
        $interval = (int)$this->get_meta( '_wpinv_subscr_interval', true );
2358
        
2359
        if ( !$interval > 0 ) {
2360
            $interval = 1;
2361
        }
2362
        
2363
        return $interval;
2364
    }
2365
    
2366 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...
2367
        if ( !$this->is_free_trial() ) {
2368
            return '';
2369
        }
2370
        
2371
        $period = $this->get_meta( '_wpinv_subscr_trial_period', true );
2372
        
2373
        // Fix period for old invoices
2374
        if ( $period == 'day' ) {
2375
            $period = 'D';
2376
        } else if ( $period == 'week' ) {
2377
            $period = 'W';
2378
        } else if ( $period == 'month' ) {
2379
            $period = 'M';
2380
        } else if ( $period == 'year' ) {
2381
            $period = 'Y';
2382
        }
2383
        
2384
        if ( !in_array( $period, array( 'D', 'W', 'M', 'Y' ) ) ) {
2385
            $period = 'D';
2386
        }
2387
        
2388
        if ( $full ) {
2389
            switch( $period ) {
2390
                case 'D':
2391
                    $period = 'day';
2392
                break;
2393
                case 'W':
2394
                    $period = 'week';
2395
                break;
2396
                case 'M':
2397
                    $period = 'month';
2398
                break;
2399
                case 'Y':
2400
                    $period = 'year';
2401
                break;
2402
            }
2403
        }
2404
        
2405
        return $period;
2406
    }
2407
    
2408
    public function get_subscription_trial_interval() {
2409
        if ( !$this->is_free_trial() ) {
2410
            return 0;
2411
        }
2412
        
2413
        $interval = (int)$this->get_meta( '_wpinv_subscr_trial_interval', true );
2414
        
2415
        if ( !$interval > 0 ) {
2416
            $interval = 1;
2417
        }
2418
        
2419
        return $interval;
2420
    }
2421
    
2422 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...
2423
        $args = array(
2424
            'status' => 'failing'
2425
        );
2426
2427
        if ( $this->update_subscription( $args ) ) {
2428
            do_action( 'wpinv_subscription_failing', $this->ID, $this );
2429
            return true;
2430
        }
2431
2432
        return false;
2433
    }
2434
    
2435 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...
2436
        $args = array(
2437
            'status' => 'stopped'
2438
        );
2439
2440
        if ( $this->update_subscription( $args ) ) {
2441
            do_action( 'wpinv_subscription_stopped', $this->ID, $this );
2442
            return true;
2443
        }
2444
2445
        return false;
2446
    }
2447
    
2448 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...
2449
        $args = array(
2450
            'status' => 'active'
2451
        );
2452
2453
        if ( $this->update_subscription( $args ) ) {
2454
            do_action( 'wpinv_subscription_restarted', $this->ID, $this );
2455
            return true;
2456
        }
2457
2458
        return false;
2459
    }
2460
2461
    public function cancel_subscription() {
2462
        $args = array(
2463
            'status' => 'cancelled'
2464
        );
2465
2466
        if ( $this->update_subscription( $args ) ) {
2467
            if ( is_user_logged_in() ) {
2468
                $userdata = get_userdata( get_current_user_id() );
2469
                $user     = $userdata->user_login;
2470
            } else {
2471
                $user = __( 'gateway', 'invoicing' );
2472
            }
2473
            
2474
            $subscription_id = $this->get_subscription_id();
2475
            if ( !$subscription_id ) {
2476
                $subscription_id = $this->ID;
2477
            }
2478
2479
            $note = sprintf( __( 'Subscription %s has been cancelled by %s', 'invoicing' ), $subscription_id, $user );
2480
            $this->add_note( $note );
2481
2482
            do_action( 'wpinv_subscription_cancelled', $this->ID, $this );
2483
            return true;
2484
        }
2485
2486
        return false;
2487
    }
2488
2489
    public function can_cancel() {
2490
        return apply_filters( 'wpinv_subscription_can_cancel', false, $this );
2491
    }
2492
    
2493
    public function add_subscription( $data = array() ) {
2494
        if ( empty( $this->ID ) ) {
2495
            return false;
2496
        }
2497
2498
        $defaults = array(
2499
            'period'            => '',
2500
            'initial_amount'    => '',
2501
            'recurring_amount'  => '',
2502
            'interval'          => 0,
2503
            'trial_interval'    => 0,
2504
            'trial_period'      => '',
2505
            'bill_times'        => 0,
2506
            'item_id'           => 0,
2507
            'created'           => '',
2508
            'expiration'        => '',
2509
            'status'            => '',
2510
            'profile_id'        => '',
2511
        );
2512
2513
        $args = wp_parse_args( $data, $defaults );
2514
2515
        if ( $args['expiration'] && strtotime( 'NOW', current_time( 'timestamp' ) ) > strtotime( $args['expiration'], current_time( 'timestamp' ) ) ) {
2516
            if ( 'active' == $args['status'] || $args['status'] == 'trialing' ) {
2517
                $args['status'] = 'expired';
2518
            }
2519
        }
2520
2521
        do_action( 'wpinv_subscription_pre_create', $args, $data, $this );
2522
        
2523
        if ( !empty( $args ) ) {
2524
            foreach ( $args as $key => $value ) {
2525
                $this->update_meta( '_wpinv_subscr_' . $key, $value );
2526
            }
2527
        }
2528
2529
        do_action( 'wpinv_subscription_post_create', $args, $data, $this );
2530
2531
        return true;
2532
    }
2533
    
2534
    public function update_subscription( $args = array() ) {
2535
        if ( empty( $this->ID ) ) {
2536
            return false;
2537
        }
2538
2539
        if ( !empty( $args['expiration'] ) && $args['expiration'] && strtotime( 'NOW', current_time( 'timestamp' ) ) > strtotime( $args['expiration'], current_time( 'timestamp' ) ) ) {
2540
            if ( !isset( $args['status'] ) || ( isset( $args['status'] ) && ( 'active' == $args['status'] || $args['status'] == 'trialing' ) ) ) {
2541
                $args['status'] = 'expired';
2542
            }
2543
        }
2544
2545
        if ( isset( $args['status'] ) && $args['status'] == 'cancelled' && empty( $args['cancelled_on'] ) ) {
2546
            $args['cancelled_on'] = date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) );
2547
        }
2548
2549
        do_action( 'wpinv_subscription_pre_update', $args, $this );
2550
        
2551
        if ( !empty( $args ) ) {
2552
            foreach ( $args as $key => $value ) {
2553
                $this->update_meta( '_wpinv_subscr_' . $key, $value );
2554
            }
2555
        }
2556
2557
        do_action( 'wpinv_subscription_post_update', $args, $this );
2558
2559
        return true;
2560
    }
2561
    
2562
    public function renew_subscription() {
2563
        $parent_invoice = $this->get_parent_payment();
2564
        $parent_invoice = empty( $parent_invoice ) ? $this : $parent_invoice;
2565
        
2566
        $current_time   = current_time( 'timestamp' );
2567
        $start          = $this->get_subscription_created();
2568
        $start          = $start ? strtotime( $start ) : $current_time;
2569
        $expires        = $this->get_expiration_time();
2570
        
2571
        if ( !$expires ) {
2572
            $expires    = strtotime( '+' . $parent_invoice->get_subscription_interval() . ' ' . $parent_invoice->get_subscription_period( true ), $start );
2573
        }
2574
        
2575
        $expiration     = date_i18n( 'Y-m-d 23:59:59', $expires );
2576
        $expiration     = apply_filters( 'wpinv_subscription_renewal_expiration', $expiration, $this->ID, $this );
2577
        $bill_times     = $parent_invoice->get_bill_times();
2578
        $times_billed   = $parent_invoice->get_total_payments();
2579
        
2580
        if ( $parent_invoice->get_subscription_status() == 'trialing' && ( $times_billed > 0 || strtotime( date_i18n( 'Y-m-d' ) ) < strtotime( $parent_invoice->get_trial_end_date( false ) ) ) ) {
2581
            $args = array(
2582
                'status'     => 'active',
2583
            );
2584
2585
            $parent_invoice->update_subscription( $args );
2586
        }
2587
        
2588
        do_action( 'wpinv_subscription_pre_renew', $this->ID, $expiration, $this );
2589
2590
        $status       = 'active';
2591
        if ( $bill_times > 0 && $times_billed >= $bill_times ) {
2592
            $this->complete_subscription();
2593
            $status = 'completed';
2594
        }
2595
2596
        $args = array(
2597
            'expiration' => $expiration,
2598
            'status'     => $status,
2599
        );
2600
2601
        $this->update_subscription( $args );
2602
2603
        do_action( 'wpinv_subscription_post_renew', $this->ID, $expiration, $this );
2604
        do_action( 'wpinv_recurring_set_subscription_status', $this->ID, $status, $this );
2605
    }
2606
    
2607
    public function complete_subscription() {
2608
        $args = array(
2609
            'status' => 'completed'
2610
        );
2611
2612
        if ( $this->update_subscription( $args ) ) {
2613
            do_action( 'wpinv_subscription_completed', $this->ID, $this );
2614
        }
2615
    }
2616
    
2617
    public function expire_subscription() {
2618
        $args = array(
2619
            'status' => 'expired'
2620
        );
2621
2622
        if ( $this->update_subscription( $args ) ) {
2623
            do_action( 'wpinv_subscription_expired', $this->ID, $this );
2624
        }
2625
    }
2626
2627
    public function get_cancel_url() {
2628
        $url = wp_nonce_url( add_query_arg( array( 'wpi_action' => 'cancel_subscription', 'sub_id' => $this->ID ) ), 'wpinv-recurring-cancel' );
2629
2630
        return apply_filters( 'wpinv_subscription_cancel_url', $url, $this );
2631
    }
2632
2633
    public function can_update() {
2634
        return apply_filters( 'wpinv_subscription_can_update', false, $this );
2635
    }
2636
2637
    public function get_update_url() {
2638
        $url = add_query_arg( array( 'action' => 'update', 'sub_id' => $this->ID ) );
2639
2640
        return apply_filters( 'wpinv_subscription_update_url', $url, $this );
2641
    }
2642
2643
    public function is_parent() {
2644
        $is_parent = empty( $this->parent_invoice ) ? true : false;
2645
2646
        return apply_filters( 'wpinv_invoice_is_parent', $is_parent, $this );
2647
    }
2648
    
2649
    public function is_renewal() {
2650
        $is_renewal = $this->parent_invoice && $this->parent_invoice != $this->ID ? true : false;
2651
2652
        return apply_filters( 'wpinv_invoice_is_renewal', $is_renewal, $this );
2653
    }
2654
    
2655
    public function get_parent_payment() {
2656
        $parent_payment = NULL;
2657
        
2658
        if ( $this->is_renewal() ) {
2659
            $parent_payment = wpinv_get_invoice( $this->parent_invoice );
2660
        }
2661
        
2662
        return $parent_payment;
2663
    }
2664
    
2665
    public function is_subscription_active() {
2666
        $ret = false;
2667
        
2668
        $subscription_status = $this->get_subscription_status();
2669
2670
        if( ! $this->is_subscription_expired() && ( $subscription_status == 'active' || $subscription_status == 'cancelled' || $subscription_status == 'trialing' ) ) {
2671
            $ret = true;
2672
        }
2673
2674
        return apply_filters( 'wpinv_subscription_is_active', $ret, $this->ID, $this );
2675
    }
2676
2677
    public function is_subscription_expired() {
2678
        $ret = false;
2679
        $subscription_status = $this->get_subscription_status();
2680
2681
        if ( $subscription_status == 'expired' ) {
2682
            $ret = true;
2683
        } else if ( 'active' === $subscription_status || 'cancelled' === $subscription_status || 'trialing' == $subscription_status ) {
2684
            $ret        = false;
2685
            $expiration = $this->get_expiration_time();
2686
2687
            if ( $expiration && strtotime( 'NOW', current_time( 'timestamp' ) ) > $expiration ) {
2688
                $ret = true;
2689
2690
                if ( 'active' === $subscription_status || 'trialing' === $subscription_status ) {
2691
                    $this->expire_subscription();
2692
                }
2693
            }
2694
        }
2695
2696
        return apply_filters( 'wpinv_subscription_is_expired', $ret, $this->ID, $this );
2697
    }
2698
    
2699
    public function get_new_expiration( $item_id = 0, $trial = true ) {
2700
        $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...
2701
        $interval = $item->get_recurring_interval();
2702
        $period = $item->get_recurring_period( true );
2703
        
2704
        $expiration_time = strtotime( '+' . $interval . ' ' . $period );
2705
        
2706
        if ( $trial && $this->is_free_trial() && $item->has_free_trial() ) {
2707
            $expiration_time += ( wpinv_period_in_days( $item->get_trial_interval(), $item->get_trial_period() ) * DAY_IN_SECONDS ) ;
2708
        }
2709
2710
        return date_i18n( 'Y-m-d 23:59:59', $expiration_time );
2711
    }
2712
    
2713
    public function get_subscription_data( $filed = '' ) {
2714
        $fields = array( 'item_id', 'status', 'period', 'initial_amount', 'recurring_amount', 'interval', 'bill_times', 'trial_period', 'trial_interval', 'expiration', 'profile_id', 'created', 'cancelled_on' );
2715
        
2716
        $subscription_meta = array();
2717
        foreach ( $fields as $field ) {
2718
            $subscription_meta[ $field ] = $this->get_meta( '_wpinv_subscr_' . $field );
2719
        }
2720
        
2721
        $item = $this->get_recurring( true );
2722
        
2723
        if ( !empty( $item ) ) {
2724
            if ( empty( $subscription_meta['item_id'] ) ) {
2725
                $subscription_meta['item_id'] = $item->ID;
2726
            }
2727
            if ( empty( $subscription_meta['period'] ) ) {
2728
                $subscription_meta['period'] = $item->get_recurring_period();
2729
            }
2730
            if ( empty( $subscription_meta['interval'] ) ) {
2731
                $subscription_meta['interval'] = $item->get_recurring_interval();
2732
            }
2733
            if ( $item->has_free_trial() ) {
2734
                if ( empty( $subscription_meta['trial_period'] ) ) {
2735
                    $subscription_meta['trial_period'] = $item->get_trial_period();
2736
                }
2737
                if ( empty( $subscription_meta['trial_interval'] ) ) {
2738
                    $subscription_meta['trial_interval'] = $item->get_trial_interval();
2739
                }
2740
            } else {
2741
                $subscription_meta['trial_period']      = '';
2742
                $subscription_meta['trial_interval']    = 0;
2743
            }
2744
            if ( !$subscription_meta['bill_times'] && $subscription_meta['bill_times'] !== 0 ) {
2745
                $subscription_meta['bill_times'] = $item->get_recurring_limit();
2746
            }
2747
            if ( $subscription_meta['initial_amount'] === '' || $subscription_meta['recurring_amount'] === '' ) {
2748
                $subscription_meta['initial_amount']    = wpinv_round_amount( $this->get_total() );
2749
                $subscription_meta['recurring_amount']  = wpinv_round_amount( $this->get_recurring_details( 'total' ) );
2750
            }
2751
        }
2752
        
2753
        if ( $filed === '' ) {
2754
            return apply_filters( 'wpinv_get_invoice_subscription_data', $subscription_meta, $this );
2755
        }
2756
        
2757
        $value = isset( $subscription_meta[$filed] ) ? $subscription_meta[$filed] : '';
2758
        
2759
        return apply_filters( 'wpinv_invoice_subscription_data_value', $value, $subscription_meta, $this );
2760
    }
2761
    
2762
    public function is_paid() {
2763
        if ( $this->has_status( array( 'publish', 'wpi-processing', 'wpi-renewal' ) ) ) {
2764
            return true;
2765
        }
2766
        
2767
        return false;
2768
    }
2769
    
2770
    public function is_refunded() {
2771
        $is_refunded = $this->has_status( array( 'wpi-refunded' ) );
2772
2773
        return apply_filters( 'wpinv_invoice_is_refunded', $is_refunded, $this );
2774
    }
2775
    
2776
    public function is_free() {
2777
        $is_free = false;
2778
        
2779
        if ( !( (float)wpinv_round_amount( $this->get_total() ) > 0 ) ) {
2780
            if ( $this->is_parent() && $this->is_recurring() ) {
2781
                $is_free = (float)wpinv_round_amount( $this->get_recurring_details( 'total' ) ) > 0 ? false : true;
2782
            } else {
2783
                $is_free = true;
2784
            }
2785
        }
2786
        
2787
        return apply_filters( 'wpinv_invoice_is_free', $is_free, $this );
2788
    }
2789
    
2790
    public function has_vat() {
2791
        global $wpinv_euvat, $wpi_country;
2792
        
2793
        $requires_vat = false;
2794
        
2795
        if ( $this->country ) {
2796
            $wpi_country        = $this->country;
2797
            
2798
            $requires_vat       = $wpinv_euvat->requires_vat( $requires_vat, $this->get_user_id(), $wpinv_euvat->invoice_has_digital_rule( $this ) );
2799
        }
2800
        
2801
        return apply_filters( 'wpinv_invoice_has_vat', $requires_vat, $this );
2802
    }
2803
    
2804
    public function refresh_item_ids() {
2805
        $item_ids = array();
2806
        
2807
        if ( !empty( $this->cart_details ) ) {
2808
            foreach ( $this->cart_details as $key => $item ) {
2809
                if ( !empty( $item['id'] ) ) {
2810
                    $item_ids[] = $item['id'];
2811
                }
2812
            }
2813
        }
2814
        
2815
        $item_ids = !empty( $item_ids ) ? implode( ',', array_unique( $item_ids ) ) : '';
2816
        
2817
        update_post_meta( $this->ID, '_wpinv_item_ids', $item_ids );
2818
    }
2819
    
2820
    public function get_invoice_quote_type( $post_id ) {
2821
        if ( empty( $post_id ) ) {
2822
            return '';
2823
        }
2824
2825
        $type = get_post_type( $post_id );
2826
2827
        if ( 'wpi_invoice' === $type ) {
2828
            $post_type = __('Invoice', 'invoicing');
2829
        } else{
2830
            $post_type = __('Quote', 'invoicing');
2831
        }
2832
2833
        return $post_type;
2834
    }
2835
}
2836