Passed
Push — master ( c463fc...5bb76a )
by Brian
11:07
created

getpaid_invoice_history()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 54
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 26
nc 12
nop 2
dl 0
loc 54
rs 8.5706
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Contains invoice functions.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
 
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Retrieves the current invoice.
13
 */
14
function getpaid_get_current_invoice_id() {
15
16
    // Ensure that we have an invoice key.
17
    if ( empty( $_GET['invoice_key'] ) ) {
18
        return 0;
19
    }
20
21
    // Retrieve an invoice using the key.
22
    $invoice = new WPInv_Invoice( $_GET['invoice_key'] );
23
24
    // Compare the invoice key and the parsed key.
25
    if ( $invoice->get_id() != 0 && $invoice->get_key() == $_GET['invoice_key'] ) {
26
        return $invoice->get_id();
27
    }
28
29
    return 0;
30
}
31
32
/**
33
 * Checks if the current user cna view an invoice.
34
 */
35
function wpinv_user_can_view_invoice( $invoice ) {
36
    $invoice = new WPInv_Invoice( $invoice );
37
38
    // Abort if the invoice does not exist.
39
    if ( 0 == $invoice->get_id() ) {
40
        return false;
41
    }
42
43
    // Don't allow trash, draft status
44
    if ( $invoice->is_draft() ) {
45
        return false;
46
    }
47
48
    // If users are not required to login to check out, compare the invoice keys.
49
    if ( ! wpinv_require_login_to_checkout() && isset( $_GET['invoice_key'] ) && trim( $_GET['invoice_key'] ) == $invoice->get_key() ) {
50
        return true;
51
    }
52
53
    // Always enable for admins..
54
    if ( wpinv_current_user_can_manage_invoicing() || current_user_can( 'view_invoices', $invoice->get_id() ) ) { // Admin user
55
        return true;
56
    }
57
58
    // Else, ensure that this is their invoice.
59
    if ( is_user_logged_in() && $invoice->get_user_id() == get_current_user_id() ) {
60
        return true;
61
    }
62
63
    return apply_filters( 'wpinv_current_user_can_view_invoice', false, $invoice );
64
}
65
66
/**
67
 * Checks if the current user cna view an invoice receipt.
68
 */
69
function wpinv_can_view_receipt( $invoice ) {
70
	return (bool) apply_filters( 'wpinv_can_view_receipt', wpinv_user_can_view_invoice( $invoice ), $invoice );
71
}
72
73
/**
74
 * Returns an array of all invoice post types.
75
 * 
76
 * @return array
77
 */
78
function getpaid_get_invoice_post_types() {
79
    $post_types = array(
80
        'wpi_quote'   => __( 'Quote', 'invoicing' ),
81
        'wpi_invoice' => __( 'Invoice', 'invoicing' ),
82
    );
83
84
    return apply_filters( 'getpaid_invoice_post_types', $post_types );
85
}
86
87
/**
88
 * Checks if this is an invocing post type.
89
 * 
90
 * 
91
 * @param string $post_type The post type to check for.
92
 */
93
function getpaid_is_invoice_post_type( $post_type ) {
94
    return ! empty( $post_type ) && array_key_exists( $post_type, getpaid_get_invoice_post_types() );
95
}
96
97
/**
98
 * Creates a new invoice.
99
 * 
100
 * @param  array $data   An array of invoice properties.
101
 * @param  bool  $wp_error       Whether to return false or WP_Error on failure.
102
 * @return int|WP_Error|WPInv_Invoice The value 0 or WP_Error on failure. The WPInv_Invoice object on success.
103
 */
