Completed
Pull Request — master (#2165)
by Justin
13:48 queued 07:56
created

WPSC_Purchase_Log   F

Complexity

Total Complexity 197

Size/Duplication

Total Lines 1467
Duplicated Lines 0 %

Coupling/Cohesion

Components 11
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 1467
rs 0.6314
c 0
b 0
f 0
wmc 197
lcom 11
cbo 5

65 Methods

Rating   Name   Duplication   Size   Complexity  
A get_column_format() 0 11 3
F fetch_stats() 0 134 16
A convert_date_args() 0 4 1
A get_stats_for_product() 0 15 2
A is_order_status_completed() 0 9 1
A update_cache() 0 3 1
A update_caches() 0 14 2
A delete_cache() 0 5 1
A delete_caches() 0 16 3
C delete() 0 50 8
C __construct() 0 42 8
A set_total_shipping() 0 9 1
A set_gateway_name() 0 13 3
A set_shipping_method_names() 0 16 2
A set_meta_props() 0 10 2
A get_meta() 0 8 3
B fetch() 0 33 5
A prepare_get() 0 3 1
A prepare_get_data() 0 3 1
A prepare_get_meta() 0 3 1
A get_cart_contents() 0 4 1
B get_items() 0 25 6
A get_cart_item() 0 10 2
A get_cart_item_from_product_id() 0 12 3
B update_cart_item() 0 25 3
B remove_cart_item() 0 24 3
B get_gateway_data() 0 44 5
C set() 0 36 8
A get_data_format() 0 9 2
B save() 0 62 6
A save_meta() 0 13 2
A update_downloadable_status() 0 16 2
A have_downloads_locked() 0 8 1
C get_log_by_meta() 0 24 8
A get_next_log_id() 0 14 2
A get_previous_log_id() 0 14 2
A is_transaction_completed() 0 3 1
A can_edit() 0 8 3
A is_order_received() 0 3 1
A is_incomplete_sale() 0 3 1
A is_accepted_payment() 0 3 1
A is_job_dispatched() 0 3 1
A is_closed_order() 0 3 1
A is_payment_declined() 0 3 1
A is_refunded() 0 3 1
A is_refund_pending() 0 3 1
A init_items() 0 9 2
A buyers_name() 0 19 4
A buyers_city() 0 9 3
A buyers_email() 0 9 3
A buyers_address() 0 9 3
C buyers_state_and_postcode() 0 26 7
A buyers_country() 0 9 3
A buyers_phone() 0 9 3
A shipping_name() 0 9 3
A shipping_city() 0 9 3
A shipping_address() 0 9 3
B shipping_state_and_postcode() 0 24 6
A shipping_country() 0 11 3
B payment_method() 0 21 7
A shipping_method() 0 15 3
A discount() 0 8 2
A shipping() 0 13 3
A taxes() 0 9 2
A total_price() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like WPSC_Purchase_Log often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WPSC_Purchase_Log, and based on these observations, apply Extract Interface, too.

1
<?php
2
// by default, expire stats cache after 48 hours
3
// this doesn't have any effect if you're not using APC or memcached
4
5
if ( ! defined( 'WPSC_PURCHASE_LOG_STATS_CACHE_EXPIRE' ) ) {
6
	define( 'WPSC_PURCHASE_LOG_STATS_CACHE_EXPIRE', DAY_IN_SECONDS * 2 );
7
}
8
9
class WPSC_Purchase_Log extends WPSC_Query_Base {
10
	const INCOMPLETE_SALE  		= 1;
11
	const ORDER_RECEIVED  	 	= 2;
12
	const ACCEPTED_PAYMENT		= 3;
13
	const JOB_DISPATCHED   		= 4;
14
	const CLOSED_ORDER     		= 5;
15
	const PAYMENT_DECLINED 		= 6;
16
	const REFUNDED         		= 7;
17
	const REFUND_PENDING   		= 8;
18
	const PARTIALLY_REFUNDED 	= 9;
19
20
	/**
21
	 * Names of column that requires escaping values as strings before being inserted
22
	 * into the database
23
	 *
24
	 * @access private
25
	 * @static
26
	 * @since 3.8.9
27
	 *
28
	 * @var array
29
	 */
30
	private static $string_cols = array(
31
		'sessionid',
32
		'transactid',
33
		'authcode',
34
		'date',
35
		'gateway',
36
		'billing_country',
37
		'shipping_country',
38
		'email_sent',
39
		'stock_adjusted',
40
		'discount_data',
41
		'track_id',
42
		'billing_region',
43
		'shipping_region',
44
		'find_us',
45
		'engrave_text',
46
		'shipping_method',
47
		'shipping_option',
48
		'affiliate_id',
49
		'plugin_version',
50
		'notes',
51
	);
52
53
	/**
54
	 * Names of column that requires escaping values as integers before being inserted
55
	 * into the database
56
	 *
57
	 * @static
58
	 * @since 3.8.9
59
	 * @var array
60
	 */
61
	private static $int_cols = array(
62
		'id',
63
		'statusno',
64
		'processed',
65
		'user_ID',
66
	);
67
68
	/**
69
	 * Names of column that requires escaping values as float before being inserted
70
	 * into the database
71
	 *
72
	 * @static
73
	 * @since 4.0
74
	 * @var array
75
	 */
76
	private static $float_cols = array(
77
		'totalprice',
78
		'base_shipping',
79
		'discount_value',
80
		'wpec_taxes_total',
81
		'wpec_taxes_rate',
82
	);
83
84
	/**
85
	 * Array of metadata
86
	 *
87
	 * @static
88
	 * @since 4.0
89
	 * @var array
90
	 */
91
	private static $metadata = array(
0 ignored issues
show
Unused Code introduced by
The property $metadata is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
92
		'totalprice',
93
		'base_shipping',
94
		'discount_value',
95
		'wpec_taxes_total',
96
		'wpec_taxes_rate',
97
	);
98
99
	private $gateway_data = array();
100
101
	private $is_status_changed = false;
102
	private $previous_status   = false;
103
104
	private $cart_contents = array();
105
	private $cart_ids = array();
106
	private $can_edit = null;
107
108
	/**
109
	 * Contains the constructor arguments. This array is necessary because we will
110
	 * lazy load the DB row into $this->data whenever necessary. Lazy loading is,
111
	 * in turn, necessary because sometimes right after saving a new record, we need
112
	 * to fetch a property with the same object.
113
	 *
114
	 * @access private
115
	 * @since 3.8.9
116
	 *
117
	 * @var array
118
	 */
119
	private $args = array(
120
		'col'   => '',
121
		'value' => '',
122
	);
123
124
   protected $buyers_name = null;
125
   protected $buyers_city = null;
126
   protected $buyers_email = null;
127
   protected $buyers_address = null;
128
   protected $buyers_state_and_postcode = null;
129
   protected $buyers_country = null;
130
   protected $buyers_phone = null;
131
   protected $shipping_name = null;
132
   protected $shipping_address = null;
133
   protected $shipping_city = null;
134
   protected $shipping_state_and_postcode = null;
135
   protected $shipping_country = null;
136
   protected $payment_method = null;
137
   protected $shipping_method = null;
138
139
	/**
140
	 * Get the SQL query format for a column
141
	 *
142
	 * @since 3.8.9
143
	 * @param  string $col Name of the column
144
	 * @return string      Placeholder
145
	 */
146
	private static function get_column_format( $col ) {
147
		if ( in_array( $col, self::$string_cols ) ) {
148
			return '%s';
149
		}
150
151
		if ( in_array( $col, self::$int_cols ) ) {
152
			return '%d';
153
		}
154
155
		return '%f';
156
	}
157
158
	/**
159
	 * Query the purchase log table to get sales and earning stats
160
	 *
161
	 * Accepts an array of arguments:
162
	 * 	- 'ids': IDs of products for which you want to get stats
163
	 * 	- 'products': array of WPSC_Product objects for which you want to get stats
164
	 * 	- 'start' and 'end': the timestamp range (integers) during which you want
165
	 * 	                     to collect the stats.
166
	 * 	                     You can use none, either, or both of them.
167
	 * 	                     Note that the [start, end) interval is a left-closed,
168
	 * 	                     right-open.
169
	 * 	                     E.g.: to get stats from Jan 1st, 2013 to
170
	 * 	                     Dec 31st, 2013 (23:59:59),
171
	 * 	                     set "start" to the timestamp for Jan 1st, 2013, set
172
	 * 	                     "end" to the timestamp for Jan 1st, 2014
173
	 *  - 'order': what to sort the results by, defaults to 'id'.
174
	 *            Can be 'ids', 'sales', 'earnings' or empty string to disable sorting
175
	 *  - 'orderby': how to sort the results, defaults to 'ASC'.
176
	 *              Can be 'ASC', 'DESC' (lowercase is fine too)
177
	 *  - 'per_page': How many items to fetch, defaults to 0, which fetches all results
178
	 *  - 'page': Which page of the results to fetch, defaults to 1.
179
	 *            Has no effect if per_page is set to 0.
180
	 *
181
	 * @since 3.8.14
182
	 * @param  array|string $args Arguments
183
	 * @return array       Array containing 'sales' and 'earnings' stats
184
	 */
185
	public static function fetch_stats( $args ) {
186
		global $wpdb;
187
188
		$defaults = array(
189
			'ids'      => array(), // IDs of the products to be queried
190
			'products' => array(), // Array of WPSC_Products objects
191
			'start'    => 0,       // Int. timestamp, has to be UTC
192
			'end'      => 0,       // Int. timestamp, has to be UTC
193
			'order'    => 'ASC',
194
			'orderby'  => 'id',
195
			'per_page' => 0,
196
			'page'     => 1,
197
		);
198
199
		$args = wp_parse_args( $args, $defaults );
200
201
		// convert more advanced date / time args into "start" and "end"
202
		$args = self::convert_date_args( $args );
203
204
		// build an array of WPSC_Product objects based on 'ids' and 'products' args
205
		$products = array_merge(
206
			$args['products'],
207
			array_map( array( 'WPSC_Product', 'get_instance' ), $args['ids'] )
208
		);
209
210
		// eliminate duplicates (Note: usage of this requires PHP 5.2.9)
211
		$products = array_unique( $products, SORT_REGULAR );
212
213
		if ( empty( $products ) ) {
214
			return null;
215
		}
216
217
		$needs_fetching = array();
218
219
		$stats = array(
220
			'sales'    => 0,
221
			'earnings' => 0,
222
		);
223
224
		// if we have date restriction, that means we won't be able to rely upon
225
		// individual stats cache inside WPSC_Product objects
226
		$has_date_restriction = ! ( empty( $args['start'] ) && empty( $args['end'] ) );
227
228
		// if we do NOT have date restriction, find out which WPSC_Product object
229
		// has stats cache, and which don't
230
		if ( ! $has_date_restriction ) {
231
			foreach ( $products as $product ) {
232
				// store the ID if this product doesn't have a stats cache yet
233
				if ( $product->post->_wpsc_stats === '' ) {
234
					$needs_fetching[] = $product->post->ID;
235
				} else {
236
237
					// tally up the sales and earnings if this one has cache already
238
					$prod_meta = get_post_meta( $product->post->ID, '_wpsc_stats', true );
239
240
					if ( isset( $prod_meta['sales'] ) && isset( $prod_meta['earnings'] ) ) {
241
						$stats['sales']    += $prod_meta['sales'];
242
						$stats['earnings'] += $prod_meta['earnings'];
243
					}
244
					$needs_fetching[]   = $product->post->ID;
245
				}
246
			}
247
		}
248
249
		// never hurts to make sure
250
		$needs_fetching = array_map( 'absint', $needs_fetching );
251
252
		// pagination arguments
253
		$limit = '';
254
255
		if ( ! empty( $args['per_page'] ) ) {
256
			$offset = ( $args['page'] - 1 ) * $args['per_page'];
257
			$limit = "LIMIT " . absint( $args['per_page'] ) . " OFFSET " . absint( $offset );
258
		}
259
260
		// sorting
261
		$order = '';
262
263
		if ( ! empty( $args['orderby'] ) )
264
			$order = "ORDER BY " . esc_sql( $args['orderby'] ) . " " . esc_sql( $args['order'] );
265
266
		// date
267
		$where = "WHERE p.processed IN (3, 4, 5)";
268
269
		if ( $has_date_restriction ) {
270
			// start date equal or larger than $args['start']
271
			if ( ! empty( $args['start'] ) )
272
				$where .= " AND CAST(p.date AS UNSIGNED) >= " . absint( $args['start'] );
273
274
			// end date less than $args['end'].
275
			// the "<" sign is not a typo, such upper limit makes it easier for
276
			// people to specify range.
277
			// E.g.: [1/1/2013 - 1/1/2014) rather than:
278
			//       [1/1/2013 - 31/12/2013 23:59:59]
279
			if ( ! empty( $args['end'] ) )
280
				$where .= " AND CAST(p.date AS UNSIGNED) < " . absint( $args['end'] );
281
		}
282
283
		// assemble the SQL query
284
		$sql = "
285
			SELECT cc.prodid AS id, SUM(cc.quantity) AS sales, SUM(cc.quantity * cc.price) AS earnings
286
			FROM $wpdb->wpsc_purchase_logs AS p
287
			INNER JOIN
288
				$wpdb->wpsc_cart_contents AS cc
289
				ON p.id = cc.purchaseid AND cc.prodid IN (" . implode( ', ', $needs_fetching ) . ")
290
			{$where}
291
			GROUP BY cc.prodid
292
			{$order}
293
			{$limit}
294
		";
295
296
		// if the result is cached, don't bother querying the database
297
		$cache_key = md5( $sql );
298
		$results   = wp_cache_get( $cache_key, 'wpsc_purchase_logs_stats' );
299
300
		if ( false === $results ) {
301
			$results = $wpdb->get_results( $sql );
302
			wp_cache_set( $cache_key, $results, 'wpsc_purchase_logs_stats', WPSC_PURCHASE_LOG_STATS_CACHE_EXPIRE );
303
		}
304
305
		// tally up the sales and earnings from the query results
306
		foreach ( $results as $row ) {
307
			if ( ! $has_date_restriction ) {
308
				$product           = WPSC_Product::get_instance( $row->id );
309
				$product->sales    = $row->sales;
310
				$product->earnings = $row->earnings;
311
			}
312
313
			$stats['sales']    += $row->sales;
314
			$stats['earnings'] += $row->earnings;
315
		}
316
317
		return $stats;
318
	}
319
320
	/**
321
	 * Convert advanced date/time arguments like year, month, day, 'ago' etc.
322
	 * into basic arguments which are "start" and "end".
323
	 *
324
	 * @since  3.8.14
325
	 * @param  array $args Arguments
326
	 * @return array       Arguments after converting
327
	 */
328
	private static function convert_date_args( $args ) {
329
		// TODO: Implement this
330
		return $args;
331
	}
332
333
	/**
334
	 * Get overall sales and earning stats for just one product
335
	 *
336
	 * @since 3.8.14
337
	 * @param  int $id ID of the product
338
	 * @return array   Array containing 'sales' and 'earnings' stats
339
	 */
340
	public static function get_stats_for_product( $id, $args = '' ) {
341
342
		$product = WPSC_Product::get_instance( $id );
343
344
		// if this product has variations
345
		if ( $product->has_variations ) {
346
			// get total stats of variations
347
			$args['products'] = $product->variations;
348
		} else {
349
			// otherwise, get stats of only this product
350
			$args['products'] = array( $product );
351
		}
352
353
		return self::fetch_stats( $args );
354
	}
355
356
	/**
357
	 * Check whether the status code indicates a completed status
358
	 *
359
	 * @since 3.8.13
360
	 * @param int  $status Status code
361
	 * @return boolean
362
	 */
363
	public static function is_order_status_completed( $status ) {
364
		$completed_status = apply_filters( 'wpsc_order_status_completed', array(
365
			self::ACCEPTED_PAYMENT,
366
			self::JOB_DISPATCHED,
367
			self::CLOSED_ORDER,
368
		) );
369
370
		return in_array( $status, $completed_status );
371
	}
372
373
	/**
374
	 * Update cache of the passed log object
375
	 *
376
	 * @access public
377
	 * @static
378
	 * @since 3.8.9
379
	 *
380
	 * @param WPSC_Purchase_Log $log The log object that you want to store into cache
381
	 * @return void
382
	 */
383
	public static function update_cache( &$log ) {
384
		return $log->update_caches();
385
	}
386
387
	/**
388
	 * Update caches.
389
	 *
390
	 * @access public
391
	 * @static
392
	 * @since 4.0
393
	 *
394
	 * @return void
395
	 */
396
	public function update_caches() {
397
398
		// wpsc_purchase_logs stores the data array, while wpsc_purchase_logs_sessionid stores the
399
		// log id that's associated with the sessionid
400
		$id = $this->get( 'id' );
401
		wp_cache_set( $id, $this->data, 'wpsc_purchase_logs' );
402
403
		if ( $sessionid = $this->get( 'sessionid' ) ) {
404
			wp_cache_set( $sessionid, $id, 'wpsc_purchase_logs_sessionid' );
405
		}
406
407
		wp_cache_set( $id, $this->cart_contents, 'wpsc_purchase_log_cart_contents' );
408
		do_action( 'wpsc_purchase_log_update_cache', $this );
409
	}
410
411
	/**
412
	 * Deletes cache of a log (either by using the log ID or sessionid)
413
	 *
414
	 * @access public
415
	 * @static
416
	 * @since 3.8.9
417
	 *
418
	 * @param string $value The value to query
419
	 * @param string $col Optional. Defaults to 'id'. Whether to delete cache by using
420
	 *                    a purchase log ID or sessionid
421
	 * @return void
422
	 */
423
	public static function delete_cache( $value, $col = 'id' ) {
424
		// this will pull from the old cache, so no worries there
425
		$log = new WPSC_Purchase_Log( $value, $col );
426
		$log->delete_caches( $value, $col );
427
	}
428
429
	/**
430
	 * Deletes caches.
431
	 *
432
	 * @access public
433
	 * @static
434
	 * @since 4.0
435
	 *
436
	 * @param string|null $value Optional (left for back-compatibility). The value which was queried.
437
	 * @param string|null $col   Optional (left for back-compatibility). The column used as the identifier.
438
	 *
439
	 * @return void
440
	 */
441
	public function delete_caches( $value = null, $col = null ) {
442
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_logs' );
443
		wp_cache_delete( $this->get( 'sessionid' ), 'wpsc_purchase_logs_sessionid' );
444
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_log_cart_contents' );
445
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_meta' );
446
447
		if ( null === $value ) {
448
			$value = $this->args['value'];
449
		}
450
451
		if ( null === $col ) {
452
			$col = $this->args['col'];
453
		}
454
455
		do_action( 'wpsc_purchase_log_delete_cache', $this, $value, $col );
456
	}
457
458
	/**
459
	 * Deletes a log from the database.
460
	 *
461
	 * @access  public
462
	 * @since   3.8.9
463
	 *
464
	 * @uses  $wpdb                              Global database instance.
465
	 * @uses  wpsc_is_store_admin()              Check user has admin capabilities.
466
	 * @uses  WPSC_Purchase_Log::delete_cache()  Delete purchaselog cache.
467
	 * @uses  WPSC_Claimed_Stock                 Claimed Stock class.
468
	 *
469
	 * @param   string   $log_id   ID of the log.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $log_id not be false|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
470
	 * @return  boolean            Deleted successfully.
471
	 */
472
	public function delete( $log_id = false ) {
473
474
		global $wpdb;
475
476
		if ( ! ( isset( $this ) && get_class( $this ) == __CLASS__ ) ) {
477
			_wpsc_doing_it_wrong( 'WPSC_Purchase_Log::delete', __( 'WPSC_Purchase_Log::delete() is no longer a static method and should not be called statically.', 'wp-e-commerce' ), '3.9.0' );
478
		}
479
480
		if ( false !== $log_id ) {
481
			_wpsc_deprecated_argument( __FUNCTION__, '3.9.0', 'The $log_id param is not used. You must first create an instance of WPSC_Purchase_Log before calling this method.' );
482
		}
483
484
		if ( ! wpsc_is_store_admin() ) {
485
			return false;
486
		}
487
488
		$log_id = $this->get( 'id' );
489
490
		if ( $log_id > 0 ) {
491
492
			do_action( 'wpsc_purchase_log_before_delete', $log_id );
493
494
			$this->delete_caches();
495
496
			// Delete claimed stock
497
			$purchlog_status = $wpdb->get_var( $wpdb->prepare( "SELECT `processed` FROM `" . WPSC_TABLE_PURCHASE_LOGS . "` WHERE `id`= %d", $log_id ) );
498
			if ( $purchlog_status == WPSC_Purchase_Log::CLOSED_ORDER || $purchlog_status == WPSC_Purchase_Log::INCOMPLETE_SALE ) {
499
				$claimed_query = new WPSC_Claimed_Stock( array(
500
					'cart_id'        => $log_id,
501
					'cart_submitted' => 1
502
				) );
503
				$claimed_query->clear_claimed_stock( 0 );
504
			}
505
506
			// Delete cart content, submitted data, then purchase log
507
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_CART_CONTENTS . "` WHERE `purchaseid` = %d", $log_id ) );
508
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_SUBMITTED_FORM_DATA . "` WHERE `log_id` IN (%d)", $log_id ) );
509
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_PURCHASE_LOGS . "` WHERE `id` = %d LIMIT 1", $log_id ) );
510
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_PURCHASE_META . "` WHERE `wpsc_purchase_id` = %d", $log_id ) );
511
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_DOWNLOAD_STATUS . "` WHERE `purchid` = %d ", $log_id ) );
512
513
			do_action( 'wpsc_purchase_log_delete', $log_id );
