Completed
Push — master ( 30b9a3...a81e2b )
by Justin
11:10 queued 05:29
created

WPSC_Purchase_Log::get_total_refunded()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
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
	private static $multiple_meta = array(
109
		'notes' => 1,
110
	);
111
112
	/**
113
	 * Contains the constructor arguments. This array is necessary because we will
114
	 * lazy load the DB row into $this->data whenever necessary. Lazy loading is,
115
	 * in turn, necessary because sometimes right after saving a new record, we need
116
	 * to fetch a property with the same object.
117
	 *
118
	 * @access private
119
	 * @since 3.8.9
120
	 *
121
	 * @var array
122
	 */
123
	private $args = array(
124
		'col'   => '',
125
		'value' => '',
126
	);
127
128
   protected $buyers_name = null;
129
   protected $buyers_city = null;
130
   protected $buyers_email = null;
131
   protected $buyers_address = null;
132
   protected $buyers_state_and_postcode = null;
133
   protected $buyers_country = null;
134
   protected $buyers_phone = null;
135
   protected $shipping_name = null;
136
   protected $shipping_address = null;
137
   protected $shipping_city = null;
138
   protected $shipping_state_and_postcode = null;
139
   protected $shipping_country = null;
140
   protected $payment_method = null;
141
   protected $shipping_method = null;
142
143
	/**
144
	 * Get the SQL query format for a column
145
	 *
146
	 * @since 3.8.9
147
	 * @param  string $col Name of the column
148
	 * @return string      Placeholder
149
	 */
150
	private static function get_column_format( $col ) {
151
		if ( in_array( $col, self::$string_cols ) ) {
152
			return '%s';
153
		}
154
155
		if ( in_array( $col, self::$int_cols ) ) {
156
			return '%d';
157
		}
158
159
		return '%f';
160
	}
161
162
	/**
163
	 * Query the purchase log table to get sales and earning stats
164
	 *
165
	 * Accepts an array of arguments:
166
	 * 	- 'ids': IDs of products for which you want to get stats
167
	 * 	- 'products': array of WPSC_Product objects for which you want to get stats
168
	 * 	- 'start' and 'end': the timestamp range (integers) during which you want
169
	 * 	                     to collect the stats.
170
	 * 	                     You can use none, either, or both of them.
171
	 * 	                     Note that the [start, end) interval is a left-closed,
172
	 * 	                     right-open.
173
	 * 	                     E.g.: to get stats from Jan 1st, 2013 to
174
	 * 	                     Dec 31st, 2013 (23:59:59),
175
	 * 	                     set "start" to the timestamp for Jan 1st, 2013, set
176
	 * 	                     "end" to the timestamp for Jan 1st, 2014
177
	 *  - 'order': what to sort the results by, defaults to 'id'.
178
	 *            Can be 'ids', 'sales', 'earnings' or empty string to disable sorting
179
	 *  - 'orderby': how to sort the results, defaults to 'ASC'.
180
	 *              Can be 'ASC', 'DESC' (lowercase is fine too)
181
	 *  - 'per_page': How many items to fetch, defaults to 0, which fetches all results
182
	 *  - 'page': Which page of the results to fetch, defaults to 1.
183
	 *            Has no effect if per_page is set to 0.
184
	 *
185
	 * @since 3.8.14
186
	 * @param  array|string $args Arguments
187
	 * @return array       Array containing 'sales' and 'earnings' stats
188
	 */
189
	public static function fetch_stats( $args ) {
190
		global $wpdb;
191
192
		$defaults = array(
193
			'ids'      => array(), // IDs of the products to be queried
194
			'products' => array(), // Array of WPSC_Products objects
195
			'start'    => 0,       // Int. timestamp, has to be UTC
196
			'end'      => 0,       // Int. timestamp, has to be UTC
197
			'order'    => 'ASC',
198
			'orderby'  => 'id',
199
			'per_page' => 0,
200
			'page'     => 1,
201
		);
202
203
		$args = wp_parse_args( $args, $defaults );
204
205
		// convert more advanced date / time args into "start" and "end"
206
		$args = self::convert_date_args( $args );
207
208
		// build an array of WPSC_Product objects based on 'ids' and 'products' args
209
		$products = array_merge(
210
			$args['products'],
211
			array_map( array( 'WPSC_Product', 'get_instance' ), $args['ids'] )
212
		);
213
214
		// eliminate duplicates (Note: usage of this requires PHP 5.2.9)
215
		$products = array_unique( $products, SORT_REGULAR );
216
217
		if ( empty( $products ) ) {
218
			return null;
219
		}
220
221
		$needs_fetching = array();
222
223
		$stats = array(
224
			'sales'    => 0,
225
			'earnings' => 0,
226
		);
227
228
		// if we have date restriction, that means we won't be able to rely upon
229
		// individual stats cache inside WPSC_Product objects
230
		$has_date_restriction = ! ( empty( $args['start'] ) && empty( $args['end'] ) );
231
232
		// if we do NOT have date restriction, find out which WPSC_Product object
233
		// has stats cache, and which don't
234
		if ( ! $has_date_restriction ) {
235
			foreach ( $products as $product ) {
236
				// store the ID if this product doesn't have a stats cache yet
237
				if ( $product->post->_wpsc_stats === '' ) {
238
					$needs_fetching[] = $product->post->ID;
239
				} else {
240
241
					// tally up the sales and earnings if this one has cache already
242
					$prod_meta = get_post_meta( $product->post->ID, '_wpsc_stats', true );
243
244
					if ( isset( $prod_meta['sales'] ) && isset( $prod_meta['earnings'] ) ) {
245
						$stats['sales']    += $prod_meta['sales'];
246
						$stats['earnings'] += $prod_meta['earnings'];
247
					}
248
					$needs_fetching[]   = $product->post->ID;
249
				}
250
			}
251
		}
252
253
		// never hurts to make sure
254
		$needs_fetching = array_map( 'absint', $needs_fetching );
255
256
		// pagination arguments
257
		$limit = '';
258
259
		if ( ! empty( $args['per_page'] ) ) {
260
			$offset = ( $args['page'] - 1 ) * $args['per_page'];
261
			$limit = "LIMIT " . absint( $args['per_page'] ) . " OFFSET " . absint( $offset );
262
		}
263
264
		// sorting
265
		$order = '';
266
267
		if ( ! empty( $args['orderby'] ) )
268
			$order = "ORDER BY " . esc_sql( $args['orderby'] ) . " " . esc_sql( $args['order'] );
269
270
		// date
271
		$where = "WHERE p.processed IN (3, 4, 5)";
272
273
		if ( $has_date_restriction ) {
274
			// start date equal or larger than $args['start']
275
			if ( ! empty( $args['start'] ) )
276
				$where .= " AND CAST(p.date AS UNSIGNED) >= " . absint( $args['start'] );
277
278
			// end date less than $args['end'].
279
			// the "<" sign is not a typo, such upper limit makes it easier for
280
			// people to specify range.
281
			// E.g.: [1/1/2013 - 1/1/2014) rather than:
282
			//       [1/1/2013 - 31/12/2013 23:59:59]
283
			if ( ! empty( $args['end'] ) )
284
				$where .= " AND CAST(p.date AS UNSIGNED) < " . absint( $args['end'] );
285
		}
286
287
		// assemble the SQL query
288
		$sql = "
289
			SELECT cc.prodid AS id, SUM(cc.quantity) AS sales, SUM(cc.quantity * cc.price) AS earnings
290
			FROM $wpdb->wpsc_purchase_logs AS p
291
			INNER JOIN
292
				$wpdb->wpsc_cart_contents AS cc
293
				ON p.id = cc.purchaseid AND cc.prodid IN (" . implode( ', ', $needs_fetching ) . ")
294
			{$where}
295
			GROUP BY cc.prodid
296
			{$order}
297
			{$limit}
298
		";
299
300
		// if the result is cached, don't bother querying the database
301
		$cache_key = md5( $sql );
302
		$results   = wp_cache_get( $cache_key, 'wpsc_purchase_logs_stats' );
303
304
		if ( false === $results ) {
305
			$results = $wpdb->get_results( $sql );
306
			wp_cache_set( $cache_key, $results, 'wpsc_purchase_logs_stats', WPSC_PURCHASE_LOG_STATS_CACHE_EXPIRE );
307
		}
308
309
		// tally up the sales and earnings from the query results
310
		foreach ( $results as $row ) {
311
			if ( ! $has_date_restriction ) {
312
				$product           = WPSC_Product::get_instance( $row->id );
313
				$product->sales    = $row->sales;
314
				$product->earnings = $row->earnings;
315
			}
316
317
			$stats['sales']    += $row->sales;
318
			$stats['earnings'] += $row->earnings;
319
		}
320
321
		return $stats;
322
	}
