1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* WooCommerce cart |
4
|
|
|
* |
5
|
|
|
* The WooCommerce cart class stores cart data and active coupons as well as handling customer sessions and some cart related urls. |
6
|
|
|
* The cart class also has a price calculation function which calls upon other classes to calculate totals. |
7
|
|
|
* |
8
|
|
|
* @package WooCommerce/Classes |
9
|
|
|
* @version 2.1.0 |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
defined( 'ABSPATH' ) || exit; |
13
|
|
|
|
14
|
|
|
require_once WC_ABSPATH . 'includes/legacy/class-wc-legacy-cart.php'; |
15
|
|
|
require_once WC_ABSPATH . 'includes/class-wc-cart-fees.php'; |
16
|
|
|
require_once WC_ABSPATH . 'includes/class-wc-cart-session.php'; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* WC_Cart class. |
20
|
|
|
*/ |
21
|
|
|
class WC_Cart extends WC_Legacy_Cart { |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Contains an array of cart items. |
25
|
|
|
* |
26
|
|
|
* @var array |
27
|
|
|
*/ |
28
|
|
|
public $cart_contents = array(); |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Contains an array of removed cart items so we can restore them if needed. |
32
|
|
|
* |
33
|
|
|
* @var array |
34
|
|
|
*/ |
35
|
|
|
public $removed_cart_contents = array(); |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Contains an array of coupon codes applied to the cart. |
39
|
|
|
* |
40
|
|
|
* @var array |
41
|
|
|
*/ |
42
|
|
|
public $applied_coupons = array(); |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Are prices in the cart displayed inc or excl tax? |
46
|
|
|
* |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
public $tax_display_cart = 'incl'; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* This stores the chosen shipping methods for the cart item packages. |
53
|
|
|
* |
54
|
|
|
* @var array |
55
|
|
|
*/ |
56
|
|
|
protected $shipping_methods; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Total defaults used to reset. |
60
|
|
|
* |
61
|
|
|
* @var array |
62
|
|
|
*/ |
63
|
|
|
protected $default_totals = array( |
64
|
|
|
'subtotal' => 0, |
65
|
|
|
'subtotal_tax' => 0, |
66
|
|
|
'shipping_total' => 0, |
67
|
|
|
'shipping_tax' => 0, |
68
|
|
|
'shipping_taxes' => array(), |
69
|
|
|
'discount_total' => 0, |
70
|
|
|
'discount_tax' => 0, |
71
|
|
|
'cart_contents_total' => 0, |
72
|
|
|
'cart_contents_tax' => 0, |
73
|
|
|
'cart_contents_taxes' => array(), |
74
|
|
|
'fee_total' => 0, |
75
|
|
|
'fee_tax' => 0, |
76
|
|
|
'fee_taxes' => array(), |
77
|
|
|
'total' => 0, |
78
|
|
|
'total_tax' => 0, |
79
|
|
|
); |
80
|
|
|
/** |
81
|
|
|
* Store calculated totals. |
82
|
|
|
* |
83
|
|
|
* @var array |
84
|
|
|
*/ |
85
|
|
|
protected $totals = array(); |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Reference to the cart session handling class. |
89
|
|
|
* |
90
|
|
|
* @var WC_Cart_Session |
91
|
|
|
*/ |
92
|
|
|
protected $session; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Reference to the cart fees API class. |
96
|
|
|
* |
97
|
|
|
* @var WC_Cart_Fees |
98
|
|
|
*/ |
99
|
|
|
protected $fees_api; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Constructor for the cart class. Loads options and hooks in the init method. |
103
|
|
|
*/ |
104
|
1 |
|
public function __construct() { |
105
|
1 |
|
$this->session = new WC_Cart_Session( $this ); |
106
|
1 |
|
$this->fees_api = new WC_Cart_Fees( $this ); |
107
|
1 |
|
$this->tax_display_cart = $this->is_tax_displayed(); |
108
|
|
|
|
109
|
|
|
// Register hooks for the objects. |
110
|
1 |
|
$this->session->init(); |
111
|
|
|
|
112
|
1 |
|
add_action( 'woocommerce_add_to_cart', array( $this, 'calculate_totals' ), 20, 0 ); |
113
|
1 |
|
add_action( 'woocommerce_applied_coupon', array( $this, 'calculate_totals' ), 20, 0 ); |
114
|
1 |
|
add_action( 'woocommerce_cart_item_removed', array( $this, 'calculate_totals' ), 20, 0 ); |
115
|
1 |
|
add_action( 'woocommerce_cart_item_restored', array( $this, 'calculate_totals' ), 20, 0 ); |
116
|
1 |
|
add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_items' ), 1 ); |
117
|
1 |
|
add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_coupons' ), 1 ); |
118
|
1 |
|
add_action( 'woocommerce_after_checkout_validation', array( $this, 'check_customer_coupons' ), 1 ); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* When cloning, ensure object properties are handled. |
123
|
|
|
* |
124
|
|
|
* These properties store a reference to the cart, so we use new instead of clone. |
125
|
|
|
*/ |
126
|
3 |
|
public function __clone() { |
127
|
3 |
|
$this->session = clone $this->session; |
128
|
3 |
|
$this->fees_api = clone $this->fees_api; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/* |
132
|
|
|
|-------------------------------------------------------------------------- |
133
|
|
|
| Getters. |
134
|
|
|
|-------------------------------------------------------------------------- |
135
|
|
|
| |
136
|
|
|
| Methods to retrieve class properties and avoid direct access. |
137
|
|
|
*/ |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Gets cart contents. |
141
|
|
|
* |
142
|
|
|
* @since 3.2.0 |
143
|
|
|
* @return array of cart items |
144
|
|
|
*/ |
145
|
176 |
|
public function get_cart_contents() { |
146
|
176 |
|
return apply_filters( 'woocommerce_get_cart_contents', (array) $this->cart_contents ); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* Return items removed from the cart. |
151
|
|
|
* |
152
|
|
|
* @since 3.2.0 |
153
|
|
|
* @return array |
154
|
|
|
*/ |
155
|
87 |
|
public function get_removed_cart_contents() { |
156
|
87 |
|
return (array) $this->removed_cart_contents; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Gets the array of applied coupon codes. |
161
|
|
|
* |
162
|
|
|
* @return array of applied coupons |
163
|
|
|
*/ |
164
|
87 |
|
public function get_applied_coupons() { |
165
|
87 |
|
return (array) $this->applied_coupons; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Return all calculated coupon totals. |
170
|
|
|
* |
171
|
|
|
* @since 3.2.0 |
172
|
|
|
* @return array |
173
|
|
|
*/ |
174
|
87 |
|
public function get_coupon_discount_totals() { |
175
|
87 |
|
return (array) $this->coupon_discount_totals; |
176
|
|
|
} |
177
|
|
|
/** |
178
|
|
|
* Return all calculated coupon tax totals. |
179
|
|
|
* |
180
|
|
|
* @since 3.2.0 |
181
|
|
|
* @return array |
182
|
|
|
*/ |
183
|
87 |
|
public function get_coupon_discount_tax_totals() { |
184
|
87 |
|
return (array) $this->coupon_discount_tax_totals; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Return all calculated totals. |
189
|
|
|
* |
190
|
|
|
* @since 3.2.0 |
191
|
|
|
* @return array |
192
|
|
|
*/ |
193
|
87 |
|
public function get_totals() { |
194
|
87 |
|
return empty( $this->totals ) ? $this->default_totals : $this->totals; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Get a total. |
199
|
|
|
* |
200
|
|
|
* @since 3.2.0 |
201
|
|
|
* @param string $key Key of element in $totals array. |
202
|
|
|
* @return mixed |
203
|
|
|
*/ |
204
|
72 |
|
protected function get_totals_var( $key ) { |
205
|
72 |
|
return isset( $this->totals[ $key ] ) ? $this->totals[ $key ] : $this->default_totals[ $key ]; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Get subtotal. |
210
|
|
|
* |
211
|
|
|
* @since 3.2.0 |
212
|
|
|
* @return float |
213
|
|
|
*/ |
214
|
65 |
|
public function get_subtotal() { |
215
|
65 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'subtotal' ) ); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Get subtotal. |
220
|
|
|
* |
221
|
|
|
* @since 3.2.0 |
222
|
|
|
* @return float |
223
|
|
|
*/ |
224
|
3 |
|
public function get_subtotal_tax() { |
225
|
3 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'subtotal_tax' ) ); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Get discount_total. |
230
|
|
|
* |
231
|
|
|
* @since 3.2.0 |
232
|
|
|
* @return float |
233
|
|
|
*/ |
234
|
6 |
|
public function get_discount_total() { |
235
|
6 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'discount_total' ) ); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Get discount_tax. |
240
|
|
|
* |
241
|
|
|
* @since 3.2.0 |
242
|
|
|
* @return float |
243
|
|
|
*/ |
244
|
2 |
|
public function get_discount_tax() { |
245
|
2 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'discount_tax' ) ); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Get shipping_total. |
250
|
|
|
* |
251
|
|
|
* @since 3.2.0 |
252
|
|
|
* @return float |
253
|
|
|
*/ |
254
|
3 |
|
public function get_shipping_total() { |
255
|
3 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'shipping_total' ) ); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Get shipping_tax. |
260
|
|
|
* |
261
|
|
|
* @since 3.2.0 |
262
|
|
|
* @return float |
263
|
|
|
*/ |
264
|
2 |
|
public function get_shipping_tax() { |
265
|
2 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'shipping_tax' ) ); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* Gets cart total. This is the total of items in the cart, but after discounts. Subtotal is before discounts. |
270
|
|
|
* |
271
|
|
|
* @since 3.2.0 |
272
|
|
|
* @return float |
273
|
|
|
*/ |
274
|
2 |
|
public function get_cart_contents_total() { |
275
|
2 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'cart_contents_total' ) ); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Gets cart tax amount. |
280
|
|
|
* |
281
|
|
|
* @since 3.2.0 |
282
|
|
|
* @return float |
283
|
|
|
*/ |
284
|
2 |
|
public function get_cart_contents_tax() { |
285
|
2 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'cart_contents_tax' ) ); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Gets cart total after calculation. |
290
|
|
|
* |
291
|
|
|
* @since 3.2.0 |
292
|
|
|
* @param string $context If the context is view, the value will be formatted for display. This keeps it compatible with pre-3.2 versions. |
293
|
|
|
* @return float |
294
|
|
|
*/ |
295
|
22 |
|
public function get_total( $context = 'view' ) { |
296
|
22 |
|
$total = apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'total' ) ); |
297
|
22 |
|
return 'view' === $context ? apply_filters( 'woocommerce_cart_total', wc_price( $total ) ) : $total; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Get total tax amount. |
302
|
|
|
* |
303
|
|
|
* @since 3.2.0 |
304
|
|
|
* @return float |
305
|
|
|
*/ |
306
|
5 |
|
public function get_total_tax() { |
307
|
5 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'total_tax' ) ); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Get total fee amount. |
312
|
|
|
* |
313
|
|
|
* @since 3.2.0 |
314
|
|
|
* @return float |
315
|
|
|
*/ |
316
|
2 |
|
public function get_fee_total() { |
317
|
2 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'fee_total' ) ); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Get total fee tax amount. |
322
|
|
|
* |
323
|
|
|
* @since 3.2.0 |
324
|
|
|
* @return float |
325
|
|
|
*/ |
326
|
2 |
|
public function get_fee_tax() { |
327
|
2 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'fee_tax' ) ); |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* Get taxes. |
332
|
|
|
* |
333
|
|
|
* @since 3.2.0 |
334
|
|
|
*/ |
335
|
1 |
|
public function get_shipping_taxes() { |
336
|
1 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'shipping_taxes' ) ); |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Get taxes. |
341
|
|
|
* |
342
|
|
|
* @since 3.2.0 |
343
|
|
|
*/ |
344
|
3 |
|
public function get_cart_contents_taxes() { |
345
|
3 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'cart_contents_taxes' ) ); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* Get taxes. |
350
|
|
|
* |
351
|
|
|
* @since 3.2.0 |
352
|
|
|
*/ |
353
|
3 |
|
public function get_fee_taxes() { |
354
|
3 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, $this->get_totals_var( 'fee_taxes' ) ); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Return whether or not the cart is displaying prices including tax, rather than excluding tax. |
359
|
|
|
* |
360
|
|
|
* @since 3.3.0 |
361
|
|
|
* @return bool |
362
|
|
|
*/ |
363
|
65 |
|
public function display_prices_including_tax() { |
364
|
65 |
|
return apply_filters( 'woocommerce_cart_' . __FUNCTION__, 'incl' === $this->tax_display_cart ); |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/* |
368
|
|
|
|-------------------------------------------------------------------------- |
369
|
|
|
| Setters. |
370
|
|
|
|-------------------------------------------------------------------------- |
371
|
|
|
| |
372
|
|
|
| Methods to set class properties and avoid direct access. |
373
|
|
|
*/ |
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* Sets the contents of the cart. |
377
|
|
|
* |
378
|
|
|
* @param array $value Cart array. |
379
|
|
|
*/ |
380
|
|
|
public function set_cart_contents( $value ) { |
381
|
|
|
$this->cart_contents = (array) $value; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
/** |
385
|
|
|
* Set items removed from the cart. |
386
|
|
|
* |
387
|
|
|
* @since 3.2.0 |
388
|
|
|
* @param array $value Item array. |
389
|
|
|
*/ |
390
|
|
|
public function set_removed_cart_contents( $value = array() ) { |
391
|
|
|
$this->removed_cart_contents = (array) $value; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
/** |
395
|
|
|
* Sets the array of applied coupon codes. |
396
|
|
|
* |
397
|
|
|
* @param array $value List of applied coupon codes. |
398
|
|
|
*/ |
399
|
49 |
|
public function set_applied_coupons( $value = array() ) { |
400
|
49 |
|
$this->applied_coupons = (array) $value; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* Sets the array of calculated coupon totals. |
405
|
|
|
* |
406
|
|
|
* @since 3.2.0 |
407
|
|
|
* @param array $value Value to set. |
408
|
|
|
*/ |
409
|
82 |
|
public function set_coupon_discount_totals( $value = array() ) { |
410
|
82 |
|
$this->coupon_discount_totals = (array) $value; |
411
|
|
|
} |
412
|
|
|
/** |
413
|
|
|
* Sets the array of calculated coupon tax totals. |
414
|
|
|
* |
415
|
|
|
* @since 3.2.0 |
416
|
|
|
* @param array $value Value to set. |
417
|
|
|
*/ |
418
|
82 |
|
public function set_coupon_discount_tax_totals( $value = array() ) { |
419
|
82 |
|
$this->coupon_discount_tax_totals = (array) $value; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* Set all calculated totals. |
424
|
|
|
* |
425
|
|
|
* @since 3.2.0 |
426
|
|
|
* @param array $value Value to set. |
427
|
|
|
*/ |
428
|
|
|
public function set_totals( $value = array() ) { |
429
|
|
|
$this->totals = wp_parse_args( $value, $this->default_totals ); |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* Set subtotal. |
434
|
|
|
* |
435
|
|
|
* @since 3.2.0 |
436
|
|
|
* @param string $value Value to set. |
437
|
|
|
*/ |
438
|
77 |
|
public function set_subtotal( $value ) { |
439
|
77 |
|
$this->totals['subtotal'] = wc_format_decimal( $value, wc_get_price_decimals() ); |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
/** |
443
|
|
|
* Set subtotal. |
444
|
|
|
* |
445
|
|
|
* @since 3.2.0 |
446
|
|
|
* @param string $value Value to set. |
447
|
|
|
*/ |
448
|
77 |
|
public function set_subtotal_tax( $value ) { |
449
|
77 |
|
$this->totals['subtotal_tax'] = wc_round_tax_total( $value ); |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* Set discount_total. |
454
|
|
|
* |
455
|
|
|
* @since 3.2.0 |
456
|
|
|
* @param string $value Value to set. |
457
|
|
|
*/ |
458
|
77 |
|
public function set_discount_total( $value ) { |
459
|
77 |
|
$this->totals['discount_total'] = wc_cart_round_discount( $value, wc_get_price_decimals() ); |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* Set discount_tax. |
464
|
|
|
* |
465
|
|
|
* @since 3.2.0 |
466
|
|
|
* @param string $value Value to set. |
467
|
|
|
*/ |
468
|
77 |
|
public function set_discount_tax( $value ) { |
469
|
77 |
|
$this->totals['discount_tax'] = wc_round_tax_total( $value ); |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
/** |
473
|
|
|
* Set shipping_total. |
474
|
|
|
* |
475
|
|
|
* @since 3.2.0 |
476
|
|
|
* @param string $value Value to set. |
477
|
|
|
*/ |
478
|
77 |
|
public function set_shipping_total( $value ) { |
479
|
77 |
|
$this->totals['shipping_total'] = wc_format_decimal( $value, wc_get_price_decimals() ); |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* Set shipping_tax. |
484
|
|
|
* |
485
|
|
|
* @since 3.2.0 |
486
|
|
|
* @param string $value Value to set. |
487
|
|
|
*/ |
488
|
77 |
|
public function set_shipping_tax( $value ) { |
489
|
77 |
|
$this->totals['shipping_tax'] = wc_round_tax_total( $value ); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* Set cart_contents_total. |
494
|
|
|
* |
495
|
|
|
* @since 3.2.0 |
496
|
|
|
* @param string $value Value to set. |
497
|
|
|
*/ |
498
|
77 |
|
public function set_cart_contents_total( $value ) { |
499
|
77 |
|
$this->totals['cart_contents_total'] = wc_format_decimal( $value, wc_get_price_decimals() ); |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
/** |
503
|
|
|
* Set cart tax amount. |
504
|
|
|
* |
505
|
|
|
* @since 3.2.0 |
506
|
|
|
* @param string $value Value to set. |
507
|
|
|
*/ |
508
|
77 |
|
public function set_cart_contents_tax( $value ) { |
509
|
77 |
|
$this->totals['cart_contents_tax'] = wc_round_tax_total( $value ); |
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
/** |
513
|
|
|
* Set cart total. |
514
|
|
|
* |
515
|
|
|
* @since 3.2.0 |
516
|
|
|
* @param string $value Value to set. |
517
|
|
|
*/ |
518
|
77 |
|
public function set_total( $value ) { |
519
|
77 |
|
$this->totals['total'] = wc_format_decimal( $value, wc_get_price_decimals() ); |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
/** |
523
|
|
|
* Set total tax amount. |
524
|
|
|
* |
525
|
|
|
* @since 3.2.0 |
526
|
|
|
* @param string $value Value to set. |
527
|
|
|
*/ |
528
|
77 |
|
public function set_total_tax( $value ) { |
529
|
77 |
|
$this->totals['total_tax'] = wc_round_tax_total( $value ); |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* Set fee amount. |
534
|
|
|
* |
535
|
|
|
* @since 3.2.0 |
536
|
|
|
* @param string $value Value to set. |
537
|
|
|
*/ |
538
|
77 |
|
public function set_fee_total( $value ) { |
539
|
77 |
|
$this->totals['fee_total'] = wc_format_decimal( $value, wc_get_price_decimals() ); |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
/** |
543
|
|
|
* Set fee tax. |
544
|
|
|
* |
545
|
|
|
* @since 3.2.0 |
546
|
|
|
* @param string $value Value to set. |
547
|
|
|
*/ |
548
|
77 |
|
public function set_fee_tax( $value ) { |
549
|
77 |
|
$this->totals['fee_tax'] = wc_round_tax_total( $value ); |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
/** |
553
|
|
|
* Set taxes. |
554
|
|
|
* |
555
|
|
|
* @since 3.2.0 |
556
|
|
|
* @param array $value Tax values. |
557
|
|
|
*/ |
558
|
77 |
|
public function set_shipping_taxes( $value ) { |
559
|
77 |
|
$this->totals['shipping_taxes'] = (array) $value; |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* Set taxes. |
564
|
|
|
* |
565
|
|
|
* @since 3.2.0 |
566
|
|
|
* @param array $value Tax values. |
567
|
|
|
*/ |
568
|
77 |
|
public function set_cart_contents_taxes( $value ) { |
569
|
77 |
|
$this->totals['cart_contents_taxes'] = (array) $value; |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* Set taxes. |
574
|
|
|
* |
575
|
|
|
* @since 3.2.0 |
576
|
|
|
* @param array $value Tax values. |
577
|
|
|
*/ |
578
|
77 |
|
public function set_fee_taxes( $value ) { |
579
|
77 |
|
$this->totals['fee_taxes'] = (array) $value; |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
/* |
583
|
|
|
|-------------------------------------------------------------------------- |
584
|
|
|
| Helper methods. |
585
|
|
|
|-------------------------------------------------------------------------- |
586
|
|
|
*/ |
587
|
|
|
|
588
|
|
|
/** |
589
|
|
|
* Returns the cart and shipping taxes, merged. |
590
|
|
|
* |
591
|
|
|
* @return array merged taxes |
592
|
|
|
*/ |
593
|
1 |
|
public function get_taxes() { |
594
|
1 |
|
return apply_filters( 'woocommerce_cart_get_taxes', wc_array_merge_recursive_numeric( $this->get_shipping_taxes(), $this->get_cart_contents_taxes(), $this->get_fee_taxes() ), $this ); |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
/** |
598
|
|
|
* Returns the contents of the cart in an array. |
599
|
|
|
* |
600
|
|
|
* @return array contents of the cart |
601
|
|
|
*/ |
602
|
176 |
|
public function get_cart() { |
603
|
176 |
|
if ( ! did_action( 'wp_loaded' ) ) { |
604
|
|
|
wc_doing_it_wrong( __FUNCTION__, __( 'Get cart should not be called before the wp_loaded action.', 'woocommerce' ), '2.3' ); |
605
|
|
|
} |
606
|
176 |
|
if ( ! did_action( 'woocommerce_load_cart_from_session' ) ) { |
607
|
|
|
$this->session->get_cart_from_session(); |
608
|
|
|
} |
609
|
176 |
|
return array_filter( $this->get_cart_contents() ); |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Returns a specific item in the cart. |
614
|
|
|
* |
615
|
|
|
* @param string $item_key Cart item key. |
616
|
|
|
* @return array Item data |
617
|
|
|
*/ |
618
|
|
|
public function get_cart_item( $item_key ) { |
619
|
|
|
return isset( $this->cart_contents[ $item_key ] ) ? $this->cart_contents[ $item_key ] : array(); |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
/** |
623
|
|
|
* Checks if the cart is empty. |
624
|
|
|
* |
625
|
|
|
* @return bool |
626
|
|
|
*/ |
627
|
84 |
|
public function is_empty() { |
628
|
84 |
|
return 0 === count( $this->get_cart() ); |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
/** |
632
|
|
|
* Empties the cart and optionally the persistent cart too. |
633
|
|
|
* |
634
|
|
|
* @param bool $clear_persistent_cart Should the persistant cart be cleared too. Defaults to true. |
635
|
|
|
*/ |
636
|
96 |
|
public function empty_cart( $clear_persistent_cart = true ) { |
637
|
96 |
|
$this->cart_contents = array(); |
638
|
96 |
|
$this->removed_cart_contents = array(); |
639
|
96 |
|
$this->shipping_methods = array(); |
640
|
96 |
|
$this->coupon_discount_totals = array(); |
641
|
96 |
|
$this->coupon_discount_tax_totals = array(); |
642
|
96 |
|
$this->applied_coupons = array(); |
643
|
96 |
|
$this->totals = $this->default_totals; |
644
|
|
|
|
645
|
96 |
|
if ( $clear_persistent_cart ) { |
646
|
95 |
|
$this->session->persistent_cart_destroy(); |
647
|
|
|
} |
648
|
|
|
|
649
|
96 |
|
$this->fees_api->remove_all_fees(); |
650
|
|
|
|
651
|
96 |
|
do_action( 'woocommerce_cart_emptied' ); |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
/** |
655
|
|
|
* Get number of items in the cart. |
656
|
|
|
* |
657
|
|
|
* @return int |
658
|
|
|
*/ |
659
|
5 |
|
public function get_cart_contents_count() { |
660
|
5 |
|
return apply_filters( 'woocommerce_cart_contents_count', array_sum( wp_list_pluck( $this->get_cart(), 'quantity' ) ) ); |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
/** |
664
|
|
|
* Get weight of items in the cart. |
665
|
|
|
* |
666
|
|
|
* @since 2.5.0 |
667
|
|
|
* @return int |
668
|
|
|
*/ |
669
|
1 |
|
public function get_cart_contents_weight() { |
670
|
1 |
|
$weight = 0; |
671
|
|
|
|
672
|
1 |
|
foreach ( $this->get_cart() as $cart_item_key => $values ) { |
673
|
1 |
|
if ( $values['data']->has_weight() ) { |
674
|
1 |
|
$weight += (float) $values['data']->get_weight() * $values['quantity']; |
675
|
|
|
} |
676
|
|
|
} |
677
|
|
|
|
678
|
1 |
|
return apply_filters( 'woocommerce_cart_contents_weight', $weight ); |
679
|
|
|
} |
680
|
|
|
|
681
|
|
|
/** |
682
|
|
|
* Get cart items quantities - merged so we can do accurate stock checks on items across multiple lines. |
683
|
|
|
* |
684
|
|
|
* @return array |
685
|
|
|
*/ |
686
|
3 |
|
public function get_cart_item_quantities() { |
687
|
3 |
|
$quantities = array(); |
688
|
|
|
|
689
|
3 |
|
foreach ( $this->get_cart() as $cart_item_key => $values ) { |
690
|
3 |
|
$product = $values['data']; |
691
|
3 |
|
$quantities[ $product->get_stock_managed_by_id() ] = isset( $quantities[ $product->get_stock_managed_by_id() ] ) ? $quantities[ $product->get_stock_managed_by_id() ] + $values['quantity'] : $values['quantity']; |
692
|
|
|
} |
693
|
|
|
|
694
|
3 |
|
return $quantities; |
695
|
|
|
} |
696
|
|
|
|
697
|
|
|
/** |
698
|
|
|
* Check all cart items for errors. |
699
|
|
|
*/ |
700
|
1 |
|
public function check_cart_items() { |
701
|
1 |
|
$return = true; |
702
|
1 |
|
$result = $this->check_cart_item_validity(); |
703
|
|
|
|
704
|
1 |
|
if ( is_wp_error( $result ) ) { |
705
|
|
|
wc_add_notice( $result->get_error_message(), 'error' ); |
706
|
|
|
$return = false; |
707
|
|
|
} |
708
|
|
|
|
709
|
1 |
|
$result = $this->check_cart_item_stock(); |
710
|
|
|
|
711
|
1 |
|
if ( is_wp_error( $result ) ) { |
712
|
|
|
wc_add_notice( $result->get_error_message(), 'error' ); |
713
|
|
|
$return = false; |
714
|
|
|
} |
715
|
|
|
|
716
|
1 |
|
return $return; |
717
|
|
|
|
718
|
|
|
} |
719
|
|
|
|
720
|
|
|
/** |
721
|
|
|
* Check cart coupons for errors. |
722
|
|
|
*/ |
723
|
|
|
public function check_cart_coupons() { |
724
|
|
|
foreach ( $this->get_applied_coupons() as $code ) { |
725
|
|
|
$coupon = new WC_Coupon( $code ); |
726
|
|
|
|
727
|
|
|
if ( ! $coupon->is_valid() ) { |
|
|
|
|
728
|
|
|
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_INVALID_REMOVED ); |
729
|
|
|
$this->remove_coupon( $code ); |
730
|
|
|
} |
731
|
|
|
} |
732
|
|
|
} |
733
|
|
|
|
734
|
|
|
/** |
735
|
|
|
* Looks through cart items and checks the posts are not trashed or deleted. |
736
|
|
|
* |
737
|
|
|
* @return bool|WP_Error |
738
|
|
|
*/ |
739
|
2 |
|
public function check_cart_item_validity() { |
740
|
2 |
|
$return = true; |
741
|
|
|
|
742
|
2 |
|
foreach ( $this->get_cart() as $cart_item_key => $values ) { |
743
|
2 |
|
$product = $values['data']; |
744
|
|
|
|
745
|
2 |
|
if ( ! $product || ! $product->exists() || 'trash' === $product->get_status() ) { |
746
|
|
|
$this->set_quantity( $cart_item_key, 0 ); |
747
|
2 |
|
$return = new WP_Error( 'invalid', __( 'An item which is no longer available was removed from your cart.', 'woocommerce' ) ); |
748
|
|
|
} |
749
|
|
|
} |
750
|
|
|
|
751
|
2 |
|
return $return; |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
/** |
755
|
|
|
* Looks through the cart to check each item is in stock. If not, add an error. |
756
|
|
|
* |
757
|
|
|
* @return bool|WP_Error |
758
|
|
|
*/ |
759
|
2 |
|
public function check_cart_item_stock() { |
760
|
2 |
|
$error = new WP_Error(); |
761
|
2 |
|
$product_qty_in_cart = $this->get_cart_item_quantities(); |
762
|
2 |
|
$hold_stock_minutes = (int) get_option( 'woocommerce_hold_stock_minutes', 0 ); |
763
|
2 |
|
$current_session_order_id = isset( WC()->session->order_awaiting_payment ) ? absint( WC()->session->order_awaiting_payment ) : 0; |
764
|
|
|
|
765
|
2 |
|
foreach ( $this->get_cart() as $cart_item_key => $values ) { |
766
|
2 |
|
$product = $values['data']; |
767
|
|
|
|
768
|
|
|
// Check stock based on stock-status. |
769
|
2 |
|
if ( ! $product->is_in_stock() ) { |
770
|
|
|
/* translators: %s: product name */ |
771
|
|
|
$error->add( 'out-of-stock', sprintf( __( 'Sorry, "%s" is not in stock. Please edit your cart and try again. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name() ) ); |
772
|
|
|
return $error; |
773
|
|
|
} |
774
|
|
|
|
775
|
|
|
// We only need to check products managing stock, with a limited stock qty. |
776
|
2 |
|
if ( ! $product->managing_stock() || $product->backorders_allowed() ) { |
777
|
2 |
|
continue; |
778
|
|
|
} |
779
|
|
|
|
780
|
|
|
// Check stock based on all items in the cart and consider any held stock within pending orders. |
781
|
|
|
$held_stock = ( $hold_stock_minutes > 0 ) ? wc_get_held_stock_quantity( $product, $current_session_order_id ) : 0; |
782
|
|
|
$required_stock = $product_qty_in_cart[ $product->get_stock_managed_by_id() ]; |
783
|
|
|
|
784
|
|
|
if ( $product->get_stock_quantity() < ( $held_stock + $required_stock ) ) { |
785
|
|
|
/* translators: 1: product name 2: quantity in stock */ |
786
|
|
|
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s available). We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity() - $held_stock, $product ) ) ); |
787
|
|
|
return $error; |
788
|
|
|
} |
789
|
|
|
} |
790
|
|
|
|
791
|
2 |
|
return true; |
792
|
|
|
} |
793
|
|
|
|
794
|
|
|
/** |
795
|
|
|
* Gets and formats a list of cart item data + variations for display on the frontend. |
796
|
|
|
* |
797
|
|
|
* @param array $cart_item Cart item object. |
798
|
|
|
* @param bool $flat Should the data be returned flat or in a list. |
799
|
|
|
* @return string |
800
|
|
|
*/ |
801
|
|
|
public function get_item_data( $cart_item, $flat = false ) { |
802
|
|
|
wc_deprecated_function( 'WC_Cart::get_item_data', '3.3', 'wc_get_formatted_cart_item_data' ); |
803
|
|
|
|
804
|
|
|
return wc_get_formatted_cart_item_data( $cart_item, $flat ); |
805
|
|
|
} |
806
|
|
|
|
807
|
|
|
/** |
808
|
|
|
* Gets cross sells based on the items in the cart. |
809
|
|
|
* |
810
|
|
|
* @return array cross_sells (item ids) |
811
|
|
|
*/ |
812
|
1 |
|
public function get_cross_sells() { |
813
|
1 |
|
$cross_sells = array(); |
814
|
1 |
|
$in_cart = array(); |
815
|
1 |
|
if ( ! $this->is_empty() ) { |
816
|
1 |
|
foreach ( $this->get_cart() as $cart_item_key => $values ) { |
817
|
1 |
|
if ( $values['quantity'] > 0 ) { |
818
|
1 |
|
$cross_sells = array_merge( $values['data']->get_cross_sell_ids(), $cross_sells ); |
819
|
1 |
|
$in_cart[] = $values['product_id']; |
820
|
|
|
} |
821
|
|
|
} |
822
|
|
|
} |
823
|
1 |
|
$cross_sells = array_diff( $cross_sells, $in_cart ); |
824
|
1 |
|
return apply_filters( 'woocommerce_cart_crosssell_ids', wp_parse_id_list( $cross_sells ), $this ); |
825
|
|
|
} |
826
|
|
|
|
827
|
|
|
/** |
828
|
|
|
* Gets the url to remove an item from the cart. |
829
|
|
|
* |
830
|
|
|
* @param string $cart_item_key contains the id of the cart item. |
831
|
|
|
* @return string url to page |
832
|
|
|
*/ |
833
|
|
|
public function get_remove_url( $cart_item_key ) { |
834
|
|
|
wc_deprecated_function( 'WC_Cart::get_remove_url', '3.3', 'wc_get_cart_remove_url' ); |
835
|
|
|
|
836
|
|
|
return wc_get_cart_remove_url( $cart_item_key ); |
837
|
|
|
} |
838
|
|
|
|
839
|
|
|
/** |
840
|
|
|
* Gets the url to re-add an item into the cart. |
841
|
|
|
* |
842
|
|
|
* @param string $cart_item_key Cart item key to undo. |
843
|
|
|
* @return string url to page |
844
|
|
|
*/ |
845
|
|
|
public function get_undo_url( $cart_item_key ) { |
846
|
|
|
wc_deprecated_function( 'WC_Cart::get_undo_url', '3.3', 'wc_get_cart_undo_url' ); |
847
|
|
|
|
848
|
|
|
return wc_get_cart_undo_url( $cart_item_key ); |
849
|
|
|
} |
850
|
|
|
|
851
|
|
|
/** |
852
|
|
|
* Get taxes, merged by code, formatted ready for output. |
853
|
|
|
* |
854
|
|
|
* @return array |
855
|
|
|
*/ |
856
|
1 |
|
public function get_tax_totals() { |
857
|
1 |
|
$taxes = $this->get_taxes(); |
858
|
1 |
|
$tax_totals = array(); |
859
|
|
|
|
860
|
1 |
|
foreach ( $taxes as $key => $tax ) { |
861
|
1 |
|
$code = WC_Tax::get_rate_code( $key ); |
862
|
|
|
|
863
|
1 |
|
if ( $code || apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) === $key ) { |
864
|
1 |
View Code Duplication |
if ( ! isset( $tax_totals[ $code ] ) ) { |
865
|
1 |
|
$tax_totals[ $code ] = new stdClass(); |
866
|
1 |
|
$tax_totals[ $code ]->amount = 0; |
867
|
|
|
} |
868
|
1 |
|
$tax_totals[ $code ]->tax_rate_id = $key; |
869
|
1 |
|
$tax_totals[ $code ]->is_compound = WC_Tax::is_compound( $key ); |
870
|
1 |
|
$tax_totals[ $code ]->label = WC_Tax::get_rate_label( $key ); |
871
|
1 |
|
$tax_totals[ $code ]->amount += wc_round_tax_total( $tax ); |
872
|
1 |
|
$tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ) ); |
873
|
|
|
} |
874
|
|
|
} |
875
|
|
|
|
876
|
1 |
View Code Duplication |
if ( apply_filters( 'woocommerce_cart_hide_zero_taxes', true ) ) { |
877
|
1 |
|
$amounts = array_filter( wp_list_pluck( $tax_totals, 'amount' ) ); |
878
|
1 |
|
$tax_totals = array_intersect_key( $tax_totals, $amounts ); |
879
|
|
|
} |
880
|
|
|
|
881
|
1 |
|
return apply_filters( 'woocommerce_cart_tax_totals', $tax_totals, $this ); |
882
|
|
|
} |
883
|
|
|
|
884
|
|
|
/** |
885
|
|
|
* Get all tax classes for items in the cart. |
886
|
|
|
* |
887
|
|
|
* @return array |
888
|
|
|
*/ |
889
|
|
View Code Duplication |
public function get_cart_item_tax_classes() { |
|
|
|
|
890
|
|
|
$found_tax_classes = array(); |
891
|
|
|
|
892
|
|
|
foreach ( WC()->cart->get_cart() as $item ) { |
893
|
|
|
if ( $item['data'] && ( $item['data']->is_taxable() || $item['data']->is_shipping_taxable() ) ) { |
894
|
|
|
$found_tax_classes[] = $item['data']->get_tax_class(); |
895
|
|
|
} |
896
|
|
|
} |
897
|
|
|
|
898
|
|
|
return array_unique( $found_tax_classes ); |
899
|
|
|
} |
900
|
|
|
|
901
|
|
|
/** |
902
|
|
|
* Get all tax classes for shipping based on the items in the cart. |
903
|
|
|
* |
904
|
|
|
* @return array |
905
|
|
|
*/ |
906
|
5 |
View Code Duplication |
public function get_cart_item_tax_classes_for_shipping() { |
|
|
|
|
907
|
5 |
|
$found_tax_classes = array(); |
908
|
|
|
|
909
|
5 |
|
foreach ( WC()->cart->get_cart() as $item ) { |
910
|
5 |
|
if ( $item['data'] && ( $item['data']->is_shipping_taxable() ) ) { |
911
|
5 |
|
$found_tax_classes[] = $item['data']->get_tax_class(); |
912
|
|
|
} |
913
|
|
|
} |
914
|
|
|
|
915
|
5 |
|
return array_unique( $found_tax_classes ); |
916
|
|
|
} |
917
|
|
|
|
918
|
|
|
/** |
919
|
|
|
* Determines the value that the customer spent and the subtotal |
920
|
|
|
* displayed, used for things like coupon validation. |
921
|
|
|
* |
922
|
|
|
* Since the coupon lines are displayed based on the TAX DISPLAY value |
923
|
|
|
* of cart, this is used to determine the spend. |
924
|
|
|
* |
925
|
|
|
* If cart totals are shown including tax, use the subtotal. |
926
|
|
|
* If cart totals are shown excluding tax, use the subtotal ex tax |
927
|
|
|
* (tax is shown after coupons). |
928
|
|
|
* |
929
|
|
|
* @since 2.6.0 |
930
|
|
|
* @return string |
931
|
|
|
*/ |
932
|
64 |
|
public function get_displayed_subtotal() { |
933
|
64 |
|
return $this->display_prices_including_tax() ? $this->get_subtotal() + $this->get_subtotal_tax() : $this->get_subtotal(); |
934
|
|
|
} |
935
|
|
|
|
936
|
|
|
/** |
937
|
|
|
* Check if product is in the cart and return cart item key. |
938
|
|
|
* |
939
|
|
|
* Cart item key will be unique based on the item and its properties, such as variations. |
940
|
|
|
* |
941
|
|
|
* @param mixed $cart_id id of product to find in the cart. |
942
|
|
|
* @return string cart item key |
943
|
|
|
*/ |
944
|
77 |
|
public function find_product_in_cart( $cart_id = false ) { |
945
|
77 |
|
if ( false !== $cart_id ) { |
946
|
77 |
|
if ( is_array( $this->cart_contents ) && isset( $this->cart_contents[ $cart_id ] ) ) { |
947
|
2 |
|
return $cart_id; |
948
|
|
|
} |
949
|
|
|
} |
950
|
77 |
|
return ''; |
951
|
|
|
} |
952
|
|
|
|
953
|
|
|
/** |
954
|
|
|
* Generate a unique ID for the cart item being added. |
955
|
|
|
* |
956
|
|
|
* @param int $product_id - id of the product the key is being generated for. |
957
|
|
|
* @param int $variation_id of the product the key is being generated for. |
958
|
|
|
* @param array $variation data for the cart item. |
959
|
|
|
* @param array $cart_item_data other cart item data passed which affects this items uniqueness in the cart. |
960
|
|
|
* @return string cart item key |
961
|
|
|
*/ |
962
|
78 |
|
public function generate_cart_id( $product_id, $variation_id = 0, $variation = array(), $cart_item_data = array() ) { |
963
|
78 |
|
$id_parts = array( $product_id ); |
964
|
|
|
|
965
|
78 |
|
if ( $variation_id && 0 !== $variation_id ) { |
966
|
6 |
|
$id_parts[] = $variation_id; |
967
|
|
|
} |
968
|
|
|
|
969
|
78 |
|
if ( is_array( $variation ) && ! empty( $variation ) ) { |
970
|
6 |
|
$variation_key = ''; |
971
|
6 |
|
foreach ( $variation as $key => $value ) { |
972
|
6 |
|
$variation_key .= trim( $key ) . trim( $value ); |
973
|
|
|
} |
974
|
6 |
|
$id_parts[] = $variation_key; |
975
|
|
|
} |
976
|
|
|
|
977
|
78 |
|
if ( is_array( $cart_item_data ) && ! empty( $cart_item_data ) ) { |
978
|
1 |
|
$cart_item_data_key = ''; |
979
|
1 |
|
foreach ( $cart_item_data as $key => $value ) { |
980
|
1 |
|
if ( is_array( $value ) || is_object( $value ) ) { |
981
|
1 |
|
$value = http_build_query( $value ); |
982
|
|
|
} |
983
|
1 |
|
$cart_item_data_key .= trim( $key ) . trim( $value ); |
984
|
|
|
|
985
|
|
|
} |
986
|
1 |
|
$id_parts[] = $cart_item_data_key; |
987
|
|
|
} |
988
|
|
|
|
989
|
78 |
|
return apply_filters( 'woocommerce_cart_id', md5( implode( '_', $id_parts ) ), $product_id, $variation_id, $variation, $cart_item_data ); |
990
|
|
|
} |
991
|
|
|
|
992
|
|
|
/** |
993
|
|
|
* Add a product to the cart. |
994
|
|
|
* |
995
|
|
|
* @throws Exception Plugins can throw an exception to prevent adding to cart. |
996
|
|
|
* @param int $product_id contains the id of the product to add to the cart. |
997
|
|
|
* @param int $quantity contains the quantity of the item to add. |
998
|
|
|
* @param int $variation_id ID of the variation being added to the cart. |
999
|
|
|
* @param array $variation attribute values. |
1000
|
|
|
* @param array $cart_item_data extra cart item data we want to pass into the item. |
1001
|
|
|
* @return string|bool $cart_item_key |
1002
|
|
|
*/ |
1003
|
78 |
|
public function add_to_cart( $product_id = 0, $quantity = 1, $variation_id = 0, $variation = array(), $cart_item_data = array() ) { |
1004
|
|
|
try { |
1005
|
78 |
|
$product_id = absint( $product_id ); |
1006
|
78 |
|
$variation_id = absint( $variation_id ); |
1007
|
|
|
|
1008
|
|
|
// Ensure we don't add a variation to the cart directly by variation ID. |
1009
|
78 |
|
if ( 'product_variation' === get_post_type( $product_id ) ) { |
1010
|
|
|
$variation_id = $product_id; |
1011
|
|
|
$product_id = wp_get_post_parent_id( $variation_id ); |
1012
|
|
|
} |
1013
|
|
|
|
1014
|
78 |
|
$product_data = wc_get_product( $variation_id ? $variation_id : $product_id ); |
1015
|
78 |
|
$quantity = apply_filters( 'woocommerce_add_to_cart_quantity', $quantity, $product_id ); |
1016
|
|
|
|
1017
|
78 |
|
if ( $quantity <= 0 || ! $product_data || 'trash' === $product_data->get_status() ) { |
1018
|
1 |
|
return false; |
1019
|
|
|
} |
1020
|
|
|
|
1021
|
|
|
// Load cart item data - may be added by other plugins. |
1022
|
77 |
|
$cart_item_data = (array) apply_filters( 'woocommerce_add_cart_item_data', $cart_item_data, $product_id, $variation_id, $quantity ); |
1023
|
|
|
|
1024
|
|
|
// Generate a ID based on product ID, variation ID, variation data, and other cart item data. |
1025
|
77 |
|
$cart_id = $this->generate_cart_id( $product_id, $variation_id, $variation, $cart_item_data ); |
1026
|
|
|
|
1027
|
|
|
// Find the cart item key in the existing cart. |
1028
|
77 |
|
$cart_item_key = $this->find_product_in_cart( $cart_id ); |
1029
|
|
|
|
1030
|
|
|
// Force quantity to 1 if sold individually and check for existing item in cart. |
1031
|
77 |
|
if ( $product_data->is_sold_individually() ) { |
1032
|
1 |
|
$quantity = apply_filters( 'woocommerce_add_to_cart_sold_individually_quantity', 1, $quantity, $product_id, $variation_id, $cart_item_data ); |
1033
|
1 |
|
$found_in_cart = apply_filters( 'woocommerce_add_to_cart_sold_individually_found_in_cart', $cart_item_key && $this->cart_contents[ $cart_item_key ]['quantity'] > 0, $product_id, $variation_id, $cart_item_data, $cart_id ); |
1034
|
|
|
|
1035
|
1 |
|
if ( $found_in_cart ) { |
1036
|
|
|
/* translators: %s: product name */ |
1037
|
|
|
throw new Exception( sprintf( '<a href="%s" class="button wc-forward">%s</a> %s', wc_get_cart_url(), __( 'View cart', 'woocommerce' ), sprintf( __( 'You cannot add another "%s" to your cart.', 'woocommerce' ), $product_data->get_name() ) ) ); |
1038
|
|
|
} |
1039
|
|
|
} |
1040
|
|
|
|
1041
|
77 |
|
if ( ! $product_data->is_purchasable() ) { |
1042
|
|
|
throw new Exception( __( 'Sorry, this product cannot be purchased.', 'woocommerce' ) ); |
1043
|
|
|
} |
1044
|
|
|
|
1045
|
|
|
// Stock check - only check if we're managing stock and backorders are not allowed. |
1046
|
77 |
|
if ( ! $product_data->is_in_stock() ) { |
1047
|
|
|
/* translators: %s: product name */ |
1048
|
|
|
throw new Exception( sprintf( __( 'You cannot add "%s" to the cart because the product is out of stock.', 'woocommerce' ), $product_data->get_name() ) ); |
1049
|
|
|
} |
1050
|
|
|
|
1051
|
77 |
View Code Duplication |
if ( ! $product_data->has_enough_stock( $quantity ) ) { |
1052
|
|
|
/* translators: 1: product name 2: quantity in stock */ |
1053
|
|
|
throw new Exception( sprintf( __( 'You cannot add that amount of "%1$s" to the cart because there is not enough stock (%2$s remaining).', 'woocommerce' ), $product_data->get_name(), wc_format_stock_quantity_for_display( $product_data->get_stock_quantity(), $product_data ) ) ); |
1054
|
|
|
} |
1055
|
|
|
|
1056
|
|
|
// Stock check - this time accounting for whats already in-cart. |
1057
|
77 |
|
if ( $product_data->managing_stock() ) { |
1058
|
|
|
$products_qty_in_cart = $this->get_cart_item_quantities(); |
1059
|
|
|
|
1060
|
|
|
if ( isset( $products_qty_in_cart[ $product_data->get_stock_managed_by_id() ] ) && ! $product_data->has_enough_stock( $products_qty_in_cart[ $product_data->get_stock_managed_by_id() ] + $quantity ) ) { |
1061
|
|
|
throw new Exception( |
1062
|
|
|
sprintf( |
1063
|
|
|
'<a href="%s" class="button wc-forward">%s</a> %s', |
1064
|
|
|
wc_get_cart_url(), |
1065
|
|
|
__( 'View cart', 'woocommerce' ), |
1066
|
|
|
/* translators: 1: quantity in stock 2: current quantity */ |
1067
|
|
|
sprintf( __( 'You cannot add that amount to the cart — we have %1$s in stock and you already have %2$s in your cart.', 'woocommerce' ), wc_format_stock_quantity_for_display( $product_data->get_stock_quantity(), $product_data ), wc_format_stock_quantity_for_display( $products_qty_in_cart[ $product_data->get_stock_managed_by_id() ], $product_data ) ) |
1068
|
|
|
) |
1069
|
|
|
); |
1070
|
|
|
} |
1071
|
|
|
} |
1072
|
|
|
|
1073
|
|
|
// If cart_item_key is set, the item is already in the cart. |
1074
|
77 |
|
if ( $cart_item_key ) { |
1075
|
1 |
|
$new_quantity = $quantity + $this->cart_contents[ $cart_item_key ]['quantity']; |
1076
|
1 |
|
$this->set_quantity( $cart_item_key, $new_quantity, false ); |
1077
|
|
|
} else { |
1078
|
77 |
|
$cart_item_key = $cart_id; |
1079
|
|
|
|
1080
|
|
|
// Add item after merging with $cart_item_data - hook to allow plugins to modify cart item. |
1081
|
77 |
|
$this->cart_contents[ $cart_item_key ] = apply_filters( |
1082
|
77 |
|
'woocommerce_add_cart_item', |
1083
|
77 |
|
array_merge( |
1084
|
77 |
|
$cart_item_data, |
1085
|
77 |
|
array( |
1086
|
77 |
|
'key' => $cart_item_key, |
1087
|
77 |
|
'product_id' => $product_id, |
1088
|
77 |
|
'variation_id' => $variation_id, |
1089
|
77 |
|
'variation' => $variation, |
1090
|
77 |
|
'quantity' => $quantity, |
1091
|
|
|
'data' => $product_data, |
1092
|
77 |
|
'data_hash' => wc_get_cart_item_data_hash( $product_data ), |
1093
|
|
|
) |
1094
|
|
|
), |
1095
|
|
|
$cart_item_key |
1096
|
77 |
|
); |
1097
|
|
|
} |
1098
|
77 |
|
|
1099
|
|
|
$this->cart_contents = apply_filters( 'woocommerce_cart_contents_changed', $this->cart_contents ); |
1100
|
77 |
|
|
1101
|
|
|
do_action( 'woocommerce_add_to_cart', $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ); |
1102
|
|
|
|
1103
|
|
|
return $cart_item_key; |
1104
|
|
|
|
1105
|
|
|
} catch ( Exception $e ) { |
1106
|
|
|
if ( $e->getMessage() ) { |
1107
|
|
|
wc_add_notice( $e->getMessage(), 'error' ); |
1108
|
|
|
} |
1109
|
|
|
return false; |
1110
|
|
|
} |
1111
|
|
|
} |
1112
|
|
|
|
1113
|
|
|
/** |
1114
|
|
|
* Remove a cart item. |
1115
|
|
|
* |
1116
|
|
|
* @since 2.3.0 |
1117
|
|
|
* @param string $cart_item_key Cart item key to remove from the cart. |
1118
|
|
|
* @return bool |
1119
|
|
|
*/ |
1120
|
|
|
public function remove_cart_item( $cart_item_key ) { |
1121
|
|
|
if ( isset( $this->cart_contents[ $cart_item_key ] ) ) { |
1122
|
|
|
$this->removed_cart_contents[ $cart_item_key ] = $this->cart_contents[ $cart_item_key ]; |
1123
|
|
|
|
1124
|
|
|
unset( $this->removed_cart_contents[ $cart_item_key ]['data'] ); |
1125
|
|
|
|
1126
|
|
|
do_action( 'woocommerce_remove_cart_item', $cart_item_key, $this ); |
1127
|
|
|
|
1128
|
|
|
unset( $this->cart_contents[ $cart_item_key ] ); |
1129
|
|
|
|
1130
|
|
|
do_action( 'woocommerce_cart_item_removed', $cart_item_key, $this ); |
1131
|
|
|
|
1132
|
|
|
return true; |
1133
|
|
|
} |
1134
|
|
|
return false; |
1135
|
|
|
} |
1136
|
|
|
|
1137
|
|
|
/** |
1138
|
|
|
* Restore a cart item. |
1139
|
|
|
* |
1140
|
|
|
* @param string $cart_item_key Cart item key to restore to the cart. |
1141
|
|
|
* @return bool |
1142
|
|
|
*/ |
1143
|
|
|
public function restore_cart_item( $cart_item_key ) { |
1144
|
|
|
if ( isset( $this->removed_cart_contents[ $cart_item_key ] ) ) { |
1145
|
|
|
$restore_item = $this->removed_cart_contents[ $cart_item_key ]; |
1146
|
|
|
$this->cart_contents[ $cart_item_key ] = $restore_item; |
1147
|
|
|
$this->cart_contents[ $cart_item_key ]['data'] = wc_get_product( $restore_item['variation_id'] ? $restore_item['variation_id'] : $restore_item['product_id'] ); |
1148
|
|
|
|
1149
|
|
|
do_action( 'woocommerce_restore_cart_item', $cart_item_key, $this ); |
1150
|
|
|
|
1151
|
|
|
unset( $this->removed_cart_contents[ $cart_item_key ] ); |
1152
|
|
|
|
1153
|
|
|
do_action( 'woocommerce_cart_item_restored', $cart_item_key, $this ); |
1154
|
|
|
|
1155
|
|
|
return true; |
1156
|
|
|
} |
1157
|
|
|
return false; |
1158
|
|
|
} |
1159
|
|
|
|
1160
|
|
|
/** |
1161
|
|
|
* Set the quantity for an item in the cart. |
1162
|
|
|
* |
1163
|
|
|
* @param string $cart_item_key contains the id of the cart item. |
1164
|
|
|
* @param int $quantity contains the quantity of the item. |
1165
|
2 |
|
* @param bool $refresh_totals whether or not to calculate totals after setting the new qty. |
1166
|
2 |
|
* @return bool |
1167
|
1 |
|
*/ |
1168
|
1 |
|
public function set_quantity( $cart_item_key, $quantity = 1, $refresh_totals = true ) { |
1169
|
|
|
if ( 0 === $quantity || $quantity < 0 ) { |
1170
|
2 |
|
do_action( 'woocommerce_before_cart_item_quantity_zero', $cart_item_key, $this ); |
1171
|
2 |
|
unset( $this->cart_contents[ $cart_item_key ] ); |
1172
|
2 |
|
} else { |
1173
|
|
|
$old_quantity = $this->cart_contents[ $cart_item_key ]['quantity']; |
1174
|
|
|
$this->cart_contents[ $cart_item_key ]['quantity'] = $quantity; |
1175
|
2 |
|
do_action( 'woocommerce_after_cart_item_quantity_update', $cart_item_key, $quantity, $old_quantity, $this ); |
1176
|
1 |
|
} |
1177
|
|
|
|
1178
|
|
|
if ( $refresh_totals ) { |
1179
|
2 |
|
$this->calculate_totals(); |
1180
|
|
|
} |
1181
|
|
|
|
1182
|
|
|
return true; |
1183
|
|
|
} |
1184
|
|
|
|
1185
|
|
|
/** |
1186
|
|
|
* Get cart's owner. |
1187
|
|
|
* |
1188
|
28 |
|
* @since 3.2.0 |
1189
|
28 |
|
* @return WC_Customer |
1190
|
|
|
*/ |
1191
|
|
|
public function get_customer() { |
1192
|
|
|
return WC()->customer; |
1193
|
|
|
} |
1194
|
|
|
|
1195
|
|
|
/** |
1196
|
|
|
* Calculate totals for the items in the cart. |
1197
|
84 |
|
* |
1198
|
84 |
|
* @uses WC_Cart_Totals |
1199
|
|
|
*/ |
1200
|
84 |
|
public function calculate_totals() { |
1201
|
9 |
|
$this->reset_totals(); |
1202
|
9 |
|
|
1203
|
|
|
if ( $this->is_empty() ) { |
1204
|
|
|
$this->session->set_session(); |
1205
|
77 |
|
return; |
1206
|
|
|
} |
1207
|
77 |
|
|
1208
|
|
|
do_action( 'woocommerce_before_calculate_totals', $this ); |
1209
|
77 |
|
|
1210
|
|
|
new WC_Cart_Totals( $this ); |
1211
|
|
|
|
1212
|
|
|
do_action( 'woocommerce_after_calculate_totals', $this ); |
1213
|
|
|
} |
1214
|
|
|
|
1215
|
|
|
/** |
1216
|
|
|
* Looks at the totals to see if payment is actually required. |
1217
|
|
|
* |
1218
|
|
|
* @return bool |
1219
|
|
|
*/ |
1220
|
|
|
public function needs_payment() { |
1221
|
|
|
return apply_filters( 'woocommerce_cart_needs_payment', 0 < $this->get_total( 'edit' ), $this ); |
1222
|
|
|
} |
1223
|
|
|
|
1224
|
|
|
/* |
1225
|
|
|
* Shipping related functions. |
1226
|
|
|
*/ |
1227
|
|
|
|
1228
|
77 |
|
/** |
1229
|
77 |
|
* Uses the shipping class to calculate shipping then gets the totals when its finished. |
1230
|
|
|
*/ |
1231
|
77 |
|
public function calculate_shipping() { |
1232
|
77 |
|
$this->shipping_methods = $this->needs_shipping() ? $this->get_chosen_shipping_methods( WC()->shipping()->calculate_shipping( $this->get_shipping_packages() ) ) : array(); |
1233
|
77 |
|
|
1234
|
9 |
|
$shipping_taxes = wp_list_pluck( $this->shipping_methods, 'taxes' ); |
1235
|
5 |
|
$merged_taxes = array(); |
1236
|
5 |
View Code Duplication |
foreach ( $shipping_taxes as $taxes ) { |
1237
|
|
|
foreach ( $taxes as $tax_id => $tax_amount ) { |
1238
|
9 |
|
if ( ! isset( $merged_taxes[ $tax_id ] ) ) { |
1239
|
|
|
$merged_taxes[ $tax_id ] = 0; |
1240
|
|
|
} |
1241
|
|
|
$merged_taxes[ $tax_id ] += $tax_amount; |
1242
|
77 |
|
} |
1243
|
77 |
|
} |
1244
|
77 |
|
|
1245
|
|
|
$this->set_shipping_total( array_sum( wp_list_pluck( $this->shipping_methods, 'cost' ) ) ); |
1246
|
77 |
|
$this->set_shipping_tax( array_sum( $merged_taxes ) ); |
1247
|
|
|
$this->set_shipping_taxes( $merged_taxes ); |
1248
|
|
|
|
1249
|
|
|
return $this->shipping_methods; |
1250
|
|
|
} |
1251
|
|
|
|
1252
|
|
|
/** |
1253
|
|
|
* Given a set of packages with rates, get the chosen ones only. |
1254
|
|
|
* |
1255
|
|
|
* @since 3.2.0 |
1256
|
15 |
|
* @param array $calculated_shipping_packages Array of packages. |
1257
|
15 |
|
* @return array |
1258
|
|
|
*/ |
1259
|
15 |
|
protected function get_chosen_shipping_methods( $calculated_shipping_packages = array() ) { |
1260
|
15 |
|
$chosen_methods = array(); |
1261
|
15 |
|
// Get chosen methods for each package to get our totals. |
1262
|
15 |
|
foreach ( $calculated_shipping_packages as $key => $package ) { |
1263
|
|
|
$chosen_method = wc_get_chosen_shipping_method_for_package( $key, $package ); |
1264
|
|
|
if ( $chosen_method ) { |
1265
|
15 |
|
$chosen_methods[ $key ] = $package['rates'][ $chosen_method ]; |
1266
|
|
|
} |
1267
|
|
|
} |
1268
|
|
|
return $chosen_methods; |
1269
|
|
|
} |
1270
|
|
|
|
1271
|
|
|
/** |
1272
|
|
|
* Filter items needing shipping callback. |
1273
|
|
|
* |
1274
|
|
|
* @since 3.0.0 |
1275
|
15 |
|
* @param array $item Item to check for shipping. |
1276
|
15 |
|
* @return bool |
1277
|
15 |
|
*/ |
1278
|
|
|
protected function filter_items_needing_shipping( $item ) { |
1279
|
|
|
$product = $item['data']; |
1280
|
|
|
return $product && $product->needs_shipping(); |
1281
|
|
|
} |
1282
|
|
|
|
1283
|
|
|
/** |
1284
|
|
|
* Get only items that need shipping. |
1285
|
|
|
* |
1286
|
15 |
|
* @since 3.0.0 |
1287
|
15 |
|
* @return array |
1288
|
|
|
*/ |
1289
|
|
|
protected function get_items_needing_shipping() { |
1290
|
|
|
return array_filter( $this->get_cart(), array( $this, 'filter_items_needing_shipping' ) ); |
1291
|
|
|
} |
1292
|
|
|
|
1293
|
|
|
/** |
1294
|
|
|
* Get packages to calculate shipping for. |
1295
|
|
|
* |
1296
|
|
|
* This lets us calculate costs for carts that are shipped to multiple locations. |
1297
|
|
|
* |
1298
|
|
|
* Shipping methods are responsible for looping through these packages. |
1299
|
|
|
* |
1300
|
|
|
* By default we pass the cart itself as a package - plugins can change this. |
1301
|
|
|
* through the filter and break it up. |
1302
|
|
|
* |
1303
|
15 |
|
* @since 1.5.4 |
1304
|
15 |
|
* @return array of cart items |
1305
|
15 |
|
*/ |
1306
|
|
|
public function get_shipping_packages() { |
1307
|
|
|
return apply_filters( |
1308
|
15 |
|
'woocommerce_cart_shipping_packages', |
1309
|
15 |
|
array( |
1310
|
15 |
|
array( |
1311
|
|
|
'contents' => $this->get_items_needing_shipping(), |
1312
|
15 |
|
'contents_cost' => array_sum( wp_list_pluck( $this->get_items_needing_shipping(), 'line_total' ) ), |
1313
|
|
|
'applied_coupons' => $this->get_applied_coupons(), |
1314
|
|
|
'user' => array( |
1315
|
15 |
|
'ID' => get_current_user_id(), |
1316
|
15 |
|
), |
1317
|
15 |
|
'destination' => array( |
1318
|
15 |
|
'country' => $this->get_customer()->get_shipping_country(), |
1319
|
15 |
|
'state' => $this->get_customer()->get_shipping_state(), |
1320
|
15 |
|
'postcode' => $this->get_customer()->get_shipping_postcode(), |
1321
|
15 |
|
'city' => $this->get_customer()->get_shipping_city(), |
1322
|
|
|
'address' => $this->get_customer()->get_shipping_address(), |
1323
|
15 |
|
'address_1' => $this->get_customer()->get_shipping_address(), // Provide both address and address_1 for backwards compatibility. |
1324
|
|
|
'address_2' => $this->get_customer()->get_shipping_address_2(), |
1325
|
|
|
), |
1326
|
|
|
'cart_subtotal' => $this->get_displayed_subtotal(), |
1327
|
|
|
), |
1328
|
|
|
) |
1329
|
|
|
); |
1330
|
|
|
} |
1331
|
|
|
|
1332
|
|
|
/** |
1333
|
|
|
* Looks through the cart to see if shipping is actually required. |
1334
|
79 |
|
* |
1335
|
79 |
|
* @return bool whether or not the cart needs shipping |
1336
|
64 |
|
*/ |
1337
|
|
|
public function needs_shipping() { |
1338
|
15 |
|
if ( ! wc_shipping_enabled() || 0 === wc_get_shipping_method_count( true ) ) { |
1339
|
|
|
return false; |
1340
|
15 |
|
} |
1341
|
15 |
|
$needs_shipping = false; |
1342
|
15 |
|
|
1343
|
15 |
|
foreach ( $this->get_cart_contents() as $cart_item_key => $values ) { |
1344
|
|
|
if ( $values['data']->needs_shipping() ) { |
1345
|
|
|
$needs_shipping = true; |
1346
|
|
|
break; |
1347
|
15 |
|
} |
1348
|
|
|
} |
1349
|
|
|
|
1350
|
|
|
return apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping ); |
1351
|
|
|
} |
1352
|
|
|
|
1353
|
|
|
/** |
1354
|
|
|
* Should the shipping address form be shown. |
1355
|
1 |
|
* |
1356
|
1 |
|
* @return bool |
1357
|
|
|
*/ |
1358
|
|
|
public function needs_shipping_address() { |
1359
|
|
|
return apply_filters( 'woocommerce_cart_needs_shipping_address', true === $this->needs_shipping() && ! wc_ship_to_billing_address_only() ); |
1360
|
|
|
} |
1361
|
|
|
|
1362
|
|
|
/** |
1363
|
|
|
* Sees if the customer has entered enough data to calc the shipping yet. |
1364
|
77 |
|
* |
1365
|
77 |
|
* @return bool |
1366
|
|
|
*/ |
1367
|
|
|
public function show_shipping() { |
1368
|
|
|
if ( ! wc_shipping_enabled() || ! $this->get_cart_contents() ) { |
1369
|
77 |
|
return false; |
1370
|
|
|
} |
1371
|
|
|
|
1372
|
|
|
if ( 'yes' === get_option( 'woocommerce_shipping_cost_requires_address' ) ) { |
1373
|
|
|
if ( ! $this->get_customer()->has_calculated_shipping() ) { |
1374
|
|
|
if ( ! $this->get_customer()->get_shipping_country() || ( ! $this->get_customer()->get_shipping_state() && ! $this->get_customer()->get_shipping_postcode() ) ) { |
1375
|
|
|
return false; |
1376
|
|
|
} |
1377
|
77 |
|
} |
1378
|
|
|
} |
1379
|
|
|
|
1380
|
|
|
return apply_filters( 'woocommerce_cart_ready_to_calc_shipping', true ); |
1381
|
|
|
} |
1382
|
|
|
|
1383
|
|
|
/** |
1384
|
|
|
* Gets the shipping total (after calculation). |
1385
|
|
|
* |
1386
|
|
|
* @return string price or string for the shipping total |
1387
|
|
|
*/ |
1388
|
|
|
public function get_cart_shipping_total() { |
1389
|
|
|
|
1390
|
|
|
// Default total assumes Free shipping. |
1391
|
|
|
$total = __( 'Free!', 'woocommerce' ); |
1392
|
|
|
|
1393
|
|
|
if ( 0 < $this->get_shipping_total() ) { |
1394
|
|
|
|
1395
|
|
|
if ( $this->display_prices_including_tax() ) { |
1396
|
|
|
$total = wc_price( $this->shipping_total + $this->shipping_tax_total ); |
1397
|
|
|
|
1398
|
|
|
if ( $this->shipping_tax_total > 0 && ! wc_prices_include_tax() ) { |
1399
|
|
|
$total .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>'; |
1400
|
|
|
} |
1401
|
|
|
} else { |
1402
|
|
|
$total = wc_price( $this->shipping_total ); |
1403
|
|
|
|
1404
|
|
|
if ( $this->shipping_tax_total > 0 && wc_prices_include_tax() ) { |
1405
|
|
|
$total .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; |
1406
|
|
|
} |
1407
|
|
|
} |
1408
|
|
|
} |
1409
|
|
|
return apply_filters( 'woocommerce_cart_shipping_total', $total, $this ); |
1410
|
|
|
} |
1411
|
|
|
|
1412
|
|
|
/** |
1413
|
|
|
* Check for user coupons (now that we have billing email). If a coupon is invalid, add an error. |
1414
|
|
|
* |
1415
|
|
|
* Checks two types of coupons: |
1416
|
|
|
* 1. Where a list of customer emails are set (limits coupon usage to those defined). |
1417
|
|
|
* 2. Where a usage_limit_per_user is set (limits coupon usage to a number based on user ID and email). |
1418
|
|
|
* |
1419
|
|
|
* @param array $posted Post data. |
1420
|
|
|
*/ |
1421
|
|
|
public function check_customer_coupons( $posted ) { |
1422
|
|
|
foreach ( $this->get_applied_coupons() as $code ) { |
1423
|
|
|
$coupon = new WC_Coupon( $code ); |
1424
|
|
|
|
1425
|
|
|
if ( $coupon->is_valid() ) { |
|
|
|
|
1426
|
|
|
|
1427
|
|
|
// Get user and posted emails to compare. |
1428
|
|
|
$current_user = wp_get_current_user(); |
1429
|
|
|
$billing_email = isset( $posted['billing_email'] ) ? $posted['billing_email'] : ''; |
1430
|
|
|
$check_emails = array_unique( |
1431
|
|
|
array_filter( |
1432
|
|
|
array_map( |
1433
|
|
|
'strtolower', |
1434
|
|
|
array_map( |
1435
|
|
|
'sanitize_email', |
1436
|
|
|
array( |
1437
|
|
|
$billing_email, |
1438
|
|
|
$current_user->user_email, |
1439
|
|
|
) |
1440
|
|
|
) |
1441
|
|
|
) |
1442
|
|
|
) |
1443
|
|
|
); |
1444
|
|
|
|
1445
|
|
|
// Limit to defined email addresses. |
1446
|
|
|
$restrictions = $coupon->get_email_restrictions(); |
1447
|
|
|
|
1448
|
|
|
if ( is_array( $restrictions ) && 0 < count( $restrictions ) && ! $this->is_coupon_emails_allowed( $check_emails, $restrictions ) ) { |
1449
|
|
|
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_YOURS_REMOVED ); |
1450
|
|
|
$this->remove_coupon( $code ); |
1451
|
|
|
} |
1452
|
|
|
|
1453
|
|
|
// Usage limits per user - check against billing and user email and user ID. |
1454
|
|
|
$limit_per_user = $coupon->get_usage_limit_per_user(); |
1455
|
|
|
|
1456
|
|
|
if ( 0 < $limit_per_user ) { |
1457
|
|
|
$used_by = $coupon->get_used_by(); |
1458
|
|
|
$usage_count = 0; |
1459
|
|
|
$user_id_matches = array( get_current_user_id() ); |
1460
|
|
|
|
1461
|
|
|
// Check usage against emails. |
1462
|
|
|
foreach ( $check_emails as $check_email ) { |
1463
|
|
|
$usage_count += count( array_keys( $used_by, $check_email, true ) ); |
1464
|
|
|
$user = get_user_by( 'email', $check_email ); |
1465
|
|
|
$user_id_matches[] = $user ? $user->ID : 0; |
1466
|
|
|
} |
1467
|
|
|
|
1468
|
|
|
// Check against billing emails of existing users. |
1469
|
|
|
$users_query = new WP_User_Query( |
1470
|
|
|
array( |
1471
|
|
|
'fields' => 'ID', |
1472
|
|
|
'meta_query' => array( |
|
|
|
|
1473
|
|
|
array( |
1474
|
|
|
'key' => '_billing_email', |
1475
|
|
|
'value' => $check_emails, |
1476
|
|
|
'compare' => 'IN', |
1477
|
|
|
), |
1478
|
|
|
), |
1479
|
|
|
) |
1480
|
|
|
); // WPCS: slow query ok. |
1481
|
|
|
|
1482
|
|
|
$user_id_matches = array_unique( array_filter( array_merge( $user_id_matches, $users_query->get_results() ) ) ); |
1483
|
|
|
|
1484
|
|
|
foreach ( $user_id_matches as $user_id ) { |
1485
|
|
|
$usage_count += count( array_keys( $used_by, (string) $user_id, true ) ); |
1486
|
|
|
} |
1487
|
|
|
|
1488
|
|
|
if ( $usage_count >= $coupon->get_usage_limit_per_user() ) { |
1489
|
|
|
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_USAGE_LIMIT_REACHED ); |
1490
|
|
|
$this->remove_coupon( $code ); |
1491
|
|
|
} |
1492
|
|
|
} |
1493
|
|
|
} |
1494
|
|
|
} |
1495
|
|
|
} |
1496
|
|
|
|
1497
|
|
|
/** |
1498
|
|
|
* Checks if the given email address(es) matches the ones specified on the coupon. |
1499
|
1 |
|
* |
1500
|
|
|
* @param array $check_emails Array of customer email addresses. |
1501
|
1 |
|
* @param array $restrictions Array of allowed email addresses. |
1502
|
|
|
* @return bool |
1503
|
1 |
|
*/ |
1504
|
1 |
|
public function is_coupon_emails_allowed( $check_emails, $restrictions ) { |
1505
|
|
|
|
1506
|
|
|
foreach ( $check_emails as $check_email ) { |
1507
|
|
|
// With a direct match we return true. |
1508
|
1 |
|
if ( in_array( $check_email, $restrictions, true ) ) { |
1509
|
|
|
return true; |
1510
|
1 |
|
} |
1511
|
1 |
|
|
1512
|
1 |
|
// Go through the allowed emails and return true if the email matches a wildcard. |
1513
|
1 |
|
foreach ( $restrictions as $restriction ) { |
1514
|
|
|
// Convert to PHP-regex syntax. |
1515
|
|
|
$regex = '/^' . str_replace( '*', '(.+)?', $restriction ) . '$/'; |
1516
|
|
|
preg_match( $regex, $check_email, $match ); |
1517
|
|
|
if ( ! empty( $match ) ) { |
1518
|
|
|
return true; |
1519
|
1 |
|
} |
1520
|
|
|
} |
1521
|
|
|
} |
1522
|
|
|
|
1523
|
|
|
// No matches, this one isn't allowed. |
1524
|
|
|
return false; |
1525
|
|
|
} |
1526
|
|
|
|
1527
|
|
|
|
1528
|
|
|
/** |
1529
|
23 |
|
* Returns whether or not a discount has been applied. |
1530
|
23 |
|
* |
1531
|
|
|
* @param string $coupon_code Coupon code to check. |
1532
|
|
|
* @return bool |
1533
|
|
|
*/ |
1534
|
|
|
public function has_discount( $coupon_code = '' ) { |
1535
|
|
|
return $coupon_code ? in_array( wc_format_coupon_code( $coupon_code ), $this->applied_coupons, true ) : count( $this->applied_coupons ) > 0; |
1536
|
|
|
} |
1537
|
|
|
|
1538
|
|
|
/** |
1539
|
23 |
|
* Applies a coupon code passed to the method. |
1540
|
|
|
* |
1541
|
23 |
|
* @param string $coupon_code - The code to apply. |
1542
|
|
|
* @return bool True if the coupon is applied, false if it does not exist or cannot be applied. |
1543
|
|
|
*/ |
1544
|
|
|
public function apply_coupon( $coupon_code ) { |
1545
|
|
|
// Coupons are globally disabled. |
1546
|
23 |
|
if ( ! wc_coupons_enabled() ) { |
1547
|
|
|
return false; |
1548
|
|
|
} |
1549
|
23 |
|
|
1550
|
|
|
// Sanitize coupon code. |
1551
|
|
|
$coupon_code = wc_format_coupon_code( $coupon_code ); |
1552
|
23 |
|
|
1553
|
1 |
|
// Get the coupon. |
1554
|
1 |
|
$the_coupon = new WC_Coupon( $coupon_code ); |
1555
|
1 |
|
|
1556
|
|
|
// Prevent adding coupons by post ID. |
1557
|
|
|
if ( $the_coupon->get_code() !== $coupon_code ) { |
1558
|
|
|
$the_coupon->set_code( $coupon_code ); |
1559
|
23 |
|
$the_coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_NOT_EXIST ); |
1560
|
|
|
return false; |
1561
|
|
|
} |
1562
|
|
|
|
1563
|
|
|
// Check it can be used with cart. |
1564
|
|
|
if ( ! $the_coupon->is_valid() ) { |
|
|
|
|
1565
|
23 |
|
wc_add_notice( $the_coupon->get_error_message(), 'error' ); |
1566
|
1 |
|
return false; |
1567
|
1 |
|
} |
1568
|
|
|
|
1569
|
|
|
// Check if applied. |
1570
|
|
|
if ( $this->has_discount( $coupon_code ) ) { |
1571
|
23 |
|
$the_coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_ALREADY_APPLIED ); |
1572
|
3 |
|
return false; |
1573
|
|
|
} |
1574
|
3 |
|
|
1575
|
2 |
|
// If its individual use then remove other coupons. |
1576
|
2 |
|
if ( $the_coupon->get_individual_use() ) { |
1577
|
2 |
|
$coupons_to_keep = apply_filters( 'woocommerce_apply_individual_use_coupon', array(), $the_coupon, $this->applied_coupons ); |
1578
|
|
|
|
1579
|
2 |
|
foreach ( $this->applied_coupons as $applied_coupon ) { |
1580
|
|
|
$keep_key = array_search( $applied_coupon, $coupons_to_keep, true ); |
1581
|
|
|
if ( false === $keep_key ) { |
1582
|
|
|
$this->remove_coupon( $applied_coupon ); |
1583
|
3 |
|
} else { |
1584
|
|
|
unset( $coupons_to_keep[ $keep_key ] ); |
1585
|
|
|
} |
1586
|
|
|
} |
1587
|
|
|
|
1588
|
|
|
if ( ! empty( $coupons_to_keep ) ) { |
1589
|
23 |
|
$this->applied_coupons += $coupons_to_keep; |
1590
|
1 |
|
} |
1591
|
1 |
|
} |
1592
|
|
|
|
1593
|
1 |
|
// Check to see if an individual use coupon is set. |
1594
|
|
|
if ( $this->applied_coupons ) { |
|
|
|
|
1595
|
|
|
foreach ( $this->applied_coupons as $code ) { |
1596
|
1 |
|
$coupon = new WC_Coupon( $code ); |
1597
|
|
|
|
1598
|
1 |
|
if ( $coupon->get_individual_use() && false === apply_filters( 'woocommerce_apply_with_individual_use_coupon', false, $the_coupon, $coupon, $this->applied_coupons ) ) { |
1599
|
|
|
|
1600
|
|
|
// Reject new coupon. |
1601
|
|
|
$coupon->add_coupon_message( WC_Coupon::E_WC_COUPON_ALREADY_APPLIED_INDIV_USE_ONLY ); |
1602
|
|
|
|
1603
|
23 |
|
return false; |
1604
|
|
|
} |
1605
|
|
|
} |
1606
|
23 |
|
} |
1607
|
|
|
|
1608
|
|
|
$this->applied_coupons[] = $coupon_code; |
1609
|
|
|
|
1610
|
|
|
// Choose free shipping. |
1611
|
|
|
if ( $the_coupon->get_free_shipping() ) { |
1612
|
|
|
$packages = WC()->shipping()->get_packages(); |
1613
|
|
|
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); |
1614
|
|
|
|
1615
|
|
|
foreach ( $packages as $i => $package ) { |
1616
|
|
|
$chosen_shipping_methods[ $i ] = 'free_shipping'; |
1617
|
23 |
|
} |
1618
|
|
|
|
1619
|
23 |
|
WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods ); |
1620
|
|
|
} |
1621
|
23 |
|
|
1622
|
|
|
$the_coupon->add_coupon_message( WC_Coupon::WC_COUPON_SUCCESS ); |
1623
|
|
|
|
1624
|
|
|
do_action( 'woocommerce_applied_coupon', $coupon_code ); |
1625
|
|
|
|
1626
|
|
|
return true; |
1627
|
|
|
} |
1628
|
|
|
|
1629
|
|
|
/** |
1630
|
81 |
|
* Get array of applied coupon objects and codes. |
1631
|
81 |
|
* |
1632
|
|
|
* @param null $deprecated No longer used. |
1633
|
81 |
|
* @return array of applied coupons |
1634
|
|
|
*/ |
1635
|
|
|
public function get_coupons( $deprecated = null ) { |
1636
|
|
|
$coupons = array(); |
1637
|
81 |
|
|
1638
|
20 |
|
if ( 'order' === $deprecated ) { |
1639
|
20 |
|
return $coupons; |
1640
|
|
|
} |
1641
|
|
|
|
1642
|
81 |
|
foreach ( $this->get_applied_coupons() as $code ) { |
1643
|
|
|
$coupon = new WC_Coupon( $code ); |
1644
|
|
|
$coupons[ $code ] = $coupon; |
1645
|
|
|
} |
1646
|
|
|
|
1647
|
|
|
return $coupons; |
1648
|
|
|
} |
1649
|
|
|
|
1650
|
|
|
/** |
1651
|
|
|
* Get the discount amount for a used coupon. |
1652
|
|
|
* |
1653
|
|
|
* @param string $code coupon code. |
1654
|
|
|
* @param bool $ex_tax inc or ex tax. |
1655
|
|
|
* @return float discount amount |
1656
|
|
|
*/ |
1657
|
|
|
public function get_coupon_discount_amount( $code, $ex_tax = true ) { |
1658
|
|
|
$totals = $this->get_coupon_discount_totals(); |
1659
|
|
|
$discount_amount = isset( $totals[ $code ] ) ? $totals[ $code ] : 0; |
1660
|
|
|
|
1661
|
|
|
if ( ! $ex_tax ) { |
1662
|
|
|
$discount_amount += $this->get_coupon_discount_tax_amount( $code ); |
1663
|
|
|
} |
1664
|
|
|
|
1665
|
|
|
return wc_cart_round_discount( $discount_amount, wc_get_price_decimals() ); |
1666
|
|
|
} |
1667
|
|
|
|
1668
|
|
|
/** |
1669
|
|
|
* Get the discount tax amount for a used coupon (for tax inclusive prices). |
1670
|
|
|
* |
1671
|
|
|
* @param string $code coupon code. |
1672
|
|
|
* @return float discount amount |
1673
|
|
|
*/ |
1674
|
|
|
public function get_coupon_discount_tax_amount( $code ) { |
1675
|
|
|
$totals = $this->get_coupon_discount_tax_totals(); |
1676
|
|
|
return wc_cart_round_discount( isset( $totals[ $code ] ) ? $totals[ $code ] : 0, wc_get_price_decimals() ); |
1677
|
|
|
} |
1678
|
|
|
|
1679
|
49 |
|
/** |
1680
|
49 |
|
* Remove coupons from the cart of a defined type. Type 1 is before tax, type 2 is after tax. |
1681
|
49 |
|
* |
1682
|
49 |
|
* @param null $deprecated No longer used. |
1683
|
49 |
|
*/ |
1684
|
|
|
public function remove_coupons( $deprecated = null ) { |
1685
|
|
|
$this->set_coupon_discount_totals( array() ); |
1686
|
|
|
$this->set_coupon_discount_tax_totals( array() ); |
1687
|
|
|
$this->set_applied_coupons( array() ); |
1688
|
|
|
$this->session->set_session(); |
1689
|
|
|
} |
1690
|
|
|
|
1691
|
|
|
/** |
1692
|
2 |
|
* Remove a single coupon by code. |
1693
|
2 |
|
* |
1694
|
2 |
|
* @param string $coupon_code Code of the coupon to remove. |
1695
|
|
|
* @return bool |
1696
|
2 |
|
*/ |
1697
|
2 |
|
public function remove_coupon( $coupon_code ) { |
1698
|
|
|
$coupon_code = wc_format_coupon_code( $coupon_code ); |
1699
|
|
|
$position = array_search( $coupon_code, $this->get_applied_coupons(), true ); |
1700
|
2 |
|
|
1701
|
|
|
if ( false !== $position ) { |
1702
|
2 |
|
unset( $this->applied_coupons[ $position ] ); |
1703
|
|
|
} |
1704
|
2 |
|
|
1705
|
|
|
WC()->session->set( 'refresh_totals', true ); |
1706
|
|
|
|
1707
|
|
|
do_action( 'woocommerce_removed_coupon', $coupon_code ); |
1708
|
|
|
|
1709
|
|
|
return true; |
1710
|
|
|
} |
1711
|
|
|
|
1712
|
77 |
|
/** |
1713
|
77 |
|
* Trigger an action so 3rd parties can add custom fees. |
1714
|
|
|
* |
1715
|
|
|
* @since 2.0.0 |
1716
|
|
|
*/ |
1717
|
|
|
public function calculate_fees() { |
1718
|
|
|
do_action( 'woocommerce_cart_calculate_fees', $this ); |
1719
|
|
|
} |
1720
|
|
|
|
1721
|
|
|
/** |
1722
|
78 |
|
* Return reference to fees API. |
1723
|
78 |
|
* |
1724
|
|
|
* @since 3.2.0 |
1725
|
|
|
* @return WC_Cart_Fees |
1726
|
|
|
*/ |
1727
|
|
|
public function fees_api() { |
1728
|
|
|
return $this->fees_api; |
1729
|
|
|
} |
1730
|
|
|
|
1731
|
|
|
/** |
1732
|
|
|
* Add additional fee to the cart. |
1733
|
|
|
* |
1734
|
|
|
* This method should be called on a callback attached to the |
1735
|
|
|
* woocommerce_cart_calculate_fees action during cart/checkout. Fees do not |
1736
|
|
|
* persist. |
1737
|
|
|
* |
1738
|
|
|
* @uses WC_Cart_Fees::add_fee |
1739
|
11 |
|
* @param string $name Unique name for the fee. Multiple fees of the same name cannot be added. |
1740
|
11 |
|
* @param float $amount Fee amount (do not enter negative amounts). |
1741
|
|
|
* @param bool $taxable Is the fee taxable? (default: false). |
1742
|
11 |
|
* @param string $tax_class The tax class for the fee if taxable. A blank string is standard tax class. (default: ''). |
1743
|
11 |
|
*/ |
1744
|
11 |
|
public function add_fee( $name, $amount, $taxable = false, $tax_class = '' ) { |
1745
|
11 |
|
$this->fees_api()->add_fee( |
1746
|
|
|
array( |
1747
|
|
|
'name' => $name, |
1748
|
|
|
'amount' => (float) $amount, |
1749
|
|
|
'taxable' => $taxable, |
1750
|
|
|
'tax_class' => $tax_class, |
1751
|
|
|
) |
1752
|
|
|
); |
1753
|
|
|
} |
1754
|
|
|
|
1755
|
|
|
/** |
1756
|
77 |
|
* Return all added fees from the Fees API. |
1757
|
77 |
|
* |
1758
|
|
|
* @uses WC_Cart_Fees::get_fees |
1759
|
77 |
|
* @return array |
1760
|
|
|
*/ |
1761
|
|
|
public function get_fees() { |
1762
|
77 |
|
$fees = $this->fees_api()->get_fees(); |
1763
|
|
|
|
1764
|
|
|
if ( property_exists( $this, 'fees' ) ) { |
1765
|
|
|
$fees = $fees + (array) $this->fees; |
1766
|
|
|
} |
1767
|
|
|
return $fees; |
1768
|
|
|
} |
1769
|
|
|
|
1770
|
1 |
|
/** |
1771
|
1 |
|
* Gets the total excluding taxes. |
1772
|
|
|
* |
1773
|
|
|
* @return string formatted price |
1774
|
|
|
*/ |
1775
|
|
|
public function get_total_ex_tax() { |
1776
|
|
|
return apply_filters( 'woocommerce_cart_total_ex_tax', wc_price( max( 0, $this->get_total( 'edit' ) - $this->get_total_tax() ) ) ); |
1777
|
|
|
} |
1778
|
|
|
|
1779
|
|
|
/** |
1780
|
|
|
* Gets the cart contents total (after calculation). |
1781
|
|
|
* |
1782
|
|
|
* @return string formatted price |
1783
|
|
|
*/ |
1784
|
|
|
public function get_cart_total() { |
1785
|
|
|
return apply_filters( 'woocommerce_cart_contents_total', wc_price( wc_prices_include_tax() ? $this->get_cart_contents_total() + $this->get_cart_contents_tax() : $this->get_cart_contents_total() ) ); |
1786
|
|
|
} |
1787
|
|
|
|
1788
|
|
|
/** |
1789
|
1 |
|
* Gets the sub total (after calculation). |
1790
|
|
|
* |
1791
|
|
|
* @param bool $compound whether to include compound taxes. |
1792
|
|
|
* @return string formatted price |
1793
|
1 |
|
*/ |
1794
|
|
|
public function get_cart_subtotal( $compound = false ) { |
1795
|
|
|
/** |
1796
|
1 |
|
* If the cart has compound tax, we want to show the subtotal as cart + shipping + non-compound taxes (after discount). |
1797
|
|
|
*/ |
1798
|
|
|
if ( $compound ) { |
1799
|
|
|
$cart_subtotal = wc_price( $this->get_cart_contents_total() + $this->get_shipping_total() + $this->get_taxes_total( false, false ) ); |
1800
|
|
|
|
1801
|
|
|
} elseif ( $this->display_prices_including_tax() ) { |
1802
|
|
|
$cart_subtotal = wc_price( $this->get_subtotal() + $this->get_subtotal_tax() ); |
1803
|
1 |
|
|
1804
|
|
|
if ( $this->get_subtotal_tax() > 0 && ! wc_prices_include_tax() ) { |
1805
|
1 |
|
$cart_subtotal .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>'; |
1806
|
|
|
} |
1807
|
|
|
} else { |
1808
|
|
|
$cart_subtotal = wc_price( $this->get_subtotal() ); |
1809
|
|
|
|
1810
|
1 |
|
if ( $this->get_subtotal_tax() > 0 && wc_prices_include_tax() ) { |
1811
|
|
|
$cart_subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; |
1812
|
|
|
} |
1813
|
|
|
} |
1814
|
|
|
|
1815
|
|
|
return apply_filters( 'woocommerce_cart_subtotal', $cart_subtotal, $compound, $this ); |
1816
|
|
|
} |
1817
|
|
|
|
1818
|
|
|
/** |
1819
|
|
|
* Get the product row price per item. |
1820
|
|
|
* |
1821
|
|
|
* @param WC_Product $product Product object. |
1822
|
|
|
* @return string formatted price |
1823
|
|
|
*/ |
1824
|
|
|
public function get_product_price( $product ) { |
1825
|
|
|
if ( $this->display_prices_including_tax() ) { |
1826
|
|
|
$product_price = wc_get_price_including_tax( $product ); |
1827
|
|
|
} else { |
1828
|
|
|
$product_price = wc_get_price_excluding_tax( $product ); |
1829
|
|
|
} |
1830
|
|
|
return apply_filters( 'woocommerce_cart_product_price', wc_price( $product_price ), $product ); |
1831
|
|
|
} |
1832
|
|
|
|
1833
|
|
|
/** |
1834
|
|
|
* Get the product row subtotal. |
1835
|
|
|
* |
1836
|
|
|
* Gets the tax etc to avoid rounding issues. |
1837
|
|
|
* |
1838
|
|
|
* When on the checkout (review order), this will get the subtotal based on the customer's tax rate rather than the base rate. |
1839
|
|
|
* |
1840
|
|
|
* @param WC_Product $product Product object. |
1841
|
|
|
* @param int $quantity Quantity being purchased. |
1842
|
|
|
* @return string formatted price |
1843
|
|
|
*/ |
1844
|
|
|
public function get_product_subtotal( $product, $quantity ) { |
1845
|
|
|
$price = $product->get_price(); |
1846
|
|
|
|
1847
|
|
|
if ( $product->is_taxable() ) { |
1848
|
|
|
|
1849
|
|
|
if ( $this->display_prices_including_tax() ) { |
1850
|
|
|
$row_price = wc_get_price_including_tax( $product, array( 'qty' => $quantity ) ); |
1851
|
|
|
$product_subtotal = wc_price( $row_price ); |
1852
|
|
|
|
1853
|
|
|
if ( ! wc_prices_include_tax() && $this->get_subtotal_tax() > 0 ) { |
1854
|
|
|
$product_subtotal .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>'; |
1855
|
|
|
} |
1856
|
|
|
} else { |
1857
|
|
|
$row_price = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) ); |
1858
|
|
|
$product_subtotal = wc_price( $row_price ); |
1859
|
|
|
|
1860
|
|
|
if ( wc_prices_include_tax() && $this->get_subtotal_tax() > 0 ) { |
1861
|
|
|
$product_subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; |
1862
|
|
|
} |
1863
|
|
|
} |
1864
|
|
|
} else { |
1865
|
|
|
$row_price = $price * $quantity; |
1866
|
|
|
$product_subtotal = wc_price( $row_price ); |
1867
|
|
|
} |
1868
|
|
|
|
1869
|
|
|
return apply_filters( 'woocommerce_cart_product_subtotal', $product_subtotal, $product, $quantity, $this ); |
1870
|
|
|
} |
1871
|
|
|
|
1872
|
|
|
/** |
1873
|
|
|
* Gets the cart tax (after calculation). |
1874
|
|
|
* |
1875
|
|
|
* @return string formatted price |
1876
|
|
|
*/ |
1877
|
|
|
public function get_cart_tax() { |
1878
|
|
|
$cart_total_tax = wc_round_tax_total( $this->get_cart_contents_tax() + $this->get_shipping_tax() + $this->get_fee_tax() ); |
1879
|
|
|
|
1880
|
|
|
return apply_filters( 'woocommerce_get_cart_tax', $cart_total_tax ? wc_price( $cart_total_tax ) : '' ); |
1881
|
|
|
} |
1882
|
|
|
|
1883
|
|
|
/** |
1884
|
|
|
* Get a tax amount. |
1885
|
|
|
* |
1886
|
|
|
* @param string $tax_rate_id ID of the tax rate to get taxes for. |
1887
|
|
|
* @return float amount |
1888
|
|
|
*/ |
1889
|
|
|
public function get_tax_amount( $tax_rate_id ) { |
1890
|
|
|
$taxes = wc_array_merge_recursive_numeric( $this->get_cart_contents_taxes(), $this->get_fee_taxes() ); |
1891
|
|
|
return isset( $taxes[ $tax_rate_id ] ) ? $taxes[ $tax_rate_id ] : 0; |
1892
|
|
|
} |
1893
|
|
|
|
1894
|
|
|
/** |
1895
|
|
|
* Get a tax amount. |
1896
|
|
|
* |
1897
|
|
|
* @param string $tax_rate_id ID of the tax rate to get taxes for. |
1898
|
|
|
* @return float amount |
1899
|
|
|
*/ |
1900
|
|
|
public function get_shipping_tax_amount( $tax_rate_id ) { |
1901
|
|
|
$taxes = $this->get_shipping_taxes(); |
1902
|
|
|
return isset( $taxes[ $tax_rate_id ] ) ? $taxes[ $tax_rate_id ] : 0; |
1903
|
|
|
} |
1904
|
|
|
|
1905
|
|
|
/** |
1906
|
|
|
* Get tax row amounts with or without compound taxes includes. |
1907
|
|
|
* |
1908
|
|
|
* @param bool $compound True if getting compound taxes. |
1909
|
|
|
* @param bool $display True if getting total to display. |
1910
|
|
|
* @return float price |
1911
|
|
|
*/ |
1912
|
|
|
public function get_taxes_total( $compound = true, $display = true ) { |
1913
|
|
|
$total = 0; |
1914
|
|
|
$taxes = $this->get_taxes(); |
1915
|
|
|
foreach ( $taxes as $key => $tax ) { |
1916
|
|
|
if ( ! $compound && WC_Tax::is_compound( $key ) ) { |
1917
|
|
|
continue; |
1918
|
|
|
} |
1919
|
|
|
$total += $tax; |
1920
|
|
|
} |
1921
|
|
|
if ( $display ) { |
1922
|
|
|
$total = wc_round_tax_total( $total ); |
1923
|
|
|
} |
1924
|
|
|
return apply_filters( 'woocommerce_cart_taxes_total', $total, $compound, $display, $this ); |
1925
|
|
|
} |
1926
|
|
|
|
1927
|
|
|
/** |
1928
|
|
|
* Gets the total discount amount. |
1929
|
|
|
* |
1930
|
|
|
* @return mixed formatted price or false if there are none |
1931
|
|
|
*/ |
1932
|
|
|
public function get_total_discount() { |
1933
|
|
|
return apply_filters( 'woocommerce_cart_total_discount', $this->get_discount_total() ? wc_price( $this->get_discount_total() ) : false, $this ); |
1934
|
84 |
|
} |
1935
|
84 |
|
|
1936
|
84 |
|
/** |
1937
|
84 |
|
* Reset cart totals to the defaults. Useful before running calculations. |
1938
|
|
|
*/ |
1939
|
|
|
private function reset_totals() { |
1940
|
|
|
$this->totals = $this->default_totals; |
1941
|
|
|
$this->fees_api->remove_all_fees(); |
1942
|
|
|
do_action( 'woocommerce_cart_reset', $this, false ); |
1943
|
|
|
} |
1944
|
|
|
|
1945
|
1 |
|
/** |
1946
|
1 |
|
* Returns 'incl' if tax should be included in cart, otherwise returns 'excl'. |
1947
|
|
|
* |
1948
|
|
|
* @return string |
1949
|
|
|
*/ |
1950
|
1 |
|
private function is_tax_displayed() { |
1951
|
|
|
if ( $this->get_customer() && $this->get_customer()->get_is_vat_exempt() ) { |
1952
|
|
|
return 'excl'; |
1953
|
|
|
} |
1954
|
|
|
|
1955
|
|
|
return get_option( 'woocommerce_tax_display_cart' ); |
1956
|
|
|
} |
1957
|
|
|
|
1958
|
|
|
/** |
1959
|
|
|
* Returns the hash based on cart contents. |
1960
|
|
|
* |
1961
|
|
|
* @since 3.6.0 |
1962
|
|
|
* @return string hash for cart content |
1963
|
|
|
*/ |
1964
|
|
|
public function get_cart_hash() { |
1965
|
|
|
$cart_session = $this->session->get_cart_for_session(); |
1966
|
|
|
$hash = $cart_session ? md5( wp_json_encode( $cart_session ) . $this->get_total( 'edit' ) ) : ''; |
1967
|
|
|
$hash = apply_filters_deprecated( 'woocommerce_add_to_cart_hash', array( $hash, $cart_session ), '3.6.0', 'woocommerce_cart_hash' ); |
1968
|
|
|
|
1969
|
|
|
return apply_filters( 'woocommerce_cart_hash', $hash, $cart_session ); |
1970
|
|
|
} |
1971
|
|
|
} |
1972
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.