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

WPSC_Purchase_Log   F

Complexity

Total Complexity 197

Size/Duplication

Total Lines 1465
Duplicated Lines 0 %

Coupling/Cohesion

Components 11
Dependencies 5

Importance

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