323
324
	/**
325
	 * Convert advanced date/time arguments like year, month, day, 'ago' etc.
326
	 * into basic arguments which are "start" and "end".
327
	 *
328
	 * @since  3.8.14
329
	 * @param  array $args Arguments
330
	 * @return array       Arguments after converting
331
	 */
332
	private static function convert_date_args( $args ) {
333
		// TODO: Implement this
334
		return $args;
335
	}
336
337
	/**
338
	 * Get overall sales and earning stats for just one product
339
	 *
340
	 * @since 3.8.14
341
	 * @param  int $id ID of the product
342
	 * @return array   Array containing 'sales' and 'earnings' stats
343
	 */
344
	public static function get_stats_for_product( $id, $args = '' ) {
345
346
		$product = WPSC_Product::get_instance( $id );
347
348
		// if this product has variations
349
		if ( $product->has_variations ) {
350
			// get total stats of variations
351
			$args['products'] = $product->variations;
352
		} else {
353
			// otherwise, get stats of only this product
354
			$args['products'] = array( $product );
355
		}
356
357
		return self::fetch_stats( $args );
358
	}
359
360
	/**
361
	 * Check whether the status code indicates a completed status
362
	 *
363
	 * @since 3.8.13
364
	 * @param int  $status Status code
365
	 * @return boolean
366
	 */
367
	public static function is_order_status_completed( $status ) {
368
		$completed_status = apply_filters( 'wpsc_order_status_completed', array(
369
			self::ACCEPTED_PAYMENT,
370
			self::JOB_DISPATCHED,
371
			self::CLOSED_ORDER,
372
		) );
373
374
		return in_array( $status, $completed_status );
375
	}
376
377
	/**
378
	 * Update cache of the passed log object
379
	 *
380
	 * @access public
381
	 * @static
382
	 * @since 3.8.9
383
	 *
384
	 * @param WPSC_Purchase_Log $log The log object that you want to store into cache
385
	 * @return void
386
	 */
387
	public static function update_cache( &$log ) {
388
		return $log->update_caches();
389
	}
390
391
	/**
392
	 * Update caches.
393
	 *
394
	 * @access public
395
	 * @static
396
	 * @since 4.0
397
	 *
398
	 * @return void
399
	 */
400
	public function update_caches() {
401
402
		// wpsc_purchase_logs stores the data array, while wpsc_purchase_logs_sessionid stores the
403
		// log id that's associated with the sessionid
404
		$id = $this->get( 'id' );
405
		wp_cache_set( $id, $this->data, 'wpsc_purchase_logs' );
406
407
		if ( $sessionid = $this->get( 'sessionid' ) ) {
408
			wp_cache_set( $sessionid, $id, 'wpsc_purchase_logs_sessionid' );
409
		}
410
411
		wp_cache_set( $id, $this->log_items, 'wpsc_purchase_log_items' );
412
		do_action( 'wpsc_purchase_log_update_cache', $this );
413
	}
414
415
	/**
416
	 * Deletes cache of a log (either by using the log ID or sessionid)
417
	 *
418
	 * @access public
419
	 * @static
420
	 * @since 3.8.9
421
	 *
422
	 * @param string $value The value to query
423
	 * @param string $col Optional. Defaults to 'id'. Whether to delete cache by using
424
	 *                    a purchase log ID or sessionid
425
	 * @return void
426
	 */
427
	public static function delete_cache( $value, $col = 'id' ) {
428
		// this will pull from the old cache, so no worries there
429
		$log = new WPSC_Purchase_Log( $value, $col );
430
		$log->delete_caches( $value, $col );
431
	}
432
433
	/**
434
	 * Deletes caches.
435
	 *
436
	 * @access public
437
	 * @static
438
	 * @since 4.0
439
	 *
440
	 * @param string|null $value Optional (left for back-compatibility). The value which was queried.
441
	 * @param string|null $col   Optional (left for back-compatibility). The column used as the identifier.
442
	 *
443
	 * @return void
444
	 */