514
515
			return true;
516
517
		}
518
519
		return false;
520
521
	}
522
523
	/**
524
	 * Constructor of the purchase log object. If no argument is passed, this simply
525
	 * create a new empty object. Otherwise, this will get the purchase log from the
526
	 * DB either by using purchase log id or sessionid (specified by the 2nd argument).
527
	 *
528
	 * Eg:
529
	 *
530
	 * // get purchase log with ID number 23
531
	 * $log = new WPSC_Purchase_Log( 23 );
532
	 *
533
	 * // get purchase log with sessionid "asdf"
534
	 * $log = new WPSC_Purchase_Log( 'asdf', 'sessionid' )
535
	 *
536
	 * @access public
537
	 * @since 3.8.9
538
	 *
539
	 * @param string $value Optional. Defaults to false.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $value not be false|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
540
	 * @param string $col Optional. Defaults to 'id'.
541
	 */
542
	public function __construct( $value = false, $col = 'id' ) {
543
		if ( false === $value ) {
544
			return;
545
		}
546
547
		if ( is_array( $value ) ) {
548
			$this->set( $value );
549
			return;
550
		}
551
552
		global $wpdb;
553
554
		if ( ! in_array( $col, array( 'id', 'sessionid' ) ) ) {
555
			return;
556
		}
557
558
		// store the constructor args into an array so that later we can lazy load the data
559
		$this->args = array(
560
			'col'   => $col,
561
			'value' => $value,
562
		);
563
564
		// if the sessionid is in cache, pull out the id
565
		if ( $col == 'sessionid'  && $id = wp_cache_get( $value, 'wpsc_purchase_logs_sessionid' ) ) {
566
				$col = 'id';
567
				$value = $id;
568
		}
569
570
		// if the id is specified, try to get from cache
571
		if ( $col == 'id' ) {
572
			$this->data = wp_cache_get( $value, 'wpsc_purchase_logs' );
573
			$this->cart_contents = wp_cache_get( $value, 'wpsc_purchase_log_cart_contents' );
574
		}
575
576
		// cache exists
577
		if ( $this->data ) {
578
			$this->set_meta_props();
579
			$this->fetched = true;
0 ignored issues
show
Documentation Bug introduced by
The property $fetched was declared of type string, but true is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
580
			$this->exists  = true;
0 ignored issues
show
Documentation Bug introduced by
The property $exists was declared of type string, but true is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
581
			return;
582
		}
583
	}
