1
|
|
|
<?php |
2
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
3
|
|
|
exit; |
4
|
|
|
} |
5
|
|
|
|
6
|
|
|
/** |
7
|
|
|
* Order Line Item (product). |
8
|
|
|
* |
9
|
|
|
* @version 2.7.0 |
10
|
|
|
* @since 2.7.0 |
11
|
|
|
* @package WooCommerce/Classes |
12
|
|
|
* @author WooThemes |
13
|
|
|
*/ |
14
|
|
|
class WC_Order_Item_Product extends WC_Order_Item { |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Data properties of this order item object. |
18
|
|
|
* @since 2.7.0 |
19
|
|
|
* @var array |
20
|
|
|
*/ |
21
|
|
|
protected $_data = array( |
22
|
|
|
'order_id' => 0, |
23
|
|
|
'id' => 0, |
24
|
|
|
'name' => '', |
25
|
|
|
'product_id' => 0, |
26
|
|
|
'variation_id' => 0, |
27
|
|
|
'quantity' => 0, |
28
|
|
|
'tax_class' => '', |
29
|
|
|
'subtotal' => 0, |
30
|
|
|
'subtotal_tax' => 0, |
31
|
|
|
'total' => 0, |
32
|
|
|
'total_tax' => 0, |
33
|
|
|
'taxes' => array( |
34
|
|
|
'subtotal' => array(), |
35
|
|
|
'total' => array() |
36
|
|
|
), |
37
|
|
|
); |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* offsetGet for ArrayAccess/Backwards compatibility. |
41
|
|
|
* @deprecated Add deprecation notices in future release. |
42
|
|
|
* @param string $offset |
43
|
|
|
* @return mixed |
44
|
|
|
*/ |
45
|
|
View Code Duplication |
public function offsetGet( $offset ) { |
|
|
|
|
46
|
|
|
if ( 'line_subtotal' === $offset ) { |
47
|
|
|
$offset = 'subtotal'; |
48
|
|
|
} elseif ( 'line_subtotal_tax' === $offset ) { |
49
|
|
|
$offset = 'subtotal_tax'; |
50
|
|
|
} elseif ( 'line_total' === $offset ) { |
51
|
|
|
$offset = 'total'; |
52
|
|
|
} elseif ( 'line_tax' === $offset ) { |
53
|
|
|
$offset = 'total_tax'; |
54
|
|
|
} elseif ( 'line_tax_data' === $offset ) { |
55
|
|
|
$offset = 'taxes'; |
56
|
|
|
} |
57
|
|
|
return parent::offsetGet( $offset ); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* offsetSet for ArrayAccess/Backwards compatibility. |
62
|
|
|
* @deprecated Add deprecation notices in future release. |
63
|
|
|
* @param string $offset |
64
|
|
|
* @param mixed $value |
65
|
|
|
*/ |
66
|
|
View Code Duplication |
public function offsetSet( $offset, $value ) { |
|
|
|
|
67
|
|
|
if ( 'line_subtotal' === $offset ) { |
68
|
|
|
$offset = 'subtotal'; |
69
|
|
|
} elseif ( 'line_subtotal_tax' === $offset ) { |
70
|
|
|
$offset = 'subtotal_tax'; |
71
|
|
|
} elseif ( 'line_total' === $offset ) { |
72
|
|
|
$offset = 'total'; |
73
|
|
|
} elseif ( 'line_tax' === $offset ) { |
74
|
|
|
$offset = 'total_tax'; |
75
|
|
|
} elseif ( 'line_tax_data' === $offset ) { |
76
|
|
|
$offset = 'taxes'; |
77
|
|
|
} |
78
|
|
|
parent::offsetSet( $offset, $value ); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* offsetExists for ArrayAccess |
83
|
|
|
* @param string $offset |
84
|
|
|
* @return bool |
85
|
|
|
*/ |
86
|
|
|
public function offsetExists( $offset ) { |
87
|
|
|
if ( in_array( $offset, array( 'line_subtotal', 'line_subtotal_tax', 'line_total', 'line_tax', 'line_tax_data', 'item_meta_array', 'item_meta' ) ) ) { |
88
|
|
|
return true; |
89
|
|
|
} |
90
|
|
|
return parent::offsetExists( $offset ); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Read/populate data properties specific to this order item. |
95
|
|
|
*/ |
96
|
|
|
public function read( $id ) { |
97
|
|
|
parent::read( $id ); |
98
|
|
|
if ( $this->get_id() ) { |
99
|
|
|
$this->set_product_id( get_metadata( 'order_item', $this->get_id(), '_product_id', true ) ); |
100
|
|
|
$this->set_variation_id( get_metadata( 'order_item', $this->get_id(), '_variation_id', true ) ); |
101
|
|
|
$this->set_quantity( get_metadata( 'order_item', $this->get_id(), '_qty', true ) ); |
102
|
|
|
$this->set_tax_class( get_metadata( 'order_item', $this->get_id(), '_tax_class', true ) ); |
103
|
|
|
$this->set_subtotal( get_metadata( 'order_item', $this->get_id(), '_line_subtotal', true ) ); |
104
|
|
|
$this->set_subtotal_tax( get_metadata( 'order_item', $this->get_id(), '_line_subtotal_tax', true ) ); |
105
|
|
|
$this->set_total( get_metadata( 'order_item', $this->get_id(), '_line_total', true ) ); |
106
|
|
|
$this->set_total_tax( get_metadata( 'order_item', $this->get_id(), '_line_tax', true ) ); |
107
|
|
|
$this->set_taxes( get_metadata( 'order_item', $this->get_id(), '_line_tax_data', true ) ); |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Save properties specific to this order item. |
113
|
|
|
* @return int Item ID |
114
|
|
|
*/ |
115
|
|
|
public function save() { |
116
|
|
|
parent::save(); |
117
|
|
|
if ( $this->get_id() ) { |
118
|
|
|
wc_update_order_item_meta( $this->get_id(), '_product_id', $this->get_product_id() ); |
119
|
|
|
wc_update_order_item_meta( $this->get_id(), '_variation_id', $this->get_variation_id() ); |
120
|
|
|
wc_update_order_item_meta( $this->get_id(), '_qty', $this->get_quantity() ); |
121
|
|
|
wc_update_order_item_meta( $this->get_id(), '_tax_class', $this->get_tax_class() ); |
122
|
|
|
wc_update_order_item_meta( $this->get_id(), '_line_subtotal', $this->get_subtotal() ); |
123
|
|
|
wc_update_order_item_meta( $this->get_id(), '_line_subtotal_tax', $this->get_subtotal_tax() ); |
124
|
|
|
wc_update_order_item_meta( $this->get_id(), '_line_total', $this->get_total() ); |
125
|
|
|
wc_update_order_item_meta( $this->get_id(), '_line_tax', $this->get_total_tax() ); |
126
|
|
|
wc_update_order_item_meta( $this->get_id(), '_line_tax_data', $this->get_taxes() ); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
return $this->get_id(); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Internal meta keys we don't want exposed as part of meta_data. |
134
|
|
|
* @return array() |
|
|
|
|
135
|
|
|
*/ |
136
|
|
|
protected function get_internal_meta_keys() { |
137
|
|
|
return array( '_product_id', '_variation_id', '_qty', '_tax_class', '_line_subtotal', '_line_subtotal_tax', '_line_total', '_line_tax', '_line_tax_data' ); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Get the associated product. |
142
|
|
|
* @return WC_Product|bool |
143
|
|
|
*/ |
144
|
|
|
public function get_product() { |
145
|
|
|
if ( $this->get_variation_id() ) { |
146
|
|
|
$product = wc_get_product( $this->get_variation_id() ); |
147
|
|
|
} else { |
148
|
|
|
$product = wc_get_product( $this->get_product_id() ); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
// Backwards compatible filter from WC_Order::get_product_from_item() |
152
|
|
|
if ( has_filter( 'woocommerce_get_product_from_item' ) ) { |
153
|
|
|
$product = apply_filters( 'woocommerce_get_product_from_item', $product, $this, wc_get_order( $this->get_order_id() ) ); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return apply_filters( 'woocommerce_order_item_product', $product, $this ); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Get the Download URL. |
161
|
|
|
* @param int $download_id |
162
|
|
|
* @return string |
163
|
|
|
*/ |
164
|
|
|
public function get_item_download_url( $download_id ) { |
165
|
|
|
$order = $this->get_order(); |
166
|
|
|
|
167
|
|
|
return $order ? add_query_arg( array( |
168
|
|
|
'download_file' => $this->get_variation_id() ? $this->get_variation_id() : $this->get_product_id(), |
169
|
|
|
'order' => $order->get_order_key(), |
170
|
|
|
'email' => urlencode( $order->get_billing_email() ), |
171
|
|
|
'key' => $download_id |
172
|
|
|
), trailingslashit( home_url() ) ) : ''; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Get any associated downloadable files. |
177
|
|
|
* @return array |
178
|
|
|
*/ |
179
|
|
|
public function get_item_downloads() { |
180
|
|
|
global $wpdb; |
181
|
|
|
|
182
|
|
|
$files = array(); |
183
|
|
|
$product = $this->get_product(); |
184
|
|
|
$order = $this->get_order(); |
185
|
|
|
|
186
|
|
|
if ( $product && $order && $product->is_downloadable() && $order->is_download_permitted() ) { |
187
|
|
|
$download_ids = $wpdb->get_col( |
188
|
|
|
$wpdb->prepare( |
189
|
|
|
"SELECT download_id FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE user_email = %s AND order_key = %s AND product_id = %d ORDER BY permission_id", |
190
|
|
|
$order->get_billing_email(), |
191
|
|
|
$order->get_order_key(), |
192
|
|
|
$this->get_variation_id() ? $this->get_variation_id() : $this->get_product_id() |
193
|
|
|
) |
194
|
|
|
); |
195
|
|
|
|
196
|
|
|
foreach ( $download_ids as $download_id ) { |
197
|
|
|
if ( $product->has_file( $download_id ) ) { |
198
|
|
|
$files[ $download_id ] = $product->get_file( $download_id ); |
199
|
|
|
$files[ $download_id ]['download_url'] = $this->get_item_download_url( $download_id ); |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return apply_filters( 'woocommerce_get_item_downloads', $files, $this, $order ); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Get tax status. |
209
|
|
|
* @return string |
210
|
|
|
*/ |
211
|
|
|
public function get_tax_status() { |
212
|
|
|
$product = $this->get_product(); |
213
|
|
|
return $product ? $product->get_tax_status() : 'taxable'; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Set meta data for backordered products. |
218
|
|
|
*/ |
219
|
|
|
public function set_backorder_meta() { |
220
|
|
|
if ( $this->get_product()->backorders_require_notification() && $this->get_product()->is_on_backorder( $this->get_quantity() ) ) { |
221
|
|
|
$this->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $this->get_quantity() - max( 0, $this->get_product()->get_total_stock() ), true ); |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/* |
226
|
|
|
|-------------------------------------------------------------------------- |
227
|
|
|
| Setters |
228
|
|
|
|-------------------------------------------------------------------------- |
229
|
|
|
*/ |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Set quantity. |
233
|
|
|
* @param int $value |
234
|
|
|
*/ |
235
|
|
|
public function set_quantity( $value ) { |
236
|
|
|
if ( 0 >= $value ) { |
|
|
|
|
237
|
|
|
//$this->throw_exception( __METHOD__, 'Quantity must be positive' ); |
|
|
|
|
238
|
|
|
} |
239
|
|
|
$this->_data['quantity'] = wc_stock_amount( $value ); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Set tax class. |
244
|
|
|
* @param string $value |
245
|
|
|
*/ |
246
|
|
|
public function set_tax_class( $value ) { |
247
|
|
|
if ( $value && ! in_array( $value, WC_Tax::get_tax_classes() ) ) { |
|
|
|
|
248
|
|
|
//$this->throw_exception( __METHOD__, 'Invalid tax class' ); |
|
|
|
|
249
|
|
|
} |
250
|
|
|
$this->_data['tax_class'] = $value; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Set Product ID |
255
|
|
|
* @param int $value |
256
|
|
|
*/ |
257
|
|
|
public function set_product_id( $value ) { |
258
|
|
|
$this->_data['product_id'] = absint( $value ); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Set variation ID. |
263
|
|
|
* @param int $value |
264
|
|
|
*/ |
265
|
|
|
public function set_variation_id( $value ) { |
266
|
|
|
$this->_data['variation_id'] = absint( $value ); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Line subtotal (before discounts). |
271
|
|
|
* @param string $value |
272
|
|
|
*/ |
273
|
|
|
public function set_subtotal( $value ) { |
274
|
|
|
$this->_data['subtotal'] = wc_format_decimal( $value ); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Line total (after discounts). |
279
|
|
|
* @param string $value |
280
|
|
|
*/ |
281
|
|
|
public function set_total( $value ) { |
282
|
|
|
$this->_data['total'] = wc_format_decimal( $value ); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Line subtotal tax (before discounts). |
287
|
|
|
* @param string $value |
288
|
|
|
*/ |
289
|
|
|
public function set_subtotal_tax( $value ) { |
290
|
|
|
$this->_data['subtotal_tax'] = wc_format_decimal( $value ); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Line total tax (after discounts). |
295
|
|
|
* @param string $value |
296
|
|
|
*/ |
297
|
|
|
public function set_total_tax( $value ) { |
298
|
|
|
$this->_data['total_tax'] = wc_format_decimal( $value ); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Set line taxes. |
303
|
|
|
* @param array $raw_tax_data |
304
|
|
|
*/ |
305
|
|
|
public function set_taxes( $raw_tax_data ) { |
306
|
|
|
$raw_tax_data = maybe_unserialize( $raw_tax_data ); |
307
|
|
|
$tax_data = array( |
308
|
|
|
'total' => array(), |
309
|
|
|
'subtotal' => array() |
310
|
|
|
); |
311
|
|
|
if ( ! empty( $raw_tax_data['total'] ) && ! empty( $raw_tax_data['subtotal'] ) ) { |
312
|
|
|
$tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] ); |
313
|
|
|
$tax_data['subtotal'] = array_map( 'wc_format_decimal', $raw_tax_data['subtotal'] ); |
314
|
|
|
} |
315
|
|
|
$this->_data['taxes'] = $tax_data; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Set variation data (stored as meta data - write only). |
320
|
|
|
* @param array $data Key/Value pairs |
321
|
|
|
*/ |
322
|
|
|
public function set_variation( $data ) { |
323
|
|
|
foreach ( $data as $key => $value ) { |
324
|
|
|
$this->add_meta_data( str_replace( 'attribute_', '', $key ), $value, true ); |
325
|
|
|
} |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* Set properties based on passed in product object. |
330
|
|
|
* @param WC_Product $product |
331
|
|
|
*/ |
332
|
|
|
public function set_product( $product ) { |
333
|
|
|
if ( ! is_a( $product, 'WC_Product' ) ) { |
|
|
|
|
334
|
|
|
//$this->throw_exception( __METHOD__, 'Invalid product' ); |
|
|
|
|
335
|
|
|
} |
336
|
|
|
$this->set_product_id( $product->get_id() ); |
337
|
|
|
$this->set_name( $product->get_title() ); |
338
|
|
|
$this->set_tax_class( $product->get_tax_class() ); |
339
|
|
|
$this->set_variation_id( is_callable( array( $product, 'get_variation_id' ) ) ? $product->get_variation_id() : 0 ); |
|
|
|
|
340
|
|
|
$this->set_variation( is_callable( array( $product, 'get_variation_attributes' ) ) ? $product->get_variation_attributes() : array() ); |
|
|
|
|
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/* |
344
|
|
|
|-------------------------------------------------------------------------- |
345
|
|
|
| Getters |
346
|
|
|
|-------------------------------------------------------------------------- |
347
|
|
|
*/ |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Get order item type. |
351
|
|
|
* @return string |
352
|
|
|
*/ |
353
|
|
|
public function get_type() { |
354
|
|
|
return 'line_item'; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Get product ID. |
359
|
|
|
* @return int |
360
|
|
|
*/ |
361
|
|
|
public function get_product_id() { |
362
|
|
|
return absint( $this->_data['product_id'] ); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* Get variation ID. |
367
|
|
|
* @return int |
368
|
|
|
*/ |
369
|
|
|
public function get_variation_id() { |
370
|
|
|
return absint( $this->_data['variation_id'] ); |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* Get quantity. |
375
|
|
|
* @return int |
376
|
|
|
*/ |
377
|
|
|
public function get_quantity() { |
378
|
|
|
return wc_stock_amount( $this->_data['quantity'] ); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
/** |
382
|
|
|
* Get tax class. |
383
|
|
|
* @return string |
384
|
|
|
*/ |
385
|
|
|
public function get_tax_class() { |
386
|
|
|
return $this->_data['tax_class']; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* Get subtotal. |
391
|
|
|
* @return string |
392
|
|
|
*/ |
393
|
|
|
public function get_subtotal() { |
394
|
|
|
return wc_format_decimal( $this->_data['subtotal'] ); |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* Get subtotal tax. |
399
|
|
|
* @return string |
400
|
|
|
*/ |
401
|
|
|
public function get_subtotal_tax() { |
402
|
|
|
return wc_format_decimal( $this->_data['subtotal_tax'] ); |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* Get total. |
407
|
|
|
* @return string |
408
|
|
|
*/ |
409
|
|
|
public function get_total() { |
410
|
|
|
return wc_format_decimal( $this->_data['total'] ); |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* Get total tax. |
415
|
|
|
* @return string |
416
|
|
|
*/ |
417
|
|
|
public function get_total_tax() { |
418
|
|
|
return wc_format_decimal( $this->_data['total_tax'] ); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Get fee taxes. |
423
|
|
|
* @return array |
424
|
|
|
*/ |
425
|
|
|
public function get_taxes() { |
426
|
|
|
return $this->_data['taxes']; |
427
|
|
|
} |
428
|
|
|
} |
429
|
|
|
|
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.