Completed
Pull Request — master (#2165)
by Justin
05:25
created

WPSC_Purchase_Log   F

Complexity

Total Complexity 200

Size/Duplication

Total Lines 1477
Duplicated Lines 0 %

Coupling/Cohesion

Components 11
Dependencies 5

Importance

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

66 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 35 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 form_data() 0 7 2
B get_gateway_data() 0 44 5
C set() 0 35 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 8 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 13 4
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 14 3
A discount() 0 8 2
A shipping() 0 13 3
A taxes() 0 9 2
A total_price() 0 6 1
A get_item() 0 10 2
A get_item_from_product_id() 0 12 3
B update_item() 0 25 3
B remove_item() 0 24 3

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
	private $form_data_obj = null;
101
102
	private $is_status_changed = false;
103
	private $previous_status   = false;
104
105
	private $log_items = array();
106
	private $log_item_ids = array();
107
	private $can_edit = null;
108
109
	/**
110
	 * Contains the constructor arguments. This array is necessary because we will
111
	 * lazy load the DB row into $this->data whenever necessary. Lazy loading is,
112
	 * in turn, necessary because sometimes right after saving a new record, we need
113
	 * to fetch a property with the same object.
114
	 *
115
	 * @access private
116
	 * @since 3.8.9
117
	 *
118
	 * @var array
119
	 */
120
	private $args = array(
121
		'col'   => '',
122
		'value' => '',
123
	);
124
125
   protected $buyers_name = null;
126
   protected $buyers_city = null;
127
   protected $buyers_email = null;
128
   protected $buyers_address = null;
129
   protected $buyers_state_and_postcode = null;
130
   protected $buyers_country = null;
131
   protected $buyers_phone = null;
132
   protected $shipping_name = null;
133
   protected $shipping_address = null;
134
   protected $shipping_city = null;
135
   protected $shipping_state_and_postcode = null;
136
   protected $shipping_country = null;
137
   protected $payment_method = null;
138
   protected $shipping_method = null;
139
140
	/**
141
	 * Get the SQL query format for a column
142
	 *
143
	 * @since 3.8.9
144
	 * @param  string $col Name of the column
145
	 * @return string      Placeholder
146
	 */
147
	private static function get_column_format( $col ) {
148
		if ( in_array( $col, self::$string_cols ) ) {
149
			return '%s';
150
		}
151
152
		if ( in_array( $col, self::$int_cols ) ) {
153
			return '%d';
154
		}
155
156
		return '%f';
157
	}
158
159
	/**
160
	 * Query the purchase log table to get sales and earning stats
161
	 *
162
	 * Accepts an array of arguments:
163
	 * 	- 'ids': IDs of products for which you want to get stats
164
	 * 	- 'products': array of WPSC_Product objects for which you want to get stats
165
	 * 	- 'start' and 'end': the timestamp range (integers) during which you want
166
	 * 	                     to collect the stats.
167
	 * 	                     You can use none, either, or both of them.
168
	 * 	                     Note that the [start, end) interval is a left-closed,
169
	 * 	                     right-open.
170
	 * 	                     E.g.: to get stats from Jan 1st, 2013 to
171
	 * 	                     Dec 31st, 2013 (23:59:59),
172
	 * 	                     set "start" to the timestamp for Jan 1st, 2013, set
173
	 * 	                     "end" to the timestamp for Jan 1st, 2014
174
	 *  - 'order': what to sort the results by, defaults to 'id'.
175
	 *            Can be 'ids', 'sales', 'earnings' or empty string to disable sorting
176
	 *  - 'orderby': how to sort the results, defaults to 'ASC'.
177
	 *              Can be 'ASC', 'DESC' (lowercase is fine too)
178
	 *  - 'per_page': How many items to fetch, defaults to 0, which fetches all results
179
	 *  - 'page': Which page of the results to fetch, defaults to 1.
180
	 *            Has no effect if per_page is set to 0.
181
	 *
182
	 * @since 3.8.14
183
	 * @param  array|string $args Arguments
184
	 * @return array       Array containing 'sales' and 'earnings' stats
185
	 */
186
	public static function fetch_stats( $args ) {
187
		global $wpdb;
188
189
		$defaults = array(
190
			'ids'      => array(), // IDs of the products to be queried
191
			'products' => array(), // Array of WPSC_Products objects
192
			'start'    => 0,       // Int. timestamp, has to be UTC
193
			'end'      => 0,       // Int. timestamp, has to be UTC
194
			'order'    => 'ASC',
195
			'orderby'  => 'id',
196
			'per_page' => 0,
197
			'page'     => 1,
198
		);
199
200
		$args = wp_parse_args( $args, $defaults );
201
202
		// convert more advanced date / time args into "start" and "end"
203
		$args = self::convert_date_args( $args );
204
205
		// build an array of WPSC_Product objects based on 'ids' and 'products' args
206
		$products = array_merge(
207
			$args['products'],
208
			array_map( array( 'WPSC_Product', 'get_instance' ), $args['ids'] )
209
		);
210
211
		// eliminate duplicates (Note: usage of this requires PHP 5.2.9)
212
		$products = array_unique( $products, SORT_REGULAR );
213
214
		if ( empty( $products ) ) {
215
			return null;
216
		}
217
218
		$needs_fetching = array();
219
220
		$stats = array(
221
			'sales'    => 0,
222
			'earnings' => 0,
223
		);
224
225
		// if we have date restriction, that means we won't be able to rely upon
226
		// individual stats cache inside WPSC_Product objects
227
		$has_date_restriction = ! ( empty( $args['start'] ) && empty( $args['end'] ) );
228
229
		// if we do NOT have date restriction, find out which WPSC_Product object
230
		// has stats cache, and which don't
231
		if ( ! $has_date_restriction ) {
232
			foreach ( $products as $product ) {
233
				// store the ID if this product doesn't have a stats cache yet
234
				if ( $product->post->_wpsc_stats === '' ) {
235
					$needs_fetching[] = $product->post->ID;
236
				} else {
237
238
					// tally up the sales and earnings if this one has cache already
239
					$prod_meta = get_post_meta( $product->post->ID, '_wpsc_stats', true );
240
241
					if ( isset( $prod_meta['sales'] ) && isset( $prod_meta['earnings'] ) ) {
242
						$stats['sales']    += $prod_meta['sales'];
243
						$stats['earnings'] += $prod_meta['earnings'];
244
					}
245
					$needs_fetching[]   = $product->post->ID;
246
				}
247
			}
248
		}
249
250
		// never hurts to make sure
251
		$needs_fetching = array_map( 'absint', $needs_fetching );
252
253
		// pagination arguments
254
		$limit = '';
255
256
		if ( ! empty( $args['per_page'] ) ) {
257
			$offset = ( $args['page'] - 1 ) * $args['per_page'];
258
			$limit = "LIMIT " . absint( $args['per_page'] ) . " OFFSET " . absint( $offset );
259
		}
260
261
		// sorting
262
		$order = '';
263
264
		if ( ! empty( $args['orderby'] ) )
265
			$order = "ORDER BY " . esc_sql( $args['orderby'] ) . " " . esc_sql( $args['order'] );
266
267
		// date
268
		$where = "WHERE p.processed IN (3, 4, 5)";
269
270
		if ( $has_date_restriction ) {
271
			// start date equal or larger than $args['start']
272
			if ( ! empty( $args['start'] ) )
273
				$where .= " AND CAST(p.date AS UNSIGNED) >= " . absint( $args['start'] );
274
275
			// end date less than $args['end'].
276
			// the "<" sign is not a typo, such upper limit makes it easier for
277
			// people to specify range.
278
			// E.g.: [1/1/2013 - 1/1/2014) rather than:
279
			//       [1/1/2013 - 31/12/2013 23:59:59]
280
			if ( ! empty( $args['end'] ) )
281
				$where .= " AND CAST(p.date AS UNSIGNED) < " . absint( $args['end'] );
282
		}
283
284
		// assemble the SQL query
285
		$sql = "
286
			SELECT cc.prodid AS id, SUM(cc.quantity) AS sales, SUM(cc.quantity * cc.price) AS earnings
287
			FROM $wpdb->wpsc_purchase_logs AS p
288
			INNER JOIN
289
				$wpdb->wpsc_cart_contents AS cc
290
				ON p.id = cc.purchaseid AND cc.prodid IN (" . implode( ', ', $needs_fetching ) . ")
291
			{$where}
292
			GROUP BY cc.prodid
293
			{$order}
294
			{$limit}
295
		";
296
297
		// if the result is cached, don't bother querying the database
298
		$cache_key = md5( $sql );
299
		$results   = wp_cache_get( $cache_key, 'wpsc_purchase_logs_stats' );
300
301
		if ( false === $results ) {
302
			$results = $wpdb->get_results( $sql );
303
			wp_cache_set( $cache_key, $results, 'wpsc_purchase_logs_stats', WPSC_PURCHASE_LOG_STATS_CACHE_EXPIRE );
304
		}
305
306
		// tally up the sales and earnings from the query results
307
		foreach ( $results as $row ) {
308
			if ( ! $has_date_restriction ) {
309
				$product           = WPSC_Product::get_instance( $row->id );
310
				$product->sales    = $row->sales;
311
				$product->earnings = $row->earnings;
312
			}
313
314
			$stats['sales']    += $row->sales;
315
			$stats['earnings'] += $row->earnings;
316
		}
317
318
		return $stats;
319
	}
320
321
	/**
322
	 * Convert advanced date/time arguments like year, month, day, 'ago' etc.
323
	 * into basic arguments which are "start" and "end".
324
	 *
325
	 * @since  3.8.14
326
	 * @param  array $args Arguments
327
	 * @return array       Arguments after converting
328
	 */
329
	private static function convert_date_args( $args ) {
330
		// TODO: Implement this
331
		return $args;
332
	}
333
334
	/**
335
	 * Get overall sales and earning stats for just one product
336
	 *
337
	 * @since 3.8.14
338
	 * @param  int $id ID of the product
339
	 * @return array   Array containing 'sales' and 'earnings' stats
340
	 */
341
	public static function get_stats_for_product( $id, $args = '' ) {
342
343
		$product = WPSC_Product::get_instance( $id );
344
345
		// if this product has variations
346
		if ( $product->has_variations ) {
347
			// get total stats of variations
348
			$args['products'] = $product->variations;
349
		} else {
350
			// otherwise, get stats of only this product
351
			$args['products'] = array( $product );
352
		}
353
354
		return self::fetch_stats( $args );
355
	}
356
357
	/**
358
	 * Check whether the status code indicates a completed status
359
	 *
360
	 * @since 3.8.13
361
	 * @param int  $status Status code
362
	 * @return boolean
363
	 */
364
	public static function is_order_status_completed( $status ) {
365
		$completed_status = apply_filters( 'wpsc_order_status_completed', array(
366
			self::ACCEPTED_PAYMENT,
367
			self::JOB_DISPATCHED,
368
			self::CLOSED_ORDER,
369
		) );
370
371
		return in_array( $status, $completed_status );
372
	}
373
374
	/**
375
	 * Update cache of the passed log object
376
	 *
377
	 * @access public
378
	 * @static
379
	 * @since 3.8.9
380
	 *
381
	 * @param WPSC_Purchase_Log $log The log object that you want to store into cache
382
	 * @return void
383
	 */
384
	public static function update_cache( &$log ) {
385
		return $log->update_caches();
386
	}
387
388
	/**
389
	 * Update caches.
390
	 *
391
	 * @access public
392
	 * @static
393
	 * @since 4.0
394
	 *
395
	 * @return void
396
	 */
397
	public function update_caches() {
398
399
		// wpsc_purchase_logs stores the data array, while wpsc_purchase_logs_sessionid stores the
400
		// log id that's associated with the sessionid
401
		$id = $this->get( 'id' );
402
		wp_cache_set( $id, $this->data, 'wpsc_purchase_logs' );
403
404
		if ( $sessionid = $this->get( 'sessionid' ) ) {
405
			wp_cache_set( $sessionid, $id, 'wpsc_purchase_logs_sessionid' );
406
		}
407
408
		wp_cache_set( $id, $this->log_items, 'wpsc_purchase_log_items' );
409
		do_action( 'wpsc_purchase_log_update_cache', $this );
410
	}
411
412
	/**
413
	 * Deletes cache of a log (either by using the log ID or sessionid)
414
	 *
415
	 * @access public
416
	 * @static
417
	 * @since 3.8.9
418
	 *
419
	 * @param string $value The value to query
420
	 * @param string $col Optional. Defaults to 'id'. Whether to delete cache by using
421
	 *                    a purchase log ID or sessionid
422
	 * @return void
423
	 */
424
	public static function delete_cache( $value, $col = 'id' ) {
425
		// this will pull from the old cache, so no worries there
426
		$log = new WPSC_Purchase_Log( $value, $col );
427
		$log->delete_caches( $value, $col );
428
	}
429
430
	/**
431
	 * Deletes caches.
432
	 *
433
	 * @access public
434
	 * @static
435
	 * @since 4.0
436
	 *
437
	 * @param string|null $value Optional (left for back-compatibility). The value which was queried.
438
	 * @param string|null $col   Optional (left for back-compatibility). The column used as the identifier.
439
	 *
440
	 * @return void
441
	 */
442
	public function delete_caches( $value = null, $col = null ) {
443
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_logs' );
444
		wp_cache_delete( $this->get( 'sessionid' ), 'wpsc_purchase_logs_sessionid' );
445
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_log_items' );
446
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_meta' );
447
448
		if ( null === $value ) {
449
			$value = $this->args['value'];
450
		}
451
452
		if ( null === $col ) {
453
			$col = $this->args['col'];
454
		}
455
456
		do_action( 'wpsc_purchase_log_delete_cache', $this, $value, $col );
457
	}
458
459
	/**
460
	 * Deletes a log from the database.
461
	 *
462
	 * @access  public
463
	 * @since   3.8.9
464
	 *
465
	 * @uses  $wpdb                              Global database instance.
466
	 * @uses  wpsc_is_store_admin()              Check user has admin capabilities.
467
	 * @uses  WPSC_Purchase_Log::delete_cache()  Delete purchaselog cache.
468
	 * @uses  WPSC_Claimed_Stock                 Claimed Stock class.
469
	 *
470
	 * @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...
471
	 * @return  boolean            Deleted successfully.
472
	 */
473
	public function delete( $log_id = false ) {
474
475
		global $wpdb;
476
477
		if ( ! ( isset( $this ) && get_class( $this ) == __CLASS__ ) ) {
478
			_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' );
479
		}
480
481
		if ( false !== $log_id ) {
482
			_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.' );
483
		}
484
485
		if ( ! wpsc_is_store_admin() ) {
486
			return false;
487
		}
488
489
		$log_id = $this->get( 'id' );
490
491
		if ( $log_id > 0 ) {
492
493
			do_action( 'wpsc_purchase_log_before_delete', $log_id );
494
495
			$this->delete_caches();
496
497
			// Delete claimed stock
498
			$purchlog_status = $wpdb->get_var( $wpdb->prepare( "SELECT `processed` FROM `" . WPSC_TABLE_PURCHASE_LOGS . "` WHERE `id`= %d", $log_id ) );
499
			if ( $purchlog_status == WPSC_Purchase_Log::CLOSED_ORDER || $purchlog_status == WPSC_Purchase_Log::INCOMPLETE_SALE ) {
500
				$claimed_query = new WPSC_Claimed_Stock( array(
501
					'cart_id'        => $log_id,
502
					'cart_submitted' => 1
503
				) );
504
				$claimed_query->clear_claimed_stock( 0 );
505
			}
506
507
			// Delete cart content, submitted data, then purchase log
508
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_CART_CONTENTS . "` WHERE `purchaseid` = %d", $log_id ) );
509
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_SUBMITTED_FORM_DATA . "` WHERE `log_id` IN (%d)", $log_id ) );
510
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_PURCHASE_LOGS . "` WHERE `id` = %d LIMIT 1", $log_id ) );
511
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_PURCHASE_META . "` WHERE `wpsc_purchase_id` = %d", $log_id ) );
512
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_DOWNLOAD_STATUS . "` WHERE `purchid` = %d ", $log_id ) );
513
514
			do_action( 'wpsc_purchase_log_delete', $log_id );