584
585
	private function set_total_shipping() {
586
587
		$base_shipping  = $this->get( 'base_shipping' );
588
		$item_shipping  = wp_list_pluck( $this->get_items(), 'pnp' );
589
590
		$this->meta_data['total_shipping'] = $base_shipping + array_sum( $item_shipping );
591
592
		return $this->meta_data['total_shipping'];
593
	}
594
595
	private function set_gateway_name() {
596
		global $wpsc_gateways;
597
		$gateway = $this->get( 'gateway' );
598
		$gateway_name = $gateway;
599
600
		if( 'wpsc_merchant_testmode' == $gateway )
601
			$gateway_name = __( 'Manual Payment', 'wp-e-commerce' );
602
		elseif ( isset( $wpsc_gateways[$gateway] ) )
603
			$gateway_name = $wpsc_gateways[$gateway]['name'];
604
605
		$this->meta_data['gateway_name'] = $gateway_name;
606
		return $this->meta_data['gateway_name'];
607
	}
608
609
	private function set_shipping_method_names() {
610
		global $wpsc_shipping_modules;
611
612
		$shipping_method = $this->get( 'shipping_method' );
613
		$shipping_option = $this->get( 'shipping_option' );
614
		$shipping_method_name = $shipping_method;
615
		$shipping_option_name = $shipping_option;
616
617
		if ( ! empty ( $wpsc_shipping_modules[$shipping_method] ) ) {
0 ignored issues
show
Coding Style introduced by
Space before opening parenthesis of function call prohibited
Loading history...
618
			$shipping_class = $wpsc_shipping_modules[$shipping_method];
619
			$shipping_method_name = $shipping_class->name;
620
		}
621
622
		$this->meta_data['shipping_method_name'] = $shipping_method_name;
623
		$this->meta_data['shipping_option_name'] = $shipping_option_name;
624
	}