445
	public function delete_caches( $value = null, $col = null ) {
446
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_logs' );
447
		wp_cache_delete( $this->get( 'sessionid' ), 'wpsc_purchase_logs_sessionid' );
448
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_log_items' );
449
		wp_cache_delete( $this->get( 'id' ), 'wpsc_purchase_meta' );
450
451
		if ( null === $value ) {
452
			$value = $this->args['value'];
453
		}
454
455
		if ( null === $col ) {
456
			$col = $this->args['col'];
457
		}
458
459
		do_action( 'wpsc_purchase_log_delete_cache', $this, $value, $col );
460
	}
461
462
	/**
463
	 * Deletes a log from the database.
464
	 *
465
	 * @access  public
466
	 * @since   3.8.9
467
	 *
468
	 * @uses  $wpdb                              Global database instance.
469
	 * @uses  wpsc_is_store_admin()              Check user has admin capabilities.
470
	 * @uses  WPSC_Purchase_Log::delete_cache()  Delete purchaselog cache.
471
	 * @uses  WPSC_Claimed_Stock                 Claimed Stock class.
472
	 *
473
	 * @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...
474
	 * @return  boolean            Deleted successfully.
475
	 */
476
	public function delete( $log_id = false ) {
477
478
		global $wpdb;
479
480
		if ( ! ( isset( $this ) && get_class( $this ) == __CLASS__ ) ) {
481
			_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' );
482
		}
483
484
		if ( false !== $log_id ) {
485
			_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.' );
486
		}
487
488
		if ( ! wpsc_is_store_admin() ) {
489
			return false;
490
		}
491
492
		$log_id = $this->get( 'id' );
493
494
		if ( $log_id > 0 ) {
495
496
			do_action( 'wpsc_purchase_log_before_delete', $log_id );
497
498
			$this->delete_caches();
499
500
			// Delete claimed stock
501
			$purchlog_status = $wpdb->get_var( $wpdb->prepare( "SELECT `processed` FROM `" . WPSC_TABLE_PURCHASE_LOGS . "` WHERE `id`= %d", $log_id ) );
502
			if ( $purchlog_status == WPSC_Purchase_Log::CLOSED_ORDER || $purchlog_status == WPSC_Purchase_Log::INCOMPLETE_SALE ) {
503
				$claimed_query = new WPSC_Claimed_Stock( array(
504
					'cart_id'        => $log_id,
505
					'cart_submitted' => 1
506
				) );
507
				$claimed_query->clear_claimed_stock( 0 );
508
			}
509
510
			// Delete cart content, submitted data, then purchase log
511
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_CART_CONTENTS . "` WHERE `purchaseid` = %d", $log_id ) );
512
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_SUBMITTED_FORM_DATA . "` WHERE `log_id` IN (%d)", $log_id ) );
513
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_PURCHASE_LOGS . "` WHERE `id` = %d LIMIT 1", $log_id ) );
514
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_PURCHASE_META . "` WHERE `wpsc_purchase_id` = %d", $log_id ) );
515
			$wpdb->query( $wpdb->prepare( "DELETE FROM `" . WPSC_TABLE_DOWNLOAD_STATUS . "` WHERE `purchid` = %d ", $log_id ) );
516
517
			do_action( 'wpsc_purchase_log_delete', $log_id );
518
519
			return true;
520
521
		}
522
523
		return false;
524
525
	}
526
527
	/**
528
	 * Constructor of the purchase log object. If no argument is passed, this simply
529
	 * create a new empty object. Otherwise, this will get the purchase log from the
530
	 * DB either by using purchase log id or sessionid (specified by the 2nd argument).
531
	 *
532
	 * Eg:
533
	 *
534
	 * // get purchase log with ID number 23
535
	 * $log = new WPSC_Purchase_Log( 23 );
536
	 *
537
	 * // get purchase log with sessionid "asdf"
538
	 * $log = new WPSC_Purchase_Log( 'asdf', 'sessionid' )
539
	 *
540
	 * @access public
541
	 * @since 3.8.9
542
	 *
543
	 * @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...
544
	 * @param string $col Optional. Defaults to 'id'.
545
	 */
546
	public function __construct( $value = false, $col = 'id' ) {
547
		if ( false === $value ) {
548
			return;
549
		}
550
551
		if ( is_array( $value ) ) {
552
			$this->set( $value );
553
			return;
554
		}
555
556
		global $wpdb;
557
558
		if ( ! in_array( $col, array( 'id', 'sessionid' ) ) ) {
559
			return;
560
		}
561
562
		// store the constructor args into an array so that later we can lazy load the data
563
		$this->args = array(
564
			'col'   => $col,
565
			'value' => $value,
566
		);
567
568
		// if the sessionid is in cache, pull out the id
569
		if ( $col == 'sessionid'  && $id = wp_cache_get( $value, 'wpsc_purchase_logs_sessionid' ) ) {
570
				$col = 'id';
571
				$value = $id;
572
		}
573
574
		// if the id is specified, try to get from cache
575
		if ( $col == 'id' ) {
576
			$this->data = wp_cache_get( $value, 'wpsc_purchase_logs' );
577
			$this->log_items = wp_cache_get( $value, 'wpsc_purchase_log_items' );
578
		}
579
580
		// cache exists
581
		if ( $this->data ) {
582
			$this->set_meta_props();
583
			$this->fetched = true;
584
			$this->exists  = true;
585
			return;
586
		}
587
	}
588
589
	private function set_total_shipping() {
590
591
		$base_shipping  = $this->get( 'base_shipping' );
592
		$item_shipping  = wp_list_pluck( $this->get_items(), 'pnp' );
593
594
		$this->meta_data['total_shipping'] = $base_shipping + array_sum( $item_shipping );
595
596
		return $this->meta_data['total_shipping'];
597
	}
598
599
	private function set_gateway_name() {
600
		global $wpsc_gateways;
601
		$gateway = $this->get( 'gateway' );
602
		$gateway_name = $gateway;
603
604
		if( 'wpsc_merchant_testmode' == $gateway )
605
			$gateway_name = __( 'Manual Payment', 'wp-e-commerce' );
606
		elseif ( isset( $wpsc_gateways[$gateway] ) )
607
			$gateway_name = $wpsc_gateways[$gateway]['name'];
608
609
		$this->meta_data['gateway_name'] = $gateway_name;
610
		return $this->meta_data['gateway_name'];
611
	}
612
613
	private function set_shipping_method_names() {
614
		global $wpsc_shipping_modules;
615
616
		$shipping_method = $this->get( 'shipping_method' );
617
		$shipping_option = $this->get( 'shipping_option' );
618
		$shipping_method_name = $shipping_method;
619
		$shipping_option_name = $shipping_option;
620
621
		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...
622
			$shipping_class = $wpsc_shipping_modules[$shipping_method];
623
			$shipping_method_name = $shipping_class->name;
624
		}