515
516
			return true;
517
518
		}
519
520
		return false;
521
522
	}
523
524
	/**
525
	 * Constructor of the purchase log object. If no argument is passed, this simply
526
	 * create a new empty object. Otherwise, this will get the purchase log from the
527
	 * DB either by using purchase log id or sessionid (specified by the 2nd argument).
528
	 *
529
	 * Eg:
530
	 *
531
	 * // get purchase log with ID number 23
532
	 * $log = new WPSC_Purchase_Log( 23 );
533
	 *
534
	 * // get purchase log with sessionid "asdf"
535
	 * $log = new WPSC_Purchase_Log( 'asdf', 'sessionid' )
536
	 *
537
	 * @access public
538
	 * @since 3.8.9
539
	 *
540
	 * @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...
541
	 * @param string $col Optional. Defaults to 'id'.
542
	 */
543
	public function __construct( $value = false, $col = 'id' ) {
544
		if ( false === $value ) {
545
			return;
546
		}
547
548
		if ( is_array( $value ) ) {
549
			$this->set( $value );
550
			return;
551
		}
552
553
		global $wpdb;
554
555
		if ( ! in_array( $col, array( 'id', 'sessionid' ) ) ) {
556
			return;
557
		}
558
559
		// store the constructor args into an array so that later we can lazy load the data
560
		$this->args = array(
561
			'col'   => $col,
562
			'value' => $value,
563
		);
564
565
		// if the sessionid is in cache, pull out the id
566
		if ( $col == 'sessionid'  && $id = wp_cache_get( $value, 'wpsc_purchase_logs_sessionid' ) ) {
567
				$col = 'id';
568
				$value = $id;
569
		}
570
571
		// if the id is specified, try to get from cache
572
		if ( $col == 'id' ) {
573
			$this->data = wp_cache_get( $value, 'wpsc_purchase_logs' );
574
			$this->log_items = wp_cache_get( $value, 'wpsc_purchase_log_items' );
575
		}
576
577
		// cache exists
578
		if ( $this->data ) {
579
			$this->set_meta_props();
580
			$this->fetched = true;
581
			$this->exists  = true;
582
			return;
583
		}
584
	}