625
626
	private function set_meta_props() {
627
628
		foreach ( wpsc_get_purchase_custom( $this->get( 'id' ) ) as $key => $value  ) {
629
			$this->meta_data[ $key ] = wpsc_get_purchase_meta( $this->get( 'id' ), $key, true );
630
		}
631
632
		$this->set_total_shipping();
633
		$this->set_gateway_name();
634
		$this->set_shipping_method_names();
635
	}
636
637
public function get_meta() {
638
639
		if ( empty( $this->data ) || empty( $this->meta_data ) ) {
640
			$this->fetch();
641
		}
642
643
		return (array) apply_filters( 'wpsc_purchase_log_meta_data', $this->meta_data );
644
	}
645
646
	/**
647
	 * Fetches the actual record from the database
648
	 *
649
	 * @access protected
650
	 * @since 3.8.9
651
	 *
652
	 * @return void
653
	 */
654
	protected function fetch() {
655
		global $wpdb;
656
657
		if ( $this->fetched ) {
658
			return;
659
		}
660
661
		// If $this->args is not set yet, it means the object contains a new unsaved
662
		// row so we don't need to fetch from DB
663
		if ( ! $this->args['col'] || ! $this->args['value'] ) {
664
			return;
665
		}
666
667
		extract( $this->args );
0 ignored issues
show
introduced by
extract() usage is highly discouraged, due to the complexity and unintended issues it might cause.
Loading history...
668
669
		$format = self::get_column_format( $col );
670
		$sql    = $wpdb->prepare( "SELECT * FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE {$col} = {$format}", $value );
671
672
		$this->exists = false;
0 ignored issues
show
Documentation Bug introduced by
The property $exists was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
673
674
		if ( $data = $wpdb->get_row( $sql, ARRAY_A ) ) {
675
			$this->exists        = true;
0 ignored issues
show
Documentation Bug introduced by
The property $exists was declared of type string, but true is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
676
			$this->data          = apply_filters( 'wpsc_purchase_log_data', $data );
677
			$this->cart_contents = $this->get_items();
678
679
			$this->set_meta_props();
680
			$this->update_caches( $this );
0 ignored issues
show
Unused Code introduced by
The call to WPSC_Purchase_Log::update_caches() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
681
		}
682
683
		do_action( 'wpsc_purchase_log_fetched', $this );
684
685
		$this->fetched = true;
0 ignored issues
show
Documentation Bug introduced by
The property $fetched was declared of type string, but true is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
686
	}