625
626
		$this->meta_data['shipping_method_name'] = $shipping_method_name;
627
		$this->meta_data['shipping_option_name'] = $shipping_option_name;
628
	}
629
630
	private function set_meta_props() {
631
632
		foreach ( wpsc_get_purchase_custom( $this->get( 'id' ) ) as $key => $value  ) {
633
			$is_multiple_meta = isset( self::$multiple_meta[ $key ] );
634
			$this->meta_data[ $key ] = wpsc_get_purchase_meta( $this->get( 'id' ), $key, ! $is_multiple_meta );
635
		}
636
637
		$this->set_total_shipping();
638
		$this->set_gateway_name();
639
		$this->set_shipping_method_names();
640
	}
641
642
	public function get_meta() {
643
644
		if ( empty( $this->data ) || empty( $this->meta_data ) ) {
645
			$this->fetch();
646
		}
647
648
		return (array) apply_filters( 'wpsc_purchase_log_meta_data', $this->meta_data );
649
	}
650
651
	/**
652
	 * Fetches the actual record from the database
653
	 *
654
	 * @access protected
655
	 * @since 3.8.9
656
	 *
657
	 * @return WPSC_Purchase_Log
658
	 */
659
	protected function fetch() {
660
		global $wpdb;
661
662
		if ( $this->fetched ) {
663
			return;
664
		}
665
666
		// If $this->args is not set yet, it means the object contains a new unsaved
667
		// row so we don't need to fetch from DB
668
		if ( ! $this->args['col'] || ! $this->args['value'] ) {
669
			return;
670
		}
671
672
		$col = $this->args['col'];
673
674
		$format = self::get_column_format( $col );
675
		$sql    = $wpdb->prepare( "SELECT * FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE {$col} = {$format}", $this->args['value'] );
676
677
		$this->exists = false;
678
679
		if ( $data = $wpdb->get_row( $sql, ARRAY_A ) ) {
680
			$this->exists    = true;
681
			$this->data      = apply_filters( 'wpsc_purchase_log_data', $data );
682
			$this->log_items = $this->get_items();
683
684
			$this->set_meta_props();
685
			$this->update_caches();
686
		}
687
688
		do_action( 'wpsc_purchase_log_fetched', $this );
689
690
		$this->fetched = true;
691
692
		return $this;
693
	}
694
695
	/**
696
	 * Returns the value of the specified property of the $data array if it exists.
697
	 *
698
	 * @access public
699
	 * @since  4.0
700
	 *
701
	 * @param  string $key Name of the property (column)
702
	 * @return mixed
703
	 */
704
	public function get( $key ) {
705
		if ( 'notes' === $key ) {
706
			_wpsc_doing_it_wrong( __FUNCTION__, __( 'Getting notes from the Log object has been deprecated in favor of the wpsc_get_order_notes() function.', 'wp-e-commerce' ), '4.0' );
707
		}
708
709
		return parent::get( $key );
710
	}
711
712
	/**
713
	 * Prepares the return value for get() (apply_filters, etc).
714
	 *
715
	 * @access protected
716
	 * @since  4.0
717
	 *
718
	 * @param  mixed  $value Value fetched
719
	 * @param  string $key   Key for $data.
720
	 *
721
	 * @return mixed
722
	 */
723
	protected function prepare_get( $value, $key ) {
724
		return apply_filters( 'wpsc_purchase_log_get_property', $value, $key, $this );
725
	}
726
727
	/**
728
	 * Prepares the return value for get_data() (apply_filters, etc).
729
	 *
730
	 * @access protected
731
	 * @since  4.0
732
	 *
733
	 * @return mixed
734
	 */
735
	protected function prepare_get_data() {
736
		return apply_filters( 'wpsc_purchase_log_get_data', $this->data, $this );
737
	}
738
739
	/**
740
	 * Prepares the return value for get_meta() (apply_filters, etc).
741
	 *
742
	 * @access protected
743
	 * @since  4.0
744
	 *
745
	 * @return mixed
746
	 */
747
	protected function prepare_get_meta() {
748
		return (array) apply_filters( 'wpsc_purchase_log_meta_data', $this->meta_data );
749
	}
750
751
	public function get_cart_contents() {
752
		_wpsc_doing_it_wrong( __FUNCTION__, __( 'This function has been deprecated in favor of the get_items() method.', 'wp-e-commerce' ), '4.0' );
753
		return $this->get_items();
754
	}
755
756
	public function get_items() {
757
		global $wpdb;
758
759
		if ( ! empty( $this->log_items ) && $this->fetched ) {
760
			return $this->log_items;
761
		}
762
763
		$id = $this->get( 'id' );
764
765
		// Bail if we don't have a log object yet (no id).
766
		if ( empty( $id ) ) {
767
			return $this->log_items;
768
		}
769
770
		$sql = $wpdb->prepare( "SELECT * FROM " . WPSC_TABLE_CART_CONTENTS . " WHERE purchaseid = %d", $id );
771
		$this->log_items = $wpdb->get_results( $sql );
772
773
		if ( is_array( $this->log_items ) ) {
774
			foreach ( $this->log_items as $index => $item ) {
775
				$this->log_item_ids[ absint( $item->id ) ] = $index;
776
			}
777
		}
778
779
		return $this->log_items;
780
	}
781
782
	public function get_item( $item_id ) {
783
		$item_id = absint( $item_id );
784
		$items   = $this->get_items();
785
786
		if ( isset( $this->log_item_ids[ $item_id ] ) ) {
787
			return $items[ $this->log_item_ids[ $item_id ] ];
788
		}
789
790
		return false;
791
	}
792
793
	public function get_item_from_product_id( $product_id ) {
794
		$product_id = absint( $product_id );
795
		$items      = $this->get_items();
796
797
		foreach ( $items as $item ) {
798
			if ( $product_id === absint( $item->prodid ) ) {
799
				return $item;
800
			}
801
		}
802
803
		return false;
804
	}
805
806
	public function update_item( $item_id, $data ) {
807
		global $wpdb;
808
809
		$item_id = absint( $item_id );
810
		$item = $this->get_item( $item_id );
811
812
		if ( $item ) {
813
			do_action( 'wpsc_purchase_log_before_update_item', $item_id );
814
815
			$data = wp_unslash( $data );
816
			$result = $wpdb->update( WPSC_TABLE_CART_CONTENTS, $data, array( 'id' => $item_id  ) );
817
818
			if ( $result ) {
819
820
				$this->log_items = array();
821
				$this->get_item( $item_id );
822
823
				do_action( 'wpsc_purchase_log_update_item', $item_id );
824
			}
825
826
			return $result;
827
		}
828
829
		return false;
830
	}
