getpaid_get_subscription_period_label()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Contains subscription functions.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
9
/**
10
 * Retrieves an invoice's subscriptions.
11
 *
12
 * @param       WPInv_Invoice $invoice
13
 * @return      WPInv_Subscription[]|WPInv_Subscription|false
14
 * @since       2.3.0
15
 */
16
function getpaid_get_invoice_subscriptions( $invoice ) {
17
18
    // Retrieve subscription groups.
19
    $subscription_ids = wp_list_pluck( getpaid_get_invoice_subscription_groups( $invoice->get_id() ), 'subscription_id' );
20
21
    // No subscription groups, normal subscription.
22
    if ( empty( $subscription_ids ) ) {
23
        return getpaid_subscriptions()->get_invoice_subscription( $invoice );
0 ignored issues
show
Bug Best Practice introduced by
The expression return getpaid_subscript..._subscription($invoice) also could return the type boolean which is incompatible with the documented return type WPInv_Subscription|WPInv_Subscription[]|false.
Loading history...
24
    }
25
26
    // Subscription groups.
27
    return array_filter( array_map( 'getpaid_get_subscription', $subscription_ids ) );
28
29
}
30
31
/**
32
 * Retrieves an invoice's subscription groups.
33
 *
34
 * @param       int $invoice_id
35
 * @return      array
36
 * @since       2.3.0
37
 */