585
586
	private function set_total_shipping() {
587
588
		$base_shipping  = $this->get( 'base_shipping' );
589
		$item_shipping  = wp_list_pluck( $this->get_items(), 'pnp' );
590
591
		$this->meta_data['total_shipping'] = $base_shipping + array_sum( $item_shipping );
592
593
		return $this->meta_data['total_shipping'];
594
	}
595
596
	private function set_gateway_name() {
597
		global $wpsc_gateways;
598
		$gateway = $this->get( 'gateway' );
599
		$gateway_name = $gateway;
600
601
		if( 'wpsc_merchant_testmode' == $gateway )
602
			$gateway_name = __( 'Manual Payment', 'wp-e-commerce' );
603
		elseif ( isset( $wpsc_gateways[$gateway] ) )
604
			$gateway_name = $wpsc_gateways[$gateway]['name'];
605
606
		$this->meta_data['gateway_name'] = $gateway_name;
607
		return $this->meta_data['gateway_name'];
608
	}
609
610
	private function set_shipping_method_names() {
611
		global $wpsc_shipping_modules;
612
613
		$shipping_method = $this->get( 'shipping_method' );
614
		$shipping_option = $this->get( 'shipping_option' );
615
		$shipping_method_name = $shipping_method;
616
		$shipping_option_name = $shipping_option;
617
618
		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...
619
			$shipping_class = $wpsc_shipping_modules[$shipping_method];
620
			$shipping_method_name = $shipping_class->name;
621
		}