831
832
	public function remove_item( $item_id ) {
833
		global $wpdb;
834
835
		$item_id = absint( $item_id );
836
		$item = $this->get_item( $item_id );
837
838
		if ( $item ) {
839
			do_action( 'wpsc_purchase_log_before_remove_item', $item_id );
840
841
			$result = $wpdb->delete( WPSC_TABLE_CART_CONTENTS, array( 'id' => $item_id ) );
842
843
			if ( $result ) {
844
845
				unset( $this->log_items[ $this->log_item_ids[ $item_id ] ] );
846
				unset( $this->log_item_ids[ $item_id ] );
847
848
				do_action( 'wpsc_purchase_log_remove_item', $item_id );
849
			}
850
851
			return $result;
852
		}
853
854
		return false;
855
	}
856
857
	public function form_data() {
858
		if ( null === $this->form_data_obj ) {
859
			$this->form_data_obj = new WPSC_Checkout_Form_Data( $this->get( 'id' ), false );
860
		}
861
862
		return $this->form_data_obj;
863
	}
864
865
	public function get_gateway_data( $from_currency = false, $to_currency = false ) {
866
		if ( ! $this->exists() ) {
867
			return array();
868
		}
869
870
		$subtotal = 0;
871
		$shipping = wpsc_convert_currency( (float) $this->get( 'base_shipping' ), $from_currency, $to_currency );
872
		$items    = array();
873
874
		$this->gateway_data = array(
875
			'amount'  => wpsc_convert_currency( $this->get( 'totalprice' ), $from_currency, $to_currency ),
876
			'invoice' => $this->get( 'sessionid' ),
877
			'tax'     => wpsc_convert_currency( $this->get( 'wpec_taxes_total' ), $from_currency, $to_currency ),
878
		);
879
880
		foreach ( $this->log_items as $item ) {
881
			$item_price = wpsc_convert_currency( $item->price, $from_currency, $to_currency );
882
			$items[] = array(
883
				'name'     => $item->name,
884
				'amount'   => $item_price,
885
				'tax'      => wpsc_convert_currency( $item->tax_charged, $from_currency, $to_currency ),
886
				'quantity' => $item->quantity,
887
			);
888
			$subtotal += $item_price * $item->quantity;
889
			$shipping += wpsc_convert_currency( $item->pnp, $from_currency, $to_currency );
890
		}
891
892
		$this->gateway_data['discount'] = wpsc_convert_currency( (float) $this->get( 'discount_value' ), $from_currency, $to_currency );
893
894
		$this->gateway_data['items'] = $items;
895
		$this->gateway_data['shipping'] = $shipping;
896
		$this->gateway_data['subtotal'] = $subtotal;
897
898
		if ( $from_currency ) {
899
			// adjust total amount in case there's slight decimal error
900
			$total = $subtotal + $shipping + $this->gateway_data['tax'] - $this->gateway_data['discount'];
901
			if ( $this->gateway_data['amount'] != $total ) {
902
				$this->gateway_data['amount'] = $total;
903
			}
904
		}
905
906
		$this->gateway_data = apply_filters( 'wpsc_purchase_log_gateway_data', $this->gateway_data, $this->get_data() );
907
		return $this->gateway_data;
908
	}
909
910
	/**
911
	 * Sets a property to a certain value. This function accepts a key and a value
912
	 * as arguments, or an associative array containing key value pairs.
913
	 *
914
	 * Loops through data, comparing against database, and saves as meta if not found in purchase log table.
915
	 *
916
	 * @access public
917
	 * @since 3.8.9
918
	 *
919
	 * @param mixed $key Name of the property (column), or an array containing key
920
	 *                   value pairs
921
	 * @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...
922
	 *                          this should be specified.
923
	 * @return WPSC_Purchase_Log The current object (for method chaining)
924
	 */
925
	public function set( $key, $value = null ) {
926
		if ( is_array( $key ) ) {
927
			$properties = $key;
928
		} else {
929
			if ( is_null( $value ) ) {
930
				return $this;
931
			}
932
933
			$properties = array( $key => $value );
934
		}
935
936
		$properties = apply_filters( 'wpsc_purchase_log_set_properties', $properties, $this );
937
938
		if ( array_key_exists( 'processed', $properties ) ) {
939
			$this->previous_status = $this->get( 'processed' );
940
941
			if ( $properties['processed'] != $this->previous_status ) {
942
				$this->is_status_changed = true;
943
			}
944
		}
945
946
		if ( ! is_array( $this->data ) ) {
947
			$this->data = array();
948
		}
949
950
		foreach ( $properties as $key => $value ) {
951
			if ( ! in_array( $key, array_merge( self::$string_cols, self::$int_cols, self::$float_cols ) ) ) {
952
				$this->meta_data[ $key ] = $value;
953
				unset( $properties[ $key ] );
954
			}
955
		}
956
957
		$this->data = array_merge( $this->data, $properties );
958
		return $this;
959
	}
960
961
	/**
962
	 * Returns an array containing the parameter format so that this can be used in
963
	 * $wpdb methods (update, insert etc.)
964
	 *
965
	 * @access private
966
	 * @since 3.8.9
967
	 *
968
	 * @param array $data
969
	 * @return array
970
	 */
971
	private function get_data_format( $data ) {
972
		$format = array();
973
974
		foreach ( $data as $key => $value ) {
975
			$format[] = self::get_column_format( $key );
976
		}
977
978
		return $format;
979
	}
980
981
	/**
982
	 * Saves the purchase log back to the database
983
	 *
984
	 * @access public
985
	 * @since 3.8.9
986
	 *
987
	 * @return void
988
	 */
