Completed
Branch BUG/11126/attendee-mover-tax (6b5324)
by
unknown
94:01 queued 81:15
created
core/helpers/EEH_Line_Item.helper.php 3 patches
Doc Comments   +2 added lines, -1 removed lines patch added patch discarded remove patch
@@ -874,7 +874,7 @@  discard block
 block discarded – undo
874 874
      * Deletes ALL children of the passed line item
875 875
      *
876 876
      * @param EE_Line_Item $parent_line_item
877
-     * @return bool
877
+     * @return integer
878 878
      * @throws \EE_Error
879 879
      */
880 880
     public static function delete_all_child_items(EE_Line_Item $parent_line_item)
@@ -1138,6 +1138,7 @@  discard block
 block discarded – undo
1138 1138
      * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1139 1139
      * @param string $line_item_type one of the EEM_Line_Item constants
1140 1140
      * @param string | NULL $obj_type object model class name (minus prefix) or NULL to ignore object type when searching
1141
+     * @param string $obj_type
1141 1142
      * @return EE_Line_Item[]
1142 1143
      */
1143 1144
     protected static function _get_descendants_by_type_and_object_type(
Please login to merge, or discard this patch.
Indentation   +1698 added lines, -1698 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (!defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 /**
@@ -23,1703 +23,1703 @@  discard block
 block discarded – undo
23 23
 class EEH_Line_Item
24 24
 {
25 25
 
26
-    //other functions: cancel ticket purchase
27
-    //delete ticket purchase
28
-    //add promotion
29
-
30
-
31
-    /**
32
-     * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
33
-     * Does NOT automatically re-calculate the line item totals or update the related transaction.
34
-     * You should call recalculate_total_including_taxes() on the grant total line item after this
35
-     * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
36
-     * to keep the registration final prices in-sync with the transaction's total.
37
-     *
38
-     * @param EE_Line_Item $parent_line_item
39
-     * @param string $name
40
-     * @param float $unit_price
41
-     * @param string $description
42
-     * @param int $quantity
43
-     * @param boolean $taxable
44
-     * @param boolean $code if set to a value, ensures there is only one line item with that code
45
-     * @return boolean success
46
-     * @throws \EE_Error
47
-     */
48
-    public static function add_unrelated_item(EE_Line_Item $parent_line_item, $name, $unit_price, $description = '', $quantity = 1, $taxable = FALSE, $code = NULL)
49
-    {
50
-        $items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
51
-        $line_item = EE_Line_Item::new_instance(array(
52
-            'LIN_name' => $name,
53
-            'LIN_desc' => $description,
54
-            'LIN_unit_price' => $unit_price,
55
-            'LIN_quantity' => $quantity,
56
-            'LIN_percent' => null,
57
-            'LIN_is_taxable' => $taxable,
58
-            'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0,
59
-            'LIN_total' => (float)$unit_price * (int)$quantity,
60
-            'LIN_type' => EEM_Line_Item::type_line_item,
61
-            'LIN_code' => $code,
62
-        ));
63
-        $line_item = apply_filters(
64
-            'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
65
-            $line_item,
66
-            $parent_line_item
67
-        );
68
-        return self::add_item($parent_line_item, $line_item);
69
-    }
70
-
71
-
72
-    /**
73
-     * Adds a simple item ( unrelated to any other model object) to the total line item,
74
-     * in the correct spot in the line item tree. Automatically
75
-     * re-calculates the line item totals and updates the related transaction. But
76
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
77
-     * should probably change because of this).
78
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
79
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
80
-     *
81
-     * @param EE_Line_Item $parent_line_item
82
-     * @param string $name
83
-     * @param float $percentage_amount
84
-     * @param string $description
85
-     * @param boolean $taxable
86
-     * @return boolean success
87
-     * @throws \EE_Error
88
-     */
89
-    public static function add_percentage_based_item(EE_Line_Item $parent_line_item, $name, $percentage_amount, $description = '', $taxable = FALSE)
90
-    {
91
-        $line_item = EE_Line_Item::new_instance(array(
92
-            'LIN_name' => $name,
93
-            'LIN_desc' => $description,
94
-            'LIN_unit_price' => 0,
95
-            'LIN_percent' => $percentage_amount,
96
-            'LIN_quantity' => 1,
97
-            'LIN_is_taxable' => $taxable,
98
-            'LIN_total' => (float)($percentage_amount * ($parent_line_item->total() / 100)),
99
-            'LIN_type' => EEM_Line_Item::type_line_item,
100
-            'LIN_parent' => $parent_line_item->ID()
101
-        ));
102
-        $line_item = apply_filters(
103
-            'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
104
-            $line_item
105
-        );
106
-        return $parent_line_item->add_child_line_item($line_item, false);
107
-    }
108
-
109
-
110
-    /**
111
-     * Returns the new line item created by adding a purchase of the ticket
112
-     * ensures that ticket line item is saved, and that cart total has been recalculated.
113
-     * If this ticket has already been purchased, just increments its count.
114
-     * Automatically re-calculates the line item totals and updates the related transaction. But
115
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
116
-     * should probably change because of this).
117
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
118
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
119
-     *
120
-     * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
121
-     * @param EE_Ticket $ticket
122
-     * @param int $qty
123
-     * @return \EE_Line_Item
124
-     * @throws \EE_Error
125
-     */
126
-    public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
127
-    {
128
-        if (!$total_line_item instanceof EE_Line_Item || !$total_line_item->is_total()) {
129
-            throw new EE_Error(sprintf(__('A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', 'event_espresso'), $ticket->ID(), $total_line_item->ID()));
130
-        }
131
-        // either increment the qty for an existing ticket
132
-        $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
133
-        // or add a new one
134
-        if (!$line_item instanceof EE_Line_Item) {
135
-            $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
136
-        }
137
-        $total_line_item->recalculate_total_including_taxes();
138
-        return $line_item;
139
-    }
140
-
141
-
142
-    /**
143
-     * Returns the new line item created by adding a purchase of the ticket
144
-     * @param \EE_Line_Item $total_line_item
145
-     * @param EE_Ticket $ticket
146
-     * @param int $qty
147
-     * @return \EE_Line_Item
148
-     * @throws \EE_Error
149
-     */
150
-    public static function increment_ticket_qty_if_already_in_cart(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
151
-    {
152
-        $line_item = null;
153
-        if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
154
-            $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
155
-            foreach ((array)$ticket_line_items as $ticket_line_item) {
156
-                if (
157
-                    $ticket_line_item instanceof EE_Line_Item
158
-                    && (int)$ticket_line_item->OBJ_ID() === (int)$ticket->ID()
159
-                ) {
160
-                    $line_item = $ticket_line_item;
161
-                    break;
162
-                }
163
-            }
164
-        }
165
-        if ($line_item instanceof EE_Line_Item) {
166
-            EEH_Line_Item::increment_quantity($line_item, $qty);
167
-            return $line_item;
168
-        }
169
-        return null;
170
-    }
171
-
172
-
173
-    /**
174
-     * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
175
-     * Does NOT save or recalculate other line items totals
176
-     *
177
-     * @param EE_Line_Item $line_item
178
-     * @param int $qty
179
-     * @return void
180
-     * @throws \EE_Error
181
-     */
182
-    public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
183
-    {
184
-        if (!$line_item->is_percent()) {
185
-            $qty += $line_item->quantity();
186
-            $line_item->set_quantity($qty);
187
-            $line_item->set_total($line_item->unit_price() * $qty);
188
-            $line_item->save();
189
-        }
190
-        foreach ($line_item->children() as $child) {
191
-            if ($child->is_sub_line_item()) {
192
-                EEH_Line_Item::update_quantity($child, $qty);
193
-            }
194
-        }
195
-    }
196
-
197
-
198
-    /**
199
-     * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
200
-     * Does NOT save or recalculate other line items totals
201
-     *
202
-     * @param EE_Line_Item $line_item
203
-     * @param int $qty
204
-     * @return void
205
-     * @throws \EE_Error
206
-     */
207
-    public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
208
-    {
209
-        if (!$line_item->is_percent()) {
210
-            $qty = $line_item->quantity() - $qty;
211
-            $qty = max($qty, 0);
212
-            $line_item->set_quantity($qty);
213
-            $line_item->set_total($line_item->unit_price() * $qty);
214
-            $line_item->save();
215
-        }
216
-        foreach ($line_item->children() as $child) {
217
-            if ($child->is_sub_line_item()) {
218
-                EEH_Line_Item::update_quantity($child, $qty);
219
-            }
220
-        }
221
-    }
222
-
223
-
224
-    /**
225
-     * Updates the line item and its children's quantities to the specified number.
226
-     * Does NOT save them or recalculate totals.
227
-     *
228
-     * @param EE_Line_Item $line_item
229
-     * @param int $new_quantity
230
-     * @throws \EE_Error
231
-     */
232
-    public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
233
-    {
234
-        if (!$line_item->is_percent()) {
235
-            $line_item->set_quantity($new_quantity);
236
-            $line_item->set_total($line_item->unit_price() * $new_quantity);
237
-            $line_item->save();
238
-        }
239
-        foreach ($line_item->children() as $child) {
240
-            if ($child->is_sub_line_item()) {
241
-                EEH_Line_Item::update_quantity($child, $new_quantity);
242
-            }
243
-        }
244
-    }
245
-
246
-
247
-    /**
248
-     * Returns the new line item created by adding a purchase of the ticket
249
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
250
-     * @param EE_Ticket $ticket
251
-     * @param int $qty
252
-     * @return \EE_Line_Item
253
-     * @throws \EE_Error
254
-     */
255
-    public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
256
-    {
257
-        $datetimes = $ticket->datetimes();
258
-        $first_datetime = reset($datetimes);
259
-        if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
260
-            $first_datetime_name = $first_datetime->event()->name();
261
-        } else {
262
-            $first_datetime_name = __('Event', 'event_espresso');
263
-        }
264
-        $event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
265
-        // get event subtotal line
266
-        $events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
267
-        // add $ticket to cart
268
-        $line_item = EE_Line_Item::new_instance(array(
269
-            'LIN_name' => $ticket->name(),
270
-            'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
271
-            'LIN_unit_price' => $ticket->price(),
272
-            'LIN_quantity' => $qty,
273
-            'LIN_is_taxable' => $ticket->taxable(),
274
-            'LIN_order' => count($events_sub_total->children()),
275
-            'LIN_total' => $ticket->price() * $qty,
276
-            'LIN_type' => EEM_Line_Item::type_line_item,
277
-            'OBJ_ID' => $ticket->ID(),
278
-            'OBJ_type' => 'Ticket'
279
-        ));
280
-        $line_item = apply_filters(
281
-            'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
282
-            $line_item
283
-        );
284
-        $events_sub_total->add_child_line_item($line_item);
285
-        //now add the sub-line items
286
-        $running_total_for_ticket = 0;
287
-        foreach ($ticket->prices(array('order_by' => array('PRC_order' => 'ASC'))) as $price) {
288
-            $sign = $price->is_discount() ? -1 : 1;
289
-            $price_total = $price->is_percent()
290
-                ? $running_total_for_ticket * $price->amount() / 100
291
-                : $price->amount() * $qty;
292
-            $sub_line_item = EE_Line_Item::new_instance(array(
293
-                'LIN_name' => $price->name(),
294
-                'LIN_desc' => $price->desc(),
295
-                'LIN_quantity' => $price->is_percent() ? null : $qty,
296
-                'LIN_is_taxable' => false,
297
-                'LIN_order' => $price->order(),
298
-                'LIN_total' => $sign * $price_total,
299
-                'LIN_type' => EEM_Line_Item::type_sub_line_item,
300
-                'OBJ_ID' => $price->ID(),
301
-                'OBJ_type' => 'Price'
302
-            ));
303
-            $sub_line_item = apply_filters(
304
-                'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
305
-                $sub_line_item
306
-            );
307
-            if ($price->is_percent()) {
308
-                $sub_line_item->set_percent($sign * $price->amount());
309
-            } else {
310
-                $sub_line_item->set_unit_price($sign * $price->amount());
311
-            }
312
-            $running_total_for_ticket += $price_total;
313
-            $line_item->add_child_line_item($sub_line_item);
314
-        }
315
-        return $line_item;
316
-    }
317
-
318
-
319
-    /**
320
-     * Adds the specified item under the pre-tax-sub-total line item. Automatically
321
-     * re-calculates the line item totals and updates the related transaction. But
322
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
323
-     * should probably change because of this).
324
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
325
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
326
-     *
327
-     * @param EE_Line_Item $total_line_item
328
-     * @param EE_Line_Item $item to be added
329
-     * @return boolean
330
-     * @throws \EE_Error
331
-     */
332
-    public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item)
333
-    {
334
-        $pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
335
-        if ($pre_tax_subtotal instanceof EE_Line_Item) {
336
-            $success = $pre_tax_subtotal->add_child_line_item($item);
337
-        } else {
338
-            return FALSE;
339
-        }
340
-        $total_line_item->recalculate_total_including_taxes();
341
-        return $success;
342
-    }
343
-
344
-
345
-    /**
346
-     * cancels an existing ticket line item,
347
-     * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
348
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
349
-     *
350
-     * @param EE_Line_Item $ticket_line_item
351
-     * @param int $qty
352
-     * @return bool success
353
-     * @throws \EE_Error
354
-     */
355
-    public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
356
-    {
357
-        // validate incoming line_item
358
-        if ($ticket_line_item->OBJ_type() !== 'Ticket') {
359
-            throw new EE_Error(
360
-                sprintf(
361
-                    __('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
362
-                    $ticket_line_item->type()
363
-                )
364
-            );
365
-        }
366
-        if ($ticket_line_item->quantity() < $qty) {
367
-            throw new EE_Error(
368
-                sprintf(
369
-                    __('Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.', 'event_espresso'),
370
-                    $qty,
371
-                    $ticket_line_item->quantity()
372
-                )
373
-            );
374
-        }
375
-        // decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
376
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
377
-        foreach ($ticket_line_item->children() as $child_line_item) {
378
-            if (
379
-                $child_line_item->is_sub_line_item()
380
-                && !$child_line_item->is_percent()
381
-                && !$child_line_item->is_cancellation()
382
-            ) {
383
-                $child_line_item->set_quantity($child_line_item->quantity() - $qty);
384
-            }
385
-        }
386
-        // get cancellation sub line item
387
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
388
-            $ticket_line_item,
389
-            EEM_Line_Item::type_cancellation
390
-        );
391
-        $cancellation_line_item = reset($cancellation_line_item);
392
-        // verify that this ticket was indeed previously cancelled
393
-        if ($cancellation_line_item instanceof EE_Line_Item) {
394
-            // increment cancelled quantity
395
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
396
-        } else {
397
-            // create cancellation sub line item
398
-            $cancellation_line_item = EE_Line_Item::new_instance(array(
399
-                'LIN_name' => __('Cancellation', 'event_espresso'),
400
-                'LIN_desc' => sprintf(
401
-                    _x('Cancelled %1$s : %2$s', 'Cancelled Ticket Name : 2015-01-01 11:11', 'event_espresso'),
402
-                    $ticket_line_item->name(),
403
-                    current_time(get_option('date_format') . ' ' . get_option('time_format'))
404
-                ),
405
-                'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
406
-                'LIN_quantity' => $qty,
407
-                'LIN_is_taxable' => $ticket_line_item->is_taxable(),
408
-                'LIN_order' => count($ticket_line_item->children()),
409
-                'LIN_total' => 0, // $ticket_line_item->unit_price()
410
-                'LIN_type' => EEM_Line_Item::type_cancellation,
411
-            ));
412
-            $ticket_line_item->add_child_line_item($cancellation_line_item);
413
-        }
414
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
415
-            // decrement parent line item quantity
416
-            $event_line_item = $ticket_line_item->parent();
417
-            if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
418
-                $event_line_item->set_quantity($event_line_item->quantity() - $qty);
419
-                $event_line_item->save();
420
-            }
421
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
422
-            return true;
423
-        }
424
-        return false;
425
-    }
426
-
427
-
428
-    /**
429
-     * reinstates (un-cancels?) a previously canceled ticket line item,
430
-     * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
431
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
432
-     *
433
-     * @param EE_Line_Item $ticket_line_item
434
-     * @param int $qty
435
-     * @return bool success
436
-     * @throws \EE_Error
437
-     */
438
-    public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
439
-    {
440
-        // validate incoming line_item
441
-        if ($ticket_line_item->OBJ_type() !== 'Ticket') {
442
-            throw new EE_Error(
443
-                sprintf(
444
-                    __('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
445
-                    $ticket_line_item->type()
446
-                )
447
-            );
448
-        }
449
-        // get cancellation sub line item
450
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
451
-            $ticket_line_item,
452
-            EEM_Line_Item::type_cancellation
453
-        );
454
-        $cancellation_line_item = reset($cancellation_line_item);
455
-        // verify that this ticket was indeed previously cancelled
456
-        if (!$cancellation_line_item instanceof EE_Line_Item) {
457
-            return false;
458
-        }
459
-        if ($cancellation_line_item->quantity() > $qty) {
460
-            // decrement cancelled quantity
461
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
462
-        } else if ($cancellation_line_item->quantity() == $qty) {
463
-            // decrement cancelled quantity in case anyone still has the object kicking around
464
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
465
-            // delete because quantity will end up as 0
466
-            $cancellation_line_item->delete();
467
-            // and attempt to destroy the object,
468
-            // even though PHP won't actually destroy it until it needs the memory
469
-            unset($cancellation_line_item);
470
-        } else {
471
-            // what ?!?! negative quantity ?!?!
472
-            throw new EE_Error(
473
-                sprintf(
474
-                    __('Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
475
-                        'event_espresso'),
476
-                    $qty,
477
-                    $cancellation_line_item->quantity()
478
-                )
479
-            );
480
-        }
481
-        // increment ticket quantity
482
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
483
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
484
-            // increment parent line item quantity
485
-            $event_line_item = $ticket_line_item->parent();
486
-            if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
487
-                $event_line_item->set_quantity($event_line_item->quantity() + $qty);
488
-            }
489
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
490
-            return true;
491
-        }
492
-        return false;
493
-    }
494
-
495
-
496
-    /**
497
-     * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
498
-     * then EE_Line_Item::recalculate_total_including_taxes() on the result
499
-     *
500
-     * @param EE_Line_Item $line_item
501
-     * @return \EE_Line_Item
502
-     */
503
-    public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
504
-    {
505
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
506
-        return $grand_total_line_item->recalculate_total_including_taxes();
507
-    }
508
-
509
-
510
-    /**
511
-     * Gets the line item which contains the subtotal of all the items
512
-     *
513
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
514
-     * @return \EE_Line_Item
515
-     * @throws \EE_Error
516
-     */
517
-    public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
518
-    {
519
-        $pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
520
-        return $pre_tax_subtotal instanceof EE_Line_Item
521
-            ? $pre_tax_subtotal
522
-            : self::create_pre_tax_subtotal($total_line_item);
523
-    }
524
-
525
-
526
-    /**
527
-     * Gets the line item for the taxes subtotal
528
-     *
529
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
530
-     * @return \EE_Line_Item
531
-     * @throws \EE_Error
532
-     */
533
-    public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
534
-    {
535
-        $taxes = $total_line_item->get_child_line_item('taxes');
536
-        return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
537
-    }
538
-
539
-
540
-    /**
541
-     * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
542
-     *
543
-     * @param EE_Line_Item $line_item
544
-     * @param EE_Transaction $transaction
545
-     * @return void
546
-     * @throws \EE_Error
547
-     */
548
-    public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = NULL)
549
-    {
550
-        if ($transaction) {
551
-            /** @type EEM_Transaction $EEM_Transaction */
552
-            $EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
553
-            $TXN_ID = $EEM_Transaction->ensure_is_ID($transaction);
554
-            $line_item->set_TXN_ID($TXN_ID);
555
-        }
556
-    }
557
-
558
-
559
-    /**
560
-     * Creates a new default total line item for the transaction,
561
-     * and its tickets subtotal and taxes subtotal line items (and adds the
562
-     * existing taxes as children of the taxes subtotal line item)
563
-     *
564
-     * @param EE_Transaction $transaction
565
-     * @return \EE_Line_Item of type total
566
-     * @throws \EE_Error
567
-     */
568
-    public static function create_total_line_item($transaction = NULL)
569
-    {
570
-        $total_line_item = EE_Line_Item::new_instance(array(
571
-            'LIN_code' => 'total',
572
-            'LIN_name' => __('Grand Total', 'event_espresso'),
573
-            'LIN_type' => EEM_Line_Item::type_total,
574
-            'OBJ_type' => 'Transaction'
575
-        ));
576
-        $total_line_item = apply_filters(
577
-            'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
578
-            $total_line_item
579
-        );
580
-        self::set_TXN_ID($total_line_item, $transaction);
581
-        self::create_pre_tax_subtotal($total_line_item, $transaction);
582
-        self::create_taxes_subtotal($total_line_item, $transaction);
583
-        return $total_line_item;
584
-    }
585
-
586
-
587
-    /**
588
-     * Creates a default items subtotal line item
589
-     *
590
-     * @param EE_Line_Item $total_line_item
591
-     * @param EE_Transaction $transaction
592
-     * @return EE_Line_Item
593
-     * @throws \EE_Error
594
-     */
595
-    protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
596
-    {
597
-        $pre_tax_line_item = EE_Line_Item::new_instance(array(
598
-            'LIN_code' => 'pre-tax-subtotal',
599
-            'LIN_name' => __('Pre-Tax Subtotal', 'event_espresso'),
600
-            'LIN_type' => EEM_Line_Item::type_sub_total
601
-        ));
602
-        $pre_tax_line_item = apply_filters(
603
-            'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
604
-            $pre_tax_line_item
605
-        );
606
-        self::set_TXN_ID($pre_tax_line_item, $transaction);
607
-        $total_line_item->add_child_line_item($pre_tax_line_item);
608
-        self::create_event_subtotal($pre_tax_line_item, $transaction);
609
-        return $pre_tax_line_item;
610
-    }
611
-
612
-
613
-    /**
614
-     * Creates a line item for the taxes subtotal and finds all the tax prices
615
-     * and applies taxes to it
616
-     *
617
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
618
-     * @param EE_Transaction $transaction
619
-     * @return EE_Line_Item
620
-     * @throws \EE_Error
621
-     */
622
-    protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
623
-    {
624
-        $tax_line_item = EE_Line_Item::new_instance(array(
625
-            'LIN_code' => 'taxes',
626
-            'LIN_name' => __('Taxes', 'event_espresso'),
627
-            'LIN_type' => EEM_Line_Item::type_tax_sub_total,
628
-            'LIN_order' => 1000,//this should always come last
629
-        ));
630
-        $tax_line_item = apply_filters(
631
-            'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
632
-            $tax_line_item
633
-        );
634
-        self::set_TXN_ID($tax_line_item, $transaction);
635
-        $total_line_item->add_child_line_item($tax_line_item);
636
-        //and lastly, add the actual taxes
637
-        self::apply_taxes($total_line_item);
638
-        return $tax_line_item;
639
-    }
640
-
641
-
642
-    /**
643
-     * Creates a default items subtotal line item
644
-     *
645
-     * @param EE_Line_Item $pre_tax_line_item
646
-     * @param EE_Transaction $transaction
647
-     * @param EE_Event $event
648
-     * @return EE_Line_Item
649
-     * @throws \EE_Error
650
-     */
651
-    public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = NULL, $event = NULL)
652
-    {
653
-        $event_line_item = EE_Line_Item::new_instance(array(
654
-            'LIN_code' => self::get_event_code($event),
655
-            'LIN_name' => self::get_event_name($event),
656
-            'LIN_desc' => self::get_event_desc($event),
657
-            'LIN_type' => EEM_Line_Item::type_sub_total,
658
-            'OBJ_type' => 'Event',
659
-            'OBJ_ID' => $event instanceof EE_Event ? $event->ID() : 0
660
-        ));
661
-        $event_line_item = apply_filters(
662
-            'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
663
-            $event_line_item
664
-        );
665
-        self::set_TXN_ID($event_line_item, $transaction);
666
-        $pre_tax_line_item->add_child_line_item($event_line_item);
667
-        return $event_line_item;
668
-    }
669
-
670
-
671
-    /**
672
-     * Gets what the event ticket's code SHOULD be
673
-     *
674
-     * @param EE_Event $event
675
-     * @return string
676
-     * @throws \EE_Error
677
-     */
678
-    public static function get_event_code($event)
679
-    {
680
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
681
-    }
682
-
683
-    /**
684
-     * Gets the event name
685
-     * @param EE_Event $event
686
-     * @return string
687
-     */
688
-    public static function get_event_name($event)
689
-    {
690
-        return $event instanceof EE_Event ? $event->name() : __('Event', 'event_espresso');
691
-    }
692
-
693
-    /**
694
-     * Gets the event excerpt
695
-     * @param EE_Event $event
696
-     * @return string
697
-     */
698
-    public static function get_event_desc($event)
699
-    {
700
-        return $event instanceof EE_Event ? $event->short_description() : '';
701
-    }
702
-
703
-    /**
704
-     * Given the grand total line item and a ticket, finds the event sub-total
705
-     * line item the ticket's purchase should be added onto
706
-     *
707
-     * @access public
708
-     * @param EE_Line_Item $grand_total the grand total line item
709
-     * @param EE_Ticket $ticket
710
-     * @throws \EE_Error
711
-     * @return EE_Line_Item
712
-     */
713
-    public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
714
-    {
715
-        $first_datetime = $ticket->first_datetime();
716
-        if (!$first_datetime instanceof EE_Datetime) {
717
-            throw new EE_Error(
718
-                sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())
719
-            );
720
-        }
721
-        $event = $first_datetime->event();
722
-        if (!$event instanceof EE_Event) {
723
-            throw new EE_Error(
724
-                sprintf(
725
-                    __('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'),
726
-                    $ticket->ID()
727
-                )
728
-            );
729
-        }
730
-        $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
731
-        if (!$events_sub_total instanceof EE_Line_Item) {
732
-            throw new EE_Error(
733
-                sprintf(
734
-                    __('There is no events sub-total for ticket %s on total line item %d', 'event_espresso'),
735
-                    $ticket->ID(),
736
-                    $grand_total->ID()
737
-                )
738
-            );
739
-        }
740
-        return $events_sub_total;
741
-    }
742
-
743
-
744
-    /**
745
-     * Gets the event line item
746
-     *
747
-     * @param EE_Line_Item $grand_total
748
-     * @param EE_Event $event
749
-     * @return EE_Line_Item for the event subtotal which is a child of $grand_total
750
-     * @throws \EE_Error
751
-     */
752
-    public static function get_event_line_item(EE_Line_Item $grand_total, $event)
753
-    {
754
-        /** @type EE_Event $event */
755
-        $event = EEM_Event::instance()->ensure_is_obj($event, true);
756
-        $event_line_item = NULL;
757
-        $found = false;
758
-        foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
759
-            // default event subtotal, we should only ever find this the first time this method is called
760
-            if (!$event_line_item->OBJ_ID()) {
761
-                // let's use this! but first... set the event details
762
-                EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
763
-                $found = true;
764
-                break;
765
-            } else if ($event_line_item->OBJ_ID() === $event->ID()) {
766
-                // found existing line item for this event in the cart, so break out of loop and use this one
767
-                $found = true;
768
-                break;
769
-            }
770
-        }
771
-        if (!$found) {
772
-            //there is no event sub-total yet, so add it
773
-            $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
774
-            // create a new "event" subtotal below that
775
-            $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
776
-            // and set the event details
777
-            EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
778
-        }
779
-        return $event_line_item;
780
-    }
781
-
782
-
783
-    /**
784
-     * Creates a default items subtotal line item
785
-     *
786
-     * @param EE_Line_Item $event_line_item
787
-     * @param EE_Event $event
788
-     * @param EE_Transaction $transaction
789
-     * @return EE_Line_Item
790
-     * @throws \EE_Error
791
-     */
792
-    public static function set_event_subtotal_details(
793
-        EE_Line_Item $event_line_item,
794
-        EE_Event $event,
795
-        $transaction = null
796
-    )
797
-    {
798
-        if ($event instanceof EE_Event) {
799
-            $event_line_item->set_code(self::get_event_code($event));
800
-            $event_line_item->set_name(self::get_event_name($event));
801
-            $event_line_item->set_desc(self::get_event_desc($event));
802
-            $event_line_item->set_OBJ_ID($event->ID());
803
-        }
804
-        self::set_TXN_ID($event_line_item, $transaction);
805
-    }
806
-
807
-
808
-    /**
809
-     * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
810
-     * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
811
-     * any old taxes are removed
812
-     *
813
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
814
-     * @throws \EE_Error
815
-     */
816
-    public static function apply_taxes(EE_Line_Item $total_line_item)
817
-    {
818
-        /** @type EEM_Price $EEM_Price */
819
-        $EEM_Price = EE_Registry::instance()->load_model('Price');
820
-        // get array of taxes via Price Model
821
-        $ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes();
822
-        ksort($ordered_taxes);
823
-        $taxes_line_item = self::get_taxes_subtotal($total_line_item);
824
-        //just to be safe, remove its old tax line items
825
-        $taxes_line_item->delete_children_line_items();
826
-        //loop thru taxes
827
-        foreach ($ordered_taxes as $order => $taxes) {
828
-            foreach ($taxes as $tax) {
829
-                if ($tax instanceof EE_Price) {
830
-                    $tax_line_item = EE_Line_Item::new_instance(
831
-                        array(
832
-                            'LIN_name' => $tax->name(),
833
-                            'LIN_desc' => $tax->desc(),
834
-                            'LIN_percent' => $tax->amount(),
835
-                            'LIN_is_taxable' => false,
836
-                            'LIN_order' => $order,
837
-                            'LIN_total' => 0,
838
-                            'LIN_type' => EEM_Line_Item::type_tax,
839
-                            'OBJ_type' => 'Price',
840
-                            'OBJ_ID' => $tax->ID()
841
-                        )
842
-                    );
843
-                    $tax_line_item = apply_filters(
844
-                        'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
845
-                        $tax_line_item
846
-                    );
847
-                    $taxes_line_item->add_child_line_item($tax_line_item);
848
-                }
849
-            }
850
-        }
851
-        $total_line_item->recalculate_total_including_taxes();
852
-    }
853
-
854
-
855
-    /**
856
-     * Ensures that taxes have been applied to the order, if not applies them.
857
-     * Returns the total amount of tax
858
-     *
859
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
860
-     * @return float
861
-     * @throws \EE_Error
862
-     */
863
-    public static function ensure_taxes_applied($total_line_item)
864
-    {
865
-        $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
866
-        if (!$taxes_subtotal->children()) {
867
-            self::apply_taxes($total_line_item);
868
-        }
869
-        return $taxes_subtotal->total();
870
-    }
871
-
872
-
873
-    /**
874
-     * Deletes ALL children of the passed line item
875
-     *
876
-     * @param EE_Line_Item $parent_line_item
877
-     * @return bool
878
-     * @throws \EE_Error
879
-     */
880
-    public static function delete_all_child_items(EE_Line_Item $parent_line_item)
881
-    {
882
-        $deleted = 0;
883
-        foreach ($parent_line_item->children() as $child_line_item) {
884
-            if ($child_line_item instanceof EE_Line_Item) {
885
-                $deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
886
-                if ($child_line_item->ID()) {
887
-                    $child_line_item->delete();
888
-                    unset($child_line_item);
889
-                } else {
890
-                    $parent_line_item->delete_child_line_item($child_line_item->code());
891
-                }
892
-                $deleted++;
893
-            }
894
-        }
895
-        return $deleted;
896
-    }
897
-
898
-
899
-    /**
900
-     * Deletes the line items as indicated by the line item code(s) provided,
901
-     * regardless of where they're found in the line item tree. Automatically
902
-     * re-calculates the line item totals and updates the related transaction. But
903
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
904
-     * should probably change because of this).
905
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
906
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
907
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
908
-     * @param array|bool|string $line_item_codes
909
-     * @return int number of items successfully removed
910
-     */
911
-    public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = FALSE)
912
-    {
913
-
914
-        if ($total_line_item->type() !== EEM_Line_Item::type_total) {
915
-            EE_Error::doing_it_wrong(
916
-                'EEH_Line_Item::delete_items',
917
-                __(
918
-                    'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
919
-                    'event_espresso'
920
-                ),
921
-                '4.6.18'
922
-            );
923
-        }
924
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
925
-
926
-        // check if only a single line_item_id was passed
927
-        if (!empty($line_item_codes) && !is_array($line_item_codes)) {
928
-            // place single line_item_id in an array to appear as multiple line_item_ids
929
-            $line_item_codes = array($line_item_codes);
930
-        }
931
-        $removals = 0;
932
-        // cycle thru line_item_ids
933
-        foreach ($line_item_codes as $line_item_id) {
934
-            $removals += $total_line_item->delete_child_line_item($line_item_id);
935
-        }
936
-
937
-        if ($removals > 0) {
938
-            $total_line_item->recalculate_taxes_and_tax_total();
939
-            return $removals;
940
-        } else {
941
-            return FALSE;
942
-        }
943
-    }
944
-
945
-
946
-    /**
947
-     * Overwrites the previous tax by clearing out the old taxes, and creates a new
948
-     * tax and updates the total line item accordingly
949
-     *
950
-     * @param EE_Line_Item $total_line_item
951
-     * @param float $amount
952
-     * @param string $name
953
-     * @param string $description
954
-     * @param string $code
955
-     * @param boolean $add_to_existing_line_item
956
-     *                          if true, and a duplicate line item with the same code is found,
957
-     *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
958
-     * @return EE_Line_Item the new tax line item created
959
-     * @throws \EE_Error
960
-     */
961
-    public static function set_total_tax_to(
962
-        EE_Line_Item $total_line_item,
963
-        $amount,
964
-        $name = null,
965
-        $description = null,
966
-        $code = null,
967
-        $add_to_existing_line_item = false
968
-    )
969
-    {
970
-        $tax_subtotal = self::get_taxes_subtotal($total_line_item);
971
-        $taxable_total = $total_line_item->taxable_total();
972
-
973
-        if ($add_to_existing_line_item) {
974
-            $new_tax = $tax_subtotal->get_child_line_item($code);
975
-            EEM_Line_Item::instance()->delete(
976
-                array(array('LIN_code' => array('!=', $code), 'LIN_parent' => $tax_subtotal->ID()))
977
-            );
978
-        } else {
979
-            $new_tax = null;
980
-            $tax_subtotal->delete_children_line_items();
981
-        }
982
-        if ($new_tax) {
983
-            $new_tax->set_total($new_tax->total() + $amount);
984
-            $new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
985
-        } else {
986
-            //no existing tax item. Create it
987
-            $new_tax = EE_Line_Item::new_instance(array(
988
-                'TXN_ID' => $total_line_item->TXN_ID(),
989
-                'LIN_name' => $name ? $name : __('Tax', 'event_espresso'),
990
-                'LIN_desc' => $description ? $description : '',
991
-                'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
992
-                'LIN_total' => $amount,
993
-                'LIN_parent' => $tax_subtotal->ID(),
994
-                'LIN_type' => EEM_Line_Item::type_tax,
995
-                'LIN_code' => $code
996
-            ));
997
-        }
998
-
999
-        $new_tax = apply_filters(
1000
-            'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
1001
-            $new_tax,
1002
-            $total_line_item
1003
-        );
1004
-        $new_tax->save();
1005
-        $tax_subtotal->set_total($new_tax->total());
1006
-        $tax_subtotal->save();
1007
-        $total_line_item->recalculate_total_including_taxes();
1008
-        return $new_tax;
1009
-    }
1010
-
1011
-
1012
-    /**
1013
-     * Makes all the line items which are children of $line_item taxable (or not).
1014
-     * Does NOT save the line items
1015
-     * @param EE_Line_Item $line_item
1016
-     * @param string $code_substring_for_whitelist if this string is part of the line item's code
1017
-     *  it will be whitelisted (ie, except from becoming taxable)
1018
-     * @param boolean $taxable
1019
-     */
1020
-    public static function set_line_items_taxable(
1021
-        EE_Line_Item $line_item,
1022
-        $taxable = true,
1023
-        $code_substring_for_whitelist = null
1024
-    )
1025
-    {
1026
-        $whitelisted = false;
1027
-        if ($code_substring_for_whitelist !== null) {
1028
-            $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false ? true : false;
1029
-        }
1030
-        if (!$whitelisted && $line_item->is_line_item()) {
1031
-            $line_item->set_is_taxable($taxable);
1032
-        }
1033
-        foreach ($line_item->children() as $child_line_item) {
1034
-            EEH_Line_Item::set_line_items_taxable($child_line_item, $taxable, $code_substring_for_whitelist);
1035
-        }
1036
-    }
1037
-
1038
-
1039
-    /**
1040
-     * Gets all descendants that are event subtotals
1041
-     *
1042
-     * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1043
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1044
-     * @return EE_Line_Item[]
1045
-     */
1046
-    public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1047
-    {
1048
-        return self::get_subtotals_of_object_type($parent_line_item, 'Event');
1049
-    }
1050
-
1051
-
1052
-    /**
1053
-     * Gets all descendants subtotals that match the supplied object type
1054
-     *
1055
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1056
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1057
-     * @param string $obj_type
1058
-     * @return EE_Line_Item[]
1059
-     */
1060
-    public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1061
-    {
1062
-        return self::_get_descendants_by_type_and_object_type(
1063
-            $parent_line_item,
1064
-            EEM_Line_Item::type_sub_total,
1065
-            $obj_type
1066
-        );
1067
-    }
1068
-
1069
-
1070
-    /**
1071
-     * Gets all descendants that are tickets
1072
-     *
1073
-     * @uses  EEH_Line_Item::get_line_items_of_object_type()
1074
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1075
-     * @return EE_Line_Item[]
1076
-     */
1077
-    public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1078
-    {
1079
-        return self::get_line_items_of_object_type($parent_line_item, 'Ticket');
1080
-    }
1081
-
1082
-
1083
-    /**
1084
-     * Gets all descendants subtotals that match the supplied object type
1085
-     *
1086
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1087
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1088
-     * @param string $obj_type
1089
-     * @return EE_Line_Item[]
1090
-     */
1091
-    public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1092
-    {
1093
-        return self::_get_descendants_by_type_and_object_type($parent_line_item, EEM_Line_Item::type_line_item, $obj_type);
1094
-    }
1095
-
1096
-
1097
-    /**
1098
-     * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1099
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1100
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1101
-     * @return EE_Line_Item[]
1102
-     */
1103
-    public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1104
-    {
1105
-        return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_tax);
1106
-    }
1107
-
1108
-
1109
-    /**
1110
-     * Gets all the real items purchased which are children of this item
1111
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1112
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1113
-     * @return EE_Line_Item[]
1114
-     */
1115
-    public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1116
-    {
1117
-        return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_line_item);
1118
-    }
1119
-
1120
-
1121
-    /**
1122
-     * Gets all descendants of supplied line item that match the supplied line item type
1123
-     *
1124
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1125
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1126
-     * @param string $line_item_type one of the EEM_Line_Item constants
1127
-     * @return EE_Line_Item[]
1128
-     */
1129
-    public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1130
-    {
1131
-        return self::_get_descendants_by_type_and_object_type($parent_line_item, $line_item_type, NULL);
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1137
-     *
1138
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1139
-     * @param string $line_item_type one of the EEM_Line_Item constants
1140
-     * @param string | NULL $obj_type object model class name (minus prefix) or NULL to ignore object type when searching
1141
-     * @return EE_Line_Item[]
1142
-     */
1143
-    protected static function _get_descendants_by_type_and_object_type(
1144
-        EE_Line_Item $parent_line_item,
1145
-        $line_item_type,
1146
-        $obj_type = null
1147
-    )
1148
-    {
1149
-        $objects = array();
1150
-        foreach ($parent_line_item->children() as $child_line_item) {
1151
-            if ($child_line_item instanceof EE_Line_Item) {
1152
-                if (
1153
-                    $child_line_item->type() === $line_item_type
1154
-                    && (
1155
-                        $child_line_item->OBJ_type() === $obj_type || $obj_type === null
1156
-                    )
1157
-                ) {
1158
-                    $objects[] = $child_line_item;
1159
-                } else {
1160
-                    //go-through-all-its children looking for more matches
1161
-                    $objects = array_merge(
1162
-                        $objects,
1163
-                        self::_get_descendants_by_type_and_object_type(
1164
-                            $child_line_item,
1165
-                            $line_item_type,
1166
-                            $obj_type
1167
-                        )
1168
-                    );
1169
-                }
1170
-            }
1171
-        }
1172
-        return $objects;
1173
-    }
1174
-
1175
-
1176
-    /**
1177
-     * Gets all descendants subtotals that match the supplied object type
1178
-     *
1179
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1180
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1181
-     * @param string $OBJ_type object type (like Event)
1182
-     * @param array $OBJ_IDs array of OBJ_IDs
1183
-     * @return EE_Line_Item[]
1184
-     */
1185
-    public static function get_line_items_by_object_type_and_IDs(
1186
-        EE_Line_Item $parent_line_item,
1187
-        $OBJ_type = '',
1188
-        $OBJ_IDs = array()
1189
-    )
1190
-    {
1191
-        return self::_get_descendants_by_object_type_and_object_ID($parent_line_item, $OBJ_type, $OBJ_IDs);
1192
-    }
1193
-
1194
-
1195
-    /**
1196
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1197
-     *
1198
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1199
-     * @param string $OBJ_type object type (like Event)
1200
-     * @param array $OBJ_IDs array of OBJ_IDs
1201
-     * @return EE_Line_Item[]
1202
-     */
1203
-    protected static function _get_descendants_by_object_type_and_object_ID(
1204
-        EE_Line_Item $parent_line_item,
1205
-        $OBJ_type,
1206
-        $OBJ_IDs
1207
-    )
1208
-    {
1209
-        $objects = array();
1210
-        foreach ($parent_line_item->children() as $child_line_item) {
1211
-            if ($child_line_item instanceof EE_Line_Item) {
1212
-                if (
1213
-                    $child_line_item->OBJ_type() === $OBJ_type
1214
-                    && is_array($OBJ_IDs)
1215
-                    && in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1216
-                ) {
1217
-                    $objects[] = $child_line_item;
1218
-                } else {
1219
-                    //go-through-all-its children looking for more matches
1220
-                    $objects = array_merge(
1221
-                        $objects,
1222
-                        self::_get_descendants_by_object_type_and_object_ID(
1223
-                            $child_line_item,
1224
-                            $OBJ_type,
1225
-                            $OBJ_IDs
1226
-                        )
1227
-                    );
1228
-                }
1229
-            }
1230
-        }
1231
-        return $objects;
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * Uses a breadth-first-search in order to find the nearest descendant of
1237
-     * the specified type and returns it, else NULL
1238
-     *
1239
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1240
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1241
-     * @param string $type like one of the EEM_Line_Item::type_*
1242
-     * @return EE_Line_Item
1243
-     */
1244
-    public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1245
-    {
1246
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1247
-    }
1248
-
1249
-
1250
-    /**
1251
-     * Uses a breadth-first-search in order to find the nearest descendant
1252
-     * having the specified LIN_code and returns it, else NULL
1253
-     *
1254
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1255
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1256
-     * @param string $code any value used for LIN_code
1257
-     * @return EE_Line_Item
1258
-     */
1259
-    public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1260
-    {
1261
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1262
-    }
1263
-
1264
-
1265
-    /**
1266
-     * Uses a breadth-first-search in order to find the nearest descendant
1267
-     * having the specified LIN_code and returns it, else NULL
1268
-     *
1269
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1270
-     * @param string $search_field name of EE_Line_Item property
1271
-     * @param string $value any value stored in $search_field
1272
-     * @return EE_Line_Item
1273
-     */
1274
-    protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1275
-    {
1276
-        foreach ($parent_line_item->children() as $child) {
1277
-            if ($child->get($search_field) == $value) {
1278
-                return $child;
1279
-            }
1280
-        }
1281
-        foreach ($parent_line_item->children() as $child) {
1282
-            $descendant_found = self::_get_nearest_descendant($child, $search_field, $value);
1283
-            if ($descendant_found) {
1284
-                return $descendant_found;
1285
-            }
1286
-        }
1287
-        return NULL;
1288
-    }
1289
-
1290
-
1291
-    /**
1292
-     * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1293
-     * else recursively walks up the line item tree until a parent of type total is found,
1294
-     *
1295
-     * @param EE_Line_Item $line_item
1296
-     * @return \EE_Line_Item
1297
-     * @throws \EE_Error
1298
-     */
1299
-    public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item)
1300
-    {
1301
-        if ($line_item->TXN_ID()) {
1302
-            $total_line_item = $line_item->transaction()->total_line_item(false);
1303
-            if ($total_line_item instanceof EE_Line_Item) {
1304
-                return $total_line_item;
1305
-            }
1306
-        } else {
1307
-            $line_item_parent = $line_item->parent();
1308
-            if ($line_item_parent instanceof EE_Line_Item) {
1309
-                if ($line_item_parent->is_total()) {
1310
-                    return $line_item_parent;
1311
-                }
1312
-                return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1313
-            }
1314
-        }
1315
-        throw new EE_Error(
1316
-            sprintf(
1317
-                __('A valid grand total for line item %1$d was not found.', 'event_espresso'),
1318
-                $line_item->ID()
1319
-            )
1320
-        );
1321
-    }
1322
-
1323
-
1324
-    /**
1325
-     * Prints out a representation of the line item tree
1326
-     *
1327
-     * @param EE_Line_Item $line_item
1328
-     * @param int $indentation
1329
-     * @return void
1330
-     * @throws \EE_Error
1331
-     */
1332
-    public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1333
-    {
1334
-        echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1335
-        if (!$indentation) {
1336
-            echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1337
-        }
1338
-        for ($i = 0; $i < $indentation; $i++) {
1339
-            echo ". ";
1340
-        }
1341
-        $breakdown = '';
1342
-        if ($line_item->is_line_item()) {
1343
-            if ($line_item->is_percent()) {
1344
-                $breakdown = "{$line_item->percent()}%";
1345
-            } else {
1346
-                $breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1347
-            }
1348
-        }
1349
-        echo $line_item->name() . " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : " . '$' . "{$line_item->total()}";
1350
-        if ($breakdown) {
1351
-            echo " ( {$breakdown} )";
1352
-        }
1353
-        if ($line_item->is_taxable()) {
1354
-            echo "  * taxable";
1355
-        }
1356
-        if ($line_item->children()) {
1357
-            foreach ($line_item->children() as $child) {
1358
-                self::visualize($child, $indentation + 1);
1359
-            }
1360
-        }
1361
-    }
1362
-
1363
-
1364
-    /**
1365
-     * Calculates the registration's final price, taking into account that they
1366
-     * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1367
-     * and receive a portion of any transaction-wide discounts.
1368
-     * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1369
-     * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1370
-     * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1371
-     * and brent's final price should be $5.50.
1372
-     *
1373
-     * In order to do this, we basically need to traverse the line item tree calculating
1374
-     * the running totals (just as if we were recalculating the total), but when we identify
1375
-     * regular line items, we need to keep track of their share of the grand total.
1376
-     * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1377
-     * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1378
-     * when there are non-taxable items; otherwise they would be the same)
1379
-     *
1380
-     * @param EE_Line_Item $line_item
1381
-     * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity that
1382
-     *                                                                            can be included in price calculations at this moment
1383
-     * @return array        keys are line items for tickets IDs and values are their share of the running total,
1384
-     *                                          plus the key 'total', and 'taxable' which also has keys of all the ticket IDs. Eg
1385
-     *                                          array(
1386
-     *                                          12 => 4.3
1387
-     *                                          23 => 8.0
1388
-     *                                          'total' => 16.6,
1389
-     *                                          'taxable' => array(
1390
-     *                                          12 => 10,
1391
-     *                                          23 => 4
1392
-     *                                          ).
1393
-     *                                          So to find which registrations have which final price, we need to find which line item
1394
-     *                                          is theirs, which can be done with
1395
-     *                                          `EEM_Line_Item::instance()->get_line_item_for_registration( $registration );`
1396
-     */
1397
-    public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array())
1398
-    {
1399
-        //init running grand total if not already
1400
-        if (!isset($running_totals['total'])) {
1401
-            $running_totals['total'] = 0;
1402
-        }
1403
-        if (!isset($running_totals['taxable'])) {
1404
-            $running_totals['taxable'] = array('total' => 0);
1405
-        }
1406
-        foreach ($line_item->children() as $child_line_item) {
1407
-            switch ($child_line_item->type()) {
1408
-
1409
-                case EEM_Line_Item::type_sub_total :
1410
-                    $running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item($child_line_item, $billable_ticket_quantities);
1411
-                    //combine arrays but preserve numeric keys
1412
-                    $running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1413
-                    $running_totals['total'] += $running_totals_from_subtotal['total'];
1414
-                    $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1415
-                    break;
1416
-
1417
-                case EEM_Line_Item::type_tax_sub_total :
1418
-
1419
-                    //find how much the taxes percentage is
1420
-                    if ($child_line_item->percent() !== 0) {
1421
-                        $tax_percent_decimal = $child_line_item->percent() / 100;
1422
-                    } else {
1423
-                        $tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1424
-                    }
1425
-                    //and apply to all the taxable totals, and add to the pretax totals
1426
-                    foreach ($running_totals as $line_item_id => $this_running_total) {
1427
-                        //"total" and "taxable" array key is an exception
1428
-                        if ($line_item_id === 'taxable') {
1429
-                            continue;
1430
-                        }
1431
-                        $taxable_total = $running_totals['taxable'][$line_item_id];
1432
-                        $running_totals[$line_item_id] += ($taxable_total * $tax_percent_decimal);
1433
-                    }
1434
-                    break;
1435
-
1436
-                case EEM_Line_Item::type_line_item :
1437
-
1438
-                    // ticket line items or ????
1439
-                    if ($child_line_item->OBJ_type() === 'Ticket') {
1440
-                        // kk it's a ticket
1441
-                        if (isset($running_totals[$child_line_item->ID()])) {
1442
-                            //huh? that shouldn't happen.
1443
-                            $running_totals['total'] += $child_line_item->total();
1444
-                        } else {
1445
-                            //its not in our running totals yet. great.
1446
-                            if ($child_line_item->is_taxable()) {
1447
-                                $taxable_amount = $child_line_item->unit_price();
1448
-                            } else {
1449
-                                $taxable_amount = 0;
1450
-                            }
1451
-                            // are we only calculating totals for some tickets?
1452
-                            if (isset($billable_ticket_quantities[$child_line_item->OBJ_ID()])) {
1453
-                                $quantity = $billable_ticket_quantities[$child_line_item->OBJ_ID()];
1454
-                                $running_totals[$child_line_item->ID()] = $quantity
1455
-                                    ? $child_line_item->unit_price()
1456
-                                    : 0;
1457
-                                $running_totals['taxable'][$child_line_item->ID()] = $quantity
1458
-                                    ? $taxable_amount
1459
-                                    : 0;
1460
-                            } else {
1461
-                                $quantity = $child_line_item->quantity();
1462
-                                $running_totals[$child_line_item->ID()] = $child_line_item->unit_price();
1463
-                                $running_totals['taxable'][$child_line_item->ID()] = $taxable_amount;
1464
-                            }
1465
-                            $running_totals['taxable']['total'] += $taxable_amount * $quantity;
1466
-                            $running_totals['total'] += $child_line_item->unit_price() * $quantity;
1467
-                        }
1468
-                    } else {
1469
-                        // it's some other type of item added to the cart
1470
-                        // it should affect the running totals
1471
-                        // basically we want to convert it into a PERCENT modifier. Because
1472
-                        // more clearly affect all registration's final price equally
1473
-                        $line_items_percent_of_running_total = $running_totals['total'] > 0
1474
-                            ? ($child_line_item->total() / $running_totals['total']) + 1
1475
-                            : 1;
1476
-                        foreach ($running_totals as $line_item_id => $this_running_total) {
1477
-                            //the "taxable" array key is an exception
1478
-                            if ($line_item_id === 'taxable') {
1479
-                                continue;
1480
-                            }
1481
-                            // update the running totals
1482
-                            // yes this actually even works for the running grand total!
1483
-                            $running_totals[$line_item_id] =
1484
-                                $line_items_percent_of_running_total * $this_running_total;
1485
-
1486
-                            if ($child_line_item->is_taxable()) {
1487
-                                $running_totals['taxable'][$line_item_id] =
1488
-                                    $line_items_percent_of_running_total * $running_totals['taxable'][$line_item_id];
1489
-                            }
1490
-                        }
1491
-                    }
1492
-                    break;
1493
-            }
1494
-        }
1495
-        return $running_totals;
1496
-    }
1497
-
1498
-
1499
-    /**
1500
-     * @param \EE_Line_Item $total_line_item
1501
-     * @param \EE_Line_Item $ticket_line_item
1502
-     * @return float | null
1503
-     * @throws \OutOfRangeException
1504
-     */
1505
-    public static function calculate_final_price_for_ticket_line_item(\EE_Line_Item $total_line_item, \EE_Line_Item $ticket_line_item)
1506
-    {
1507
-        static $final_prices_per_ticket_line_item = array();
1508
-        if (empty($final_prices_per_ticket_line_item)) {
1509
-            $final_prices_per_ticket_line_item = \EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1510
-                $total_line_item
1511
-            );
1512
-        }
1513
-        //ok now find this new registration's final price
1514
-        if (isset($final_prices_per_ticket_line_item[$ticket_line_item->ID()])) {
1515
-            return $final_prices_per_ticket_line_item[$ticket_line_item->ID()];
1516
-        }
1517
-        $message = sprintf(
1518
-            __(
1519
-                'The final price for the ticket line item (ID:%1$d) could not be calculated.',
1520
-                'event_espresso'
1521
-            ),
1522
-            $ticket_line_item->ID()
1523
-        );
1524
-        if (WP_DEBUG) {
1525
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1526
-            throw new \OutOfRangeException($message);
1527
-        } else {
1528
-            EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1529
-        }
1530
-        return null;
1531
-    }
1532
-
1533
-
1534
-    /**
1535
-     * Creates a duplicate of the line item tree, except only includes billable items
1536
-     * and the portion of line items attributed to billable things
1537
-     *
1538
-     * @param EE_Line_Item $line_item
1539
-     * @param EE_Registration[] $registrations
1540
-     * @return \EE_Line_Item
1541
-     * @throws \EE_Error
1542
-     */
1543
-    public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1544
-    {
1545
-        $copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1546
-        foreach ($line_item->children() as $child_li) {
1547
-            $copy_li->add_child_line_item(EEH_Line_Item::billable_line_item_tree($child_li, $registrations));
1548
-        }
1549
-        //if this is the grand total line item, make sure the totals all add up
1550
-        //(we could have duplicated this logic AS we copied the line items, but
1551
-        //it seems DRYer this way)
1552
-        if ($copy_li->type() === EEM_Line_Item::type_total) {
1553
-            $copy_li->recalculate_total_including_taxes();
1554
-        }
1555
-        return $copy_li;
1556
-    }
1557
-
1558
-
1559
-    /**
1560
-     * Creates a new, unsaved line item from $line_item that factors in the
1561
-     * number of billable registrations on $registrations.
1562
-     *
1563
-     * @param EE_Line_Item $line_item
1564
-     * @return EE_Line_Item
1565
-     * @throws \EE_Error
1566
-     * @param EE_Registration[] $registrations
1567
-     */
1568
-    public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1569
-    {
1570
-        $new_li_fields = $line_item->model_field_array();
1571
-        if ($line_item->type() === EEM_Line_Item::type_line_item &&
1572
-            $line_item->OBJ_type() === 'Ticket'
1573
-        ) {
1574
-            $count = 0;
1575
-            foreach ($registrations as $registration) {
1576
-                if ($line_item->OBJ_ID() === $registration->ticket_ID() &&
1577
-                    in_array($registration->status_ID(), EEM_Registration::reg_statuses_that_allow_payment())
1578
-                ) {
1579
-                    $count++;
1580
-                }
1581
-            }
1582
-            $new_li_fields['LIN_quantity'] = $count;
1583
-        }
1584
-        //don't set the total. We'll leave that up to the code that calculates it
1585
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1586
-        return EE_Line_Item::new_instance($new_li_fields);
1587
-    }
1588
-
1589
-
1590
-    /**
1591
-     * Returns a modified line item tree where all the subtotals which have a total of 0
1592
-     * are removed, and line items with a quantity of 0
1593
-     *
1594
-     * @param EE_Line_Item $line_item |null
1595
-     * @return \EE_Line_Item|null
1596
-     * @throws \EE_Error
1597
-     */
1598
-    public static function non_empty_line_items(EE_Line_Item $line_item)
1599
-    {
1600
-        $copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1601
-        if ($copied_li === null) {
1602
-            return null;
1603
-        }
1604
-        //if this is an event subtotal, we want to only include it if it
1605
-        //has a non-zero total and at least one ticket line item child
1606
-        $ticket_children = 0;
1607
-        foreach ($line_item->children() as $child_li) {
1608
-            $child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1609
-            if ($child_li_copy !== null) {
1610
-                $copied_li->add_child_line_item($child_li_copy);
1611
-                if ($child_li_copy->type() === EEM_Line_Item::type_line_item &&
1612
-                    $child_li_copy->OBJ_type() === 'Ticket'
1613
-                ) {
1614
-                    $ticket_children++;
1615
-                }
1616
-            }
1617
-        }
1618
-        //if this is an event subtotal with NO ticket children
1619
-        //we basically want to ignore it
1620
-        if (
1621
-            $ticket_children === 0
1622
-            && $line_item->type() === EEM_Line_Item::type_sub_total
1623
-            && $line_item->OBJ_type() === 'Event'
1624
-            && $line_item->total() === 0
1625
-        ) {
1626
-            return null;
1627
-        }
1628
-        return $copied_li;
1629
-    }
1630
-
1631
-
1632
-    /**
1633
-     * Creates a new, unsaved line item, but if it's a ticket line item
1634
-     * with a total of 0, or a subtotal of 0, returns null instead
1635
-     *
1636
-     * @param EE_Line_Item $line_item
1637
-     * @return EE_Line_Item
1638
-     * @throws \EE_Error
1639
-     */
1640
-    public static function non_empty_line_item(EE_Line_Item $line_item)
1641
-    {
1642
-        if ($line_item->type() === EEM_Line_Item::type_line_item &&
1643
-            $line_item->OBJ_type() === 'Ticket' &&
1644
-            $line_item->quantity() === 0
1645
-        ) {
1646
-            return null;
1647
-        }
1648
-        $new_li_fields = $line_item->model_field_array();
1649
-        //don't set the total. We'll leave that up to the code that calculates it
1650
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1651
-        return EE_Line_Item::new_instance($new_li_fields);
1652
-    }
1653
-
1654
-
1655
-
1656
-    /**************************************** @DEPRECATED METHODS *************************************** */
1657
-    /**
1658
-     * @deprecated
1659
-     * @param EE_Line_Item $total_line_item
1660
-     * @return \EE_Line_Item
1661
-     * @throws \EE_Error
1662
-     */
1663
-    public static function get_items_subtotal(EE_Line_Item $total_line_item)
1664
-    {
1665
-        EE_Error::doing_it_wrong('EEH_Line_Item::get_items_subtotal()', __('Method replaced with EEH_Line_Item::get_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1666
-        return self::get_pre_tax_subtotal($total_line_item);
1667
-    }
1668
-
1669
-
1670
-    /**
1671
-     * @deprecated
1672
-     * @param EE_Transaction $transaction
1673
-     * @return \EE_Line_Item
1674
-     * @throws \EE_Error
1675
-     */
1676
-    public static function create_default_total_line_item($transaction = NULL)
1677
-    {
1678
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_total_line_item()', __('Method replaced with EEH_Line_Item::create_total_line_item()', 'event_espresso'), '4.6.0');
1679
-        return self::create_total_line_item($transaction);
1680
-    }
1681
-
1682
-
1683
-    /**
1684
-     * @deprecated
1685
-     * @param EE_Line_Item $total_line_item
1686
-     * @param EE_Transaction $transaction
1687
-     * @return \EE_Line_Item
1688
-     * @throws \EE_Error
1689
-     */
1690
-    public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1691
-    {
1692
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_tickets_subtotal()', __('Method replaced with EEH_Line_Item::create_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1693
-        return self::create_pre_tax_subtotal($total_line_item, $transaction);
1694
-    }
1695
-
1696
-
1697
-    /**
1698
-     * @deprecated
1699
-     * @param EE_Line_Item $total_line_item
1700
-     * @param EE_Transaction $transaction
1701
-     * @return \EE_Line_Item
1702
-     * @throws \EE_Error
1703
-     */
1704
-    public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1705
-    {
1706
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_taxes_subtotal()', __('Method replaced with EEH_Line_Item::create_taxes_subtotal()', 'event_espresso'), '4.6.0');
1707
-        return self::create_taxes_subtotal($total_line_item, $transaction);
1708
-    }
1709
-
1710
-
1711
-    /**
1712
-     * @deprecated
1713
-     * @param EE_Line_Item $total_line_item
1714
-     * @param EE_Transaction $transaction
1715
-     * @return \EE_Line_Item
1716
-     * @throws \EE_Error
1717
-     */
1718
-    public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1719
-    {
1720
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_event_subtotal()', __('Method replaced with EEH_Line_Item::create_event_subtotal()', 'event_espresso'), '4.6.0');
1721
-        return self::create_event_subtotal($total_line_item, $transaction);
1722
-    }
26
+	//other functions: cancel ticket purchase
27
+	//delete ticket purchase
28
+	//add promotion
29
+
30
+
31
+	/**
32
+	 * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
33
+	 * Does NOT automatically re-calculate the line item totals or update the related transaction.
34
+	 * You should call recalculate_total_including_taxes() on the grant total line item after this
35
+	 * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
36
+	 * to keep the registration final prices in-sync with the transaction's total.
37
+	 *
38
+	 * @param EE_Line_Item $parent_line_item
39
+	 * @param string $name
40
+	 * @param float $unit_price
41
+	 * @param string $description
42
+	 * @param int $quantity
43
+	 * @param boolean $taxable
44
+	 * @param boolean $code if set to a value, ensures there is only one line item with that code
45
+	 * @return boolean success
46
+	 * @throws \EE_Error
47
+	 */
48
+	public static function add_unrelated_item(EE_Line_Item $parent_line_item, $name, $unit_price, $description = '', $quantity = 1, $taxable = FALSE, $code = NULL)
49
+	{
50
+		$items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
51
+		$line_item = EE_Line_Item::new_instance(array(
52
+			'LIN_name' => $name,
53
+			'LIN_desc' => $description,
54
+			'LIN_unit_price' => $unit_price,
55
+			'LIN_quantity' => $quantity,
56
+			'LIN_percent' => null,
57
+			'LIN_is_taxable' => $taxable,
58
+			'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0,
59
+			'LIN_total' => (float)$unit_price * (int)$quantity,
60
+			'LIN_type' => EEM_Line_Item::type_line_item,
61
+			'LIN_code' => $code,
62
+		));
63
+		$line_item = apply_filters(
64
+			'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
65
+			$line_item,
66
+			$parent_line_item
67
+		);
68
+		return self::add_item($parent_line_item, $line_item);
69
+	}
70
+
71
+
72
+	/**
73
+	 * Adds a simple item ( unrelated to any other model object) to the total line item,
74
+	 * in the correct spot in the line item tree. Automatically
75
+	 * re-calculates the line item totals and updates the related transaction. But
76
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
77
+	 * should probably change because of this).
78
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
79
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
80
+	 *
81
+	 * @param EE_Line_Item $parent_line_item
82
+	 * @param string $name
83
+	 * @param float $percentage_amount
84
+	 * @param string $description
85
+	 * @param boolean $taxable
86
+	 * @return boolean success
87
+	 * @throws \EE_Error
88
+	 */
89
+	public static function add_percentage_based_item(EE_Line_Item $parent_line_item, $name, $percentage_amount, $description = '', $taxable = FALSE)
90
+	{
91
+		$line_item = EE_Line_Item::new_instance(array(
92
+			'LIN_name' => $name,
93
+			'LIN_desc' => $description,
94
+			'LIN_unit_price' => 0,
95
+			'LIN_percent' => $percentage_amount,
96
+			'LIN_quantity' => 1,
97
+			'LIN_is_taxable' => $taxable,
98
+			'LIN_total' => (float)($percentage_amount * ($parent_line_item->total() / 100)),
99
+			'LIN_type' => EEM_Line_Item::type_line_item,
100
+			'LIN_parent' => $parent_line_item->ID()
101
+		));
102
+		$line_item = apply_filters(
103
+			'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
104
+			$line_item
105
+		);
106
+		return $parent_line_item->add_child_line_item($line_item, false);
107
+	}
108
+
109
+
110
+	/**
111
+	 * Returns the new line item created by adding a purchase of the ticket
112
+	 * ensures that ticket line item is saved, and that cart total has been recalculated.
113
+	 * If this ticket has already been purchased, just increments its count.
114
+	 * Automatically re-calculates the line item totals and updates the related transaction. But
115
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
116
+	 * should probably change because of this).
117
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
118
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
119
+	 *
120
+	 * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
121
+	 * @param EE_Ticket $ticket
122
+	 * @param int $qty
123
+	 * @return \EE_Line_Item
124
+	 * @throws \EE_Error
125
+	 */
126
+	public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
127
+	{
128
+		if (!$total_line_item instanceof EE_Line_Item || !$total_line_item->is_total()) {
129
+			throw new EE_Error(sprintf(__('A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', 'event_espresso'), $ticket->ID(), $total_line_item->ID()));
130
+		}
131
+		// either increment the qty for an existing ticket
132
+		$line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
133
+		// or add a new one
134
+		if (!$line_item instanceof EE_Line_Item) {
135
+			$line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
136
+		}
137
+		$total_line_item->recalculate_total_including_taxes();
138
+		return $line_item;
139
+	}
140
+
141
+
142
+	/**
143
+	 * Returns the new line item created by adding a purchase of the ticket
144
+	 * @param \EE_Line_Item $total_line_item
145
+	 * @param EE_Ticket $ticket
146
+	 * @param int $qty
147
+	 * @return \EE_Line_Item
148
+	 * @throws \EE_Error
149
+	 */
150
+	public static function increment_ticket_qty_if_already_in_cart(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
151
+	{
152
+		$line_item = null;
153
+		if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
154
+			$ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
155
+			foreach ((array)$ticket_line_items as $ticket_line_item) {
156
+				if (
157
+					$ticket_line_item instanceof EE_Line_Item
158
+					&& (int)$ticket_line_item->OBJ_ID() === (int)$ticket->ID()
159
+				) {
160
+					$line_item = $ticket_line_item;
161
+					break;
162
+				}
163
+			}
164
+		}
165
+		if ($line_item instanceof EE_Line_Item) {
166
+			EEH_Line_Item::increment_quantity($line_item, $qty);
167
+			return $line_item;
168
+		}
169
+		return null;
170
+	}
171
+
172
+
173
+	/**
174
+	 * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
175
+	 * Does NOT save or recalculate other line items totals
176
+	 *
177
+	 * @param EE_Line_Item $line_item
178
+	 * @param int $qty
179
+	 * @return void
180
+	 * @throws \EE_Error
181
+	 */
182
+	public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
183
+	{
184
+		if (!$line_item->is_percent()) {
185
+			$qty += $line_item->quantity();
186
+			$line_item->set_quantity($qty);
187
+			$line_item->set_total($line_item->unit_price() * $qty);
188
+			$line_item->save();
189
+		}
190
+		foreach ($line_item->children() as $child) {
191
+			if ($child->is_sub_line_item()) {
192
+				EEH_Line_Item::update_quantity($child, $qty);
193
+			}
194
+		}
195
+	}
196
+
197
+
198
+	/**
199
+	 * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
200
+	 * Does NOT save or recalculate other line items totals
201
+	 *
202
+	 * @param EE_Line_Item $line_item
203
+	 * @param int $qty
204
+	 * @return void
205
+	 * @throws \EE_Error
206
+	 */
207
+	public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
208
+	{
209
+		if (!$line_item->is_percent()) {
210
+			$qty = $line_item->quantity() - $qty;
211
+			$qty = max($qty, 0);
212
+			$line_item->set_quantity($qty);
213
+			$line_item->set_total($line_item->unit_price() * $qty);
214
+			$line_item->save();
215
+		}
216
+		foreach ($line_item->children() as $child) {
217
+			if ($child->is_sub_line_item()) {
218
+				EEH_Line_Item::update_quantity($child, $qty);
219
+			}
220
+		}
221
+	}
222
+
223
+
224
+	/**
225
+	 * Updates the line item and its children's quantities to the specified number.
226
+	 * Does NOT save them or recalculate totals.
227
+	 *
228
+	 * @param EE_Line_Item $line_item
229
+	 * @param int $new_quantity
230
+	 * @throws \EE_Error
231
+	 */
232
+	public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
233
+	{
234
+		if (!$line_item->is_percent()) {
235
+			$line_item->set_quantity($new_quantity);
236
+			$line_item->set_total($line_item->unit_price() * $new_quantity);
237
+			$line_item->save();
238
+		}
239
+		foreach ($line_item->children() as $child) {
240
+			if ($child->is_sub_line_item()) {
241
+				EEH_Line_Item::update_quantity($child, $new_quantity);
242
+			}
243
+		}
244
+	}
245
+
246
+
247
+	/**
248
+	 * Returns the new line item created by adding a purchase of the ticket
249
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
250
+	 * @param EE_Ticket $ticket
251
+	 * @param int $qty
252
+	 * @return \EE_Line_Item
253
+	 * @throws \EE_Error
254
+	 */
255
+	public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
256
+	{
257
+		$datetimes = $ticket->datetimes();
258
+		$first_datetime = reset($datetimes);
259
+		if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
260
+			$first_datetime_name = $first_datetime->event()->name();
261
+		} else {
262
+			$first_datetime_name = __('Event', 'event_espresso');
263
+		}
264
+		$event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
265
+		// get event subtotal line
266
+		$events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
267
+		// add $ticket to cart
268
+		$line_item = EE_Line_Item::new_instance(array(
269
+			'LIN_name' => $ticket->name(),
270
+			'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
271
+			'LIN_unit_price' => $ticket->price(),
272
+			'LIN_quantity' => $qty,
273
+			'LIN_is_taxable' => $ticket->taxable(),
274
+			'LIN_order' => count($events_sub_total->children()),
275
+			'LIN_total' => $ticket->price() * $qty,
276
+			'LIN_type' => EEM_Line_Item::type_line_item,
277
+			'OBJ_ID' => $ticket->ID(),
278
+			'OBJ_type' => 'Ticket'
279
+		));
280
+		$line_item = apply_filters(
281
+			'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
282
+			$line_item
283
+		);
284
+		$events_sub_total->add_child_line_item($line_item);
285
+		//now add the sub-line items
286
+		$running_total_for_ticket = 0;
287
+		foreach ($ticket->prices(array('order_by' => array('PRC_order' => 'ASC'))) as $price) {
288
+			$sign = $price->is_discount() ? -1 : 1;
289
+			$price_total = $price->is_percent()
290
+				? $running_total_for_ticket * $price->amount() / 100
291
+				: $price->amount() * $qty;
292
+			$sub_line_item = EE_Line_Item::new_instance(array(
293
+				'LIN_name' => $price->name(),
294
+				'LIN_desc' => $price->desc(),
295
+				'LIN_quantity' => $price->is_percent() ? null : $qty,
296
+				'LIN_is_taxable' => false,
297
+				'LIN_order' => $price->order(),
298
+				'LIN_total' => $sign * $price_total,
299
+				'LIN_type' => EEM_Line_Item::type_sub_line_item,
300
+				'OBJ_ID' => $price->ID(),
301
+				'OBJ_type' => 'Price'
302
+			));
303
+			$sub_line_item = apply_filters(
304
+				'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
305
+				$sub_line_item
306
+			);
307
+			if ($price->is_percent()) {
308
+				$sub_line_item->set_percent($sign * $price->amount());
309
+			} else {
310
+				$sub_line_item->set_unit_price($sign * $price->amount());
311
+			}
312
+			$running_total_for_ticket += $price_total;
313
+			$line_item->add_child_line_item($sub_line_item);
314
+		}
315
+		return $line_item;
316
+	}
317
+
318
+
319
+	/**
320
+	 * Adds the specified item under the pre-tax-sub-total line item. Automatically
321
+	 * re-calculates the line item totals and updates the related transaction. But
322
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
323
+	 * should probably change because of this).
324
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
325
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
326
+	 *
327
+	 * @param EE_Line_Item $total_line_item
328
+	 * @param EE_Line_Item $item to be added
329
+	 * @return boolean
330
+	 * @throws \EE_Error
331
+	 */
332
+	public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item)
333
+	{
334
+		$pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
335
+		if ($pre_tax_subtotal instanceof EE_Line_Item) {
336
+			$success = $pre_tax_subtotal->add_child_line_item($item);
337
+		} else {
338
+			return FALSE;
339
+		}
340
+		$total_line_item->recalculate_total_including_taxes();
341
+		return $success;
342
+	}
343
+
344
+
345
+	/**
346
+	 * cancels an existing ticket line item,
347
+	 * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
348
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
349
+	 *
350
+	 * @param EE_Line_Item $ticket_line_item
351
+	 * @param int $qty
352
+	 * @return bool success
353
+	 * @throws \EE_Error
354
+	 */
355
+	public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
356
+	{
357
+		// validate incoming line_item
358
+		if ($ticket_line_item->OBJ_type() !== 'Ticket') {
359
+			throw new EE_Error(
360
+				sprintf(
361
+					__('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
362
+					$ticket_line_item->type()
363
+				)
364
+			);
365
+		}
366
+		if ($ticket_line_item->quantity() < $qty) {
367
+			throw new EE_Error(
368
+				sprintf(
369
+					__('Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.', 'event_espresso'),
370
+					$qty,
371
+					$ticket_line_item->quantity()
372
+				)
373
+			);
374
+		}
375
+		// decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
376
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
377
+		foreach ($ticket_line_item->children() as $child_line_item) {
378
+			if (
379
+				$child_line_item->is_sub_line_item()
380
+				&& !$child_line_item->is_percent()
381
+				&& !$child_line_item->is_cancellation()
382
+			) {
383
+				$child_line_item->set_quantity($child_line_item->quantity() - $qty);
384
+			}
385
+		}
386
+		// get cancellation sub line item
387
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
388
+			$ticket_line_item,
389
+			EEM_Line_Item::type_cancellation
390
+		);
391
+		$cancellation_line_item = reset($cancellation_line_item);
392
+		// verify that this ticket was indeed previously cancelled
393
+		if ($cancellation_line_item instanceof EE_Line_Item) {
394
+			// increment cancelled quantity
395
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
396
+		} else {
397
+			// create cancellation sub line item
398
+			$cancellation_line_item = EE_Line_Item::new_instance(array(
399
+				'LIN_name' => __('Cancellation', 'event_espresso'),
400
+				'LIN_desc' => sprintf(
401
+					_x('Cancelled %1$s : %2$s', 'Cancelled Ticket Name : 2015-01-01 11:11', 'event_espresso'),
402
+					$ticket_line_item->name(),
403
+					current_time(get_option('date_format') . ' ' . get_option('time_format'))
404
+				),
405
+				'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
406
+				'LIN_quantity' => $qty,
407
+				'LIN_is_taxable' => $ticket_line_item->is_taxable(),
408
+				'LIN_order' => count($ticket_line_item->children()),
409
+				'LIN_total' => 0, // $ticket_line_item->unit_price()
410
+				'LIN_type' => EEM_Line_Item::type_cancellation,
411
+			));
412
+			$ticket_line_item->add_child_line_item($cancellation_line_item);
413
+		}
414
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
415
+			// decrement parent line item quantity
416
+			$event_line_item = $ticket_line_item->parent();
417
+			if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
418
+				$event_line_item->set_quantity($event_line_item->quantity() - $qty);
419
+				$event_line_item->save();
420
+			}
421
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
422
+			return true;
423
+		}
424
+		return false;
425
+	}
426
+
427
+
428
+	/**
429
+	 * reinstates (un-cancels?) a previously canceled ticket line item,
430
+	 * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
431
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
432
+	 *
433
+	 * @param EE_Line_Item $ticket_line_item
434
+	 * @param int $qty
435
+	 * @return bool success
436
+	 * @throws \EE_Error
437
+	 */
438
+	public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
439
+	{
440
+		// validate incoming line_item
441
+		if ($ticket_line_item->OBJ_type() !== 'Ticket') {
442
+			throw new EE_Error(
443
+				sprintf(
444
+					__('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
445
+					$ticket_line_item->type()
446
+				)
447
+			);
448
+		}
449
+		// get cancellation sub line item
450
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
451
+			$ticket_line_item,
452
+			EEM_Line_Item::type_cancellation
453
+		);
454
+		$cancellation_line_item = reset($cancellation_line_item);
455
+		// verify that this ticket was indeed previously cancelled
456
+		if (!$cancellation_line_item instanceof EE_Line_Item) {
457
+			return false;
458
+		}
459
+		if ($cancellation_line_item->quantity() > $qty) {
460
+			// decrement cancelled quantity
461
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
462
+		} else if ($cancellation_line_item->quantity() == $qty) {
463
+			// decrement cancelled quantity in case anyone still has the object kicking around
464
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
465
+			// delete because quantity will end up as 0
466
+			$cancellation_line_item->delete();
467
+			// and attempt to destroy the object,
468
+			// even though PHP won't actually destroy it until it needs the memory
469
+			unset($cancellation_line_item);
470
+		} else {
471
+			// what ?!?! negative quantity ?!?!
472
+			throw new EE_Error(
473
+				sprintf(
474
+					__('Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
475
+						'event_espresso'),
476
+					$qty,
477
+					$cancellation_line_item->quantity()
478
+				)
479
+			);
480
+		}
481
+		// increment ticket quantity
482
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
483
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
484
+			// increment parent line item quantity
485
+			$event_line_item = $ticket_line_item->parent();
486
+			if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
487
+				$event_line_item->set_quantity($event_line_item->quantity() + $qty);
488
+			}
489
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
490
+			return true;
491
+		}
492
+		return false;
493
+	}
494
+
495
+
496
+	/**
497
+	 * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
498
+	 * then EE_Line_Item::recalculate_total_including_taxes() on the result
499
+	 *
500
+	 * @param EE_Line_Item $line_item
501
+	 * @return \EE_Line_Item
502
+	 */
503
+	public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
504
+	{
505
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
506
+		return $grand_total_line_item->recalculate_total_including_taxes();
507
+	}
508
+
509
+
510
+	/**
511
+	 * Gets the line item which contains the subtotal of all the items
512
+	 *
513
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
514
+	 * @return \EE_Line_Item
515
+	 * @throws \EE_Error
516
+	 */
517
+	public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
518
+	{
519
+		$pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
520
+		return $pre_tax_subtotal instanceof EE_Line_Item
521
+			? $pre_tax_subtotal
522
+			: self::create_pre_tax_subtotal($total_line_item);
523
+	}
524
+
525
+
526
+	/**
527
+	 * Gets the line item for the taxes subtotal
528
+	 *
529
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
530
+	 * @return \EE_Line_Item
531
+	 * @throws \EE_Error
532
+	 */
533
+	public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
534
+	{
535
+		$taxes = $total_line_item->get_child_line_item('taxes');
536
+		return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
537
+	}
538
+
539
+
540
+	/**
541
+	 * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
542
+	 *
543
+	 * @param EE_Line_Item $line_item
544
+	 * @param EE_Transaction $transaction
545
+	 * @return void
546
+	 * @throws \EE_Error
547
+	 */
548
+	public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = NULL)
549
+	{
550
+		if ($transaction) {
551
+			/** @type EEM_Transaction $EEM_Transaction */
552
+			$EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
553
+			$TXN_ID = $EEM_Transaction->ensure_is_ID($transaction);
554
+			$line_item->set_TXN_ID($TXN_ID);
555
+		}
556
+	}
557
+
558
+
559
+	/**
560
+	 * Creates a new default total line item for the transaction,
561
+	 * and its tickets subtotal and taxes subtotal line items (and adds the
562
+	 * existing taxes as children of the taxes subtotal line item)
563
+	 *
564
+	 * @param EE_Transaction $transaction
565
+	 * @return \EE_Line_Item of type total
566
+	 * @throws \EE_Error
567
+	 */
568
+	public static function create_total_line_item($transaction = NULL)
569
+	{
570
+		$total_line_item = EE_Line_Item::new_instance(array(
571
+			'LIN_code' => 'total',
572
+			'LIN_name' => __('Grand Total', 'event_espresso'),
573
+			'LIN_type' => EEM_Line_Item::type_total,
574
+			'OBJ_type' => 'Transaction'
575
+		));
576
+		$total_line_item = apply_filters(
577
+			'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
578
+			$total_line_item
579
+		);
580
+		self::set_TXN_ID($total_line_item, $transaction);
581
+		self::create_pre_tax_subtotal($total_line_item, $transaction);
582
+		self::create_taxes_subtotal($total_line_item, $transaction);
583
+		return $total_line_item;
584
+	}
585
+
586
+
587
+	/**
588
+	 * Creates a default items subtotal line item
589
+	 *
590
+	 * @param EE_Line_Item $total_line_item
591
+	 * @param EE_Transaction $transaction
592
+	 * @return EE_Line_Item
593
+	 * @throws \EE_Error
594
+	 */
595
+	protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
596
+	{
597
+		$pre_tax_line_item = EE_Line_Item::new_instance(array(
598
+			'LIN_code' => 'pre-tax-subtotal',
599
+			'LIN_name' => __('Pre-Tax Subtotal', 'event_espresso'),
600
+			'LIN_type' => EEM_Line_Item::type_sub_total
601
+		));
602
+		$pre_tax_line_item = apply_filters(
603
+			'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
604
+			$pre_tax_line_item
605
+		);
606
+		self::set_TXN_ID($pre_tax_line_item, $transaction);
607
+		$total_line_item->add_child_line_item($pre_tax_line_item);
608
+		self::create_event_subtotal($pre_tax_line_item, $transaction);
609
+		return $pre_tax_line_item;
610
+	}
611
+
612
+
613
+	/**
614
+	 * Creates a line item for the taxes subtotal and finds all the tax prices
615
+	 * and applies taxes to it
616
+	 *
617
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
618
+	 * @param EE_Transaction $transaction
619
+	 * @return EE_Line_Item
620
+	 * @throws \EE_Error
621
+	 */
622
+	protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
623
+	{
624
+		$tax_line_item = EE_Line_Item::new_instance(array(
625
+			'LIN_code' => 'taxes',
626
+			'LIN_name' => __('Taxes', 'event_espresso'),
627
+			'LIN_type' => EEM_Line_Item::type_tax_sub_total,
628
+			'LIN_order' => 1000,//this should always come last
629
+		));
630
+		$tax_line_item = apply_filters(
631
+			'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
632
+			$tax_line_item
633
+		);
634
+		self::set_TXN_ID($tax_line_item, $transaction);
635
+		$total_line_item->add_child_line_item($tax_line_item);
636
+		//and lastly, add the actual taxes
637
+		self::apply_taxes($total_line_item);
638
+		return $tax_line_item;
639
+	}
640
+
641
+
642
+	/**
643
+	 * Creates a default items subtotal line item
644
+	 *
645
+	 * @param EE_Line_Item $pre_tax_line_item
646
+	 * @param EE_Transaction $transaction
647
+	 * @param EE_Event $event
648
+	 * @return EE_Line_Item
649
+	 * @throws \EE_Error
650
+	 */
651
+	public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = NULL, $event = NULL)
652
+	{
653
+		$event_line_item = EE_Line_Item::new_instance(array(
654
+			'LIN_code' => self::get_event_code($event),
655
+			'LIN_name' => self::get_event_name($event),
656
+			'LIN_desc' => self::get_event_desc($event),
657
+			'LIN_type' => EEM_Line_Item::type_sub_total,
658
+			'OBJ_type' => 'Event',
659
+			'OBJ_ID' => $event instanceof EE_Event ? $event->ID() : 0
660
+		));
661
+		$event_line_item = apply_filters(
662
+			'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
663
+			$event_line_item
664
+		);
665
+		self::set_TXN_ID($event_line_item, $transaction);
666
+		$pre_tax_line_item->add_child_line_item($event_line_item);
667
+		return $event_line_item;
668
+	}
669
+
670
+
671
+	/**
672
+	 * Gets what the event ticket's code SHOULD be
673
+	 *
674
+	 * @param EE_Event $event
675
+	 * @return string
676
+	 * @throws \EE_Error
677
+	 */
678
+	public static function get_event_code($event)
679
+	{
680
+		return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
681
+	}
682
+
683
+	/**
684
+	 * Gets the event name
685
+	 * @param EE_Event $event
686
+	 * @return string
687
+	 */
688
+	public static function get_event_name($event)
689
+	{
690
+		return $event instanceof EE_Event ? $event->name() : __('Event', 'event_espresso');
691
+	}
692
+
693
+	/**
694
+	 * Gets the event excerpt
695
+	 * @param EE_Event $event
696
+	 * @return string
697
+	 */
698
+	public static function get_event_desc($event)
699
+	{
700
+		return $event instanceof EE_Event ? $event->short_description() : '';
701
+	}
702
+
703
+	/**
704
+	 * Given the grand total line item and a ticket, finds the event sub-total
705
+	 * line item the ticket's purchase should be added onto
706
+	 *
707
+	 * @access public
708
+	 * @param EE_Line_Item $grand_total the grand total line item
709
+	 * @param EE_Ticket $ticket
710
+	 * @throws \EE_Error
711
+	 * @return EE_Line_Item
712
+	 */
713
+	public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
714
+	{
715
+		$first_datetime = $ticket->first_datetime();
716
+		if (!$first_datetime instanceof EE_Datetime) {
717
+			throw new EE_Error(
718
+				sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())
719
+			);
720
+		}
721
+		$event = $first_datetime->event();
722
+		if (!$event instanceof EE_Event) {
723
+			throw new EE_Error(
724
+				sprintf(
725
+					__('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'),
726
+					$ticket->ID()
727
+				)
728
+			);
729
+		}
730
+		$events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
731
+		if (!$events_sub_total instanceof EE_Line_Item) {
732
+			throw new EE_Error(
733
+				sprintf(
734
+					__('There is no events sub-total for ticket %s on total line item %d', 'event_espresso'),
735
+					$ticket->ID(),
736
+					$grand_total->ID()
737
+				)
738
+			);
739
+		}
740
+		return $events_sub_total;
741
+	}
742
+
743
+
744
+	/**
745
+	 * Gets the event line item
746
+	 *
747
+	 * @param EE_Line_Item $grand_total
748
+	 * @param EE_Event $event
749
+	 * @return EE_Line_Item for the event subtotal which is a child of $grand_total
750
+	 * @throws \EE_Error
751
+	 */
752
+	public static function get_event_line_item(EE_Line_Item $grand_total, $event)
753
+	{
754
+		/** @type EE_Event $event */
755
+		$event = EEM_Event::instance()->ensure_is_obj($event, true);
756
+		$event_line_item = NULL;
757
+		$found = false;
758
+		foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
759
+			// default event subtotal, we should only ever find this the first time this method is called
760
+			if (!$event_line_item->OBJ_ID()) {
761
+				// let's use this! but first... set the event details
762
+				EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
763
+				$found = true;
764
+				break;
765
+			} else if ($event_line_item->OBJ_ID() === $event->ID()) {
766
+				// found existing line item for this event in the cart, so break out of loop and use this one
767
+				$found = true;
768
+				break;
769
+			}
770
+		}
771
+		if (!$found) {
772
+			//there is no event sub-total yet, so add it
773
+			$pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
774
+			// create a new "event" subtotal below that
775
+			$event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
776
+			// and set the event details
777
+			EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
778
+		}
779
+		return $event_line_item;
780
+	}
781
+
782
+
783
+	/**
784
+	 * Creates a default items subtotal line item
785
+	 *
786
+	 * @param EE_Line_Item $event_line_item
787
+	 * @param EE_Event $event
788
+	 * @param EE_Transaction $transaction
789
+	 * @return EE_Line_Item
790
+	 * @throws \EE_Error
791
+	 */
792
+	public static function set_event_subtotal_details(
793
+		EE_Line_Item $event_line_item,
794
+		EE_Event $event,
795
+		$transaction = null
796
+	)
797
+	{
798
+		if ($event instanceof EE_Event) {
799
+			$event_line_item->set_code(self::get_event_code($event));
800
+			$event_line_item->set_name(self::get_event_name($event));
801
+			$event_line_item->set_desc(self::get_event_desc($event));
802
+			$event_line_item->set_OBJ_ID($event->ID());
803
+		}
804
+		self::set_TXN_ID($event_line_item, $transaction);
805
+	}
806
+
807
+
808
+	/**
809
+	 * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
810
+	 * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
811
+	 * any old taxes are removed
812
+	 *
813
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
814
+	 * @throws \EE_Error
815
+	 */
816
+	public static function apply_taxes(EE_Line_Item $total_line_item)
817
+	{
818
+		/** @type EEM_Price $EEM_Price */
819
+		$EEM_Price = EE_Registry::instance()->load_model('Price');
820
+		// get array of taxes via Price Model
821
+		$ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes();
822
+		ksort($ordered_taxes);
823
+		$taxes_line_item = self::get_taxes_subtotal($total_line_item);
824
+		//just to be safe, remove its old tax line items
825
+		$taxes_line_item->delete_children_line_items();
826
+		//loop thru taxes
827
+		foreach ($ordered_taxes as $order => $taxes) {
828
+			foreach ($taxes as $tax) {
829
+				if ($tax instanceof EE_Price) {
830
+					$tax_line_item = EE_Line_Item::new_instance(
831
+						array(
832
+							'LIN_name' => $tax->name(),
833
+							'LIN_desc' => $tax->desc(),
834
+							'LIN_percent' => $tax->amount(),
835
+							'LIN_is_taxable' => false,
836
+							'LIN_order' => $order,
837
+							'LIN_total' => 0,
838
+							'LIN_type' => EEM_Line_Item::type_tax,
839
+							'OBJ_type' => 'Price',
840
+							'OBJ_ID' => $tax->ID()
841
+						)
842
+					);
843
+					$tax_line_item = apply_filters(
844
+						'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
845
+						$tax_line_item
846
+					);
847
+					$taxes_line_item->add_child_line_item($tax_line_item);
848
+				}
849
+			}
850
+		}
851
+		$total_line_item->recalculate_total_including_taxes();
852
+	}
853
+
854
+
855
+	/**
856
+	 * Ensures that taxes have been applied to the order, if not applies them.
857
+	 * Returns the total amount of tax
858
+	 *
859
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
860
+	 * @return float
861
+	 * @throws \EE_Error
862
+	 */
863
+	public static function ensure_taxes_applied($total_line_item)
864
+	{
865
+		$taxes_subtotal = self::get_taxes_subtotal($total_line_item);
866
+		if (!$taxes_subtotal->children()) {
867
+			self::apply_taxes($total_line_item);
868
+		}
869
+		return $taxes_subtotal->total();
870
+	}
871
+
872
+
873
+	/**
874
+	 * Deletes ALL children of the passed line item
875
+	 *
876
+	 * @param EE_Line_Item $parent_line_item
877
+	 * @return bool
878
+	 * @throws \EE_Error
879
+	 */
880
+	public static function delete_all_child_items(EE_Line_Item $parent_line_item)
881
+	{
882
+		$deleted = 0;
883
+		foreach ($parent_line_item->children() as $child_line_item) {
884
+			if ($child_line_item instanceof EE_Line_Item) {
885
+				$deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
886
+				if ($child_line_item->ID()) {
887
+					$child_line_item->delete();
888
+					unset($child_line_item);
889
+				} else {
890
+					$parent_line_item->delete_child_line_item($child_line_item->code());
891
+				}
892
+				$deleted++;
893
+			}
894
+		}
895
+		return $deleted;
896
+	}
897
+
898
+
899
+	/**
900
+	 * Deletes the line items as indicated by the line item code(s) provided,
901
+	 * regardless of where they're found in the line item tree. Automatically
902
+	 * re-calculates the line item totals and updates the related transaction. But
903
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
904
+	 * should probably change because of this).
905
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
906
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
907
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
908
+	 * @param array|bool|string $line_item_codes
909
+	 * @return int number of items successfully removed
910
+	 */
911
+	public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = FALSE)
912
+	{
913
+
914
+		if ($total_line_item->type() !== EEM_Line_Item::type_total) {
915
+			EE_Error::doing_it_wrong(
916
+				'EEH_Line_Item::delete_items',
917
+				__(
918
+					'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
919
+					'event_espresso'
920
+				),
921
+				'4.6.18'
922
+			);
923
+		}
924
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
925
+
926
+		// check if only a single line_item_id was passed
927
+		if (!empty($line_item_codes) && !is_array($line_item_codes)) {
928
+			// place single line_item_id in an array to appear as multiple line_item_ids
929
+			$line_item_codes = array($line_item_codes);
930
+		}
931
+		$removals = 0;
932
+		// cycle thru line_item_ids
933
+		foreach ($line_item_codes as $line_item_id) {
934
+			$removals += $total_line_item->delete_child_line_item($line_item_id);
935
+		}
936
+
937
+		if ($removals > 0) {
938
+			$total_line_item->recalculate_taxes_and_tax_total();
939
+			return $removals;
940
+		} else {
941
+			return FALSE;
942
+		}
943
+	}
944
+
945
+
946
+	/**
947
+	 * Overwrites the previous tax by clearing out the old taxes, and creates a new
948
+	 * tax and updates the total line item accordingly
949
+	 *
950
+	 * @param EE_Line_Item $total_line_item
951
+	 * @param float $amount
952
+	 * @param string $name
953
+	 * @param string $description
954
+	 * @param string $code
955
+	 * @param boolean $add_to_existing_line_item
956
+	 *                          if true, and a duplicate line item with the same code is found,
957
+	 *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
958
+	 * @return EE_Line_Item the new tax line item created
959
+	 * @throws \EE_Error
960
+	 */
961
+	public static function set_total_tax_to(
962
+		EE_Line_Item $total_line_item,
963
+		$amount,
964
+		$name = null,
965
+		$description = null,
966
+		$code = null,
967
+		$add_to_existing_line_item = false
968
+	)
969
+	{
970
+		$tax_subtotal = self::get_taxes_subtotal($total_line_item);
971
+		$taxable_total = $total_line_item->taxable_total();
972
+
973
+		if ($add_to_existing_line_item) {
974
+			$new_tax = $tax_subtotal->get_child_line_item($code);
975
+			EEM_Line_Item::instance()->delete(
976
+				array(array('LIN_code' => array('!=', $code), 'LIN_parent' => $tax_subtotal->ID()))
977
+			);
978
+		} else {
979
+			$new_tax = null;
980
+			$tax_subtotal->delete_children_line_items();
981
+		}
982
+		if ($new_tax) {
983
+			$new_tax->set_total($new_tax->total() + $amount);
984
+			$new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
985
+		} else {
986
+			//no existing tax item. Create it
987
+			$new_tax = EE_Line_Item::new_instance(array(
988
+				'TXN_ID' => $total_line_item->TXN_ID(),
989
+				'LIN_name' => $name ? $name : __('Tax', 'event_espresso'),
990
+				'LIN_desc' => $description ? $description : '',
991
+				'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
992
+				'LIN_total' => $amount,
993
+				'LIN_parent' => $tax_subtotal->ID(),
994
+				'LIN_type' => EEM_Line_Item::type_tax,
995
+				'LIN_code' => $code
996
+			));
997
+		}
998
+
999
+		$new_tax = apply_filters(
1000
+			'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
1001
+			$new_tax,
1002
+			$total_line_item
1003
+		);
1004
+		$new_tax->save();
1005
+		$tax_subtotal->set_total($new_tax->total());
1006
+		$tax_subtotal->save();
1007
+		$total_line_item->recalculate_total_including_taxes();
1008
+		return $new_tax;
1009
+	}
1010
+
1011
+
1012
+	/**
1013
+	 * Makes all the line items which are children of $line_item taxable (or not).
1014
+	 * Does NOT save the line items
1015
+	 * @param EE_Line_Item $line_item
1016
+	 * @param string $code_substring_for_whitelist if this string is part of the line item's code
1017
+	 *  it will be whitelisted (ie, except from becoming taxable)
1018
+	 * @param boolean $taxable
1019
+	 */
1020
+	public static function set_line_items_taxable(
1021
+		EE_Line_Item $line_item,
1022
+		$taxable = true,
1023
+		$code_substring_for_whitelist = null
1024
+	)
1025
+	{
1026
+		$whitelisted = false;
1027
+		if ($code_substring_for_whitelist !== null) {
1028
+			$whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false ? true : false;
1029
+		}
1030
+		if (!$whitelisted && $line_item->is_line_item()) {
1031
+			$line_item->set_is_taxable($taxable);
1032
+		}
1033
+		foreach ($line_item->children() as $child_line_item) {
1034
+			EEH_Line_Item::set_line_items_taxable($child_line_item, $taxable, $code_substring_for_whitelist);
1035
+		}
1036
+	}
1037
+
1038
+
1039
+	/**
1040
+	 * Gets all descendants that are event subtotals
1041
+	 *
1042
+	 * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1043
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1044
+	 * @return EE_Line_Item[]
1045
+	 */
1046
+	public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1047
+	{
1048
+		return self::get_subtotals_of_object_type($parent_line_item, 'Event');
1049
+	}
1050
+
1051
+
1052
+	/**
1053
+	 * Gets all descendants subtotals that match the supplied object type
1054
+	 *
1055
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1056
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1057
+	 * @param string $obj_type
1058
+	 * @return EE_Line_Item[]
1059
+	 */
1060
+	public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1061
+	{
1062
+		return self::_get_descendants_by_type_and_object_type(
1063
+			$parent_line_item,
1064
+			EEM_Line_Item::type_sub_total,
1065
+			$obj_type
1066
+		);
1067
+	}
1068
+
1069
+
1070
+	/**
1071
+	 * Gets all descendants that are tickets
1072
+	 *
1073
+	 * @uses  EEH_Line_Item::get_line_items_of_object_type()
1074
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1075
+	 * @return EE_Line_Item[]
1076
+	 */
1077
+	public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1078
+	{
1079
+		return self::get_line_items_of_object_type($parent_line_item, 'Ticket');
1080
+	}
1081
+
1082
+
1083
+	/**
1084
+	 * Gets all descendants subtotals that match the supplied object type
1085
+	 *
1086
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1087
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1088
+	 * @param string $obj_type
1089
+	 * @return EE_Line_Item[]
1090
+	 */
1091
+	public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1092
+	{
1093
+		return self::_get_descendants_by_type_and_object_type($parent_line_item, EEM_Line_Item::type_line_item, $obj_type);
1094
+	}
1095
+
1096
+
1097
+	/**
1098
+	 * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1099
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1100
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1101
+	 * @return EE_Line_Item[]
1102
+	 */
1103
+	public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1104
+	{
1105
+		return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_tax);
1106
+	}
1107
+
1108
+
1109
+	/**
1110
+	 * Gets all the real items purchased which are children of this item
1111
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1112
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1113
+	 * @return EE_Line_Item[]
1114
+	 */
1115
+	public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1116
+	{
1117
+		return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_line_item);
1118
+	}
1119
+
1120
+
1121
+	/**
1122
+	 * Gets all descendants of supplied line item that match the supplied line item type
1123
+	 *
1124
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1125
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1126
+	 * @param string $line_item_type one of the EEM_Line_Item constants
1127
+	 * @return EE_Line_Item[]
1128
+	 */
1129
+	public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1130
+	{
1131
+		return self::_get_descendants_by_type_and_object_type($parent_line_item, $line_item_type, NULL);
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1137
+	 *
1138
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1139
+	 * @param string $line_item_type one of the EEM_Line_Item constants
1140
+	 * @param string | NULL $obj_type object model class name (minus prefix) or NULL to ignore object type when searching
1141
+	 * @return EE_Line_Item[]
1142
+	 */
1143
+	protected static function _get_descendants_by_type_and_object_type(
1144
+		EE_Line_Item $parent_line_item,
1145
+		$line_item_type,
1146
+		$obj_type = null
1147
+	)
1148
+	{
1149
+		$objects = array();
1150
+		foreach ($parent_line_item->children() as $child_line_item) {
1151
+			if ($child_line_item instanceof EE_Line_Item) {
1152
+				if (
1153
+					$child_line_item->type() === $line_item_type
1154
+					&& (
1155
+						$child_line_item->OBJ_type() === $obj_type || $obj_type === null
1156
+					)
1157
+				) {
1158
+					$objects[] = $child_line_item;
1159
+				} else {
1160
+					//go-through-all-its children looking for more matches
1161
+					$objects = array_merge(
1162
+						$objects,
1163
+						self::_get_descendants_by_type_and_object_type(
1164
+							$child_line_item,
1165
+							$line_item_type,
1166
+							$obj_type
1167
+						)
1168
+					);
1169
+				}
1170
+			}
1171
+		}
1172
+		return $objects;
1173
+	}
1174
+
1175
+
1176
+	/**
1177
+	 * Gets all descendants subtotals that match the supplied object type
1178
+	 *
1179
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1180
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1181
+	 * @param string $OBJ_type object type (like Event)
1182
+	 * @param array $OBJ_IDs array of OBJ_IDs
1183
+	 * @return EE_Line_Item[]
1184
+	 */
1185
+	public static function get_line_items_by_object_type_and_IDs(
1186
+		EE_Line_Item $parent_line_item,
1187
+		$OBJ_type = '',
1188
+		$OBJ_IDs = array()
1189
+	)
1190
+	{
1191
+		return self::_get_descendants_by_object_type_and_object_ID($parent_line_item, $OBJ_type, $OBJ_IDs);
1192
+	}
1193
+
1194
+
1195
+	/**
1196
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1197
+	 *
1198
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1199
+	 * @param string $OBJ_type object type (like Event)
1200
+	 * @param array $OBJ_IDs array of OBJ_IDs
1201
+	 * @return EE_Line_Item[]
1202
+	 */
1203
+	protected static function _get_descendants_by_object_type_and_object_ID(
1204
+		EE_Line_Item $parent_line_item,
1205
+		$OBJ_type,
1206
+		$OBJ_IDs
1207
+	)
1208
+	{
1209
+		$objects = array();
1210
+		foreach ($parent_line_item->children() as $child_line_item) {
1211
+			if ($child_line_item instanceof EE_Line_Item) {
1212
+				if (
1213
+					$child_line_item->OBJ_type() === $OBJ_type
1214
+					&& is_array($OBJ_IDs)
1215
+					&& in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1216
+				) {
1217
+					$objects[] = $child_line_item;
1218
+				} else {
1219
+					//go-through-all-its children looking for more matches
1220
+					$objects = array_merge(
1221
+						$objects,
1222
+						self::_get_descendants_by_object_type_and_object_ID(
1223
+							$child_line_item,
1224
+							$OBJ_type,
1225
+							$OBJ_IDs
1226
+						)
1227
+					);
1228
+				}
1229
+			}
1230
+		}
1231
+		return $objects;
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * Uses a breadth-first-search in order to find the nearest descendant of
1237
+	 * the specified type and returns it, else NULL
1238
+	 *
1239
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1240
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1241
+	 * @param string $type like one of the EEM_Line_Item::type_*
1242
+	 * @return EE_Line_Item
1243
+	 */
1244
+	public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1245
+	{
1246
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1247
+	}
1248
+
1249
+
1250
+	/**
1251
+	 * Uses a breadth-first-search in order to find the nearest descendant
1252
+	 * having the specified LIN_code and returns it, else NULL
1253
+	 *
1254
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1255
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1256
+	 * @param string $code any value used for LIN_code
1257
+	 * @return EE_Line_Item
1258
+	 */
1259
+	public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1260
+	{
1261
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1262
+	}
1263
+
1264
+
1265
+	/**
1266
+	 * Uses a breadth-first-search in order to find the nearest descendant
1267
+	 * having the specified LIN_code and returns it, else NULL
1268
+	 *
1269
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1270
+	 * @param string $search_field name of EE_Line_Item property
1271
+	 * @param string $value any value stored in $search_field
1272
+	 * @return EE_Line_Item
1273
+	 */
1274
+	protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1275
+	{
1276
+		foreach ($parent_line_item->children() as $child) {
1277
+			if ($child->get($search_field) == $value) {
1278
+				return $child;
1279
+			}
1280
+		}
1281
+		foreach ($parent_line_item->children() as $child) {
1282
+			$descendant_found = self::_get_nearest_descendant($child, $search_field, $value);
1283
+			if ($descendant_found) {
1284
+				return $descendant_found;
1285
+			}
1286
+		}
1287
+		return NULL;
1288
+	}
1289
+
1290
+
1291
+	/**
1292
+	 * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1293
+	 * else recursively walks up the line item tree until a parent of type total is found,
1294
+	 *
1295
+	 * @param EE_Line_Item $line_item
1296
+	 * @return \EE_Line_Item
1297
+	 * @throws \EE_Error
1298
+	 */
1299
+	public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item)
1300
+	{
1301
+		if ($line_item->TXN_ID()) {
1302
+			$total_line_item = $line_item->transaction()->total_line_item(false);
1303
+			if ($total_line_item instanceof EE_Line_Item) {
1304
+				return $total_line_item;
1305
+			}
1306
+		} else {
1307
+			$line_item_parent = $line_item->parent();
1308
+			if ($line_item_parent instanceof EE_Line_Item) {
1309
+				if ($line_item_parent->is_total()) {
1310
+					return $line_item_parent;
1311
+				}
1312
+				return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1313
+			}
1314
+		}
1315
+		throw new EE_Error(
1316
+			sprintf(
1317
+				__('A valid grand total for line item %1$d was not found.', 'event_espresso'),
1318
+				$line_item->ID()
1319
+			)
1320
+		);
1321
+	}
1322
+
1323
+
1324
+	/**
1325
+	 * Prints out a representation of the line item tree
1326
+	 *
1327
+	 * @param EE_Line_Item $line_item
1328
+	 * @param int $indentation
1329
+	 * @return void
1330
+	 * @throws \EE_Error
1331
+	 */
1332
+	public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1333
+	{
1334
+		echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1335
+		if (!$indentation) {
1336
+			echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1337
+		}
1338
+		for ($i = 0; $i < $indentation; $i++) {
1339
+			echo ". ";
1340
+		}
1341
+		$breakdown = '';
1342
+		if ($line_item->is_line_item()) {
1343
+			if ($line_item->is_percent()) {
1344
+				$breakdown = "{$line_item->percent()}%";
1345
+			} else {
1346
+				$breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1347
+			}
1348
+		}
1349
+		echo $line_item->name() . " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : " . '$' . "{$line_item->total()}";
1350
+		if ($breakdown) {
1351
+			echo " ( {$breakdown} )";
1352
+		}
1353
+		if ($line_item->is_taxable()) {
1354
+			echo "  * taxable";
1355
+		}
1356
+		if ($line_item->children()) {
1357
+			foreach ($line_item->children() as $child) {
1358
+				self::visualize($child, $indentation + 1);
1359
+			}
1360
+		}
1361
+	}
1362
+
1363
+
1364
+	/**
1365
+	 * Calculates the registration's final price, taking into account that they
1366
+	 * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1367
+	 * and receive a portion of any transaction-wide discounts.
1368
+	 * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1369
+	 * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1370
+	 * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1371
+	 * and brent's final price should be $5.50.
1372
+	 *
1373
+	 * In order to do this, we basically need to traverse the line item tree calculating
1374
+	 * the running totals (just as if we were recalculating the total), but when we identify
1375
+	 * regular line items, we need to keep track of their share of the grand total.
1376
+	 * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1377
+	 * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1378
+	 * when there are non-taxable items; otherwise they would be the same)
1379
+	 *
1380
+	 * @param EE_Line_Item $line_item
1381
+	 * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity that
1382
+	 *                                                                            can be included in price calculations at this moment
1383
+	 * @return array        keys are line items for tickets IDs and values are their share of the running total,
1384
+	 *                                          plus the key 'total', and 'taxable' which also has keys of all the ticket IDs. Eg
1385
+	 *                                          array(
1386
+	 *                                          12 => 4.3
1387
+	 *                                          23 => 8.0
1388
+	 *                                          'total' => 16.6,
1389
+	 *                                          'taxable' => array(
1390
+	 *                                          12 => 10,
1391
+	 *                                          23 => 4
1392
+	 *                                          ).
1393
+	 *                                          So to find which registrations have which final price, we need to find which line item
1394
+	 *                                          is theirs, which can be done with
1395
+	 *                                          `EEM_Line_Item::instance()->get_line_item_for_registration( $registration );`
1396
+	 */
1397
+	public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array())
1398
+	{
1399
+		//init running grand total if not already
1400
+		if (!isset($running_totals['total'])) {
1401
+			$running_totals['total'] = 0;
1402
+		}
1403
+		if (!isset($running_totals['taxable'])) {
1404
+			$running_totals['taxable'] = array('total' => 0);
1405
+		}
1406
+		foreach ($line_item->children() as $child_line_item) {
1407
+			switch ($child_line_item->type()) {
1408
+
1409
+				case EEM_Line_Item::type_sub_total :
1410
+					$running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item($child_line_item, $billable_ticket_quantities);
1411
+					//combine arrays but preserve numeric keys
1412
+					$running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1413
+					$running_totals['total'] += $running_totals_from_subtotal['total'];
1414
+					$running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1415
+					break;
1416
+
1417
+				case EEM_Line_Item::type_tax_sub_total :
1418
+
1419
+					//find how much the taxes percentage is
1420
+					if ($child_line_item->percent() !== 0) {
1421
+						$tax_percent_decimal = $child_line_item->percent() / 100;
1422
+					} else {
1423
+						$tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1424
+					}
1425
+					//and apply to all the taxable totals, and add to the pretax totals
1426
+					foreach ($running_totals as $line_item_id => $this_running_total) {
1427
+						//"total" and "taxable" array key is an exception
1428
+						if ($line_item_id === 'taxable') {
1429
+							continue;
1430
+						}
1431
+						$taxable_total = $running_totals['taxable'][$line_item_id];
1432
+						$running_totals[$line_item_id] += ($taxable_total * $tax_percent_decimal);
1433
+					}
1434
+					break;
1435
+
1436
+				case EEM_Line_Item::type_line_item :
1437
+
1438
+					// ticket line items or ????
1439
+					if ($child_line_item->OBJ_type() === 'Ticket') {
1440
+						// kk it's a ticket
1441
+						if (isset($running_totals[$child_line_item->ID()])) {
1442
+							//huh? that shouldn't happen.
1443
+							$running_totals['total'] += $child_line_item->total();
1444
+						} else {
1445
+							//its not in our running totals yet. great.
1446
+							if ($child_line_item->is_taxable()) {
1447
+								$taxable_amount = $child_line_item->unit_price();
1448
+							} else {
1449
+								$taxable_amount = 0;
1450
+							}
1451
+							// are we only calculating totals for some tickets?
1452
+							if (isset($billable_ticket_quantities[$child_line_item->OBJ_ID()])) {
1453
+								$quantity = $billable_ticket_quantities[$child_line_item->OBJ_ID()];
1454
+								$running_totals[$child_line_item->ID()] = $quantity
1455
+									? $child_line_item->unit_price()
1456
+									: 0;
1457
+								$running_totals['taxable'][$child_line_item->ID()] = $quantity
1458
+									? $taxable_amount
1459
+									: 0;
1460
+							} else {
1461
+								$quantity = $child_line_item->quantity();
1462
+								$running_totals[$child_line_item->ID()] = $child_line_item->unit_price();
1463
+								$running_totals['taxable'][$child_line_item->ID()] = $taxable_amount;
1464
+							}
1465
+							$running_totals['taxable']['total'] += $taxable_amount * $quantity;
1466
+							$running_totals['total'] += $child_line_item->unit_price() * $quantity;
1467
+						}
1468
+					} else {
1469
+						// it's some other type of item added to the cart
1470
+						// it should affect the running totals
1471
+						// basically we want to convert it into a PERCENT modifier. Because
1472
+						// more clearly affect all registration's final price equally
1473
+						$line_items_percent_of_running_total = $running_totals['total'] > 0
1474
+							? ($child_line_item->total() / $running_totals['total']) + 1
1475
+							: 1;
1476
+						foreach ($running_totals as $line_item_id => $this_running_total) {
1477
+							//the "taxable" array key is an exception
1478
+							if ($line_item_id === 'taxable') {
1479
+								continue;
1480
+							}
1481
+							// update the running totals
1482
+							// yes this actually even works for the running grand total!
1483
+							$running_totals[$line_item_id] =
1484
+								$line_items_percent_of_running_total * $this_running_total;
1485
+
1486
+							if ($child_line_item->is_taxable()) {
1487
+								$running_totals['taxable'][$line_item_id] =
1488
+									$line_items_percent_of_running_total * $running_totals['taxable'][$line_item_id];
1489
+							}
1490
+						}
1491
+					}
1492
+					break;
1493
+			}
1494
+		}
1495
+		return $running_totals;
1496
+	}
1497
+
1498
+
1499
+	/**
1500
+	 * @param \EE_Line_Item $total_line_item
1501
+	 * @param \EE_Line_Item $ticket_line_item
1502
+	 * @return float | null
1503
+	 * @throws \OutOfRangeException
1504
+	 */
1505
+	public static function calculate_final_price_for_ticket_line_item(\EE_Line_Item $total_line_item, \EE_Line_Item $ticket_line_item)
1506
+	{
1507
+		static $final_prices_per_ticket_line_item = array();
1508
+		if (empty($final_prices_per_ticket_line_item)) {
1509
+			$final_prices_per_ticket_line_item = \EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1510
+				$total_line_item
1511
+			);
1512
+		}
1513
+		//ok now find this new registration's final price
1514
+		if (isset($final_prices_per_ticket_line_item[$ticket_line_item->ID()])) {
1515
+			return $final_prices_per_ticket_line_item[$ticket_line_item->ID()];
1516
+		}
1517
+		$message = sprintf(
1518
+			__(
1519
+				'The final price for the ticket line item (ID:%1$d) could not be calculated.',
1520
+				'event_espresso'
1521
+			),
1522
+			$ticket_line_item->ID()
1523
+		);
1524
+		if (WP_DEBUG) {
1525
+			$message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1526
+			throw new \OutOfRangeException($message);
1527
+		} else {
1528
+			EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1529
+		}
1530
+		return null;
1531
+	}
1532
+
1533
+
1534
+	/**
1535
+	 * Creates a duplicate of the line item tree, except only includes billable items
1536
+	 * and the portion of line items attributed to billable things
1537
+	 *
1538
+	 * @param EE_Line_Item $line_item
1539
+	 * @param EE_Registration[] $registrations
1540
+	 * @return \EE_Line_Item
1541
+	 * @throws \EE_Error
1542
+	 */
1543
+	public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1544
+	{
1545
+		$copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1546
+		foreach ($line_item->children() as $child_li) {
1547
+			$copy_li->add_child_line_item(EEH_Line_Item::billable_line_item_tree($child_li, $registrations));
1548
+		}
1549
+		//if this is the grand total line item, make sure the totals all add up
1550
+		//(we could have duplicated this logic AS we copied the line items, but
1551
+		//it seems DRYer this way)
1552
+		if ($copy_li->type() === EEM_Line_Item::type_total) {
1553
+			$copy_li->recalculate_total_including_taxes();
1554
+		}
1555
+		return $copy_li;
1556
+	}
1557
+
1558
+
1559
+	/**
1560
+	 * Creates a new, unsaved line item from $line_item that factors in the
1561
+	 * number of billable registrations on $registrations.
1562
+	 *
1563
+	 * @param EE_Line_Item $line_item
1564
+	 * @return EE_Line_Item
1565
+	 * @throws \EE_Error
1566
+	 * @param EE_Registration[] $registrations
1567
+	 */
1568
+	public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1569
+	{
1570
+		$new_li_fields = $line_item->model_field_array();
1571
+		if ($line_item->type() === EEM_Line_Item::type_line_item &&
1572
+			$line_item->OBJ_type() === 'Ticket'
1573
+		) {
1574
+			$count = 0;
1575
+			foreach ($registrations as $registration) {
1576
+				if ($line_item->OBJ_ID() === $registration->ticket_ID() &&
1577
+					in_array($registration->status_ID(), EEM_Registration::reg_statuses_that_allow_payment())
1578
+				) {
1579
+					$count++;
1580
+				}
1581
+			}
1582
+			$new_li_fields['LIN_quantity'] = $count;
1583
+		}
1584
+		//don't set the total. We'll leave that up to the code that calculates it
1585
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1586
+		return EE_Line_Item::new_instance($new_li_fields);
1587
+	}
1588
+
1589
+
1590
+	/**
1591
+	 * Returns a modified line item tree where all the subtotals which have a total of 0
1592
+	 * are removed, and line items with a quantity of 0
1593
+	 *
1594
+	 * @param EE_Line_Item $line_item |null
1595
+	 * @return \EE_Line_Item|null
1596
+	 * @throws \EE_Error
1597
+	 */
1598
+	public static function non_empty_line_items(EE_Line_Item $line_item)
1599
+	{
1600
+		$copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1601
+		if ($copied_li === null) {
1602
+			return null;
1603
+		}
1604
+		//if this is an event subtotal, we want to only include it if it
1605
+		//has a non-zero total and at least one ticket line item child
1606
+		$ticket_children = 0;
1607
+		foreach ($line_item->children() as $child_li) {
1608
+			$child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1609
+			if ($child_li_copy !== null) {
1610
+				$copied_li->add_child_line_item($child_li_copy);
1611
+				if ($child_li_copy->type() === EEM_Line_Item::type_line_item &&
1612
+					$child_li_copy->OBJ_type() === 'Ticket'
1613
+				) {
1614
+					$ticket_children++;
1615
+				}
1616
+			}
1617
+		}
1618
+		//if this is an event subtotal with NO ticket children
1619
+		//we basically want to ignore it
1620
+		if (
1621
+			$ticket_children === 0
1622
+			&& $line_item->type() === EEM_Line_Item::type_sub_total
1623
+			&& $line_item->OBJ_type() === 'Event'
1624
+			&& $line_item->total() === 0
1625
+		) {
1626
+			return null;
1627
+		}
1628
+		return $copied_li;
1629
+	}
1630
+
1631
+
1632
+	/**
1633
+	 * Creates a new, unsaved line item, but if it's a ticket line item
1634
+	 * with a total of 0, or a subtotal of 0, returns null instead
1635
+	 *
1636
+	 * @param EE_Line_Item $line_item
1637
+	 * @return EE_Line_Item
1638
+	 * @throws \EE_Error
1639
+	 */
1640
+	public static function non_empty_line_item(EE_Line_Item $line_item)
1641
+	{
1642
+		if ($line_item->type() === EEM_Line_Item::type_line_item &&
1643
+			$line_item->OBJ_type() === 'Ticket' &&
1644
+			$line_item->quantity() === 0
1645
+		) {
1646
+			return null;
1647
+		}
1648
+		$new_li_fields = $line_item->model_field_array();
1649
+		//don't set the total. We'll leave that up to the code that calculates it
1650
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1651
+		return EE_Line_Item::new_instance($new_li_fields);
1652
+	}
1653
+
1654
+
1655
+
1656
+	/**************************************** @DEPRECATED METHODS *************************************** */
1657
+	/**
1658
+	 * @deprecated
1659
+	 * @param EE_Line_Item $total_line_item
1660
+	 * @return \EE_Line_Item
1661
+	 * @throws \EE_Error
1662
+	 */
1663
+	public static function get_items_subtotal(EE_Line_Item $total_line_item)
1664
+	{
1665
+		EE_Error::doing_it_wrong('EEH_Line_Item::get_items_subtotal()', __('Method replaced with EEH_Line_Item::get_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1666
+		return self::get_pre_tax_subtotal($total_line_item);
1667
+	}
1668
+
1669
+
1670
+	/**
1671
+	 * @deprecated
1672
+	 * @param EE_Transaction $transaction
1673
+	 * @return \EE_Line_Item
1674
+	 * @throws \EE_Error
1675
+	 */
1676
+	public static function create_default_total_line_item($transaction = NULL)
1677
+	{
1678
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_total_line_item()', __('Method replaced with EEH_Line_Item::create_total_line_item()', 'event_espresso'), '4.6.0');
1679
+		return self::create_total_line_item($transaction);
1680
+	}
1681
+
1682
+
1683
+	/**
1684
+	 * @deprecated
1685
+	 * @param EE_Line_Item $total_line_item
1686
+	 * @param EE_Transaction $transaction
1687
+	 * @return \EE_Line_Item
1688
+	 * @throws \EE_Error
1689
+	 */
1690
+	public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1691
+	{
1692
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_tickets_subtotal()', __('Method replaced with EEH_Line_Item::create_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1693
+		return self::create_pre_tax_subtotal($total_line_item, $transaction);
1694
+	}
1695
+
1696
+
1697
+	/**
1698
+	 * @deprecated
1699
+	 * @param EE_Line_Item $total_line_item
1700
+	 * @param EE_Transaction $transaction
1701
+	 * @return \EE_Line_Item
1702
+	 * @throws \EE_Error
1703
+	 */
1704
+	public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1705
+	{
1706
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_taxes_subtotal()', __('Method replaced with EEH_Line_Item::create_taxes_subtotal()', 'event_espresso'), '4.6.0');
1707
+		return self::create_taxes_subtotal($total_line_item, $transaction);
1708
+	}
1709
+
1710
+
1711
+	/**
1712
+	 * @deprecated
1713
+	 * @param EE_Line_Item $total_line_item
1714
+	 * @param EE_Transaction $transaction
1715
+	 * @return \EE_Line_Item
1716
+	 * @throws \EE_Error
1717
+	 */
1718
+	public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1719
+	{
1720
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_event_subtotal()', __('Method replaced with EEH_Line_Item::create_event_subtotal()', 'event_espresso'), '4.6.0');
1721
+		return self::create_event_subtotal($total_line_item, $transaction);
1722
+	}
1723 1723
 
1724 1724
 
1725 1725
 }
Please login to merge, or discard this patch.
Spacing   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@  discard block
 block discarded – undo
1
-<?php if (!defined('EVENT_ESPRESSO_VERSION')) {
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2 2
     exit('No direct script access allowed');
3 3
 }
4 4
 
@@ -56,7 +56,7 @@  discard block
 block discarded – undo
56 56
             'LIN_percent' => null,
57 57
             'LIN_is_taxable' => $taxable,
58 58
             'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0,
59
-            'LIN_total' => (float)$unit_price * (int)$quantity,
59
+            'LIN_total' => (float) $unit_price * (int) $quantity,
60 60
             'LIN_type' => EEM_Line_Item::type_line_item,
61 61
             'LIN_code' => $code,
62 62
         ));