622
623
		$this->meta_data['shipping_method_name'] = $shipping_method_name;
624
		$this->meta_data['shipping_option_name'] = $shipping_option_name;
625
	}
626
627
	private function set_meta_props() {
628
629
		foreach ( wpsc_get_purchase_custom( $this->get( 'id' ) ) as $key => $value  ) {
630
			$this->meta_data[ $key ] = wpsc_get_purchase_meta( $this->get( 'id' ), $key, true );
631
		}
632
633
		$this->set_total_shipping();
634
		$this->set_gateway_name();
635
		$this->set_shipping_method_names();
636
	}
637
638
	public function get_meta() {
639
640
		if ( empty( $this->data ) || empty( $this->meta_data ) ) {
641
			$this->fetch();
642
		}
643
644
		return (array) apply_filters( 'wpsc_purchase_log_meta_data', $this->meta_data );
645
	}
646
647
	/**
648
	 * Fetches the actual record from the database
649
	 *
650
	 * @access protected
651
	 * @since 3.8.9
652
	 *
653
	 * @return WPSC_Query_Base
654
	 */
655
	protected function fetch() {
656
		global $wpdb;
657
658
		if ( $this->fetched ) {
659
			return;
660
		}
661
662
		// If $this->args is not set yet, it means the object contains a new unsaved
663
		// row so we don't need to fetch from DB
664
		if ( ! $this->args['col'] || ! $this->args['value'] ) {
665
			return;
666
		}
667
668
		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...
669
670
		$format = self::get_column_format( $col );
671
		$sql    = $wpdb->prepare( "SELECT * FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE {$col} = {$format}", $value );
672
673
		$this->exists = false;
674
675
		if ( $data = $wpdb->get_row( $sql, ARRAY_A ) ) {
676
			$this->exists        = true;
677
			$this->data          = apply_filters( 'wpsc_purchase_log_data', $data );
678
			$this->log_items = $this->get_items();
679
680
			$this->set_meta_props();
681
			$this->update_caches();
682
		}
683
684
		do_action( 'wpsc_purchase_log_fetched', $this );
685
686
		$this->fetched = true;
687
688
		return $this;
689
	}
690
691
	/**
692
	 * Prepares the return value for get() (apply_filters, etc).
693
	 *
694
	 * @access protected
695
	 * @since  4.0
696
	 *
697
	 * @param  mixed  $value Value fetched
698
	 * @param  string $key   Key for $data.
699
	 *
700
	 * @return mixed
701
	 */
702
	protected function prepare_get( $value, $key ) {
703
		return apply_filters( 'wpsc_purchase_log_get_property', $value, $key, $this );
704
	}
705
706
	/**
707
	 * Prepares the return value for get_data() (apply_filters, etc).
708
	 *
709
	 * @access protected
710
	 * @since  4.0
711
	 *
712
	 * @return mixed
713
	 */
714
	protected function prepare_get_data() {
715
		return apply_filters( 'wpsc_purchase_log_get_data', $this->data, $this );
716
	}
717
718
	/**
719
	 * Prepares the return value for get_meta() (apply_filters, etc).
720
	 *
721
	 * @access protected
722
	 * @since  4.0
723
	 *
724
	 * @return mixed
725
	 */
726
	protected function prepare_get_meta() {
727
		return (array) apply_filters( 'wpsc_purchase_log_meta_data', $this->meta_data );
728
	}
729
730
	public function get_cart_contents() {
731
		_wpsc_doing_it_wrong( __FUNCTION__, __( 'This function has been deprecated in favor of the get_items() method.', 'wp-e-commerce' ), '4.0' );
732
		return $this->get_items();
733
	}
734
735
	public function get_items() {
736
		global $wpdb;
737
738
		if ( ! empty( $this->log_items ) && $this->fetched ) {
739
			return $this->log_items;
740
		}
741
742
		$id = $this->get( 'id' );
743
744
		// Bail if we don't have a log object yet (no id).
745
		if ( empty( $id ) ) {
746
			return $this->log_items;
747
		}
748
749
		$sql = $wpdb->prepare( "SELECT * FROM " . WPSC_TABLE_CART_CONTENTS . " WHERE purchaseid = %d", $id );
750
		$this->log_items = $wpdb->get_results( $sql );
751
752
		if ( is_array( $this->log_items ) ) {
753
			foreach ( $this->log_items as $index => $item ) {
754
				$this->log_item_ids[ absint( $item->id ) ] = $index;
755
			}
756
		}
757
758
		return $this->log_items;
759
	}
760
761
	public function get_item( $item_id ) {
762
		$item_id = absint( $item_id );
763
		$items   = $this->get_items();
764
765
		if ( isset( $this->log_item_ids[ $item_id ] ) ) {
766
			return $items[ $this->log_item_ids[ $item_id ] ];
767
		}
768
769
		return false;
770
	}
771
772
	public function get_item_from_product_id( $product_id ) {
773
		$product_id = absint( $product_id );
774
		$items      = $this->get_items();
775
776
		foreach ( $items as $item ) {
777
			if ( $product_id === absint( $item->prodid ) ) {
778
				return $item;
779
			}
780
		}
781
782
		return false;
783
	}