989
	public function save() {
990
		global $wpdb;
991
992
		do_action( 'wpsc_purchase_log_pre_save', $this );
993
994
		// $this->args['col'] is empty when the record is new and needs to
995
		// be inserted. Otherwise, it means we're performing an update
996
		$where_col = $this->args['col'];
997
998
		$result = false;
999
1000
		if ( $where_col ) {
1001
			$where_val = $this->args['value'];
1002
			$where_format = self::get_column_format( $where_col );
1003
			do_action( 'wpsc_purchase_log_pre_update', $this );
1004
			self::delete_cache( $where_val, $where_col );
1005
			$data = apply_filters( 'wpsc_purchase_log_update_data', $this->data );
1006
			$format = $this->get_data_format( $data );
1007
			$result = $wpdb->update( WPSC_TABLE_PURCHASE_LOGS, $data, array( $where_col => $where_val ), $format, array( $where_format ) );
1008
			do_action( 'wpsc_purchase_log_update', $this );
1009
		} else {
1010
			do_action( 'wpsc_purchase_log_pre_insert', $this );
1011
			$data = apply_filters( 'wpsc_purchase_log_insert_data', $this->data );
1012
			$format = $this->get_data_format( $data );
1013
			$result = $wpdb->insert( WPSC_TABLE_PURCHASE_LOGS, $data, $format );
1014
1015
			if ( $result ) {
1016
				$this->set( 'id', $wpdb->insert_id );
1017
1018
				// set $this->args so that properties can be lazy loaded right after
1019
				// the row is inserted into the db
1020
				$this->args = array(
1021
					'col'   => 'id',
1022
					'value' => $this->get( 'id' ),
1023
				);
1024
			}
1025
1026
			do_action( 'wpsc_purchase_log_insert', $this );
1027
		}
1028
1029
		if ( $this->is_status_changed ) {
1030
1031
			if ( $this->is_transaction_completed() ) {
1032
				$this->update_downloadable_status();
1033
			}
1034
1035
			$current_status          = $this->get( 'processed' );
1036
			$previous_status         = $this->previous_status;
1037
			$this->previous_status   = $current_status;
1038
			$this->is_status_changed = false;
1039
1040
			do_action( 'wpsc_update_purchase_log_status', $this->get( 'id' ), $current_status, $previous_status, $this );
1041
		}
1042
1043
		if ( ! empty( $this->meta_data ) ) {
1044
			$this->save_meta();
1045
		}
1046
1047
		do_action( 'wpsc_purchase_log_save', $this );
1048
1049
		return $result;
1050
	}
1051
1052
	/**
1053
	 * Save meta data for purchase log, if any was set via set().
1054
	 *
1055
	 * @access public
1056
	 * @since  4.0
1057
	 *
1058
	 * @return WPSC_Purchase_Log  The current object (for method chaining)
1059
	 */
1060
	public function save_meta() {
1061
		do_action( 'wpsc_purchase_log_pre_save_meta', $this );
1062
1063
		$meta = $this->get_meta();
1064
1065
		foreach ( $meta as $key => $value ) {
1066
			$is_multiple_meta = isset( self::$multiple_meta[ $key ] );
1067
1068
			if ( $is_multiple_meta ) {
1069
1070
				if ( is_array( $value ) ) {
1071
					foreach ( $value as $val ) {
1072
						wpsc_add_purchase_meta( $this->get( 'id' ), $key, $val );
1073
					}
1074
				}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
1075
1076
			} else {
1077
				wpsc_update_purchase_meta( $this->get( 'id' ), $key, $value );
1078
			}
1079
		}
1080
1081
		do_action( 'wpsc_purchase_log_save_meta', $this );
1082
1083
		return $this;
1084
	}
1085
1086
	private function update_downloadable_status() {
1087
		global $wpdb;
1088
1089
		foreach ( $this->get_items() as $item ) {
1090
			$wpdb->update(
1091
				WPSC_TABLE_DOWNLOAD_STATUS,
1092
				array(
1093
					'active' => '1'
1094
				),
1095
				array(
1096
					'cartid'  => $item->id,
1097
					'purchid' => $this->get( 'id' ),
1098
				)
1099
			);
1100
		}
1101
	}
1102
1103
	public function have_downloads_locked() {
1104
		global $wpdb;
1105
1106
		$sql = $wpdb->prepare( "SELECT `ip_number` FROM `" . WPSC_TABLE_DOWNLOAD_STATUS . "` WHERE `purchid` = %d ", $this->get( 'id' ) );
1107
		$ip_number = $wpdb->get_var( $sql );
1108
1109
		return $ip_number;
1110
	}
1111
1112
	/**
1113
	 * Adds ability to retrieve a purchase log by a meta key or value.
1114
	 *
1115
	 * @since  4.0
1116
	 *
1117
	 * @param  string $key   Meta key. Optional.
1118
	 * @param  string $value Meta value. Optional.
1119
	 *
1120
	 * @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.
1121
	 */
1122
	public static function get_log_by_meta( $key = '', $value = '' ) {
1123
1124
		if ( empty( $key ) && empty( $value ) ) {
1125
			return false;
1126
		}
1127
1128
		global $wpdb;
1129
1130
		if ( ! empty( $key ) && empty( $value ) ) {
1131
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_key = %s', $key );
1132
		} else if ( empty( $key ) && ! empty( $value ) ) {
1133
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_value = %s', $value );
1134
		} else {
1135
			$sql = $wpdb->prepare( 'SELECT wpsc_purchase_id FROM ' . WPSC_TABLE_PURCHASE_META . ' WHERE meta_key = %s AND meta_value = %s', $key, $value );
1136
		}
1137
1138
		$id = $wpdb->get_var( $sql );
1139
1140
		if ( $id ) {
1141
			return new WPSC_Purchase_Log( $id );
1142
		} else {
1143
			return false;
1144
		}
1145
	}
1146
1147
	public function get_next_log_id() {
1148
		if ( ! $this->exists() ) {
1149
			return false;
1150
		}
1151
1152
		global $wpdb;
1153
1154
		$sql = $wpdb->prepare(
1155
			"SELECT MIN(id) FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE id > %d",
1156
			$this->get( 'id' )
1157
		);
1158
1159
		return $wpdb->get_var( $sql );
1160
	}
1161
1162
	public function get_previous_log_id() {
1163
		if ( ! $this->exists() ) {
1164
			return false;
1165
		}
1166
1167
		global $wpdb;
1168
1169
		$sql = $wpdb->prepare(
1170
			"SELECT MAX(id) FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE id < %d",
1171
			$this->get( 'id' )
1172
		);
1173
1174
		return $wpdb->get_var( $sql );
1175
	}
1176
1177
	public function is_transaction_completed() {
1178
		return WPSC_Purchase_Log::is_order_status_completed( $this->get( 'processed' ) );
1179
	}
1180
1181
	public function can_edit() {
1182
		if ( null === $this->can_edit ) {
1183
			$can_edit = current_user_can( 'edit_others_posts' ) && ! $this->is_transaction_completed();
1184
			$this->can_edit = apply_filters( 'wpsc_can_edit_order', $can_edit, $this );
1185
		}
1186
1187
		return $this->can_edit;
1188
	}