@@ -95,7 +95,7 @@  discard block
 block discarded – undo
95 95
             'LIN_percent' => $percentage_amount,
96 96
             'LIN_quantity' => 1,
97 97
             'LIN_is_taxable' => $taxable,
98
-            'LIN_total' => (float)($percentage_amount * ($parent_line_item->total() / 100)),
98
+            'LIN_total' => (float) ($percentage_amount * ($parent_line_item->total() / 100)),
99 99
             'LIN_type' => EEM_Line_Item::type_line_item,
100 100
             'LIN_parent' => $parent_line_item->ID()
101 101
         ));
@@ -125,13 +125,13 @@  discard block
 block discarded – undo
125 125
      */
126 126
     public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
127 127
     {
128
-        if (!$total_line_item instanceof EE_Line_Item || !$total_line_item->is_total()) {
128
+        if ( ! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
129 129
             throw new EE_Error(sprintf(__('A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', 'event_espresso'), $ticket->ID(), $total_line_item->ID()));
130 130
         }
131 131
         // either increment the qty for an existing ticket
132 132
         $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
133 133
         // or add a new one
134
-        if (!$line_item instanceof EE_Line_Item) {
134
+        if ( ! $line_item instanceof EE_Line_Item) {
135 135
             $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
136 136
         }
137 137
         $total_line_item->recalculate_total_including_taxes();
@@ -152,10 +152,10 @@  discard block
 block discarded – undo
152 152
         $line_item = null;
153 153
         if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
154 154
             $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
155
-            foreach ((array)$ticket_line_items as $ticket_line_item) {
155
+            foreach ((array) $ticket_line_items as $ticket_line_item) {
156 156
                 if (
157 157
                     $ticket_line_item instanceof EE_Line_Item
158
-                    && (int)$ticket_line_item->OBJ_ID() === (int)$ticket->ID()
158
+                    && (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID()
159 159
                 ) {
160 160
                     $line_item = $ticket_line_item;
161 161
                     break;
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
      */
182 182
     public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
183 183
     {
184
-        if (!$line_item->is_percent()) {
184
+        if ( ! $line_item->is_percent()) {
185 185
             $qty += $line_item->quantity();
186 186
             $line_item->set_quantity($qty);
187 187
             $line_item->set_total($line_item->unit_price() * $qty);
@@ -206,7 +206,7 @@  discard block
 block discarded – undo
206 206
      */
207 207
     public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
208 208
     {
209
-        if (!$line_item->is_percent()) {
209
+        if ( ! $line_item->is_percent()) {
210 210
             $qty = $line_item->quantity() - $qty;
211 211
             $qty = max($qty, 0);
212 212
             $line_item->set_quantity($qty);
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
      */
232 232
     public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
233 233
     {
234
-        if (!$line_item->is_percent()) {
234
+        if ( ! $line_item->is_percent()) {
235 235
             $line_item->set_quantity($new_quantity);
236 236
             $line_item->set_total($line_item->unit_price() * $new_quantity);
237 237
             $line_item->save();
@@ -267,7 +267,7 @@  discard block
 block discarded – undo
267 267
         // add $ticket to cart
268 268
         $line_item = EE_Line_Item::new_instance(array(
269 269
             'LIN_name' => $ticket->name(),
270
-            'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
270
+            'LIN_desc' => $ticket->description() !== '' ? $ticket->description().' '.$event : $event,
271 271
             'LIN_unit_price' => $ticket->price(),
272 272
             'LIN_quantity' => $qty,
273 273
             'LIN_is_taxable' => $ticket->taxable(),
@@ -377,8 +377,8 @@  discard block
 block discarded – undo
377 377
         foreach ($ticket_line_item->children() as $child_line_item) {
378 378
             if (
379 379
                 $child_line_item->is_sub_line_item()
380
-                && !$child_line_item->is_percent()
381
-                && !$child_line_item->is_cancellation()
380
+                && ! $child_line_item->is_percent()
381
+                && ! $child_line_item->is_cancellation()
382 382
             ) {
383 383
                 $child_line_item->set_quantity($child_line_item->quantity() - $qty);
384 384
             }
@@ -400,7 +400,7 @@  discard block
 block discarded – undo
400 400
                 'LIN_desc' => sprintf(
401 401
                     _x('Cancelled %1$s : %2$s', 'Cancelled Ticket Name : 2015-01-01 11:11', 'event_espresso'),
402 402
                     $ticket_line_item->name(),
403
-                    current_time(get_option('date_format') . ' ' . get_option('time_format'))
403
+                    current_time(get_option('date_format').' '.get_option('time_format'))
404 404
                 ),
405 405
                 'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
406 406
                 'LIN_quantity' => $qty,
@@ -453,7 +453,7 @@  discard block
 block discarded – undo
453 453
         );
454 454
         $cancellation_line_item = reset($cancellation_line_item);
455 455
         // verify that this ticket was indeed previously cancelled
456
-        if (!$cancellation_line_item instanceof EE_Line_Item) {
456
+        if ( ! $cancellation_line_item instanceof EE_Line_Item) {
457 457
             return false;
458 458
         }
459 459
         if ($cancellation_line_item->quantity() > $qty) {
@@ -625,7 +625,7 @@  discard block
 block discarded – undo
625 625
             'LIN_code' => 'taxes',
626 626
             'LIN_name' => __('Taxes', 'event_espresso'),
627 627
             'LIN_type' => EEM_Line_Item::type_tax_sub_total,
628
-            'LIN_order' => 1000,//this should always come last
628
+            'LIN_order' => 1000, //this should always come last
629 629
         ));
630 630
         $tax_line_item = apply_filters(
631 631
             'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
@@ -677,7 +677,7 @@  discard block
 block discarded – undo
677 677
      */
678 678
     public static function get_event_code($event)
679 679
     {
680
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
680
+        return 'event-'.($event instanceof EE_Event ? $event->ID() : '0');
681 681
     }
682 682
 
683 683
     /**
@@ -713,13 +713,13 @@  discard block
 block discarded – undo
713 713
     public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
714 714
     {
715 715
         $first_datetime = $ticket->first_datetime();
716
-        if (!$first_datetime instanceof EE_Datetime) {
716
+        if ( ! $first_datetime instanceof EE_Datetime) {
717 717
             throw new EE_Error(
718 718
                 sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())
719 719
             );
720 720
         }
721 721
         $event = $first_datetime->event();
722
-        if (!$event instanceof EE_Event) {
722
+        if ( ! $event instanceof EE_Event) {
723 723
             throw new EE_Error(
724 724
                 sprintf(
725 725
                     __('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'),
@@ -728,7 +728,7 @@  discard block
 block discarded – undo
728 728
             );
729 729
         }
730 730
         $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
731
-        if (!$events_sub_total instanceof EE_Line_Item) {
731
+        if ( ! $events_sub_total instanceof EE_Line_Item) {
732 732
             throw new EE_Error(
733 733
                 sprintf(
734 734
                     __('There is no events sub-total for ticket %s on total line item %d', 'event_espresso'),
@@ -757,7 +757,7 @@  discard block
 block discarded – undo
757 757
         $found = false;
758 758
         foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
759 759
             // default event subtotal, we should only ever find this the first time this method is called
760
-            if (!$event_line_item->OBJ_ID()) {
760
+            if ( ! $event_line_item->OBJ_ID()) {
761 761
                 // let's use this! but first... set the event details
762 762
                 EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
763 763
                 $found = true;
@@ -768,7 +768,7 @@  discard block
 block discarded – undo
768 768
                 break;
769 769
             }
770 770
         }
771
-        if (!$found) {
771
+        if ( ! $found) {
772 772
             //there is no event sub-total yet, so add it
773 773
             $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
774 774
             // create a new "event" subtotal below that
@@ -863,7 +863,7 @@  discard block
 block discarded – undo
863 863
     public static function ensure_taxes_applied($total_line_item)
864 864
     {
865 865
         $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
866
-        if (!$taxes_subtotal->children()) {
866
+        if ( ! $taxes_subtotal->children()) {
867 867
             self::apply_taxes($total_line_item);
868 868
         }
869 869
         return $taxes_subtotal->total();
@@ -924,7 +924,7 @@  discard block
 block discarded – undo
924 924
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
925 925
 
926 926
         // check if only a single line_item_id was passed
927
-        if (!empty($line_item_codes) && !is_array($line_item_codes)) {
927
+        if ( ! empty($line_item_codes) && ! is_array($line_item_codes)) {
928 928
             // place single line_item_id in an array to appear as multiple line_item_ids
929 929
             $line_item_codes = array($line_item_codes);
930 930
         }
@@ -1027,7 +1027,7 @@  discard block
 block discarded – undo
1027 1027
         if ($code_substring_for_whitelist !== null) {
1028 1028
             $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false ? true : false;
1029 1029
         }
1030
-        if (!$whitelisted && $line_item->is_line_item()) {
1030
+        if ( ! $whitelisted && $line_item->is_line_item()) {
1031 1031
             $line_item->set_is_taxable($taxable);
1032 1032
         }
1033 1033
         foreach ($line_item->children() as $child_line_item) {
@@ -1332,7 +1332,7 @@  discard block
 block discarded – undo
1332 1332
     public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1333 1333
     {
1334 1334
         echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1335
-        if (!$indentation) {
1335
+        if ( ! $indentation) {
1336 1336
             echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1337 1337
         }
1338 1338
         for ($i = 0; $i < $indentation; $i++) {
@@ -1343,10 +1343,10 @@  discard block
 block discarded – undo
1343 1343
             if ($line_item->is_percent()) {
1344 1344
                 $breakdown = "{$line_item->percent()}%";
1345 1345
             } else {
1346
-                $breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1346
+                $breakdown = '$'."{$line_item->unit_price()} x {$line_item->quantity()}";
1347 1347
             }
1348 1348
         }
1349
-        echo $line_item->name() . " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : " . '$' . "{$line_item->total()}";
1349
+        echo $line_item->name()." [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : ".'$'."{$line_item->total()}";
1350 1350
         if ($breakdown) {
1351 1351
             echo " ( {$breakdown} )";
1352 1352
         }
@@ -1397,10 +1397,10 @@  discard block
 block discarded – undo
1397 1397
     public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array())
1398 1398
     {
1399 1399
         //init running grand total if not already
1400
-        if (!isset($running_totals['total'])) {
1400
+        if ( ! isset($running_totals['total'])) {
1401 1401
             $running_totals['total'] = 0;
1402 1402
         }
1403
-        if (!isset($running_totals['taxable'])) {
1403
+        if ( ! isset($running_totals['taxable'])) {
1404 1404
             $running_totals['taxable'] = array('total' => 0);
1405 1405
         }
1406 1406
         foreach ($line_item->children() as $child_line_item) {
@@ -1522,7 +1522,7 @@  discard block
 block discarded – undo
1522 1522
             $ticket_line_item->ID()
1523 1523
         );
1524 1524
         if (WP_DEBUG) {
1525
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1525
+            $message .= '<br>'.print_r($final_prices_per_ticket_line_item, true);
1526 1526
             throw new \OutOfRangeException($message);
1527 1527
         } else {
1528 1528
             EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
Please login to merge, or discard this patch.
core/db_classes/EE_Line_Item.class.php 2 patches
Indentation   +1426 added lines, -1426 removed lines patch added patch discarded remove patch
@@ -17,1432 +17,1432 @@
 block discarded – undo
17 17
 class EE_Line_Item extends EE_Base_Class implements EEI_Line_Item
18 18
 {
19 19
 
20
-    /**
21
-     * for children line items (currently not a normal relation)
22
-     *
23
-     * @type EE_Line_Item[]
24
-     */
25
-    protected $_children = array();
26
-
27
-    /**
28
-     * for the parent line item
29
-     *
30
-     * @var EE_Line_Item
31
-     */
32
-    protected $_parent;
33
-
34
-
35
-    /**
36
-     *
37
-     * @param array $props_n_values incoming values
38
-     * @param string $timezone incoming timezone (if not set the timezone set for the website will be
39
-     *                                        used.)
40
-     * @param array $date_formats incoming date_formats in an array where the first value is the
41
-     *                                        date_format and the second value is the time format
42
-     * @return EE_Line_Item
43
-     * @throws EE_Error
44
-     */
45
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
46
-    {
47
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
48
-        return $has_object
49
-            ? $has_object
50
-            : new self($props_n_values, false, $timezone);
51
-    }
52
-
53
-
54
-    /**
55
-     * @param array $props_n_values incoming values from the database
56
-     * @param string $timezone incoming timezone as set by the model.  If not set the timezone for
57
-     *                                the website will be used.
58
-     * @return EE_Line_Item
59
-     * @throws EE_Error
60
-     */
61
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
62
-    {
63
-        return new self($props_n_values, true, $timezone);
64
-    }
65
-
66
-
67
-    /**
68
-     * Adds some defaults if they're not specified
69
-     *
70
-     * @param array $fieldValues
71
-     * @param bool $bydb
72
-     * @param string $timezone
73
-     * @throws EE_Error
74
-     */
75
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
76
-    {
77
-        parent::__construct($fieldValues, $bydb, $timezone);
78
-        if (!$this->get('LIN_code')) {
79
-            $this->set_code($this->generate_code());
80
-        }
81
-    }
82
-
83
-
84
-    /**
85
-     * Gets ID
86
-     *
87
-     * @return int
88
-     * @throws EE_Error
89
-     */
90
-    public function ID()
91
-    {
92
-        return $this->get('LIN_ID');
93
-    }
94
-
95
-
96
-    /**
97
-     * Gets TXN_ID
98
-     *
99
-     * @return int
100
-     * @throws EE_Error
101
-     */
102
-    public function TXN_ID()
103
-    {
104
-        return $this->get('TXN_ID');
105
-    }
106
-
107
-
108
-    /**
109
-     * Sets TXN_ID
110
-     *
111
-     * @param int $TXN_ID
112
-     * @throws EE_Error
113
-     */
114
-    public function set_TXN_ID($TXN_ID)
115
-    {
116
-        $this->set('TXN_ID', $TXN_ID);
117
-    }
118
-
119
-
120
-    /**
121
-     * Gets name
122
-     *
123
-     * @return string
124
-     * @throws EE_Error
125
-     */
126
-    public function name()
127
-    {
128
-        $name = $this->get('LIN_name');
129
-        if (!$name) {
130
-            $name = ucwords(str_replace('-', ' ', $this->type()));
131
-        }
132
-        return $name;
133
-    }
134
-
135
-
136
-    /**
137
-     * Sets name
138
-     *
139
-     * @param string $name
140
-     * @throws EE_Error
141
-     */
142
-    public function set_name($name)
143
-    {
144
-        $this->set('LIN_name', $name);
145
-    }
146
-
147
-
148
-    /**
149
-     * Gets desc
150
-     *
151
-     * @return string
152
-     * @throws EE_Error
153
-     */
154
-    public function desc()
155
-    {
156
-        return $this->get('LIN_desc');
157
-    }
158
-
159
-
160
-    /**
161
-     * Sets desc
162
-     *
163
-     * @param string $desc
164
-     * @throws EE_Error
165
-     */
166
-    public function set_desc($desc)
167
-    {
168
-        $this->set('LIN_desc', $desc);
169
-    }
170
-
171
-
172
-    /**
173
-     * Gets quantity
174
-     *
175
-     * @return int
176
-     * @throws EE_Error
177
-     */
178
-    public function quantity()
179
-    {
180
-        return $this->get('LIN_quantity');
181
-    }
182
-
183
-
184
-    /**
185
-     * Sets quantity
186
-     *
187
-     * @param int $quantity
188
-     * @throws EE_Error
189
-     */
190
-    public function set_quantity($quantity)
191
-    {
192
-        $this->set('LIN_quantity', max($quantity, 0));
193
-    }
194
-
195
-
196
-    /**
197
-     * Gets item_id
198
-     *
199
-     * @return string
200
-     * @throws EE_Error
201
-     */
202
-    public function OBJ_ID()
203
-    {
204
-        return $this->get('OBJ_ID');
205
-    }
206
-
207
-
208
-    /**
209
-     * Sets item_id
210
-     *
211
-     * @param string $item_id
212
-     * @throws EE_Error
213
-     */
214
-    public function set_OBJ_ID($item_id)
215
-    {
216
-        $this->set('OBJ_ID', $item_id);
217
-    }
218
-
219
-
220
-    /**
221
-     * Gets item_type
222
-     *
223
-     * @return string
224
-     * @throws EE_Error
225
-     */
226
-    public function OBJ_type()
227
-    {
228
-        return $this->get('OBJ_type');
229
-    }
230
-
231
-
232
-    /**
233
-     * Gets item_type
234
-     *
235
-     * @return string
236
-     * @throws EE_Error
237
-     */
238
-    public function OBJ_type_i18n()
239
-    {
240
-        $obj_type = $this->OBJ_type();
241
-        switch ($obj_type) {
242
-            case 'Event':
243
-                $obj_type = __('Event', 'event_espresso');
244
-                break;
245
-            case 'Price':
246
-                $obj_type = __('Price', 'event_espresso');
247
-                break;
248
-            case 'Promotion':
249
-                $obj_type = __('Promotion', 'event_espresso');
250
-                break;
251
-            case 'Ticket':
252
-                $obj_type = __('Ticket', 'event_espresso');
253
-                break;
254
-            case 'Transaction':
255
-                $obj_type = __('Transaction', 'event_espresso');
256
-                break;
257
-        }
258
-        return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
259
-    }
260
-
261
-
262
-    /**
263
-     * Sets item_type
264
-     *
265
-     * @param string $OBJ_type
266
-     * @throws EE_Error
267
-     */
268
-    public function set_OBJ_type($OBJ_type)
269
-    {
270
-        $this->set('OBJ_type', $OBJ_type);
271
-    }
272
-
273
-
274
-    /**
275
-     * Gets unit_price
276
-     *
277
-     * @return float
278
-     * @throws EE_Error
279
-     */
280
-    public function unit_price()
281
-    {
282
-        return $this->get('LIN_unit_price');
283
-    }
284
-
285
-
286
-    /**
287
-     * Sets unit_price
288
-     *
289
-     * @param float $unit_price
290
-     * @throws EE_Error
291
-     */
292
-    public function set_unit_price($unit_price)
293
-    {
294
-        $this->set('LIN_unit_price', $unit_price);
295
-    }
296
-
297
-
298
-    /**
299
-     * Checks if this item is a percentage modifier or not
300
-     *
301
-     * @return boolean
302
-     * @throws EE_Error
303
-     */
304
-    public function is_percent()
305
-    {
306
-        if ($this->is_tax_sub_total()) {
307
-            //tax subtotals HAVE a percent on them, that percentage only applies
308
-            //to taxable items, so its' an exception. Treat it like a flat line item
309
-            return false;
310
-        }
311
-        $unit_price = abs($this->get('LIN_unit_price'));
312
-        $percent = abs($this->get('LIN_percent'));
313
-        if ($unit_price < .001 && $percent) {
314
-            return true;
315
-        }
316
-        if ($unit_price >= .001 && !$percent) {
317
-            return false;
318
-        }
319
-        if ($unit_price >= .001 && $percent) {
320
-            throw new EE_Error(
321
-                sprintf(
322
-                    esc_html__('A Line Item can not have a unit price of (%s) AND a percent (%s)!', 'event_espresso'),
323
-                    $unit_price, $percent
324
-                )
325
-            );
326
-        }
327
-        // if they're both 0, assume its not a percent item
328
-        return false;
329
-    }
330
-
331
-
332
-    /**
333
-     * Gets percent (between 100-.001)
334
-     *
335
-     * @return float
336
-     * @throws EE_Error
337
-     */
338
-    public function percent()
339
-    {
340
-        return $this->get('LIN_percent');
341
-    }
342
-
343
-
344
-    /**
345
-     * Sets percent (between 100-0.01)
346
-     *
347
-     * @param float $percent
348
-     * @throws EE_Error
349
-     */
350
-    public function set_percent($percent)
351
-    {
352
-        $this->set('LIN_percent', $percent);
353
-    }
354
-
355
-
356
-    /**
357
-     * Gets total
358
-     *
359
-     * @return float
360
-     * @throws EE_Error
361
-     */
362
-    public function total()
363
-    {
364
-        return $this->get('LIN_total');
365
-    }
366
-
367
-
368
-    /**
369
-     * Sets total
370
-     *
371
-     * @param float $total
372
-     * @throws EE_Error
373
-     */
374
-    public function set_total($total)
375
-    {
376
-        $this->set('LIN_total', $total);
377
-    }
378
-
379
-
380
-    /**
381
-     * Gets order
382
-     *
383
-     * @return int
384
-     * @throws EE_Error
385
-     */
386
-    public function order()
387
-    {
388
-        return $this->get('LIN_order');
389
-    }
390
-
391
-
392
-    /**
393
-     * Sets order
394
-     *
395
-     * @param int $order
396
-     * @throws EE_Error
397
-     */
398
-    public function set_order($order)
399
-    {
400
-        $this->set('LIN_order', $order);
401
-    }
402
-
403
-
404
-    /**
405
-     * Gets parent
406
-     *
407
-     * @return int
408
-     * @throws EE_Error
409
-     */
410
-    public function parent_ID()
411
-    {
412
-        return $this->get('LIN_parent');
413
-    }
414
-
415
-
416
-    /**
417
-     * Sets parent
418
-     *
419
-     * @param int $parent
420
-     * @throws EE_Error
421
-     */
422
-    public function set_parent_ID($parent)
423
-    {
424
-        $this->set('LIN_parent', $parent);
425
-    }
426
-
427
-
428
-    /**
429
-     * Gets type
430
-     *
431
-     * @return string
432
-     * @throws EE_Error
433
-     */
434
-    public function type()
435
-    {
436
-        return $this->get('LIN_type');
437
-    }
438
-
439
-
440
-    /**
441
-     * Sets type
442
-     *
443
-     * @param string $type
444
-     * @throws EE_Error
445
-     */
446
-    public function set_type($type)
447
-    {
448
-        $this->set('LIN_type', $type);
449
-    }
450
-
451
-
452
-    /**
453
-     * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
454
-     * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
455
-     * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
456
-     * or indirectly by `EE_Line_item::add_child_line_item()`)
457
-     *
458
-     * @return EE_Base_Class|EE_Line_Item
459
-     * @throws EE_Error
460
-     */
461
-    public function parent()
462
-    {
463
-        return $this->ID()
464
-            ? $this->get_model()->get_one_by_ID($this->parent_ID())
465
-            : $this->_parent;
466
-    }
467
-
468
-
469
-    /**
470
-     * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
471
-     *
472
-     * @return EE_Base_Class[]|EE_Line_Item[]
473
-     * @throws EE_Error
474
-     */
475
-    public function children()
476
-    {
477
-        if ($this->ID()) {
478
-            return $this->get_model()->get_all(
479
-                array(
480
-                    array('LIN_parent' => $this->ID()),
481
-                    'order_by' => array('LIN_order' => 'ASC'),
482
-                )
483
-            );
484
-        }
485
-        if (!is_array($this->_children)) {
486
-            $this->_children = array();
487
-        }
488
-        return $this->_children;
489
-    }
490
-
491
-
492
-    /**
493
-     * Gets code
494
-     *
495
-     * @return string
496
-     * @throws EE_Error
497
-     */
498
-    public function code()
499
-    {
500
-        return $this->get('LIN_code');
501
-    }
502
-
503
-
504
-    /**
505
-     * Sets code
506
-     *
507
-     * @param string $code
508
-     * @throws EE_Error
509
-     */
510
-    public function set_code($code)
511
-    {
512
-        $this->set('LIN_code', $code);
513
-    }
514
-
515
-
516
-    /**
517
-     * Gets is_taxable
518
-     *
519
-     * @return boolean
520
-     * @throws EE_Error
521
-     */
522
-    public function is_taxable()
523
-    {
524
-        return $this->get('LIN_is_taxable');
525
-    }
526
-
527
-
528
-    /**
529
-     * Sets is_taxable
530
-     *
531
-     * @param boolean $is_taxable
532
-     * @throws EE_Error
533
-     */
534
-    public function set_is_taxable($is_taxable)
535
-    {
536
-        $this->set('LIN_is_taxable', $is_taxable);
537
-    }
538
-
539
-
540
-    /**
541
-     * Gets the object that this model-joins-to.
542
-     * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
543
-     * EEM_Promotion_Object
544
-     *
545
-     *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
546
-     *
547
-     * @return EE_Base_Class | NULL
548
-     * @throws EE_Error
549
-     */
550
-    public function get_object()
551
-    {
552
-        $model_name_of_related_obj = $this->OBJ_type();
553
-        return $this->get_model()->has_relation($model_name_of_related_obj)
554
-            ? $this->get_first_related($model_name_of_related_obj)
555
-            : null;
556
-    }
557
-
558
-
559
-    /**
560
-     * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
561
-     * (IE, if this line item is for a price or something else, will return NULL)
562
-     *
563
-     * @param array $query_params
564
-     * @return EE_Base_Class|EE_Ticket
565
-     * @throws EE_Error
566
-     */
567
-    public function ticket($query_params = array())
568
-    {
569
-        //we're going to assume that when this method is called we always want to receive the attached ticket EVEN if that ticket is archived.  This can be overridden via the incoming $query_params argument
570
-        $remove_defaults = array('default_where_conditions' => 'none');
571
-        $query_params = array_merge($remove_defaults, $query_params);
572
-        return $this->get_first_related('Ticket', $query_params);
573
-    }
574
-
575
-
576
-    /**
577
-     * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
578
-     *
579
-     * @return EE_Datetime | NULL
580
-     * @throws EE_Error
581
-     */
582
-    public function get_ticket_datetime()
583
-    {
584
-        if ($this->OBJ_type() === 'Ticket') {
585
-            $ticket = $this->ticket();
586
-            if ($ticket instanceof EE_Ticket) {
587
-                $datetime = $ticket->first_datetime();
588
-                if ($datetime instanceof EE_Datetime) {
589
-                    return $datetime;
590
-                }
591
-            }
592
-        }
593
-        return null;
594
-    }
595
-
596
-
597
-    /**
598
-     * Gets the event's name that's related to the ticket, if this is for
599
-     * a ticket
600
-     *
601
-     * @return string
602
-     * @throws EE_Error
603
-     */
604
-    public function ticket_event_name()
605
-    {
606
-        $event_name = esc_html__('Unknown', 'event_espresso');
607
-        $event = $this->ticket_event();
608
-        if ($event instanceof EE_Event) {
609
-            $event_name = $event->name();
610
-        }
611
-        return $event_name;
612
-    }
613
-
614
-
615
-    /**
616
-     * Gets the event that's related to the ticket, if this line item represents a ticket.
617
-     *
618
-     * @return EE_Event|null
619
-     * @throws EE_Error
620
-     */
621
-    public function ticket_event()
622
-    {
623
-        $event = null;
624
-        $ticket = $this->ticket();
625
-        if ($ticket instanceof EE_Ticket) {
626
-            $datetime = $ticket->first_datetime();
627
-            if ($datetime instanceof EE_Datetime) {
628
-                $event = $datetime->event();
629
-            }
630
-        }
631
-        return $event;
632
-    }
633
-
634
-
635
-    /**
636
-     * Gets the first datetime for this lien item, assuming it's for a ticket
637
-     *
638
-     * @param string $date_format
639
-     * @param string $time_format
640
-     * @return string
641
-     * @throws EE_Error
642
-     */
643
-    public function ticket_datetime_start($date_format = '', $time_format = '')
644
-    {
645
-        $first_datetime_string = esc_html__('Unknown', 'event_espresso');
646
-        $datetime = $this->get_ticket_datetime();
647
-        if ($datetime) {
648
-            $first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
649
-        }
650
-        return $first_datetime_string;
651
-    }
652
-
653
-
654
-    /**
655
-     * Adds the line item as a child to this line item. If there is another child line
656
-     * item with the same LIN_code, it is overwritten by this new one
657
-     *
658
-     * @param EEI_Line_Item $line_item
659
-     * @param bool $set_order
660
-     * @return bool success
661
-     * @throws EE_Error
662
-     */
663
-    public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
664
-    {
665
-        // should we calculate the LIN_order for this line item ?
666
-        if ($set_order || $line_item->order() === null) {
667
-            $line_item->set_order(count($this->children()));
668
-        }
669
-        if ($this->ID()) {
670
-            //check for any duplicate line items (with the same code), if so, this replaces it
671
-            $line_item_with_same_code = $this->get_child_line_item($line_item->code());
672
-            if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
673
-                $this->delete_child_line_item($line_item_with_same_code->code());
674
-            }
675
-            $line_item->set_parent_ID($this->ID());
676
-            if ($this->TXN_ID()) {
677
-                $line_item->set_TXN_ID($this->TXN_ID());
678
-            }
679
-            return $line_item->save();
680
-        }
681
-        $this->_children[$line_item->code()] = $line_item;
682
-        if ($line_item->parent() !== $this) {
683
-            $line_item->set_parent($this);
684
-        }
685
-        return true;
686
-    }
687
-
688
-
689
-    /**
690
-     * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
691
-     * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
692
-     * However, if this line item is NOT saved to the DB, this just caches the parent on
693
-     * the EE_Line_Item::_parent property.
694
-     *
695
-     * @param EE_Line_Item $line_item
696
-     * @throws EE_Error
697
-     */
698
-    public function set_parent($line_item)
699
-    {
700
-        if ($this->ID()) {
701
-            if (!$line_item->ID()) {
702
-                $line_item->save();
703
-            }
704
-            $this->set_parent_ID($line_item->ID());
705
-            $this->save();
706
-        } else {
707
-            $this->_parent = $line_item;
708
-            $this->set_parent_ID($line_item->ID());
709
-        }
710
-    }
711
-
712
-
713
-    /**
714
-     * Gets the child line item as specified by its code. Because this returns an object (by reference)
715
-     * you can modify this child line item and the parent (this object) can know about them
716
-     * because it also has a reference to that line item
717
-     *
718
-     * @param string $code
719
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
720
-     * @throws EE_Error
721
-     */
722
-    public function get_child_line_item($code)
723
-    {
724
-        if ($this->ID()) {
725
-            return $this->get_model()->get_one(
726
-                array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
727
-            );
728
-        }
729
-        return isset($this->_children[$code])
730
-            ? $this->_children[$code]
731
-            : null;
732
-    }
733
-
734
-
735
-    /**
736
-     * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
737
-     * cached on it)
738
-     *
739
-     * @return int
740
-     * @throws EE_Error
741
-     */
742
-    public function delete_children_line_items()
743
-    {
744
-        if ($this->ID()) {
745
-            return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
746
-        }
747
-        $count = count($this->_children);
748
-        $this->_children = array();
749
-        return $count;
750
-    }
751
-
752
-
753
-    /**
754
-     * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
755
-     * HAS NOT been saved to the DB, removes the child line item with index $code.
756
-     * Also searches through the child's children for a matching line item. However, once a line item has been found
757
-     * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
758
-     * deleted)
759
-     *
760
-     * @param string $code
761
-     * @param bool $stop_search_once_found
762
-     * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
763
-     *             the DB yet)
764
-     * @throws EE_Error
765
-     */
766
-    public function delete_child_line_item($code, $stop_search_once_found = true)
767
-    {
768
-        if ($this->ID()) {
769
-            $items_deleted = 0;
770
-            if ($this->code() === $code) {
771
-                $items_deleted += EEH_Line_Item::delete_all_child_items($this);
772
-                $items_deleted += (int)$this->delete();
773
-                if ($stop_search_once_found) {
774
-                    return $items_deleted;
775
-                }
776
-            }
777
-            foreach ($this->children() as $child_line_item) {
778
-                $items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
779
-            }
780
-            return $items_deleted;
781
-        }
782
-        if (isset($this->_children[$code])) {
783
-            unset($this->_children[$code]);
784
-            return 1;
785
-        }
786
-        return 0;
787
-    }
788
-
789
-
790
-    /**
791
-     * If this line item is in the database, is of the type subtotal, and
792
-     * has no children, why do we have it? It should be deleted so this function
793
-     * does that
794
-     *
795
-     * @return boolean
796
-     * @throws EE_Error
797
-     */
798
-    public function delete_if_childless_subtotal()
799
-    {
800
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && !$this->children()) {
801
-            return $this->delete();
802
-        }
803
-        return false;
804
-    }
805
-
806
-
807
-    /**
808
-     * Creates a code and returns a string. doesn't assign the code to this model object
809
-     *
810
-     * @return string
811
-     * @throws EE_Error
812
-     */
813
-    public function generate_code()
814
-    {
815
-        // each line item in the cart requires a unique identifier
816
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
817
-    }
818
-
819
-
820
-    /**
821
-     * @return bool
822
-     * @throws EE_Error
823
-     */
824
-    public function is_tax()
825
-    {
826
-        return $this->type() === EEM_Line_Item::type_tax;
827
-    }
828
-
829
-
830
-    /**
831
-     * @return bool
832
-     * @throws EE_Error
833
-     */
834
-    public function is_tax_sub_total()
835
-    {
836
-        return $this->type() === EEM_Line_Item::type_tax_sub_total;
837
-    }
838
-
839
-
840
-    /**
841
-     * @return bool
842
-     * @throws EE_Error
843
-     */
844
-    public function is_line_item()
845
-    {
846
-        return $this->type() === EEM_Line_Item::type_line_item;
847
-    }
848
-
849
-
850
-    /**
851
-     * @return bool
852
-     * @throws EE_Error
853
-     */
854
-    public function is_sub_line_item()
855
-    {
856
-        return $this->type() === EEM_Line_Item::type_sub_line_item;
857
-    }
858
-
859
-
860
-    /**
861
-     * @return bool
862
-     * @throws EE_Error
863
-     */
864
-    public function is_sub_total()
865
-    {
866
-        return $this->type() === EEM_Line_Item::type_sub_total;
867
-    }
868
-
869
-
870
-    /**
871
-     * Whether or not this line item is a cancellation line item
872
-     *
873
-     * @return boolean
874
-     * @throws EE_Error
875
-     */
876
-    public function is_cancellation()
877
-    {
878
-        return EEM_Line_Item::type_cancellation === $this->type();
879
-    }
880
-
881
-
882
-    /**
883
-     * @return bool
884
-     * @throws EE_Error
885
-     */
886
-    public function is_total()
887
-    {
888
-        return $this->type() === EEM_Line_Item::type_total;
889
-    }
890
-
891
-
892
-    /**
893
-     * @return bool
894
-     * @throws EE_Error
895
-     */
896
-    public function is_cancelled()
897
-    {
898
-        return $this->type() === EEM_Line_Item::type_cancellation;
899
-    }
900
-
901
-
902
-    /**
903
-     * @return string like '2, 004.00', formatted according to the localized currency
904
-     * @throws EE_Error
905
-     */
906
-    public function unit_price_no_code()
907
-    {
908
-        return $this->get_pretty('LIN_unit_price', 'no_currency_code');
909
-    }
910
-
911
-
912
-    /**
913
-     * @return string like '2, 004.00', formatted according to the localized currency
914
-     * @throws EE_Error
915
-     */
916
-    public function total_no_code()
917
-    {
918
-        return $this->get_pretty('LIN_total', 'no_currency_code');
919
-    }
920
-
921
-
922
-    /**
923
-     * Gets the final total on this item, taking taxes into account.
924
-     * Has the side-effect of setting the sub-total as it was just calculated.
925
-     * If this is used on a grand-total line item, also updates the transaction's
926
-     * TXN_total (provided this line item is allowed to persist, otherwise we don't
927
-     * want to change a persistable transaction with info from a non-persistent line item)
928
-     *
929
-     * @return float
930
-     * @throws EE_Error
931
-     * @throws InvalidArgumentException
932
-     * @throws InvalidInterfaceException
933
-     * @throws InvalidDataTypeException
934
-     */
935
-    public function recalculate_total_including_taxes()
936
-    {
937
-        $pre_tax_total = $this->recalculate_pre_tax_total();
938
-        $tax_total = $this->recalculate_taxes_and_tax_total();
939
-        $total = $pre_tax_total + $tax_total;
940
-        // no negative totals plz
941
-        $total = max($total, 0);
942
-        $this->set_total($total);
943
-        //only update the related transaction's total
944
-        //if we intend to save this line item and its a grand total
945
-        if (
946
-            $this->allow_persist() && $this->type() === EEM_Line_Item::type_total
947
-            && $this->transaction()
948
-            instanceof
949
-            EE_Transaction
950
-        ) {
951
-            $this->transaction()->set_total($total);
952
-            if ($this->transaction()->ID()) {
953
-                $this->transaction()->save();
954
-            }
955
-        }
956
-        $this->maybe_save();
957
-        return $total;
958
-    }
959
-
960
-
961
-    /**
962
-     * Recursively goes through all the children and recalculates sub-totals EXCEPT for
963
-     * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
964
-     * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
965
-     * when this is called on the grand total
966
-     *
967
-     * @return float
968
-     * @throws InvalidArgumentException
969
-     * @throws InvalidInterfaceException
970
-     * @throws InvalidDataTypeException
971
-     * @throws EE_Error
972
-     */
973
-    public function recalculate_pre_tax_total()
974
-    {
975
-        $total = 0;
976
-        $my_children = $this->children();
977
-        $has_children = !empty($my_children);
978
-        if ($has_children && $this->is_line_item()) {
979
-            $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
980
-        } elseif (!$has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
981
-            $total = $this->unit_price() * $this->quantity();
982
-        } elseif ($this->is_sub_total() || $this->is_total()) {
983
-            $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
984
-        } elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
985
-            // completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
986
-            return 0;
987
-        }
988
-        // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
989
-        if (
990
-            !$this->is_line_item() && !$this->is_sub_line_item() && !$this->is_cancellation()
991
-        ) {
992
-            if ($this->OBJ_type() !== 'Event') {
993
-                $this->set_quantity(1);
994
-            }
995
-            if (!$this->is_percent()) {
996
-                $this->set_unit_price($total);
997
-            }
998
-        }
999
-        //we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1000
-        //so it ought to be
1001
-        if (!$this->is_total()) {
1002
-            $this->set_total($total);
1003
-            //if not a percent line item, make sure we keep the unit price in sync
1004
-            if (
1005
-                $has_children
1006
-                && $this->is_line_item()
1007
-                && !$this->is_percent()
1008
-            ) {
1009
-                if ($this->quantity() === 0) {
1010
-                    $new_unit_price = 0;
1011
-                } else {
1012
-                    $new_unit_price = $this->total() / $this->quantity();
1013
-                }
1014
-                $this->set_unit_price($new_unit_price);
1015
-            }
1016
-            $this->maybe_save();
1017
-        }
1018
-        return $total;
1019
-    }
1020
-
1021
-
1022
-    /**
1023
-     * Calculates the pretax total when this line item is a subtotal or total line item.
1024
-     * Basically does a sum-then-round approach (ie, any percent line item that are children
1025
-     * will calculate their total based on the un-rounded total we're working with so far, and
1026
-     * THEN round the result; instead of rounding as we go like with sub-line-items)
1027
-     *
1028
-     * @param float $calculated_total_so_far
1029
-     * @param EE_Line_Item[] $my_children
1030
-     * @return float
1031
-     * @throws InvalidArgumentException
1032
-     * @throws InvalidInterfaceException
1033
-     * @throws InvalidDataTypeException
1034
-     * @throws EE_Error
1035
-     */
1036
-    protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1037
-    {
1038
-        if ($my_children === null) {
1039
-            $my_children = $this->children();
1040
-        }
1041
-        $subtotal_quantity = 0;
1042
-        //get the total of all its children
1043
-        foreach ($my_children as $child_line_item) {
1044
-            if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1045
-                // percentage line items are based on total so far
1046
-                if ($child_line_item->is_percent()) {
1047
-                    //round as we go so that the line items add up ok
1048
-                    $percent_total = round(
1049
-                        $calculated_total_so_far * $child_line_item->percent() / 100,
1050
-                        EE_Registry::instance()->CFG->currency->dec_plc
1051
-                    );
1052
-                    $child_line_item->set_total($percent_total);
1053
-                    //so far all percent line items should have a quantity of 1
1054
-                    //(ie, no double percent discounts. Although that might be requested someday)
1055
-                    $child_line_item->set_quantity(1);
1056
-                    $child_line_item->maybe_save();
1057
-                    $calculated_total_so_far += $percent_total;
1058
-                } else {
1059
-                    //verify flat sub-line-item quantities match their parent
1060
-                    if ($child_line_item->is_sub_line_item()) {
1061
-                        $child_line_item->set_quantity($this->quantity());
1062
-                    }
1063
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1064
-                    $subtotal_quantity += $child_line_item->quantity();
1065
-                }
1066
-            }
1067
-        }
1068
-        if ($this->is_sub_total()) {
1069
-            // no negative totals plz
1070
-            $calculated_total_so_far = max($calculated_total_so_far, 0);
1071
-            $subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1072
-            $this->set_quantity($subtotal_quantity);
1073
-            $this->maybe_save();
1074
-        }
1075
-        return $calculated_total_so_far;
1076
-    }
1077
-
1078
-
1079
-    /**
1080
-     * Calculates the pretax total for a normal line item, in a round-then-sum approach
1081
-     * (where each sub-line-item is applied to the base price for the line item
1082
-     * and the result is immediately rounded, rather than summing all the sub-line-items
1083
-     * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1084
-     *
1085
-     * @param float $calculated_total_so_far
1086
-     * @param EE_Line_Item[] $my_children
1087
-     * @return float
1088
-     * @throws InvalidArgumentException
1089
-     * @throws InvalidInterfaceException
1090
-     * @throws InvalidDataTypeException
1091
-     * @throws EE_Error
1092
-     */
1093
-    protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1094
-    {
1095
-        if ($my_children === null) {
1096
-            $my_children = $this->children();
1097
-        }
1098
-        //we need to keep track of the running total for a single item,
1099
-        //because we need to round as we go
1100
-        $unit_price_for_total = 0;
1101
-        $quantity_for_total = 1;
1102
-        //get the total of all its children
1103
-        foreach ($my_children as $child_line_item) {
1104
-            if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1105
-                if ($child_line_item->is_percent()) {
1106
-                    //it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1107
-                    //not total multiplied by percent, because that ignores rounding along-the-way
1108
-                    $percent_unit_price = round(
1109
-                        $unit_price_for_total * $child_line_item->percent() / 100,
1110
-                        EE_Registry::instance()->CFG->currency->dec_plc
1111
-                    );
1112
-                    $percent_total = $percent_unit_price * $quantity_for_total;
1113
-                    $child_line_item->set_total($percent_total);
1114
-                    //so far all percent line items should have a quantity of 1
1115
-                    //(ie, no double percent discounts. Although that might be requested someday)
1116
-                    $child_line_item->set_quantity(1);
1117
-                    $child_line_item->maybe_save();
1118
-                    $calculated_total_so_far += $percent_total;
1119
-                    $unit_price_for_total += $percent_unit_price;
1120
-                } else {
1121
-                    //verify flat sub-line-item quantities match their parent
1122
-                    if ($child_line_item->is_sub_line_item()) {
1123
-                        $child_line_item->set_quantity($this->quantity());
1124
-                    }
1125
-                    $quantity_for_total = $child_line_item->quantity();
1126
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1127
-                    $unit_price_for_total += $child_line_item->unit_price();
1128
-                }
1129
-            }
1130
-        }
1131
-        return $calculated_total_so_far;
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1137
-     * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1138
-     * and tax sub-total if already in the DB
1139
-     *
1140
-     * @return float
1141
-     * @throws EE_Error
1142
-     */
1143
-    public function recalculate_taxes_and_tax_total()
1144
-    {
1145
-        //get all taxes
1146
-        $taxes = $this->tax_descendants();
1147
-        //calculate the pretax total
1148
-        $taxable_total = $this->taxable_total();
1149
-        $tax_total = 0;
1150
-        foreach ($taxes as $tax) {
1151
-            $total_on_this_tax = $taxable_total * $tax->percent() / 100;
1152
-            //remember the total on this line item
1153
-            $tax->set_total($total_on_this_tax);
1154
-            $tax->maybe_save();
1155
-            $tax_total += $tax->total();
1156
-        }
1157
-        $this->_recalculate_tax_sub_total();
1158
-        return $tax_total;
1159
-    }
1160
-
1161
-
1162
-    /**
1163
-     * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1164
-     *
1165
-     * @return void
1166
-     * @throws EE_Error
1167
-     */
1168
-    private function _recalculate_tax_sub_total()
1169
-    {
1170
-        if ($this->is_tax_sub_total()) {
1171
-            $total = 0;
1172
-            $total_percent = 0;
1173
-            //simply loop through all its children (which should be taxes) and sum their total
1174
-            foreach ($this->children() as $child_tax) {
1175
-                if ($child_tax instanceof EE_Line_Item) {
1176
-                    $total += $child_tax->total();
1177
-                    $total_percent += $child_tax->percent();
1178
-                }
1179
-            }
1180
-            $this->set_total($total);
1181
-            $this->set_percent($total_percent);
1182
-            $this->maybe_save();
1183
-        } elseif ($this->is_total()) {
1184
-            foreach ($this->children() as $maybe_tax_subtotal) {
1185
-                if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1186
-                    $maybe_tax_subtotal->_recalculate_tax_sub_total();
1187
-                }
1188
-            }
1189
-        }
1190
-    }
1191
-
1192
-
1193
-    /**
1194
-     * Gets the total tax on this line item. Assumes taxes have already been calculated using
1195
-     * recalculate_taxes_and_total
1196
-     *
1197
-     * @return float
1198
-     * @throws EE_Error
1199
-     */
1200
-    public function get_total_tax()
1201
-    {
1202
-        $this->_recalculate_tax_sub_total();
1203
-        $total = 0;
1204
-        foreach ($this->tax_descendants() as $tax_line_item) {
1205
-            if ($tax_line_item instanceof EE_Line_Item) {
1206
-                $total += $tax_line_item->total();
1207
-            }
1208
-        }
1209
-        return $total;
1210
-    }
1211
-
1212
-
1213
-    /**
1214
-     * Gets the total for all the items purchased only
1215
-     *
1216
-     * @return float
1217
-     * @throws EE_Error
1218
-     */
1219
-    public function get_items_total()
1220
-    {
1221
-        //by default, let's make sure we're consistent with the existing line item
1222
-        if ($this->is_total()) {
1223
-            $pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1224
-            if ($pretax_subtotal_li instanceof EE_Line_Item) {
1225
-                return $pretax_subtotal_li->total();
1226
-            }
1227
-        }
1228
-        $total = 0;
1229
-        foreach ($this->get_items() as $item) {
1230
-            if ($item instanceof EE_Line_Item) {
1231
-                $total += $item->total();
1232
-            }
1233
-        }
1234
-        return $total;
1235
-    }
1236
-
1237
-
1238
-    /**
1239
-     * Gets all the descendants (ie, children or children of children etc) that
1240
-     * are of the type 'tax'
1241
-     *
1242
-     * @return EE_Line_Item[]
1243
-     */
1244
-    public function tax_descendants()
1245
-    {
1246
-        return EEH_Line_Item::get_tax_descendants($this);
1247
-    }
1248
-
1249
-
1250
-    /**
1251
-     * Gets all the real items purchased which are children of this item
1252
-     *
1253
-     * @return EE_Line_Item[]
1254
-     */
1255
-    public function get_items()
1256
-    {
1257
-        return EEH_Line_Item::get_line_item_descendants($this);
1258
-    }
1259
-
1260
-
1261
-    /**
1262
-     * Returns the amount taxable among this line item's children (or if it has no children,
1263
-     * how much of it is taxable). Does not recalculate totals or subtotals.
1264
-     * If the taxable total is negative, (eg, if none of the tickets were taxable,
1265
-     * but there is a "Taxable" discount), returns 0.
1266
-     *
1267
-     * @return float
1268
-     * @throws EE_Error
1269
-     */
1270
-    public function taxable_total()
1271
-    {
1272
-        $total = 0;
1273
-        if ($this->children()) {
1274
-            foreach ($this->children() as $child_line_item) {
1275
-                if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1276
-                    //if it's a percent item, only take into account the percent
1277
-                    //that's taxable too (the taxable total so far)
1278
-                    if ($child_line_item->is_percent()) {
1279
-                        $total += ($total * $child_line_item->percent() / 100);
1280
-                    } else {
1281
-                        $total += $child_line_item->total();
1282
-                    }
1283
-                } elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1284
-                    $total += $child_line_item->taxable_total();
1285
-                }
1286
-            }
1287
-        }
1288
-        return max($total, 0);
1289
-    }
1290
-
1291
-
1292
-    /**
1293
-     * Gets the transaction for this line item
1294
-     *
1295
-     * @return EE_Base_Class|EE_Transaction
1296
-     * @throws EE_Error
1297
-     */
1298
-    public function transaction()
1299
-    {
1300
-        return $this->get_first_related('Transaction');
1301
-    }
1302
-
1303
-
1304
-    /**
1305
-     * Saves this line item to the DB, and recursively saves its descendants.
1306
-     * Because there currently is no proper parent-child relation on the model,
1307
-     * save_this_and_cached() will NOT save the descendants.
1308
-     * Also sets the transaction on this line item and all its descendants before saving
1309
-     *
1310
-     * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1311
-     * @return int count of items saved
1312
-     * @throws EE_Error
1313
-     */
1314
-    public function save_this_and_descendants_to_txn($txn_id = null)
1315
-    {
1316
-        $count = 0;
1317
-        if (!$txn_id) {
1318
-            $txn_id = $this->TXN_ID();
1319
-        }
1320
-        $this->set_TXN_ID($txn_id);
1321
-        $children = $this->children();
1322
-        $count += $this->save()
1323
-            ? 1
1324
-            : 0;
1325
-        foreach ($children as $child_line_item) {
1326
-            if ($child_line_item instanceof EE_Line_Item) {
1327
-                $child_line_item->set_parent_ID($this->ID());
1328
-                $count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1329
-            }
1330
-        }
1331
-        return $count;
1332
-    }
1333
-
1334
-
1335
-    /**
1336
-     * Saves this line item to the DB, and recursively saves its descendants.
1337
-     *
1338
-     * @return int count of items saved
1339
-     * @throws EE_Error
1340
-     */
1341
-    public function save_this_and_descendants()
1342
-    {
1343
-        $count = 0;
1344
-        $children = $this->children();
1345
-        $count += $this->save()
1346
-            ? 1
1347
-            : 0;
1348
-        foreach ($children as $child_line_item) {
1349
-            if ($child_line_item instanceof EE_Line_Item) {
1350
-                $child_line_item->set_parent_ID($this->ID());
1351
-                $count += $child_line_item->save_this_and_descendants();
1352
-            }
1353
-        }
1354
-        return $count;
1355
-    }
1356
-
1357
-
1358
-    /**
1359
-     * returns the cancellation line item if this item was cancelled
1360
-     *
1361
-     * @return EE_Line_Item[]
1362
-     * @throws InvalidArgumentException
1363
-     * @throws InvalidInterfaceException
1364
-     * @throws InvalidDataTypeException
1365
-     * @throws ReflectionException
1366
-     * @throws EE_Error
1367
-     */
1368
-    public function get_cancellations()
1369
-    {
1370
-        EE_Registry::instance()->load_helper('Line_Item');
1371
-        return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1372
-    }
1373
-
1374
-
1375
-    /**
1376
-     * If this item has an ID, then this saves it again to update the db
1377
-     *
1378
-     * @return int count of items saved
1379
-     * @throws EE_Error
1380
-     */
1381
-    public function maybe_save()
1382
-    {
1383
-        if ($this->ID()) {
1384
-            return $this->save();
1385
-        }
1386
-        return false;
1387
-    }
1388
-
1389
-
1390
-    /**
1391
-     * clears the cached children and parent from the line item
1392
-     *
1393
-     * @return void
1394
-     */
1395
-    public function clear_related_line_item_cache()
1396
-    {
1397
-        $this->_children = array();
1398
-        $this->_parent = null;
1399
-    }
1400
-
1401
-
1402
-    /**
1403
-     * @param bool $raw
1404
-     * @return int
1405
-     * @throws EE_Error
1406
-     */
1407
-    public function timestamp($raw = false)
1408
-    {
1409
-        return $raw
1410
-            ? $this->get_raw('LIN_timestamp')
1411
-            : $this->get('LIN_timestamp');
1412
-    }
1413
-
1414
-
1415
-
1416
-
1417
-    /************************* DEPRECATED *************************/
1418
-    /**
1419
-     * @deprecated 4.6.0
1420
-     * @param string $type one of the constants on EEM_Line_Item
1421
-     * @return EE_Line_Item[]
1422
-     */
1423
-    protected function _get_descendants_of_type($type)
1424
-    {
1425
-        EE_Error::doing_it_wrong(
1426
-            'EE_Line_Item::_get_descendants_of_type()',
1427
-            __('Method replaced with EEH_Line_Item::get_descendants_of_type()', 'event_espresso'), '4.6.0'
1428
-        );
1429
-        return EEH_Line_Item::get_descendants_of_type($this, $type);
1430
-    }
1431
-
1432
-
1433
-    /**
1434
-     * @deprecated 4.6.0
1435
-     * @param string $type like one of the EEM_Line_Item::type_*
1436
-     * @return EE_Line_Item
1437
-     */
1438
-    public function get_nearest_descendant_of_type($type)
1439
-    {
1440
-        EE_Error::doing_it_wrong(
1441
-            'EE_Line_Item::get_nearest_descendant_of_type()',
1442
-            __('Method replaced with EEH_Line_Item::get_nearest_descendant_of_type()', 'event_espresso'), '4.6.0'
1443
-        );
1444
-        return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1445
-    }
20
+	/**
21
+	 * for children line items (currently not a normal relation)
22
+	 *
23
+	 * @type EE_Line_Item[]
24
+	 */
25
+	protected $_children = array();
26
+
27
+	/**
28
+	 * for the parent line item
29
+	 *
30
+	 * @var EE_Line_Item
31
+	 */
32
+	protected $_parent;
33
+
34
+
35
+	/**
36
+	 *
37
+	 * @param array $props_n_values incoming values
38
+	 * @param string $timezone incoming timezone (if not set the timezone set for the website will be
39
+	 *                                        used.)
40
+	 * @param array $date_formats incoming date_formats in an array where the first value is the
41
+	 *                                        date_format and the second value is the time format
42
+	 * @return EE_Line_Item
43
+	 * @throws EE_Error
44
+	 */
45
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
46
+	{
47
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
48
+		return $has_object
49
+			? $has_object
50
+			: new self($props_n_values, false, $timezone);
51
+	}
52
+
53
+
54
+	/**
55
+	 * @param array $props_n_values incoming values from the database
56
+	 * @param string $timezone incoming timezone as set by the model.  If not set the timezone for
57
+	 *                                the website will be used.
58
+	 * @return EE_Line_Item
59
+	 * @throws EE_Error
60
+	 */
61
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
62
+	{
63
+		return new self($props_n_values, true, $timezone);
64
+	}
65
+
66
+
67
+	/**
68
+	 * Adds some defaults if they're not specified
69
+	 *
70
+	 * @param array $fieldValues
71
+	 * @param bool $bydb
72
+	 * @param string $timezone
73
+	 * @throws EE_Error
74
+	 */
75
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
76
+	{
77
+		parent::__construct($fieldValues, $bydb, $timezone);
78
+		if (!$this->get('LIN_code')) {
79
+			$this->set_code($this->generate_code());
80
+		}
81
+	}
82
+
83
+
84
+	/**
85
+	 * Gets ID
86
+	 *
87
+	 * @return int
88
+	 * @throws EE_Error
89
+	 */
90
+	public function ID()
91
+	{
92
+		return $this->get('LIN_ID');
93
+	}
94
+
95
+
96
+	/**
97
+	 * Gets TXN_ID
98
+	 *
99
+	 * @return int
100
+	 * @throws EE_Error
101
+	 */
102
+	public function TXN_ID()
103
+	{
104
+		return $this->get('TXN_ID');
105
+	}
106
+
107
+
108
+	/**
109
+	 * Sets TXN_ID
110
+	 *
111
+	 * @param int $TXN_ID
112
+	 * @throws EE_Error
113
+	 */
114
+	public function set_TXN_ID($TXN_ID)
115
+	{
116
+		$this->set('TXN_ID', $TXN_ID);
117
+	}
118
+
119
+
120
+	/**
121
+	 * Gets name
122
+	 *
123
+	 * @return string
124
+	 * @throws EE_Error
125
+	 */
126
+	public function name()
127
+	{
128
+		$name = $this->get('LIN_name');
129
+		if (!$name) {
130
+			$name = ucwords(str_replace('-', ' ', $this->type()));
131
+		}
132
+		return $name;
133
+	}
134
+
135
+
136
+	/**
137
+	 * Sets name
138
+	 *
139
+	 * @param string $name
140
+	 * @throws EE_Error
141
+	 */
142
+	public function set_name($name)
143
+	{
144
+		$this->set('LIN_name', $name);
145
+	}
146
+
147
+
148
+	/**
149
+	 * Gets desc
150
+	 *
151
+	 * @return string
152
+	 * @throws EE_Error
153
+	 */
154
+	public function desc()
155
+	{
156
+		return $this->get('LIN_desc');
157
+	}
158
+
159
+
160
+	/**
161
+	 * Sets desc
162
+	 *
163
+	 * @param string $desc
164
+	 * @throws EE_Error
165
+	 */
166
+	public function set_desc($desc)
167
+	{
168
+		$this->set('LIN_desc', $desc);
169
+	}
170
+
171
+
172
+	/**
173
+	 * Gets quantity
174
+	 *
175
+	 * @return int
176
+	 * @throws EE_Error
177
+	 */
178
+	public function quantity()
179
+	{
180
+		return $this->get('LIN_quantity');
181
+	}
182
+
183
+
184
+	/**
185
+	 * Sets quantity
186
+	 *
187
+	 * @param int $quantity
188
+	 * @throws EE_Error
189
+	 */
190
+	public function set_quantity($quantity)
191
+	{
192
+		$this->set('LIN_quantity', max($quantity, 0));
193
+	}
194
+
195
+
196
+	/**
197
+	 * Gets item_id
198
+	 *
199
+	 * @return string
200
+	 * @throws EE_Error
201
+	 */
202
+	public function OBJ_ID()
203
+	{
204
+		return $this->get('OBJ_ID');
205
+	}
206
+
207
+
208
+	/**
209
+	 * Sets item_id
210
+	 *
211
+	 * @param string $item_id
212
+	 * @throws EE_Error
213
+	 */
214
+	public function set_OBJ_ID($item_id)
215
+	{
216
+		$this->set('OBJ_ID', $item_id);
217
+	}
218
+
219
+
220
+	/**
221
+	 * Gets item_type
222
+	 *
223
+	 * @return string
224
+	 * @throws EE_Error
225
+	 */
226
+	public function OBJ_type()
227
+	{
228
+		return $this->get('OBJ_type');
229
+	}
230
+
231
+
232
+	/**
233
+	 * Gets item_type
234
+	 *
235
+	 * @return string
236
+	 * @throws EE_Error
237
+	 */
238
+	public function OBJ_type_i18n()
239
+	{
240
+		$obj_type = $this->OBJ_type();
241
+		switch ($obj_type) {
242
+			case 'Event':
243
+				$obj_type = __('Event', 'event_espresso');
244
+				break;
245
+			case 'Price':
246
+				$obj_type = __('Price', 'event_espresso');
247
+				break;
248
+			case 'Promotion':
249
+				$obj_type = __('Promotion', 'event_espresso');
250
+				break;
251
+			case 'Ticket':
252
+				$obj_type = __('Ticket', 'event_espresso');
253
+				break;
254
+			case 'Transaction':
255
+				$obj_type = __('Transaction', 'event_espresso');
256
+				break;
257
+		}
258
+		return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
259
+	}
260
+
261
+
262
+	/**
263
+	 * Sets item_type
264
+	 *
265
+	 * @param string $OBJ_type
266
+	 * @throws EE_Error
267
+	 */
268
+	public function set_OBJ_type($OBJ_type)
269
+	{
270
+		$this->set('OBJ_type', $OBJ_type);
271
+	}
272
+
273
+
274
+	/**
275
+	 * Gets unit_price
276
+	 *
277
+	 * @return float
278
+	 * @throws EE_Error
279
+	 */
280
+	public function unit_price()
281
+	{
282
+		return $this->get('LIN_unit_price');
283
+	}
284
+
285
+
286
+	/**
287
+	 * Sets unit_price
288
+	 *
289
+	 * @param float $unit_price
290
+	 * @throws EE_Error
291
+	 */
292
+	public function set_unit_price($unit_price)
293
+	{
294
+		$this->set('LIN_unit_price', $unit_price);
295
+	}
296
+
297
+
298
+	/**
299
+	 * Checks if this item is a percentage modifier or not
300
+	 *
301
+	 * @return boolean
302
+	 * @throws EE_Error
303
+	 */
304
+	public function is_percent()
305
+	{
306
+		if ($this->is_tax_sub_total()) {
307
+			//tax subtotals HAVE a percent on them, that percentage only applies
308
+			//to taxable items, so its' an exception. Treat it like a flat line item
309
+			return false;
310
+		}
311
+		$unit_price = abs($this->get('LIN_unit_price'));
312
+		$percent = abs($this->get('LIN_percent'));
313
+		if ($unit_price < .001 && $percent) {
314
+			return true;
315
+		}
316
+		if ($unit_price >= .001 && !$percent) {
317
+			return false;
318
+		}
319
+		if ($unit_price >= .001 && $percent) {
320
+			throw new EE_Error(
321
+				sprintf(
322
+					esc_html__('A Line Item can not have a unit price of (%s) AND a percent (%s)!', 'event_espresso'),
323
+					$unit_price, $percent
324
+				)
325
+			);
326
+		}
327
+		// if they're both 0, assume its not a percent item
328
+		return false;
329
+	}
330
+
331
+
332
+	/**
333
+	 * Gets percent (between 100-.001)
334
+	 *
335
+	 * @return float
336
+	 * @throws EE_Error
337
+	 */
338
+	public function percent()
339
+	{
340
+		return $this->get('LIN_percent');
341
+	}
342
+
343
+
344
+	/**
345
+	 * Sets percent (between 100-0.01)
346
+	 *
347
+	 * @param float $percent
348
+	 * @throws EE_Error
349
+	 */
350
+	public function set_percent($percent)
351
+	{
352
+		$this->set('LIN_percent', $percent);
353
+	}
354
+
355
+
356
+	/**
357
+	 * Gets total
358
+	 *
359
+	 * @return float
360
+	 * @throws EE_Error
361
+	 */
362
+	public function total()
363
+	{
364
+		return $this->get('LIN_total');
365
+	}
366
+
367
+
368
+	/**
369
+	 * Sets total
370
+	 *
371
+	 * @param float $total
372
+	 * @throws EE_Error
373
+	 */
374
+	public function set_total($total)
375
+	{
376
+		$this->set('LIN_total', $total);
377
+	}
378
+
379
+
380
+	/**
381
+	 * Gets order
382
+	 *
383
+	 * @return int
384
+	 * @throws EE_Error
385
+	 */
386
+	public function order()
387
+	{
388
+		return $this->get('LIN_order');
389
+	}
390
+
391
+
392
+	/**
393
+	 * Sets order
394
+	 *
395
+	 * @param int $order
396
+	 * @throws EE_Error
397
+	 */
398
+	public function set_order($order)
399
+	{
400
+		$this->set('LIN_order', $order);
401
+	}
402
+
403
+
404
+	/**
405
+	 * Gets parent
406
+	 *
407
+	 * @return int
408
+	 * @throws EE_Error
409
+	 */
410
+	public function parent_ID()
411
+	{
412
+		return $this->get('LIN_parent');
413
+	}
414
+
415
+
416
+	/**
417
+	 * Sets parent
418
+	 *
419
+	 * @param int $parent
420
+	 * @throws EE_Error
421
+	 */
422
+	public function set_parent_ID($parent)
423
+	{
424
+		$this->set('LIN_parent', $parent);
425
+	}
426
+
427
+
428
+	/**
429
+	 * Gets type
430
+	 *
431
+	 * @return string
432
+	 * @throws EE_Error
433
+	 */
434
+	public function type()
435
+	{
436
+		return $this->get('LIN_type');
437
+	}
438
+
439
+
440
+	/**
441
+	 * Sets type
442
+	 *
443
+	 * @param string $type
444
+	 * @throws EE_Error
445
+	 */
446
+	public function set_type($type)
447
+	{
448
+		$this->set('LIN_type', $type);
449
+	}
450
+
451
+
452
+	/**
453
+	 * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
454
+	 * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
455
+	 * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
456
+	 * or indirectly by `EE_Line_item::add_child_line_item()`)
457
+	 *
458
+	 * @return EE_Base_Class|EE_Line_Item
459
+	 * @throws EE_Error
460
+	 */
461
+	public function parent()
462
+	{
463
+		return $this->ID()
464
+			? $this->get_model()->get_one_by_ID($this->parent_ID())
465
+			: $this->_parent;
466
+	}
467
+
468
+
469
+	/**
470
+	 * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
471
+	 *
472
+	 * @return EE_Base_Class[]|EE_Line_Item[]
473
+	 * @throws EE_Error
474
+	 */
475
+	public function children()
476
+	{
477
+		if ($this->ID()) {
478
+			return $this->get_model()->get_all(
479
+				array(
480
+					array('LIN_parent' => $this->ID()),
481
+					'order_by' => array('LIN_order' => 'ASC'),
482
+				)
483
+			);
484
+		}
485
+		if (!is_array($this->_children)) {
486
+			$this->_children = array();
487
+		}
488
+		return $this->_children;
489
+	}
490
+
491
+
492
+	/**
493
+	 * Gets code
494
+	 *
495
+	 * @return string
496
+	 * @throws EE_Error
497
+	 */
498
+	public function code()
499
+	{
500
+		return $this->get('LIN_code');
501
+	}
502
+
503
+
504
+	/**
505
+	 * Sets code
506
+	 *
507
+	 * @param string $code
508
+	 * @throws EE_Error
509
+	 */
510
+	public function set_code($code)
511
+	{
512
+		$this->set('LIN_code', $code);
513
+	}
514
+
515
+
516
+	/**
517
+	 * Gets is_taxable
518
+	 *
519
+	 * @return boolean
520
+	 * @throws EE_Error
521
+	 */
522
+	public function is_taxable()
523
+	{
524
+		return $this->get('LIN_is_taxable');
525
+	}
526
+
527
+
528
+	/**
529
+	 * Sets is_taxable
530
+	 *
531
+	 * @param boolean $is_taxable
532
+	 * @throws EE_Error
533
+	 */
534
+	public function set_is_taxable($is_taxable)
535
+	{
536
+		$this->set('LIN_is_taxable', $is_taxable);
537
+	}
538
+
539
+
540
+	/**
541
+	 * Gets the object that this model-joins-to.
542
+	 * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
543
+	 * EEM_Promotion_Object
544
+	 *
545
+	 *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
546
+	 *
547
+	 * @return EE_Base_Class | NULL
548
+	 * @throws EE_Error
549
+	 */
550
+	public function get_object()
551
+	{
552
+		$model_name_of_related_obj = $this->OBJ_type();
553
+		return $this->get_model()->has_relation($model_name_of_related_obj)
554
+			? $this->get_first_related($model_name_of_related_obj)
555
+			: null;
556
+	}
557
+
558
+
559
+	/**
560
+	 * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
561
+	 * (IE, if this line item is for a price or something else, will return NULL)
562
+	 *
563
+	 * @param array $query_params
564
+	 * @return EE_Base_Class|EE_Ticket
565
+	 * @throws EE_Error
566
+	 */
567
+	public function ticket($query_params = array())
568
+	{
569
+		//we're going to assume that when this method is called we always want to receive the attached ticket EVEN if that ticket is archived.  This can be overridden via the incoming $query_params argument
570
+		$remove_defaults = array('default_where_conditions' => 'none');
571
+		$query_params = array_merge($remove_defaults, $query_params);
572
+		return $this->get_first_related('Ticket', $query_params);
573
+	}
574
+
575
+
576
+	/**
577
+	 * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
578
+	 *
579
+	 * @return EE_Datetime | NULL
580
+	 * @throws EE_Error
581
+	 */
582
+	public function get_ticket_datetime()
583
+	{
584
+		if ($this->OBJ_type() === 'Ticket') {
585
+			$ticket = $this->ticket();
586
+			if ($ticket instanceof EE_Ticket) {
587
+				$datetime = $ticket->first_datetime();
588
+				if ($datetime instanceof EE_Datetime) {
589
+					return $datetime;
590
+				}
591
+			}
592
+		}
593
+		return null;
594
+	}
595
+
596
+
597
+	/**
598
+	 * Gets the event's name that's related to the ticket, if this is for
599
+	 * a ticket
600
+	 *
601
+	 * @return string
602
+	 * @throws EE_Error
603
+	 */
604
+	public function ticket_event_name()
605
+	{
606
+		$event_name = esc_html__('Unknown', 'event_espresso');
607
+		$event = $this->ticket_event();
608
+		if ($event instanceof EE_Event) {
609
+			$event_name = $event->name();
610
+		}
611
+		return $event_name;
612
+	}
613
+
614
+
615
+	/**
616
+	 * Gets the event that's related to the ticket, if this line item represents a ticket.
617
+	 *
618
+	 * @return EE_Event|null
619
+	 * @throws EE_Error
620
+	 */
621
+	public function ticket_event()
622
+	{
623
+		$event = null;
624
+		$ticket = $this->ticket();
625
+		if ($ticket instanceof EE_Ticket) {
626
+			$datetime = $ticket->first_datetime();
627
+			if ($datetime instanceof EE_Datetime) {
628
+				$event = $datetime->event();
629
+			}
630
+		}
631
+		return $event;
632
+	}
633
+
634
+
635
+	/**
636
+	 * Gets the first datetime for this lien item, assuming it's for a ticket
637
+	 *
638
+	 * @param string $date_format
639
+	 * @param string $time_format
640
+	 * @return string
641
+	 * @throws EE_Error
642
+	 */
643
+	public function ticket_datetime_start($date_format = '', $time_format = '')
644
+	{
645
+		$first_datetime_string = esc_html__('Unknown', 'event_espresso');
646
+		$datetime = $this->get_ticket_datetime();
647
+		if ($datetime) {
648
+			$first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
649
+		}
650
+		return $first_datetime_string;
651
+	}
652
+
653
+
654
+	/**
655
+	 * Adds the line item as a child to this line item. If there is another child line
656
+	 * item with the same LIN_code, it is overwritten by this new one
657
+	 *
658
+	 * @param EEI_Line_Item $line_item
659
+	 * @param bool $set_order
660
+	 * @return bool success
661
+	 * @throws EE_Error
662
+	 */
663
+	public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
664
+	{
665
+		// should we calculate the LIN_order for this line item ?
666
+		if ($set_order || $line_item->order() === null) {
667
+			$line_item->set_order(count($this->children()));
668
+		}
669
+		if ($this->ID()) {
670
+			//check for any duplicate line items (with the same code), if so, this replaces it
671
+			$line_item_with_same_code = $this->get_child_line_item($line_item->code());
672
+			if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
673
+				$this->delete_child_line_item($line_item_with_same_code->code());
674
+			}
675
+			$line_item->set_parent_ID($this->ID());
676
+			if ($this->TXN_ID()) {
677
+				$line_item->set_TXN_ID($this->TXN_ID());
678
+			}
679
+			return $line_item->save();
680
+		}
681
+		$this->_children[$line_item->code()] = $line_item;
682
+		if ($line_item->parent() !== $this) {
683
+			$line_item->set_parent($this);
684
+		}
685
+		return true;
686
+	}
687
+
688
+
689
+	/**
690
+	 * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
691
+	 * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
692
+	 * However, if this line item is NOT saved to the DB, this just caches the parent on
693
+	 * the EE_Line_Item::_parent property.
694
+	 *
695
+	 * @param EE_Line_Item $line_item
696
+	 * @throws EE_Error
697
+	 */
698
+	public function set_parent($line_item)
699
+	{
700
+		if ($this->ID()) {
701
+			if (!$line_item->ID()) {
702
+				$line_item->save();
703
+			}
704
+			$this->set_parent_ID($line_item->ID());
705
+			$this->save();
706
+		} else {
707
+			$this->_parent = $line_item;
708
+			$this->set_parent_ID($line_item->ID());
709
+		}
710
+	}
711
+
712
+
713
+	/**
714
+	 * Gets the child line item as specified by its code. Because this returns an object (by reference)
715
+	 * you can modify this child line item and the parent (this object) can know about them
716
+	 * because it also has a reference to that line item
717
+	 *
718
+	 * @param string $code
719
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
720
+	 * @throws EE_Error
721
+	 */
722
+	public function get_child_line_item($code)
723
+	{
724
+		if ($this->ID()) {
725
+			return $this->get_model()->get_one(
726
+				array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
727
+			);
728
+		}
729
+		return isset($this->_children[$code])
730
+			? $this->_children[$code]
731
+			: null;
732
+	}
733
+
734
+
735
+	/**
736
+	 * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
737
+	 * cached on it)
738
+	 *
739
+	 * @return int
740
+	 * @throws EE_Error
741
+	 */
742
+	public function delete_children_line_items()
743
+	{
744
+		if ($this->ID()) {
745
+			return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
746
+		}
747
+		$count = count($this->_children);
748
+		$this->_children = array();
749
+		return $count;
750
+	}
751
+
752
+
753
+	/**
754
+	 * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
755
+	 * HAS NOT been saved to the DB, removes the child line item with index $code.
756
+	 * Also searches through the child's children for a matching line item. However, once a line item has been found
757
+	 * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
758
+	 * deleted)
759
+	 *
760
+	 * @param string $code
761
+	 * @param bool $stop_search_once_found
762
+	 * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
763
+	 *             the DB yet)
764
+	 * @throws EE_Error
765
+	 */
766
+	public function delete_child_line_item($code, $stop_search_once_found = true)
767
+	{
768
+		if ($this->ID()) {
769
+			$items_deleted = 0;
770
+			if ($this->code() === $code) {
771
+				$items_deleted += EEH_Line_Item::delete_all_child_items($this);
772
+				$items_deleted += (int)$this->delete();
773
+				if ($stop_search_once_found) {
774
+					return $items_deleted;
775
+				}
776
+			}
777
+			foreach ($this->children() as $child_line_item) {
778
+				$items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
779
+			}
780
+			return $items_deleted;
781
+		}
782
+		if (isset($this->_children[$code])) {
783
+			unset($this->_children[$code]);
784
+			return 1;
785
+		}
786
+		return 0;
787
+	}
788
+
789
+
790
+	/**
791
+	 * If this line item is in the database, is of the type subtotal, and
792
+	 * has no children, why do we have it? It should be deleted so this function
793
+	 * does that
794
+	 *
795
+	 * @return boolean
796
+	 * @throws EE_Error
797
+	 */
798
+	public function delete_if_childless_subtotal()
799
+	{
800
+		if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && !$this->children()) {
801
+			return $this->delete();
802
+		}
803
+		return false;
804
+	}
805
+
806
+
807
+	/**
808
+	 * Creates a code and returns a string. doesn't assign the code to this model object
809
+	 *
810
+	 * @return string
811
+	 * @throws EE_Error
812
+	 */
813
+	public function generate_code()
814
+	{
815
+		// each line item in the cart requires a unique identifier
816
+		return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
817
+	}
818
+
819
+
820
+	/**
821
+	 * @return bool
822
+	 * @throws EE_Error
823
+	 */
824
+	public function is_tax()
825
+	{
826
+		return $this->type() === EEM_Line_Item::type_tax;
827
+	}
828
+
829
+
830
+	/**
831
+	 * @return bool
832
+	 * @throws EE_Error
833
+	 */
834
+	public function is_tax_sub_total()
835
+	{
836
+		return $this->type() === EEM_Line_Item::type_tax_sub_total;
837
+	}
838
+
839
+
840
+	/**
841
+	 * @return bool
842
+	 * @throws EE_Error
843
+	 */
844
+	public function is_line_item()
845
+	{
846
+		return $this->type() === EEM_Line_Item::type_line_item;
847
+	}
848
+
849
+
850
+	/**
851
+	 * @return bool
852
+	 * @throws EE_Error
853
+	 */
854
+	public function is_sub_line_item()
855
+	{
856
+		return $this->type() === EEM_Line_Item::type_sub_line_item;
857
+	}
858
+
859
+
860
+	/**
861
+	 * @return bool
862
+	 * @throws EE_Error
863
+	 */
864
+	public function is_sub_total()
865
+	{
866
+		return $this->type() === EEM_Line_Item::type_sub_total;
867
+	}
868
+
869
+
870
+	/**
871
+	 * Whether or not this line item is a cancellation line item
872
+	 *
873
+	 * @return boolean
874
+	 * @throws EE_Error
875
+	 */
876
+	public function is_cancellation()
877
+	{
878
+		return EEM_Line_Item::type_cancellation === $this->type();
879
+	}
880
+
881
+
882
+	/**
883
+	 * @return bool
884
+	 * @throws EE_Error
885
+	 */
886
+	public function is_total()
887
+	{
888
+		return $this->type() === EEM_Line_Item::type_total;
889
+	}
890
+
891
+
892
+	/**
893
+	 * @return bool
894
+	 * @throws EE_Error
895
+	 */
896
+	public function is_cancelled()
897
+	{
898
+		return $this->type() === EEM_Line_Item::type_cancellation;
899
+	}
900
+
901
+
902
+	/**
903
+	 * @return string like '2, 004.00', formatted according to the localized currency
904
+	 * @throws EE_Error
905
+	 */
906
+	public function unit_price_no_code()
907
+	{
908
+		return $this->get_pretty('LIN_unit_price', 'no_currency_code');
909
+	}
910
+
911
+
912
+	/**
913
+	 * @return string like '2, 004.00', formatted according to the localized currency
914
+	 * @throws EE_Error
915
+	 */
916
+	public function total_no_code()
917
+	{
918
+		return $this->get_pretty('LIN_total', 'no_currency_code');
919
+	}
920
+
921
+
922
+	/**
923
+	 * Gets the final total on this item, taking taxes into account.
924
+	 * Has the side-effect of setting the sub-total as it was just calculated.
925
+	 * If this is used on a grand-total line item, also updates the transaction's
926
+	 * TXN_total (provided this line item is allowed to persist, otherwise we don't
927
+	 * want to change a persistable transaction with info from a non-persistent line item)
928
+	 *
929
+	 * @return float
930
+	 * @throws EE_Error
931
+	 * @throws InvalidArgumentException
932
+	 * @throws InvalidInterfaceException
933
+	 * @throws InvalidDataTypeException
934
+	 */
935
+	public function recalculate_total_including_taxes()
936
+	{
937
+		$pre_tax_total = $this->recalculate_pre_tax_total();
938
+		$tax_total = $this->recalculate_taxes_and_tax_total();
939
+		$total = $pre_tax_total + $tax_total;
940
+		// no negative totals plz
941
+		$total = max($total, 0);
942
+		$this->set_total($total);
943
+		//only update the related transaction's total
944
+		//if we intend to save this line item and its a grand total
945
+		if (
946
+			$this->allow_persist() && $this->type() === EEM_Line_Item::type_total
947
+			&& $this->transaction()
948
+			instanceof
949
+			EE_Transaction
950
+		) {
951
+			$this->transaction()->set_total($total);
952
+			if ($this->transaction()->ID()) {
953
+				$this->transaction()->save();
954
+			}
955
+		}
956
+		$this->maybe_save();
957
+		return $total;
958
+	}
959
+
960
+
961
+	/**
962
+	 * Recursively goes through all the children and recalculates sub-totals EXCEPT for
963
+	 * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
964
+	 * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
965
+	 * when this is called on the grand total
966
+	 *
967
+	 * @return float
968
+	 * @throws InvalidArgumentException
969
+	 * @throws InvalidInterfaceException
970
+	 * @throws InvalidDataTypeException
971
+	 * @throws EE_Error
972
+	 */
973
+	public function recalculate_pre_tax_total()
974
+	{
975
+		$total = 0;
976
+		$my_children = $this->children();
977
+		$has_children = !empty($my_children);
978
+		if ($has_children && $this->is_line_item()) {
979
+			$total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
980
+		} elseif (!$has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
981
+			$total = $this->unit_price() * $this->quantity();
982
+		} elseif ($this->is_sub_total() || $this->is_total()) {
983
+			$total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
984
+		} elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
985
+			// completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
986
+			return 0;
987
+		}
988
+		// ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
989
+		if (
990
+			!$this->is_line_item() && !$this->is_sub_line_item() && !$this->is_cancellation()
991
+		) {
992
+			if ($this->OBJ_type() !== 'Event') {
993
+				$this->set_quantity(1);
994
+			}
995
+			if (!$this->is_percent()) {
996
+				$this->set_unit_price($total);
997
+			}
998
+		}
999
+		//we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1000
+		//so it ought to be
1001
+		if (!$this->is_total()) {
1002
+			$this->set_total($total);
1003
+			//if not a percent line item, make sure we keep the unit price in sync
1004
+			if (
1005
+				$has_children
1006
+				&& $this->is_line_item()
1007
+				&& !$this->is_percent()
1008
+			) {
1009
+				if ($this->quantity() === 0) {
1010
+					$new_unit_price = 0;
1011
+				} else {
1012
+					$new_unit_price = $this->total() / $this->quantity();
1013
+				}
1014
+				$this->set_unit_price($new_unit_price);
1015
+			}
1016
+			$this->maybe_save();
1017
+		}
1018
+		return $total;
1019
+	}
1020
+
1021
+
1022
+	/**
1023
+	 * Calculates the pretax total when this line item is a subtotal or total line item.
1024
+	 * Basically does a sum-then-round approach (ie, any percent line item that are children
1025
+	 * will calculate their total based on the un-rounded total we're working with so far, and
1026
+	 * THEN round the result; instead of rounding as we go like with sub-line-items)
1027
+	 *
1028
+	 * @param float $calculated_total_so_far
1029
+	 * @param EE_Line_Item[] $my_children
1030
+	 * @return float
1031
+	 * @throws InvalidArgumentException
1032
+	 * @throws InvalidInterfaceException
1033
+	 * @throws InvalidDataTypeException
1034
+	 * @throws EE_Error
1035
+	 */
1036
+	protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1037
+	{
1038
+		if ($my_children === null) {
1039
+			$my_children = $this->children();
1040
+		}
1041
+		$subtotal_quantity = 0;
1042
+		//get the total of all its children
1043
+		foreach ($my_children as $child_line_item) {
1044
+			if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1045
+				// percentage line items are based on total so far
1046
+				if ($child_line_item->is_percent()) {
1047
+					//round as we go so that the line items add up ok
1048
+					$percent_total = round(
1049
+						$calculated_total_so_far * $child_line_item->percent() / 100,
1050
+						EE_Registry::instance()->CFG->currency->dec_plc
1051
+					);
1052
+					$child_line_item->set_total($percent_total);
1053
+					//so far all percent line items should have a quantity of 1
1054
+					//(ie, no double percent discounts. Although that might be requested someday)
1055
+					$child_line_item->set_quantity(1);
1056
+					$child_line_item->maybe_save();
1057
+					$calculated_total_so_far += $percent_total;
1058
+				} else {
1059
+					//verify flat sub-line-item quantities match their parent
1060
+					if ($child_line_item->is_sub_line_item()) {
1061
+						$child_line_item->set_quantity($this->quantity());
1062
+					}
1063
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1064
+					$subtotal_quantity += $child_line_item->quantity();
1065
+				}
1066
+			}
1067
+		}
1068
+		if ($this->is_sub_total()) {
1069
+			// no negative totals plz
1070
+			$calculated_total_so_far = max($calculated_total_so_far, 0);
1071
+			$subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1072
+			$this->set_quantity($subtotal_quantity);
1073
+			$this->maybe_save();
1074
+		}
1075
+		return $calculated_total_so_far;
1076
+	}
1077
+
1078
+
1079
+	/**
1080
+	 * Calculates the pretax total for a normal line item, in a round-then-sum approach
1081
+	 * (where each sub-line-item is applied to the base price for the line item
1082
+	 * and the result is immediately rounded, rather than summing all the sub-line-items
1083
+	 * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1084
+	 *
1085
+	 * @param float $calculated_total_so_far
1086
+	 * @param EE_Line_Item[] $my_children
1087
+	 * @return float
1088
+	 * @throws InvalidArgumentException
1089
+	 * @throws InvalidInterfaceException
1090
+	 * @throws InvalidDataTypeException
1091
+	 * @throws EE_Error
1092
+	 */
1093
+	protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1094
+	{
1095
+		if ($my_children === null) {
1096
+			$my_children = $this->children();
1097
+		}
1098
+		//we need to keep track of the running total for a single item,
1099
+		//because we need to round as we go
1100
+		$unit_price_for_total = 0;
1101
+		$quantity_for_total = 1;
1102
+		//get the total of all its children
1103
+		foreach ($my_children as $child_line_item) {
1104
+			if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1105
+				if ($child_line_item->is_percent()) {
1106
+					//it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1107
+					//not total multiplied by percent, because that ignores rounding along-the-way
1108
+					$percent_unit_price = round(
1109
+						$unit_price_for_total * $child_line_item->percent() / 100,
1110
+						EE_Registry::instance()->CFG->currency->dec_plc
1111
+					);
1112
+					$percent_total = $percent_unit_price * $quantity_for_total;
1113
+					$child_line_item->set_total($percent_total);
1114
+					//so far all percent line items should have a quantity of 1
1115
+					//(ie, no double percent discounts. Although that might be requested someday)
1116
+					$child_line_item->set_quantity(1);
1117
+					$child_line_item->maybe_save();
1118
+					$calculated_total_so_far += $percent_total;
1119
+					$unit_price_for_total += $percent_unit_price;
1120
+				} else {
1121
+					//verify flat sub-line-item quantities match their parent
1122
+					if ($child_line_item->is_sub_line_item()) {
1123
+						$child_line_item->set_quantity($this->quantity());
1124
+					}
1125
+					$quantity_for_total = $child_line_item->quantity();
1126
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1127
+					$unit_price_for_total += $child_line_item->unit_price();
1128
+				}
1129
+			}
1130
+		}
1131
+		return $calculated_total_so_far;
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1137
+	 * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1138
+	 * and tax sub-total if already in the DB
1139
+	 *
1140
+	 * @return float
1141
+	 * @throws EE_Error
1142
+	 */
1143
+	public function recalculate_taxes_and_tax_total()
1144
+	{
1145
+		//get all taxes
1146
+		$taxes = $this->tax_descendants();
1147
+		//calculate the pretax total
1148
+		$taxable_total = $this->taxable_total();
1149
+		$tax_total = 0;
1150
+		foreach ($taxes as $tax) {
1151
+			$total_on_this_tax = $taxable_total * $tax->percent() / 100;
1152
+			//remember the total on this line item
1153
+			$tax->set_total($total_on_this_tax);
1154
+			$tax->maybe_save();
1155
+			$tax_total += $tax->total();
1156
+		}
1157
+		$this->_recalculate_tax_sub_total();
1158
+		return $tax_total;
1159
+	}
1160
+
1161
+
1162
+	/**
1163
+	 * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1164
+	 *
1165
+	 * @return void
1166
+	 * @throws EE_Error
1167
+	 */
1168
+	private function _recalculate_tax_sub_total()
1169
+	{
1170
+		if ($this->is_tax_sub_total()) {
1171
+			$total = 0;
1172
+			$total_percent = 0;
1173
+			//simply loop through all its children (which should be taxes) and sum their total
1174
+			foreach ($this->children() as $child_tax) {
1175
+				if ($child_tax instanceof EE_Line_Item) {
1176
+					$total += $child_tax->total();
1177
+					$total_percent += $child_tax->percent();
1178
+				}
1179
+			}
1180
+			$this->set_total($total);
1181
+			$this->set_percent($total_percent);
1182
+			$this->maybe_save();
1183
+		} elseif ($this->is_total()) {
1184
+			foreach ($this->children() as $maybe_tax_subtotal) {
1185
+				if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1186
+					$maybe_tax_subtotal->_recalculate_tax_sub_total();
1187
+				}
1188
+			}
1189
+		}
1190
+	}
1191
+
1192
+
1193
+	/**
1194
+	 * Gets the total tax on this line item. Assumes taxes have already been calculated using
1195
+	 * recalculate_taxes_and_total
1196
+	 *
1197
+	 * @return float
1198
+	 * @throws EE_Error
1199
+	 */
1200
+	public function get_total_tax()
1201
+	{
1202
+		$this->_recalculate_tax_sub_total();
1203
+		$total = 0;
1204
+		foreach ($this->tax_descendants() as $tax_line_item) {
1205
+			if ($tax_line_item instanceof EE_Line_Item) {
1206
+				$total += $tax_line_item->total();
1207
+			}
1208
+		}
1209
+		return $total;
1210
+	}
1211
+
1212
+
1213
+	/**
1214
+	 * Gets the total for all the items purchased only
1215
+	 *
1216
+	 * @return float
1217
+	 * @throws EE_Error
1218
+	 */
1219
+	public function get_items_total()
1220
+	{
1221
+		//by default, let's make sure we're consistent with the existing line item
1222
+		if ($this->is_total()) {
1223
+			$pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1224
+			if ($pretax_subtotal_li instanceof EE_Line_Item) {
1225
+				return $pretax_subtotal_li->total();
1226
+			}
1227
+		}
1228
+		$total = 0;
1229
+		foreach ($this->get_items() as $item) {
1230
+			if ($item instanceof EE_Line_Item) {
1231
+				$total += $item->total();
1232
+			}
1233
+		}
1234
+		return $total;
1235
+	}
1236
+
1237
+
1238
+	/**
1239
+	 * Gets all the descendants (ie, children or children of children etc) that
1240
+	 * are of the type 'tax'
1241
+	 *
1242
+	 * @return EE_Line_Item[]
1243
+	 */
1244
+	public function tax_descendants()
1245
+	{
1246
+		return EEH_Line_Item::get_tax_descendants($this);
1247
+	}
1248
+
1249
+
1250
+	/**
1251
+	 * Gets all the real items purchased which are children of this item
1252
+	 *
1253
+	 * @return EE_Line_Item[]
1254
+	 */
1255
+	public function get_items()
1256
+	{
1257
+		return EEH_Line_Item::get_line_item_descendants($this);
1258
+	}
1259
+
1260
+
1261
+	/**
1262
+	 * Returns the amount taxable among this line item's children (or if it has no children,
1263
+	 * how much of it is taxable). Does not recalculate totals or subtotals.
1264
+	 * If the taxable total is negative, (eg, if none of the tickets were taxable,
1265
+	 * but there is a "Taxable" discount), returns 0.
1266
+	 *
1267
+	 * @return float
1268
+	 * @throws EE_Error
1269
+	 */
1270
+	public function taxable_total()
1271
+	{
1272
+		$total = 0;
1273
+		if ($this->children()) {
1274
+			foreach ($this->children() as $child_line_item) {
1275
+				if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1276
+					//if it's a percent item, only take into account the percent
1277
+					//that's taxable too (the taxable total so far)
1278
+					if ($child_line_item->is_percent()) {
1279
+						$total += ($total * $child_line_item->percent() / 100);
1280
+					} else {
1281
+						$total += $child_line_item->total();
1282
+					}
1283
+				} elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1284
+					$total += $child_line_item->taxable_total();
1285
+				}
1286
+			}
1287
+		}
1288
+		return max($total, 0);
1289
+	}
1290
+
1291
+
1292
+	/**
1293
+	 * Gets the transaction for this line item
1294
+	 *
1295
+	 * @return EE_Base_Class|EE_Transaction
1296
+	 * @throws EE_Error
1297
+	 */
1298
+	public function transaction()
1299
+	{
1300
+		return $this->get_first_related('Transaction');
1301
+	}
1302
+
1303
+
1304
+	/**
1305
+	 * Saves this line item to the DB, and recursively saves its descendants.
1306
+	 * Because there currently is no proper parent-child relation on the model,
1307
+	 * save_this_and_cached() will NOT save the descendants.
1308
+	 * Also sets the transaction on this line item and all its descendants before saving
1309
+	 *
1310
+	 * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1311
+	 * @return int count of items saved
1312
+	 * @throws EE_Error
1313
+	 */
1314
+	public function save_this_and_descendants_to_txn($txn_id = null)
1315
+	{
1316
+		$count = 0;
1317
+		if (!$txn_id) {
1318
+			$txn_id = $this->TXN_ID();
1319
+		}
1320
+		$this->set_TXN_ID($txn_id);
1321
+		$children = $this->children();
1322
+		$count += $this->save()
1323
+			? 1
1324
+			: 0;
1325
+		foreach ($children as $child_line_item) {
1326
+			if ($child_line_item instanceof EE_Line_Item) {
1327
+				$child_line_item->set_parent_ID($this->ID());
1328
+				$count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1329
+			}
1330
+		}
1331
+		return $count;
1332
+	}
1333
+
1334
+
1335
+	/**
1336
+	 * Saves this line item to the DB, and recursively saves its descendants.
1337
+	 *
1338
+	 * @return int count of items saved
1339
+	 * @throws EE_Error
1340
+	 */
1341
+	public function save_this_and_descendants()
1342
+	{
1343
+		$count = 0;
1344
+		$children = $this->children();
1345
+		$count += $this->save()
1346
+			? 1
1347
+			: 0;
1348
+		foreach ($children as $child_line_item) {
1349
+			if ($child_line_item instanceof EE_Line_Item) {
1350
+				$child_line_item->set_parent_ID($this->ID());
1351
+				$count += $child_line_item->save_this_and_descendants();
1352
+			}
1353
+		}
1354
+		return $count;
1355
+	}
1356
+
1357
+
1358
+	/**
1359
+	 * returns the cancellation line item if this item was cancelled
1360
+	 *
1361
+	 * @return EE_Line_Item[]
1362
+	 * @throws InvalidArgumentException
1363
+	 * @throws InvalidInterfaceException
1364
+	 * @throws InvalidDataTypeException
1365
+	 * @throws ReflectionException
1366
+	 * @throws EE_Error
1367
+	 */
1368
+	public function get_cancellations()
1369
+	{
1370
+		EE_Registry::instance()->load_helper('Line_Item');
1371
+		return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1372
+	}
1373
+
1374
+
1375
+	/**
1376
+	 * If this item has an ID, then this saves it again to update the db
1377
+	 *
1378
+	 * @return int count of items saved
1379
+	 * @throws EE_Error
1380
+	 */
1381
+	public function maybe_save()
1382
+	{
1383
+		if ($this->ID()) {
1384
+			return $this->save();
1385
+		}
1386
+		return false;
1387
+	}
1388
+
1389
+
1390
+	/**
1391
+	 * clears the cached children and parent from the line item
1392
+	 *
1393
+	 * @return void
1394
+	 */
1395
+	public function clear_related_line_item_cache()
1396
+	{
1397
+		$this->_children = array();
1398
+		$this->_parent = null;
1399
+	}
1400
+
1401
+
1402
+	/**
1403
+	 * @param bool $raw
1404
+	 * @return int
1405
+	 * @throws EE_Error
1406
+	 */
1407
+	public function timestamp($raw = false)
1408
+	{
1409
+		return $raw
1410
+			? $this->get_raw('LIN_timestamp')
1411
+			: $this->get('LIN_timestamp');
1412
+	}
1413
+
1414
+
1415
+
1416
+
1417
+	/************************* DEPRECATED *************************/
1418
+	/**
1419
+	 * @deprecated 4.6.0
1420
+	 * @param string $type one of the constants on EEM_Line_Item
1421
+	 * @return EE_Line_Item[]
1422
+	 */
1423
+	protected function _get_descendants_of_type($type)
1424
+	{
1425
+		EE_Error::doing_it_wrong(
1426
+			'EE_Line_Item::_get_descendants_of_type()',
1427
+			__('Method replaced with EEH_Line_Item::get_descendants_of_type()', 'event_espresso'), '4.6.0'
1428
+		);
1429
+		return EEH_Line_Item::get_descendants_of_type($this, $type);
1430
+	}
1431
+
1432
+
1433
+	/**
1434
+	 * @deprecated 4.6.0
1435
+	 * @param string $type like one of the EEM_Line_Item::type_*
1436
+	 * @return EE_Line_Item
1437
+	 */
1438
+	public function get_nearest_descendant_of_type($type)
1439
+	{
1440
+		EE_Error::doing_it_wrong(
1441
+			'EE_Line_Item::get_nearest_descendant_of_type()',
1442
+			__('Method replaced with EEH_Line_Item::get_nearest_descendant_of_type()', 'event_espresso'), '4.6.0'
1443
+		);
1444
+		return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1445
+	}
1446 1446
 
1447 1447
 
1448 1448
 }
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
     protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
76 76
     {
77 77
         parent::__construct($fieldValues, $bydb, $timezone);
78
-        if (!$this->get('LIN_code')) {
78
+        if ( ! $this->get('LIN_code')) {
79 79
             $this->set_code($this->generate_code());
80 80
         }
81 81
     }
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
     public function name()
127 127
     {
128 128
         $name = $this->get('LIN_name');
129
-        if (!$name) {
129
+        if ( ! $name) {
130 130
             $name = ucwords(str_replace('-', ' ', $this->type()));
131 131
         }
132 132
         return $name;
@@ -313,7 +313,7 @@  discard block
 block discarded – undo
313 313
         if ($unit_price < .001 && $percent) {
314 314
             return true;
315 315
         }
316
-        if ($unit_price >= .001 && !$percent) {
316
+        if ($unit_price >= .001 && ! $percent) {
317 317
             return false;
318 318
         }
319 319
         if ($unit_price >= .001 && $percent) {
@@ -482,7 +482,7 @@  discard block
 block discarded – undo
482 482
                 )
483 483
             );
484 484
         }
485
-        if (!is_array($this->_children)) {
485
+        if ( ! is_array($this->_children)) {
486 486
             $this->_children = array();
487 487
         }
488 488
         return $this->_children;
@@ -698,7 +698,7 @@  discard block
 block discarded – undo
698 698
     public function set_parent($line_item)
699 699
     {
700 700
         if ($this->ID()) {
701
-            if (!$line_item->ID()) {
701
+            if ( ! $line_item->ID()) {
702 702
                 $line_item->save();
703 703
             }
704 704
             $this->set_parent_ID($line_item->ID());
@@ -769,7 +769,7 @@  discard block
 block discarded – undo
769 769
             $items_deleted = 0;
770 770
             if ($this->code() === $code) {
771 771
                 $items_deleted += EEH_Line_Item::delete_all_child_items($this);
772
-                $items_deleted += (int)$this->delete();
772
+                $items_deleted += (int) $this->delete();
773 773
                 if ($stop_search_once_found) {
774 774
                     return $items_deleted;
775 775
                 }
@@ -797,7 +797,7 @@  discard block
 block discarded – undo
797 797
      */
798 798
     public function delete_if_childless_subtotal()
799 799
     {
800
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && !$this->children()) {
800
+        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
801 801
             return $this->delete();
802 802
         }
803 803
         return false;
@@ -813,7 +813,7 @@  discard block
 block discarded – undo
813 813
     public function generate_code()
814 814
     {
815 815
         // each line item in the cart requires a unique identifier
816
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
816
+        return md5($this->get('OBJ_type').$this->get('OBJ_ID').microtime());
817 817
     }
818 818
 
819 819
 
@@ -974,10 +974,10 @@  discard block
 block discarded – undo
974 974
     {
975 975
         $total = 0;
976 976
         $my_children = $this->children();
977
-        $has_children = !empty($my_children);
977
+        $has_children = ! empty($my_children);
978 978
         if ($has_children && $this->is_line_item()) {
979 979
             $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
980
-        } elseif (!$has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
980
+        } elseif ( ! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
981 981
             $total = $this->unit_price() * $this->quantity();
982 982
         } elseif ($this->is_sub_total() || $this->is_total()) {
983 983
             $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
@@ -987,24 +987,24 @@  discard block
 block discarded – undo
987 987
         }
988 988
         // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
989 989
         if (
990
-            !$this->is_line_item() && !$this->is_sub_line_item() && !$this->is_cancellation()
990
+            ! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
991 991
         ) {
992 992
             if ($this->OBJ_type() !== 'Event') {
993 993
                 $this->set_quantity(1);
994 994
             }
995
-            if (!$this->is_percent()) {
995
+            if ( ! $this->is_percent()) {
996 996
                 $this->set_unit_price($total);
997 997
             }
998 998
         }
999 999
         //we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1000 1000
         //so it ought to be
1001
-        if (!$this->is_total()) {
1001
+        if ( ! $this->is_total()) {
1002 1002
             $this->set_total($total);
1003 1003
             //if not a percent line item, make sure we keep the unit price in sync
1004 1004
             if (
1005 1005
                 $has_children
1006 1006
                 && $this->is_line_item()
1007
-                && !$this->is_percent()
1007
+                && ! $this->is_percent()
1008 1008
             ) {
1009 1009
                 if ($this->quantity() === 0) {
1010 1010
                     $new_unit_price = 0;
@@ -1041,7 +1041,7 @@  discard block
 block discarded – undo
1041 1041
         $subtotal_quantity = 0;
1042 1042
         //get the total of all its children
1043 1043
         foreach ($my_children as $child_line_item) {
1044
-            if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1044
+            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1045 1045
                 // percentage line items are based on total so far
1046 1046
                 if ($child_line_item->is_percent()) {
1047 1047
                     //round as we go so that the line items add up ok
@@ -1101,7 +1101,7 @@  discard block
 block discarded – undo
1101 1101
         $quantity_for_total = 1;
1102 1102
         //get the total of all its children
1103 1103
         foreach ($my_children as $child_line_item) {
1104
-            if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1104
+            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1105 1105
                 if ($child_line_item->is_percent()) {
1106 1106
                     //it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1107 1107
                     //not total multiplied by percent, because that ignores rounding along-the-way
@@ -1314,7 +1314,7 @@  discard block
 block discarded – undo
1314 1314
     public function save_this_and_descendants_to_txn($txn_id = null)
1315 1315
     {
1316 1316
         $count = 0;
1317
-        if (!$txn_id) {
1317
+        if ( ! $txn_id) {
1318 1318
             $txn_id = $this->TXN_ID();
1319 1319
         }
1320 1320
         $this->set_TXN_ID($txn_id);
Please login to merge, or discard this patch.
core/db_models/EEM_Line_Item.model.php 3 patches
Indentation   +375 added lines, -375 removed lines patch added patch discarded remove patch
@@ -41,384 +41,384 @@
 block discarded – undo
41 41
 class EEM_Line_Item extends EEM_Base
42 42
 {
43 43
 
44
-    /**
45
-     * Tax sub-total is just the total of all the taxes, which should be children
46
-     * of this line item. There should only ever be one tax sub-total, and it should
47
-     * be a direct child of
48
-     */
49
-    const type_tax_sub_total = 'tax-sub-total';
50
-
51
-    /**
52
-     * Tax line items indicate a tax applied to all the taxable line items.
53
-     * Should not have any children line items.
54
-     */
55
-    const type_tax = 'tax';
56
-
57
-    /**
58
-     * Indicating individual items purchased, or discounts or surcharges.
59
-     * The sum of all the regular line items  plus the tax items should equal
60
-     * the grand total.
61
-     * Possible children fo sub-line-items and cancellations.
62
-     */
63
-    const type_line_item = 'line-item';
64
-
65
-    /**
66
-     * line item indicating all the factors that make a single line item.
67
-     * Sub-line items should have NO children line items.
68
-     */
69
-    const type_sub_line_item = 'sub-item';
70
-
71
-    /**
72
-     * line item indicating a sub-total (eg total for an event, or before taxes).
73
-     * Direct children can be line items and other sub-totals
74
-     *
75
-     */
76
-    const type_sub_total = 'sub-total';
77
-
78
-    /**
79
-     * line item for teh grand total of an order. Its direct children
80
-     * should be tax subtotals and subtotals, and possibly a regular line item
81
-     * indicating a transaction-wide discount/surcharge
82
-     */
83
-    const type_total = 'total';
84
-
85
-    /**
86
-     * When a line item is cancelled, a sub-line-item of type 'cancellation'
87
-     * should be created, indicating the quantity that were cancelled
88
-     * (because a line item could have a quantity of 1, and its cancellation item
89
-     * could be for 3, indicating that originally 4 were purchased, but 3 have been
90
-     * cancelled, and only one remains).
91
-     * When items are refunded, a cancellation line item should be made, which points
92
-     * to teh payment model object which actually refunded the payment.
93
-     * Cancellations should NOT have any children line items; the should NOT affect
94
-     * any calculations, and are only meant as a record that cancellations have occurred.
95
-     */
96
-    const type_cancellation = 'cancellation';
97
-
98
-    // private instance of the EEM_Line_Item object
99
-    protected static $_instance = NULL;
100
-
101
-
102
-    /**
103
-     *        private constructor to prevent direct creation
104
-     * @Constructor
105
-     * @access protected
106
-     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any incoming timezone data that gets saved).  Note this just sends the timezone info to the date time model field objects.  Default is NULL (and will be assumed using the set timezone in the 'timezone_string' wp option)
107
-     * @return \EEM_Line_Item
108
-     */
109
-    protected function __construct($timezone)
110
-    {
111
-        $this->singular_item = __('Line Item', 'event_espresso');
112
-        $this->plural_item = __('Line Items', 'event_espresso');
113
-
114
-        $this->_tables = array(
115
-            'Line_Item' => new EE_Primary_Table('esp_line_item', 'LIN_ID')
116
-        );
117
-        $line_items_can_be_for = apply_filters('FHEE__EEM_Line_Item__line_items_can_be_for', array('Ticket', 'Price', 'Event'));
118
-        $this->_fields = array(
119
-            'Line_Item' => array(
120
-                'LIN_ID' => new EE_Primary_Key_Int_Field('LIN_ID', __("ID", "event_espresso")),
121
-                'LIN_code' => new EE_Slug_Field('LIN_code', __("Code for index into Cart", "event_espresso"), TRUE),
122
-                'TXN_ID' => new EE_Foreign_Key_Int_Field('TXN_ID', __("Transaction ID", "event_espresso"), TRUE, NULL, 'Transaction'),
123
-                'LIN_name' => new EE_Full_HTML_Field('LIN_name', __("Line Item Name", "event_espresso"), FALSE, ''),
124
-                'LIN_desc' => new EE_Full_HTML_Field('LIN_desc', __("Line Item Description", "event_espresso"), TRUE),
125
-                'LIN_unit_price' => new EE_Money_Field('LIN_unit_price', __("Unit Price", "event_espresso"), FALSE, 0),
126
-                'LIN_percent' => new EE_Float_Field('LIN_percent', __("Percent", "event_espresso"), FALSE, 0),
127
-                'LIN_is_taxable' => new EE_Boolean_Field('LIN_is_taxable', __("Taxable", "event_espresso"), FALSE, FALSE),
128
-                'LIN_order' => new EE_Integer_Field('LIN_order', __("Order of Application towards total of parent", "event_espresso"), FALSE, 1),
129
-                'LIN_total' => new EE_Money_Field('LIN_total', __("Total (unit price x quantity)", "event_espresso"), FALSE, 0),
130
-                'LIN_quantity' => new EE_Integer_Field('LIN_quantity', __("Quantity", "event_espresso"), TRUE, 1),
131
-                'LIN_parent' => new EE_Integer_Field('LIN_parent', __("Parent ID (this item goes towards that Line Item's total)", "event_espresso"), TRUE, NULL),
132
-                'LIN_type' => new EE_Enum_Text_Field('LIN_type', __("Type", "event_espresso"), FALSE, 'line-item', array(
133
-                        self::type_line_item => __("Line Item", "event_espresso"),
134
-                        self::type_sub_line_item => __("Sub-Item", "event_espresso"),
135
-                        self::type_sub_total => __("Subtotal", "event_espresso"),
136
-                        self::type_tax_sub_total => __("Tax Subtotal", "event_espresso"),
137
-                        self::type_tax => __("Tax", "event_espresso"),
138
-                        self::type_total => __("Total", "event_espresso"),
139
-                        self::type_cancellation => __('Cancellation', 'event_espresso')
140
-                    )
141
-                ),
142
-                'OBJ_ID' => new EE_Foreign_Key_Int_Field('OBJ_ID', __('ID of Item purchased.', 'event_espresso'), TRUE, NULL, $line_items_can_be_for),
143
-                'OBJ_type' => new EE_Any_Foreign_Model_Name_Field('OBJ_type', __("Model Name this Line Item is for", "event_espresso"), TRUE, NULL, $line_items_can_be_for),
144
-                'LIN_timestamp' => new EE_Datetime_Field('LIN_timestamp', __('When the line item was created', 'event_espresso'), false, EE_Datetime_Field::now, $timezone),
145
-            )
146
-        );
147
-        $this->_model_relations = array(
148
-            'Transaction' => new EE_Belongs_To_Relation(),
149
-            'Ticket' => new EE_Belongs_To_Any_Relation(),
150
-            'Price' => new EE_Belongs_To_Any_Relation(),
151
-            'Event' => new EE_Belongs_To_Any_Relation()
152
-        );
153
-        $this->_model_chain_to_wp_user = 'Transaction.Registration.Event';
154
-        $this->_caps_slug = 'transactions';
155
-        parent::__construct($timezone);
156
-    }
157
-
158
-
159
-    /**
160
-     * Gets all the line items for this transaction of the given type
161
-     * @param string $line_item_type like one of EEM_Line_Item::type_*
162
-     * @param EE_Transaction|int $transaction
163
-     * @return EE_Line_Item[]
164
-     */
165
-    public function get_all_of_type_for_transaction($line_item_type, $transaction)
166
-    {
167
-        $transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
168
-        return $this->get_all(array(array(
169
-            'LIN_type' => $line_item_type,
170
-            'TXN_ID' => $transaction
171
-        )));
172
-    }
173
-
174
-
175
-    /**
176
-     * Gets all line items unrelated to tickets that are normal line items
177
-     * (eg shipping, promotions, and miscellaneous other stuff should probably fit in this category)
178
-     * @param EE_Transaction|int $transaction
179
-     * @return EE_Line_Item[]
180
-     */
181
-    public function get_all_non_ticket_line_items_for_transaction($transaction)
182
-    {
183
-        $transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
184
-        return $this->get_all(array(array(
185
-            'LIN_type' => self::type_line_item,
186
-            'TXN_ID' => $transaction,
187
-            'OR' => array(
188
-                'OBJ_type*notticket' => array('!=', 'Ticket'),
189
-                'OBJ_type*null' => array('IS_NULL'))
190
-        )));
191
-    }
192
-
193
-    /**
194
-     * Deletes line items with no transaction who have passed the transaction cutoff time.
195
-     * This needs to be very efficient
196
-     * because if there are spam bots afoot there will be LOTS of line items
197
-     * @return int count of how many deleted
198
-     */
199
-    public function delete_line_items_with_no_transaction()
200
-    {
201
-        /** @type WPDB $wpdb */
202
-        global $wpdb;
203
-        $time_to_leave_alone = apply_filters(
204
-            'FHEE__EEM_Line_Item__delete_line_items_with_no_transaction__time_to_leave_alone', WEEK_IN_SECONDS
205
-        );
206
-        $query = $wpdb->prepare(
207
-            'DELETE li
44
+	/**
45
+	 * Tax sub-total is just the total of all the taxes, which should be children
46
+	 * of this line item. There should only ever be one tax sub-total, and it should
47
+	 * be a direct child of
48
+	 */
49
+	const type_tax_sub_total = 'tax-sub-total';
50
+
51
+	/**
52
+	 * Tax line items indicate a tax applied to all the taxable line items.
53
+	 * Should not have any children line items.
54
+	 */
55
+	const type_tax = 'tax';
56
+
57
+	/**
58
+	 * Indicating individual items purchased, or discounts or surcharges.
59
+	 * The sum of all the regular line items  plus the tax items should equal
60
+	 * the grand total.
61
+	 * Possible children fo sub-line-items and cancellations.
62
+	 */
63
+	const type_line_item = 'line-item';
64
+
65
+	/**
66
+	 * line item indicating all the factors that make a single line item.
67
+	 * Sub-line items should have NO children line items.
68
+	 */
69
+	const type_sub_line_item = 'sub-item';
70
+
71
+	/**
72
+	 * line item indicating a sub-total (eg total for an event, or before taxes).
73
+	 * Direct children can be line items and other sub-totals
74
+	 *
75
+	 */
76
+	const type_sub_total = 'sub-total';
77
+
78
+	/**
79
+	 * line item for teh grand total of an order. Its direct children
80
+	 * should be tax subtotals and subtotals, and possibly a regular line item
81
+	 * indicating a transaction-wide discount/surcharge
82
+	 */
83
+	const type_total = 'total';
84
+
85
+	/**
86
+	 * When a line item is cancelled, a sub-line-item of type 'cancellation'
87
+	 * should be created, indicating the quantity that were cancelled
88
+	 * (because a line item could have a quantity of 1, and its cancellation item
89
+	 * could be for 3, indicating that originally 4 were purchased, but 3 have been
90
+	 * cancelled, and only one remains).
91
+	 * When items are refunded, a cancellation line item should be made, which points
92
+	 * to teh payment model object which actually refunded the payment.
93
+	 * Cancellations should NOT have any children line items; the should NOT affect
94
+	 * any calculations, and are only meant as a record that cancellations have occurred.
95
+	 */
96
+	const type_cancellation = 'cancellation';
97
+
98
+	// private instance of the EEM_Line_Item object
99
+	protected static $_instance = NULL;
100
+
101
+
102
+	/**
103
+	 *        private constructor to prevent direct creation
104
+	 * @Constructor
105
+	 * @access protected
106
+	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any incoming timezone data that gets saved).  Note this just sends the timezone info to the date time model field objects.  Default is NULL (and will be assumed using the set timezone in the 'timezone_string' wp option)
107
+	 * @return \EEM_Line_Item
108
+	 */
109
+	protected function __construct($timezone)
110
+	{
111
+		$this->singular_item = __('Line Item', 'event_espresso');
112
+		$this->plural_item = __('Line Items', 'event_espresso');
113
+
114
+		$this->_tables = array(
115
+			'Line_Item' => new EE_Primary_Table('esp_line_item', 'LIN_ID')
116
+		);
117
+		$line_items_can_be_for = apply_filters('FHEE__EEM_Line_Item__line_items_can_be_for', array('Ticket', 'Price', 'Event'));
118
+		$this->_fields = array(
119
+			'Line_Item' => array(
120
+				'LIN_ID' => new EE_Primary_Key_Int_Field('LIN_ID', __("ID", "event_espresso")),
121
+				'LIN_code' => new EE_Slug_Field('LIN_code', __("Code for index into Cart", "event_espresso"), TRUE),
122
+				'TXN_ID' => new EE_Foreign_Key_Int_Field('TXN_ID', __("Transaction ID", "event_espresso"), TRUE, NULL, 'Transaction'),
123
+				'LIN_name' => new EE_Full_HTML_Field('LIN_name', __("Line Item Name", "event_espresso"), FALSE, ''),
124
+				'LIN_desc' => new EE_Full_HTML_Field('LIN_desc', __("Line Item Description", "event_espresso"), TRUE),
125
+				'LIN_unit_price' => new EE_Money_Field('LIN_unit_price', __("Unit Price", "event_espresso"), FALSE, 0),
126
+				'LIN_percent' => new EE_Float_Field('LIN_percent', __("Percent", "event_espresso"), FALSE, 0),
127
+				'LIN_is_taxable' => new EE_Boolean_Field('LIN_is_taxable', __("Taxable", "event_espresso"), FALSE, FALSE),
128
+				'LIN_order' => new EE_Integer_Field('LIN_order', __("Order of Application towards total of parent", "event_espresso"), FALSE, 1),
129
+				'LIN_total' => new EE_Money_Field('LIN_total', __("Total (unit price x quantity)", "event_espresso"), FALSE, 0),
130
+				'LIN_quantity' => new EE_Integer_Field('LIN_quantity', __("Quantity", "event_espresso"), TRUE, 1),
131
+				'LIN_parent' => new EE_Integer_Field('LIN_parent', __("Parent ID (this item goes towards that Line Item's total)", "event_espresso"), TRUE, NULL),
132
+				'LIN_type' => new EE_Enum_Text_Field('LIN_type', __("Type", "event_espresso"), FALSE, 'line-item', array(
133
+						self::type_line_item => __("Line Item", "event_espresso"),
134
+						self::type_sub_line_item => __("Sub-Item", "event_espresso"),
135
+						self::type_sub_total => __("Subtotal", "event_espresso"),
136
+						self::type_tax_sub_total => __("Tax Subtotal", "event_espresso"),
137
+						self::type_tax => __("Tax", "event_espresso"),
138
+						self::type_total => __("Total", "event_espresso"),
139
+						self::type_cancellation => __('Cancellation', 'event_espresso')
140
+					)
141
+				),
142
+				'OBJ_ID' => new EE_Foreign_Key_Int_Field('OBJ_ID', __('ID of Item purchased.', 'event_espresso'), TRUE, NULL, $line_items_can_be_for),
143
+				'OBJ_type' => new EE_Any_Foreign_Model_Name_Field('OBJ_type', __("Model Name this Line Item is for", "event_espresso"), TRUE, NULL, $line_items_can_be_for),
144
+				'LIN_timestamp' => new EE_Datetime_Field('LIN_timestamp', __('When the line item was created', 'event_espresso'), false, EE_Datetime_Field::now, $timezone),
145
+			)
146
+		);
147
+		$this->_model_relations = array(
148
+			'Transaction' => new EE_Belongs_To_Relation(),
149
+			'Ticket' => new EE_Belongs_To_Any_Relation(),
150
+			'Price' => new EE_Belongs_To_Any_Relation(),
151
+			'Event' => new EE_Belongs_To_Any_Relation()
152
+		);
153
+		$this->_model_chain_to_wp_user = 'Transaction.Registration.Event';
154
+		$this->_caps_slug = 'transactions';
155
+		parent::__construct($timezone);
156
+	}
157
+
158
+
159
+	/**
160
+	 * Gets all the line items for this transaction of the given type
161
+	 * @param string $line_item_type like one of EEM_Line_Item::type_*
162
+	 * @param EE_Transaction|int $transaction
163
+	 * @return EE_Line_Item[]
164
+	 */
165
+	public function get_all_of_type_for_transaction($line_item_type, $transaction)
166
+	{
167
+		$transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
168
+		return $this->get_all(array(array(
169
+			'LIN_type' => $line_item_type,
170
+			'TXN_ID' => $transaction
171
+		)));
172
+	}
173
+
174
+
175
+	/**
176
+	 * Gets all line items unrelated to tickets that are normal line items
177
+	 * (eg shipping, promotions, and miscellaneous other stuff should probably fit in this category)
178
+	 * @param EE_Transaction|int $transaction
179
+	 * @return EE_Line_Item[]
180
+	 */
181
+	public function get_all_non_ticket_line_items_for_transaction($transaction)
182
+	{
183
+		$transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
184
+		return $this->get_all(array(array(
185
+			'LIN_type' => self::type_line_item,
186
+			'TXN_ID' => $transaction,
187
+			'OR' => array(
188
+				'OBJ_type*notticket' => array('!=', 'Ticket'),
189
+				'OBJ_type*null' => array('IS_NULL'))
190
+		)));
191
+	}
192
+
193
+	/**
194
+	 * Deletes line items with no transaction who have passed the transaction cutoff time.
195
+	 * This needs to be very efficient
196
+	 * because if there are spam bots afoot there will be LOTS of line items
197
+	 * @return int count of how many deleted
198
+	 */
199
+	public function delete_line_items_with_no_transaction()
200
+	{
201
+		/** @type WPDB $wpdb */
202
+		global $wpdb;
203
+		$time_to_leave_alone = apply_filters(
204
+			'FHEE__EEM_Line_Item__delete_line_items_with_no_transaction__time_to_leave_alone', WEEK_IN_SECONDS
205
+		);
206
+		$query = $wpdb->prepare(
207
+			'DELETE li
208 208
 				FROM ' . $this->table() . ' li
209 209
 				LEFT JOIN ' . EEM_Transaction::instance()->table() . ' t ON li.TXN_ID = t.TXN_ID
210 210
 				WHERE t.TXN_ID IS NULL AND li.LIN_timestamp < %s',
211
-            // use GMT time because that's what TXN_timestamps are in
212
-            date('Y-m-d H:i:s', time() - $time_to_leave_alone)
213
-        );
214
-        return $wpdb->query($query);
215
-    }
216
-
217
-
218
-    /**
219
-     * get_line_item_for_transaction_object
220
-     * Gets a transaction's line item record for a specific object such as a EE_Event or EE_Ticket
221
-     *
222
-     * @param int $TXN_ID
223
-     * @param \EE_Base_Class $object
224
-     * @return EE_Line_Item[]
225
-     */
226
-    public function get_line_item_for_transaction_object($TXN_ID, EE_Base_Class $object)
227
-    {
228
-        return $this->get_all(array(array(
229
-            'TXN_ID' => $TXN_ID,
230
-            'OBJ_type' => str_replace('EE_', '', get_class($object)),
231
-            'OBJ_ID' => $object->ID()
232
-        )));
233
-    }
234
-
235
-
236
-    /**
237
-     * get_object_line_items_for_transaction
238
-     * Gets all of the the object line items for a transaction, based on an object type plus an array of object IDs
239
-     *
240
-     * @param int $TXN_ID
241
-     * @param string $OBJ_type
242
-     * @param array $OBJ_IDs
243
-     * @return EE_Line_Item[]
244
-     */
245
-    public function get_object_line_items_for_transaction($TXN_ID, $OBJ_type = 'Event', $OBJ_IDs = array())
246
-    {
247
-        $query_params = array(
248
-            'OBJ_type' => $OBJ_type,
249
-            // if incoming $OBJ_IDs is an array, then make sure it is formatted correctly for the query
250
-            'OBJ_ID' => is_array($OBJ_IDs) && !isset($OBJ_IDs['IN']) ? array('IN', $OBJ_IDs) : $OBJ_IDs
251
-        );
252
-        if ($TXN_ID) {
253
-            $query_params['TXN_ID'] = $TXN_ID;
254
-        }
255
-        return $this->get_all(array($query_params));
256
-    }
257
-
258
-
259
-    /**
260
-     * get_all_ticket_line_items_for_transaction
261
-     *
262
-     * @param EE_Transaction $transaction
263
-     * @return EE_Line_Item[]
264
-     */
265
-    public function get_all_ticket_line_items_for_transaction(EE_Transaction $transaction)
266
-    {
267
-        return $this->get_all(array(
268
-            array(
269
-                'TXN_ID' => $transaction->ID(),
270
-                'OBJ_type' => 'Ticket',
271
-            )
272
-        ));
273
-    }
274
-
275
-
276
-    /**
277
-     * get_ticket_line_item_for_transaction
278
-     *
279
-     * @param int $TXN_ID
280
-     * @param int $TKT_ID
281
-     * @return \EE_Line_Item
282
-     */
283
-    public function get_ticket_line_item_for_transaction($TXN_ID, $TKT_ID)
284
-    {
285
-        return $this->get_one(array(
286
-            array(
287
-                'TXN_ID' => EEM_Transaction::instance()->ensure_is_ID($TXN_ID),
288
-                'OBJ_ID' => $TKT_ID,
289
-                'OBJ_type' => 'Ticket',
290
-            )
291
-        ));
292
-    }
293
-
294
-
295
-    /**
296
-     * get_existing_promotion_line_item
297
-     * searches the cart for existing line items for the specified promotion
298
-     *
299
-     * @since   1.0.0
300
-     *
301
-     * @param EE_Line_Item $parent_line_item
302
-     * @param EE_Promotion $promotion
303
-     * @return EE_Line_Item
304
-     */
305
-    public function get_existing_promotion_line_item(EE_Line_Item $parent_line_item, EE_Promotion $promotion)
306
-    {
307
-        return $this->get_one(array(
308
-            array(
309
-                'TXN_ID' => $parent_line_item->TXN_ID(),
310
-                'LIN_parent' => $parent_line_item->ID(),
311
-                'OBJ_type' => 'Promotion',
312
-                'OBJ_ID' => $promotion->ID()
313
-            )
314
-        ));
315
-    }
316
-
317
-
318
-    /**
319
-     * get_all_promotion_line_items
320
-     * searches the cart for any and all existing promotion line items
321
-     *
322
-     * @since   1.0.0
323
-     *
324
-     * @param EE_Line_Item $parent_line_item
325
-     * @return EE_Line_Item[]
326
-     */
327
-    public function get_all_promotion_line_items(EE_Line_Item $parent_line_item)
328
-    {
329
-        return $this->get_all(array(
330
-            array(
331
-                'TXN_ID' => $parent_line_item->TXN_ID(),
332
-                'LIN_parent' => $parent_line_item->ID(),
333
-                'OBJ_type' => 'Promotion'
334
-            )
335
-        ));
336
-    }
337
-
338
-    /**
339
-     * Gets the registration's corresponding line item.
340
-     * Note: basically does NOT support having multiple line items for a single ticket,
341
-     * which would happen if some of the registrations had a price modifier while others didn't.
342
-     * In order to support that, we'd probably need a LIN_ID on registrations or something.
343
-     * @param EE_Registration $registration
344
-     * @return EE_Line_ITem
345
-     */
346
-    public function get_line_item_for_registration(EE_Registration $registration)
347
-    {
348
-        return $this->get_one($this->line_item_for_registration_query_params($registration));
349
-    }
350
-
351
-    /**
352
-     * Gets the query params used to retrieve a specific line item for the given registration
353
-     * @param EE_Registration $registration
354
-     * @param array $original_query_params any extra query params you'd like to be merged with
355
-     * @return array like EEM_Base::get_all()'s $query_params
356
-     */
357
-    public function line_item_for_registration_query_params(EE_Registration $registration, $original_query_params = array())
358
-    {
359
-        return array_replace_recursive($original_query_params, array(
360
-            array(
361
-                'OBJ_ID' => $registration->ticket_ID(),
362
-                'OBJ_type' => 'Ticket',
363
-                'TXN_ID' => $registration->transaction_ID()
364
-            )
365
-        ));
366
-    }
367
-
368
-
369
-    /**
370
-     * @return EE_Base_Class[]|EE_Line_Item[]
371
-     * @throws \EE_Error
372
-     */
373
-    public function get_total_line_items_with_no_transaction()
374
-    {
375
-        return $this->get_total_line_items_for_carts();
376
-    }
377
-
378
-
379
-    /**
380
-     * @return EE_Base_Class[]|EE_Line_Item[]
381
-     * @throws \EE_Error
382
-     */
383
-    public function get_total_line_items_for_active_carts()
384
-    {
385
-        return $this->get_total_line_items_for_carts(false);
386
-    }
387
-
388
-
389
-    /**
390
-     * @return EE_Base_Class[]|EE_Line_Item[]
391
-     * @throws \EE_Error
392
-     */
393
-    public function get_total_line_items_for_expired_carts()
394
-    {
395
-        return $this->get_total_line_items_for_carts(true);
396
-    }
397
-
398
-
399
-    /**
400
-     * Returns an array of grand total line items where the TXN_ID is 0.
401
-     * If $expired is set to true, then only line items for expired sessions will be returned.
402
-     * If $expired is set to false, then only line items for active sessions will be returned.
403
-     *
404
-     * @param bool|null $expired
405
-     * @return EE_Base_Class[]|EE_Line_Item[]
406
-     * @throws \EE_Error
407
-     */
408
-    private function get_total_line_items_for_carts($expired = null)
409
-    {
410
-        $where_params = array(
411
-            'TXN_ID' => 0,
412
-            'LIN_type' => 'total',
413
-        );
414
-        if ($expired !== null) {
415
-            $where_params['LIN_timestamp'] = array(
416
-                $expired ? '<=' : '>',
417
-                time() - EE_Registry::instance()->SSN->lifespan(),
418
-            );
419
-        }
420
-        return $this->get_all(array($where_params));
421
-    }
211
+			// use GMT time because that's what TXN_timestamps are in
212
+			date('Y-m-d H:i:s', time() - $time_to_leave_alone)
213
+		);
214
+		return $wpdb->query($query);
215
+	}
216
+
217
+
218
+	/**
219
+	 * get_line_item_for_transaction_object
220
+	 * Gets a transaction's line item record for a specific object such as a EE_Event or EE_Ticket
221
+	 *
222
+	 * @param int $TXN_ID
223
+	 * @param \EE_Base_Class $object
224
+	 * @return EE_Line_Item[]
225
+	 */
226
+	public function get_line_item_for_transaction_object($TXN_ID, EE_Base_Class $object)
227
+	{
228
+		return $this->get_all(array(array(
229
+			'TXN_ID' => $TXN_ID,
230
+			'OBJ_type' => str_replace('EE_', '', get_class($object)),
231
+			'OBJ_ID' => $object->ID()
232
+		)));
233
+	}
234
+
235
+
236
+	/**
237
+	 * get_object_line_items_for_transaction
238
+	 * Gets all of the the object line items for a transaction, based on an object type plus an array of object IDs
239
+	 *
240
+	 * @param int $TXN_ID
241
+	 * @param string $OBJ_type
242
+	 * @param array $OBJ_IDs
243
+	 * @return EE_Line_Item[]
244
+	 */
245
+	public function get_object_line_items_for_transaction($TXN_ID, $OBJ_type = 'Event', $OBJ_IDs = array())
246
+	{
247
+		$query_params = array(
248
+			'OBJ_type' => $OBJ_type,
249
+			// if incoming $OBJ_IDs is an array, then make sure it is formatted correctly for the query
250
+			'OBJ_ID' => is_array($OBJ_IDs) && !isset($OBJ_IDs['IN']) ? array('IN', $OBJ_IDs) : $OBJ_IDs
251
+		);
252
+		if ($TXN_ID) {
253
+			$query_params['TXN_ID'] = $TXN_ID;
254
+		}
255
+		return $this->get_all(array($query_params));
256
+	}
257
+
258
+
259
+	/**
260
+	 * get_all_ticket_line_items_for_transaction
261
+	 *
262
+	 * @param EE_Transaction $transaction
263
+	 * @return EE_Line_Item[]
264
+	 */
265
+	public function get_all_ticket_line_items_for_transaction(EE_Transaction $transaction)
266
+	{
267
+		return $this->get_all(array(
268
+			array(
269
+				'TXN_ID' => $transaction->ID(),
270
+				'OBJ_type' => 'Ticket',
271
+			)
272
+		));
273
+	}
274
+
275
+
276
+	/**
277
+	 * get_ticket_line_item_for_transaction
278
+	 *
279
+	 * @param int $TXN_ID
280
+	 * @param int $TKT_ID
281
+	 * @return \EE_Line_Item
282
+	 */
283
+	public function get_ticket_line_item_for_transaction($TXN_ID, $TKT_ID)
284
+	{
285
+		return $this->get_one(array(
286
+			array(
287
+				'TXN_ID' => EEM_Transaction::instance()->ensure_is_ID($TXN_ID),
288
+				'OBJ_ID' => $TKT_ID,
289
+				'OBJ_type' => 'Ticket',
290
+			)
291
+		));
292
+	}
293
+
294
+
295
+	/**
296
+	 * get_existing_promotion_line_item
297
+	 * searches the cart for existing line items for the specified promotion
298
+	 *
299
+	 * @since   1.0.0
300
+	 *
301
+	 * @param EE_Line_Item $parent_line_item
302
+	 * @param EE_Promotion $promotion
303
+	 * @return EE_Line_Item
304
+	 */
305
+	public function get_existing_promotion_line_item(EE_Line_Item $parent_line_item, EE_Promotion $promotion)
306
+	{
307
+		return $this->get_one(array(
308
+			array(
309
+				'TXN_ID' => $parent_line_item->TXN_ID(),
310
+				'LIN_parent' => $parent_line_item->ID(),
311
+				'OBJ_type' => 'Promotion',
312
+				'OBJ_ID' => $promotion->ID()
313
+			)
314
+		));
315
+	}
316
+
317
+
318
+	/**
319
+	 * get_all_promotion_line_items
320
+	 * searches the cart for any and all existing promotion line items
321
+	 *
322
+	 * @since   1.0.0
323
+	 *
324
+	 * @param EE_Line_Item $parent_line_item
325
+	 * @return EE_Line_Item[]
326
+	 */
327
+	public function get_all_promotion_line_items(EE_Line_Item $parent_line_item)
328
+	{
329
+		return $this->get_all(array(
330
+			array(
331
+				'TXN_ID' => $parent_line_item->TXN_ID(),
332
+				'LIN_parent' => $parent_line_item->ID(),
333
+				'OBJ_type' => 'Promotion'
334
+			)
335
+		));
336
+	}
337
+
338
+	/**
339
+	 * Gets the registration's corresponding line item.
340
+	 * Note: basically does NOT support having multiple line items for a single ticket,
341
+	 * which would happen if some of the registrations had a price modifier while others didn't.
342
+	 * In order to support that, we'd probably need a LIN_ID on registrations or something.
343
+	 * @param EE_Registration $registration
344
+	 * @return EE_Line_ITem
345
+	 */
346
+	public function get_line_item_for_registration(EE_Registration $registration)
347
+	{
348
+		return $this->get_one($this->line_item_for_registration_query_params($registration));
349
+	}
350
+
351
+	/**
352
+	 * Gets the query params used to retrieve a specific line item for the given registration
353
+	 * @param EE_Registration $registration
354
+	 * @param array $original_query_params any extra query params you'd like to be merged with
355
+	 * @return array like EEM_Base::get_all()'s $query_params
356
+	 */
357
+	public function line_item_for_registration_query_params(EE_Registration $registration, $original_query_params = array())
358
+	{
359
+		return array_replace_recursive($original_query_params, array(
360
+			array(
361
+				'OBJ_ID' => $registration->ticket_ID(),
362
+				'OBJ_type' => 'Ticket',
363
+				'TXN_ID' => $registration->transaction_ID()
364
+			)
365
+		));
366
+	}
367
+
368
+
369
+	/**
370
+	 * @return EE_Base_Class[]|EE_Line_Item[]
371
+	 * @throws \EE_Error
372
+	 */
373
+	public function get_total_line_items_with_no_transaction()
374
+	{
375
+		return $this->get_total_line_items_for_carts();
376
+	}
377
+
378
+
379
+	/**
380
+	 * @return EE_Base_Class[]|EE_Line_Item[]
381
+	 * @throws \EE_Error
382
+	 */
383
+	public function get_total_line_items_for_active_carts()
384
+	{
385
+		return $this->get_total_line_items_for_carts(false);
386
+	}
387
+
388
+
389
+	/**
390
+	 * @return EE_Base_Class[]|EE_Line_Item[]
391
+	 * @throws \EE_Error
392
+	 */
393
+	public function get_total_line_items_for_expired_carts()
394
+	{
395
+		return $this->get_total_line_items_for_carts(true);
396
+	}
397
+
398
+
399
+	/**
400
+	 * Returns an array of grand total line items where the TXN_ID is 0.
401
+	 * If $expired is set to true, then only line items for expired sessions will be returned.
402
+	 * If $expired is set to false, then only line items for active sessions will be returned.
403
+	 *
404
+	 * @param bool|null $expired
405
+	 * @return EE_Base_Class[]|EE_Line_Item[]
406
+	 * @throws \EE_Error
407
+	 */
408
+	private function get_total_line_items_for_carts($expired = null)
409
+	{
410
+		$where_params = array(
411
+			'TXN_ID' => 0,
412
+			'LIN_type' => 'total',
413
+		);
414
+		if ($expired !== null) {
415
+			$where_params['LIN_timestamp'] = array(
416
+				$expired ? '<=' : '>',
417
+				time() - EE_Registry::instance()->SSN->lifespan(),
418
+			);
419
+		}
420
+		return $this->get_all(array($where_params));
421
+	}
422 422
 
423 423
 
424 424
 }
425 425
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@  discard block
 block discarded – undo
1
-<?php if (!defined('EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed');
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed');
2 2
 
3 3
 /**
4 4
  * Event Espresso
@@ -205,8 +205,8 @@  discard block
 block discarded – undo
205 205
         );
206 206
         $query = $wpdb->prepare(
207 207
             'DELETE li
208
-				FROM ' . $this->table() . ' li
209
-				LEFT JOIN ' . EEM_Transaction::instance()->table() . ' t ON li.TXN_ID = t.TXN_ID
208
+				FROM ' . $this->table().' li
209
+				LEFT JOIN ' . EEM_Transaction::instance()->table().' t ON li.TXN_ID = t.TXN_ID
210 210
 				WHERE t.TXN_ID IS NULL AND li.LIN_timestamp < %s',
211 211
             // use GMT time because that's what TXN_timestamps are in
212 212
             date('Y-m-d H:i:s', time() - $time_to_leave_alone)
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
         $query_params = array(
248 248
             'OBJ_type' => $OBJ_type,
249 249
             // if incoming $OBJ_IDs is an array, then make sure it is formatted correctly for the query
250
-            'OBJ_ID' => is_array($OBJ_IDs) && !isset($OBJ_IDs['IN']) ? array('IN', $OBJ_IDs) : $OBJ_IDs
250
+            'OBJ_ID' => is_array($OBJ_IDs) && ! isset($OBJ_IDs['IN']) ? array('IN', $OBJ_IDs) : $OBJ_IDs
251 251
         );
252 252
         if ($TXN_ID) {
253 253
             $query_params['TXN_ID'] = $TXN_ID;
Please login to merge, or discard this patch.
Braces   +3 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1,4 +1,6 @@
 block discarded – undo
1
-<?php if (!defined('EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed');
1
+<?php if (!defined('EVENT_ESPRESSO_VERSION')) {
2
+	exit('No direct script access allowed');
3
+}
2 4
 
3 5
 /**
4 6
  * Event Espresso
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +191 added lines, -191 removed lines patch added patch discarded remove patch
@@ -38,216 +38,216 @@
 block discarded – undo
38 38
  * @since       4.0
39 39
  */
40 40
 if (function_exists('espresso_version')) {
41
-    if (! function_exists('espresso_duplicate_plugin_error')) {
42
-        /**
43
-         *    espresso_duplicate_plugin_error
44
-         *    displays if more than one version of EE is activated at the same time
45
-         */
46
-        function espresso_duplicate_plugin_error()
47
-        {
48
-            ?>
41
+	if (! function_exists('espresso_duplicate_plugin_error')) {
42
+		/**
43
+		 *    espresso_duplicate_plugin_error
44
+		 *    displays if more than one version of EE is activated at the same time
45
+		 */
46
+		function espresso_duplicate_plugin_error()
47
+		{
48
+			?>
49 49
             <div class="error">
50 50
                 <p>
51 51
                     <?php
52
-                    echo esc_html__(
53
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
-                        'event_espresso'
55
-                    ); ?>
52
+					echo esc_html__(
53
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
+						'event_espresso'
55
+					); ?>
56 56
                 </p>
57 57
             </div>
58 58
             <?php
59
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
60
-        }
61
-    }
62
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
59
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
60
+		}
61
+	}
62
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
63 63
 
64 64
 } else {
65
-    define('EE_MIN_PHP_VER_REQUIRED', '5.3.9');
66
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
67
-        /**
68
-         * espresso_minimum_php_version_error
69
-         * @return void
70
-         */
71
-        function espresso_minimum_php_version_error()
72
-        {
73
-            ?>
65
+	define('EE_MIN_PHP_VER_REQUIRED', '5.3.9');
66
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
67
+		/**
68
+		 * espresso_minimum_php_version_error
69
+		 * @return void
70
+		 */
71
+		function espresso_minimum_php_version_error()
72
+		{
73
+			?>
74 74
             <div class="error">
75 75
                 <p>
76 76
                     <?php
77
-                    printf(
78
-                        esc_html__(
79
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
-                            'event_espresso'
81
-                        ),
82
-                        EE_MIN_PHP_VER_REQUIRED,
83
-                        PHP_VERSION,
84
-                        '<br/>',
85
-                        '<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
-                    );
87
-                    ?>
77
+					printf(
78
+						esc_html__(
79
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
+							'event_espresso'
81
+						),
82
+						EE_MIN_PHP_VER_REQUIRED,
83
+						PHP_VERSION,
84
+						'<br/>',
85
+						'<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
+					);
87
+					?>
88 88
                 </p>
89 89
             </div>
90 90
             <?php
91
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
92
-        }
91
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
92
+		}
93 93
 
94
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
-    } else {
96
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
-        /**
98
-         * espresso_version
99
-         * Returns the plugin version
100
-         *
101
-         * @return string
102
-         */
103
-        function espresso_version()
104
-        {
105
-            return apply_filters('FHEE__espresso__espresso_version', '4.9.54.rc.030');
106
-        }
94
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
+	} else {
96
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
+		/**
98
+		 * espresso_version
99
+		 * Returns the plugin version
100
+		 *
101
+		 * @return string
102
+		 */
103
+		function espresso_version()
104
+		{
105
+			return apply_filters('FHEE__espresso__espresso_version', '4.9.54.rc.030');
106
+		}
107 107
 
108
-        /**
109
-         * espresso_plugin_activation
110
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
-         */
112
-        function espresso_plugin_activation()
113
-        {
114
-            update_option('ee_espresso_activation', true);
115
-        }
108
+		/**
109
+		 * espresso_plugin_activation
110
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
+		 */
112
+		function espresso_plugin_activation()
113
+		{
114
+			update_option('ee_espresso_activation', true);
115
+		}
116 116
 
117
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
118
-        /**
119
-         *    espresso_load_error_handling
120
-         *    this function loads EE's class for handling exceptions and errors
121
-         */
122
-        function espresso_load_error_handling()
123
-        {
124
-            static $error_handling_loaded = false;
125
-            if ($error_handling_loaded) {
126
-                return;
127
-            }
128
-            // load debugging tools
129
-            if (WP_DEBUG === true && is_readable(EE_HELPERS . 'EEH_Debug_Tools.helper.php')) {
130
-                require_once   EE_HELPERS . 'EEH_Debug_Tools.helper.php';
131
-                \EEH_Debug_Tools::instance();
132
-            }
133
-            // load error handling
134
-            if (is_readable(EE_CORE . 'EE_Error.core.php')) {
135
-                require_once EE_CORE . 'EE_Error.core.php';
136
-            } else {
137
-                wp_die(esc_html__('The EE_Error core class could not be loaded.', 'event_espresso'));
138
-            }
139
-            $error_handling_loaded = true;
140
-        }
117
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
118
+		/**
119
+		 *    espresso_load_error_handling
120
+		 *    this function loads EE's class for handling exceptions and errors
121
+		 */
122
+		function espresso_load_error_handling()
123
+		{
124
+			static $error_handling_loaded = false;
125
+			if ($error_handling_loaded) {
126
+				return;
127
+			}
128
+			// load debugging tools
129
+			if (WP_DEBUG === true && is_readable(EE_HELPERS . 'EEH_Debug_Tools.helper.php')) {
130
+				require_once   EE_HELPERS . 'EEH_Debug_Tools.helper.php';
131
+				\EEH_Debug_Tools::instance();
132
+			}
133
+			// load error handling
134
+			if (is_readable(EE_CORE . 'EE_Error.core.php')) {
135
+				require_once EE_CORE . 'EE_Error.core.php';
136
+			} else {
137
+				wp_die(esc_html__('The EE_Error core class could not be loaded.', 'event_espresso'));
138
+			}
139
+			$error_handling_loaded = true;
140
+		}
141 141
 
142
-        /**
143
-         *    espresso_load_required
144
-         *    given a class name and path, this function will load that file or throw an exception
145
-         *
146
-         * @param    string $classname
147
-         * @param    string $full_path_to_file
148
-         * @throws    EE_Error
149
-         */
150
-        function espresso_load_required($classname, $full_path_to_file)
151
-        {
152
-            if (is_readable($full_path_to_file)) {
153
-                require_once $full_path_to_file;
154
-            } else {
155
-                throw new \EE_Error (
156
-                    sprintf(
157
-                        esc_html__(
158
-                            'The %s class file could not be located or is not readable due to file permissions.',
159
-                            'event_espresso'
160
-                        ),
161
-                        $classname
162
-                    )
163
-                );
164
-            }
165
-        }
142
+		/**
143
+		 *    espresso_load_required
144
+		 *    given a class name and path, this function will load that file or throw an exception
145
+		 *
146
+		 * @param    string $classname
147
+		 * @param    string $full_path_to_file
148
+		 * @throws    EE_Error
149
+		 */
150
+		function espresso_load_required($classname, $full_path_to_file)
151
+		{
152
+			if (is_readable($full_path_to_file)) {
153
+				require_once $full_path_to_file;
154
+			} else {
155
+				throw new \EE_Error (
156
+					sprintf(
157
+						esc_html__(
158
+							'The %s class file could not be located or is not readable due to file permissions.',
159
+							'event_espresso'
160
+						),
161
+						$classname
162
+					)
163
+				);
164
+			}
165
+		}
166 166
 
167
-        /**
168
-         * @since 4.9.27
169
-         * @throws \EE_Error
170
-         * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
171
-         * @throws \EventEspresso\core\exceptions\InvalidEntityException
172
-         * @throws \EventEspresso\core\exceptions\InvalidIdentifierException
173
-         * @throws \EventEspresso\core\exceptions\InvalidClassException
174
-         * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
175
-         * @throws \EventEspresso\core\services\container\exceptions\ServiceExistsException
176
-         * @throws \EventEspresso\core\services\container\exceptions\ServiceNotFoundException
177
-         * @throws \OutOfBoundsException
178
-         */
179
-        function bootstrap_espresso()
180
-        {
181
-            require_once __DIR__ . '/core/espresso_definitions.php';
182
-            try {
183
-                espresso_load_error_handling();
184
-                espresso_load_required(
185
-                    'EEH_Base',
186
-                    EE_CORE . 'helpers' . DS . 'EEH_Base.helper.php'
187
-                );
188
-                espresso_load_required(
189
-                    'EEH_File',
190
-                    EE_CORE . 'interfaces' . DS . 'EEHI_File.interface.php'
191
-                );
192
-                espresso_load_required(
193
-                    'EEH_File',
194
-                    EE_CORE . 'helpers' . DS . 'EEH_File.helper.php'
195
-                );
196
-                espresso_load_required(
197
-                    'EEH_Array',
198
-                    EE_CORE . 'helpers' . DS . 'EEH_Array.helper.php'
199
-                );
200
-                // instantiate and configure PSR4 autoloader
201
-                espresso_load_required(
202
-                    'Psr4Autoloader',
203
-                    EE_CORE . 'Psr4Autoloader.php'
204
-                );
205
-                espresso_load_required(
206
-                    'EE_Psr4AutoloaderInit',
207
-                    EE_CORE . 'EE_Psr4AutoloaderInit.core.php'
208
-                );
209
-                $AutoloaderInit = new EE_Psr4AutoloaderInit();
210
-                $AutoloaderInit->initializeAutoloader();
211
-                espresso_load_required(
212
-                    'EE_Request',
213
-                    EE_CORE . 'request_stack' . DS . 'EE_Request.core.php'
214
-                );
215
-                espresso_load_required(
216
-                    'EE_Response',
217
-                    EE_CORE . 'request_stack' . DS . 'EE_Response.core.php'
218
-                );
219
-                espresso_load_required(
220
-                    'EE_Bootstrap',
221
-                    EE_CORE . 'EE_Bootstrap.core.php'
222
-                );
223
-                // bootstrap EE and the request stack
224
-                new EE_Bootstrap(
225
-                    new EE_Request($_GET, $_POST, $_COOKIE),
226
-                    new EE_Response()
227
-                );
228
-            } catch (Exception $e) {
229
-                require_once EE_CORE . 'exceptions' . DS . 'ExceptionStackTraceDisplay.php';
230
-                new EventEspresso\core\exceptions\ExceptionStackTraceDisplay($e);
231
-            }
232
-        }
233
-        bootstrap_espresso();
234
-    }
167
+		/**
168
+		 * @since 4.9.27
169
+		 * @throws \EE_Error
170
+		 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
171
+		 * @throws \EventEspresso\core\exceptions\InvalidEntityException
172
+		 * @throws \EventEspresso\core\exceptions\InvalidIdentifierException
173
+		 * @throws \EventEspresso\core\exceptions\InvalidClassException
174
+		 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
175
+		 * @throws \EventEspresso\core\services\container\exceptions\ServiceExistsException
176
+		 * @throws \EventEspresso\core\services\container\exceptions\ServiceNotFoundException
177
+		 * @throws \OutOfBoundsException
178
+		 */
179
+		function bootstrap_espresso()
180
+		{
181
+			require_once __DIR__ . '/core/espresso_definitions.php';
182
+			try {
183
+				espresso_load_error_handling();
184
+				espresso_load_required(
185
+					'EEH_Base',
186
+					EE_CORE . 'helpers' . DS . 'EEH_Base.helper.php'
187
+				);
188
+				espresso_load_required(
189
+					'EEH_File',
190
+					EE_CORE . 'interfaces' . DS . 'EEHI_File.interface.php'
191
+				);
192
+				espresso_load_required(
193
+					'EEH_File',
194
+					EE_CORE . 'helpers' . DS . 'EEH_File.helper.php'
195
+				);
196
+				espresso_load_required(
197
+					'EEH_Array',
198
+					EE_CORE . 'helpers' . DS . 'EEH_Array.helper.php'
199
+				);
200
+				// instantiate and configure PSR4 autoloader
201
+				espresso_load_required(
202
+					'Psr4Autoloader',
203
+					EE_CORE . 'Psr4Autoloader.php'
204
+				);
205
+				espresso_load_required(
206
+					'EE_Psr4AutoloaderInit',
207
+					EE_CORE . 'EE_Psr4AutoloaderInit.core.php'
208
+				);
209
+				$AutoloaderInit = new EE_Psr4AutoloaderInit();
210
+				$AutoloaderInit->initializeAutoloader();
211
+				espresso_load_required(
212
+					'EE_Request',
213
+					EE_CORE . 'request_stack' . DS . 'EE_Request.core.php'
214
+				);
215
+				espresso_load_required(
216
+					'EE_Response',
217
+					EE_CORE . 'request_stack' . DS . 'EE_Response.core.php'
218
+				);
219
+				espresso_load_required(
220
+					'EE_Bootstrap',
221
+					EE_CORE . 'EE_Bootstrap.core.php'
222
+				);
223
+				// bootstrap EE and the request stack
224
+				new EE_Bootstrap(
225
+					new EE_Request($_GET, $_POST, $_COOKIE),
226
+					new EE_Response()
227
+				);
228
+			} catch (Exception $e) {
229
+				require_once EE_CORE . 'exceptions' . DS . 'ExceptionStackTraceDisplay.php';
230
+				new EventEspresso\core\exceptions\ExceptionStackTraceDisplay($e);
231
+			}
232
+		}
233
+		bootstrap_espresso();
234
+	}
235 235
 }
236 236
 if (! function_exists('espresso_deactivate_plugin')) {
237
-    /**
238
-     *    deactivate_plugin
239
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
240
-     *
241
-     * @access public
242
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
243
-     * @return    void
244
-     */
245
-    function espresso_deactivate_plugin($plugin_basename = '')
246
-    {
247
-        if (! function_exists('deactivate_plugins')) {
248
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
249
-        }
250
-        unset($_GET['activate'], $_REQUEST['activate']);
251
-        deactivate_plugins($plugin_basename);
252
-    }
237
+	/**
238
+	 *    deactivate_plugin
239
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
240
+	 *
241
+	 * @access public
242
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
243
+	 * @return    void
244
+	 */
245
+	function espresso_deactivate_plugin($plugin_basename = '')
246
+	{
247
+		if (! function_exists('deactivate_plugins')) {
248
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
249
+		}
250
+		unset($_GET['activate'], $_REQUEST['activate']);
251
+		deactivate_plugins($plugin_basename);
252
+	}
253 253
 }
Please login to merge, or discard this patch.