784
785
	public function update_item( $item_id, $data ) {
786
		global $wpdb;
787
788
		$item_id = absint( $item_id );
789
		$item = $this->get_item( $item_id );
790
791
		if ( $item ) {
792
			do_action( 'wpsc_purchase_log_before_update_item', $item_id );
793
794
			$data = wp_unslash( $data );
795
			$result = $wpdb->update( WPSC_TABLE_CART_CONTENTS, $data, array( 'id' => $item_id  ) );
796
797
			if ( $result ) {
798
799
				$this->log_items = array();
800
				$this->get_item( $item_id );
801
802
				do_action( 'wpsc_purchase_log_update_item', $item_id );
803
			}
804
805
			return $result;
806
		}
807
808
		return false;
809
	}
810
811
	public function remove_item( $item_id ) {
812
		global $wpdb;
813
814
		$item_id = absint( $item_id );
815
		$item = $this->get_item( $item_id );
816
817
		if ( $item ) {
818
			do_action( 'wpsc_purchase_log_before_remove_item', $item_id );
819
820
			$result = $wpdb->delete( WPSC_TABLE_CART_CONTENTS, array( 'id' => $item_id ) );
821
822
			if ( $result ) {
823
824
				unset( $this->log_items[ $this->log_item_ids[ $item_id ] ] );
825
				unset( $this->log_item_ids[ $item_id ] );
826
827
				do_action( 'wpsc_purchase_log_remove_item', $item_id );
828
			}
829
830
			return $result;
831
		}
832
833
		return false;
834
	}
835
836
	public function form_data() {
837
		if ( null === $this->form_data_obj ) {
838
			$this->form_data_obj = new WPSC_Checkout_Form_Data( $this->get( 'id' ), false );
0 ignored issues
show
Unused Code introduced by
The call to WPSC_Checkout_Form_Data::__construct() has too many arguments starting with false.

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...
839
		}
840
841
		return $this->form_data_obj;
842
	}
843
844
	public function get_gateway_data( $from_currency = false, $to_currency = false ) {
845
		if ( ! $this->exists() ) {
846
			return array();
847
		}
848
849
		$subtotal = 0;
850
		$shipping = wpsc_convert_currency( (float) $this->get( 'base_shipping' ), $from_currency, $to_currency );
851
		$items    = array();
852
853
		$this->gateway_data = array(
854
			'amount'  => wpsc_convert_currency( $this->get( 'totalprice' ), $from_currency, $to_currency ),
855
			'invoice' => $this->get( 'sessionid' ),
856
			'tax'     => wpsc_convert_currency( $this->get( 'wpec_taxes_total' ), $from_currency, $to_currency ),
857
		);
858
859
		foreach ( $this->log_items as $item ) {
860
			$item_price = wpsc_convert_currency( $item->price, $from_currency, $to_currency );
861
			$items[] = array(
862
				'name'     => $item->name,
863
				'amount'   => $item_price,
864
				'tax'      => wpsc_convert_currency( $item->tax_charged, $from_currency, $to_currency ),
865
				'quantity' => $item->quantity,
866
			);
867
			$subtotal += $item_price * $item->quantity;
868
			$shipping += wpsc_convert_currency( $item->pnp, $from_currency, $to_currency );
869
		}
870
871
		$this->gateway_data['discount'] = wpsc_convert_currency( (float) $this->get( 'discount_value' ), $from_currency, $to_currency );
872
873
		$this->gateway_data['items'] = $items;
874
		$this->gateway_data['shipping'] = $shipping;
875
		$this->gateway_data['subtotal'] = $subtotal;
876
877
		if ( $from_currency ) {
878
			// adjust total amount in case there's slight decimal error
879
			$total = $subtotal + $shipping + $this->gateway_data['tax'] - $this->gateway_data['discount'];
880
			if ( $this->gateway_data['amount'] != $total ) {
881
				$this->gateway_data['amount'] = $total;
882
			}
883
		}
884
885
		$this->gateway_data = apply_filters( 'wpsc_purchase_log_gateway_data', $this->gateway_data, $this->get_data() );
886
		return $this->gateway_data;
887
	}
888
889
	/**
890
	 * Sets a property to a certain value. This function accepts a key and a value
891
	 * as arguments, or an associative array containing key value pairs.
892
	 *
893
	 * Loops through data, comparing against database, and saves as meta if not found in purchase log table.
894
	 *
895
	 * @access public
896
	 * @since 3.8.9
897
	 *
898
	 * @param mixed $key Name of the property (column), or an array containing key
899
	 *                   value pairs
900
	 * @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...
901
	 *                          this should be specified.
902
	 * @return WPSC_Purchase_Log The current object (for method chaining)
903
	 */
904
	public function set( $key, $value = null ) {
905
		if ( is_array( $key ) ) {
906
			$properties = $key;
907
		} else {
908
			if ( is_null( $value ) ) {
909
				return $this;
910
			}
911
912
			$properties = array( $key => $value );
913
		}
914
915
		$properties = apply_filters( 'wpsc_purchase_log_set_properties', $properties, $this );
916
917
		if ( array_key_exists( 'processed', $properties ) ) {
918
			$this->previous_status = $this->get( 'processed' );
919
920
			if ( $properties['processed'] != $this->previous_status ) {
921
				$this->is_status_changed = true;
922
			}
923
		}
924
925
		if ( ! is_array( $this->data ) ) {
926
			$this->data = array();
927
		}
928
929
		foreach ( $properties as $key => $value ) {
930
			if ( ! in_array( $key, array_merge( self::$string_cols, self::$int_cols, self::$float_cols ) ) ) {
931
				$this->meta_data[ $key ] = $value;
932
				unset( $properties[ $key ] );
933
			}
934
		}
935
936
		$this->data = array_merge( $this->data, $properties );
937
		return $this;
938
	}
939
940
	/**
941
	 * Returns an array containing the parameter format so that this can be used in
942
	 * $wpdb methods (update, insert etc.)
943
	 *
944
	 * @access private
945
	 * @since 3.8.9
946
	 *
947
	 * @param array $data
948
	 * @return array
949
	 */
950
	private function get_data_format( $data ) {
951
		$format = array();
952
953
		foreach ( $data as $key => $value ) {
954
			$format[] = self::get_column_format( $key );
955
		}
956
957
		return $format;
958
	}
959
960
	/**
961
	 * Saves the purchase log back to the database
962
	 *
963
	 * @access public
964
	 * @since 3.8.9
965
	 *
966
	 * @return void
967
	 */