1189
1190
	public function is_order_received() {
1191
		return $this->get( 'processed' ) == self::ORDER_RECEIVED;
1192
	}
1193
1194
	public function is_incomplete_sale() {
1195
		return $this->get( 'processed' ) == self::INCOMPLETE_SALE;
1196
	}
1197
1198
	public function is_accepted_payment() {
1199
		return $this->get( 'processed' ) == self::ACCEPTED_PAYMENT;
1200
	}
1201
1202
	public function is_job_dispatched() {
1203
		return $this->get( 'processed' ) == self::JOB_DISPATCHED;
1204
	}
1205
1206
	public function is_closed_order() {
1207
		return $this->get( 'processed' ) == self::CLOSED_ORDER;
1208
	}
1209
1210
	public function is_payment_declined() {
1211
		return $this->get( 'processed' ) == self::PAYMENT_DECLINED;
1212
	}
1213
1214
	public function is_refunded() {
1215
		return $this->get( 'processed' ) == self::REFUNDED;
1216
	}
1217
1218
	public function is_refund_pending() {
1219
		return $this->get( 'processed' ) == self::REFUND_PENDING;
1220
	}
1221
1222
	/*
1223
	 * Utility methods using the $purchlogitem global.. Global usage to be replaced in the future.
1224
	 *
1225
	 * TODO: seriously get rid of all these badly coded purchaselogs.functions.php functions
1226
	 * and wpsc_purchaselogs/wpsc_purchaselogs_items classes.
1227
	 */
1228
1229
	/**
1230
	 * Init the purchase log items for this purchase log.
1231
	 *
1232
	 * @since  4.0
1233
	 *
1234
	 * @return wpsc_purchaselogs_items|false The purhchase log item object or false.
1235
	 */
1236
	public function init_items() {
1237
		global $purchlogitem;
1238
		if ( ! $this->exists() ) {
1239
			return false;
1240
		}
1241
1242
		$purchlogitem = new wpsc_purchaselogs_items( $this->get( 'id' ), $this );
1243
	}
1244
1245
	public function buyers_name() {
1246
		global $purchlogitem;
1247
1248
		if ( null === $this->buyers_name ) {
1249
			$first_name = $last_name = '';
1250
1251
			if ( isset( $purchlogitem->userinfo['billingfirstname'] ) ) {
1252
				$first_name = $purchlogitem->userinfo['billingfirstname']['value'];
1253
			}
1254
1255
			if ( isset( $purchlogitem->userinfo['billinglastname'] ) ) {
1256
				$last_name = ' ' . $purchlogitem->userinfo['billinglastname']['value'];
1257
			}
1258
1259
			$this->buyers_name = trim( $first_name . $last_name );
1260
		}
1261
1262
		return $this->buyers_name;
1263
	}
1264
1265
	public function buyers_city() {
1266
		global $purchlogitem;
1267
1268
		if ( null === $this->buyers_city ) {
1269
			$this->buyers_city = isset( $purchlogitem->userinfo['billingcity']['value'] ) ? $purchlogitem->userinfo['billingcity']['value'] : '';
1270
		}
1271
1272
		return $this->buyers_city;
1273
	}
1274
1275
	public function buyers_email() {
1276
		global $purchlogitem;
1277
1278
		if ( null === $this->buyers_email ) {
1279
			$this->buyers_email = isset( $purchlogitem->userinfo['billingemail']['value'] ) ? $purchlogitem->userinfo['billingemail']['value'] : '';
1280
		}
1281
1282
		return $this->buyers_email;
1283
	}
1284
1285
	public function buyers_address() {
1286
		global $purchlogitem;
1287
1288
		if ( null === $this->buyers_address ) {
1289
			$this->buyers_address = isset( $purchlogitem->userinfo['billingaddress']['value'] ) ? nl2br( esc_html( $purchlogitem->userinfo['billingaddress']['value'] ) ) : '';
1290
		}
1291
1292
		return $this->buyers_address;
1293
	}
1294
1295
	public function buyers_state_and_postcode() {
1296
		global $purchlogitem;
1297
1298
		if ( null === $this->buyers_state_and_postcode ) {
1299
1300
			if ( is_numeric( $this->get( 'billing_region' ) ) ) {
1301
				$state = wpsc_get_region( $this->get( 'billing_region' ) );
1302
			} else {
1303
				$state = $purchlogitem->userinfo['billingstate']['value'];
1304
				$state = is_numeric( $state ) ? wpsc_get_region( $state ) : $state;
1305
			}
1306
1307
			$output = esc_html( $state );
1308
1309
			if ( isset( $purchlogitem->userinfo['billingpostcode']['value'] ) && ! empty( $purchlogitem->userinfo['billingpostcode']['value'] ) ) {
1310
				if ( $output ) {
1311
					$output .= ', '; // TODO determine if it's ok to make this a space only (like shipping_state_and_postcode)
1312
				}
1313
				$output .= $purchlogitem->userinfo['billingpostcode']['value'];
1314
			}
1315
1316
			$this->buyers_state_and_postcode = $output;
1317
		}
1318
1319
		return $this->buyers_state_and_postcode;
1320
	}
1321
1322
	public function buyers_country() {
1323
		global $purchlogitem;
1324
1325
		if ( null === $this->buyers_country ) {
1326
			$this->buyers_country = isset( $purchlogitem->userinfo['billingcountry']['value'] ) ? wpsc_get_country( $purchlogitem->userinfo['billingcountry']['value'] ) : '';
1327
		}
1328
1329
		return $this->buyers_country;
1330
	}
1331
1332
	public function buyers_phone() {
1333
		global $purchlogitem;
1334
1335
		if ( null === $this->buyers_phone ) {
1336
			$this->buyers_phone = isset( $purchlogitem->userinfo['billingphone']['value'] ) ? $purchlogitem->userinfo['billingphone']['value'] : '';
1337
		}
1338
1339
		return $this->buyers_phone;
1340
	}
1341
1342
	public function shipping_name() {
1343
		global $purchlogitem;
1344
1345
		if ( null === $this->shipping_name ) {
1346
			$this->shipping_name = isset( $purchlogitem->shippinginfo['shippingfirstname']['value'] ) ? $purchlogitem->shippinginfo['shippingfirstname']['value'] : '';
1347
1348
			if ( isset( $purchlogitem->shippinginfo['shippinglastname']['value'] ) ) {
1349
				$this->shipping_name .= ' ' . $purchlogitem->shippinginfo['shippinglastname']['value'];
1350
			}
1351
		}
1352
1353
		return $this->shipping_name;
1354
	}