687
688
	/**
689
	 * Prepares the return value for get() (apply_filters, etc).
690
	 *
691
	 * @access protected
692
	 * @since  4.0
693
	 *
694
	 * @param  mixed  $value Value fetched
695
	 * @param  string $key   Key for $data.
696
	 *
697
	 * @return mixed
698
	 */
699
	protected function prepare_get( $value, $key ) {
700
		return apply_filters( 'wpsc_purchase_log_get_property', $value, $key, $this );
701
	}
702
703
	/**
704
	 * Prepares the return value for get_data() (apply_filters, etc).
705
	 *
706
	 * @access protected
707
	 * @since  4.0
708
	 *
709
	 * @return mixed
710
	 */
711
	protected function prepare_get_data() {
712
		return apply_filters( 'wpsc_purchase_log_get_data', $this->data, $this );
713
	}
714
715
	/**
716
	 * Prepares the return value for get_meta() (apply_filters, etc).
717
	 *
718
	 * @access protected
719
	 * @since  4.0
720
	 *
721
	 * @return mixed
722
	 */
723
	protected function prepare_get_meta() {
724
		return (array) apply_filters( 'wpsc_purchase_log_meta_data', $this->meta_data );
725
	}
726
727
	public function get_cart_contents() {
728
		_wpsc_doing_it_wrong( __FUNCTION__, __( 'This function has been deprecated in favor of the get_items() method.', 'wp-e-commerce' ), '4.0' );
729
		return $this->get_items();
730
	}
731
732
	public function get_items() {
733
		global $wpdb;
734
735
		if ( ! empty( $this->cart_contents ) && $this->fetched ) {
736
			return $this->cart_contents;
737
		}
738
739
		$id = $this->get( 'id' );
740
741
		// Bail if we don't have a log object yet (no id).
742
		if ( empty( $id ) ) {
743
			return $this->cart_contents;
744
		}
745
746
		$sql = $wpdb->prepare( "SELECT * FROM " . WPSC_TABLE_CART_CONTENTS . " WHERE purchaseid = %d", $id );
747
		$this->cart_contents = $wpdb->get_results( $sql );
748
749
		if ( is_array( $this->cart_contents ) ) {
750
			foreach ( $this->cart_contents as $index => $item ) {
751
				$this->cart_ids[ absint( $item->id ) ] = $index;
752
			}
753
		}
754
755
		return $this->cart_contents;
756
	}
757
758
759
760
	public function get_cart_item( $item_id ) {
761
		$item_id = absint( $item_id );
762
		$cart    = $this->get_items();
763
764
		if ( isset( $this->cart_ids[ $item_id ] ) ) {
765
			return $cart[ $this->cart_ids[ $item_id ] ];
766
		}
767
768
		return false;
769
	}
770
771
	public function get_cart_item_from_product_id( $product_id ) {
772
		$product_id = absint( $product_id );
773
		$cart       = $this->get_items();
774
775
		foreach ( $cart as $item ) {
776
			if ( $product_id === absint( $item->prodid ) ) {
777
				return $item;
778
			}
779
		}
780
781
		return false;
782
	}
783
784
	public function update_cart_item( $item_id, $data ) {
785
		global $wpdb;
786
787
		$item_id = absint( $item_id );
788
		$item = $this->get_cart_item( $item_id );
789
790
		if ( $item ) {
791
			do_action( 'wpsc_purchase_log_before_update_cart_item', $item_id );
792
793
			$data = wp_unslash( $data );
794
			$result = $wpdb->update( WPSC_TABLE_CART_CONTENTS, $data, array( 'id' => $item_id  ) );
795
796
			if ( $result ) {
797
798
				$this->cart_contents = array();
799
				$this->get_cart_item( $item_id );
800
801
				do_action( 'wpsc_purchase_log_update_cart_item', $item_id );
802
			}
803
804
			return $result;
805
		}
806
807
		return false;
808
	}
809
810
	public function remove_cart_item( $item_id ) {
811
		global $wpdb;
812
813
		$item_id = absint( $item_id );
814
		$item = $this->get_cart_item( $item_id );
815
816
		if ( $item ) {
817
			do_action( 'wpsc_purchase_log_before_remove_cart_item', $item_id );
818
819
			$result = $wpdb->delete( WPSC_TABLE_CART_CONTENTS, array( 'id' => $item_id ) );
820
821
			if ( $result ) {
822
823
				unset( $this->cart_contents[ $this->cart_ids[ $item_id ] ] );
824
				unset( $this->cart_ids[ $item_id ] );
825
826
				do_action( 'wpsc_purchase_log_remove_cart_item', $item_id );
827
			}
828
829
			return $result;
830
		}
831
832
		return false;
833
	}
834
835
	public function get_gateway_data( $from_currency = false, $to_currency = false ) {
836
		if ( ! $this->exists() ) {
837
			return array();
838
		}
839
840
		$subtotal = 0;
841
		$shipping = wpsc_convert_currency( (float) $this->get( 'base_shipping' ), $from_currency, $to_currency );
842
		$items    = array();
843
844
		$this->gateway_data = array(
845
			'amount'  => wpsc_convert_currency( $this->get( 'totalprice' ), $from_currency, $to_currency ),
846
			'invoice' => $this->get( 'sessionid' ),
847
			'tax'     => wpsc_convert_currency( $this->get( 'wpec_taxes_total' ), $from_currency, $to_currency ),
848
		);
849
850
		foreach ( $this->cart_contents as $item ) {
851
			$item_price = wpsc_convert_currency( $item->price, $from_currency, $to_currency );
852
			$items[] = array(
853
				'name'     => $item->name,
854
				'amount'   => $item_price,
855
				'tax'      => wpsc_convert_currency( $item->tax_charged, $from_currency, $to_currency ),
856
				'quantity' => $item->quantity,
857
			);
858
			$subtotal += $item_price * $item->quantity;
859
			$shipping += wpsc_convert_currency( $item->pnp, $from_currency, $to_currency );
860
		}
861
862
		$this->gateway_data['discount'] = wpsc_convert_currency( (float) $this->get( 'discount_value' ), $from_currency, $to_currency );
863
864
		$this->gateway_data['items'] = $items;
865
		$this->gateway_data['shipping'] = $shipping;
866
		$this->gateway_data['subtotal'] = $subtotal;
867
868
		if ( $from_currency ) {
869
			// adjust total amount in case there's slight decimal error
870
			$total = $subtotal + $shipping + $this->gateway_data['tax'] - $this->gateway_data['discount'];
871
			if ( $this->gateway_data['amount'] != $total ) {
872
				$this->gateway_data['amount'] = $total;
873
			}
874
		}
875
876
		$this->gateway_data = apply_filters( 'wpsc_purchase_log_gateway_data', $this->gateway_data, $this->get_data() );
877
		return $this->gateway_data;
878
	}