968
	public function save() {
969
		global $wpdb;
970
971
		do_action( 'wpsc_purchase_log_pre_save', $this );
972
973
		// $this->args['col'] is empty when the record is new and needs to
974
		// be inserted. Otherwise, it means we're performing an update
975
		$where_col = $this->args['col'];
976
977
		$result = false;
978
979
		if ( $where_col ) {
980
			$where_val = $this->args['value'];
981
			$where_format = self::get_column_format( $where_col );
982
			do_action( 'wpsc_purchase_log_pre_update', $this );
983
			self::delete_cache( $where_val, $where_col );
984
			$data = apply_filters( 'wpsc_purchase_log_update_data', $this->data );
985
			$format = $this->get_data_format( $data );
986
			$result = $wpdb->update( WPSC_TABLE_PURCHASE_LOGS, $data, array( $where_col => $where_val ), $format, array( $where_format ) );
987
			do_action( 'wpsc_purchase_log_update', $this );
988
		} else {
989
			do_action( 'wpsc_purchase_log_pre_insert', $this );
990
			$data = apply_filters( 'wpsc_purchase_log_insert_data', $this->data );
991
			$format = $this->get_data_format( $data );
992
			$result = $wpdb->insert( WPSC_TABLE_PURCHASE_LOGS, $data, $format );
993
994
			if ( $result ) {
995
				$this->set( 'id', $wpdb->insert_id );
996
997
				// set $this->args so that properties can be lazy loaded right after
998
				// the row is inserted into the db
999
				$this->args = array(
1000
					'col'   => 'id',
1001
					'value' => $this->get( 'id' ),
1002
				);
1003
			}
1004
1005
			do_action( 'wpsc_purchase_log_insert', $this );
1006
		}
1007
1008
		if ( $this->is_status_changed ) {
1009
1010
			if ( $this->is_transaction_completed() ) {
1011
				$this->update_downloadable_status();
1012
			}
1013
1014
			$current_status          = $this->get( 'processed' );
1015
			$previous_status         = $this->previous_status;
1016
			$this->previous_status   = $current_status;
1017
			$this->is_status_changed = false;
1018
1019
			do_action( 'wpsc_update_purchase_log_status', $this->get( 'id' ), $current_status, $previous_status, $this );
1020
		}
1021
1022
		if ( ! empty( $this->meta_data ) ) {
1023
			$this->save_meta();
1024
		}
1025
1026
		do_action( 'wpsc_purchase_log_save', $this );
1027
1028
		return $result;
1029
	}
1030
1031
	/**
1032
	 * Save meta data for purchase log, if any was set via set().
1033
	 *
1034
	 * @access public
1035
	 * @since  4.0
1036
	 *
1037
	 * @return WPSC_Purchase_Log  The current object (for method chaining)
1038
	 */
1039
	public function save_meta() {
1040
		do_action( 'wpsc_purchase_log_pre_save_meta', $this );
1041
1042
		$meta = $this->get_meta();
1043
1044
		foreach ( $meta as $key => $value ) {
1045
			wpsc_update_purchase_meta( $this->get( 'id' ), $key, $value );
1046
		}
1047
1048
		do_action( 'wpsc_purchase_log_save_meta', $this );
1049
1050
		return $this;
1051
	}
1052
1053
	private function update_downloadable_status() {
1054
		global $wpdb;
1055
1056
		foreach ( $this->get_items() as $item ) {
1057
			$wpdb->update(
1058
				WPSC_TABLE_DOWNLOAD_STATUS,
1059
				array(
1060
					'active' => '1'
1061
				),
1062
				array(
1063
					'cartid'  => $item->id,
1064
					'purchid' => $this->get( 'id' ),
1065
				)
1066
			);
1067
		}
1068
	}
1069
1070
	public function have_downloads_locked() {
1071
		global $wpdb;
1072
1073
		$sql = $wpdb->prepare( "SELECT `ip_number` FROM `" . WPSC_TABLE_DOWNLOAD_STATUS . "` WHERE `purchid` = %d ", $this->get( 'id' ) );
1074
		$ip_number = $wpdb->get_var( $sql );
1075
1076
		return $ip_number;
1077
	}
1078
1079
	/**
1080
	 * Adds ability to retrieve a purchase log by a meta key or value.
1081
	 *
1082
	 * @since  4.0
1083
	 *
1084
	 * @param  string $key   Meta key. Optional.
1085
	 * @param  string $value Meta value. Optional.
1086
	 *
1087
	 * @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.
1088
	 */
1089
	public static function get_log_by_meta( $key = '', $value = '' ) {
1090
1091
		if ( empty( $key ) && empty( $value ) ) {
1092
			return false;
1093
		}
1094
1095
		global $wpdb;
1096
1097
		if ( ! empty( $key ) && empty( $value ) ) {
1098
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_key = %s', $key );
1099
		} else if ( empty( $key ) && ! empty( $value ) ) {
1100
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_value = %s', $value );
1101
		} else {
1102
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_key = %s AND meta_value = %s', $key, $value );
1103
		}
1104
1105
		$id = $wpdb->get_var( $sql );
1106
1107
		if ( $id ) {
1108
			return new WPSC_Purchase_Log( $id );
1109
		} else {
1110
			return false;
1111
		}
1112
	}
1113
1114
	public function get_next_log_id() {
1115
		if ( ! $this->exists() ) {
1116
			return false;
1117
		}
1118
1119
		global $wpdb;
1120
1121
		$sql = $wpdb->prepare(
1122
			"SELECT MIN(id) FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE id > %d",
1123
			$this->get( 'id' )
1124
		);
1125
1126
		return $wpdb->get_var( $sql );
1127
	}
1128
1129
	public function get_previous_log_id() {
1130
		if ( ! $this->exists() ) {
1131
			return false;
1132
		}
1133
1134
		global $wpdb;
1135
1136
		$sql = $wpdb->prepare(
1137
			"SELECT MAX(id) FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE id < %d",
1138
			$this->get( 'id' )
1139
		);
1140
1141
		return $wpdb->get_var( $sql );
1142
	}
1143
1144
	public function is_transaction_completed() {
1145
		return WPSC_Purchase_Log::is_order_status_completed( $this->get( 'processed' ) );
1146
	}
1147
1148
	public function can_edit() {
1149
		if ( null === $this->can_edit ) {
1150
			$can_edit = current_user_can( 'edit_others_posts' ) && ! $this->is_transaction_completed();
1151
			$this->can_edit = apply_filters( 'wpsc_can_edit_order', $can_edit, $this );
1152
		}
1153
1154
		return $this->can_edit;
1155
	}