1355
1356
	public function shipping_city() {
1357
		global $purchlogitem;
1358
1359
		if ( null === $this->shipping_city ) {
1360
			$this->shipping_city = isset( $purchlogitem->shippinginfo['shippingcity']['value'] ) ? $purchlogitem->shippinginfo['shippingcity']['value'] : '';
1361
		}
1362
1363
		return $this->shipping_city;
1364
	}
1365
1366
	public function shipping_address() {
1367
		global $purchlogitem;
1368
1369
		if ( null === $this->shipping_address ) {
1370
			$this->shipping_address = isset( $purchlogitem->shippinginfo['shippingaddress']['value'] ) ? nl2br( esc_html( $purchlogitem->shippinginfo['shippingaddress']['value'] ) ) : '';
1371
		}
1372
1373
		return $this->shipping_address;
1374
	}
1375
1376
	public function shipping_state_and_postcode() {
1377
		global $purchlogitem;
1378
1379
		if ( null === $this->shipping_state_and_postcode ) {
1380
			if ( is_numeric( $this->get( 'shipping_region' ) ) ) {
1381
				$output = wpsc_get_region( $this->get( 'shipping_region' ) );
1382
			} else {
1383
				$state = $purchlogitem->shippinginfo['shippingstate']['value'];
1384
				$output = is_numeric( $state ) ? wpsc_get_region( $state ) : $state;
1385
			}
1386
1387
			if ( !empty( $purchlogitem->shippinginfo['shippingpostcode']['value'] ) ){
1388
				if ( $output ) {
1389
					$output .= ' ';
1390
				}
1391
1392
				$output .= $purchlogitem->shippinginfo['shippingpostcode']['value'];
1393
			}
1394
1395
			$this->shipping_state_and_postcode = $output;
1396
		}
1397
1398
		return $this->shipping_state_and_postcode;
1399
	}
1400
1401
	public function shipping_country() {
1402
		global $purchlogitem;
1403
1404
		if ( null === $this->shipping_country ) {
1405
			$this->shipping_country = isset( $purchlogitem->shippinginfo['shippingcountry'] )
1406
				? wpsc_get_country( $purchlogitem->shippinginfo['shippingcountry']['value'] )
1407
				: '';
1408
		}
1409
1410
		return $this->shipping_country;
1411
	}
1412
1413
	public function payment_method() {
1414
		global $nzshpcrt_gateways;
1415
1416
		if ( null === $this->payment_method ) {
1417
			if ( 'wpsc_merchant_testmode' == $this->get( 'gateway' ) ) {
1418
				$this->payment_method = __( 'Manual Payment', 'wp-e-commerce' );
1419
			} else {
1420
				foreach ( (array) $nzshpcrt_gateways as $gateway ) {
1421
					if ( isset( $gateway['internalname'] ) && $gateway['internalname'] == $this->get( 'gateway' ) ) {
1422
						$this->payment_method = $gateway['name'];
1423
					}
1424
				}
1425
1426
				if ( ! $this->payment_method ) {
1427
					$this->payment_method = $this->get( 'gateway' );
1428
				}
1429
			}
1430
		}
1431
1432
		return $this->payment_method;
1433
	}
1434
1435
	public function shipping_method() {
1436
		global $wpsc_shipping_modules;
1437
1438
		if ( null === $this->shipping_method ) {
1439
1440
			if ( ! empty( $wpsc_shipping_modules[ $this->get( 'shipping_method' ) ] ) ) {
1441
				$this->shipping_method = $wpsc_shipping_modules[ $this->get( 'shipping_method' ) ]->getName();
1442
			} else {
1443
				$this->shipping_method = $this->get( 'shipping_method' );
1444
			}
1445
		}
1446
1447
		return $this->shipping_method;
1448
	}
1449
1450
	/**
1451
	 * Returns base shipping should make a function to calculate items shipping as well
1452
	 *
1453
	 * @since  4.0
1454
	 *
1455
	 * @param  boolean $numeric Return numeric value.
1456
	 *
1457
	 * @return mixed
1458
	 */
1459
	public function discount( $numeric = false ) {
1460
		$discount = $this->get( 'discount_value' );
1461
		if ( ! $numeric ) {
1462
			$discount = wpsc_currency_display( $discount, array( 'display_as_html' => false ) );
1463
		}
1464
1465
		return $discount;
1466
	}
1467
1468
	/**
1469
	 * Returns base shipping should make a function to calculate items shipping as well
1470
	 *
1471
	 * @since  4.0
1472
	 *
1473
	 * @param  boolean $numeric       Return numeric value.
1474
	 * @param  boolean $include_items Whether to calculate per-item-shipping.
1475
	 *
1476
	 * @return mixed
1477
	 */
1478
	public function shipping( $numeric = false, $include_items = false ) {
1479
		$total_shipping = $this->get( 'base_shipping' );
1480
1481
		if ( $include_items ) {
1482
			$total_shipping = $this->meta_data['total_shipping'];
1483
		}
1484
1485
		if ( ! $numeric ) {
1486
			$total_shipping = wpsc_currency_display( $total_shipping, array( 'display_as_html' => false ) );
1487
		}
1488
1489
		return $total_shipping;
1490
	}
1491
1492
	/**
1493
	 * Returns taxes total.
1494
	 *
1495
	 * @since  4.0
1496
	 *
1497
	 * @param  boolean $numeric Return numeric value.
1498
	 *
1499
	 * @return mixed
1500
	 */
1501
	public function taxes( $numeric = false ) {
1502
		$taxes = $this->get( 'wpec_taxes_total' );
1503
1504
		if ( ! $numeric ) {
1505
			$taxes = wpsc_currency_display( $taxes, array( 'display_as_html' => false ) );
1506
		}
1507
1508
		return $taxes;
1509
	}
1510
1511
	public function total_price() {
1512
		global $purchlogitem;
1513
1514
		$total = $purchlogitem->totalAmount - $this->discount( true ) + $this->shipping( true ) + $this->taxes( true );
1515
		return wpsc_currency_display( $total, array( 'display_as_html' => false ) );
1516
	}
1517
1518
	public function get_total_refunded() {
1519
		return $this->get( 'total_order_refunded' );
1520
	}
1521
1522
	public function get_remaining_refund() {
1523
		return $this->get( 'totalprice' ) - $this->get( 'total_order_refunded' );
1524
	}
1525
1526
}
1527