879
880
	/**
881
	 * Sets a property to a certain value. This function accepts a key and a value
882
	 * as arguments, or an associative array containing key value pairs.
883
	 *
884
	 * Loops through data, comparing against database, and saves as meta if not found in purchase log table.
885
	 *
886
	 * @access public
887
	 * @since 3.8.9
888
	 *
889
	 * @param mixed $key Name of the property (column), or an array containing key
890
	 *                   value pairs
891
	 * @param string|int $value Optional. Defaults to false. In case $key is a string,
0 ignored issues
show
Documentation introduced by
Should the type for parameter $value not be string|integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
892
	 *                          this should be specified.
893
	 * @return WPSC_Purchase_Log The current object (for method chaining)
894
	 */
895
	public function set( $key, $value = null ) {
896
		if ( is_array( $key ) ) {
897
			$properties = $key;
898
		} else {
899
			if ( is_null( $value ) ) {
900
				return $this;
901
			}
902
903
			$properties = array( $key => $value );
904
		}
905
906
		$properties = apply_filters( 'wpsc_purchase_log_set_properties', $properties, $this );
907
908
		if ( array_key_exists( 'processed', $properties ) ) {
909
			$this->previous_status = $this->get( 'processed' );
910
911
			if ( $properties['processed'] != $this->previous_status ) {
912
				$this->is_status_changed = true;
913
			}
914
		}
915
916
		if ( ! is_array( $this->data ) ) {
917
			$this->data = array();
918
		}
919
920
		foreach ( $properties as $key => $value ) {
921
			if ( ! in_array( $key, array_merge( self::$string_cols, self::$int_cols, self::$float_cols ) ) ) {
922
				$this->meta_data[ $key ] = $value;
923
				unset( $properties[ $key ] );
924
			}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
925
926
		}
927
928
		$this->data = array_merge( $this->data, $properties );
929
		return $this;
930
	}
931
932
	/**
933
	 * Returns an array containing the parameter format so that this can be used in
934
	 * $wpdb methods (update, insert etc.)
935
	 *
936
	 * @access private
937
	 * @since 3.8.9
938
	 *
939
	 * @param array $data
940
	 * @return array
941
	 */
942
	private function get_data_format( $data ) {
943
		$format = array();
944
945
		foreach ( $data as $key => $value ) {
946
			$format[] = self::get_column_format( $key );
947
		}
948
949
		return $format;
950
	}
951
952
	/**
953
	 * Saves the purchase log back to the database
954
	 *
955
	 * @access public
956
	 * @since 3.8.9
957
	 *
958
	 * @return void
959
	 */
960
	public function save() {
961
		global $wpdb;
962
963
		do_action( 'wpsc_purchase_log_pre_save', $this );
964
965
		// $this->args['col'] is empty when the record is new and needs to
966
		// be inserted. Otherwise, it means we're performing an update
967
		$where_col = $this->args['col'];
968
969
		$result = false;
970
971
		if ( $where_col ) {
972
			$where_val = $this->args['value'];
973
			$where_format = self::get_column_format( $where_col );
974
			do_action( 'wpsc_purchase_log_pre_update', $this );
975
			self::delete_cache( $where_val, $where_col );
976
			$data = apply_filters( 'wpsc_purchase_log_update_data', $this->data );
977
			$format = $this->get_data_format( $data );
978
			$result = $wpdb->update( WPSC_TABLE_PURCHASE_LOGS, $data, array( $where_col => $where_val ), $format, array( $where_format ) );
979
			do_action( 'wpsc_purchase_log_update', $this );
980
		} else {
981
			do_action( 'wpsc_purchase_log_pre_insert', $this );
982
			$data = apply_filters( 'wpsc_purchase_log_insert_data', $this->data );
983
			$format = $this->get_data_format( $data );
984
			$result = $wpdb->insert( WPSC_TABLE_PURCHASE_LOGS, $data, $format );
985
986
			if ( $result ) {
987
				$this->set( 'id', $wpdb->insert_id );
988
989
				// set $this->args so that properties can be lazy loaded right after
990
				// the row is inserted into the db
991
				$this->args = array(
992
					'col'   => 'id',
993
					'value' => $this->get( 'id' ),
994
				);
995
			}
996
997
			do_action( 'wpsc_purchase_log_insert', $this );
998
		}
999
1000
		if ( $this->is_status_changed ) {
1001
1002
			if ( $this->is_transaction_completed() ) {
1003
				$this->update_downloadable_status();
1004
			}
1005
1006
			$current_status          = $this->get( 'processed' );
1007
			$previous_status         = $this->previous_status;
1008
			$this->previous_status   = $current_status;
1009
			$this->is_status_changed = false;
1010
1011
			do_action( 'wpsc_update_purchase_log_status', $this->get( 'id' ), $current_status, $previous_status, $this );
1012
		}
1013
1014
		if ( ! empty( $this->meta_data ) ) {
1015
			$this->save_meta();
1016
		}
1017
1018
		do_action( 'wpsc_purchase_log_save', $this );
1019
1020
		return $result;
1021
	}
1022
1023
	/**
1024
	 * Save meta data for purchase log, if any was set via set().
1025
	 *
1026
	 * @access public
1027
	 * @since  4.0
1028
	 *
1029
	 * @return WPSC_Query_Base  The current object (for method chaining)
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WPSC_Purchase_Log.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1030
	 */
1031
	public function save_meta() {
1032
		do_action( 'wpsc_purchase_log_pre_save_meta', $this );
1033
1034
		$meta = $this->get_meta();
1035
1036
		foreach ( $meta as $key => $value ) {
1037
			wpsc_update_purchase_meta( $this->get( 'id' ), $key, $value );
1038
		}
1039
1040
		do_action( 'wpsc_purchase_log_save_meta', $this );
1041
1042
		return $this;
1043
	}
1044
1045
	private function update_downloadable_status() {
1046
		global $wpdb;
1047
1048
		foreach ( $this->get_items() as $item ) {
1049
			$wpdb->update(
1050
				WPSC_TABLE_DOWNLOAD_STATUS,
1051
				array(
1052
					'active' => '1'
1053
				),
1054
				array(
1055
					'cartid'  => $item->id,
1056
					'purchid' => $this->get( 'id' ),
1057
				)
1058
			);
1059
		}
1060
	}
1061
1062
	public function have_downloads_locked() {
1063
		global $wpdb;
1064
1065
		$sql = $wpdb->prepare( "SELECT `ip_number` FROM `" . WPSC_TABLE_DOWNLOAD_STATUS . "` WHERE `purchid` = %d ", $this->get( 'id' ) );
1066
		$ip_number = $wpdb->get_var( $sql );
1067
1068
		return $ip_number;
1069
	}
1070
1071
	/**
1072
	 * Adds ability to retrieve a purchase log by a meta key or value.
1073
	 *
1074
	 * @since  4.0
1075
	 *
1076
	 * @param  string $key   Meta key. Optional.
1077
	 * @param  string $value Meta value. Optional.
1078
	 *
1079
	 * @return false|WPSC_Purchase_Log  False if no log is found or meta key and value are both not provided. WPSC_Purchase_Log object if found.
1080
	 */