104
function wpinv_create_invoice( $data = array(), $deprecated = null, $wp_error = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $deprecated is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

104
function wpinv_create_invoice( $data = array(), /** @scrutinizer ignore-unused */ $deprecated = null, $wp_error = false ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
105
    $data[ 'invoice_id' ] = 0;
106
    return wpinv_insert_invoice( $data, $wp_error );
107
}
108
109
/**
110
 * Updates an existing invoice.
111
 * 
112
 * @param  array $data   An array of invoice properties.
113
 * @param  bool  $wp_error       Whether to return false or WP_Error on failure.
114
 * @return int|WP_Error|WPInv_Invoice The value 0 or WP_Error on failure. The WPInv_Invoice object on success.
115
 */
116
function wpinv_update_invoice( $data = array(), $wp_error = false ) {
117
118
    // Backwards compatibility.
119
    if ( ! empty( $data['ID'] ) ) {
120
        $data['invoice_id'] = $data['ID'];
121
    }
122
123
    // Do we have an invoice id?
124
    if ( empty( $data['invoice_id'] ) ) {
125
        return $wp_error ? new WP_Error( 'invalid_invoice_id', __( 'Invalid invoice ID.', 'invoicing' ) ) : 0;
126
    }
127
128
    // Retrieve the invoice.
129
    $invoice = wpinv_get_invoice( $data['invoice_id'] );
130
131
    // And abort if it does not exist.
132
    if ( empty( $invoice ) ) {
133
        return $wp_error ? new WP_Error( 'missing_invoice', __( 'Invoice not found.', 'invoicing' ) ) : 0;
134
    }
135
136
    // Do not update totals for paid / refunded invoices.
137
    if ( $invoice->is_paid() || $invoice->is_refunded() ) {
138
139
        if ( ! empty( $data['items'] ) || ! empty( $data['cart_details'] ) ) {
140
            return $wp_error ? new WP_Error( 'paid_invoice', __( 'You can not update cart items for invoices that have already been paid for.', 'invoicing' ) ) : 0;
141
        }
142
143
    }
144
145
    return wpinv_insert_invoice( $data, $wp_error );
146
147
}
148
149
/**
150
 * Create/Update an invoice
151
 * 
152
 * @param  array $data   An array of invoice properties.
153
 * @param  bool  $wp_error       Whether to return false or WP_Error on failure.
154
 * @return int|WP_Error|WPInv_Invoice The value 0 or WP_Error on failure. The WPInv_Invoice object on success.
155
 */
156
function wpinv_insert_invoice( $data = array(), $wp_error = false ) {
157
158
    // Ensure that we have invoice data.
159
    if ( empty( $data ) ) {
160
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type WPInv_Invoice|WP_Error|integer.
Loading history...
161
    }
162
163
    // The invoice id will be provided when updating an invoice.
164
    $data['invoice_id'] = ! empty( $data['invoice_id'] ) ? (int) $data['invoice_id'] : false;
165
166
    // Retrieve the invoice.
167
    $invoice = new WPInv_Invoice( $data['invoice_id'] );
168
169
    // Do we have an error?
170
    if ( ! empty( $invoice->last_error ) ) {
171
        return $wp_error ? new WP_Error( 'invalid_invoice_id', $invoice->last_error ) : 0;
172
    }
173
174
    // Backwards compatibility (billing address).
175
    if ( ! empty( $data['user_info'] ) ) {
176
177
        foreach ( $data['user_info'] as $key => $value ) {
178
179
            if ( $key == 'discounts' ) {
180
                $value = (array) $value;
181
                $data[ 'discount_code' ] = empty( $value ) ? null : $value[0];
182
            } else {
183
                $data[ $key ] = $value;
184
            }
185
186
        }
187
188
    }
189
190
    // Backwards compatibility.
191
    if ( ! empty( $data['payment_details'] ) ) {
192
193
        foreach ( $data['payment_details'] as $key => $value ) {
194
            $data[ $key ] = $value;
195
        }
196
197
    }
198
199
    // Set up the owner of the invoice.
200
    $user_id = ! empty( $data['user_id'] ) ? wpinv_clean( $data['user_id'] ) : get_current_user_id();
201
202
    // Make sure the user exists.
203
    if ( ! get_userdata( $user_id ) ) {
204
        return $wp_error ? new WP_Error( 'wpinv_invalid_user', __( 'There is no user with that ID.', 'invoicing' ) ) : 0;
205
    }
206
207
    $address = wpinv_get_user_address( $user_id );
208
209
    foreach ( $address as $key => $value ) {
210
211
        if ( $value == '' ) {
212
            $address[ $key ] = null;
213
        } else {
214
            $address[ $key ] = wpinv_clean( $value );
215
        }
216
217
    }
218
219
    // Load new data.
220
    $invoice->set_props(
221
222
        array(
223
224
            // Basic info.
225
            'template'             => isset( $data['template'] ) ? wpinv_clean( $data['template'] ) : null,
226
            'email_cc'             => isset( $data['email_cc'] ) ? wpinv_clean( $data['email_cc'] ) : null,
227
            'date_created'         => isset( $data['created_date'] ) ? wpinv_clean( $data['created_date'] ) : null,
228
            'due_date'             => isset( $data['due_date'] ) ? wpinv_clean( $data['due_date'] ) : null,
229
            'date_completed'       => isset( $data['date_completed'] ) ? wpinv_clean( $data['date_completed'] ) : null,
230
            'number'               => isset( $data['number'] ) ? wpinv_clean( $data['number'] ) : null,
231
            'key'                  => isset( $data['key'] ) ? wpinv_clean( $data['key'] ) : null,
232
            'status'               => isset( $data['status'] ) ? wpinv_clean( $data['status'] ) : null,
233
            'post_type'            => isset( $data['post_type'] ) ? wpinv_clean( $data['post_type'] ) : null,
234
            'user_ip'              => isset( $data['ip'] ) ? wpinv_clean( $data['ip'] ) : wpinv_get_ip(),
235
            'parent_id'            => isset( $data['parent'] ) ? intval( $data['parent'] ) : null,
236
            'mode'                 => isset( $data['mode'] ) ? wpinv_clean( $data['mode'] ) : null,
237
            'description'          => isset( $data['description'] ) ? wp_kses_post( $data['description'] ) : null,
238
239
            // Payment info.
240
            'disable_taxes'        => ! empty( $data['disable_taxes'] ),
241
            'currency'             => isset( $data['currency'] ) ? wpinv_clean( $data['currency'] ) : wpinv_get_currency(),
242
            'gateway'              => isset( $data['gateway'] ) ? wpinv_clean( $data['gateway'] ) : null,
243
            'transaction_id'       => isset( $data['transaction_id'] ) ? wpinv_clean( $data['transaction_id'] ) : null,
244
            'discount_code'        => isset( $data['discount_code'] ) ? wpinv_clean( $data['discount_code'] ) : null,
245
            'payment_form'         => isset( $data['payment_form'] ) ? intval( $data['payment_form'] ) : null,
246
            'submission_id'        => isset( $data['submission_id'] ) ? wpinv_clean( $data['submission_id'] ) : null,
247
            'subscription_id'      => isset( $data['subscription_id'] ) ? wpinv_clean( $data['subscription_id'] ) : null,
248
            'is_viewed'            => isset( $data['is_viewed'] ) ? wpinv_clean( $data['is_viewed'] ) : null,
249
            'fees'                 => isset( $data['fees'] ) ? wpinv_clean( $data['fees'] ) : null,
250
            'discounts'            => isset( $data['discounts'] ) ? wpinv_clean( $data['discounts'] ) : null,
251
            'taxes'                => isset( $data['taxes'] ) ? wpinv_clean( $data['taxes'] ) : null,
252
            
253
254
            // Billing details.
255
            'user_id'              => $data['user_id'],
256
            'first_name'           => isset( $data['first_name'] ) ? wpinv_clean( $data['first_name'] ) : $address['first_name'],
257
            'last_name'            => isset( $data['last_name'] ) ? wpinv_clean( $data['last_name'] ) : $address['last_name'],
258
            'address'              => isset( $data['address'] ) ? wpinv_clean( $data['address'] ) : $address['address'] ,
259
            'vat_number'           => isset( $data['vat_number'] ) ? wpinv_clean( $data['vat_number'] ) : $address['vat_number'],
260
            'company'              => isset( $data['company'] ) ? wpinv_clean( $data['company'] ) : $address['company'],
261
            'zip'                  => isset( $data['zip'] ) ? wpinv_clean( $data['zip'] ) : $address['zip'],
262
            'state'                => isset( $data['state'] ) ? wpinv_clean( $data['state'] ) : $address['state'],
263
            'city'                 => isset( $data['city'] ) ? wpinv_clean( $data['city'] ) : $address['city'],
264
            'country'              => isset( $data['country'] ) ? wpinv_clean( $data['country'] ) : $address['country'],
265
            'phone'                => isset( $data['phone'] ) ? wpinv_clean( $data['phone'] ) : $address['phone'],
266
            'address_confirmed'    => ! empty( $data['address_confirmed'] ),
267
268
        )
269
270
    );
271
272
    // Backwards compatibililty.
273
    if ( ! empty( $data['cart_details'] ) && is_array( $data['cart_details'] ) ) {
274
        $data['items'] = array();
275
276
        foreach( $data['cart_details'] as $_item ) {
277
278
            // Ensure that we have an item id.
279
            if ( empty(  $_item['id']  ) ) {
280
                continue;
281
            }
282
283
            // Retrieve the item.
284
            $item = new GetPaid_Form_Item(  $_item['id']  );
285
286
            // Ensure that it is purchasable.
287
            if ( ! $item->can_purchase() ) {
288
                continue;
289
            }
290
291
            // Set quantity.
292
            if ( ! empty( $_item['quantity'] ) && is_numeric( $_item['quantity'] ) ) {
293
                $item->set_quantity( $_item['quantity'] );
294
            }
295
296
            // Set price.
297
            if ( isset( $_item['item_price'] ) ) {
298
                $item->set_price( $_item['item_price'] );
299
            }
300
301
            if ( isset( $_item['custom_price'] ) ) {
302
                $item->set_price( $_item['custom_price'] );
303
            }
304
305
            // Set name.
306
            if ( ! empty( $_item['name'] ) ) {
307
                $item->set_name( $_item['name'] );
308
            }
309
310
            // Set description.
311
            if ( isset( $_item['description'] ) ) {
312
                $item->set_custom_description( $_item['description'] );
313
            }
314
315
            // Set meta.
316
            if ( isset( $_item['meta'] ) && is_array( $_item['meta'] ) ) {
317
318
                $item->set_item_meta( $_item['meta'] );
319
320
                if ( isset( $_item['meta']['description'] ) ) {
321
                    $item->set_custom_description( $_item['meta']['description'] );
322
                }
323
324
            }
325
326
            $data['items'][] = $item;
327
328
        }
329
    }
330
331
    // Add invoice items.
332
    if ( ! empty( $data['items'] ) && is_array( $data['items'] ) ) {
333
334
        $invoice->set_items( array() );
335
336
        foreach ( $data['items'] as $item ) {
337
338
            if ( is_object( $item ) && is_a( $item, 'GetPaid_Form_Item' ) && $item->can_purchase() ) {
339
                $invoice->add_item( $item );
340
            }
341
342
        }
343
344
    }
345
346
    // Save the invoice.
347
    $invoice->recalculate_total();
348
    $invoice->save();
349
350
    if ( ! $invoice->get_id() ) {
351
        return $wp_error ? new WP_Error( 'wpinv_insert_invoice_error', __( 'An error occured when saving your invoice.', 'invoicing' ) ) : 0;
352
    }
353
354
    // Add private note.
355
    if ( ! empty( $data['private_note'] ) ) {
356
        $invoice->add_note( $data['private_note'] );
357
    }
358
359
    // User notes.
360
    if ( !empty( $data['user_note'] ) ) {
361
        $invoice->add_note( $data['user_note'], true );
362
    }
363
364
    // Created via.
365
    if ( isset( $data['created_via'] ) ) {
366
        update_post_meta( $invoice->get_id(), 'wpinv_created_via', $data['created_via'] );
367
    }
368
369
    // Backwards compatiblity.
370
    if ( $invoice->is_quote() ) {
371
372
        if ( isset( $data['valid_until'] ) ) {
373
            update_post_meta( $invoice->get_id(), 'wpinv_quote_valid_until', $data['valid_until'] );
374
        }
375
        return $invoice;
376
377
    }
378
379
}
380
381
/**
382
 * Retrieves an invoice.
383
 * 
384
 * @param int|string|object|WPInv_Invoice|WPInv_Legacy_Invoice|WP_Post $invoice Invoice id, key, transaction id, number or object.
385
 * @param $bool $deprecated
0 ignored issues
show
Documentation Bug introduced by
The doc comment $bool at position 0 could not be parsed: Unknown type name '$bool' at position 0 in $bool.
Loading history...
386
 * @return WPInv_Invoice|null
387
 */
388
function wpinv_get_invoice( $invoice = 0, $deprecated = false ) {
389
390
    // If we are retrieving the invoice from the cart...
391
    if ( $deprecated && empty( $invoice ) ) {
392
        $invoice = (int) getpaid_get_current_invoice_id();
393
    }
394
395
    // Retrieve the invoice.
396
    $invoice = new WPInv_Invoice( $invoice );
397
398
    // Check if it exists.
399
    if ( $invoice->get_id() != 0 ) {
400
        return $invoice;
401
    }
402
403
    return null;
404
}
405
406
/**
407
 * Retrieves several invoices.
408
 * 
409
 * @param array $args Args to search for.
410
 * @return WPInv_Invoice[]|int[]|object
411
 */
412
function wpinv_get_invoices( $args ) {
413
414
    // Prepare args.
415
    $args = wp_parse_args(
416
        $args,
417
        array(
418
            'status'   => array_keys( wpinv_get_invoice_statuses() ),
419
            'type'     => 'wpi_invoice',
420
            'limit'    => get_option( 'posts_per_page' ),
421
            'return'   => 'objects',
422
        )
423
    );
424
425
    // Map params to wp_query params.
426
    $map_legacy = array(
427
        'numberposts'    => 'limit',
428
        'post_type'      => 'type',
429
        'post_status'    => 'status',
430
        'post_parent'    => 'parent',
431
        'author'         => 'user',
432
        'posts_per_page' => 'limit',
433
        'paged'          => 'page',
434
        'post__not_in'   => 'exclude',
435
        'post__in'       => 'include',
436
    );
437
438
    foreach ( $map_legacy as $to => $from ) {
439
        if ( isset( $args[ $from ] ) ) {
440
            $args[ $to ] = $args[ $from ];
441
            unset( $args[ $from ] );
442
        }
443
    }
444
445
    // Backwards compatibility.
446
    if ( ! empty( $args['email'] ) && empty( $args['user'] ) ) {
447
        $args['user'] = $args['email'];
448
        unset( $args['email'] );
449
    }
450
451
    // Handle cases where the user is set as an email.
452
    if ( ! empty( $args['author'] ) && is_email( $args['author'] ) ) {
453
        $user = get_user_by( 'email', $args['user'] );
454
455
        if ( $user ) {
456
            $args['author'] = $user->user_email;
457
        }
458
459
    }
460
461
    // We only want invoice ids.
462
    $args['fields'] = 'ids';
463
464
    // Show all posts.
465
    $paginate = true;
466
    if ( isset( $args['paginate'] ) ) {
467
        
468
        $paginate = $args['paginate'];
469
        $args['no_found_rows'] = empty( $args['paginate'] );
470
        unset( $args['paginate'] );
471
472
    }
473
474
    // Whether to return objects or fields.
475
    $return = $args['return'];
476
    unset( $args['return'] );
477
478
    // Get invoices.
479
    $invoices = new WP_Query( apply_filters( 'wpinv_get_invoices_args', $args ) );
480
481
    // Prepare the results.
482
    if ( 'objects' === $return ) {
483
        $results = array_map( 'wpinv_get_invoice', $invoices->posts );
484
    } elseif ( 'self' === $return ) {
485
        return $invoices;
486
    } else {
487
        $results = $invoices->posts;
488
    }
489
490
    if ( $paginate ) {
491
        return (object) array(
492
            'invoices'      => $results,
493
            'total'         => $invoices->found_posts,
494
            'max_num_pages' => $invoices->max_num_pages,
495
        );
496
    }
497
498
    return $results;
499
500
}
501
502
/**
503
 * Retrieves an invoice's id from a transaction id.
504
 * 
505
 * @param string $transaction_id The transaction id to check.
506
 * @return int Invoice id on success or 0 on failure
507
 */
508
function wpinv_get_id_by_transaction_id( $transaction_id ) {
509
    return WPInv_Invoice::get_invoice_id_by_field( $transaction_id, 'transaction_id' );
510
}
511
512
/**
513
 * Retrieves an invoice's id from the invoice number.
514
 * 
515
 * @param string $invoice_number The invoice number to check.
516
 * @return int Invoice id on success or 0 on failure
517
 */
518
function wpinv_get_id_by_invoice_number( $invoice_number ) {
519
    return WPInv_Invoice::get_invoice_id_by_field( $invoice_number, 'number' );
520
}
521
522
/**
523
 * Retrieves an invoice's id from the invoice key.
524
 * 
525
 * @param string $invoice_key The invoice key to check.
526
 * @return int Invoice id on success or 0 on failure
527
 */
528
function wpinv_get_invoice_id_by_key( $invoice_key ) {
529
    return WPInv_Invoice::get_invoice_id_by_field( $invoice_key, 'key' );
530
}
531
532
/**
533
 * Retrieves an invoice's notes.
534
 * 
535
 * @param int|string|object|WPInv_Invoice|WPInv_Legacy_Invoice|WP_Post $invoice Invoice id, key, transaction id, number or object.
536
 * @param string $type Optionally filter by type i.e customer|system
537
 * @return array|null
538
 */
539
function wpinv_get_invoice_notes( $invoice = 0, $type = '' ) {
540
541
    // Prepare the invoice.
542
    $invoice = wpinv_get_invoice( $invoice );
543
    if ( empty( $invoice ) ) {
544
        return NULL;
545
    }
546
547
    // Fetch notes.
548
    $notes = getpaid_notes()->get_invoice_notes( $invoice->get_id(), $type );
549
550
    // Filter the notes.
551
    return apply_filters( 'wpinv_invoice_notes', $notes, $invoice->get_id(), $type );
552
}
553
554
/**
555
 * Returns an array of columns to display on the invoices page.
556
 * 
557
 * @param string $post_type
558
 */
559
function wpinv_get_user_invoices_columns( $post_type = 'wpi_invoice' ) {
560
561
    $label   = getpaid_get_post_type_label( $post_type, false );
562
    $label   = empty( $label ) ? __( 'Invoice', 'invoicing' ) : sanitize_text_field( $label );
563
    $columns = array(
564
565
            'invoice-number'  => array(
566
                'title' => $label,
567
                'class' => 'text-left'
568
            ),
569
570
            'created-date'    => array(
571
                'title' => __( 'Created Date', 'invoicing' ),
572
                'class' => 'text-left'
573
            ),
574
575
            'payment-date'    => array(
576
                'title' => __( 'Payment Date', 'invoicing' ),
577
                'class' => 'text-left'
578
            ),
579
580
            'invoice-status'  => array(
581
                'title' => __( 'Status', 'invoicing' ),
582
                'class' => 'text-center'
583
            ),
584
585
            'invoice-total'   => array(
586
                'title' => __( 'Total', 'invoicing' ),
587
                'class' => 'text-right'
588
            ),
589
590
            'invoice-actions' => array(
591
                'title' => '&nbsp;',
592
                'class' => 'text-center'
593
            ),
594
595
        );
596
597
    return apply_filters( 'wpinv_user_invoices_columns', $columns, $post_type );
598
}
599
600
/**
601
 * Displays the invoice receipt.
602
 */
603
function wpinv_payment_receipt() {
604
605
    // Find the invoice.
606
    $invoice_id = getpaid_get_current_invoice_id();
607
    $invoice = new WPInv_Invoice( $invoice_id );
608
609
    // Abort if non was found.
610
    if ( empty( $invoice_id ) || $invoice->is_draft() ) {
611
612
        return aui()->alert(
613
            array(
614
                'type'    => 'warning',
615
                'content' => __( 'We could not find your invoice', 'invoicing' ),
616
            )
617
        );
618
619
    }
620
621
    // Can the user view this invoice?
622
    if ( ! wpinv_can_view_receipt( $invoice_id ) ) {
623
624
        return aui()->alert(
625
            array(
626
                'type'    => 'warning',
627
                'content' => __( 'You are not allowed to view this receipt', 'invoicing' ),
628
            )
629
        );
630
631
    }
632
633
    // Load the template.
634
    return wpinv_get_template_html( 'invoice-receipt.php', compact( 'invoice' ) );
635
636
}
637
638
/**
639
 * Displays the invoice history.
640
 */
641
function getpaid_invoice_history( $user_id = 0, $post_type = 'wpi_invoice' ) {
642
643
    // Ensure that we have a user id.
644
    if ( empty( $user_id ) || ! is_numeric( $user_id ) ) {
645
        $user_id = get_current_user_id();
646
    }
647
648
    $label = getpaid_get_post_type_label( $post_type );
649
    $label = empty( $label ) ? __( 'Invoices', 'invoicing' ) : sanitize_text_field( $label );
650
651
    // View user id.
652
    if ( empty( $user_id ) ) {
653
654
        return aui()->alert(
655
            array(
656
                'type'    => 'warning',
657
                'content' => sprintf(
658
                    __( 'You must be logged in to view your %s.', 'invoicing' ),
659
                    strtolower( $label )
660
                )
661
            )
662
        );
663
664
    }
665
666
    // Fetch invoices.
667
    $invoices = wpinv_get_invoices(
668
669
        array(
670
            'page'      => ( get_query_var( 'paged' ) ) ? absint( get_query_var( 'paged' ) ) : 1,
671
            'user'      => $user_id,
672
            'paginate'  => true,
673
            'type'      => $post_type,
674
            'status'    => array_keys( wpinv_get_invoice_statuses( false, false, $post_type ) ),
675
        )
676
677
    );
678
679
    if ( empty( $invoices->total ) ) {
680
681
        return aui()->alert(
682
            array(
683
                'type'    => 'info',
684
                'content' => sprintf(
685
                    __( 'No %s found.', 'invoicing' ),
686
                    strtolower( $label )
687
                )
688
            )
689
        );
690
691
    }
692
693
    // Load the template.
694
    return wpinv_get_template_html( 'invoice-history.php', compact( 'invoices', 'post_type' ) );
695
696
}
697
698
/**
699
 * Formats an invoice number given an invoice type.
700
 */
701
function wpinv_format_invoice_number( $number, $type = '' ) {
702
703
    // Allow other plugins to overide this.
704
    $check = apply_filters( 'wpinv_pre_format_invoice_number', null, $number, $type );
705
    if ( null !== $check ) {
706
        return $check;
707
    }
708
709
    // Ensure that we have a numeric number.
710
    if ( ! is_numeric( $number ) ) {
711
        return $number;
712
    }
713
714
    // Format the number.
715
    $padd             = absint( (int) wpinv_get_option( 'invoice_number_padd' ) );
716
    $prefix           = sanitize_text_field( (string) wpinv_get_option( 'invoice_number_prefix', 'INV-' ) );
717
    $prefix           = sanitize_text_field( apply_filters( 'getpaid_invoice_type_prefix', $prefix, $type ) );
718
    $postfix          = sanitize_text_field( (string) wpinv_get_option( 'invoice_number_postfix' ) );
719
    $postfix          = sanitize_text_field( apply_filters( 'getpaid_invoice_type_postfix', $postfix, $type ) );
720
    $formatted_number = zeroise( absint( $number ), $padd );
721
722
    // Add the prefix and post fix.
723
    $formatted_number = $prefix . $formatted_number . $postfix;
724
725
    return apply_filters( 'wpinv_format_invoice_number', $formatted_number, $number, $prefix, $postfix, $padd );
726
}
727
728
/**
729
 * Returns the next invoice number.
730
 * 
731
 * @param string $type.
732
 * @return int|null|bool
733
 */
734
function wpinv_get_next_invoice_number( $type = '' ) {
735
736
    // Allow plugins to overide this.
737
    $check = apply_filters( 'wpinv_get_pre_next_invoice_number', null, $type );
738
    if ( null !== $check ) {
739
        return $check;
740
    }
741
742
    // Ensure sequential invoice numbers is active.
743
    if ( ! wpinv_sequential_number_active() ) {
744
        return false;
745
    }
746
747
    // Retrieve the current number and the start number.
748
    $number = (int) get_option( 'wpinv_last_invoice_number', 0 );
749
    $start  = absint( (int) wpinv_get_option( 'invoice_sequence_start', 1 ) );
750
751
    // Ensure that we are starting at a positive integer.
752
    $start  = max( $start, 1 );
753
754
    // If this is the first invoice, use the start number.
755
    $number = max( $start, $number );
756
757
    // Format the invoice number.
758
    $formatted_number = wpinv_format_invoice_number( $number, $type );
759
760
    // Ensure that this number is unique.
761
    $invoice_id = WPInv_Invoice::get_invoice_id_by_field( $formatted_number, 'number' );
762
763
    // We found a match. Nice.
764
    if ( empty( $invoice_id ) ) {
765
        update_option( 'wpinv_last_invoice_number', $number );
766
        return apply_filters( 'wpinv_get_next_invoice_number', $number );
767
    }
768
769
    update_option( 'wpinv_last_invoice_number', $number + 1 );
770
    return wpinv_get_next_invoice_number( $type );
771
772
}
773
774
/**
775
 * The prefix used for invoice paths.
776
 */
777
function wpinv_post_name_prefix( $post_type = 'wpi_invoice' ) {
778
    return apply_filters( 'wpinv_post_name_prefix', 'inv-', $post_type );
779
}
780
781
function wpinv_generate_post_name( $post_ID ) {
782
    $prefix = wpinv_post_name_prefix( get_post_type( $post_ID ) );
783
    $post_name = sanitize_title( $prefix . $post_ID );
784
785
    return apply_filters( 'wpinv_generate_post_name', $post_name, $post_ID, $prefix );
786
}
787
788
/**
789
 * Checks if an invoice was viewed by the customer.
790
 * 
791
 * @param int|string|object|WPInv_Invoice|WPInv_Legacy_Invoice|WP_Post $invoice Invoice id, key, transaction id, number or object.
792
 */
793
function wpinv_is_invoice_viewed( $invoice ) {
794
    $invoice = new WPInv_Invoice( $invoice );
795
    return (bool) $invoice->get_is_viewed();
796
}
797
798
/**
799
 * Marks an invoice as viewed.
800
 * 
801
 * @param int|string|object|WPInv_Invoice|WPInv_Legacy_Invoice|WP_Post $invoice Invoice id, key, transaction id, number or object.
802
 */
803
function getpaid_maybe_mark_invoice_as_viewed( $invoice ) {
804
    $invoice = new WPInv_Invoice( $invoice );
805
806
    if ( get_current_user_id() == $invoice->get_user_id() && ! $invoice->get_is_viewed() ) {
807
        $invoice->set_is_viewed( true );
808
        $invoice->save();
809
    }
810
811
}
812
add_action( 'wpinv_invoice_print_before_display', 'getpaid_maybe_mark_invoice_as_viewed' );
813
add_action( 'wpinv_before_receipt', 'getpaid_maybe_mark_invoice_as_viewed' );
814
815
/**
816
 * Processes an invoice refund.
817
 * 
818
 * @param WPInv_Invoice $invoice
819
 * @param array $status_transition
820
 * @todo: descrease customer/store earnings
821
 */
822
function getpaid_maybe_process_refund( $invoice, $status_transition ) {
823
824
    if ( empty( $status_transition['from'] ) || ! in_array( $status_transition['from'], array( 'publish', 'wpi-processing', 'wpi-renewal' ) ) ) {
825
        return;
826
    }
827
828
    $discount_code = $invoice->get_discount_code();
829
    if ( ! empty( $discount_code ) ) {
830
        $discount = wpinv_get_discount_obj( $discount_code );
831
832
        if ( $discount->exists() ) {
833
            $discount->increase_usage( -1 );
834
        }
835
836
    }
837
838
    do_action( 'wpinv_pre_refund_invoice', $invoice, $invoice->get_id() );
839
    do_action( 'wpinv_refund_invoice', $invoice, $invoice->get_id() );
840
    do_action( 'wpinv_post_refund_invoice', $invoice, $invoice->get_id() );
841
}
842
add_action( 'getpaid_invoice_status_wpi-refunded', 'getpaid_maybe_process_refund', 10, 2 );
843
844
845
/**
846
 * Processes invoice payments.
847
 *
848
 * @param int $invoice_id
849
 */
850
function getpaid_process_invoice_payment( $invoice_id ) {
851
852
    // Fetch the invoice.
853
    $invoice = new WPInv_Invoice( $invoice_id );
854
855
    // We only want to do this once.
856
    if ( 1 ==  get_post_meta( $invoice->get_id(), 'wpinv_processed_payment', true ) ) {
857
        return;
858
    }
859
860
    update_post_meta( $invoice->get_id(), 'wpinv_processed_payment', 1 );
861
862
    // Fires when processing a payment.
863
    do_action( 'getpaid_process_payment', $invoice );
864
865
    // Fire an action for each invoice item.
866
    foreach( $invoice->get_items() as $item ) {
867
        do_action( 'getpaid_process_item_payment', $item, $invoice );
868
    }
869
870
    // Increase discount usage.
871
    $discount_code = $invoice->get_discount_code();
872
    if ( ! empty( $discount_code ) && ! $invoice->is_renewal() ) {
873
        $discount = wpinv_get_discount_obj( $discount_code );
874
875
        if ( $discount->exists() ) {
876
            $discount->increase_usage();
877
        }
878
879
    }
880
881
    // Record reverse vat.
882
    if ( 'invoice' == $invoice->get_type() && wpinv_use_taxes() && ! $invoice->get_disable_taxes() ) {
883
884
        if ( WPInv_EUVat::same_country_rule() == 'no' && wpinv_is_base_country( $invoice->get_country() ) ) {
885
            $invoice->add_note( __( 'VAT was reverse charged', 'invoicing' ), false, false, true );
886
        }
887
888
    }
889
890
}
891
add_action( 'getpaid_invoice_payment_status_changed', 'getpaid_process_invoice_payment' );
892
893
/**
894
 * Returns an array of invoice item columns
895
 * 
896
 * @param int|WPInv_Invoice $invoice
897
 * @return array
898
 */
899
function getpaid_invoice_item_columns( $invoice ) {
900
901
    // Prepare the invoice.
902
    $invoice = new WPInv_Invoice( $invoice );
903
904
    // Abort if there is no invoice.
905
    if ( 0 == $invoice->get_id() ) {
906
        return array();
907
    }
908
909
    // Line item columns.
910
    $columns = apply_filters(
911
        'getpaid_invoice_item_columns',
912
        array(
913
            'name'     => __( 'Item', 'invoicing' ),
914
            'price'    => __( 'Price', 'invoicing' ),
915
            'quantity' => __( 'Quantity', 'invoicing' ),
916
            'subtotal' => __( 'Subtotal', 'invoicing' ),
917
        ),
918
        $invoice
919
    );
920
921
    // Quantities.
922
    if ( isset( $columns[ 'quantity' ] ) ) {
923
924
        if ( 'hours' == $invoice->get_template() ) {
925
            $columns[ 'quantity' ] = __( 'Hours', 'invoicing' );
926
        }
927
928
        if ( ! wpinv_item_quantities_enabled() || 'amount' == $invoice->get_template() ) {
929
            unset( $columns[ 'quantity' ] );
930
        }
931
932
    }
933
934
935
    // Price.
936
    if ( isset( $columns[ 'price' ] ) ) {
937
938
        if ( 'amount' == $invoice->get_template() ) {
939
            $columns[ 'price' ] = __( 'Amount', 'invoicing' );
940
        }
941
942
        if ( 'hours' == $invoice->get_template() ) {
943
            $columns[ 'price' ] = __( 'Rate', 'invoicing' );
944
        }
945
946
    }
947
948
949
    // Sub total.
950
    if ( isset( $columns[ 'subtotal' ] ) ) {
951
952
        if ( 'amount' == $invoice->get_template() ) {
953
            unset( $columns[ 'subtotal' ] );
954
        }
955
956
    }
957
958
    return $columns;
959
}
960
961
/**
962
 * Returns an array of invoice totals rows
963
 * 
964
 * @param int|WPInv_Invoice $invoice
965
 * @return array
966
 */
967
function getpaid_invoice_totals_rows( $invoice ) {
968
969
    // Prepare the invoice.
970
    $invoice = new WPInv_Invoice( $invoice );
971
972
    // Abort if there is no invoice.
973
    if ( 0 == $invoice->get_id() ) {
974
        return array();
975
    }
976
977
    $totals = apply_filters(
978
        'getpaid_invoice_totals_rows',
979
        array(
980
            'subtotal' => __( 'Subtotal', 'invoicing' ),
981
            'tax'      => __( 'Tax', 'invoicing' ),
982
            'fee'      => __( 'Fee', 'invoicing' ),
983
            'discount' => __( 'Discount', 'invoicing' ),
984
            'total'    => __( 'Total', 'invoicing' ),
985
        ),
986
        $invoice
987
    );
988
989
    if ( ( $invoice->get_disable_taxes() || ! wpinv_use_taxes() ) && isset( $totals['tax'] ) ) {
990
        unset( $totals['tax'] );
991
    }
992
993
    return $totals;
994
}
995
996
/**
997
 * This function is called whenever an invoice is created.
998
 * 
999
 * @param WPInv_Invoice $invoice
1000
 */
1001
function getpaid_new_invoice( $invoice ) {
1002
1003
    if ( ! $invoice->get_status() ) {
1004
        return;
1005
    }
1006
1007
    // Add an invoice created note.
1008
    $invoice->add_note(
1009
        sprintf(
1010
            __( '%s created with the status "%s".', 'invoicing' ),
1011
            ucfirst( $invoice->get_type() ),
1012
            wpinv_status_nicename( $invoice->get_status(), $invoice  )
1013
        )
1014
    );
1015
1016
}
1017
add_action( 'getpaid_new_invoice', 'getpaid_new_invoice' );
1018
1019
/**
1020
 * This function updates invoice caches.
1021
 * 
1022
 * @param WPInv_Invoice $invoice
1023
 */
1024
function getpaid_update_invoice_caches( $invoice ) {
1025
1026
    // Cache invoice number.
1027
    wp_cache_set( $invoice->get_number(), $invoice->get_id(), "getpaid_invoice_numbers_to_invoice_ids" );
1028
1029
    // Cache invoice key.
1030
    wp_cache_set( $invoice->get_key(), $invoice->get_id(), "getpaid_invoice_keys_to_invoice_ids" );
1031
1032
    // (Maybe) cache transaction id.
1033
    $transaction_id = $invoice->get_transaction_id();
1034
1035
    if ( ! empty( $transaction_id ) ) {
1036
        wp_cache_set( $transaction_id, $invoice->get_id(), "getpaid_invoice_transaction_ids_to_invoice_ids" );
1037
    }
1038
1039
}
1040
add_action( 'getpaid_new_invoice', 'getpaid_update_invoice_caches', 5 );
1041
add_action( 'getpaid_update_invoice', 'getpaid_update_invoice_caches', 5 );
1042
1043
/**
1044
 * Duplicates an invoice.
1045
 * 
1046
 * Please note that this function does not save the duplicated invoice.
1047
 * 
1048
 * @param  WPInv_Invoice $old_invoice The invoice to duplicate
1049
 * @return WPInv_Invoice The new invoice.
1050
 */
1051
function getpaid_duplicate_invoice( $old_invoice ) {
1052
1053
    // Create the new invoice.
1054
    $invoice = new WPInv_Invoice();
1055
    $invoice->set_props(
1056
1057
        array(
1058
1059
            // Basic info.
1060
            'template'             => $old_invoice->get_template(),
1061
            'email_cc'             => $old_invoice->get_email_cc(),
1062
            'post_type'            => $old_invoice->get_post_type(),
1063
            'user_ip'              => $old_invoice->get_user_ip(),
1064
            'parent_id'            => $old_invoice->get_parent_id(),
1065
            'mode'                 => $old_invoice->get_mode(),
1066
            'description'          => $old_invoice->get_description(),
1067
            'created_via'          => $old_invoice->get_created_via(),
1068
1069
            // Payment info.
1070
            'disable_taxes'        => $old_invoice->get_disable_taxes(),
1071
            'currency'             => $old_invoice->get_currency(),
1072
            'gateway'              => $old_invoice->get_gateway(),
1073
            'discount_code'        => $old_invoice->get_discount_code(),
1074
            'payment_form'         => $old_invoice->get_payment_form(),
1075
            'submission_id'        => $old_invoice->get_submission_id(),
1076
            'subscription_id'      => $old_invoice->get_subscription_id(),
1077
            'fees'                 => $old_invoice->get_fees(),
1078
            'discounts'            => $old_invoice->get_discounts(),
1079
            'taxes'                => $old_invoice->get_taxes(),
1080
            'items'                => $old_invoice->get_items(),
1081
1082
            // Billing details.
1083
            'user_id'              => $old_invoice->get_user_id(),
1084
            'first_name'           => $old_invoice->get_first_name(),
1085
            'last_name'            => $old_invoice->get_last_name(),
1086
            'address'              => $old_invoice->get_address(),
1087
            'vat_number'           => $old_invoice->get_vat_number(),
1088
            'company'              => $old_invoice->get_company(),
1089
            'zip'                  => $old_invoice->get_zip(),
1090
            'state'                => $old_invoice->get_state(),
1091
            'city'                 => $old_invoice->get_city(),
1092
            'country'              => $old_invoice->get_country(),
1093
            'phone'                => $old_invoice->get_phone(),
1094
            'address_confirmed'    => $old_invoice->get_address_confirmed(),
1095
1096
        )
1097
1098
    );
1099
1100
    // Recalculate totals.
1101
    $invoice->recalculate_total();
1102
1103
    return $invoice;
1104
}
1105
1106
/**
1107
 * Retrieves invoice meta fields.
1108
 *
1109
 * @param WPInv_Invoice $invoice
1110
 * @return array
1111
 */
1112
function getpaid_get_invoice_meta( $invoice ) {
1113
1114
    // Load the invoice meta.
1115
    $meta = array(
1116
1117
        'number' => array(
1118
            'label' => sprintf(
1119
                __( '%s Number', 'invoicing' ),
1120
                ucfirst( $invoice->get_type() )
1121
            ),
1122
            'value' => sanitize_text_field( $invoice->get_number() ),
1123
        ),
1124
1125
        'status' => array(
1126
            'label' => sprintf(
1127
                __( '%s Status', 'invoicing' ),
1128
                ucfirst( $invoice->get_type() )
1129
            ),
1130
            'value' => $invoice->get_status_label_html(),
1131
        ),
1132
1133
        'date' => array(
1134
            'label' => sprintf(
1135
                __( '%s Date', 'invoicing' ),
1136
                ucfirst( $invoice->get_type() )
1137
            ),
1138
            'value' => getpaid_format_date( $invoice->get_created_date() ),
1139
        ),
1140
1141
        'date_paid' => array(
1142
            'label' => __( 'Paid On', 'invoicing' ),
1143
            'value' => getpaid_format_date( $invoice->get_completed_date() ),
1144
        ),
1145
1146
        'gateway'   => array(
1147
            'label' => __( 'Payment Method', 'invoicing' ),
1148
            'value' => sanitize_text_field( $invoice->get_gateway_title() ),
1149
        ),
1150
1151
        'transaction_id' => array(
1152
            'label' => __( 'Transaction ID', 'invoicing' ),
1153
            'value' => sanitize_text_field( $invoice->get_transaction_id() ),
1154
        ),
1155
1156
        'due_date'  => array(
1157
            'label' => __( 'Due Date', 'invoicing' ),
1158
            'value' => getpaid_format_date( $invoice->get_due_date() ),
1159
        ),
1160
1161
        'vat_number' => array(
1162
            'label' => sprintf(
1163
                __( '%s Number', 'invoicing' ),
1164
                getpaid_tax()->get_vat_name()
1165
            ),
1166
            'value' => sanitize_text_field( $invoice->get_vat_number() ),
1167
        ),
1168
1169
    );
1170
1171
    // If it is not paid, remove the date of payment.
1172
    if ( ! $invoice->is_paid() ) {
1173
        unset( $meta[ 'date_paid' ] );
1174
        unset( $meta[ 'transaction_id' ] );
1175
    }
1176
1177
    if ( ! $invoice->is_paid() || 'none' == $invoice->get_gateway() ) {
1178
        unset( $meta[ 'gateway' ] );
1179
    }
1180
1181
    // Only display the due date if due dates are enabled.
1182
    if ( ! $invoice->needs_payment() || ! wpinv_get_option( 'overdue_active' ) ) {
1183
        unset( $meta[ 'due_date' ] );
1184
    }
1185
1186
    // Only display the vat number if taxes are enabled.
1187
    if ( ! wpinv_use_taxes() ) {
1188
        unset( $meta[ 'vat_number' ] );
1189
    }
1190
1191
    if ( $invoice->is_recurring() ) {
1192
1193
        // Link to the parent invoice.
1194
        if ( $invoice->is_renewal() ) {
1195
1196
            $meta[ 'parent' ] = array(
1197
1198
                'label' => sprintf(
1199
                    __( 'Parent %s', 'invoicing' ),
1200
                    ucfirst( $invoice->get_type() )
1201
                ),
1202
1203
                'value' => wpinv_invoice_link( $invoice->get_parent_id() ),
1204
1205
            );
1206
1207
        }
1208
1209
        $subscription = wpinv_get_subscription( $invoice );
1210
1211
        if ( ! empty ( $subscription ) ) {
1212
1213
            // Display the renewal date.
1214
            if ( $subscription->is_active() && 'cancelled' != $subscription->get_status() ) {
1215
1216
                $meta[ 'renewal_date' ] = array(
1217
1218
                    'label' => __( 'Renews On', 'invoicing' ),
1219
                    'value' => getpaid_format_date( $subscription->get_expiration() ),
1220
        
1221
                );
1222
1223
            }
1224
1225
            if ( $invoice->is_parent() ) {
1226
1227
                // Display the recurring amount.
1228
                $meta[ 'recurring_total' ] = array(
1229
1230
                    'label' => __( 'Recurring Amount', 'invoicing' ),
1231
                    'value' => wpinv_price( wpinv_format_amount( $subscription->get_recurring_amount() ), $invoice->get_currency() ),
0 ignored issues
show
Bug introduced by
wpinv_format_amount($sub...get_recurring_amount()) of type string is incompatible with the type double expected by parameter $amount of wpinv_price(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1231
                    'value' => wpinv_price( /** @scrutinizer ignore-type */ wpinv_format_amount( $subscription->get_recurring_amount() ), $invoice->get_currency() ),
Loading history...
1232
        
1233
                );
1234
1235
            }
1236
            
1237
        }
1238
    }
1239
1240
    // Add the invoice total to the meta.
1241
    $meta[ 'invoice_total' ] = array(
1242
1243
        'label' => __( 'Total Amount', 'invoicing' ),
1244
        'value' => wpinv_price( wpinv_format_amount( $invoice->get_total() ), $invoice->get_currency() ),
1245
1246
    );
1247
1248
    // Provide a way for third party plugins to filter the meta.
1249
    $meta = apply_filters( 'getpaid_invoice_meta_data', $meta, $invoice );
1250
1251
    return $meta;
1252
1253
}
1254