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

WPSC_Purchase_Log::shipping_country()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 0
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
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_Purchase_Log
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 );
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