1081
	public static function get_log_by_meta( $key = '', $value = '' ) {
1082
1083
		if ( empty( $key ) && empty( $value ) ) {
1084
			return false;
1085
		}
1086
1087
		global $wpdb;
1088
1089
		if ( ! empty( $key ) && empty( $value ) ) {
1090
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_key = %s', $key );
1091
		} else if ( empty( $key ) && ! empty( $value ) ) {
1092
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_value = %s', $value );
1093
		} else {
1094
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_key = %s AND meta_value = %s', $key, $value );
1095
		}
1096
1097
		$id = $wpdb->get_var( $sql );
1098
1099
		if ( $id ) {
1100
			return new WPSC_Purchase_Log( $id );
1101
		} else {
1102
			return false;
1103
		}
1104
	}
1105
1106
	public function get_next_log_id() {
1107
		if ( ! $this->exists() ) {
1108
			return false;
1109
		}
1110
1111
		global $wpdb;
1112
1113
		$sql = $wpdb->prepare(
1114
			"SELECT MIN(id) FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE id > %d",
1115
			$this->get( 'id' )
1116
		);
1117
1118
		return $wpdb->get_var( $sql );
1119
	}
1120
1121
	public function get_previous_log_id() {
1122
		if ( ! $this->exists() ) {
1123
			return false;
1124
		}
1125
1126
		global $wpdb;
1127
1128
		$sql = $wpdb->prepare(
1129
			"SELECT MAX(id) FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE id < %d",
1130
			$this->get( 'id' )
1131
		);
1132
1133
		return $wpdb->get_var( $sql );
1134
	}
1135
1136
	public function is_transaction_completed() {
1137
		return WPSC_Purchase_Log::is_order_status_completed( $this->get( 'processed' ) );
1138
	}
1139
1140
	public function can_edit() {
1141
		if ( null === $this->can_edit ) {
1142
			$can_edit = current_user_can( 'edit_others_posts' ) && ! $this->is_transaction_completed();
1143
			$this->can_edit = apply_filters( 'wpsc_can_edit_order', $can_edit, $this );
1144
		}
1145
1146
		return $this->can_edit;
1147
	}
1148
1149
	public function is_order_received() {
1150
		return $this->get( 'processed' ) == self::ORDER_RECEIVED;
1151
	}
1152
1153
	public function is_incomplete_sale() {
1154
		return $this->get( 'processed' ) == self::INCOMPLETE_SALE;
1155
	}
1156
1157
	public function is_accepted_payment() {
1158
		return $this->get( 'processed' ) == self::ACCEPTED_PAYMENT;
1159
	}
1160
1161
	public function is_job_dispatched() {
1162
		return $this->get( 'processed' ) == self::JOB_DISPATCHED;
1163
	}
1164
1165
	public function is_closed_order() {
1166
		return $this->get( 'processed' ) == self::CLOSED_ORDER;
1167
	}
1168
1169
	public function is_payment_declined() {
1170
		return $this->get( 'processed' ) == self::PAYMENT_DECLINED;
1171
	}
1172
1173
	public function is_refunded() {
1174
		return $this->get( 'processed' ) == self::REFUNDED;
1175
	}
1176
1177
	public function is_refund_pending() {
1178
		return $this->get( 'processed' ) == self::REFUND_PENDING;
1179
	}
1180
1181
	/*
1182
	 * Utility methods using the $purchlogitem global.. Global usage to be replaced in the future.
1183
	 *
1184
	 * TODO: seriously get rid of all these badly coded purchaselogs.functions.php functions
1185
	 * and wpsc_purchaselogs/wpsc_purchaselogs_items classes.
1186
	 */
1187
1188
	/**
1189
	 * Init the purchase log items for this purchase log.
1190
	 *
1191
	 * @since  4.0
1192
	 *
1193
	 * @return wpsc_purchaselogs_items|false The purhchase log item object or false.
1194
	 */
1195
	public function init_items() {
1196
		global $purchlogitem;
1197
		if ( ! $this->exists() ) {
1198
			return false;
1199
		}
1200
1201
		$form_data_obj = new WPSC_Checkout_Form_Data( $this->get( 'id' ) );
1202
		$purchlogitem = new wpsc_purchaselogs_items( $this->get( 'id' ), $this, $form_data_obj );
1203
	}
1204
1205
	public function buyers_name() {
1206
		global $purchlogitem;
1207
1208
		if ( null === $this->buyers_name ) {
1209
			$first_name = $last_name = '';
1210
1211
			if ( isset( $purchlogitem->userinfo['billingfirstname'] ) ) {
1212
				$first_name = $purchlogitem->userinfo['billingfirstname']['value'];
1213
			}
1214
1215
			if ( isset( $purchlogitem->userinfo['billinglastname'] ) ) {
1216
				$last_name = ' ' . $purchlogitem->userinfo['billinglastname']['value'];
1217
			}
1218
1219
			$this->buyers_name = trim( $first_name . $last_name );
1220
		}
1221
1222
		return $this->buyers_name;
1223
	}
1224
1225
	public function buyers_city() {
1226
		global $purchlogitem;
1227
1228
		if ( null === $this->buyers_city ) {
1229
			$this->buyers_city = isset( $purchlogitem->userinfo['billingcity']['value'] ) ? $purchlogitem->userinfo['billingcity']['value'] : '';
1230
		}
1231
1232
		return $this->buyers_city;
1233
	}
1234
1235
	public function buyers_email() {
1236
		global $purchlogitem;
1237
1238
		if ( null === $this->buyers_email ) {
1239
			$this->buyers_email = isset( $purchlogitem->userinfo['billingemail']['value'] ) ? $purchlogitem->userinfo['billingemail']['value'] : '';
1240
		}
1241
1242
		return $this->buyers_email;
1243
	}
1244
1245
	public function buyers_address() {
1246
		global $purchlogitem;
1247
1248
		if ( null === $this->buyers_address ) {
1249
			$this->buyers_address = isset( $purchlogitem->userinfo['billingaddress']['value'] ) ? nl2br( esc_html( $purchlogitem->userinfo['billingaddress']['value'] ) ) : '';
1250
		}
1251
1252
		return $this->buyers_address;
1253
	}
1254
1255
	public function buyers_state_and_postcode() {
1256
		global $purchlogitem;
1257
1258
		if ( null === $this->buyers_state_and_postcode ) {
1259
1260
			if ( is_numeric( $this->get( 'billing_region' ) ) ) {
1261
				$state = wpsc_get_region( $this->get( 'billing_region' ) );
1262
			} else {
1263
				$state = $purchlogitem->userinfo['billingstate']['value'];
1264
				$state = is_numeric( $state ) ? wpsc_get_region( $state ) : $state;
1265
			}
1266
1267
			$output = esc_html( $state );
1268
1269
			if ( isset( $purchlogitem->userinfo['billingpostcode']['value'] ) && ! empty( $purchlogitem->userinfo['billingpostcode']['value'] ) ) {
1270
				if ( $output ) {
1271
					$output .= ', '; // TODO determine if it's ok to make this a space only (like shipping_state_and_postcode)
1272
				}
1273
				$output .= $purchlogitem->userinfo['billingpostcode']['value'];
1274
			}
1275
1276
			$this->buyers_state_and_postcode = $output;
1277
		}
1278
1279
		return $this->buyers_state_and_postcode;
1280
	}