38
function getpaid_get_invoice_subscription_groups( $invoice_id ) {
39
    $subscription_groups = get_post_meta( $invoice_id, 'getpaid_subscription_groups', true );
40
    return empty( $subscription_groups ) ? array() : $subscription_groups;
0 ignored issues
show
Bug Best Practice introduced by
The expression return empty($subscripti... : $subscription_groups also could return the type string which is incompatible with the documented return type array.
Loading history...
41
}
42
43
/**
44
 * Retrieves an invoice's subscription's subscription groups.
45
 *
46
 * @param       int $invoice_id
47
 * @param       int $subscription_id
48
 * @return      array|false
49
 * @since       2.3.0
50
 */
51
function getpaid_get_invoice_subscription_group( $invoice_id, $subscription_id ) {
52
    $subscription_groups = getpaid_get_invoice_subscription_groups( $invoice_id );
53
	$matching_group      = wp_list_filter( $subscription_groups, compact( 'subscription_id' ) );
54
    return reset( $matching_group );
55
}
56
57
/**
58
 * Retrieves a subscription given an id.
59
 *
60
 * @param int|string|object|WPInv_Subscription $subscription Subscription object, id, profile_id, or object to read.
61
 * @since       2.3.0
62
 * @return WPInv_Subscription|false
63
 */
64
function getpaid_get_subscription( $subscription ) {
65
66
	if ( ! is_a( $subscription, 'WPInv_Subscription' ) ) {
67
		$subscription = new WPInv_Subscription( $subscription );
68
	}
69
70
	return $subscription->exists() ? $subscription : false;
71
}
72
73
/**
74
 * Queries the subscriptions database.
75
 *
76
 * @param array $args Query arguments.For a list of all supported args, refer to GetPaid_Subscriptions_Query::prepare_query()
77
 * @param string $return 'results' returns the found subscriptions, $count returns the total count while 'query' returns GetPaid_Subscriptions_Query
78
 *
79
 *
80
 * @return int|array|WPInv_Subscription[]|GetPaid_Subscriptions_Query
81
 */
82
function getpaid_get_subscriptions( $args = array(), $return = 'results' ) {
83
84
	// Do not retrieve all fields if we just want the count.
85
	if ( 'count' == $return ) {
86
		$args['fields'] = 'id';
87
		$args['number'] = 1;
88
	}
89
90
	// Do not count all matches if we just want the results.
91
	if ( 'results' == $return ) {
92
		$args['count_total'] = false;
93
	}
94
95
	$query = new GetPaid_Subscriptions_Query( $args );
96
97
	if ( 'results' == $return ) {
98
		return $query->get_results();
99
	}
100
101
	if ( 'count' == $return ) {
102
		return $query->get_total();
103
	}
104
105
	return $query;
106
}
107
108
/**
109
 * Returns an array of valid subscription statuses.
110
 *
111
 * @return array
112
 */
113
function getpaid_get_subscription_statuses() {
114
115
	return apply_filters(
116
		'getpaid_get_subscription_statuses',
117
		array(
118
			'pending'   => __( 'Pending', 'invoicing' ),
119
			'trialling' => __( 'Trialing', 'invoicing' ),
120
			'active'    => __( 'Active', 'invoicing' ),
121
			'failing'   => __( 'Failing', 'invoicing' ),
122
			'expired'   => __( 'Expired', 'invoicing' ),
123
			'completed' => __( 'Complete', 'invoicing' ),
124
			'cancelled' => __( 'Cancelled', 'invoicing' ),
125
		)
126
	);
127
128
}
129
130
/**
131
 * Returns a subscription status label
132
 *
133
 * @return string
134
 */
135
function getpaid_get_subscription_status_label( $status ) {
136
	$statuses = getpaid_get_subscription_statuses();
137
	return isset( $statuses[ $status ] ) ? $statuses[ $status ] : ucfirst( sanitize_text_field( $status ) );
138
}
139
140
/**
141
 * Returns an array of valid subscription status classes.
142
 *
143
 * @return array
144
 */
145
function getpaid_get_subscription_status_classes() {
146
147
	return apply_filters(
148
		'getpaid_get_subscription_status_classes',
149
		array(
150
			'pending'   => 'bg-dark',
151
			'trialling' => 'bg-info',
152
			'active'    => 'bg-success',
153
			'failing'   => 'bg-warning text-dark',
154
			'expired'   => 'bg-danger',
155
			'completed' => 'bg-primary',
156
			'cancelled' => 'bg-secondary',
157
		)
158
	);
159
160
}
161
162
/**
163
 * Counts subscriptions in each status.
164
 *
165
 * @return array
166
 */
167
function getpaid_get_subscription_status_counts( $args = array() ) {
168
169
	$statuses = array_keys( getpaid_get_subscription_statuses() );
170
	$counts   = array();
171
172
	foreach ( $statuses as $status ) {
173
		$_args             = wp_parse_args( "status=$status", $args );
174
		$counts[ $status ] = getpaid_get_subscriptions( $_args, 'count' );
175
	}
176
177
	return $counts;
178
179
}
180
181
/**
182
 * Returns valid subscription periods.
183
 *
184
 * @return array
185
 */
186
function getpaid_get_subscription_periods() {
187
188
	return apply_filters(
189
		'getpaid_get_subscription_periods',
190
		array(
191
192
			'day'   => array(
193
				'singular' => __( '%s day', 'invoicing' ),
194
				'plural'   => __( '%d days', 'invoicing' ),
195
			),
196
197
			'week'  => array(
198
				'singular' => __( '%s week', 'invoicing' ),
199
				'plural'   => __( '%d weeks', 'invoicing' ),
200
			),
201
202
			'month' => array(
203
				'singular' => __( '%s month', 'invoicing' ),
204
				'plural'   => __( '%d months', 'invoicing' ),
205
			),
206
207
			'year'  => array(
208
				'singular' => __( '%s year', 'invoicing' ),
209
				'plural'   => __( '%d years', 'invoicing' ),
210
			),
211
212
		)
213
	);
214
215
}
216
217
/**
218
 * Given a subscription trial, e.g, 1 month, returns the interval (1)
219
 *
220
 * @param string $trial_period
221
 * @return int
222
 */
223
function getpaid_get_subscription_trial_period_interval( $trial_period ) {
224
	return (int) preg_replace( '/[^0-9]/', '', $trial_period );
225
}
226
227
/**
228
 * Given a subscription trial, e.g, 1 month, returns the period (month)
229
 *
230
 * @param string $trial_period
231
 * @return string
232
 */
233
function getpaid_get_subscription_trial_period_period( $trial_period ) {
234
	return preg_replace( '/[^a-z]/', '', strtolower( $trial_period ) );
235
}
236
237
/**
238
 * Returns a singular period label..
239
 *
240
 * @param string $period
241
 * @param int $interval
242
 * @return string
243
 */
244
function getpaid_get_subscription_period_label( $period, $interval = 1, $singular_prefix = '1' ) {
245
	$label = (int) $interval > 1 ? getpaid_get_plural_subscription_period_label( $period, $interval ) : getpaid_get_singular_subscription_period_label( $period, $singular_prefix );
246
	return strtolower( sanitize_text_field( $label ) );
247
}
248
249
/**
250
 * Returns a singular period label..
251
 *
252
 * @param string $period
253
 * @return string
254
 */
255
function getpaid_get_singular_subscription_period_label( $period, $singular_prefix = '1' ) {
256
257
	$periods = getpaid_get_subscription_periods();
258
	$period  = strtolower( $period );
259
260
	if ( isset( $periods[ $period ] ) ) {
261
		return sprintf( $periods[ $period ]['singular'], $singular_prefix );
262
	}
263
264
	// Backwards compatibility.
265
	foreach ( $periods as $key => $data ) {
266
		if ( strpos( $key, $period ) === 0 ) {
267
			return sprintf( $data['singular'], $singular_prefix );
268
		}
269
	}
270
271
	// Invalid string.
272
	return '';
273
}
274
275
/**
276
 * Returns a plural period label..
277
 *
278
 * @param string $period
279
 * @param int $interval
280
 * @return string
281
 */
282
function getpaid_get_plural_subscription_period_label( $period, $interval ) {
283
284
	$periods = getpaid_get_subscription_periods();
285
	$period  = strtolower( $period );
286
287
	if ( isset( $periods[ $period ] ) ) {
288
		return sprintf( $periods[ $period ]['plural'], $interval );
289
	}
290
291
	// Backwards compatibility.
292
	foreach ( $periods as $key => $data ) {
293
		if ( strpos( $key, $period ) === 0 ) {
294
			return sprintf( $data['plural'], $interval );
295
		}
296
	}
297
298
	// Invalid string.
299
	return '';
300
}
301
302
/**
303
 * Returns formatted subscription amout
304
 *
305
 * @param WPInv_Subscription $subscription
306
 * @return string
307
 */
308
function getpaid_get_formatted_subscription_amount( $subscription ) {
309
310
	$initial    = wpinv_price( $subscription->get_initial_amount(), $subscription->get_parent_payment()->get_currency() );
311
	$recurring  = wpinv_price( $subscription->get_recurring_amount(), $subscription->get_parent_payment()->get_currency() );
312
	$period     = getpaid_get_subscription_period_label( $subscription->get_period(), $subscription->get_frequency(), '' );
313
	$bill_times = $subscription->get_bill_times();
314
	$bill_times_less = $bill_times - 1;
315
316
	if ( ! empty( $bill_times ) ) {
317
		$bill_times = $subscription->get_frequency() * $bill_times;
318
		$bill_times_less = getpaid_get_subscription_period_label( $subscription->get_period(), $bill_times - $subscription->get_frequency() );
319
		$bill_times = getpaid_get_subscription_period_label( $subscription->get_period(), $bill_times );
320
	}
321
322
	// Trial periods.
323
	if ( $subscription->has_trial_period() ) {
324
325
		$trial_period   = getpaid_get_subscription_trial_period_period( $subscription->get_trial_period() );
326
		$trial_interval = getpaid_get_subscription_trial_period_interval( $subscription->get_trial_period() );
327
328
		if ( empty( $bill_times ) ) {
329
330
			return sprintf(
331
				// translators: $1: is the initial amount, $2: is the trial period, $3: is the recurring amount, $4: is the recurring period
332
				_x( '%1$s trial for %2$s then %3$s / %4$s', 'Subscription amount. (e.g.: $10 trial for 1 month then $120 / year)', 'invoicing' ),
333
				$initial,
334
				getpaid_get_subscription_period_label( $trial_period, $trial_interval ),
335
				$recurring,
336
				$period
337
			);
338
339
		}
340
341
		return sprintf(
342
			// translators: $1: is the initial amount, $2: is the trial period, $3: is the recurring amount, $4: is the recurring period, $5: is the bill times
343
			_x( '%1$s trial for %2$s then %3$s / %4$s for %5$s', 'Subscription amount. (e.g.: $10 trial for 1 month then $120 / year for 4 years)', 'invoicing' ),
344
			$initial,
345
			getpaid_get_subscription_period_label( $trial_period, $trial_interval ),
346
			$recurring,
347
			$period,
348
			$bill_times
349
		);
350
351
	}
352
353
	if ( $initial != $recurring ) {
354
355
		if ( empty( $bill_times ) ) {
356
357
			return sprintf(
358
				// translators: $1: is the initial amount, $2: is the recurring amount, $3: is the recurring period
359
				_x( 'Initial payment of %1$s which renews at %2$s / %3$s', 'Subscription amount. (e.g.:Initial payment of $100 which renews at $120 / year)', 'invoicing' ),
360
				$initial,
361
				$recurring,
362
				$period
363
			);
364
365
		}
366
367
		return sprintf(
368
			// translators: $1: is the initial amount, $2: is the recurring amount, $3: is the recurring period, $4: is the bill times
369
			_x( 'Initial payment of %1$s which renews at %2$s / %3$s for %4$s', 'Subscription amount. (e.g.:Initial payment of $100 which renews at $120 / year for 5 years)', 'invoicing' ),
370
			$initial,
371
			$recurring,
372
			$period,
373
			$bill_times_less
374
		);
375
376
	}
377
378
	if ( empty( $bill_times ) ) {
379
380
		return sprintf(
381
			// translators: $1: is the recurring amount, $2: is the recurring period
382
			_x( '%1$s / %2$s', 'Subscription amount. (e.g.: $120 / year)', 'invoicing' ),
383
			$initial,
384
			$period
385
		);
386
387
	}
388
389
	return sprintf(
390
		// translators: $1: is the bill times, $2: is the recurring amount, $3: is the recurring period
391
		_x( '%2$s / %3$s for %1$s', 'Subscription amount. (e.g.: $120 / year for 5 years)', 'invoicing' ),
392
		$bill_times,
393
		$initial,
394
		$period
395
	);
396
397
}
398
399
/**
400
 * Returns an invoice subscription.
401
 *
402
 * @param WPInv_Invoice $invoice
403
 * @return WPInv_Subscription|false
404
 */
405
function getpaid_get_invoice_subscription( $invoice ) {
406
	return getpaid_subscriptions()->get_invoice_subscription( $invoice );
0 ignored issues
show
Bug Best Practice introduced by
The expression return getpaid_subscript..._subscription($invoice) also could return the type boolean which is incompatible with the documented return type WPInv_Subscription|false.
Loading history...
407
}
408
409
/**
410
 * Activates an invoice subscription.
411
 *
412
 * @param WPInv_Invoice $invoice
413
 */
414
function getpaid_activate_invoice_subscription( $invoice ) {
415
	$subscription = getpaid_get_invoice_subscription( $invoice );
416
	if ( is_a( $subscription, 'WPInv_Subscription' ) ) {
417
		$subscription->activate();
418
	}
419
}
420
421
/**
422
 * Returns the subscriptions controller.
423
 *
424
 * @return WPInv_Subscriptions
425
 */
426
function getpaid_subscriptions() {
427
	return getpaid()->get( 'subscriptions' );
428
}
429
430
/**
431
 * Fetchs an invoice subscription from the database.
432
 *
433
 * @since 2.3.0
434
 * @return WPInv_Subscription|bool
435
 */
436
function wpinv_get_invoice_subscription( $invoice ) {
437
438
    // Retrieve the invoice.
439
    $invoice = new WPInv_Invoice( $invoice );
440
441
    // Ensure it is a recurring invoice.
442
    if ( ! $invoice->is_recurring() ) {
443
        return false;
444
    }
445
446
	// Fetch the invoice subscription.
447
	$subscription = getpaid_get_subscriptions(
448
		array(
449
			'invoice_in' => $invoice->is_renewal() ? $invoice->get_parent_id() : $invoice->get_id(),
450
			'number'     => 1,
451
		)
452
	);
453
454
	return empty( $subscription ) ? false : $subscription[0];
455
456
}
457
458
/**
459
 * Construct a cart key based on the billing schedule of a subscription product.
460
 *
461
 * Subscriptions groups products by billing schedule when calculating cart totals, so that gateway fees and other "per invoice" amounts
462
 * can be calculated for each group of items for each renewal. This method constructs a cart key based on the billing schedule
463
 * to allow products on the same billing schedule to be grouped together - free trials are accounted for by
464
 * the trial interval and period of the subscription.
465
 *
466
 * @param GetPaid_Form_Item|WPInv_Item $cart_item
467
 * @return string
468
 */
469
function getpaid_get_recurring_item_key( $cart_item ) {
470
471
	$cart_key     = 'renews_';
472
	$interval     = $cart_item->get_recurring_interval();
473
	$period       = $cart_item->get_recurring_period( true );
474
	$length       = $cart_item->get_recurring_limit() * $interval;
475
	$trial_period = $cart_item->get_trial_period( true );
476
	$trial_length = $cart_item->get_trial_interval();
477
478
	// First start with the billing interval and period
479
	switch ( $interval ) {
480
		case 1:
481
			if ( 'day' == $period ) {
482
				$cart_key .= 'daily';
483
			} else {
484
				$cart_key .= sprintf( '%sly', $period );
485
			}
486
			break;
487
		case 2:
488
			$cart_key .= sprintf( 'every_2nd_%s', $period );
489
			break;
490
		case 3:
491
			$cart_key .= sprintf( 'every_3rd_%s', $period );
492
		    break;
493
		default:
494
			$cart_key .= sprintf( 'every_%dth_%s', $interval, $period );
495
			break;
496
	}
497
498
	// Maybe add the optional maximum billing periods...
499
	if ( $length > 0 ) {
500
		$cart_key .= '_for_';
501
		$cart_key .= sprintf( '%d_%s', $length, $period );
502
		if ( $length > 1 ) {
503
			$cart_key .= 's';
504
		}
505
	}
506
507
	// And an optional free trial.
508
	if ( $cart_item->has_free_trial() ) {
509
		$cart_key .= sprintf( '_after_a_%d_%s_trial', $trial_length, $trial_period );
510
	}
511
512
	return apply_filters( 'getpaid_get_recurring_item_key', $cart_key, $cart_item );
513
}
514
515
/**
516
 * Retrieves subscription groups for all items in an invoice/payment form submission.
517
 *
518
 * @param WPInv_Invoice|GetPaid_Payment_Form_Submission|GetPaid_Payment_Form $invoice
519
 * @return array
520
 */
521
function getpaid_get_subscription_groups( $invoice ) {
522
523
	// Generate subscription groups.
524
	$subscription_groups = array();
525
	foreach ( $invoice->get_items() as $item ) {
526
527
		if ( $item->is_recurring() ) {
528
			$subscription_groups[ getpaid_get_recurring_item_key( $item ) ][] = $item;
529
		}
530
}
531
532
	return $subscription_groups;
533
}
534
535
/**
536
 * Calculate the initial and recurring totals for all subscription products in an invoice/payment form submission.
537
 *
538
 * We group subscriptions by billing schedule to make the display and creation of recurring totals sane,
539
 * when there are multiple subscriptions in the cart.
540
 *
541
 * @param WPInv_Invoice|GetPaid_Payment_Form_Submission|GetPaid_Payment_Form $invoice
542
 * @return array
543
 */
544
function getpaid_calculate_subscription_totals( $invoice ) {
545
546
	// Generate subscription groups.
547
	$subscription_groups = getpaid_get_subscription_groups( $invoice );
548
549
	// Now let's calculate the totals for each group of subscriptions
550
	$subscription_totals = array();
551
552
	foreach ( $subscription_groups as $subscription_key => $items ) {
553
554
		if ( empty( $subscription_totals[ $subscription_key ] ) ) {
555
556
			$subscription_totals[ $subscription_key ] = array(
557
				'initial_total'   => 0,
558
				'recurring_total' => 0,
559
				'items'           => array(),
560
				'trialling'       => false,
561
			);
562
563
		}
564
565
		/**
566
		 * Get the totals of the group.
567
		 * @var GetPaid_Form_Item $item
568
		 */
569
		foreach ( $items as $item ) {
570
571
			$subscription_totals[ $subscription_key ]['items'][ $item->get_id() ]  = $item->prepare_data_for_saving();
572
			$subscription_totals[ $subscription_key ]['item_id']                 = $item->get_id();
573
			$subscription_totals[ $subscription_key ]['period']                  = $item->get_recurring_period( true );
574
			$subscription_totals[ $subscription_key ]['interval']                = $item->get_recurring_interval();
575
			$subscription_totals[ $subscription_key ]['initial_total']          += $item->get_sub_total() + $item->item_tax - $item->item_discount;
576
			$subscription_totals[ $subscription_key ]['recurring_total']        += $item->get_recurring_sub_total() + $item->item_tax - $item->recurring_item_discount;
577
			$subscription_totals[ $subscription_key ]['recurring_limit']         = $item->get_recurring_limit();
578
579
			// Calculate the next renewal date.
580
			$period       = $item->get_recurring_period( true );
581
			$interval     = $item->get_recurring_interval();
582
583
			// If the subscription item has a trial period...
584
			if ( $item->has_free_trial() ) {
585
				$period   = $item->get_trial_period( true );
586
				$interval = $item->get_trial_interval();
587
				$subscription_totals[ $subscription_key ]['trialling'] = $interval . ' ' . $period;
588
			}
589
590
			$subscription_totals[ $subscription_key ]['renews_on'] = date( 'Y-m-d H:i:s', strtotime( "+$interval $period", current_time( 'timestamp' ) ) );
591
592
		}
593
}
594
595
	return apply_filters( 'getpaid_calculate_subscription_totals', $subscription_totals, $invoice );
596
}
597
598
/**
599
 * Checks if we should group a subscription.
600
 *
601
 * @param WPInv_Invoice|GetPaid_Payment_Form_Submission|GetPaid_Payment_Form $invoice
602
 * @return array
603
 */
604
function getpaid_should_group_subscriptions( $invoice ) {
605
606
	$recurring_items = 0;
607
608
	foreach ( $invoice->get_items() as $item ) {
609
610
		if ( $item->is_recurring() ) {
611
			$recurring_items ++;
612
		}
613
}
614
615
	return apply_filters( 'getpaid_should_group_subscriptions', $recurring_items > 1, $invoice );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('ge...ng_items > 1, $invoice) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
616
}
617
618
/**
619
 * Counts the invoices belonging to a subscription.
620
 *
621
 * @param int $parent_invoice_id
622
 * @param int|false $subscription_id
623
 * @return int
624
 */
625
function getpaid_count_subscription_invoices( $parent_invoice_id, $subscription_id = false ) {
626
	global $wpdb;
627
628
	$parent_invoice_id = (int) $parent_invoice_id;
629
630
	if ( false === $subscription_id || ! (bool) get_post_meta( $parent_invoice_id, '_wpinv_subscription_id', true ) ) {
631
632
		return (int) $wpdb->get_var(
633
			$wpdb->prepare(
634
				"SELECT COUNT(ID) FROM $wpdb->posts WHERE ( post_parent=%d OR ID=%d ) AND post_status IN ( 'publish', 'wpi-processing', 'wpi-renewal' )",
635
				$parent_invoice_id,
636
				$parent_invoice_id
637
			)
638
		);
639
640
	}
641
642
	$invoice_ids = $wpdb->get_col(
643
		$wpdb->prepare(
644
			"SELECT ID FROM $wpdb->posts WHERE ( post_parent=%d OR ID=%d ) AND post_status IN ( 'publish', 'wpi-processing', 'wpi-renewal' )",
645
			$parent_invoice_id,
646
			$parent_invoice_id
647
		)
648
	);
649
650
	$count = 0;
651
652
	foreach ( wp_parse_id_list( $invoice_ids ) as $invoice_id ) {
653
654
		if ( $invoice_id == $parent_invoice_id || $subscription_id == (int) get_post_meta( $invoice_id, '_wpinv_subscription_id', true ) ) {
655
			$count ++;
656
			continue;
657
		}
658
}
659
660
	return $count;
661
}
662