1156
1157
	public function is_order_received() {
1158
		return $this->get( 'processed' ) == self::ORDER_RECEIVED;
1159
	}
1160
1161
	public function is_incomplete_sale() {
1162
		return $this->get( 'processed' ) == self::INCOMPLETE_SALE;
1163
	}
1164
1165
	public function is_accepted_payment() {
1166
		return $this->get( 'processed' ) == self::ACCEPTED_PAYMENT;
1167
	}
1168
1169
	public function is_job_dispatched() {
1170
		return $this->get( 'processed' ) == self::JOB_DISPATCHED;
1171
	}
1172
1173
	public function is_closed_order() {
1174
		return $this->get( 'processed' ) == self::CLOSED_ORDER;
1175
	}
1176
1177
	public function is_payment_declined() {
1178
		return $this->get( 'processed' ) == self::PAYMENT_DECLINED;
1179
	}
1180
1181
	public function is_refunded() {
1182
		return $this->get( 'processed' ) == self::REFUNDED;
1183
	}
1184
1185
	public function is_refund_pending() {
1186
		return $this->get( 'processed' ) == self::REFUND_PENDING;
1187
	}
1188
1189
	/*
1190
	 * Utility methods using the $purchlogitem global.. Global usage to be replaced in the future.
1191
	 *
1192
	 * TODO: seriously get rid of all these badly coded purchaselogs.functions.php functions
1193
	 * and wpsc_purchaselogs/wpsc_purchaselogs_items classes.
1194
	 */
1195
1196
	/**
1197
	 * Init the purchase log items for this purchase log.
1198
	 *
1199
	 * @since  4.0
1200
	 *
1201
	 * @return wpsc_purchaselogs_items|false The purhchase log item object or false.
1202
	 */
1203
	public function init_items() {
1204
		global $purchlogitem;
1205
		if ( ! $this->exists() ) {
1206
			return false;
1207
		}
1208
1209
		$purchlogitem = new wpsc_purchaselogs_items( $this->get( 'id' ), $this );
1210
	}
1211
1212
	public function buyers_name() {
1213
		global $purchlogitem;
1214
1215
		if ( null === $this->buyers_name ) {
1216
			$first_name = $last_name = '';
1217
1218
			if ( isset( $purchlogitem->userinfo['billingfirstname'] ) ) {
1219
				$first_name = $purchlogitem->userinfo['billingfirstname']['value'];
1220
			}
1221
1222
			if ( isset( $purchlogitem->userinfo['billinglastname'] ) ) {
1223
				$last_name = ' ' . $purchlogitem->userinfo['billinglastname']['value'];
1224
			}
1225
1226
			$this->buyers_name = trim( $first_name . $last_name );
1227
		}
1228
1229
		return $this->buyers_name;
1230
	}
1231
1232
	public function buyers_city() {
1233
		global $purchlogitem;
1234
1235
		if ( null === $this->buyers_city ) {
1236
			$this->buyers_city = isset( $purchlogitem->userinfo['billingcity']['value'] ) ? $purchlogitem->userinfo['billingcity']['value'] : '';
1237
		}
1238
1239
		return $this->buyers_city;
1240
	}
1241
1242
	public function buyers_email() {
1243
		global $purchlogitem;
1244
1245
		if ( null === $this->buyers_email ) {
1246
			$this->buyers_email = isset( $purchlogitem->userinfo['billingemail']['value'] ) ? $purchlogitem->userinfo['billingemail']['value'] : '';
1247
		}
1248
1249
		return $this->buyers_email;
1250
	}
1251
1252
	public function buyers_address() {
1253
		global $purchlogitem;
1254
1255
		if ( null === $this->buyers_address ) {
1256
			$this->buyers_address = isset( $purchlogitem->userinfo['billingaddress']['value'] ) ? nl2br( esc_html( $purchlogitem->userinfo['billingaddress']['value'] ) ) : '';
1257
		}
1258
1259
		return $this->buyers_address;
1260
	}
1261
1262
	public function buyers_state_and_postcode() {
1263
		global $purchlogitem;
1264
1265
		if ( null === $this->buyers_state_and_postcode ) {
1266
1267
			if ( is_numeric( $this->get( 'billing_region' ) ) ) {
1268
				$state = wpsc_get_region( $this->get( 'billing_region' ) );
1269
			} else {
1270
				$state = $purchlogitem->userinfo['billingstate']['value'];
1271
				$state = is_numeric( $state ) ? wpsc_get_region( $state ) : $state;
1272
			}
1273
1274
			$output = esc_html( $state );
1275
1276
			if ( isset( $purchlogitem->userinfo['billingpostcode']['value'] ) && ! empty( $purchlogitem->userinfo['billingpostcode']['value'] ) ) {
1277
				if ( $output ) {
1278
					$output .= ', '; // TODO determine if it's ok to make this a space only (like shipping_state_and_postcode)
1279
				}
1280
				$output .= $purchlogitem->userinfo['billingpostcode']['value'];
1281
			}
1282
1283
			$this->buyers_state_and_postcode = $output;
1284
		}
1285
1286
		return $this->buyers_state_and_postcode;
1287
	}
1288
1289
	public function buyers_country() {
1290
		global $purchlogitem;
1291
1292
		if ( null === $this->buyers_country ) {
1293
			$this->buyers_country = isset( $purchlogitem->userinfo['billingcountry']['value'] ) ? wpsc_get_country( $purchlogitem->userinfo['billingcountry']['value'] ) : '';
1294
		}
1295
1296
		return $this->buyers_country;
1297
	}
1298
1299
	public function buyers_phone() {
1300
		global $purchlogitem;
1301
1302
		if ( null === $this->buyers_phone ) {
1303
			$this->buyers_phone = isset( $purchlogitem->userinfo['billingphone']['value'] ) ? $purchlogitem->userinfo['billingphone']['value'] : '';
1304
		}
1305
1306
		return $this->buyers_phone;
1307
	}
1308
1309
	public function shipping_name() {
1310
		global $purchlogitem;
1311
1312
		if ( null === $this->shipping_name ) {
1313
			$this->shipping_name = isset( $purchlogitem->shippinginfo['shippingfirstname']['value'] ) ? $purchlogitem->shippinginfo['shippingfirstname']['value'] : '';
1314
1315
			if ( isset( $purchlogitem->shippinginfo['shippinglastname']['value'] ) ) {
1316
				$this->shipping_name .= ' ' . $purchlogitem->shippinginfo['shippinglastname']['value'];
1317
			}
1318
		}
1319
1320
		return $this->shipping_name;
1321
	}