1281
1282
	public function buyers_country() {
1283
		global $purchlogitem;
1284
1285
		if ( null === $this->buyers_country ) {
1286
			$this->buyers_country = isset( $purchlogitem->userinfo['billingcountry']['value'] ) ? wpsc_get_country( $purchlogitem->userinfo['billingcountry']['value'] ) : '';
1287
		}
1288
1289
		return $this->buyers_country;
1290
	}
1291
1292
	public function buyers_phone() {
1293
		global $purchlogitem;
1294
1295
		if ( null === $this->buyers_phone ) {
1296
			$this->buyers_phone = isset( $purchlogitem->userinfo['billingphone']['value'] ) ? $purchlogitem->userinfo['billingphone']['value'] : '';
1297
		}
1298
1299
		return $this->buyers_phone;
1300
	}
1301
1302
	public function shipping_name() {
1303
		global $purchlogitem;
1304
1305
		if ( null === $this->shipping_name ) {
1306
			$this->shipping_name = isset( $purchlogitem->shippinginfo['shippingfirstname']['value'] ) ? $purchlogitem->shippinginfo['shippingfirstname']['value'] : '';
1307
		}
1308
1309
		return $this->shipping_name;
1310
	}
1311
1312
	public function shipping_city() {
1313
		global $purchlogitem;
1314
1315
		if ( null === $this->shipping_city ) {
1316
			$this->shipping_city = isset( $purchlogitem->shippinginfo['shippingcity']['value'] ) ? $purchlogitem->shippinginfo['shippingcity']['value'] : '';
1317
		}
1318
1319
		return $this->shipping_city;
1320
	}
1321
1322
	public function shipping_address() {
1323
		global $purchlogitem;
1324
1325
		if ( null === $this->shipping_address ) {
1326
			$this->shipping_address = isset( $purchlogitem->shippinginfo['shippingaddress']['value'] ) ? nl2br( esc_html( $purchlogitem->shippinginfo['shippingaddress']['value'] ) ) : '';
1327
		}
1328
1329
		return $this->shipping_address;
1330
	}
1331
1332
	public function shipping_state_and_postcode() {
1333
		global $purchlogitem;
1334
1335
		if ( null === $this->shipping_state_and_postcode ) {
1336
			if ( is_numeric( $this->get( 'shipping_region' ) ) ) {
1337
				$output = wpsc_get_region( $this->get( 'shipping_region' ) );
1338
			} else {
1339
				$state = $purchlogitem->shippinginfo['shippingstate']['value'];
1340
				$output = is_numeric( $state ) ? wpsc_get_region( $state ) : $state;
1341
			}
1342
1343
			if ( !empty( $purchlogitem->shippinginfo['shippingpostcode']['value'] ) ){
1344
				if ( $output ) {
1345
					$output .= ' ';
1346
				}
1347
1348
				$output .= $purchlogitem->shippinginfo['shippingpostcode']['value'];
1349
			}
1350
1351
			$this->shipping_state_and_postcode = $output;
1352
		}
1353
1354
		return $this->shipping_state_and_postcode;
1355
	}
1356
1357
	public function shipping_country() {
1358
		global $purchlogitem;
1359
1360
		if ( null === $this->shipping_country ) {
1361
			$this->shipping_country = isset( $purchlogitem->shippinginfo['shippingcountry'] )
1362
				? wpsc_get_country( $purchlogitem->shippinginfo['shippingcountry']['value'] )
1363
				: '';
1364
		}
1365
1366
		return $this->shipping_country;
1367
	}
1368
1369
	public function payment_method() {
1370
		global $nzshpcrt_gateways;
1371
1372
		if ( null === $this->payment_method ) {
1373
			if ( 'wpsc_merchant_testmode' == $this->get( 'gateway' ) ) {
1374
				$this->payment_method = __( 'Manual Payment', 'wp-e-commerce' );
1375
			} else {
1376
				foreach ( (array) $nzshpcrt_gateways as $gateway ) {
1377
					if ( isset( $gateway['internalname'] ) && $gateway['internalname'] == $this->get( 'gateway' ) ) {
1378
						$this->payment_method = $gateway['name'];
1379
					}
1380
				}
1381
1382
				if ( ! $this->payment_method ) {
1383
					$this->payment_method = $this->get( 'gateway' );
1384
				}
1385
			}
1386
		}
1387
1388
		return $this->payment_method;
1389
	}
1390
1391
	public function shipping_method() {
1392
		global $wpsc_shipping_modules;
1393
1394
		if ( null === $this->shipping_method ) {
1395
1396
			if ( ! empty( $wpsc_shipping_modules[ $this->get( 'shipping_method' ) ] ) ) {
1397
				$this->shipping_method = $wpsc_shipping_modules[ $this->get( 'shipping_method' ) ]->getName();
1398
			} else {
1399
				$this->shipping_method = $this->get( 'shipping_method' );
1400
			}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
1401
1402
		}
1403
1404
		return $this->shipping_method;
1405
	}
1406
1407
	/**
1408
	 * Returns base shipping should make a function to calculate items shipping as well
1409
	 *
1410
	 * @since  4.0
1411
	 *
1412
	 * @param  boolean $numeric Return numeric value.
1413
	 *
1414
	 * @return mixed
1415
	 */
1416
	public function discount( $numeric = false ) {
1417
		$discount = $this->get( 'discount_value' );
1418
		if ( ! $numeric ) {
1419
			$discount = wpsc_currency_display( $discount, array( 'display_as_html' => false ) );
1420
		}
1421
1422
		return $discount;
1423
	}
1424
1425
	/**
1426
	 * Returns base shipping should make a function to calculate items shipping as well
1427
	 *
1428
	 * @since  4.0
1429
	 *
1430
	 * @param  boolean $numeric       Return numeric value.
1431
	 * @param  boolean $include_items Whether to calculate per-item-shipping.
1432
	 *
1433
	 * @return mixed
1434
	 */
1435
	public function shipping( $numeric = false, $include_items = false ) {
1436
		$total_shipping = $this->get( 'base_shipping' );
1437
1438
		if ( $include_items ) {
1439
			$total_shipping = $this->meta_data['total_shipping'];
1440
		}
1441
1442
		if ( ! $numeric ) {
1443
			$total_shipping = wpsc_currency_display( $total_shipping, array( 'display_as_html' => false ) );
1444
		}
1445
1446
		return $total_shipping;
1447
	}
1448
1449
	/**
1450
	 * Returns taxes total.
1451
	 *
1452
	 * @since  4.0
1453
	 *
1454
	 * @param  boolean $numeric Return numeric value.
1455
	 *
1456
	 * @return mixed
1457
	 */
1458
	public function taxes( $numeric = false ) {
1459
		$taxes = $this->get( 'wpec_taxes_total' );
1460
1461
		if ( ! $numeric ) {
1462
			$taxes = wpsc_currency_display( $taxes, array( 'display_as_html' => false ) );
1463
		}
1464
1465
		return $taxes;
1466
	}
1467
1468
	public function total_price() {
1469
		global $purchlogitem;
1470
1471
		$total = $purchlogitem->totalAmount - $this->discount( true ) + $this->shipping( true ) + $this->taxes( true );
1472
		return wpsc_currency_display( $total, array( 'display_as_html' => false ) );
1473
	}
1474
1475
}
1476