1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Abstract Order |
4
|
|
|
* |
5
|
|
|
* Handles generic order data and database interaction which is extended by both |
6
|
|
|
* WC_Order (regular orders) and WC_Order_Refund (refunds are negative orders). |
7
|
|
|
* |
8
|
|
|
* @class WC_Abstract_Order |
9
|
|
|
* @version 3.0.0 |
10
|
|
|
* @package WooCommerce/Classes |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
defined( 'ABSPATH' ) || exit; |
14
|
|
|
|
15
|
|
|
require_once WC_ABSPATH . 'includes/legacy/abstract-wc-legacy-order.php'; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* WC_Abstract_Order class. |
19
|
|
|
*/ |
20
|
|
|
abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Order Data array. This is the core order data exposed in APIs since 3.0.0. |
24
|
|
|
* |
25
|
|
|
* Notes: cart_tax = cart_tax is the new name for the legacy 'order_tax' |
26
|
|
|
* which is the tax for items only, not shipping. |
27
|
|
|
* |
28
|
|
|
* @since 3.0.0 |
29
|
|
|
* @var array |
30
|
|
|
*/ |
31
|
|
|
protected $data = array( |
32
|
|
|
'parent_id' => 0, |
33
|
|
|
'status' => '', |
34
|
|
|
'currency' => '', |
35
|
|
|
'version' => '', |
36
|
|
|
'prices_include_tax' => false, |
37
|
|
|
'date_created' => null, |
38
|
|
|
'date_modified' => null, |
39
|
|
|
'discount_total' => 0, |
40
|
|
|
'discount_tax' => 0, |
41
|
|
|
'shipping_total' => 0, |
42
|
|
|
'shipping_tax' => 0, |
43
|
|
|
'cart_tax' => 0, |
44
|
|
|
'total' => 0, |
45
|
|
|
'total_tax' => 0, |
46
|
|
|
); |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Order items will be stored here, sometimes before they persist in the DB. |
50
|
|
|
* |
51
|
|
|
* @since 3.0.0 |
52
|
|
|
* @var array |
53
|
|
|
*/ |
54
|
|
|
protected $items = array(); |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Order items that need deleting are stored here. |
58
|
|
|
* |
59
|
|
|
* @since 3.0.0 |
60
|
|
|
* @var array |
61
|
|
|
*/ |
62
|
|
|
protected $items_to_delete = array(); |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Stores meta in cache for future reads. |
66
|
|
|
* |
67
|
|
|
* A group must be set to to enable caching. |
68
|
|
|
* |
69
|
|
|
* @var string |
70
|
|
|
*/ |
71
|
|
|
protected $cache_group = 'orders'; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Which data store to load. |
75
|
|
|
* |
76
|
|
|
* @var string |
77
|
|
|
*/ |
78
|
|
|
protected $data_store_name = 'order'; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* This is the name of this object type. |
82
|
|
|
* |
83
|
|
|
* @var string |
84
|
|
|
*/ |
85
|
|
|
protected $object_type = 'order'; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Get the order if ID is passed, otherwise the order is new and empty. |
89
|
|
|
* This class should NOT be instantiated, but the get_order function or new WC_Order_Factory. |
90
|
|
|
* should be used. It is possible, but the aforementioned are preferred and are the only. |
91
|
|
|
* methods that will be maintained going forward. |
92
|
|
|
* |
93
|
|
|
* @param int|object|WC_Order $order Order to read. |
94
|
|
|
*/ |
95
|
244 |
|
public function __construct( $order = 0 ) { |
96
|
244 |
|
parent::__construct( $order ); |
97
|
|
|
|
98
|
244 |
|
if ( is_numeric( $order ) && $order > 0 ) { |
99
|
88 |
|
$this->set_id( $order ); |
100
|
244 |
|
} elseif ( $order instanceof self ) { |
101
|
|
|
$this->set_id( $order->get_id() ); |
102
|
244 |
|
} elseif ( ! empty( $order->ID ) ) { |
103
|
|
|
$this->set_id( $order->ID ); |
104
|
|
|
} else { |
105
|
244 |
|
$this->set_object_read( true ); |
106
|
|
|
} |
107
|
|
|
|
108
|
244 |
|
$this->data_store = WC_Data_Store::load( $this->data_store_name ); |
109
|
|
|
|
110
|
244 |
|
if ( $this->get_id() > 0 ) { |
111
|
88 |
|
$this->data_store->read( $this ); |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Get internal type. |
117
|
|
|
* |
118
|
|
|
* @return string |
119
|
|
|
*/ |
120
|
162 |
|
public function get_type() { |
121
|
162 |
|
return 'shop_order'; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Get all class data in array format. |
126
|
|
|
* |
127
|
|
|
* @since 3.0.0 |
128
|
|
|
* @return array |
129
|
|
|
*/ |
130
|
1 |
View Code Duplication |
public function get_data() { |
|
|
|
|
131
|
1 |
|
return array_merge( |
132
|
|
|
array( |
133
|
1 |
|
'id' => $this->get_id(), |
134
|
|
|
), |
135
|
1 |
|
$this->data, |
136
|
|
|
array( |
137
|
1 |
|
'meta_data' => $this->get_meta_data(), |
138
|
1 |
|
'line_items' => $this->get_items( 'line_item' ), |
139
|
1 |
|
'tax_lines' => $this->get_items( 'tax' ), |
140
|
1 |
|
'shipping_lines' => $this->get_items( 'shipping' ), |
141
|
1 |
|
'fee_lines' => $this->get_items( 'fee' ), |
142
|
1 |
|
'coupon_lines' => $this->get_items( 'coupon' ), |
143
|
|
|
) |
144
|
|
|
); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/* |
148
|
|
|
|-------------------------------------------------------------------------- |
149
|
|
|
| CRUD methods |
150
|
|
|
|-------------------------------------------------------------------------- |
151
|
|
|
| |
152
|
|
|
| Methods which create, read, update and delete orders from the database. |
153
|
|
|
| Written in abstract fashion so that the way orders are stored can be |
154
|
|
|
| changed more easily in the future. |
155
|
|
|
| |
156
|
|
|
| A save method is included for convenience (chooses update or create based |
157
|
|
|
| on if the order exists yet). |
158
|
|
|
| |
159
|
|
|
*/ |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Save data to the database. |
163
|
|
|
* |
164
|
|
|
* @since 3.0.0 |
165
|
|
|
* @return int order ID |
166
|
|
|
*/ |
167
|
9 |
View Code Duplication |
public function save() { |
|
|
|
|
168
|
9 |
|
if ( $this->data_store ) { |
169
|
|
|
// Trigger action before saving to the DB. Allows you to adjust object props before save. |
170
|
9 |
|
do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store ); |
171
|
|
|
|
172
|
9 |
|
if ( $this->get_id() ) { |
173
|
8 |
|
$this->data_store->update( $this ); |
174
|
|
|
} else { |
175
|
9 |
|
$this->data_store->create( $this ); |
176
|
|
|
} |
177
|
|
|
} |
178
|
9 |
|
$this->save_items(); |
179
|
9 |
|
return $this->get_id(); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Save all order items which are part of this order. |
184
|
|
|
*/ |
185
|
161 |
|
protected function save_items() { |
186
|
161 |
|
$items_changed = false; |
187
|
|
|
|
188
|
161 |
|
foreach ( $this->items_to_delete as $item ) { |
189
|
12 |
|
$item->delete(); |
190
|
12 |
|
$items_changed = true; |
191
|
|
|
} |
192
|
161 |
|
$this->items_to_delete = array(); |
193
|
|
|
|
194
|
|
|
// Add/save items. |
195
|
161 |
|
foreach ( $this->items as $item_group => $items ) { |
196
|
129 |
|
if ( is_array( $items ) ) { |
197
|
129 |
|
$items = array_filter( $items ); |
198
|
129 |
|
foreach ( $items as $item_key => $item ) { |
199
|
118 |
|
$item->set_order_id( $this->get_id() ); |
200
|
|
|
|
201
|
118 |
|
$item_id = $item->save(); |
202
|
|
|
|
203
|
|
|
// If ID changed (new item saved to DB)... |
204
|
118 |
|
if ( $item_id !== $item_key ) { |
205
|
114 |
|
$this->items[ $item_group ][ $item_id ] = $item; |
206
|
|
|
|
207
|
114 |
|
unset( $this->items[ $item_group ][ $item_key ] ); |
208
|
|
|
|
209
|
114 |
|
$items_changed = true; |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|
215
|
161 |
|
if ( $items_changed ) { |
216
|
116 |
|
delete_transient( 'wc_order_' . $this->get_id() . '_needs_processing' ); |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/* |
221
|
|
|
|-------------------------------------------------------------------------- |
222
|
|
|
| Getters |
223
|
|
|
|-------------------------------------------------------------------------- |
224
|
|
|
*/ |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Get parent order ID. |
228
|
|
|
* |
229
|
|
|
* @since 3.0.0 |
230
|
|
|
* @param string $context View or edit context. |
231
|
|
|
* @return integer |
232
|
|
|
*/ |
233
|
161 |
|
public function get_parent_id( $context = 'view' ) { |
234
|
161 |
|
return $this->get_prop( 'parent_id', $context ); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* Gets order currency. |
239
|
|
|
* |
240
|
|
|
* @param string $context View or edit context. |
241
|
|
|
* @return string |
242
|
|
|
*/ |
243
|
163 |
|
public function get_currency( $context = 'view' ) { |
244
|
163 |
|
return $this->get_prop( 'currency', $context ); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Get order_version. |
249
|
|
|
* |
250
|
|
|
* @param string $context View or edit context. |
251
|
|
|
* @return string |
252
|
|
|
*/ |
253
|
162 |
|
public function get_version( $context = 'view' ) { |
254
|
162 |
|
return $this->get_prop( 'version', $context ); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Get prices_include_tax. |
259
|
|
|
* |
260
|
|
|
* @param string $context View or edit context. |
261
|
|
|
* @return bool |
262
|
|
|
*/ |
263
|
162 |
|
public function get_prices_include_tax( $context = 'view' ) { |
264
|
162 |
|
return $this->get_prop( 'prices_include_tax', $context ); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Get date_created. |
269
|
|
|
* |
270
|
|
|
* @param string $context View or edit context. |
271
|
|
|
* @return WC_DateTime|NULL object if the date is set or null if there is no date. |
272
|
|
|
*/ |
273
|
163 |
|
public function get_date_created( $context = 'view' ) { |
274
|
163 |
|
return $this->get_prop( 'date_created', $context ); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Get date_modified. |
279
|
|
|
* |
280
|
|
|
* @param string $context View or edit context. |
281
|
|
|
* @return WC_DateTime|NULL object if the date is set or null if there is no date. |
282
|
|
|
*/ |
283
|
1 |
|
public function get_date_modified( $context = 'view' ) { |
284
|
1 |
|
return $this->get_prop( 'date_modified', $context ); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Return the order statuses without wc- internal prefix. |
289
|
|
|
* |
290
|
|
|
* @param string $context View or edit context. |
291
|
|
|
* @return string |
292
|
|
|
*/ |
293
|
167 |
|
public function get_status( $context = 'view' ) { |
294
|
167 |
|
$status = $this->get_prop( 'status', $context ); |
295
|
|
|
|
296
|
167 |
|
if ( empty( $status ) && 'view' === $context ) { |
297
|
|
|
// In view context, return the default status if no status has been set. |
298
|
134 |
|
$status = apply_filters( 'woocommerce_default_order_status', 'pending' ); |
299
|
|
|
} |
300
|
167 |
|
return $status; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Get discount_total. |
305
|
|
|
* |
306
|
|
|
* @param string $context View or edit context. |
307
|
|
|
* @return string |
308
|
|
|
*/ |
309
|
163 |
|
public function get_discount_total( $context = 'view' ) { |
310
|
163 |
|
return $this->get_prop( 'discount_total', $context ); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Get discount_tax. |
315
|
|
|
* |
316
|
|
|
* @param string $context View or edit context. |
317
|
|
|
* @return string |
318
|
|
|
*/ |
319
|
163 |
|
public function get_discount_tax( $context = 'view' ) { |
320
|
163 |
|
return $this->get_prop( 'discount_tax', $context ); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* Get shipping_total. |
325
|
|
|
* |
326
|
|
|
* @param string $context View or edit context. |
327
|
|
|
* @return string |
328
|
|
|
*/ |
329
|
162 |
|
public function get_shipping_total( $context = 'view' ) { |
330
|
162 |
|
return $this->get_prop( 'shipping_total', $context ); |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* Get shipping_tax. |
335
|
|
|
* |
336
|
|
|
* @param string $context View or edit context. |
337
|
|
|
* @return string |
338
|
|
|
*/ |
339
|
164 |
|
public function get_shipping_tax( $context = 'view' ) { |
340
|
164 |
|
return $this->get_prop( 'shipping_tax', $context ); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Gets cart tax amount. |
345
|
|
|
* |
346
|
|
|
* @param string $context View or edit context. |
347
|
|
|
* @return float |
348
|
|
|
*/ |
349
|
164 |
|
public function get_cart_tax( $context = 'view' ) { |
350
|
164 |
|
return $this->get_prop( 'cart_tax', $context ); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* Gets order grand total. incl. taxes. Used in gateways. |
355
|
|
|
* |
356
|
|
|
* @param string $context View or edit context. |
357
|
|
|
* @return float |
358
|
|
|
*/ |
359
|
164 |
|
public function get_total( $context = 'view' ) { |
360
|
164 |
|
return $this->get_prop( 'total', $context ); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Get total tax amount. Alias for get_order_tax(). |
365
|
|
|
* |
366
|
|
|
* @param string $context View or edit context. |
367
|
|
|
* @return float |
368
|
|
|
*/ |
369
|
5 |
|
public function get_total_tax( $context = 'view' ) { |
370
|
5 |
|
return $this->get_prop( 'total_tax', $context ); |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/* |
374
|
|
|
|-------------------------------------------------------------------------- |
375
|
|
|
| Non-CRUD Getters |
376
|
|
|
|-------------------------------------------------------------------------- |
377
|
|
|
*/ |
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Gets the total discount amount. |
381
|
|
|
* |
382
|
|
|
* @param bool $ex_tax Show discount excl any tax. |
383
|
|
|
* @return float |
384
|
|
|
*/ |
385
|
2 |
|
public function get_total_discount( $ex_tax = true ) { |
386
|
2 |
|
if ( $ex_tax ) { |
387
|
1 |
|
$total_discount = $this->get_discount_total(); |
388
|
|
|
} else { |
389
|
2 |
|
$total_discount = $this->get_discount_total() + $this->get_discount_tax(); |
390
|
|
|
} |
391
|
2 |
|
return apply_filters( 'woocommerce_order_get_total_discount', round( $total_discount, WC_ROUNDING_PRECISION ), $this ); |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
/** |
395
|
|
|
* Gets order subtotal. |
396
|
|
|
* |
397
|
|
|
* @return float |
398
|
|
|
*/ |
399
|
12 |
|
public function get_subtotal() { |
400
|
12 |
|
$subtotal = 0; |
401
|
|
|
|
402
|
12 |
|
foreach ( $this->get_items() as $item ) { |
403
|
12 |
|
$subtotal += $item->get_subtotal(); |
|
|
|
|
404
|
|
|
} |
405
|
|
|
|
406
|
12 |
|
return apply_filters( 'woocommerce_order_get_subtotal', (float) $subtotal, $this ); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* Get taxes, merged by code, formatted ready for output. |
411
|
|
|
* |
412
|
|
|
* @return array |
413
|
|
|
*/ |
414
|
1 |
|
public function get_tax_totals() { |
415
|
1 |
|
$tax_totals = array(); |
416
|
|
|
|
417
|
1 |
|
foreach ( $this->get_items( 'tax' ) as $key => $tax ) { |
418
|
|
|
$code = $tax->get_rate_code(); |
|
|
|
|
419
|
|
|
|
420
|
|
View Code Duplication |
if ( ! isset( $tax_totals[ $code ] ) ) { |
421
|
|
|
$tax_totals[ $code ] = new stdClass(); |
422
|
|
|
$tax_totals[ $code ]->amount = 0; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
$tax_totals[ $code ]->id = $key; |
426
|
|
|
$tax_totals[ $code ]->rate_id = $tax->get_rate_id(); |
|
|
|
|
427
|
|
|
$tax_totals[ $code ]->is_compound = $tax->is_compound(); |
|
|
|
|
428
|
|
|
$tax_totals[ $code ]->label = $tax->get_label(); |
|
|
|
|
429
|
|
|
$tax_totals[ $code ]->amount += (float) $tax->get_tax_total() + (float) $tax->get_shipping_tax_total(); |
|
|
|
|
430
|
|
|
$tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array( 'currency' => $this->get_currency() ) ); |
431
|
|
|
} |
432
|
|
|
|
433
|
1 |
View Code Duplication |
if ( apply_filters( 'woocommerce_order_hide_zero_taxes', true ) ) { |
434
|
1 |
|
$amounts = array_filter( wp_list_pluck( $tax_totals, 'amount' ) ); |
435
|
1 |
|
$tax_totals = array_intersect_key( $tax_totals, $amounts ); |
436
|
|
|
} |
437
|
|
|
|
438
|
1 |
|
return apply_filters( 'woocommerce_order_get_tax_totals', $tax_totals, $this ); |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
/** |
442
|
|
|
* Get all valid statuses for this order |
443
|
|
|
* |
444
|
|
|
* @since 3.0.0 |
445
|
|
|
* @return array Internal status keys e.g. 'wc-processing' |
446
|
|
|
*/ |
447
|
116 |
|
protected function get_valid_statuses() { |
448
|
116 |
|
return array_keys( wc_get_order_statuses() ); |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
/** |
452
|
|
|
* Get user ID. Used by orders, not other order types like refunds. |
453
|
|
|
* |
454
|
|
|
* @param string $context View or edit context. |
455
|
|
|
* @return int |
456
|
|
|
*/ |
457
|
|
|
public function get_user_id( $context = 'view' ) { |
458
|
|
|
return 0; |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* Get user. Used by orders, not other order types like refunds. |
463
|
|
|
* |
464
|
|
|
* @return WP_User|false |
465
|
|
|
*/ |
466
|
|
|
public function get_user() { |
467
|
|
|
return false; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
/* |
471
|
|
|
|-------------------------------------------------------------------------- |
472
|
|
|
| Setters |
473
|
|
|
|-------------------------------------------------------------------------- |
474
|
|
|
| |
475
|
|
|
| Functions for setting order data. These should not update anything in the |
476
|
|
|
| database itself and should only change what is stored in the class |
477
|
|
|
| object. However, for backwards compatibility pre 3.0.0 some of these |
478
|
|
|
| setters may handle both. |
479
|
|
|
*/ |
480
|
|
|
|
481
|
|
|
/** |
482
|
|
|
* Set parent order ID. |
483
|
|
|
* |
484
|
|
|
* @since 3.0.0 |
485
|
|
|
* @param int $value Value to set. |
486
|
|
|
* @throws WC_Data_Exception Exception thrown if parent ID does not exist or is invalid. |
487
|
|
|
*/ |
488
|
88 |
|
public function set_parent_id( $value ) { |
489
|
88 |
|
if ( $value && ( $value === $this->get_id() || ! wc_get_order( $value ) ) ) { |
490
|
|
|
$this->error( 'order_invalid_parent_id', __( 'Invalid parent ID', 'woocommerce' ) ); |
491
|
|
|
} |
492
|
88 |
|
$this->set_prop( 'parent_id', absint( $value ) ); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Set order status. |
497
|
|
|
* |
498
|
|
|
* @since 3.0.0 |
499
|
|
|
* @param string $new_status Status to change the order to. No internal wc- prefix is required. |
500
|
|
|
* @return array details of change |
501
|
|
|
*/ |
502
|
133 |
|
public function set_status( $new_status ) { |
503
|
133 |
|
$old_status = $this->get_status(); |
504
|
133 |
|
$new_status = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status; |
505
|
|
|
|
506
|
|
|
// If setting the status, ensure it's set to a valid status. |
507
|
133 |
|
if ( true === $this->object_read ) { |
508
|
|
|
// Only allow valid new status. |
509
|
116 |
View Code Duplication |
if ( ! in_array( 'wc-' . $new_status, $this->get_valid_statuses(), true ) && 'trash' !== $new_status ) { |
510
|
|
|
$new_status = 'pending'; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
// If the old status is set but unknown (e.g. draft) assume its pending for action usage. |
514
|
116 |
View Code Duplication |
if ( $old_status && ! in_array( 'wc-' . $old_status, $this->get_valid_statuses(), true ) && 'trash' !== $old_status ) { |
515
|
|
|
$old_status = 'pending'; |
516
|
|
|
} |
517
|
|
|
} |
518
|
|
|
|
519
|
133 |
|
$this->set_prop( 'status', $new_status ); |
520
|
|
|
|
521
|
|
|
return array( |
522
|
133 |
|
'from' => $old_status, |
523
|
133 |
|
'to' => $new_status, |
524
|
|
|
); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
/** |
528
|
|
|
* Set order_version. |
529
|
|
|
* |
530
|
|
|
* @param string $value Value to set. |
531
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
532
|
|
|
*/ |
533
|
162 |
|
public function set_version( $value ) { |
534
|
162 |
|
$this->set_prop( 'version', $value ); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
/** |
538
|
|
|
* Set order_currency. |
539
|
|
|
* |
540
|
|
|
* @param string $value Value to set. |
541
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
542
|
|
|
*/ |
543
|
163 |
|
public function set_currency( $value ) { |
544
|
163 |
|
if ( $value && ! in_array( $value, array_keys( get_woocommerce_currencies() ), true ) ) { |
545
|
|
|
$this->error( 'order_invalid_currency', __( 'Invalid currency code', 'woocommerce' ) ); |
546
|
|
|
} |
547
|
163 |
|
$this->set_prop( 'currency', $value ? $value : get_woocommerce_currency() ); |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
/** |
551
|
|
|
* Set prices_include_tax. |
552
|
|
|
* |
553
|
|
|
* @param bool $value Value to set. |
554
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
555
|
|
|
*/ |
556
|
128 |
|
public function set_prices_include_tax( $value ) { |
557
|
128 |
|
$this->set_prop( 'prices_include_tax', (bool) $value ); |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Set date_created. |
562
|
|
|
* |
563
|
|
|
* @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if there is no date. |
564
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
565
|
|
|
*/ |
566
|
163 |
|
public function set_date_created( $date = null ) { |
567
|
163 |
|
$this->set_date_prop( 'date_created', $date ); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
/** |
571
|
|
|
* Set date_modified. |
572
|
|
|
* |
573
|
|
|
* @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if there is no date. |
574
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
575
|
|
|
*/ |
576
|
89 |
|
public function set_date_modified( $date = null ) { |
577
|
89 |
|
$this->set_date_prop( 'date_modified', $date ); |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Set discount_total. |
582
|
|
|
* |
583
|
|
|
* @param string $value Value to set. |
584
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
585
|
|
|
*/ |
586
|
134 |
|
public function set_discount_total( $value ) { |
587
|
134 |
|
$this->set_prop( 'discount_total', wc_format_decimal( $value ) ); |
588
|
|
|
} |
589
|
|
|
|
590
|
|
|
/** |
591
|
|
|
* Set discount_tax. |
592
|
|
|
* |
593
|
|
|
* @param string $value Value to set. |
594
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
595
|
|
|
*/ |
596
|
134 |
|
public function set_discount_tax( $value ) { |
597
|
134 |
|
$this->set_prop( 'discount_tax', wc_format_decimal( $value ) ); |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
/** |
601
|
|
|
* Set shipping_total. |
602
|
|
|
* |
603
|
|
|
* @param string $value Value to set. |
604
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
605
|
|
|
*/ |
606
|
134 |
|
public function set_shipping_total( $value ) { |
607
|
134 |
|
$this->set_prop( 'shipping_total', wc_format_decimal( $value ) ); |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
/** |
611
|
|
|
* Set shipping_tax. |
612
|
|
|
* |
613
|
|
|
* @param string $value Value to set. |
614
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
615
|
|
|
*/ |
616
|
137 |
|
public function set_shipping_tax( $value ) { |
617
|
137 |
|
$this->set_prop( 'shipping_tax', wc_format_decimal( $value ) ); |
618
|
137 |
|
$this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() ); |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
/** |
622
|
|
|
* Set cart tax. |
623
|
|
|
* |
624
|
|
|
* @param string $value Value to set. |
625
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
626
|
|
|
*/ |
627
|
137 |
|
public function set_cart_tax( $value ) { |
628
|
137 |
|
$this->set_prop( 'cart_tax', wc_format_decimal( $value ) ); |
629
|
137 |
|
$this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() ); |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
/** |
633
|
|
|
* Sets order tax (sum of cart and shipping tax). Used internally only. |
634
|
|
|
* |
635
|
|
|
* @param string $value Value to set. |
636
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
637
|
|
|
*/ |
638
|
138 |
|
protected function set_total_tax( $value ) { |
639
|
138 |
|
$this->set_prop( 'total_tax', wc_format_decimal( $value ) ); |
640
|
|
|
} |
641
|
|
|
|
642
|
|
|
/** |
643
|
|
|
* Set total. |
644
|
|
|
* |
645
|
|
|
* @param string $value Value to set. |
646
|
|
|
* @param string $deprecated Function used to set different totals based on this. |
647
|
|
|
* |
648
|
|
|
* @return bool|void |
649
|
|
|
* @throws WC_Data_Exception Exception may be thrown if value is invalid. |
650
|
|
|
*/ |
651
|
135 |
|
public function set_total( $value, $deprecated = '' ) { |
652
|
135 |
|
if ( $deprecated ) { |
653
|
|
|
wc_deprecated_argument( 'total_type', '3.0', 'Use dedicated total setter methods instead.' ); |
654
|
|
|
return $this->legacy_set_total( $value, $deprecated ); |
655
|
|
|
} |
656
|
135 |
|
$this->set_prop( 'total', wc_format_decimal( $value, wc_get_price_decimals() ) ); |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
/* |
660
|
|
|
|-------------------------------------------------------------------------- |
661
|
|
|
| Order Item Handling |
662
|
|
|
|-------------------------------------------------------------------------- |
663
|
|
|
| |
664
|
|
|
| Order items are used for products, taxes, shipping, and fees within |
665
|
|
|
| each order. |
666
|
|
|
*/ |
667
|
|
|
|
668
|
|
|
/** |
669
|
|
|
* Remove all line items (products, coupons, shipping, taxes) from the order. |
670
|
|
|
* |
671
|
|
|
* @param string $type Order item type. Default null. |
672
|
|
|
*/ |
673
|
1 |
|
public function remove_order_items( $type = null ) { |
674
|
1 |
|
if ( ! empty( $type ) ) { |
675
|
|
|
$this->data_store->delete_items( $this, $type ); |
676
|
|
|
|
677
|
|
|
$group = $this->type_to_group( $type ); |
678
|
|
|
|
679
|
|
|
if ( $group ) { |
680
|
|
|
unset( $this->items[ $group ] ); |
681
|
|
|
} |
682
|
|
|
} else { |
683
|
1 |
|
$this->data_store->delete_items( $this ); |
684
|
1 |
|
$this->items = array(); |
685
|
|
|
} |
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
/** |
689
|
|
|
* Convert a type to a types group. |
690
|
|
|
* |
691
|
|
|
* @param string $type type to lookup. |
692
|
|
|
* @return string |
693
|
|
|
*/ |
694
|
140 |
|
protected function type_to_group( $type ) { |
695
|
140 |
|
$type_to_group = apply_filters( |
696
|
140 |
|
'woocommerce_order_type_to_group', |
697
|
|
|
array( |
698
|
140 |
|
'line_item' => 'line_items', |
699
|
|
|
'tax' => 'tax_lines', |
700
|
|
|
'shipping' => 'shipping_lines', |
701
|
|
|
'fee' => 'fee_lines', |
702
|
|
|
'coupon' => 'coupon_lines', |
703
|
|
|
) |
704
|
|
|
); |
705
|
140 |
|
return isset( $type_to_group[ $type ] ) ? $type_to_group[ $type ] : ''; |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
/** |
709
|
|
|
* Return an array of items/products within this order. |
710
|
|
|
* |
711
|
|
|
* @param string|array $types Types of line items to get (array or string). |
712
|
|
|
* @return WC_Order_Item[] |
713
|
|
|
*/ |
714
|
140 |
|
public function get_items( $types = 'line_item' ) { |
715
|
140 |
|
$items = array(); |
716
|
140 |
|
$types = array_filter( (array) $types ); |
717
|
|
|
|
718
|
140 |
|
foreach ( $types as $type ) { |
719
|
140 |
|
$group = $this->type_to_group( $type ); |
720
|
|
|
|
721
|
140 |
|
if ( $group ) { |
722
|
140 |
|
if ( ! isset( $this->items[ $group ] ) ) { |
723
|
140 |
|
$this->items[ $group ] = array_filter( $this->data_store->read_items( $this, $type ) ); |
724
|
|
|
} |
725
|
|
|
// Don't use array_merge here because keys are numeric. |
726
|
140 |
|
$items = $items + $this->items[ $group ]; |
727
|
|
|
} |
728
|
|
|
} |
729
|
|
|
|
730
|
140 |
|
return apply_filters( 'woocommerce_order_get_items', $items, $this, $types ); |
731
|
|
|
} |
732
|
|
|
|
733
|
|
|
/** |
734
|
|
|
* Return an array of coupons within this order. |
735
|
|
|
* |
736
|
|
|
* @since 3.7.0 |
737
|
|
|
* @return WC_Order_Item_Coupon[] |
738
|
|
|
*/ |
739
|
2 |
|
public function get_coupons() { |
740
|
2 |
|
return $this->get_items( 'coupon' ); |
741
|
|
|
} |
742
|
|
|
|
743
|
|
|
/** |
744
|
|
|
* Return an array of fees within this order. |
745
|
|
|
* |
746
|
|
|
* @return WC_Order_item_Fee[] |
747
|
|
|
*/ |
748
|
37 |
|
public function get_fees() { |
749
|
37 |
|
return $this->get_items( 'fee' ); |
750
|
|
|
} |
751
|
|
|
|
752
|
|
|
/** |
753
|
|
|
* Return an array of taxes within this order. |
754
|
|
|
* |
755
|
|
|
* @return WC_Order_Item_Tax[] |
756
|
|
|
*/ |
757
|
39 |
|
public function get_taxes() { |
758
|
39 |
|
return $this->get_items( 'tax' ); |
759
|
|
|
} |
760
|
|
|
|
761
|
|
|
/** |
762
|
|
|
* Return an array of shipping costs within this order. |
763
|
|
|
* |
764
|
|
|
* @return WC_Order_Item_Shipping[] |
765
|
|
|
*/ |
766
|
44 |
|
public function get_shipping_methods() { |
767
|
44 |
|
return $this->get_items( 'shipping' ); |
768
|
|
|
} |
769
|
|
|
|
770
|
|
|
/** |
771
|
|
|
* Gets formatted shipping method title. |
772
|
|
|
* |
773
|
|
|
* @return string |
774
|
|
|
*/ |
775
|
1 |
|
public function get_shipping_method() { |
776
|
1 |
|
$names = array(); |
777
|
1 |
|
foreach ( $this->get_shipping_methods() as $shipping_method ) { |
778
|
1 |
|
$names[] = $shipping_method->get_name(); |
779
|
|
|
} |
780
|
1 |
|
return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $names ), $this ); |
781
|
|
|
} |
782
|
|
|
|
783
|
|
|
/** |
784
|
|
|
* Get used coupon codes only. |
785
|
|
|
* |
786
|
|
|
* @since 3.7.0 |
787
|
|
|
* @return array |
788
|
|
|
*/ |
789
|
22 |
|
public function get_coupon_codes() { |
790
|
22 |
|
$coupon_codes = array(); |
791
|
22 |
|
$coupons = $this->get_items( 'coupon' ); |
792
|
|
|
|
793
|
22 |
|
if ( $coupons ) { |
|
|
|
|
794
|
2 |
|
foreach ( $coupons as $coupon ) { |
795
|
2 |
|
$coupon_codes[] = $coupon->get_code(); |
|
|
|
|
796
|
|
|
} |
797
|
|
|
} |
798
|
22 |
|
return $coupon_codes; |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
/** |
802
|
|
|
* Gets the count of order items of a certain type. |
803
|
|
|
* |
804
|
|
|
* @param string $item_type Item type to lookup. |
805
|
|
|
* @return int|string |
806
|
|
|
*/ |
807
|
10 |
|
public function get_item_count( $item_type = '' ) { |
808
|
10 |
|
$items = $this->get_items( empty( $item_type ) ? 'line_item' : $item_type ); |
809
|
10 |
|
$count = 0; |
810
|
|
|
|
811
|
10 |
|
foreach ( $items as $item ) { |
812
|
7 |
|
$count += $item->get_quantity(); |
813
|
|
|
} |
814
|
|
|
|
815
|
10 |
|
return apply_filters( 'woocommerce_get_item_count', $count, $item_type, $this ); |
816
|
|
|
} |
817
|
|
|
|
818
|
|
|
/** |
819
|
|
|
* Get an order item object, based on its type. |
820
|
|
|
* |
821
|
|
|
* @since 3.0.0 |
822
|
|
|
* @param int $item_id ID of item to get. |
823
|
|
|
* @param bool $load_from_db Prior to 3.2 this item was loaded direct from WC_Order_Factory, not this object. This param is here for backwards compatility with that. If false, uses the local items variable instead. |
824
|
|
|
* @return WC_Order_Item|false |
825
|
|
|
*/ |
826
|
21 |
|
public function get_item( $item_id, $load_from_db = true ) { |
827
|
21 |
|
if ( $load_from_db ) { |
828
|
1 |
|
return WC_Order_Factory::get_order_item( $item_id ); |
829
|
|
|
} |
830
|
|
|
|
831
|
|
|
// Search for item id. |
832
|
20 |
|
if ( $this->items ) { |
|
|
|
|
833
|
17 |
|
foreach ( $this->items as $group => $items ) { |
834
|
17 |
|
if ( isset( $items[ $item_id ] ) ) { |
835
|
17 |
|
return $items[ $item_id ]; |
836
|
|
|
} |
837
|
|
|
} |
838
|
|
|
} |
839
|
|
|
|
840
|
|
|
// Load all items of type and cache. |
841
|
5 |
|
$type = $this->data_store->get_order_item_type( $this, $item_id ); |
842
|
|
|
|
843
|
5 |
|
if ( ! $type ) { |
844
|
|
|
return false; |
845
|
|
|
} |
846
|
|
|
|
847
|
5 |
|
$items = $this->get_items( $type ); |
848
|
|
|
|
849
|
5 |
|
return ! empty( $items[ $item_id ] ) ? $items[ $item_id ] : false; |
850
|
|
|
} |
851
|
|
|
|
852
|
|
|
/** |
853
|
|
|
* Get key for where a certain item type is stored in _items. |
854
|
|
|
* |
855
|
|
|
* @since 3.0.0 |
856
|
|
|
* @param string $item object Order item (product, shipping, fee, coupon, tax). |
857
|
|
|
* @return string |
858
|
|
|
*/ |
859
|
121 |
|
protected function get_items_key( $item ) { |
860
|
121 |
|
if ( is_a( $item, 'WC_Order_Item_Product' ) ) { |
861
|
113 |
|
return 'line_items'; |
862
|
113 |
|
} elseif ( is_a( $item, 'WC_Order_Item_Fee' ) ) { |
863
|
4 |
|
return 'fee_lines'; |
864
|
111 |
|
} elseif ( is_a( $item, 'WC_Order_Item_Shipping' ) ) { |
865
|
98 |
|
return 'shipping_lines'; |
866
|
24 |
|
} elseif ( is_a( $item, 'WC_Order_Item_Tax' ) ) { |
867
|
10 |
|
return 'tax_lines'; |
868
|
19 |
|
} elseif ( is_a( $item, 'WC_Order_Item_Coupon' ) ) { |
869
|
19 |
|
return 'coupon_lines'; |
870
|
|
|
} |
871
|
1 |
|
return apply_filters( 'woocommerce_get_items_key', '', $item ); |
872
|
|
|
} |
873
|
|
|
|
874
|
|
|
/** |
875
|
|
|
* Remove item from the order. |
876
|
|
|
* |
877
|
|
|
* @param int $item_id Item ID to delete. |
878
|
|
|
* @return false|void |
879
|
|
|
*/ |
880
|
12 |
|
public function remove_item( $item_id ) { |
881
|
12 |
|
$item = $this->get_item( $item_id, false ); |
882
|
12 |
|
$items_key = $item ? $this->get_items_key( $item ) : false; |
883
|
|
|
|
884
|
12 |
|
if ( ! $items_key ) { |
|
|
|
|
885
|
|
|
return false; |
886
|
|
|
} |
887
|
|
|
|
888
|
|
|
// Unset and remove later. |
889
|
12 |
|
$this->items_to_delete[] = $item; |
890
|
12 |
|
unset( $this->items[ $items_key ][ $item->get_id() ] ); |
891
|
|
|
} |
892
|
|
|
|
893
|
|
|
/** |
894
|
|
|
* Adds an order item to this order. The order item will not persist until save. |
895
|
|
|
* |
896
|
|
|
* @since 3.0.0 |
897
|
|
|
* @param WC_Order_Item $item Order item object (product, shipping, fee, coupon, tax). |
898
|
|
|
* @return false|void |
899
|
|
|
*/ |
900
|
121 |
|
public function add_item( $item ) { |
901
|
121 |
|
$items_key = $this->get_items_key( $item ); |
902
|
|
|
|
903
|
121 |
|
if ( ! $items_key ) { |
904
|
1 |
|
return false; |
905
|
|
|
} |
906
|
|
|
|
907
|
|
|
// Make sure existing items are loaded so we can append this new one. |
908
|
121 |
|
if ( ! isset( $this->items[ $items_key ] ) ) { |
909
|
120 |
|
$this->items[ $items_key ] = $this->get_items( $item->get_type() ); |
910
|
|
|
} |
911
|
|
|
|
912
|
|
|
// Set parent. |
913
|
121 |
|
$item->set_order_id( $this->get_id() ); |
914
|
|
|
|
915
|
|
|
// Append new row with generated temporary ID. |
916
|
121 |
|
$item_id = $item->get_id(); |
917
|
|
|
|
918
|
121 |
|
if ( $item_id ) { |
919
|
103 |
|
$this->items[ $items_key ][ $item_id ] = $item; |
920
|
|
|
} else { |
921
|
116 |
|
$this->items[ $items_key ][ 'new:' . $items_key . count( $this->items[ $items_key ] ) ] = $item; |
922
|
|
|
} |
923
|
|
|
} |
924
|
|
|
|
925
|
|
|
/** |
926
|
|
|
* Apply a coupon to the order and recalculate totals. |
927
|
|
|
* |
928
|
|
|
* @since 3.2.0 |
929
|
|
|
* @param string|WC_Coupon $raw_coupon Coupon code or object. |
930
|
|
|
* @return true|WP_Error True if applied, error if not. |
931
|
|
|
*/ |
932
|
12 |
|
public function apply_coupon( $raw_coupon ) { |
933
|
12 |
|
if ( is_a( $raw_coupon, 'WC_Coupon' ) ) { |
934
|
3 |
|
$coupon = $raw_coupon; |
935
|
9 |
|
} elseif ( is_string( $raw_coupon ) ) { |
936
|
9 |
|
$code = wc_format_coupon_code( $raw_coupon ); |
937
|
9 |
|
$coupon = new WC_Coupon( $code ); |
938
|
|
|
|
939
|
9 |
|
if ( $coupon->get_code() !== $code ) { |
940
|
|
|
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon code', 'woocommerce' ) ); |
941
|
|
|
} |
942
|
|
|
|
943
|
9 |
|
$discounts = new WC_Discounts( $this ); |
944
|
9 |
|
$valid = $discounts->is_coupon_valid( $coupon ); |
945
|
|
|
|
946
|
9 |
|
if ( is_wp_error( $valid ) ) { |
947
|
9 |
|
return $valid; |
948
|
|
|
} |
949
|
|
|
} else { |
950
|
|
|
return new WP_Error( 'invalid_coupon', __( 'Invalid coupon', 'woocommerce' ) ); |
951
|
|
|
} |
952
|
|
|
|
953
|
|
|
// Check to make sure coupon is not already applied. |
954
|
11 |
|
$applied_coupons = $this->get_items( 'coupon' ); |
955
|
11 |
|
foreach ( $applied_coupons as $applied_coupon ) { |
956
|
4 |
|
if ( $applied_coupon->get_code() === $coupon->get_code() ) { |
|
|
|
|
957
|
|
|
return new WP_Error( 'invalid_coupon', __( 'Coupon code already applied!', 'woocommerce' ) ); |
958
|
|
|
} |
959
|
|
|
} |
960
|
|
|
|
961
|
11 |
|
$discounts = new WC_Discounts( $this ); |
962
|
11 |
|
$applied = $discounts->apply_coupon( $coupon ); |
|
|
|
|
963
|
|
|
|
964
|
11 |
|
if ( is_wp_error( $applied ) ) { |
965
|
|
|
return $applied; |
966
|
|
|
} |
967
|
|
|
|
968
|
11 |
|
$data_store = $coupon->get_data_store(); |
969
|
|
|
|
970
|
|
|
// Check specific for guest checkouts here as well since WC_Cart handles that seperately in check_customer_coupons. |
971
|
11 |
|
if ( $data_store && 0 === $this->get_customer_id() ) { |
972
|
1 |
|
$usage_count = $data_store->get_usage_by_email( $coupon, $this->get_billing_email() ); |
973
|
1 |
|
if ( 0 < $coupon->get_usage_limit_per_user() && $usage_count >= $coupon->get_usage_limit_per_user() ) { |
974
|
1 |
|
return new WP_Error( |
975
|
1 |
|
'invalid_coupon', |
976
|
1 |
|
$coupon->get_coupon_error( 106 ), |
977
|
|
|
array( |
978
|
1 |
|
'status' => 400, |
979
|
|
|
) |
980
|
|
|
); |
981
|
|
|
} |
982
|
|
|
} |
983
|
|
|
|
984
|
11 |
|
$this->set_coupon_discount_amounts( $discounts ); |
985
|
11 |
|
$this->save(); |
986
|
|
|
|
987
|
|
|
// Recalculate totals and taxes. |
988
|
11 |
|
$this->recalculate_coupons(); |
989
|
|
|
|
990
|
|
|
// Record usage so counts and validation is correct. |
991
|
11 |
|
$used_by = $this->get_user_id(); |
992
|
|
|
|
993
|
11 |
|
if ( ! $used_by ) { |
994
|
1 |
|
$used_by = $this->get_billing_email(); |
995
|
|
|
} |
996
|
|
|
|
997
|
11 |
|
$coupon->increase_usage_count( $used_by ); |
998
|
|
|
|
999
|
11 |
|
return true; |
1000
|
|
|
} |
1001
|
|
|
|
1002
|
|
|
/** |
1003
|
|
|
* Remove a coupon from the order and recalculate totals. |
1004
|
|
|
* |
1005
|
|
|
* Coupons affect line item totals, but there is no relationship between |
1006
|
|
|
* coupon and line total, so to remove a coupon we need to work from the |
1007
|
|
|
* line subtotal (price before discount) and re-apply all coupons in this |
1008
|
|
|
* order. |
1009
|
|
|
* |
1010
|
|
|
* Manual discounts are not affected; those are separate and do not affect |
1011
|
|
|
* stored line totals. |
1012
|
|
|
* |
1013
|
|
|
* @since 3.2.0 |
1014
|
|
|
* @param string $code Coupon code. |
1015
|
|
|
* @return void |
1016
|
|
|
*/ |
1017
|
7 |
|
public function remove_coupon( $code ) { |
1018
|
7 |
|
$coupons = $this->get_items( 'coupon' ); |
1019
|
|
|
|
1020
|
|
|
// Remove the coupon line. |
1021
|
7 |
|
foreach ( $coupons as $item_id => $coupon ) { |
1022
|
7 |
|
if ( $coupon->get_code() === $code ) { |
|
|
|
|
1023
|
7 |
|
$this->remove_item( $item_id ); |
1024
|
7 |
|
$coupon_object = new WC_Coupon( $code ); |
1025
|
7 |
|
$coupon_object->decrease_usage_count( $this->get_user_id() ); |
1026
|
7 |
|
$this->recalculate_coupons(); |
1027
|
7 |
|
break; |
1028
|
|
|
} |
1029
|
|
|
} |
1030
|
|
|
} |
1031
|
|
|
|
1032
|
|
|
/** |
1033
|
|
|
* Apply all coupons in this order again to all line items. |
1034
|
|
|
* |
1035
|
|
|
* @since 3.2.0 |
1036
|
|
|
*/ |
1037
|
15 |
|
protected function recalculate_coupons() { |
1038
|
|
|
// Reset line item totals. |
1039
|
15 |
|
foreach ( $this->get_items() as $item ) { |
1040
|
15 |
|
$item->set_total( $item->get_subtotal() ); |
|
|
|
|
1041
|
15 |
|
$item->set_total_tax( $item->get_subtotal_tax() ); |
|
|
|
|
1042
|
|
|
} |
1043
|
|
|
|
1044
|
15 |
|
$discounts = new WC_Discounts( $this ); |
1045
|
|
|
|
1046
|
15 |
|
foreach ( $this->get_items( 'coupon' ) as $coupon_item ) { |
1047
|
15 |
|
$coupon_code = $coupon_item->get_code(); |
|
|
|
|
1048
|
15 |
|
$coupon_id = wc_get_coupon_id_by_code( $coupon_code ); |
1049
|
|
|
|
1050
|
|
|
// If we have a coupon ID (loaded via wc_get_coupon_id_by_code) we can simply load the new coupon object using the ID. |
1051
|
15 |
|
if ( $coupon_id ) { |
1052
|
15 |
|
$coupon_object = new WC_Coupon( $coupon_id ); |
1053
|
|
|
|
1054
|
|
|
} else { |
1055
|
|
|
|
1056
|
|
|
// If we do not have a coupon ID (was it virtual? has it been deleted?) we must create a temporary coupon using what data we have stored during checkout. |
1057
|
8 |
|
$coupon_object = new WC_Coupon(); |
1058
|
8 |
|
$coupon_object->set_props( (array) $coupon_item->get_meta( 'coupon_data', true ) ); |
1059
|
8 |
|
$coupon_object->set_code( $coupon_code ); |
1060
|
8 |
|
$coupon_object->set_virtual( true ); |
1061
|
|
|
|
1062
|
|
|
// If there is no coupon amount (maybe dynamic?), set it to the given **discount** amount so the coupon's same value is applied. |
1063
|
8 |
|
if ( ! $coupon_object->get_amount() ) { |
1064
|
|
|
|
1065
|
|
|
// If the order originally had prices including tax, remove the discount + discount tax. |
1066
|
8 |
|
if ( $this->get_prices_include_tax() ) { |
1067
|
4 |
|
$coupon_object->set_amount( $coupon_item->get_discount() + $coupon_item->get_discount_tax() ); |
|
|
|
|
1068
|
|
|
} else { |
1069
|
4 |
|
$coupon_object->set_amount( $coupon_item->get_discount() ); |
|
|
|
|
1070
|
|
|
} |
1071
|
8 |
|
$coupon_object->set_discount_type( 'fixed_cart' ); |
1072
|
|
|
} |
1073
|
|
|
} |
1074
|
|
|
|
1075
|
|
|
/** |
1076
|
|
|
* Allow developers to filter this coupon before it get's re-applied to the order. |
1077
|
|
|
* |
1078
|
|
|
* @since 3.2.0 |
1079
|
|
|
*/ |
1080
|
15 |
|
$coupon_object = apply_filters( 'woocommerce_order_recalculate_coupons_coupon_object', $coupon_object, $coupon_code, $coupon_item, $this ); |
1081
|
|
|
|
1082
|
15 |
|
if ( $coupon_object ) { |
1083
|
15 |
|
$discounts->apply_coupon( $coupon_object, false ); |
1084
|
|
|
} |
1085
|
|
|
} |
1086
|
|
|
|
1087
|
15 |
|
$this->set_coupon_discount_amounts( $discounts ); |
1088
|
15 |
|
$this->set_item_discount_amounts( $discounts ); |
1089
|
|
|
|
1090
|
|
|
// Recalculate totals and taxes. |
1091
|
15 |
|
$this->calculate_totals( true ); |
1092
|
|
|
} |
1093
|
|
|
|
1094
|
|
|
/** |
1095
|
|
|
* After applying coupons via the WC_Discounts class, update line items. |
1096
|
|
|
* |
1097
|
|
|
* @since 3.2.0 |
1098
|
|
|
* @param WC_Discounts $discounts Discounts class. |
1099
|
|
|
*/ |
1100
|
15 |
|
protected function set_item_discount_amounts( $discounts ) { |
1101
|
15 |
|
$item_discounts = $discounts->get_discounts_by_item(); |
1102
|
|
|
|
1103
|
15 |
|
if ( $item_discounts ) { |
1104
|
15 |
|
foreach ( $item_discounts as $item_id => $amount ) { |
|
|
|
|
1105
|
15 |
|
$item = $this->get_item( $item_id, false ); |
1106
|
|
|
|
1107
|
|
|
// If the prices include tax, discounts should be taken off the tax inclusive prices like in the cart. |
1108
|
15 |
|
if ( $this->get_prices_include_tax() && wc_tax_enabled() ) { |
1109
|
2 |
|
$taxes = WC_Tax::calc_tax( $amount, WC_Tax::get_rates( $item->get_tax_class() ), true ); |
1110
|
|
|
|
1111
|
2 |
|
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { |
1112
|
2 |
|
$taxes = array_map( 'wc_round_tax_total', $taxes ); |
1113
|
|
|
} |
1114
|
|
|
|
1115
|
2 |
|
$amount = $amount - array_sum( $taxes ); |
1116
|
2 |
|
$item->set_total( max( 0, round( $item->get_total() - $amount, wc_get_price_decimals() ) ) ); |
1117
|
|
|
} else { |
1118
|
13 |
|
$item->set_total( max( 0, $item->get_total() - $amount ) ); |
1119
|
|
|
} |
1120
|
|
|
} |
1121
|
|
|
} |
1122
|
|
|
} |
1123
|
|
|
|
1124
|
|
|
/** |
1125
|
|
|
* After applying coupons via the WC_Discounts class, update or create coupon items. |
1126
|
|
|
* |
1127
|
|
|
* @since 3.2.0 |
1128
|
|
|
* @param WC_Discounts $discounts Discounts class. |
1129
|
|
|
*/ |
1130
|
15 |
|
protected function set_coupon_discount_amounts( $discounts ) { |
1131
|
15 |
|
$coupons = $this->get_items( 'coupon' ); |
1132
|
15 |
|
$coupon_code_to_id = wc_list_pluck( $coupons, 'get_id', 'get_code' ); |
1133
|
15 |
|
$all_discounts = $discounts->get_discounts(); |
1134
|
15 |
|
$coupon_discounts = $discounts->get_discounts_by_coupon(); |
1135
|
|
|
|
1136
|
15 |
|
if ( $coupon_discounts ) { |
1137
|
15 |
|
foreach ( $coupon_discounts as $coupon_code => $amount ) { |
|
|
|
|
1138
|
15 |
|
$item_id = isset( $coupon_code_to_id[ $coupon_code ] ) ? $coupon_code_to_id[ $coupon_code ] : 0; |
1139
|
|
|
|
1140
|
15 |
|
if ( ! $item_id ) { |
1141
|
11 |
|
$coupon_item = new WC_Order_Item_Coupon(); |
1142
|
11 |
|
$coupon_item->set_code( $coupon_code ); |
1143
|
|
|
} else { |
1144
|
15 |
|
$coupon_item = $this->get_item( $item_id, false ); |
1145
|
|
|
} |
1146
|
|
|
|
1147
|
15 |
|
$discount_tax = 0; |
1148
|
|
|
|
1149
|
|
|
// Work out how much tax has been removed as a result of the discount from this coupon. |
1150
|
15 |
|
foreach ( $all_discounts[ $coupon_code ] as $item_id => $item_discount_amount ) { |
1151
|
15 |
|
$item = $this->get_item( $item_id, false ); |
1152
|
|
|
|
1153
|
15 |
|
if ( $this->get_prices_include_tax() && wc_tax_enabled() ) { |
1154
|
2 |
|
$taxes = WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ), true ); |
1155
|
|
|
|
1156
|
2 |
|
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { |
1157
|
2 |
|
$taxes = array_map( 'wc_round_tax_total', $taxes ); |
1158
|
|
|
} |
1159
|
|
|
|
1160
|
2 |
|
$discount_tax += array_sum( $taxes ); |
1161
|
2 |
|
$amount = $amount - array_sum( $taxes ); |
1162
|
|
|
} else { |
1163
|
13 |
|
$taxes = WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ) ); |
1164
|
|
|
|
1165
|
13 |
|
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { |
1166
|
13 |
|
$taxes = array_map( 'wc_round_tax_total', $taxes ); |
1167
|
|
|
} |
1168
|
|
|
|
1169
|
13 |
|
$discount_tax += array_sum( $taxes ); |
1170
|
|
|
} |
1171
|
|
|
} |
1172
|
|
|
|
1173
|
15 |
|
$coupon_item->set_discount( $amount ); |
1174
|
15 |
|
$coupon_item->set_discount_tax( $discount_tax ); |
1175
|
|
|
|
1176
|
15 |
|
$this->add_item( $coupon_item ); |
1177
|
|
|
} |
1178
|
|
|
} |
1179
|
|
|
} |
1180
|
|
|
|
1181
|
|
|
/** |
1182
|
|
|
* Add a product line item to the order. This is the only line item type with |
1183
|
|
|
* its own method because it saves looking up order amounts (costs are added up for you). |
1184
|
|
|
* |
1185
|
|
|
* @param WC_Product $product Product object. |
1186
|
|
|
* @param int $qty Quantity to add. |
1187
|
|
|
* @param array $args Args for the added product. |
1188
|
|
|
* @return int |
1189
|
|
|
* @throws WC_Data_Exception Exception thrown if the item cannot be added to the cart. |
1190
|
|
|
*/ |
1191
|
6 |
|
public function add_product( $product, $qty = 1, $args = array() ) { |
1192
|
6 |
|
if ( $product ) { |
1193
|
|
|
$default_args = array( |
1194
|
6 |
|
'name' => $product->get_name(), |
1195
|
6 |
|
'tax_class' => $product->get_tax_class(), |
1196
|
6 |
|
'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(), |
1197
|
6 |
|
'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0, |
1198
|
6 |
|
'variation' => $product->is_type( 'variation' ) ? $product->get_attributes() : array(), |
1199
|
6 |
|
'subtotal' => wc_get_price_excluding_tax( $product, array( 'qty' => $qty ) ), |
1200
|
6 |
|
'total' => wc_get_price_excluding_tax( $product, array( 'qty' => $qty ) ), |
1201
|
6 |
|
'quantity' => $qty, |
1202
|
|
|
); |
1203
|
|
|
} else { |
1204
|
|
|
$default_args = array( |
1205
|
|
|
'quantity' => $qty, |
1206
|
|
|
); |
1207
|
|
|
} |
1208
|
|
|
|
1209
|
6 |
|
$args = wp_parse_args( $args, $default_args ); |
1210
|
|
|
|
1211
|
|
|
// BW compatibility with old args. |
1212
|
6 |
|
if ( isset( $args['totals'] ) ) { |
1213
|
|
|
foreach ( $args['totals'] as $key => $value ) { |
1214
|
|
|
if ( 'tax' === $key ) { |
1215
|
|
|
$args['total_tax'] = $value; |
1216
|
|
|
} elseif ( 'tax_data' === $key ) { |
1217
|
|
|
$args['taxes'] = $value; |
1218
|
|
|
} else { |
1219
|
|
|
$args[ $key ] = $value; |
1220
|
|
|
} |
1221
|
|
|
} |
1222
|
|
|
} |
1223
|
|
|
|
1224
|
6 |
|
$item = new WC_Order_Item_Product(); |
1225
|
6 |
|
$item->set_props( $args ); |
1226
|
6 |
|
$item->set_backorder_meta(); |
1227
|
6 |
|
$item->set_order_id( $this->get_id() ); |
1228
|
6 |
|
$item->save(); |
1229
|
6 |
|
$this->add_item( $item ); |
1230
|
6 |
|
wc_do_deprecated_action( 'woocommerce_order_add_product', array( $this->get_id(), $item->get_id(), $product, $qty, $args ), '3.0', 'woocommerce_new_order_item action instead' ); |
1231
|
6 |
|
delete_transient( 'wc_order_' . $this->get_id() . '_needs_processing' ); |
1232
|
6 |
|
return $item->get_id(); |
1233
|
|
|
} |
1234
|
|
|
|
1235
|
|
|
/* |
1236
|
|
|
|-------------------------------------------------------------------------- |
1237
|
|
|
| Payment Token Handling |
1238
|
|
|
|-------------------------------------------------------------------------- |
1239
|
|
|
| |
1240
|
|
|
| Payment tokens are hashes used to take payments by certain gateways. |
1241
|
|
|
| |
1242
|
|
|
*/ |
1243
|
|
|
|
1244
|
|
|
/** |
1245
|
|
|
* Add a payment token to an order |
1246
|
|
|
* |
1247
|
|
|
* @since 2.6 |
1248
|
|
|
* @param WC_Payment_Token $token Payment token object. |
1249
|
|
|
* @return boolean|int The new token ID or false if it failed. |
1250
|
|
|
*/ |
1251
|
3 |
|
public function add_payment_token( $token ) { |
1252
|
3 |
|
if ( empty( $token ) || ! ( $token instanceof WC_Payment_Token ) ) { |
1253
|
1 |
|
return false; |
1254
|
|
|
} |
1255
|
|
|
|
1256
|
3 |
|
$token_ids = $this->data_store->get_payment_token_ids( $this ); |
1257
|
3 |
|
$token_ids[] = $token->get_id(); |
1258
|
3 |
|
$this->data_store->update_payment_token_ids( $this, $token_ids ); |
1259
|
|
|
|
1260
|
3 |
|
do_action( 'woocommerce_payment_token_added_to_order', $this->get_id(), $token->get_id(), $token, $token_ids ); |
1261
|
3 |
|
return $token->get_id(); |
1262
|
|
|
} |
1263
|
|
|
|
1264
|
|
|
/** |
1265
|
|
|
* Returns a list of all payment tokens associated with the current order |
1266
|
|
|
* |
1267
|
|
|
* @since 2.6 |
1268
|
|
|
* @return array An array of payment token objects |
1269
|
|
|
*/ |
1270
|
4 |
|
public function get_payment_tokens() { |
1271
|
4 |
|
return $this->data_store->get_payment_token_ids( $this ); |
1272
|
|
|
} |
1273
|
|
|
|
1274
|
|
|
/* |
1275
|
|
|
|-------------------------------------------------------------------------- |
1276
|
|
|
| Calculations. |
1277
|
|
|
|-------------------------------------------------------------------------- |
1278
|
|
|
| |
1279
|
|
|
| These methods calculate order totals and taxes based on the current data. |
1280
|
|
|
| |
1281
|
|
|
*/ |
1282
|
|
|
|
1283
|
|
|
/** |
1284
|
|
|
* Calculate shipping total. |
1285
|
|
|
* |
1286
|
|
|
* @since 2.2 |
1287
|
|
|
* @return float |
1288
|
|
|
*/ |
1289
|
1 |
|
public function calculate_shipping() { |
1290
|
1 |
|
$shipping_total = 0; |
1291
|
|
|
|
1292
|
1 |
|
foreach ( $this->get_shipping_methods() as $shipping ) { |
1293
|
1 |
|
$shipping_total += $shipping->get_total(); |
|
|
|
|
1294
|
|
|
} |
1295
|
|
|
|
1296
|
1 |
|
$this->set_shipping_total( $shipping_total ); |
1297
|
1 |
|
$this->save(); |
1298
|
|
|
|
1299
|
1 |
|
return $this->get_shipping_total(); |
1300
|
|
|
} |
1301
|
|
|
|
1302
|
|
|
/** |
1303
|
|
|
* Get all tax classes for items in the order. |
1304
|
|
|
* |
1305
|
|
|
* @since 2.6.3 |
1306
|
|
|
* @return array |
1307
|
|
|
*/ |
1308
|
33 |
|
public function get_items_tax_classes() { |
1309
|
33 |
|
$found_tax_classes = array(); |
1310
|
|
|
|
1311
|
33 |
|
foreach ( $this->get_items() as $item ) { |
1312
|
33 |
|
if ( is_callable( array( $item, 'get_tax_status' ) ) && in_array( $item->get_tax_status(), array( 'taxable', 'shipping' ), true ) ) { |
1313
|
33 |
|
$found_tax_classes[] = $item->get_tax_class(); |
1314
|
|
|
} |
1315
|
|
|
} |
1316
|
|
|
|
1317
|
33 |
|
return array_unique( $found_tax_classes ); |
1318
|
|
|
} |
1319
|
|
|
|
1320
|
|
|
/** |
1321
|
|
|
* Get tax location for this order. |
1322
|
|
|
* |
1323
|
|
|
* @since 3.2.0 |
1324
|
|
|
* @param array $args array Override the location. |
1325
|
|
|
* @return array |
1326
|
|
|
*/ |
1327
|
32 |
|
protected function get_tax_location( $args = array() ) { |
1328
|
32 |
|
$tax_based_on = get_option( 'woocommerce_tax_based_on' ); |
1329
|
|
|
|
1330
|
32 |
|
if ( 'shipping' === $tax_based_on && ! $this->get_shipping_country() ) { |
1331
|
19 |
|
$tax_based_on = 'billing'; |
1332
|
|
|
} |
1333
|
|
|
|
1334
|
32 |
|
$args = wp_parse_args( |
1335
|
32 |
|
$args, |
1336
|
|
|
array( |
1337
|
32 |
|
'country' => 'billing' === $tax_based_on ? $this->get_billing_country() : $this->get_shipping_country(), |
1338
|
32 |
|
'state' => 'billing' === $tax_based_on ? $this->get_billing_state() : $this->get_shipping_state(), |
1339
|
32 |
|
'postcode' => 'billing' === $tax_based_on ? $this->get_billing_postcode() : $this->get_shipping_postcode(), |
1340
|
32 |
|
'city' => 'billing' === $tax_based_on ? $this->get_billing_city() : $this->get_shipping_city(), |
1341
|
|
|
) |
1342
|
|
|
); |
1343
|
|
|
|
1344
|
|
|
// Default to base. |
1345
|
32 |
|
if ( 'base' === $tax_based_on || empty( $args['country'] ) ) { |
1346
|
15 |
|
$args['country'] = WC()->countries->get_base_country(); |
1347
|
15 |
|
$args['state'] = WC()->countries->get_base_state(); |
1348
|
15 |
|
$args['postcode'] = WC()->countries->get_base_postcode(); |
1349
|
15 |
|
$args['city'] = WC()->countries->get_base_city(); |
1350
|
|
|
} |
1351
|
|
|
|
1352
|
32 |
|
return $args; |
1353
|
|
|
} |
1354
|
|
|
|
1355
|
|
|
/** |
1356
|
|
|
* Calculate taxes for all line items and shipping, and store the totals and tax rows. |
1357
|
|
|
* |
1358
|
|
|
* If by default the taxes are based on the shipping address and the current order doesn't |
1359
|
|
|
* have any, it would use the billing address rather than using the Shopping base location. |
1360
|
|
|
* |
1361
|
|
|
* Will use the base country unless customer addresses are set. |
1362
|
|
|
* |
1363
|
|
|
* @param array $args Added in 3.0.0 to pass things like location. |
1364
|
|
|
*/ |
1365
|
32 |
|
public function calculate_taxes( $args = array() ) { |
1366
|
32 |
|
do_action( 'woocommerce_order_before_calculate_taxes', $args, $this ); |
1367
|
|
|
|
1368
|
32 |
|
$calculate_tax_for = $this->get_tax_location( $args ); |
1369
|
32 |
|
$shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' ); |
1370
|
|
|
|
1371
|
32 |
|
if ( 'inherit' === $shipping_tax_class ) { |
1372
|
32 |
|
$found_classes = array_intersect( array_merge( array( '' ), WC_Tax::get_tax_class_slugs() ), $this->get_items_tax_classes() ); |
1373
|
32 |
|
$shipping_tax_class = count( $found_classes ) ? current( $found_classes ) : false; |
1374
|
|
|
} |
1375
|
|
|
|
1376
|
32 |
|
$is_vat_exempt = apply_filters( 'woocommerce_order_is_vat_exempt', 'yes' === $this->get_meta( 'is_vat_exempt' ), $this ); |
1377
|
|
|
|
1378
|
|
|
// Trigger tax recalculation for all items. |
1379
|
32 |
|
foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) { |
1380
|
32 |
|
if ( ! $is_vat_exempt ) { |
1381
|
32 |
|
$item->calculate_taxes( $calculate_tax_for ); |
1382
|
|
|
} else { |
1383
|
1 |
|
$item->set_taxes( false ); |
|
|
|
|
1384
|
|
|
} |
1385
|
|
|
} |
1386
|
|
|
|
1387
|
32 |
|
foreach ( $this->get_shipping_methods() as $item_id => $item ) { |
1388
|
20 |
|
if ( false !== $shipping_tax_class && ! $is_vat_exempt ) { |
1389
|
20 |
|
$item->calculate_taxes( array_merge( $calculate_tax_for, array( 'tax_class' => $shipping_tax_class ) ) ); |
1390
|
|
|
} else { |
1391
|
1 |
|
$item->set_taxes( false ); |
|
|
|
|
1392
|
|
|
} |
1393
|
|
|
} |
1394
|
|
|
|
1395
|
32 |
|
$this->update_taxes(); |
1396
|
|
|
} |
1397
|
|
|
|
1398
|
|
|
/** |
1399
|
|
|
* Update tax lines for the order based on the line item taxes themselves. |
1400
|
|
|
*/ |
1401
|
39 |
|
public function update_taxes() { |
1402
|
39 |
|
$cart_taxes = array(); |
1403
|
39 |
|
$shipping_taxes = array(); |
1404
|
39 |
|
$existing_taxes = $this->get_taxes(); |
1405
|
39 |
|
$saved_rate_ids = array(); |
1406
|
|
|
|
1407
|
39 |
View Code Duplication |
foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) { |
1408
|
32 |
|
$taxes = $item->get_taxes(); |
|
|
|
|
1409
|
32 |
|
foreach ( $taxes['total'] as $tax_rate_id => $tax ) { |
1410
|
10 |
|
$tax_amount = (float) $tax; |
1411
|
|
|
|
1412
|
10 |
|
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { |
1413
|
10 |
|
$tax_amount = wc_round_tax_total( $tax_amount ); |
1414
|
|
|
} |
1415
|
|
|
|
1416
|
10 |
|
$cart_taxes[ $tax_rate_id ] = isset( $cart_taxes[ $tax_rate_id ] ) ? $cart_taxes[ $tax_rate_id ] + $tax_amount : $tax_amount; |
1417
|
|
|
} |
1418
|
|
|
} |
1419
|
|
|
|
1420
|
39 |
View Code Duplication |
foreach ( $this->get_shipping_methods() as $item_id => $item ) { |
1421
|
20 |
|
$taxes = $item->get_taxes(); |
|
|
|
|
1422
|
20 |
|
foreach ( $taxes['total'] as $tax_rate_id => $tax ) { |
1423
|
4 |
|
$tax_amount = (float) $tax; |
1424
|
|
|
|
1425
|
4 |
|
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { |
1426
|
4 |
|
$tax_amount = wc_round_tax_total( $tax_amount ); |
1427
|
|
|
} |
1428
|
|
|
|
1429
|
4 |
|
$shipping_taxes[ $tax_rate_id ] = isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] + $tax_amount : $tax_amount; |
1430
|
|
|
} |
1431
|
|
|
} |
1432
|
|
|
|
1433
|
39 |
|
foreach ( $existing_taxes as $tax ) { |
1434
|
|
|
// Remove taxes which no longer exist for cart/shipping. |
1435
|
5 |
|
if ( ( ! array_key_exists( $tax->get_rate_id(), $cart_taxes ) && ! array_key_exists( $tax->get_rate_id(), $shipping_taxes ) ) || in_array( $tax->get_rate_id(), $saved_rate_ids, true ) ) { |
|
|
|
|
1436
|
1 |
|
$this->remove_item( $tax->get_id() ); |
1437
|
1 |
|
continue; |
1438
|
|
|
} |
1439
|
4 |
|
$saved_rate_ids[] = $tax->get_rate_id(); |
|
|
|
|
1440
|
4 |
|
$tax->set_tax_total( isset( $cart_taxes[ $tax->get_rate_id() ] ) ? $cart_taxes[ $tax->get_rate_id() ] : 0 ); |
|
|
|
|
1441
|
4 |
|
$tax->set_shipping_tax_total( ! empty( $shipping_taxes[ $tax->get_rate_id() ] ) ? $shipping_taxes[ $tax->get_rate_id() ] : 0 ); |
|
|
|
|
1442
|
4 |
|
$tax->save(); |
1443
|
|
|
} |
1444
|
|
|
|
1445
|
39 |
|
$new_rate_ids = wp_parse_id_list( array_diff( array_keys( $cart_taxes + $shipping_taxes ), $saved_rate_ids ) ); |
1446
|
|
|
|
1447
|
|
|
// New taxes. |
1448
|
39 |
|
foreach ( $new_rate_ids as $tax_rate_id ) { |
1449
|
10 |
|
$item = new WC_Order_Item_Tax(); |
1450
|
10 |
|
$item->set_rate( $tax_rate_id ); |
1451
|
10 |
|
$item->set_tax_total( isset( $cart_taxes[ $tax_rate_id ] ) ? $cart_taxes[ $tax_rate_id ] : 0 ); |
1452
|
10 |
|
$item->set_shipping_tax_total( ! empty( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 ); |
1453
|
10 |
|
$this->add_item( $item ); |
1454
|
|
|
} |
1455
|
|
|
|
1456
|
39 |
|
$this->set_shipping_tax( wc_round_tax_total( array_sum( $shipping_taxes ) ) ); |
1457
|
39 |
|
$this->set_cart_tax( wc_round_tax_total( array_sum( $cart_taxes ) ) ); |
1458
|
39 |
|
$this->save(); |
1459
|
|
|
} |
1460
|
|
|
|
1461
|
|
|
/** |
1462
|
|
|
* Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total. |
1463
|
|
|
* |
1464
|
|
|
* @since 2.2 |
1465
|
|
|
* @param bool $and_taxes Calc taxes if true. |
1466
|
|
|
* @return float calculated grand total. |
1467
|
|
|
*/ |
1468
|
36 |
|
public function calculate_totals( $and_taxes = true ) { |
1469
|
36 |
|
do_action( 'woocommerce_order_before_calculate_totals', $and_taxes, $this ); |
1470
|
|
|
|
1471
|
36 |
|
$cart_subtotal = 0; |
1472
|
36 |
|
$cart_total = 0; |
1473
|
36 |
|
$fees_total = 0; |
1474
|
36 |
|
$shipping_total = 0; |
1475
|
36 |
|
$cart_subtotal_tax = 0; |
1476
|
36 |
|
$cart_total_tax = 0; |
1477
|
|
|
|
1478
|
|
|
// Sum line item costs. |
1479
|
36 |
|
foreach ( $this->get_items() as $item ) { |
1480
|
29 |
|
$cart_subtotal += round( $item->get_subtotal(), wc_get_price_decimals() ); |
|
|
|
|
1481
|
29 |
|
$cart_total += round( $item->get_total(), wc_get_price_decimals() ); |
|
|
|
|
1482
|
|
|
} |
1483
|
|
|
|
1484
|
|
|
// Sum shipping costs. |
1485
|
36 |
|
foreach ( $this->get_shipping_methods() as $shipping ) { |
1486
|
18 |
|
$shipping_total += round( $shipping->get_total(), wc_get_price_decimals() ); |
|
|
|
|
1487
|
|
|
} |
1488
|
|
|
|
1489
|
36 |
|
$this->set_shipping_total( $shipping_total ); |
1490
|
|
|
|
1491
|
|
|
// Sum fee costs. |
1492
|
36 |
|
foreach ( $this->get_fees() as $item ) { |
1493
|
|
|
$fee_total = $item->get_total(); |
|
|
|
|
1494
|
|
|
|
1495
|
|
|
if ( 0 > $fee_total ) { |
1496
|
|
|
$max_discount = round( $cart_total + $fees_total + $shipping_total, wc_get_price_decimals() ) * -1; |
1497
|
|
|
|
1498
|
|
|
if ( $fee_total < $max_discount ) { |
1499
|
|
|
$item->set_total( $max_discount ); |
|
|
|
|
1500
|
|
|
} |
1501
|
|
|
} |
1502
|
|
|
|
1503
|
|
|
$fees_total += $item->get_total(); |
|
|
|
|
1504
|
|
|
} |
1505
|
|
|
|
1506
|
|
|
// Calculate taxes for items, shipping, discounts. Note; this also triggers save(). |
1507
|
36 |
|
if ( $and_taxes ) { |
1508
|
29 |
|
$this->calculate_taxes(); |
1509
|
|
|
} |
1510
|
|
|
|
1511
|
|
|
// Sum taxes again so we can work out how much tax was discounted. This uses original values, not those possibly rounded to 2dp. |
1512
|
36 |
|
foreach ( $this->get_items() as $item ) { |
1513
|
29 |
|
$taxes = $item->get_taxes(); |
|
|
|
|
1514
|
|
|
|
1515
|
29 |
|
foreach ( $taxes['total'] as $tax_rate_id => $tax ) { |
1516
|
7 |
|
$cart_total_tax += (float) $tax; |
1517
|
|
|
} |
1518
|
|
|
|
1519
|
29 |
|
foreach ( $taxes['subtotal'] as $tax_rate_id => $tax ) { |
1520
|
7 |
|
$cart_subtotal_tax += (float) $tax; |
1521
|
|
|
} |
1522
|
|
|
} |
1523
|
|
|
|
1524
|
36 |
|
$this->set_discount_total( $cart_subtotal - $cart_total ); |
1525
|
36 |
|
$this->set_discount_tax( wc_round_tax_total( $cart_subtotal_tax - $cart_total_tax ) ); |
1526
|
36 |
|
$this->set_total( round( $cart_total + $fees_total + $this->get_shipping_total() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() ) ); |
1527
|
|
|
|
1528
|
36 |
|
do_action( 'woocommerce_order_after_calculate_totals', $and_taxes, $this ); |
1529
|
|
|
|
1530
|
36 |
|
$this->save(); |
1531
|
|
|
|
1532
|
36 |
|
return $this->get_total(); |
1533
|
|
|
} |
1534
|
|
|
|
1535
|
|
|
/** |
1536
|
|
|
* Get item subtotal - this is the cost before discount. |
1537
|
|
|
* |
1538
|
|
|
* @param object $item Item to get total from. |
1539
|
|
|
* @param bool $inc_tax (default: false). |
1540
|
|
|
* @param bool $round (default: true). |
1541
|
|
|
* @return float |
1542
|
|
|
*/ |
1543
|
|
View Code Duplication |
public function get_item_subtotal( $item, $inc_tax = false, $round = true ) { |
|
|
|
|
1544
|
|
|
$subtotal = 0; |
1545
|
|
|
|
1546
|
|
|
if ( is_callable( array( $item, 'get_subtotal' ) ) && $item->get_quantity() ) { |
1547
|
|
|
if ( $inc_tax ) { |
1548
|
|
|
$subtotal = ( $item->get_subtotal() + $item->get_subtotal_tax() ) / $item->get_quantity(); |
1549
|
|
|
} else { |
1550
|
|
|
$subtotal = floatval( $item->get_subtotal() ) / $item->get_quantity(); |
1551
|
|
|
} |
1552
|
|
|
|
1553
|
|
|
$subtotal = $round ? number_format( (float) $subtotal, wc_get_price_decimals(), '.', '' ) : $subtotal; |
1554
|
|
|
} |
1555
|
|
|
|
1556
|
|
|
return apply_filters( 'woocommerce_order_amount_item_subtotal', $subtotal, $this, $item, $inc_tax, $round ); |
1557
|
|
|
} |
1558
|
|
|
|
1559
|
|
|
/** |
1560
|
|
|
* Get line subtotal - this is the cost before discount. |
1561
|
|
|
* |
1562
|
|
|
* @param object $item Item to get total from. |
1563
|
|
|
* @param bool $inc_tax (default: false). |
1564
|
|
|
* @param bool $round (default: true). |
1565
|
|
|
* @return float |
1566
|
|
|
*/ |
1567
|
|
View Code Duplication |
public function get_line_subtotal( $item, $inc_tax = false, $round = true ) { |
|
|
|
|
1568
|
|
|
$subtotal = 0; |
1569
|
|
|
|
1570
|
|
|
if ( is_callable( array( $item, 'get_subtotal' ) ) ) { |
1571
|
|
|
if ( $inc_tax ) { |
1572
|
|
|
$subtotal = $item->get_subtotal() + $item->get_subtotal_tax(); |
1573
|
|
|
} else { |
1574
|
|
|
$subtotal = $item->get_subtotal(); |
1575
|
|
|
} |
1576
|
|
|
|
1577
|
|
|
$subtotal = $round ? round( $subtotal, wc_get_price_decimals() ) : $subtotal; |
1578
|
|
|
} |
1579
|
|
|
|
1580
|
|
|
return apply_filters( 'woocommerce_order_amount_line_subtotal', $subtotal, $this, $item, $inc_tax, $round ); |
1581
|
|
|
} |
1582
|
|
|
|
1583
|
|
|
/** |
1584
|
|
|
* Calculate item cost - useful for gateways. |
1585
|
|
|
* |
1586
|
|
|
* @param object $item Item to get total from. |
1587
|
|
|
* @param bool $inc_tax (default: false). |
1588
|
|
|
* @param bool $round (default: true). |
1589
|
|
|
* @return float |
1590
|
|
|
*/ |
1591
|
|
View Code Duplication |
public function get_item_total( $item, $inc_tax = false, $round = true ) { |
|
|
|
|
1592
|
|
|
$total = 0; |
1593
|
|
|
|
1594
|
|
|
if ( is_callable( array( $item, 'get_total' ) ) && $item->get_quantity() ) { |
1595
|
|
|
if ( $inc_tax ) { |
1596
|
|
|
$total = ( $item->get_total() + $item->get_total_tax() ) / $item->get_quantity(); |
1597
|
|
|
} else { |
1598
|
|
|
$total = floatval( $item->get_total() ) / $item->get_quantity(); |
1599
|
|
|
} |
1600
|
|
|
|
1601
|
|
|
$total = $round ? round( $total, wc_get_price_decimals() ) : $total; |
1602
|
|
|
} |
1603
|
|
|
|
1604
|
|
|
return apply_filters( 'woocommerce_order_amount_item_total', $total, $this, $item, $inc_tax, $round ); |
1605
|
|
|
} |
1606
|
|
|
|
1607
|
|
|
/** |
1608
|
|
|
* Calculate line total - useful for gateways. |
1609
|
|
|
* |
1610
|
|
|
* @param object $item Item to get total from. |
1611
|
|
|
* @param bool $inc_tax (default: false). |
1612
|
|
|
* @param bool $round (default: true). |
1613
|
|
|
* @return float |
1614
|
|
|
*/ |
1615
|
|
View Code Duplication |
public function get_line_total( $item, $inc_tax = false, $round = true ) { |
|
|
|
|
1616
|
|
|
$total = 0; |
1617
|
|
|
|
1618
|
|
|
if ( is_callable( array( $item, 'get_total' ) ) ) { |
1619
|
|
|
// Check if we need to add line tax to the line total. |
1620
|
|
|
$total = $inc_tax ? $item->get_total() + $item->get_total_tax() : $item->get_total(); |
1621
|
|
|
|
1622
|
|
|
// Check if we need to round. |
1623
|
|
|
$total = $round ? round( $total, wc_get_price_decimals() ) : $total; |
1624
|
|
|
} |
1625
|
|
|
|
1626
|
|
|
return apply_filters( 'woocommerce_order_amount_line_total', $total, $this, $item, $inc_tax, $round ); |
1627
|
|
|
} |
1628
|
|
|
|
1629
|
|
|
/** |
1630
|
|
|
* Get item tax - useful for gateways. |
1631
|
|
|
* |
1632
|
|
|
* @param mixed $item Item to get total from. |
1633
|
|
|
* @param bool $round (default: true). |
1634
|
|
|
* @return float |
1635
|
|
|
*/ |
1636
|
|
|
public function get_item_tax( $item, $round = true ) { |
1637
|
|
|
$tax = 0; |
1638
|
|
|
|
1639
|
|
|
if ( is_callable( array( $item, 'get_total_tax' ) ) && $item->get_quantity() ) { |
1640
|
|
|
$tax = $item->get_total_tax() / $item->get_quantity(); |
1641
|
|
|
$tax = $round ? wc_round_tax_total( $tax ) : $tax; |
1642
|
|
|
} |
1643
|
|
|
|
1644
|
|
|
return apply_filters( 'woocommerce_order_amount_item_tax', $tax, $item, $round, $this ); |
1645
|
|
|
} |
1646
|
|
|
|
1647
|
|
|
/** |
1648
|
|
|
* Get line tax - useful for gateways. |
1649
|
|
|
* |
1650
|
|
|
* @param mixed $item Item to get total from. |
1651
|
|
|
* @return float |
1652
|
|
|
*/ |
1653
|
|
|
public function get_line_tax( $item ) { |
1654
|
|
|
return apply_filters( 'woocommerce_order_amount_line_tax', is_callable( array( $item, 'get_total_tax' ) ) ? wc_round_tax_total( $item->get_total_tax() ) : 0, $item, $this ); |
1655
|
|
|
} |
1656
|
|
|
|
1657
|
|
|
/** |
1658
|
|
|
* Gets line subtotal - formatted for display. |
1659
|
|
|
* |
1660
|
|
|
* @param object $item Item to get total from. |
1661
|
|
|
* @param string $tax_display Incl or excl tax display mode. |
1662
|
|
|
* @return string |
1663
|
|
|
*/ |
1664
|
|
|
public function get_formatted_line_subtotal( $item, $tax_display = '' ) { |
1665
|
|
|
$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); |
1666
|
|
|
|
1667
|
|
|
if ( 'excl' === $tax_display ) { |
1668
|
|
|
$ex_tax_label = $this->get_prices_include_tax() ? 1 : 0; |
1669
|
|
|
|
1670
|
|
|
$subtotal = wc_price( |
1671
|
|
|
$this->get_line_subtotal( $item ), |
1672
|
|
|
array( |
1673
|
|
|
'ex_tax_label' => $ex_tax_label, |
1674
|
|
|
'currency' => $this->get_currency(), |
1675
|
|
|
) |
1676
|
|
|
); |
1677
|
|
|
} else { |
1678
|
|
|
$subtotal = wc_price( $this->get_line_subtotal( $item, true ), array( 'currency' => $this->get_currency() ) ); |
1679
|
|
|
} |
1680
|
|
|
|
1681
|
|
|
return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this ); |
1682
|
|
|
} |
1683
|
|
|
|
1684
|
|
|
/** |
1685
|
|
|
* Gets order total - formatted for display. |
1686
|
|
|
* |
1687
|
|
|
* @return string |
1688
|
|
|
*/ |
1689
|
|
|
public function get_formatted_order_total() { |
1690
|
|
|
$formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_currency() ) ); |
1691
|
|
|
return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this ); |
1692
|
|
|
} |
1693
|
|
|
|
1694
|
|
|
/** |
1695
|
|
|
* Gets subtotal - subtotal is shown before discounts, but with localised taxes. |
1696
|
|
|
* |
1697
|
|
|
* @param bool $compound (default: false). |
1698
|
|
|
* @param string $tax_display (default: the tax_display_cart value). |
1699
|
|
|
* @return string |
1700
|
|
|
*/ |
1701
|
|
|
public function get_subtotal_to_display( $compound = false, $tax_display = '' ) { |
1702
|
|
|
$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); |
1703
|
|
|
$subtotal = 0; |
1704
|
|
|
|
1705
|
|
|
if ( ! $compound ) { |
1706
|
|
|
foreach ( $this->get_items() as $item ) { |
1707
|
|
|
$subtotal += $item->get_subtotal(); |
|
|
|
|
1708
|
|
|
|
1709
|
|
|
if ( 'incl' === $tax_display ) { |
1710
|
|
|
$subtotal += $item->get_subtotal_tax(); |
|
|
|
|
1711
|
|
|
} |
1712
|
|
|
} |
1713
|
|
|
|
1714
|
|
|
$subtotal = wc_price( $subtotal, array( 'currency' => $this->get_currency() ) ); |
1715
|
|
|
|
1716
|
|
View Code Duplication |
if ( 'excl' === $tax_display && $this->get_prices_include_tax() && wc_tax_enabled() ) { |
1717
|
|
|
$subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; |
1718
|
|
|
} |
1719
|
|
|
} else { |
1720
|
|
|
if ( 'incl' === $tax_display ) { |
1721
|
|
|
return ''; |
1722
|
|
|
} |
1723
|
|
|
|
1724
|
|
|
foreach ( $this->get_items() as $item ) { |
1725
|
|
|
$subtotal += $item->get_subtotal(); |
|
|
|
|
1726
|
|
|
} |
1727
|
|
|
|
1728
|
|
|
// Add Shipping Costs. |
1729
|
|
|
$subtotal += $this->get_shipping_total(); |
1730
|
|
|
|
1731
|
|
|
// Remove non-compound taxes. |
1732
|
|
|
foreach ( $this->get_taxes() as $tax ) { |
1733
|
|
|
if ( $tax->is_compound() ) { |
|
|
|
|
1734
|
|
|
continue; |
1735
|
|
|
} |
1736
|
|
|
$subtotal = $subtotal + $tax->get_tax_total() + $tax->get_shipping_tax_total(); |
|
|
|
|
1737
|
|
|
} |
1738
|
|
|
|
1739
|
|
|
// Remove discounts. |
1740
|
|
|
$subtotal = $subtotal - $this->get_total_discount(); |
1741
|
|
|
$subtotal = wc_price( $subtotal, array( 'currency' => $this->get_currency() ) ); |
1742
|
|
|
} |
1743
|
|
|
|
1744
|
|
|
return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this ); |
1745
|
|
|
} |
1746
|
|
|
|
1747
|
|
|
/** |
1748
|
|
|
* Gets shipping (formatted). |
1749
|
|
|
* |
1750
|
|
|
* @param string $tax_display Excl or incl tax display mode. |
1751
|
|
|
* @return string |
1752
|
|
|
*/ |
1753
|
|
|
public function get_shipping_to_display( $tax_display = '' ) { |
1754
|
|
|
$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); |
1755
|
|
|
|
1756
|
|
|
if ( 0 < abs( (float) $this->get_shipping_total() ) ) { |
1757
|
|
|
|
1758
|
|
|
if ( 'excl' === $tax_display ) { |
1759
|
|
|
|
1760
|
|
|
// Show shipping excluding tax. |
1761
|
|
|
$shipping = wc_price( $this->get_shipping_total(), array( 'currency' => $this->get_currency() ) ); |
1762
|
|
|
|
1763
|
|
View Code Duplication |
if ( (float) $this->get_shipping_tax() > 0 && $this->get_prices_include_tax() ) { |
1764
|
|
|
$shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>', $this, $tax_display ); |
1765
|
|
|
} |
1766
|
|
|
} else { |
1767
|
|
|
|
1768
|
|
|
// Show shipping including tax. |
1769
|
|
|
$shipping = wc_price( $this->get_shipping_total() + $this->get_shipping_tax(), array( 'currency' => $this->get_currency() ) ); |
1770
|
|
|
|
1771
|
|
View Code Duplication |
if ( (float) $this->get_shipping_tax() > 0 && ! $this->get_prices_include_tax() ) { |
1772
|
|
|
$shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>', $this, $tax_display ); |
1773
|
|
|
} |
1774
|
|
|
} |
1775
|
|
|
|
1776
|
|
|
/* translators: %s: method */ |
1777
|
|
|
$shipping .= apply_filters( 'woocommerce_order_shipping_to_display_shipped_via', ' <small class="shipped_via">' . sprintf( __( 'via %s', 'woocommerce' ), $this->get_shipping_method() ) . '</small>', $this ); |
1778
|
|
|
|
1779
|
|
|
} elseif ( $this->get_shipping_method() ) { |
1780
|
|
|
$shipping = $this->get_shipping_method(); |
1781
|
|
|
} else { |
1782
|
|
|
$shipping = __( 'Free!', 'woocommerce' ); |
1783
|
|
|
} |
1784
|
|
|
|
1785
|
|
|
return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this, $tax_display ); |
1786
|
|
|
} |
1787
|
|
|
|
1788
|
|
|
/** |
1789
|
|
|
* Get the discount amount (formatted). |
1790
|
|
|
* |
1791
|
|
|
* @since 2.3.0 |
1792
|
|
|
* @param string $tax_display Excl or incl tax display mode. |
1793
|
|
|
* @return string |
1794
|
|
|
*/ |
1795
|
|
|
public function get_discount_to_display( $tax_display = '' ) { |
1796
|
|
|
$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); |
1797
|
|
|
return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_total_discount( 'excl' === $tax_display && 'excl' === get_option( 'woocommerce_tax_display_cart' ) ), array( 'currency' => $this->get_currency() ) ), $this ); |
1798
|
|
|
} |
1799
|
|
|
|
1800
|
|
|
/** |
1801
|
|
|
* Add total row for subtotal. |
1802
|
|
|
* |
1803
|
|
|
* @param array $total_rows Reference to total rows array. |
1804
|
|
|
* @param string $tax_display Excl or incl tax display mode. |
1805
|
|
|
*/ |
1806
|
|
|
protected function add_order_item_totals_subtotal_row( &$total_rows, $tax_display ) { |
1807
|
|
|
$subtotal = $this->get_subtotal_to_display( false, $tax_display ); |
1808
|
|
|
|
1809
|
|
|
if ( $subtotal ) { |
1810
|
|
|
$total_rows['cart_subtotal'] = array( |
1811
|
|
|
'label' => __( 'Subtotal:', 'woocommerce' ), |
1812
|
|
|
'value' => $subtotal, |
1813
|
|
|
); |
1814
|
|
|
} |
1815
|
|
|
} |
1816
|
|
|
|
1817
|
|
|
/** |
1818
|
|
|
* Add total row for discounts. |
1819
|
|
|
* |
1820
|
|
|
* @param array $total_rows Reference to total rows array. |
1821
|
|
|
* @param string $tax_display Excl or incl tax display mode. |
1822
|
|
|
*/ |
1823
|
|
|
protected function add_order_item_totals_discount_row( &$total_rows, $tax_display ) { |
1824
|
|
|
if ( $this->get_total_discount() > 0 ) { |
1825
|
|
|
$total_rows['discount'] = array( |
1826
|
|
|
'label' => __( 'Discount:', 'woocommerce' ), |
1827
|
|
|
'value' => '-' . $this->get_discount_to_display( $tax_display ), |
1828
|
|
|
); |
1829
|
|
|
} |
1830
|
|
|
} |
1831
|
|
|
|
1832
|
|
|
/** |
1833
|
|
|
* Add total row for shipping. |
1834
|
|
|
* |
1835
|
|
|
* @param array $total_rows Reference to total rows array. |
1836
|
|
|
* @param string $tax_display Excl or incl tax display mode. |
1837
|
|
|
*/ |
1838
|
|
|
protected function add_order_item_totals_shipping_row( &$total_rows, $tax_display ) { |
1839
|
|
|
if ( $this->get_shipping_method() ) { |
1840
|
|
|
$total_rows['shipping'] = array( |
1841
|
|
|
'label' => __( 'Shipping:', 'woocommerce' ), |
1842
|
|
|
'value' => $this->get_shipping_to_display( $tax_display ), |
1843
|
|
|
); |
1844
|
|
|
} |
1845
|
|
|
} |
1846
|
|
|
|
1847
|
|
|
/** |
1848
|
|
|
* Add total row for fees. |
1849
|
|
|
* |
1850
|
|
|
* @param array $total_rows Reference to total rows array. |
1851
|
|
|
* @param string $tax_display Excl or incl tax display mode. |
1852
|
|
|
*/ |
1853
|
|
|
protected function add_order_item_totals_fee_rows( &$total_rows, $tax_display ) { |
1854
|
|
|
$fees = $this->get_fees(); |
1855
|
|
|
|
1856
|
|
|
if ( $fees ) { |
|
|
|
|
1857
|
|
|
foreach ( $fees as $id => $fee ) { |
1858
|
|
|
if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', empty( $fee['line_total'] ) && empty( $fee['line_tax'] ), $id ) ) { |
1859
|
|
|
continue; |
1860
|
|
|
} |
1861
|
|
|
$total_rows[ 'fee_' . $fee->get_id() ] = array( |
1862
|
|
|
'label' => $fee->get_name() . ':', |
1863
|
|
|
'value' => wc_price( 'excl' === $tax_display ? $fee->get_total() : $fee->get_total() + $fee->get_total_tax(), array( 'currency' => $this->get_currency() ) ), |
|
|
|
|
1864
|
|
|
); |
1865
|
|
|
} |
1866
|
|
|
} |
1867
|
|
|
} |
1868
|
|
|
|
1869
|
|
|
/** |
1870
|
|
|
* Add total row for taxes. |
1871
|
|
|
* |
1872
|
|
|
* @param array $total_rows Reference to total rows array. |
1873
|
|
|
* @param string $tax_display Excl or incl tax display mode. |
1874
|
|
|
*/ |
1875
|
|
|
protected function add_order_item_totals_tax_rows( &$total_rows, $tax_display ) { |
1876
|
|
|
// Tax for tax exclusive prices. |
1877
|
|
|
if ( 'excl' === $tax_display && wc_tax_enabled() ) { |
1878
|
|
|
if ( 'itemized' === get_option( 'woocommerce_tax_total_display' ) ) { |
1879
|
|
|
foreach ( $this->get_tax_totals() as $code => $tax ) { |
1880
|
|
|
$total_rows[ sanitize_title( $code ) ] = array( |
1881
|
|
|
'label' => $tax->label . ':', |
1882
|
|
|
'value' => $tax->formatted_amount, |
1883
|
|
|
); |
1884
|
|
|
} |
1885
|
|
|
} else { |
1886
|
|
|
$total_rows['tax'] = array( |
1887
|
|
|
'label' => WC()->countries->tax_or_vat() . ':', |
1888
|
|
|
'value' => wc_price( $this->get_total_tax(), array( 'currency' => $this->get_currency() ) ), |
1889
|
|
|
); |
1890
|
|
|
} |
1891
|
|
|
} |
1892
|
|
|
} |
1893
|
|
|
|
1894
|
|
|
/** |
1895
|
|
|
* Add total row for grand total. |
1896
|
|
|
* |
1897
|
|
|
* @param array $total_rows Reference to total rows array. |
1898
|
|
|
* @param string $tax_display Excl or incl tax display mode. |
1899
|
|
|
*/ |
1900
|
|
|
protected function add_order_item_totals_total_row( &$total_rows, $tax_display ) { |
1901
|
|
|
$total_rows['order_total'] = array( |
1902
|
|
|
'label' => __( 'Total:', 'woocommerce' ), |
1903
|
|
|
'value' => $this->get_formatted_order_total( $tax_display ), |
1904
|
|
|
); |
1905
|
|
|
} |
1906
|
|
|
|
1907
|
|
|
/** |
1908
|
|
|
* Get totals for display on pages and in emails. |
1909
|
|
|
* |
1910
|
|
|
* @param mixed $tax_display Excl or incl tax display mode. |
1911
|
|
|
* @return array |
1912
|
|
|
*/ |
1913
|
|
|
public function get_order_item_totals( $tax_display = '' ) { |
1914
|
|
|
$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); |
1915
|
|
|
$total_rows = array(); |
1916
|
|
|
|
1917
|
|
|
$this->add_order_item_totals_subtotal_row( $total_rows, $tax_display ); |
1918
|
|
|
$this->add_order_item_totals_discount_row( $total_rows, $tax_display ); |
1919
|
|
|
$this->add_order_item_totals_shipping_row( $total_rows, $tax_display ); |
1920
|
|
|
$this->add_order_item_totals_fee_rows( $total_rows, $tax_display ); |
1921
|
|
|
$this->add_order_item_totals_tax_rows( $total_rows, $tax_display ); |
1922
|
|
|
$this->add_order_item_totals_total_row( $total_rows, $tax_display ); |
1923
|
|
|
|
1924
|
|
|
return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this, $tax_display ); |
1925
|
|
|
} |
1926
|
|
|
|
1927
|
|
|
/* |
1928
|
|
|
|-------------------------------------------------------------------------- |
1929
|
|
|
| Conditionals |
1930
|
|
|
|-------------------------------------------------------------------------- |
1931
|
|
|
| |
1932
|
|
|
| Checks if a condition is true or false. |
1933
|
|
|
| |
1934
|
|
|
*/ |
1935
|
|
|
|
1936
|
|
|
/** |
1937
|
|
|
* Checks the order status against a passed in status. |
1938
|
|
|
* |
1939
|
|
|
* @param array|string $status Status to check. |
1940
|
|
|
* @return bool |
1941
|
|
|
*/ |
1942
|
30 |
|
public function has_status( $status ) { |
1943
|
30 |
|
return apply_filters( 'woocommerce_order_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status, true ) ) || $this->get_status() === $status, $this, $status ); |
1944
|
|
|
} |
1945
|
|
|
|
1946
|
|
|
/** |
1947
|
|
|
* Check whether this order has a specific shipping method or not. |
1948
|
|
|
* |
1949
|
|
|
* @param string $method_id Method ID to check. |
1950
|
|
|
* @return bool |
1951
|
|
|
*/ |
1952
|
1 |
|
public function has_shipping_method( $method_id ) { |
1953
|
1 |
|
foreach ( $this->get_shipping_methods() as $shipping_method ) { |
1954
|
1 |
|
if ( strpos( $shipping_method->get_method_id(), $method_id ) === 0 ) { |
|
|
|
|
1955
|
1 |
|
return true; |
1956
|
|
|
} |
1957
|
|
|
} |
1958
|
1 |
|
return false; |
1959
|
|
|
} |
1960
|
|
|
|
1961
|
|
|
/** |
1962
|
|
|
* Returns true if the order contains a free product. |
1963
|
|
|
* |
1964
|
|
|
* @since 2.5.0 |
1965
|
|
|
* @return bool |
1966
|
|
|
*/ |
1967
|
2 |
|
public function has_free_item() { |
1968
|
2 |
|
foreach ( $this->get_items() as $item ) { |
1969
|
1 |
|
if ( ! $item->get_total() ) { |
|
|
|
|
1970
|
1 |
|
return true; |
1971
|
|
|
} |
1972
|
|
|
} |
1973
|
2 |
|
return false; |
1974
|
|
|
} |
1975
|
|
|
} |
1976
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.