1322
1323
	public function shipping_city() {
1324
		global $purchlogitem;
1325
1326
		if ( null === $this->shipping_city ) {
1327
			$this->shipping_city = isset( $purchlogitem->shippinginfo['shippingcity']['value'] ) ? $purchlogitem->shippinginfo['shippingcity']['value'] : '';
1328
		}
1329
1330
		return $this->shipping_city;
1331
	}
1332
1333
	public function shipping_address() {
1334
		global $purchlogitem;
1335
1336
		if ( null === $this->shipping_address ) {
1337
			$this->shipping_address = isset( $purchlogitem->shippinginfo['shippingaddress']['value'] ) ? nl2br( esc_html( $purchlogitem->shippinginfo['shippingaddress']['value'] ) ) : '';
1338
		}
1339
1340
		return $this->shipping_address;
1341
	}
1342
1343
	public function shipping_state_and_postcode() {
1344
		global $purchlogitem;
1345
1346
		if ( null === $this->shipping_state_and_postcode ) {
1347
			if ( is_numeric( $this->get( 'shipping_region' ) ) ) {
1348
				$output = wpsc_get_region( $this->get( 'shipping_region' ) );
1349
			} else {
1350
				$state = $purchlogitem->shippinginfo['shippingstate']['value'];
1351
				$output = is_numeric( $state ) ? wpsc_get_region( $state ) : $state;
1352
			}
1353
1354
			if ( !empty( $purchlogitem->shippinginfo['shippingpostcode']['value'] ) ){
1355
				if ( $output ) {
1356
					$output .= ' ';
1357
				}
1358
1359
				$output .= $purchlogitem->shippinginfo['shippingpostcode']['value'];
1360
			}
1361
1362
			$this->shipping_state_and_postcode = $output;
1363
		}
1364
1365
		return $this->shipping_state_and_postcode;
1366
	}
1367
1368
	public function shipping_country() {
1369
		global $purchlogitem;
1370
1371
		if ( null === $this->shipping_country ) {
1372
			$this->shipping_country = isset( $purchlogitem->shippinginfo['shippingcountry'] )
1373
				? wpsc_get_country( $purchlogitem->shippinginfo['shippingcountry']['value'] )
1374
				: '';
1375
		}
1376
1377
		return $this->shipping_country;
1378
	}
1379
1380
	public function payment_method() {
1381
		global $nzshpcrt_gateways;
1382
1383
		if ( null === $this->payment_method ) {
1384
			if ( 'wpsc_merchant_testmode' == $this->get( 'gateway' ) ) {
1385
				$this->payment_method = __( 'Manual Payment', 'wp-e-commerce' );
1386
			} else {
1387
				foreach ( (array) $nzshpcrt_gateways as $gateway ) {
1388
					if ( isset( $gateway['internalname'] ) && $gateway['internalname'] == $this->get( 'gateway' ) ) {
1389
						$this->payment_method = $gateway['name'];
1390
					}
1391
				}
1392
1393
				if ( ! $this->payment_method ) {
1394
					$this->payment_method = $this->get( 'gateway' );
1395
				}
1396
			}
1397
		}
1398
1399
		return $this->payment_method;
1400
	}
1401
1402
	public function shipping_method() {
1403
		global $wpsc_shipping_modules;
1404
1405
		if ( null === $this->shipping_method ) {
1406
1407
			if ( ! empty( $wpsc_shipping_modules[ $this->get( 'shipping_method' ) ] ) ) {
1408
				$this->shipping_method = $wpsc_shipping_modules[ $this->get( 'shipping_method' ) ]->getName();
1409
			} else {
1410
				$this->shipping_method = $this->get( 'shipping_method' );
1411
			}
1412
		}
1413
1414
		return $this->shipping_method;
1415
	}
1416
1417
	/**
1418
	 * Returns base shipping should make a function to calculate items shipping as well
1419
	 *
1420
	 * @since  4.0
1421
	 *
1422
	 * @param  boolean $numeric Return numeric value.
1423
	 *
1424
	 * @return mixed
1425
	 */
1426
	public function discount( $numeric = false ) {
1427
		$discount = $this->get( 'discount_value' );
1428
		if ( ! $numeric ) {
1429
			$discount = wpsc_currency_display( $discount, array( 'display_as_html' => false ) );
1430
		}
1431
1432
		return $discount;
1433
	}
1434
1435
	/**
1436
	 * Returns base shipping should make a function to calculate items shipping as well
1437
	 *
1438
	 * @since  4.0
1439
	 *
1440
	 * @param  boolean $numeric       Return numeric value.
1441
	 * @param  boolean $include_items Whether to calculate per-item-shipping.
1442
	 *
1443
	 * @return mixed
1444
	 */
1445
	public function shipping( $numeric = false, $include_items = false ) {
1446
		$total_shipping = $this->get( 'base_shipping' );
1447
1448
		if ( $include_items ) {
1449
			$total_shipping = $this->meta_data['total_shipping'];
1450
		}
1451
1452
		if ( ! $numeric ) {
1453
			$total_shipping = wpsc_currency_display( $total_shipping, array( 'display_as_html' => false ) );
1454
		}
1455
1456
		return $total_shipping;
1457
	}
1458
1459
	/**
1460
	 * Returns taxes total.
1461
	 *
1462
	 * @since  4.0
1463
	 *
1464
	 * @param  boolean $numeric Return numeric value.
1465
	 *
1466
	 * @return mixed
1467
	 */
1468
	public function taxes( $numeric = false ) {
1469
		$taxes = $this->get( 'wpec_taxes_total' );
1470
1471
		if ( ! $numeric ) {
1472
			$taxes = wpsc_currency_display( $taxes, array( 'display_as_html' => false ) );
1473
		}
1474
1475
		return $taxes;
1476
	}
1477
1478
	public function total_price() {
1479
		global $purchlogitem;
1480
1481
		$total = $purchlogitem->totalAmount - $this->discount( true ) + $this->shipping( true ) + $this->taxes( true );
1482
		return wpsc_currency_display( $total, array( 'display_as_html' => false ) );
1483
	}
1484
1485
}
1486