-
Notifications
You must be signed in to change notification settings - Fork 0
/
commerce_physical.module
454 lines (383 loc) · 15.7 KB
/
commerce_physical.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
<?php
/**
* @file
* API for working with physical product types in Backdrop Commerce.
*/
/**
* Determines the weight field to use for a given entity.
*
* @param string $entity_type
* The type of entity passed to the function.
* @param object $entity
* The actual entity whose weight field name should be determined.
*
* @return string
* The name of the field to use on the entity to find a weight value or NULL
* if none was found.
*/
function commerce_physical_entity_weight_field_name($entity_type, $entity) {
$field_name = commerce_physical_entity_field_name($entity_type, $entity, 'physical_weight');
// Allow other modules to specify a different field name.
backdrop_alter('commerce_physical_entity_weight_field_name', $field_name, $entity_type, $entity);
return $field_name;
}
/**
* Determines the dimensions field to use for a given entity.
*
* @param string $entity_type
* The type of entity passed to the function.
* @param object $entity
* The actual entity whose weight field name should be determined.
*
* @return string
* The name of the field to use on the entity to find a weight value or NULL
* if none was found.
*/
function commerce_physical_entity_dimensions_field_name($entity_type, $entity) {
$field_name = commerce_physical_entity_field_name($entity_type, $entity, 'physical_dimensions');
// Allow other modules to specify a different field name.
backdrop_alter('commerce_physical_entity_dimensions_field_name', $field_name, $entity_type, $entity);
return $field_name;
}
/**
* Determines the field of a certain type to use for a given entity.
*
* @param string $entity_type
* The type of entity passed to the function.
* @param object $entity
* The actual entity whose weight field name should be determined.
* @param string $field_type
* The field type to use.
*
* @return string
* The name of the field to use on the entity to find a weight value or NULL
* if none was found.
*/
function commerce_physical_entity_field_name($entity_type, $entity, $field_type) {
$bundle = field_extract_bundle($entity_type, $entity);
$field_name = NULL;
// Look for the first field available for the entity.
foreach (field_info_instances($entity_type, $bundle) as $instance_name => $instance) {
// Load the field info for the current instance.
$field = field_info_field($instance['field_name']);
// If it's of the proper type...
if ($field['type'] == $field_type) {
// Save its name and exit the loop.
$field_name = $instance_name;
break;
}
}
return $field_name;
}
/**
* Determines the weight to use for a product line item on an order.
*
* @param commerce_line_item $line_item
* A product line item whose weight should be determined.
*
* @return array
* A weight field value array representing the weight of the product line item
* or NULL if none was found.
*/
function commerce_physical_product_line_item_weight($line_item) {
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
$weight = NULL;
// If the line item references a valid product...
if (!empty($line_item_wrapper->commerce_product)) {
$product = $line_item_wrapper->commerce_product->value();
if (!empty($product)) {
// If the product has a valid weight field...
$field_name = commerce_physical_entity_weight_field_name('commerce_product', $product);
if (!empty($field_name) && !empty($product->{$field_name})) {
// Extract the weight value from the product.
$product_wrapper = entity_metadata_wrapper('commerce_product', $product);
$weight = $product_wrapper->{$field_name}->value();
// Multiply the weight value by the quantity of the line item.
$weight['weight'] *= $line_item->quantity;
}
}
}
// Allow other modules to alter the weight if necessary.
backdrop_alter('commerce_physical_product_line_item_weight', $weight, $line_item);
return $weight;
}
/**
* Determines the dimensions of a product line item on an order.
*
* @param commerce_line_item $line_item
* A product line item whose dimensions should be determined.
*
* @return array
* An array of dimensions field value arrays. There'll be one entry in the
* array per product, with the entry being an array of that product's
* dimensions. If this line item contains no products with dimensions, an
* empty array will be returned.
*/
function commerce_physical_product_line_item_dimensions($line_item) {
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
$dimensions = array();
// If the line item references a valid product...
if (!empty($line_item_wrapper->commerce_product)) {
$product = $line_item_wrapper->commerce_product->value();
if (!empty($product)) {
// If the product has a valid dimensions field...
$field_name = commerce_physical_entity_dimensions_field_name('commerce_product', $product);
if (!empty($field_name) && !empty($product->{$field_name})) {
$product_wrapper = entity_metadata_wrapper('commerce_product', $product);
// Add dimension values per product in the line item.
for ($i = 0; $i < $line_item_wrapper->quantity->value(); $i++) {
$dimensions[] = $product_wrapper->{$field_name}->value();
}
}
}
}
// Allow other modules to alter the weight if necessary.
backdrop_alter('commerce_physical_product_line_item_dimensions', $dimensions, $line_item);
return $dimensions;
}
/**
* Determines the weight of an entire order.
*
* @param commerce_order $order
* The order object whose weight value should be calculated.
* @param string $unit
* The unit of measurement to use for the returned weight of the order.
*
* @return array
* A weight field value array representing the total weight of the order using
* the specified unit of measurement or NULL if no weight could be determined.
*/
function commerce_physical_order_weight($order, $unit = 'lb') {
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$weight = NULL;
// Loop over each line item on the order.
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
// Get the weight value of product line items.
if (in_array($line_item_wrapper->getBundle(), commerce_product_line_item_types())) {
$line_item_weight = commerce_physical_product_line_item_weight($line_item_wrapper->value());
// Add it to the running total converting it to the required weight unit.
if (!empty($line_item_weight['weight'])) {
$converted_weight = physical_weight_convert($line_item_weight, $unit);
if (empty($weight['weight'])) {
$weight = $converted_weight;
}
else {
$weight['weight'] += $converted_weight['weight'];
}
}
}
}
// Allow other modules to alter the weight if necessary.
backdrop_alter('commerce_physical_order_weight', $weight, $order, $unit);
return $weight;
}
/**
* Determines the volume of an entire order.
*
* @param commerce_order $order
* The order object whose volume should be calculated.
* @param string $unit
* The unit of measurement to convert dimensions to before calculating the
* volume of the order in the related cubic unit.
*
* @return array
* A volume value array with keys representing the total 'volume' of the order
* in the 'unit' specified or NULL if no volume could be determined.
*/
function commerce_physical_order_volume($order, $unit = 'in') {
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$volume = NULL;
// Loop over each line item on the order.
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
// Get the dimensions value of product line items.
if (in_array($line_item_wrapper->type->value(), commerce_product_line_item_types())) {
$line_item_dimensions = commerce_physical_product_line_item_dimensions($line_item_wrapper->value());
// Each product in a line item has the same dimensions, so we index into
// only the first product.
$line_item_dimensions = reset($line_item_dimensions);
// Add it to the running total converting it to the required weight unit.
if (!physical_field_is_empty($line_item_dimensions, array('type' => 'physical_dimensions'))) {
$converted_dimensions = physical_dimensions_convert($line_item_dimensions, $unit);
$converted_dimensions['volume'] = $converted_dimensions['width'] * $converted_dimensions['length'] * $converted_dimensions['height'] * $line_item_wrapper->quantity->value();
if (empty($volume['volume'])) {
// Create a volume value array using the converted unit type.
$volume = array(
'volume' => $converted_dimensions['volume'],
'unit' => $unit,
);
}
else {
$volume['volume'] += $converted_dimensions['volume'];
}
}
}
}
// Allow other modules to alter the volume if necessary.
backdrop_alter('commerce_physical_order_volume', $volume, $order, $unit);
return $volume;
}
/**
* Determines the dimensions of each product in an entire order.
*
* Other code can then use this data to figure out things like what the maximum
* dimensions of any product in the order is, or what size shipping container
* everything will fit into.
*
* @param commerce_order $order
* The order object whose dimensions should be returned.
* @param string $unit
* The unit of measurement to use for the returned dimensions.
*
* @return array
* An array of dimension arrays. One per product in the order.
* weight field value array representing the total weight of the order using
* the specified unit of measurement or NULL if no weight could be determined.
*/
function commerce_physical_order_dimensions($order, $unit = 'cm') {
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$order_dimensions = array();
// Loop over each line item on the order.
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
// Get the weight value of product line items.
if (in_array($line_item_wrapper->type->value(), commerce_product_line_item_types())) {
$line_item_dimensions = commerce_physical_product_line_item_dimensions($line_item_wrapper->value());
$order_dimensions = array_merge($order_dimensions, $line_item_dimensions);
}
}
// Now ensure that all dimensions supplied are in the requested units.
foreach ($order_dimensions as $key => $dimensions) {
$order_dimensions[$key] = physical_dimensions_convert($dimensions, $unit);
}
// Allow other modules to alter the weight if necessary.
backdrop_alter('commerce_physical_order_dimensions', $order_dimensions, $order, $unit);
return $order_dimensions;
}
/**
* Determines whether or not a line item should be considered shippable.
*
* @param commerce_line_item $line_item
* The line item object whose shippability is being determined.
*
* @return bool
* Boolean indicating whether or not the given line item represents something
* shippable; defaults to FALSE unless the line item represents a product line
* item with a discernible weight.
*/
function commerce_physical_line_item_shippable($line_item) {
$shippable = FALSE;
// If this is a product line item...
if (in_array($line_item->type, commerce_product_line_item_types())) {
// Mark this line item as shippable if we can determine its weight.
$weight = commerce_physical_product_line_item_weight($line_item);
if (!empty($weight)) {
$shippable = TRUE;
}
}
// Allow other modules to alter the shippability of the line item.
backdrop_alter('commerce_physical_line_item_shippable', $shippable, $line_item);
return $shippable;
}
/**
* Determines whether or not an order should be considered shippable.
*
* @param commerce_order $order
* The order object whose shippability should be determined.
*
* @return bool
* Boolean indicating whether or not the given order is shippable; defaults to
* FALSE unless any line item on the order is determined to be shippable.
*/
function commerce_physical_order_shippable($order) {
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$shippable = FALSE;
// Loop over all the line items on the order.
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
// Mark the order as shippable if the current line item is determined to be
// shippable.
if (commerce_physical_line_item_shippable($line_item_wrapper->value())) {
$shippable = TRUE;
}
}
// Allow other modules to alter the shippability of the line item.
backdrop_alter('commerce_physical_order_shippable', $shippable, $order);
return $shippable;
}
/**
* Name of the shipping customer profile reference field for the given order.
*
* @param commerce_order $order
* The order whose shipping customer profile reference field name should be
* determined.
*
* @return string
* The name of the field to use on the order to find shipping information or
* NULL if none was found; defaults to commerce_customer_shipping if available
* or another customer profile reference if not (preferring profiles other
* than the default billing profile if available).
*/
function commerce_physical_order_shipping_field_name($order) {
$field_name = NULL;
if (!empty($order->commerce_customer_shipping)) {
$field_name = 'commerce_customer_shipping';
}
else {
// Look for customer profile references fields available for the order.
foreach (field_info_instances('commerce_order', $order->type) as $instance_name => $instance) {
// Load the field info for the current instance.
$field = field_info_field($instance['field_name']);
// If it's of the proper type...
if ($field['type'] == 'commerce_customer_profile_reference') {
// Save its name and exit the loop if it isn't the billing profile.
$field_name = $instance_name;
if ($field_name != 'commerce_customer_billing') {
break;
}
}
}
}
// Allow other modules to specify a different field name.
backdrop_alter('commerce_physical_order_shipping_field_name', $field_name, $order);
return $field_name;
}
/**
* Determines the name of the phone number field of a customer profile.
*
* @param commerce_cutomer_profile $profile
* The customer profile whose phone number field name should be determined.
*
* @return string
* The name of the field to use on the customer profile to find a phone number
* or NULL if none was found. Defaults to field_phone or field_phone_number if
* available; otherwise it's up to you to alter it in a custom module since we
* don't want to depend on an actual phone number field.
*/
function commerce_physical_customer_profile_phone_number_field_name($profile) {
if (!empty($profile->field_phone)) {
$field_name = 'field_phone';
}
elseif (!empty($profile->field_phone_number)) {
$field_name = 'field_phone_number';
}
else {
$field_name = '';
}
// Allow other modules to specify a different field name.
backdrop_alter('commerce_physical_customer_profile_phone_number_field_name', $field_name, $profile);
return $field_name;
}
/**
* Whether or not a shipping customer profile is a residential address.
*
* @param commerce_customer_profile $profile
* The customer profile whose residential status should be determined.
*
* @return bool
* Boolean indicating whether or not the given profile has a residential
* address; defaults to TRUE unless otherwise determined by a custom module.
*/
function commerce_physical_customer_profile_residential($profile) {
$residential = TRUE;
// Allow other modules to determine if the profile has a residential address.
backdrop_alter('commerce_physical_customer_profile_residential', $residential, $profile);
return $residential;
}