Completed
Branch BUG/tax-calculation (77d43f)
by
unknown
17:22 queued 09:03
created
core/db_classes/EE_Line_Item.class.php 1 patch
Indentation   +1738 added lines, -1738 removed lines patch added patch discarded remove patch
@@ -14,1742 +14,1742 @@
 block discarded – undo
14 14
 class EE_Line_Item extends EE_Base_Class implements EEI_Line_Item
15 15
 {
16 16
 
17
-    /**
18
-     * for children line items (currently not a normal relation)
19
-     *
20
-     * @type EE_Line_Item[]
21
-     */
22
-    protected $_children = array();
23
-
24
-    /**
25
-     * for the parent line item
26
-     *
27
-     * @var EE_Line_Item
28
-     */
29
-    protected $_parent;
30
-
31
-
32
-    /**
33
-     * @param array  $props_n_values          incoming values
34
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
35
-     *                                        used.)
36
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
37
-     *                                        date_format and the second value is the time format
38
-     * @return EE_Line_Item
39
-     * @throws EE_Error
40
-     * @throws InvalidArgumentException
41
-     * @throws InvalidDataTypeException
42
-     * @throws InvalidInterfaceException
43
-     * @throws ReflectionException
44
-     */
45
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
46
-    {
47
-        $has_object = parent::_check_for_object(
48
-            $props_n_values,
49
-            __CLASS__,
50
-            $timezone,
51
-            $date_formats
52
-        );
53
-        return $has_object
54
-            ? $has_object
55
-            : new self($props_n_values, false, $timezone);
56
-    }
57
-
58
-
59
-    /**
60
-     * @param array  $props_n_values  incoming values from the database
61
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
62
-     *                                the website will be used.
63
-     * @return EE_Line_Item
64
-     * @throws EE_Error
65
-     * @throws InvalidArgumentException
66
-     * @throws InvalidDataTypeException
67
-     * @throws InvalidInterfaceException
68
-     * @throws ReflectionException
69
-     */
70
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
71
-    {
72
-        return new self($props_n_values, true, $timezone);
73
-    }
74
-
75
-
76
-    /**
77
-     * Adds some defaults if they're not specified
78
-     *
79
-     * @param array  $fieldValues
80
-     * @param bool   $bydb
81
-     * @param string $timezone
82
-     * @throws EE_Error
83
-     * @throws InvalidArgumentException
84
-     * @throws InvalidDataTypeException
85
-     * @throws InvalidInterfaceException
86
-     * @throws ReflectionException
87
-     */
88
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
89
-    {
90
-        parent::__construct($fieldValues, $bydb, $timezone);
91
-        if (! $this->get('LIN_code')) {
92
-            $this->set_code($this->generate_code());
93
-        }
94
-    }
95
-
96
-
97
-    /**
98
-     * Gets ID
99
-     *
100
-     * @return int
101
-     * @throws EE_Error
102
-     * @throws InvalidArgumentException
103
-     * @throws InvalidDataTypeException
104
-     * @throws InvalidInterfaceException
105
-     * @throws ReflectionException
106
-     */
107
-    public function ID()
108
-    {
109
-        return $this->get('LIN_ID');
110
-    }
111
-
112
-
113
-    /**
114
-     * Gets TXN_ID
115
-     *
116
-     * @return int
117
-     * @throws EE_Error
118
-     * @throws InvalidArgumentException
119
-     * @throws InvalidDataTypeException
120
-     * @throws InvalidInterfaceException
121
-     * @throws ReflectionException
122
-     */
123
-    public function TXN_ID()
124
-    {
125
-        return $this->get('TXN_ID');
126
-    }
127
-
128
-
129
-    /**
130
-     * Sets TXN_ID
131
-     *
132
-     * @param int $TXN_ID
133
-     * @throws EE_Error
134
-     * @throws InvalidArgumentException
135
-     * @throws InvalidDataTypeException
136
-     * @throws InvalidInterfaceException
137
-     * @throws ReflectionException
138
-     */
139
-    public function set_TXN_ID($TXN_ID)
140
-    {
141
-        $this->set('TXN_ID', $TXN_ID);
142
-    }
143
-
144
-
145
-    /**
146
-     * Gets name
147
-     *
148
-     * @return string
149
-     * @throws EE_Error
150
-     * @throws InvalidArgumentException
151
-     * @throws InvalidDataTypeException
152
-     * @throws InvalidInterfaceException
153
-     * @throws ReflectionException
154
-     */
155
-    public function name()
156
-    {
157
-        $name = $this->get('LIN_name');
158
-        if (! $name) {
159
-            $name = ucwords(str_replace('-', ' ', $this->type()));
160
-        }
161
-        return $name;
162
-    }
163
-
164
-
165
-    /**
166
-     * Sets name
167
-     *
168
-     * @param string $name
169
-     * @throws EE_Error
170
-     * @throws InvalidArgumentException
171
-     * @throws InvalidDataTypeException
172
-     * @throws InvalidInterfaceException
173
-     * @throws ReflectionException
174
-     */
175
-    public function set_name($name)
176
-    {
177
-        $this->set('LIN_name', $name);
178
-    }
179
-
180
-
181
-    /**
182
-     * Gets desc
183
-     *
184
-     * @return string
185
-     * @throws EE_Error
186
-     * @throws InvalidArgumentException
187
-     * @throws InvalidDataTypeException
188
-     * @throws InvalidInterfaceException
189
-     * @throws ReflectionException
190
-     */
191
-    public function desc()
192
-    {
193
-        return $this->get('LIN_desc');
194
-    }
195
-
196
-
197
-    /**
198
-     * Sets desc
199
-     *
200
-     * @param string $desc
201
-     * @throws EE_Error
202
-     * @throws InvalidArgumentException
203
-     * @throws InvalidDataTypeException
204
-     * @throws InvalidInterfaceException
205
-     * @throws ReflectionException
206
-     */
207
-    public function set_desc($desc)
208
-    {
209
-        $this->set('LIN_desc', $desc);
210
-    }
211
-
212
-
213
-    /**
214
-     * Gets quantity
215
-     *
216
-     * @return int
217
-     * @throws EE_Error
218
-     * @throws InvalidArgumentException
219
-     * @throws InvalidDataTypeException
220
-     * @throws InvalidInterfaceException
221
-     * @throws ReflectionException
222
-     */
223
-    public function quantity()
224
-    {
225
-        return $this->get('LIN_quantity');
226
-    }
227
-
228
-
229
-    /**
230
-     * Sets quantity
231
-     *
232
-     * @param int $quantity
233
-     * @throws EE_Error
234
-     * @throws InvalidArgumentException
235
-     * @throws InvalidDataTypeException
236
-     * @throws InvalidInterfaceException
237
-     * @throws ReflectionException
238
-     */
239
-    public function set_quantity($quantity)
240
-    {
241
-        $this->set('LIN_quantity', max($quantity, 0));
242
-    }
243
-
244
-
245
-    /**
246
-     * Gets item_id
247
-     *
248
-     * @return string
249
-     * @throws EE_Error
250
-     * @throws InvalidArgumentException
251
-     * @throws InvalidDataTypeException
252
-     * @throws InvalidInterfaceException
253
-     * @throws ReflectionException
254
-     */
255
-    public function OBJ_ID()
256
-    {
257
-        return $this->get('OBJ_ID');
258
-    }
259
-
260
-
261
-    /**
262
-     * Sets item_id
263
-     *
264
-     * @param string $item_id
265
-     * @throws EE_Error
266
-     * @throws InvalidArgumentException
267
-     * @throws InvalidDataTypeException
268
-     * @throws InvalidInterfaceException
269
-     * @throws ReflectionException
270
-     */
271
-    public function set_OBJ_ID($item_id)
272
-    {
273
-        $this->set('OBJ_ID', $item_id);
274
-    }
275
-
276
-
277
-    /**
278
-     * Gets item_type
279
-     *
280
-     * @return string
281
-     * @throws EE_Error
282
-     * @throws InvalidArgumentException
283
-     * @throws InvalidDataTypeException
284
-     * @throws InvalidInterfaceException
285
-     * @throws ReflectionException
286
-     */
287
-    public function OBJ_type()
288
-    {
289
-        return $this->get('OBJ_type');
290
-    }
291
-
292
-
293
-    /**
294
-     * Gets item_type
295
-     *
296
-     * @return string
297
-     * @throws EE_Error
298
-     * @throws InvalidArgumentException
299
-     * @throws InvalidDataTypeException
300
-     * @throws InvalidInterfaceException
301
-     * @throws ReflectionException
302
-     */
303
-    public function OBJ_type_i18n()
304
-    {
305
-        $obj_type = $this->OBJ_type();
306
-        switch ($obj_type) {
307
-            case EEM_Line_Item::OBJ_TYPE_EVENT:
308
-                $obj_type = esc_html__('Event', 'event_espresso');
309
-                break;
310
-            case EEM_Line_Item::OBJ_TYPE_PRICE:
311
-                $obj_type = esc_html__('Price', 'event_espresso');
312
-                break;
313
-            case EEM_Line_Item::OBJ_TYPE_PROMOTION:
314
-                $obj_type = esc_html__('Promotion', 'event_espresso');
315
-                break;
316
-            case EEM_Line_Item::OBJ_TYPE_TICKET:
317
-                $obj_type = esc_html__('Ticket', 'event_espresso');
318
-                break;
319
-            case EEM_Line_Item::OBJ_TYPE_TRANSACTION:
320
-                $obj_type = esc_html__('Transaction', 'event_espresso');
321
-                break;
322
-        }
323
-        return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
324
-    }
325
-
326
-
327
-    /**
328
-     * Sets item_type
329
-     *
330
-     * @param string $OBJ_type
331
-     * @throws EE_Error
332
-     * @throws InvalidArgumentException
333
-     * @throws InvalidDataTypeException
334
-     * @throws InvalidInterfaceException
335
-     * @throws ReflectionException
336
-     */
337
-    public function set_OBJ_type($OBJ_type)
338
-    {
339
-        $this->set('OBJ_type', $OBJ_type);
340
-    }
341
-
342
-
343
-    /**
344
-     * Gets unit_price
345
-     *
346
-     * @return float
347
-     * @throws EE_Error
348
-     * @throws InvalidArgumentException
349
-     * @throws InvalidDataTypeException
350
-     * @throws InvalidInterfaceException
351
-     * @throws ReflectionException
352
-     */
353
-    public function unit_price()
354
-    {
355
-        return $this->get('LIN_unit_price');
356
-    }
357
-
358
-
359
-    /**
360
-     * Sets unit_price
361
-     *
362
-     * @param float $unit_price
363
-     * @throws EE_Error
364
-     * @throws InvalidArgumentException
365
-     * @throws InvalidDataTypeException
366
-     * @throws InvalidInterfaceException
367
-     * @throws ReflectionException
368
-     */
369
-    public function set_unit_price($unit_price)
370
-    {
371
-        $this->set('LIN_unit_price', $unit_price);
372
-    }
373
-
374
-
375
-    /**
376
-     * Checks if this item is a percentage modifier or not
377
-     *
378
-     * @return boolean
379
-     * @throws EE_Error
380
-     * @throws InvalidArgumentException
381
-     * @throws InvalidDataTypeException
382
-     * @throws InvalidInterfaceException
383
-     * @throws ReflectionException
384
-     */
385
-    public function is_percent()
386
-    {
387
-        if ($this->is_tax_sub_total()) {
388
-            // tax subtotals HAVE a percent on them, that percentage only applies
389
-            // to taxable items, so its' an exception. Treat it like a flat line item
390
-            return false;
391
-        }
392
-        $unit_price = abs($this->get('LIN_unit_price'));
393
-        $percent = abs($this->get('LIN_percent'));
394
-        if ($unit_price < .001 && $percent) {
395
-            return true;
396
-        }
397
-        if ($unit_price >= .001 && ! $percent) {
398
-            return false;
399
-        }
400
-        if ($unit_price >= .001 && $percent) {
401
-            throw new EE_Error(
402
-                sprintf(
403
-                    esc_html__(
404
-                        'A Line Item can not have a unit price of (%s) AND a percent (%s)!',
405
-                        'event_espresso'
406
-                    ),
407
-                    $unit_price,
408
-                    $percent
409
-                )
410
-            );
411
-        }
412
-        // if they're both 0, assume its not a percent item
413
-        return false;
414
-    }
415
-
416
-
417
-    /**
418
-     * Gets percent (between 100-.001)
419
-     *
420
-     * @return float
421
-     * @throws EE_Error
422
-     * @throws InvalidArgumentException
423
-     * @throws InvalidDataTypeException
424
-     * @throws InvalidInterfaceException
425
-     * @throws ReflectionException
426
-     */
427
-    public function percent()
428
-    {
429
-        return $this->get('LIN_percent');
430
-    }
431
-
432
-
433
-    /**
434
-     * Sets percent (between 100-0.01)
435
-     *
436
-     * @param float $percent
437
-     * @throws EE_Error
438
-     * @throws InvalidArgumentException
439
-     * @throws InvalidDataTypeException
440
-     * @throws InvalidInterfaceException
441
-     * @throws ReflectionException
442
-     */
443
-    public function set_percent($percent)
444
-    {
445
-        $this->set('LIN_percent', $percent);
446
-    }
447
-
448
-
449
-    /**
450
-     * Gets total
451
-     *
452
-     * @return float
453
-     * @throws EE_Error
454
-     * @throws InvalidArgumentException
455
-     * @throws InvalidDataTypeException
456
-     * @throws InvalidInterfaceException
457
-     * @throws ReflectionException
458
-     */
459
-    public function total()
460
-    {
461
-        return $this->get('LIN_total');
462
-    }
463
-
464
-
465
-    /**
466
-     * Sets total
467
-     *
468
-     * @param float $total
469
-     * @throws EE_Error
470
-     * @throws InvalidArgumentException
471
-     * @throws InvalidDataTypeException
472
-     * @throws InvalidInterfaceException
473
-     * @throws ReflectionException
474
-     */
475
-    public function set_total($total)
476
-    {
477
-        $this->set('LIN_total', $total);
478
-    }
479
-
480
-
481
-    /**
482
-     * Gets order
483
-     *
484
-     * @return int
485
-     * @throws EE_Error
486
-     * @throws InvalidArgumentException
487
-     * @throws InvalidDataTypeException
488
-     * @throws InvalidInterfaceException
489
-     * @throws ReflectionException
490
-     */
491
-    public function order()
492
-    {
493
-        return $this->get('LIN_order');
494
-    }
495
-
496
-
497
-    /**
498
-     * Sets order
499
-     *
500
-     * @param int $order
501
-     * @throws EE_Error
502
-     * @throws InvalidArgumentException
503
-     * @throws InvalidDataTypeException
504
-     * @throws InvalidInterfaceException
505
-     * @throws ReflectionException
506
-     */
507
-    public function set_order($order)
508
-    {
509
-        $this->set('LIN_order', $order);
510
-    }
511
-
512
-
513
-    /**
514
-     * Gets parent
515
-     *
516
-     * @return int
517
-     * @throws EE_Error
518
-     * @throws InvalidArgumentException
519
-     * @throws InvalidDataTypeException
520
-     * @throws InvalidInterfaceException
521
-     * @throws ReflectionException
522
-     */
523
-    public function parent_ID()
524
-    {
525
-        return $this->get('LIN_parent');
526
-    }
527
-
528
-
529
-    /**
530
-     * Sets parent
531
-     *
532
-     * @param int $parent
533
-     * @throws EE_Error
534
-     * @throws InvalidArgumentException
535
-     * @throws InvalidDataTypeException
536
-     * @throws InvalidInterfaceException
537
-     * @throws ReflectionException
538
-     */
539
-    public function set_parent_ID($parent)
540
-    {
541
-        $this->set('LIN_parent', $parent);
542
-    }
543
-
544
-
545
-    /**
546
-     * Gets type
547
-     *
548
-     * @return string
549
-     * @throws EE_Error
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidDataTypeException
552
-     * @throws InvalidInterfaceException
553
-     * @throws ReflectionException
554
-     */
555
-    public function type()
556
-    {
557
-        return $this->get('LIN_type');
558
-    }
559
-
560
-
561
-    /**
562
-     * Sets type
563
-     *
564
-     * @param string $type
565
-     * @throws EE_Error
566
-     * @throws InvalidArgumentException
567
-     * @throws InvalidDataTypeException
568
-     * @throws InvalidInterfaceException
569
-     * @throws ReflectionException
570
-     */
571
-    public function set_type($type)
572
-    {
573
-        $this->set('LIN_type', $type);
574
-    }
575
-
576
-
577
-    /**
578
-     * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
579
-     * 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
580
-     * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
581
-     * or indirectly by `EE_Line_item::add_child_line_item()`)
582
-     *
583
-     * @return EE_Base_Class|EE_Line_Item
584
-     * @throws EE_Error
585
-     * @throws InvalidArgumentException
586
-     * @throws InvalidDataTypeException
587
-     * @throws InvalidInterfaceException
588
-     * @throws ReflectionException
589
-     */
590
-    public function parent()
591
-    {
592
-        return $this->ID()
593
-            ? $this->get_model()->get_one_by_ID($this->parent_ID())
594
-            : $this->_parent;
595
-    }
596
-
597
-
598
-    /**
599
-     * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
600
-     *
601
-     * @return EE_Base_Class[]|EE_Line_Item[]
602
-     * @throws EE_Error
603
-     * @throws InvalidArgumentException
604
-     * @throws InvalidDataTypeException
605
-     * @throws InvalidInterfaceException
606
-     * @throws ReflectionException
607
-     */
608
-    public function children()
609
-    {
610
-        if ($this->ID()) {
611
-            return $this->get_model()->get_all(
612
-                array(
613
-                    array('LIN_parent' => $this->ID()),
614
-                    'order_by' => array('LIN_order' => 'ASC'),
615
-                )
616
-            );
617
-        }
618
-        if (! is_array($this->_children)) {
619
-            $this->_children = array();
620
-        }
621
-        return $this->_children;
622
-    }
623
-
624
-
625
-    /**
626
-     * Gets code
627
-     *
628
-     * @return string
629
-     * @throws EE_Error
630
-     * @throws InvalidArgumentException
631
-     * @throws InvalidDataTypeException
632
-     * @throws InvalidInterfaceException
633
-     * @throws ReflectionException
634
-     */
635
-    public function code()
636
-    {
637
-        return $this->get('LIN_code');
638
-    }
639
-
640
-
641
-    /**
642
-     * Sets code
643
-     *
644
-     * @param string $code
645
-     * @throws EE_Error
646
-     * @throws InvalidArgumentException
647
-     * @throws InvalidDataTypeException
648
-     * @throws InvalidInterfaceException
649
-     * @throws ReflectionException
650
-     */
651
-    public function set_code($code)
652
-    {
653
-        $this->set('LIN_code', $code);
654
-    }
655
-
656
-
657
-    /**
658
-     * Gets is_taxable
659
-     *
660
-     * @return boolean
661
-     * @throws EE_Error
662
-     * @throws InvalidArgumentException
663
-     * @throws InvalidDataTypeException
664
-     * @throws InvalidInterfaceException
665
-     * @throws ReflectionException
666
-     */
667
-    public function is_taxable()
668
-    {
669
-        return $this->get('LIN_is_taxable');
670
-    }
671
-
672
-
673
-    /**
674
-     * Sets is_taxable
675
-     *
676
-     * @param boolean $is_taxable
677
-     * @throws EE_Error
678
-     * @throws InvalidArgumentException
679
-     * @throws InvalidDataTypeException
680
-     * @throws InvalidInterfaceException
681
-     * @throws ReflectionException
682
-     */
683
-    public function set_is_taxable($is_taxable)
684
-    {
685
-        $this->set('LIN_is_taxable', $is_taxable);
686
-    }
687
-
688
-
689
-    /**
690
-     * Gets the object that this model-joins-to.
691
-     * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
692
-     * EEM_Promotion_Object
693
-     *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
694
-     *
695
-     * @return EE_Base_Class | NULL
696
-     * @throws EE_Error
697
-     * @throws InvalidArgumentException
698
-     * @throws InvalidDataTypeException
699
-     * @throws InvalidInterfaceException
700
-     * @throws ReflectionException
701
-     */
702
-    public function get_object()
703
-    {
704
-        $model_name_of_related_obj = $this->OBJ_type();
705
-        return $this->get_model()->has_relation($model_name_of_related_obj)
706
-            ? $this->get_first_related($model_name_of_related_obj)
707
-            : null;
708
-    }
709
-
710
-
711
-    /**
712
-     * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
713
-     * (IE, if this line item is for a price or something else, will return NULL)
714
-     *
715
-     * @param array $query_params
716
-     * @return EE_Base_Class|EE_Ticket
717
-     * @throws EE_Error
718
-     * @throws InvalidArgumentException
719
-     * @throws InvalidDataTypeException
720
-     * @throws InvalidInterfaceException
721
-     * @throws ReflectionException
722
-     */
723
-    public function ticket($query_params = array())
724
-    {
725
-        // we're going to assume that when this method is called
726
-        // we always want to receive the attached ticket EVEN if that ticket is archived.
727
-        // This can be overridden via the incoming $query_params argument
728
-        $remove_defaults = array('default_where_conditions' => 'none');
729
-        $query_params = array_merge($remove_defaults, $query_params);
730
-        return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TICKET, $query_params);
731
-    }
732
-
733
-
734
-    /**
735
-     * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
736
-     *
737
-     * @return EE_Datetime | NULL
738
-     * @throws EE_Error
739
-     * @throws InvalidArgumentException
740
-     * @throws InvalidDataTypeException
741
-     * @throws InvalidInterfaceException
742
-     * @throws ReflectionException
743
-     */
744
-    public function get_ticket_datetime()
745
-    {
746
-        if ($this->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
747
-            $ticket = $this->ticket();
748
-            if ($ticket instanceof EE_Ticket) {
749
-                $datetime = $ticket->first_datetime();
750
-                if ($datetime instanceof EE_Datetime) {
751
-                    return $datetime;
752
-                }
753
-            }
754
-        }
755
-        return null;
756
-    }
757
-
758
-
759
-    /**
760
-     * Gets the event's name that's related to the ticket, if this is for
761
-     * a ticket
762
-     *
763
-     * @return string
764
-     * @throws EE_Error
765
-     * @throws InvalidArgumentException
766
-     * @throws InvalidDataTypeException
767
-     * @throws InvalidInterfaceException
768
-     * @throws ReflectionException
769
-     */
770
-    public function ticket_event_name()
771
-    {
772
-        $event_name = esc_html__('Unknown', 'event_espresso');
773
-        $event = $this->ticket_event();
774
-        if ($event instanceof EE_Event) {
775
-            $event_name = $event->name();
776
-        }
777
-        return $event_name;
778
-    }
779
-
780
-
781
-    /**
782
-     * Gets the event that's related to the ticket, if this line item represents a ticket.
783
-     *
784
-     * @return EE_Event|null
785
-     * @throws EE_Error
786
-     * @throws InvalidArgumentException
787
-     * @throws InvalidDataTypeException
788
-     * @throws InvalidInterfaceException
789
-     * @throws ReflectionException
790
-     */
791
-    public function ticket_event()
792
-    {
793
-        $event = null;
794
-        $ticket = $this->ticket();
795
-        if ($ticket instanceof EE_Ticket) {
796
-            $datetime = $ticket->first_datetime();
797
-            if ($datetime instanceof EE_Datetime) {
798
-                $event = $datetime->event();
799
-            }
800
-        }
801
-        return $event;
802
-    }
803
-
804
-
805
-    /**
806
-     * Gets the first datetime for this lien item, assuming it's for a ticket
807
-     *
808
-     * @param string $date_format
809
-     * @param string $time_format
810
-     * @return string
811
-     * @throws EE_Error
812
-     * @throws InvalidArgumentException
813
-     * @throws InvalidDataTypeException
814
-     * @throws InvalidInterfaceException
815
-     * @throws ReflectionException
816
-     */
817
-    public function ticket_datetime_start($date_format = '', $time_format = '')
818
-    {
819
-        $first_datetime_string = esc_html__('Unknown', 'event_espresso');
820
-        $datetime = $this->get_ticket_datetime();
821
-        if ($datetime) {
822
-            $first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
823
-        }
824
-        return $first_datetime_string;
825
-    }
826
-
827
-
828
-    /**
829
-     * Adds the line item as a child to this line item. If there is another child line
830
-     * item with the same LIN_code, it is overwritten by this new one
831
-     *
832
-     * @param EEI_Line_Item $line_item
833
-     * @param bool          $set_order
834
-     * @return bool success
835
-     * @throws EE_Error
836
-     * @throws InvalidArgumentException
837
-     * @throws InvalidDataTypeException
838
-     * @throws InvalidInterfaceException
839
-     * @throws ReflectionException
840
-     */
841
-    public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
842
-    {
843
-        // should we calculate the LIN_order for this line item ?
844
-        if ($set_order || $line_item->order() === null) {
845
-            $line_item->set_order(count($this->children()));
846
-        }
847
-        if ($this->ID()) {
848
-            // check for any duplicate line items (with the same code), if so, this replaces it
849
-            $line_item_with_same_code = $this->get_child_line_item($line_item->code());
850
-            if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
851
-                $this->delete_child_line_item($line_item_with_same_code->code());
852
-            }
853
-            $line_item->set_parent_ID($this->ID());
854
-            if ($this->TXN_ID()) {
855
-                $line_item->set_TXN_ID($this->TXN_ID());
856
-            }
857
-            return $line_item->save();
858
-        }
859
-        $this->_children[ $line_item->code() ] = $line_item;
860
-        if ($line_item->parent() !== $this) {
861
-            $line_item->set_parent($this);
862
-        }
863
-        return true;
864
-    }
865
-
866
-
867
-    /**
868
-     * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
869
-     * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
870
-     * However, if this line item is NOT saved to the DB, this just caches the parent on
871
-     * the EE_Line_Item::_parent property.
872
-     *
873
-     * @param EE_Line_Item $line_item
874
-     * @throws EE_Error
875
-     * @throws InvalidArgumentException
876
-     * @throws InvalidDataTypeException
877
-     * @throws InvalidInterfaceException
878
-     * @throws ReflectionException
879
-     */
880
-    public function set_parent($line_item)
881
-    {
882
-        if ($this->ID()) {
883
-            if (! $line_item->ID()) {
884
-                $line_item->save();
885
-            }
886
-            $this->set_parent_ID($line_item->ID());
887
-            $this->save();
888
-        } else {
889
-            $this->_parent = $line_item;
890
-            $this->set_parent_ID($line_item->ID());
891
-        }
892
-    }
893
-
894
-
895
-    /**
896
-     * Gets the child line item as specified by its code. Because this returns an object (by reference)
897
-     * you can modify this child line item and the parent (this object) can know about them
898
-     * because it also has a reference to that line item
899
-     *
900
-     * @param string $code
901
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
902
-     * @throws EE_Error
903
-     * @throws InvalidArgumentException
904
-     * @throws InvalidDataTypeException
905
-     * @throws InvalidInterfaceException
906
-     * @throws ReflectionException
907
-     */
908
-    public function get_child_line_item($code)
909
-    {
910
-        if ($this->ID()) {
911
-            return $this->get_model()->get_one(
912
-                array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
913
-            );
914
-        }
915
-        return isset($this->_children[ $code ])
916
-            ? $this->_children[ $code ]
917
-            : null;
918
-    }
919
-
920
-
921
-    /**
922
-     * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
923
-     * cached on it)
924
-     *
925
-     * @return int
926
-     * @throws EE_Error
927
-     * @throws InvalidArgumentException
928
-     * @throws InvalidDataTypeException
929
-     * @throws InvalidInterfaceException
930
-     * @throws ReflectionException
931
-     */
932
-    public function delete_children_line_items()
933
-    {
934
-        if ($this->ID()) {
935
-            return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
936
-        }
937
-        $count = count($this->_children);
938
-        $this->_children = array();
939
-        return $count;
940
-    }
941
-
942
-
943
-    /**
944
-     * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
945
-     * HAS NOT been saved to the DB, removes the child line item with index $code.
946
-     * Also searches through the child's children for a matching line item. However, once a line item has been found
947
-     * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
948
-     * deleted)
949
-     *
950
-     * @param string $code
951
-     * @param bool   $stop_search_once_found
952
-     * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
953
-     *             the DB yet)
954
-     * @throws EE_Error
955
-     * @throws InvalidArgumentException
956
-     * @throws InvalidDataTypeException
957
-     * @throws InvalidInterfaceException
958
-     * @throws ReflectionException
959
-     */
960
-    public function delete_child_line_item($code, $stop_search_once_found = true)
961
-    {
962
-        if ($this->ID()) {
963
-            $items_deleted = 0;
964
-            if ($this->code() === $code) {
965
-                $items_deleted += EEH_Line_Item::delete_all_child_items($this);
966
-                $items_deleted += (int) $this->delete();
967
-                if ($stop_search_once_found) {
968
-                    return $items_deleted;
969
-                }
970
-            }
971
-            foreach ($this->children() as $child_line_item) {
972
-                $items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
973
-            }
974
-            return $items_deleted;
975
-        }
976
-        if (isset($this->_children[ $code ])) {
977
-            unset($this->_children[ $code ]);
978
-            return 1;
979
-        }
980
-        return 0;
981
-    }
982
-
983
-
984
-    /**
985
-     * If this line item is in the database, is of the type subtotal, and
986
-     * has no children, why do we have it? It should be deleted so this function
987
-     * does that
988
-     *
989
-     * @return boolean
990
-     * @throws EE_Error
991
-     * @throws InvalidArgumentException
992
-     * @throws InvalidDataTypeException
993
-     * @throws InvalidInterfaceException
994
-     * @throws ReflectionException
995
-     */
996
-    public function delete_if_childless_subtotal()
997
-    {
998
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
999
-            return $this->delete();
1000
-        }
1001
-        return false;
1002
-    }
1003
-
1004
-
1005
-    /**
1006
-     * Creates a code and returns a string. doesn't assign the code to this model object
1007
-     *
1008
-     * @return string
1009
-     * @throws EE_Error
1010
-     * @throws InvalidArgumentException
1011
-     * @throws InvalidDataTypeException
1012
-     * @throws InvalidInterfaceException
1013
-     * @throws ReflectionException
1014
-     */
1015
-    public function generate_code()
1016
-    {
1017
-        // each line item in the cart requires a unique identifier
1018
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1019
-    }
1020
-
1021
-
1022
-    /**
1023
-     * @return bool
1024
-     * @throws EE_Error
1025
-     * @throws InvalidArgumentException
1026
-     * @throws InvalidDataTypeException
1027
-     * @throws InvalidInterfaceException
1028
-     * @throws ReflectionException
1029
-     */
1030
-    public function is_tax()
1031
-    {
1032
-        return $this->type() === EEM_Line_Item::type_tax;
1033
-    }
1034
-
1035
-
1036
-    /**
1037
-     * @return bool
1038
-     * @throws EE_Error
1039
-     * @throws InvalidArgumentException
1040
-     * @throws InvalidDataTypeException
1041
-     * @throws InvalidInterfaceException
1042
-     * @throws ReflectionException
1043
-     */
1044
-    public function is_tax_sub_total()
1045
-    {
1046
-        return $this->type() === EEM_Line_Item::type_tax_sub_total;
1047
-    }
1048
-
1049
-
1050
-    /**
1051
-     * @return bool
1052
-     * @throws EE_Error
1053
-     * @throws InvalidArgumentException
1054
-     * @throws InvalidDataTypeException
1055
-     * @throws InvalidInterfaceException
1056
-     * @throws ReflectionException
1057
-     */
1058
-    public function is_line_item()
1059
-    {
1060
-        return $this->type() === EEM_Line_Item::type_line_item;
1061
-    }
1062
-
1063
-
1064
-    /**
1065
-     * @return bool
1066
-     * @throws EE_Error
1067
-     * @throws InvalidArgumentException
1068
-     * @throws InvalidDataTypeException
1069
-     * @throws InvalidInterfaceException
1070
-     * @throws ReflectionException
1071
-     */
1072
-    public function is_sub_line_item()
1073
-    {
1074
-        return $this->type() === EEM_Line_Item::type_sub_line_item;
1075
-    }
1076
-
1077
-
1078
-    /**
1079
-     * @return bool
1080
-     * @throws EE_Error
1081
-     * @throws InvalidArgumentException
1082
-     * @throws InvalidDataTypeException
1083
-     * @throws InvalidInterfaceException
1084
-     * @throws ReflectionException
1085
-     */
1086
-    public function is_sub_total()
1087
-    {
1088
-        return $this->type() === EEM_Line_Item::type_sub_total;
1089
-    }
1090
-
1091
-
1092
-    /**
1093
-     * Whether or not this line item is a cancellation line item
1094
-     *
1095
-     * @return boolean
1096
-     * @throws EE_Error
1097
-     * @throws InvalidArgumentException
1098
-     * @throws InvalidDataTypeException
1099
-     * @throws InvalidInterfaceException
1100
-     * @throws ReflectionException
1101
-     */
1102
-    public function is_cancellation()
1103
-    {
1104
-        return EEM_Line_Item::type_cancellation === $this->type();
1105
-    }
1106
-
1107
-
1108
-    /**
1109
-     * @return bool
1110
-     * @throws EE_Error
1111
-     * @throws InvalidArgumentException
1112
-     * @throws InvalidDataTypeException
1113
-     * @throws InvalidInterfaceException
1114
-     * @throws ReflectionException
1115
-     */
1116
-    public function is_total()
1117
-    {
1118
-        return $this->type() === EEM_Line_Item::type_total;
1119
-    }
1120
-
1121
-
1122
-    /**
1123
-     * @return bool
1124
-     * @throws EE_Error
1125
-     * @throws InvalidArgumentException
1126
-     * @throws InvalidDataTypeException
1127
-     * @throws InvalidInterfaceException
1128
-     * @throws ReflectionException
1129
-     */
1130
-    public function is_cancelled()
1131
-    {
1132
-        return $this->type() === EEM_Line_Item::type_cancellation;
1133
-    }
1134
-
1135
-
1136
-    /**
1137
-     * @return string like '2, 004.00', formatted according to the localized currency
1138
-     * @throws EE_Error
1139
-     * @throws InvalidArgumentException
1140
-     * @throws InvalidDataTypeException
1141
-     * @throws InvalidInterfaceException
1142
-     * @throws ReflectionException
1143
-     */
1144
-    public function unit_price_no_code()
1145
-    {
1146
-        return $this->get_pretty('LIN_unit_price', 'no_currency_code');
1147
-    }
1148
-
1149
-
1150
-    /**
1151
-     * @return string like '2, 004.00', formatted according to the localized currency
1152
-     * @throws EE_Error
1153
-     * @throws InvalidArgumentException
1154
-     * @throws InvalidDataTypeException
1155
-     * @throws InvalidInterfaceException
1156
-     * @throws ReflectionException
1157
-     */
1158
-    public function total_no_code()
1159
-    {
1160
-        return $this->get_pretty('LIN_total', 'no_currency_code');
1161
-    }
1162
-
1163
-
1164
-    /**
1165
-     * Gets the final total on this item, taking taxes into account.
1166
-     * Has the side-effect of setting the sub-total as it was just calculated.
1167
-     * If this is used on a grand-total line item, also updates the transaction's
1168
-     * TXN_total (provided this line item is allowed to persist, otherwise we don't
1169
-     * want to change a persistable transaction with info from a non-persistent line item)
1170
-     *
1171
-     * @param bool $update_txn_status
1172
-     * @return float
1173
-     * @throws EE_Error
1174
-     * @throws InvalidArgumentException
1175
-     * @throws InvalidDataTypeException
1176
-     * @throws InvalidInterfaceException
1177
-     * @throws ReflectionException
1178
-     * @throws RuntimeException
1179
-     */
1180
-    public function recalculate_total_including_taxes($update_txn_status = false)
1181
-    {
1182
-        $pre_tax_total = $this->recalculate_pre_tax_total();
1183
-        $tax_total = $this->recalculate_taxes_and_tax_total();
1184
-        $total = $pre_tax_total + $tax_total;
1185
-        // no negative totals plz
1186
-        $total = max($total, 0);
1187
-        $this->set_total($total);
1188
-        // only update the related transaction's total
1189
-        // if we intend to save this line item and its a grand total
1190
-        if ($this->allow_persist() && $this->type() === EEM_Line_Item::type_total
1191
-            && $this->transaction()
1192
-               instanceof
1193
-               EE_Transaction
1194
-        ) {
1195
-            $this->transaction()->set_total($total);
1196
-            if ($update_txn_status) {
1197
-                // don't save the TXN because that will be done below
1198
-                // and the following method only saves if the status changes
1199
-                $this->transaction()->update_status_based_on_total_paid(false);
1200
-            }
1201
-            if ($this->transaction()->ID()) {
1202
-                $this->transaction()->save();
1203
-            }
1204
-        }
1205
-        $this->maybe_save();
1206
-        return $total;
1207
-    }
1208
-
1209
-
1210
-    /**
1211
-     * Recursively goes through all the children and recalculates sub-totals EXCEPT for
1212
-     * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
1213
-     * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
1214
-     * when this is called on the grand total
1215
-     *
1216
-     * @return float
1217
-     * @throws EE_Error
1218
-     * @throws InvalidArgumentException
1219
-     * @throws InvalidDataTypeException
1220
-     * @throws InvalidInterfaceException
1221
-     * @throws ReflectionException
1222
-     */
1223
-    public function recalculate_pre_tax_total()
1224
-    {
1225
-        $total = 0;
1226
-        $my_children = $this->children();
1227
-        $has_children = ! empty($my_children);
1228
-        if ($has_children && $this->is_line_item()) {
1229
-            $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
1230
-        } elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
1231
-            $total = $this->unit_price() * $this->quantity();
1232
-        } elseif ($this->is_sub_total() || $this->is_total()) {
1233
-            $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
1234
-        } elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
1235
-            // completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
1236
-            return 0;
1237
-        }
1238
-        // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
1239
-        if (! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
1240
-        ) {
1241
-            if ($this->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_EVENT) {
1242
-                $this->set_quantity(1);
1243
-            }
1244
-            if (! $this->is_percent()) {
1245
-                $this->set_unit_price($total);
1246
-            }
1247
-        }
1248
-        // we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1249
-        // so it ought to be
1250
-        if (! $this->is_total()) {
1251
-            $this->set_total($total);
1252
-            // if not a percent line item, make sure we keep the unit price in sync
1253
-            if ($has_children
1254
-                && $this->is_line_item()
1255
-                && ! $this->is_percent()
1256
-            ) {
1257
-                if ($this->quantity() === 0) {
1258
-                    $new_unit_price = 0;
1259
-                } else {
1260
-                    $new_unit_price = $this->total() / $this->quantity();
1261
-                }
1262
-                $this->set_unit_price($new_unit_price);
1263
-            }
1264
-            $this->maybe_save();
1265
-        }
1266
-        return $total;
1267
-    }
1268
-
1269
-
1270
-    /**
1271
-     * Calculates the pretax total when this line item is a subtotal or total line item.
1272
-     * Basically does a sum-then-round approach (ie, any percent line item that are children
1273
-     * will calculate their total based on the un-rounded total we're working with so far, and
1274
-     * THEN round the result; instead of rounding as we go like with sub-line-items)
1275
-     *
1276
-     * @param float          $calculated_total_so_far
1277
-     * @param EE_Line_Item[] $my_children
1278
-     * @return float
1279
-     * @throws EE_Error
1280
-     * @throws InvalidArgumentException
1281
-     * @throws InvalidDataTypeException
1282
-     * @throws InvalidInterfaceException
1283
-     * @throws ReflectionException
1284
-     */
1285
-    protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1286
-    {
1287
-        if ($my_children === null) {
1288
-            $my_children = $this->children();
1289
-        }
1290
-        $subtotal_quantity = 0;
1291
-        // get the total of all its children
1292
-        foreach ($my_children as $child_line_item) {
1293
-            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1294
-                // percentage line items are based on total so far
1295
-                if ($child_line_item->is_percent()) {
1296
-                    // round as we go so that the line items add up ok
1297
-                    $percent_total = round(
1298
-                        $calculated_total_so_far * $child_line_item->percent() / 100,
1299
-                        EE_Registry::instance()->CFG->currency->dec_plc
1300
-                    );
1301
-                    $child_line_item->set_total($percent_total);
1302
-                    // so far all percent line items should have a quantity of 1
1303
-                    // (ie, no double percent discounts. Although that might be requested someday)
1304
-                    $child_line_item->set_quantity(1);
1305
-                    $child_line_item->maybe_save();
1306
-                    $calculated_total_so_far += $percent_total;
1307
-                } else {
1308
-                    // verify flat sub-line-item quantities match their parent
1309
-                    if ($child_line_item->is_sub_line_item()) {
1310
-                        $child_line_item->set_quantity($this->quantity());
1311
-                    }
1312
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1313
-                    $subtotal_quantity += $child_line_item->quantity();
1314
-                }
1315
-            }
1316
-        }
1317
-        if ($this->is_sub_total()) {
1318
-            // no negative totals plz
1319
-            $calculated_total_so_far = max($calculated_total_so_far, 0);
1320
-            $subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1321
-            $this->set_quantity($subtotal_quantity);
1322
-            $this->maybe_save();
1323
-        }
1324
-        return $calculated_total_so_far;
1325
-    }
1326
-
1327
-
1328
-    /**
1329
-     * Calculates the pretax total for a normal line item, in a round-then-sum approach
1330
-     * (where each sub-line-item is applied to the base price for the line item
1331
-     * and the result is immediately rounded, rather than summing all the sub-line-items
1332
-     * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1333
-     *
1334
-     * @param float          $calculated_total_so_far
1335
-     * @param EE_Line_Item[] $my_children
1336
-     * @return float
1337
-     * @throws EE_Error
1338
-     * @throws InvalidArgumentException
1339
-     * @throws InvalidDataTypeException
1340
-     * @throws InvalidInterfaceException
1341
-     * @throws ReflectionException
1342
-     */
1343
-    protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1344
-    {
1345
-        if ($my_children === null) {
1346
-            $my_children = $this->children();
1347
-        }
1348
-        // we need to keep track of the running total for a single item,
1349
-        // because we need to round as we go
1350
-        $unit_price_for_total = 0;
1351
-        $quantity_for_total = 1;
1352
-        // get the total of all its children
1353
-        foreach ($my_children as $child_line_item) {
1354
-            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1355
-                if ($child_line_item->is_percent()) {
1356
-                    // it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1357
-                    // not total multiplied by percent, because that ignores rounding along-the-way
1358
-                    $percent_unit_price = round(
1359
-                        $unit_price_for_total * $child_line_item->percent() / 100,
1360
-                        EE_Registry::instance()->CFG->currency->dec_plc
1361
-                    );
1362
-                    $percent_total = $percent_unit_price * $quantity_for_total;
1363
-                    $child_line_item->set_total($percent_total);
1364
-                    // so far all percent line items should have a quantity of 1
1365
-                    // (ie, no double percent discounts. Although that might be requested someday)
1366
-                    $child_line_item->set_quantity(1);
1367
-                    $child_line_item->maybe_save();
1368
-                    $calculated_total_so_far += $percent_total;
1369
-                    $unit_price_for_total += $percent_unit_price;
1370
-                } else {
1371
-                    // verify flat sub-line-item quantities match their parent
1372
-                    if ($child_line_item->is_sub_line_item()) {
1373
-                        $child_line_item->set_quantity($this->quantity());
1374
-                    }
1375
-                    $quantity_for_total = $child_line_item->quantity();
1376
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1377
-                    $unit_price_for_total += $child_line_item->unit_price();
1378
-                }
1379
-            }
1380
-        }
1381
-        return $calculated_total_so_far;
1382
-    }
1383
-
1384
-
1385
-    /**
1386
-     * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1387
-     * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1388
-     * and tax sub-total if already in the DB
1389
-     *
1390
-     * @return float
1391
-     * @throws EE_Error
1392
-     * @throws InvalidArgumentException
1393
-     * @throws InvalidDataTypeException
1394
-     * @throws InvalidInterfaceException
1395
-     * @throws ReflectionException
1396
-     */
1397
-    public function recalculate_taxes_and_tax_total()
1398
-    {
1399
-        // get all taxes
1400
-        $taxes = $this->tax_descendants();
1401
-        // calculate the pretax total
1402
-        $taxable_total = $this->taxable_total();
1403
-        $tax_total = 0;
1404
-        foreach ($taxes as $tax) {
1405
-            $total_on_this_tax = $taxable_total * $tax->percent() / 100;
1406
-            // remember the total on this line item
1407
-            $tax->set_total($total_on_this_tax);
1408
-            $tax->maybe_save();
1409
-            $tax_total += $tax->total();
1410
-        }
1411
-        $this->_recalculate_tax_sub_total();
1412
-        return $tax_total;
1413
-    }
1414
-
1415
-
1416
-    /**
1417
-     * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1418
-     *
1419
-     * @return void
1420
-     * @throws EE_Error
1421
-     * @throws InvalidArgumentException
1422
-     * @throws InvalidDataTypeException
1423
-     * @throws InvalidInterfaceException
1424
-     * @throws ReflectionException
1425
-     */
1426
-    private function _recalculate_tax_sub_total()
1427
-    {
1428
-        if ($this->is_tax_sub_total()) {
1429
-            $total = 0;
1430
-            $total_percent = 0;
1431
-            // simply loop through all its children (which should be taxes) and sum their total
1432
-            foreach ($this->children() as $child_tax) {
1433
-                if ($child_tax instanceof EE_Line_Item) {
1434
-                    $total += $child_tax->total();
1435
-                    $total_percent += $child_tax->percent();
1436
-                }
1437
-            }
1438
-            $this->set_total($total);
1439
-            $this->set_percent($total_percent);
1440
-            $this->maybe_save();
1441
-        } elseif ($this->is_total()) {
1442
-            foreach ($this->children() as $maybe_tax_subtotal) {
1443
-                if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1444
-                    $maybe_tax_subtotal->_recalculate_tax_sub_total();
1445
-                }
1446
-            }
1447
-        }
1448
-    }
1449
-
1450
-
1451
-    /**
1452
-     * Gets the total tax on this line item. Assumes taxes have already been calculated using
1453
-     * recalculate_taxes_and_total
1454
-     *
1455
-     * @return float
1456
-     * @throws EE_Error
1457
-     * @throws InvalidArgumentException
1458
-     * @throws InvalidDataTypeException
1459
-     * @throws InvalidInterfaceException
1460
-     * @throws ReflectionException
1461
-     */
1462
-    public function get_total_tax()
1463
-    {
1464
-        $this->_recalculate_tax_sub_total();
1465
-        $total = 0;
1466
-        foreach ($this->tax_descendants() as $tax_line_item) {
1467
-            if ($tax_line_item instanceof EE_Line_Item) {
1468
-                $total += $tax_line_item->total();
1469
-            }
1470
-        }
1471
-        return $total;
1472
-    }
1473
-
1474
-
1475
-    /**
1476
-     * Gets the total for all the items purchased only
1477
-     *
1478
-     * @return float
1479
-     * @throws EE_Error
1480
-     * @throws InvalidArgumentException
1481
-     * @throws InvalidDataTypeException
1482
-     * @throws InvalidInterfaceException
1483
-     * @throws ReflectionException
1484
-     */
1485
-    public function get_items_total()
1486
-    {
1487
-        // by default, let's make sure we're consistent with the existing line item
1488
-        if ($this->is_total()) {
1489
-            $pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1490
-            if ($pretax_subtotal_li instanceof EE_Line_Item) {
1491
-                return $pretax_subtotal_li->total();
1492
-            }
1493
-        }
1494
-        $total = 0;
1495
-        foreach ($this->get_items() as $item) {
1496
-            if ($item instanceof EE_Line_Item) {
1497
-                $total += $item->total();
1498
-            }
1499
-        }
1500
-        return $total;
1501
-    }
1502
-
1503
-
1504
-    /**
1505
-     * Gets all the descendants (ie, children or children of children etc) that
1506
-     * are of the type 'tax'
1507
-     *
1508
-     * @return EE_Line_Item[]
1509
-     * @throws EE_Error
1510
-     */
1511
-    public function tax_descendants()
1512
-    {
1513
-        return EEH_Line_Item::get_tax_descendants($this);
1514
-    }
1515
-
1516
-
1517
-    /**
1518
-     * Gets all the real items purchased which are children of this item
1519
-     *
1520
-     * @return EE_Line_Item[]
1521
-     * @throws EE_Error
1522
-     */
1523
-    public function get_items()
1524
-    {
1525
-        return EEH_Line_Item::get_line_item_descendants($this);
1526
-    }
1527
-
1528
-
1529
-    /**
1530
-     * Returns the amount taxable among this line item's children (or if it has no children,
1531
-     * how much of it is taxable). Does not recalculate totals or subtotals.
1532
-     * If the taxable total is negative, (eg, if none of the tickets were taxable,
1533
-     * but there is a "Taxable" discount), returns 0.
1534
-     *
1535
-     * @return float
1536
-     * @throws EE_Error
1537
-     * @throws InvalidArgumentException
1538
-     * @throws InvalidDataTypeException
1539
-     * @throws InvalidInterfaceException
1540
-     * @throws ReflectionException
1541
-     */
1542
-    public function taxable_total()
1543
-    {
1544
-        $total = 0;
1545
-        if ($this->children()) {
1546
-            foreach ($this->children() as $child_line_item) {
1547
-                if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1548
-                    // if it's a percent item, only take into account the percent
1549
-                    // that's taxable too (the taxable total so far)
1550
-                    if ($child_line_item->is_percent()) {
1551
-                        $total += ($total * $child_line_item->percent() / 100);
1552
-                    } else {
1553
-                        $total += $child_line_item->total();
1554
-                    }
1555
-                } elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1556
-                    $total += $child_line_item->taxable_total();
1557
-                } elseif ($child_line_item->is_percent()) {
1558
-                    // Taxes should be charged on the discounted price.
1559
-                    $total += $child_line_item->total();
1560
-                }
1561
-            }
1562
-        }
1563
-        return max($total, 0);
1564
-    }
1565
-
1566
-
1567
-    /**
1568
-     * Gets the transaction for this line item
1569
-     *
1570
-     * @return EE_Base_Class|EE_Transaction
1571
-     * @throws EE_Error
1572
-     * @throws InvalidArgumentException
1573
-     * @throws InvalidDataTypeException
1574
-     * @throws InvalidInterfaceException
1575
-     * @throws ReflectionException
1576
-     */
1577
-    public function transaction()
1578
-    {
1579
-        return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TRANSACTION);
1580
-    }
1581
-
1582
-
1583
-    /**
1584
-     * Saves this line item to the DB, and recursively saves its descendants.
1585
-     * Because there currently is no proper parent-child relation on the model,
1586
-     * save_this_and_cached() will NOT save the descendants.
1587
-     * Also sets the transaction on this line item and all its descendants before saving
1588
-     *
1589
-     * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1590
-     * @return int count of items saved
1591
-     * @throws EE_Error
1592
-     * @throws InvalidArgumentException
1593
-     * @throws InvalidDataTypeException
1594
-     * @throws InvalidInterfaceException
1595
-     * @throws ReflectionException
1596
-     */
1597
-    public function save_this_and_descendants_to_txn($txn_id = null)
1598
-    {
1599
-        $count = 0;
1600
-        if (! $txn_id) {
1601
-            $txn_id = $this->TXN_ID();
1602
-        }
1603
-        $this->set_TXN_ID($txn_id);
1604
-        $children = $this->children();
1605
-        $count += $this->save()
1606
-            ? 1
1607
-            : 0;
1608
-        foreach ($children as $child_line_item) {
1609
-            if ($child_line_item instanceof EE_Line_Item) {
1610
-                $child_line_item->set_parent_ID($this->ID());
1611
-                $count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1612
-            }
1613
-        }
1614
-        return $count;
1615
-    }
1616
-
1617
-
1618
-    /**
1619
-     * Saves this line item to the DB, and recursively saves its descendants.
1620
-     *
1621
-     * @return int count of items saved
1622
-     * @throws EE_Error
1623
-     * @throws InvalidArgumentException
1624
-     * @throws InvalidDataTypeException
1625
-     * @throws InvalidInterfaceException
1626
-     * @throws ReflectionException
1627
-     */
1628
-    public function save_this_and_descendants()
1629
-    {
1630
-        $count = 0;
1631
-        $children = $this->children();
1632
-        $count += $this->save()
1633
-            ? 1
1634
-            : 0;
1635
-        foreach ($children as $child_line_item) {
1636
-            if ($child_line_item instanceof EE_Line_Item) {
1637
-                $child_line_item->set_parent_ID($this->ID());
1638
-                $count += $child_line_item->save_this_and_descendants();
1639
-            }
1640
-        }
1641
-        return $count;
1642
-    }
1643
-
1644
-
1645
-    /**
1646
-     * returns the cancellation line item if this item was cancelled
1647
-     *
1648
-     * @return EE_Line_Item[]
1649
-     * @throws InvalidArgumentException
1650
-     * @throws InvalidInterfaceException
1651
-     * @throws InvalidDataTypeException
1652
-     * @throws ReflectionException
1653
-     * @throws EE_Error
1654
-     */
1655
-    public function get_cancellations()
1656
-    {
1657
-        EE_Registry::instance()->load_helper('Line_Item');
1658
-        return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1659
-    }
1660
-
1661
-
1662
-    /**
1663
-     * If this item has an ID, then this saves it again to update the db
1664
-     *
1665
-     * @return int count of items saved
1666
-     * @throws EE_Error
1667
-     * @throws InvalidArgumentException
1668
-     * @throws InvalidDataTypeException
1669
-     * @throws InvalidInterfaceException
1670
-     * @throws ReflectionException
1671
-     */
1672
-    public function maybe_save()
1673
-    {
1674
-        if ($this->ID()) {
1675
-            return $this->save();
1676
-        }
1677
-        return false;
1678
-    }
1679
-
1680
-
1681
-    /**
1682
-     * clears the cached children and parent from the line item
1683
-     *
1684
-     * @return void
1685
-     */
1686
-    public function clear_related_line_item_cache()
1687
-    {
1688
-        $this->_children = array();
1689
-        $this->_parent = null;
1690
-    }
1691
-
1692
-
1693
-    /**
1694
-     * @param bool $raw
1695
-     * @return int
1696
-     * @throws EE_Error
1697
-     * @throws InvalidArgumentException
1698
-     * @throws InvalidDataTypeException
1699
-     * @throws InvalidInterfaceException
1700
-     * @throws ReflectionException
1701
-     */
1702
-    public function timestamp($raw = false)
1703
-    {
1704
-        return $raw
1705
-            ? $this->get_raw('LIN_timestamp')
1706
-            : $this->get('LIN_timestamp');
1707
-    }
1708
-
1709
-
1710
-
1711
-
1712
-    /************************* DEPRECATED *************************/
1713
-    /**
1714
-     * @deprecated 4.6.0
1715
-     * @param string $type one of the constants on EEM_Line_Item
1716
-     * @return EE_Line_Item[]
1717
-     * @throws EE_Error
1718
-     */
1719
-    protected function _get_descendants_of_type($type)
1720
-    {
1721
-        EE_Error::doing_it_wrong(
1722
-            'EE_Line_Item::_get_descendants_of_type()',
1723
-            sprintf(
1724
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
1725
-                'EEH_Line_Item::get_descendants_of_type()'
1726
-            ),
1727
-            '4.6.0'
1728
-        );
1729
-        return EEH_Line_Item::get_descendants_of_type($this, $type);
1730
-    }
1731
-
1732
-
1733
-    /**
1734
-     * @deprecated 4.6.0
1735
-     * @param string $type like one of the EEM_Line_Item::type_*
1736
-     * @return EE_Line_Item
1737
-     * @throws EE_Error
1738
-     * @throws InvalidArgumentException
1739
-     * @throws InvalidDataTypeException
1740
-     * @throws InvalidInterfaceException
1741
-     * @throws ReflectionException
1742
-     */
1743
-    public function get_nearest_descendant_of_type($type)
1744
-    {
1745
-        EE_Error::doing_it_wrong(
1746
-            'EE_Line_Item::get_nearest_descendant_of_type()',
1747
-            sprintf(
1748
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
1749
-                'EEH_Line_Item::get_nearest_descendant_of_type()'
1750
-            ),
1751
-            '4.6.0'
1752
-        );
1753
-        return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1754
-    }
17
+	/**
18
+	 * for children line items (currently not a normal relation)
19
+	 *
20
+	 * @type EE_Line_Item[]
21
+	 */
22
+	protected $_children = array();
23
+
24
+	/**
25
+	 * for the parent line item
26
+	 *
27
+	 * @var EE_Line_Item
28
+	 */
29
+	protected $_parent;
30
+
31
+
32
+	/**
33
+	 * @param array  $props_n_values          incoming values
34
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
35
+	 *                                        used.)
36
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
37
+	 *                                        date_format and the second value is the time format
38
+	 * @return EE_Line_Item
39
+	 * @throws EE_Error
40
+	 * @throws InvalidArgumentException
41
+	 * @throws InvalidDataTypeException
42
+	 * @throws InvalidInterfaceException
43
+	 * @throws ReflectionException
44
+	 */
45
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
46
+	{
47
+		$has_object = parent::_check_for_object(
48
+			$props_n_values,
49
+			__CLASS__,
50
+			$timezone,
51
+			$date_formats
52
+		);
53
+		return $has_object
54
+			? $has_object
55
+			: new self($props_n_values, false, $timezone);
56
+	}
57
+
58
+
59
+	/**
60
+	 * @param array  $props_n_values  incoming values from the database
61
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
62
+	 *                                the website will be used.
63
+	 * @return EE_Line_Item
64
+	 * @throws EE_Error
65
+	 * @throws InvalidArgumentException
66
+	 * @throws InvalidDataTypeException
67
+	 * @throws InvalidInterfaceException
68
+	 * @throws ReflectionException
69
+	 */
70
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
71
+	{
72
+		return new self($props_n_values, true, $timezone);
73
+	}
74
+
75
+
76
+	/**
77
+	 * Adds some defaults if they're not specified
78
+	 *
79
+	 * @param array  $fieldValues
80
+	 * @param bool   $bydb
81
+	 * @param string $timezone
82
+	 * @throws EE_Error
83
+	 * @throws InvalidArgumentException
84
+	 * @throws InvalidDataTypeException
85
+	 * @throws InvalidInterfaceException
86
+	 * @throws ReflectionException
87
+	 */
88
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
89
+	{
90
+		parent::__construct($fieldValues, $bydb, $timezone);
91
+		if (! $this->get('LIN_code')) {
92
+			$this->set_code($this->generate_code());
93
+		}
94
+	}
95
+
96
+
97
+	/**
98
+	 * Gets ID
99
+	 *
100
+	 * @return int
101
+	 * @throws EE_Error
102
+	 * @throws InvalidArgumentException
103
+	 * @throws InvalidDataTypeException
104
+	 * @throws InvalidInterfaceException
105
+	 * @throws ReflectionException
106
+	 */
107
+	public function ID()
108
+	{
109
+		return $this->get('LIN_ID');
110
+	}
111
+
112
+
113
+	/**
114
+	 * Gets TXN_ID
115
+	 *
116
+	 * @return int
117
+	 * @throws EE_Error
118
+	 * @throws InvalidArgumentException
119
+	 * @throws InvalidDataTypeException
120
+	 * @throws InvalidInterfaceException
121
+	 * @throws ReflectionException
122
+	 */
123
+	public function TXN_ID()
124
+	{
125
+		return $this->get('TXN_ID');
126
+	}
127
+
128
+
129
+	/**
130
+	 * Sets TXN_ID
131
+	 *
132
+	 * @param int $TXN_ID
133
+	 * @throws EE_Error
134
+	 * @throws InvalidArgumentException
135
+	 * @throws InvalidDataTypeException
136
+	 * @throws InvalidInterfaceException
137
+	 * @throws ReflectionException
138
+	 */
139
+	public function set_TXN_ID($TXN_ID)
140
+	{
141
+		$this->set('TXN_ID', $TXN_ID);
142
+	}
143
+
144
+
145
+	/**
146
+	 * Gets name
147
+	 *
148
+	 * @return string
149
+	 * @throws EE_Error
150
+	 * @throws InvalidArgumentException
151
+	 * @throws InvalidDataTypeException
152
+	 * @throws InvalidInterfaceException
153
+	 * @throws ReflectionException
154
+	 */
155
+	public function name()
156
+	{
157
+		$name = $this->get('LIN_name');
158
+		if (! $name) {
159
+			$name = ucwords(str_replace('-', ' ', $this->type()));
160
+		}
161
+		return $name;
162
+	}
163
+
164
+
165
+	/**
166
+	 * Sets name
167
+	 *
168
+	 * @param string $name
169
+	 * @throws EE_Error
170
+	 * @throws InvalidArgumentException
171
+	 * @throws InvalidDataTypeException
172
+	 * @throws InvalidInterfaceException
173
+	 * @throws ReflectionException
174
+	 */
175
+	public function set_name($name)
176
+	{
177
+		$this->set('LIN_name', $name);
178
+	}
179
+
180
+
181
+	/**
182
+	 * Gets desc
183
+	 *
184
+	 * @return string
185
+	 * @throws EE_Error
186
+	 * @throws InvalidArgumentException
187
+	 * @throws InvalidDataTypeException
188
+	 * @throws InvalidInterfaceException
189
+	 * @throws ReflectionException
190
+	 */
191
+	public function desc()
192
+	{
193
+		return $this->get('LIN_desc');
194
+	}
195
+
196
+
197
+	/**
198
+	 * Sets desc
199
+	 *
200
+	 * @param string $desc
201
+	 * @throws EE_Error
202
+	 * @throws InvalidArgumentException
203
+	 * @throws InvalidDataTypeException
204
+	 * @throws InvalidInterfaceException
205
+	 * @throws ReflectionException
206
+	 */
207
+	public function set_desc($desc)
208
+	{
209
+		$this->set('LIN_desc', $desc);
210
+	}
211
+
212
+
213
+	/**
214
+	 * Gets quantity
215
+	 *
216
+	 * @return int
217
+	 * @throws EE_Error
218
+	 * @throws InvalidArgumentException
219
+	 * @throws InvalidDataTypeException
220
+	 * @throws InvalidInterfaceException
221
+	 * @throws ReflectionException
222
+	 */
223
+	public function quantity()
224
+	{
225
+		return $this->get('LIN_quantity');
226
+	}
227
+
228
+
229
+	/**
230
+	 * Sets quantity
231
+	 *
232
+	 * @param int $quantity
233
+	 * @throws EE_Error
234
+	 * @throws InvalidArgumentException
235
+	 * @throws InvalidDataTypeException
236
+	 * @throws InvalidInterfaceException
237
+	 * @throws ReflectionException
238
+	 */
239
+	public function set_quantity($quantity)
240
+	{
241
+		$this->set('LIN_quantity', max($quantity, 0));
242
+	}
243
+
244
+
245
+	/**
246
+	 * Gets item_id
247
+	 *
248
+	 * @return string
249
+	 * @throws EE_Error
250
+	 * @throws InvalidArgumentException
251
+	 * @throws InvalidDataTypeException
252
+	 * @throws InvalidInterfaceException
253
+	 * @throws ReflectionException
254
+	 */
255
+	public function OBJ_ID()
256
+	{
257
+		return $this->get('OBJ_ID');
258
+	}
259
+
260
+
261
+	/**
262
+	 * Sets item_id
263
+	 *
264
+	 * @param string $item_id
265
+	 * @throws EE_Error
266
+	 * @throws InvalidArgumentException
267
+	 * @throws InvalidDataTypeException
268
+	 * @throws InvalidInterfaceException
269
+	 * @throws ReflectionException
270
+	 */
271
+	public function set_OBJ_ID($item_id)
272
+	{
273
+		$this->set('OBJ_ID', $item_id);
274
+	}
275
+
276
+
277
+	/**
278
+	 * Gets item_type
279
+	 *
280
+	 * @return string
281
+	 * @throws EE_Error
282
+	 * @throws InvalidArgumentException
283
+	 * @throws InvalidDataTypeException
284
+	 * @throws InvalidInterfaceException
285
+	 * @throws ReflectionException
286
+	 */
287
+	public function OBJ_type()
288
+	{
289
+		return $this->get('OBJ_type');
290
+	}
291
+
292
+
293
+	/**
294
+	 * Gets item_type
295
+	 *
296
+	 * @return string
297
+	 * @throws EE_Error
298
+	 * @throws InvalidArgumentException
299
+	 * @throws InvalidDataTypeException
300
+	 * @throws InvalidInterfaceException
301
+	 * @throws ReflectionException
302
+	 */
303
+	public function OBJ_type_i18n()
304
+	{
305
+		$obj_type = $this->OBJ_type();
306
+		switch ($obj_type) {
307
+			case EEM_Line_Item::OBJ_TYPE_EVENT:
308
+				$obj_type = esc_html__('Event', 'event_espresso');
309
+				break;
310
+			case EEM_Line_Item::OBJ_TYPE_PRICE:
311
+				$obj_type = esc_html__('Price', 'event_espresso');
312
+				break;
313
+			case EEM_Line_Item::OBJ_TYPE_PROMOTION:
314
+				$obj_type = esc_html__('Promotion', 'event_espresso');
315
+				break;
316
+			case EEM_Line_Item::OBJ_TYPE_TICKET:
317
+				$obj_type = esc_html__('Ticket', 'event_espresso');
318
+				break;
319
+			case EEM_Line_Item::OBJ_TYPE_TRANSACTION:
320
+				$obj_type = esc_html__('Transaction', 'event_espresso');
321
+				break;
322
+		}
323
+		return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
324
+	}
325
+
326
+
327
+	/**
328
+	 * Sets item_type
329
+	 *
330
+	 * @param string $OBJ_type
331
+	 * @throws EE_Error
332
+	 * @throws InvalidArgumentException
333
+	 * @throws InvalidDataTypeException
334
+	 * @throws InvalidInterfaceException
335
+	 * @throws ReflectionException
336
+	 */
337
+	public function set_OBJ_type($OBJ_type)
338
+	{
339
+		$this->set('OBJ_type', $OBJ_type);
340
+	}
341
+
342
+
343
+	/**
344
+	 * Gets unit_price
345
+	 *
346
+	 * @return float
347
+	 * @throws EE_Error
348
+	 * @throws InvalidArgumentException
349
+	 * @throws InvalidDataTypeException
350
+	 * @throws InvalidInterfaceException
351
+	 * @throws ReflectionException
352
+	 */
353
+	public function unit_price()
354
+	{
355
+		return $this->get('LIN_unit_price');
356
+	}
357
+
358
+
359
+	/**
360
+	 * Sets unit_price
361
+	 *
362
+	 * @param float $unit_price
363
+	 * @throws EE_Error
364
+	 * @throws InvalidArgumentException
365
+	 * @throws InvalidDataTypeException
366
+	 * @throws InvalidInterfaceException
367
+	 * @throws ReflectionException
368
+	 */
369
+	public function set_unit_price($unit_price)
370
+	{
371
+		$this->set('LIN_unit_price', $unit_price);
372
+	}
373
+
374
+
375
+	/**
376
+	 * Checks if this item is a percentage modifier or not
377
+	 *
378
+	 * @return boolean
379
+	 * @throws EE_Error
380
+	 * @throws InvalidArgumentException
381
+	 * @throws InvalidDataTypeException
382
+	 * @throws InvalidInterfaceException
383
+	 * @throws ReflectionException
384
+	 */
385
+	public function is_percent()
386
+	{
387
+		if ($this->is_tax_sub_total()) {
388
+			// tax subtotals HAVE a percent on them, that percentage only applies
389
+			// to taxable items, so its' an exception. Treat it like a flat line item
390
+			return false;
391
+		}
392
+		$unit_price = abs($this->get('LIN_unit_price'));
393
+		$percent = abs($this->get('LIN_percent'));
394
+		if ($unit_price < .001 && $percent) {
395
+			return true;
396
+		}
397
+		if ($unit_price >= .001 && ! $percent) {
398
+			return false;
399
+		}
400
+		if ($unit_price >= .001 && $percent) {
401
+			throw new EE_Error(
402
+				sprintf(
403
+					esc_html__(
404
+						'A Line Item can not have a unit price of (%s) AND a percent (%s)!',
405
+						'event_espresso'
406
+					),
407
+					$unit_price,
408
+					$percent
409
+				)
410
+			);
411
+		}
412
+		// if they're both 0, assume its not a percent item
413
+		return false;
414
+	}
415
+
416
+
417
+	/**
418
+	 * Gets percent (between 100-.001)
419
+	 *
420
+	 * @return float
421
+	 * @throws EE_Error
422
+	 * @throws InvalidArgumentException
423
+	 * @throws InvalidDataTypeException
424
+	 * @throws InvalidInterfaceException
425
+	 * @throws ReflectionException
426
+	 */
427
+	public function percent()
428
+	{
429
+		return $this->get('LIN_percent');
430
+	}
431
+
432
+
433
+	/**
434
+	 * Sets percent (between 100-0.01)
435
+	 *
436
+	 * @param float $percent
437
+	 * @throws EE_Error
438
+	 * @throws InvalidArgumentException
439
+	 * @throws InvalidDataTypeException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws ReflectionException
442
+	 */
443
+	public function set_percent($percent)
444
+	{
445
+		$this->set('LIN_percent', $percent);
446
+	}
447
+
448
+
449
+	/**
450
+	 * Gets total
451
+	 *
452
+	 * @return float
453
+	 * @throws EE_Error
454
+	 * @throws InvalidArgumentException
455
+	 * @throws InvalidDataTypeException
456
+	 * @throws InvalidInterfaceException
457
+	 * @throws ReflectionException
458
+	 */
459
+	public function total()
460
+	{
461
+		return $this->get('LIN_total');
462
+	}
463
+
464
+
465
+	/**
466
+	 * Sets total
467
+	 *
468
+	 * @param float $total
469
+	 * @throws EE_Error
470
+	 * @throws InvalidArgumentException
471
+	 * @throws InvalidDataTypeException
472
+	 * @throws InvalidInterfaceException
473
+	 * @throws ReflectionException
474
+	 */
475
+	public function set_total($total)
476
+	{
477
+		$this->set('LIN_total', $total);
478
+	}
479
+
480
+
481
+	/**
482
+	 * Gets order
483
+	 *
484
+	 * @return int
485
+	 * @throws EE_Error
486
+	 * @throws InvalidArgumentException
487
+	 * @throws InvalidDataTypeException
488
+	 * @throws InvalidInterfaceException
489
+	 * @throws ReflectionException
490
+	 */
491
+	public function order()
492
+	{
493
+		return $this->get('LIN_order');
494
+	}
495
+
496
+
497
+	/**
498
+	 * Sets order
499
+	 *
500
+	 * @param int $order
501
+	 * @throws EE_Error
502
+	 * @throws InvalidArgumentException
503
+	 * @throws InvalidDataTypeException
504
+	 * @throws InvalidInterfaceException
505
+	 * @throws ReflectionException
506
+	 */
507
+	public function set_order($order)
508
+	{
509
+		$this->set('LIN_order', $order);
510
+	}
511
+
512
+
513
+	/**
514
+	 * Gets parent
515
+	 *
516
+	 * @return int
517
+	 * @throws EE_Error
518
+	 * @throws InvalidArgumentException
519
+	 * @throws InvalidDataTypeException
520
+	 * @throws InvalidInterfaceException
521
+	 * @throws ReflectionException
522
+	 */
523
+	public function parent_ID()
524
+	{
525
+		return $this->get('LIN_parent');
526
+	}
527
+
528
+
529
+	/**
530
+	 * Sets parent
531
+	 *
532
+	 * @param int $parent
533
+	 * @throws EE_Error
534
+	 * @throws InvalidArgumentException
535
+	 * @throws InvalidDataTypeException
536
+	 * @throws InvalidInterfaceException
537
+	 * @throws ReflectionException
538
+	 */
539
+	public function set_parent_ID($parent)
540
+	{
541
+		$this->set('LIN_parent', $parent);
542
+	}
543
+
544
+
545
+	/**
546
+	 * Gets type
547
+	 *
548
+	 * @return string
549
+	 * @throws EE_Error
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidDataTypeException
552
+	 * @throws InvalidInterfaceException
553
+	 * @throws ReflectionException
554
+	 */
555
+	public function type()
556
+	{
557
+		return $this->get('LIN_type');
558
+	}
559
+
560
+
561
+	/**
562
+	 * Sets type
563
+	 *
564
+	 * @param string $type
565
+	 * @throws EE_Error
566
+	 * @throws InvalidArgumentException
567
+	 * @throws InvalidDataTypeException
568
+	 * @throws InvalidInterfaceException
569
+	 * @throws ReflectionException
570
+	 */
571
+	public function set_type($type)
572
+	{
573
+		$this->set('LIN_type', $type);
574
+	}
575
+
576
+
577
+	/**
578
+	 * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
579
+	 * 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
580
+	 * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
581
+	 * or indirectly by `EE_Line_item::add_child_line_item()`)
582
+	 *
583
+	 * @return EE_Base_Class|EE_Line_Item
584
+	 * @throws EE_Error
585
+	 * @throws InvalidArgumentException
586
+	 * @throws InvalidDataTypeException
587
+	 * @throws InvalidInterfaceException
588
+	 * @throws ReflectionException
589
+	 */
590
+	public function parent()
591
+	{
592
+		return $this->ID()
593
+			? $this->get_model()->get_one_by_ID($this->parent_ID())
594
+			: $this->_parent;
595
+	}
596
+
597
+
598
+	/**
599
+	 * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
600
+	 *
601
+	 * @return EE_Base_Class[]|EE_Line_Item[]
602
+	 * @throws EE_Error
603
+	 * @throws InvalidArgumentException
604
+	 * @throws InvalidDataTypeException
605
+	 * @throws InvalidInterfaceException
606
+	 * @throws ReflectionException
607
+	 */
608
+	public function children()
609
+	{
610
+		if ($this->ID()) {
611
+			return $this->get_model()->get_all(
612
+				array(
613
+					array('LIN_parent' => $this->ID()),
614
+					'order_by' => array('LIN_order' => 'ASC'),
615
+				)
616
+			);
617
+		}
618
+		if (! is_array($this->_children)) {
619
+			$this->_children = array();
620
+		}
621
+		return $this->_children;
622
+	}
623
+
624
+
625
+	/**
626
+	 * Gets code
627
+	 *
628
+	 * @return string
629
+	 * @throws EE_Error
630
+	 * @throws InvalidArgumentException
631
+	 * @throws InvalidDataTypeException
632
+	 * @throws InvalidInterfaceException
633
+	 * @throws ReflectionException
634
+	 */
635
+	public function code()
636
+	{
637
+		return $this->get('LIN_code');
638
+	}
639
+
640
+
641
+	/**
642
+	 * Sets code
643
+	 *
644
+	 * @param string $code
645
+	 * @throws EE_Error
646
+	 * @throws InvalidArgumentException
647
+	 * @throws InvalidDataTypeException
648
+	 * @throws InvalidInterfaceException
649
+	 * @throws ReflectionException
650
+	 */
651
+	public function set_code($code)
652
+	{
653
+		$this->set('LIN_code', $code);
654
+	}
655
+
656
+
657
+	/**
658
+	 * Gets is_taxable
659
+	 *
660
+	 * @return boolean
661
+	 * @throws EE_Error
662
+	 * @throws InvalidArgumentException
663
+	 * @throws InvalidDataTypeException
664
+	 * @throws InvalidInterfaceException
665
+	 * @throws ReflectionException
666
+	 */
667
+	public function is_taxable()
668
+	{
669
+		return $this->get('LIN_is_taxable');
670
+	}
671
+
672
+
673
+	/**
674
+	 * Sets is_taxable
675
+	 *
676
+	 * @param boolean $is_taxable
677
+	 * @throws EE_Error
678
+	 * @throws InvalidArgumentException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws InvalidInterfaceException
681
+	 * @throws ReflectionException
682
+	 */
683
+	public function set_is_taxable($is_taxable)
684
+	{
685
+		$this->set('LIN_is_taxable', $is_taxable);
686
+	}
687
+
688
+
689
+	/**
690
+	 * Gets the object that this model-joins-to.
691
+	 * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
692
+	 * EEM_Promotion_Object
693
+	 *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
694
+	 *
695
+	 * @return EE_Base_Class | NULL
696
+	 * @throws EE_Error
697
+	 * @throws InvalidArgumentException
698
+	 * @throws InvalidDataTypeException
699
+	 * @throws InvalidInterfaceException
700
+	 * @throws ReflectionException
701
+	 */
702
+	public function get_object()
703
+	{
704
+		$model_name_of_related_obj = $this->OBJ_type();
705
+		return $this->get_model()->has_relation($model_name_of_related_obj)
706
+			? $this->get_first_related($model_name_of_related_obj)
707
+			: null;
708
+	}
709
+
710
+
711
+	/**
712
+	 * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
713
+	 * (IE, if this line item is for a price or something else, will return NULL)
714
+	 *
715
+	 * @param array $query_params
716
+	 * @return EE_Base_Class|EE_Ticket
717
+	 * @throws EE_Error
718
+	 * @throws InvalidArgumentException
719
+	 * @throws InvalidDataTypeException
720
+	 * @throws InvalidInterfaceException
721
+	 * @throws ReflectionException
722
+	 */
723
+	public function ticket($query_params = array())
724
+	{
725
+		// we're going to assume that when this method is called
726
+		// we always want to receive the attached ticket EVEN if that ticket is archived.
727
+		// This can be overridden via the incoming $query_params argument
728
+		$remove_defaults = array('default_where_conditions' => 'none');
729
+		$query_params = array_merge($remove_defaults, $query_params);
730
+		return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TICKET, $query_params);
731
+	}
732
+
733
+
734
+	/**
735
+	 * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
736
+	 *
737
+	 * @return EE_Datetime | NULL
738
+	 * @throws EE_Error
739
+	 * @throws InvalidArgumentException
740
+	 * @throws InvalidDataTypeException
741
+	 * @throws InvalidInterfaceException
742
+	 * @throws ReflectionException
743
+	 */
744
+	public function get_ticket_datetime()
745
+	{
746
+		if ($this->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
747
+			$ticket = $this->ticket();
748
+			if ($ticket instanceof EE_Ticket) {
749
+				$datetime = $ticket->first_datetime();
750
+				if ($datetime instanceof EE_Datetime) {
751
+					return $datetime;
752
+				}
753
+			}
754
+		}
755
+		return null;
756
+	}
757
+
758
+
759
+	/**
760
+	 * Gets the event's name that's related to the ticket, if this is for
761
+	 * a ticket
762
+	 *
763
+	 * @return string
764
+	 * @throws EE_Error
765
+	 * @throws InvalidArgumentException
766
+	 * @throws InvalidDataTypeException
767
+	 * @throws InvalidInterfaceException
768
+	 * @throws ReflectionException
769
+	 */
770
+	public function ticket_event_name()
771
+	{
772
+		$event_name = esc_html__('Unknown', 'event_espresso');
773
+		$event = $this->ticket_event();
774
+		if ($event instanceof EE_Event) {
775
+			$event_name = $event->name();
776
+		}
777
+		return $event_name;
778
+	}
779
+
780
+
781
+	/**
782
+	 * Gets the event that's related to the ticket, if this line item represents a ticket.
783
+	 *
784
+	 * @return EE_Event|null
785
+	 * @throws EE_Error
786
+	 * @throws InvalidArgumentException
787
+	 * @throws InvalidDataTypeException
788
+	 * @throws InvalidInterfaceException
789
+	 * @throws ReflectionException
790
+	 */
791
+	public function ticket_event()
792
+	{
793
+		$event = null;
794
+		$ticket = $this->ticket();
795
+		if ($ticket instanceof EE_Ticket) {
796
+			$datetime = $ticket->first_datetime();
797
+			if ($datetime instanceof EE_Datetime) {
798
+				$event = $datetime->event();
799
+			}
800
+		}
801
+		return $event;
802
+	}
803
+
804
+
805
+	/**
806
+	 * Gets the first datetime for this lien item, assuming it's for a ticket
807
+	 *
808
+	 * @param string $date_format
809
+	 * @param string $time_format
810
+	 * @return string
811
+	 * @throws EE_Error
812
+	 * @throws InvalidArgumentException
813
+	 * @throws InvalidDataTypeException
814
+	 * @throws InvalidInterfaceException
815
+	 * @throws ReflectionException
816
+	 */
817
+	public function ticket_datetime_start($date_format = '', $time_format = '')
818
+	{
819
+		$first_datetime_string = esc_html__('Unknown', 'event_espresso');
820
+		$datetime = $this->get_ticket_datetime();
821
+		if ($datetime) {
822
+			$first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
823
+		}
824
+		return $first_datetime_string;
825
+	}
826
+
827
+
828
+	/**
829
+	 * Adds the line item as a child to this line item. If there is another child line
830
+	 * item with the same LIN_code, it is overwritten by this new one
831
+	 *
832
+	 * @param EEI_Line_Item $line_item
833
+	 * @param bool          $set_order
834
+	 * @return bool success
835
+	 * @throws EE_Error
836
+	 * @throws InvalidArgumentException
837
+	 * @throws InvalidDataTypeException
838
+	 * @throws InvalidInterfaceException
839
+	 * @throws ReflectionException
840
+	 */
841
+	public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
842
+	{
843
+		// should we calculate the LIN_order for this line item ?
844
+		if ($set_order || $line_item->order() === null) {
845
+			$line_item->set_order(count($this->children()));
846
+		}
847
+		if ($this->ID()) {
848
+			// check for any duplicate line items (with the same code), if so, this replaces it
849
+			$line_item_with_same_code = $this->get_child_line_item($line_item->code());
850
+			if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
851
+				$this->delete_child_line_item($line_item_with_same_code->code());
852
+			}
853
+			$line_item->set_parent_ID($this->ID());
854
+			if ($this->TXN_ID()) {
855
+				$line_item->set_TXN_ID($this->TXN_ID());
856
+			}
857
+			return $line_item->save();
858
+		}
859
+		$this->_children[ $line_item->code() ] = $line_item;
860
+		if ($line_item->parent() !== $this) {
861
+			$line_item->set_parent($this);
862
+		}
863
+		return true;
864
+	}
865
+
866
+
867
+	/**
868
+	 * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
869
+	 * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
870
+	 * However, if this line item is NOT saved to the DB, this just caches the parent on
871
+	 * the EE_Line_Item::_parent property.
872
+	 *
873
+	 * @param EE_Line_Item $line_item
874
+	 * @throws EE_Error
875
+	 * @throws InvalidArgumentException
876
+	 * @throws InvalidDataTypeException
877
+	 * @throws InvalidInterfaceException
878
+	 * @throws ReflectionException
879
+	 */
880
+	public function set_parent($line_item)
881
+	{
882
+		if ($this->ID()) {
883
+			if (! $line_item->ID()) {
884
+				$line_item->save();
885
+			}
886
+			$this->set_parent_ID($line_item->ID());
887
+			$this->save();
888
+		} else {
889
+			$this->_parent = $line_item;
890
+			$this->set_parent_ID($line_item->ID());
891
+		}
892
+	}
893
+
894
+
895
+	/**
896
+	 * Gets the child line item as specified by its code. Because this returns an object (by reference)
897
+	 * you can modify this child line item and the parent (this object) can know about them
898
+	 * because it also has a reference to that line item
899
+	 *
900
+	 * @param string $code
901
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
902
+	 * @throws EE_Error
903
+	 * @throws InvalidArgumentException
904
+	 * @throws InvalidDataTypeException
905
+	 * @throws InvalidInterfaceException
906
+	 * @throws ReflectionException
907
+	 */
908
+	public function get_child_line_item($code)
909
+	{
910
+		if ($this->ID()) {
911
+			return $this->get_model()->get_one(
912
+				array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
913
+			);
914
+		}
915
+		return isset($this->_children[ $code ])
916
+			? $this->_children[ $code ]
917
+			: null;
918
+	}
919
+
920
+
921
+	/**
922
+	 * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
923
+	 * cached on it)
924
+	 *
925
+	 * @return int
926
+	 * @throws EE_Error
927
+	 * @throws InvalidArgumentException
928
+	 * @throws InvalidDataTypeException
929
+	 * @throws InvalidInterfaceException
930
+	 * @throws ReflectionException
931
+	 */
932
+	public function delete_children_line_items()
933
+	{
934
+		if ($this->ID()) {
935
+			return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
936
+		}
937
+		$count = count($this->_children);
938
+		$this->_children = array();
939
+		return $count;
940
+	}
941
+
942
+
943
+	/**
944
+	 * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
945
+	 * HAS NOT been saved to the DB, removes the child line item with index $code.
946
+	 * Also searches through the child's children for a matching line item. However, once a line item has been found
947
+	 * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
948
+	 * deleted)
949
+	 *
950
+	 * @param string $code
951
+	 * @param bool   $stop_search_once_found
952
+	 * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
953
+	 *             the DB yet)
954
+	 * @throws EE_Error
955
+	 * @throws InvalidArgumentException
956
+	 * @throws InvalidDataTypeException
957
+	 * @throws InvalidInterfaceException
958
+	 * @throws ReflectionException
959
+	 */
960
+	public function delete_child_line_item($code, $stop_search_once_found = true)
961
+	{
962
+		if ($this->ID()) {
963
+			$items_deleted = 0;
964
+			if ($this->code() === $code) {
965
+				$items_deleted += EEH_Line_Item::delete_all_child_items($this);
966
+				$items_deleted += (int) $this->delete();
967
+				if ($stop_search_once_found) {
968
+					return $items_deleted;
969
+				}
970
+			}
971
+			foreach ($this->children() as $child_line_item) {
972
+				$items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
973
+			}
974
+			return $items_deleted;
975
+		}
976
+		if (isset($this->_children[ $code ])) {
977
+			unset($this->_children[ $code ]);
978
+			return 1;
979
+		}
980
+		return 0;
981
+	}
982
+
983
+
984
+	/**
985
+	 * If this line item is in the database, is of the type subtotal, and
986
+	 * has no children, why do we have it? It should be deleted so this function
987
+	 * does that
988
+	 *
989
+	 * @return boolean
990
+	 * @throws EE_Error
991
+	 * @throws InvalidArgumentException
992
+	 * @throws InvalidDataTypeException
993
+	 * @throws InvalidInterfaceException
994
+	 * @throws ReflectionException
995
+	 */
996
+	public function delete_if_childless_subtotal()
997
+	{
998
+		if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
999
+			return $this->delete();
1000
+		}
1001
+		return false;
1002
+	}
1003
+
1004
+
1005
+	/**
1006
+	 * Creates a code and returns a string. doesn't assign the code to this model object
1007
+	 *
1008
+	 * @return string
1009
+	 * @throws EE_Error
1010
+	 * @throws InvalidArgumentException
1011
+	 * @throws InvalidDataTypeException
1012
+	 * @throws InvalidInterfaceException
1013
+	 * @throws ReflectionException
1014
+	 */
1015
+	public function generate_code()
1016
+	{
1017
+		// each line item in the cart requires a unique identifier
1018
+		return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1019
+	}
1020
+
1021
+
1022
+	/**
1023
+	 * @return bool
1024
+	 * @throws EE_Error
1025
+	 * @throws InvalidArgumentException
1026
+	 * @throws InvalidDataTypeException
1027
+	 * @throws InvalidInterfaceException
1028
+	 * @throws ReflectionException
1029
+	 */
1030
+	public function is_tax()
1031
+	{
1032
+		return $this->type() === EEM_Line_Item::type_tax;
1033
+	}
1034
+
1035
+
1036
+	/**
1037
+	 * @return bool
1038
+	 * @throws EE_Error
1039
+	 * @throws InvalidArgumentException
1040
+	 * @throws InvalidDataTypeException
1041
+	 * @throws InvalidInterfaceException
1042
+	 * @throws ReflectionException
1043
+	 */
1044
+	public function is_tax_sub_total()
1045
+	{
1046
+		return $this->type() === EEM_Line_Item::type_tax_sub_total;
1047
+	}
1048
+
1049
+
1050
+	/**
1051
+	 * @return bool
1052
+	 * @throws EE_Error
1053
+	 * @throws InvalidArgumentException
1054
+	 * @throws InvalidDataTypeException
1055
+	 * @throws InvalidInterfaceException
1056
+	 * @throws ReflectionException
1057
+	 */
1058
+	public function is_line_item()
1059
+	{
1060
+		return $this->type() === EEM_Line_Item::type_line_item;
1061
+	}
1062
+
1063
+
1064
+	/**
1065
+	 * @return bool
1066
+	 * @throws EE_Error
1067
+	 * @throws InvalidArgumentException
1068
+	 * @throws InvalidDataTypeException
1069
+	 * @throws InvalidInterfaceException
1070
+	 * @throws ReflectionException
1071
+	 */
1072
+	public function is_sub_line_item()
1073
+	{
1074
+		return $this->type() === EEM_Line_Item::type_sub_line_item;
1075
+	}
1076
+
1077
+
1078
+	/**
1079
+	 * @return bool
1080
+	 * @throws EE_Error
1081
+	 * @throws InvalidArgumentException
1082
+	 * @throws InvalidDataTypeException
1083
+	 * @throws InvalidInterfaceException
1084
+	 * @throws ReflectionException
1085
+	 */
1086
+	public function is_sub_total()
1087
+	{
1088
+		return $this->type() === EEM_Line_Item::type_sub_total;
1089
+	}
1090
+
1091
+
1092
+	/**
1093
+	 * Whether or not this line item is a cancellation line item
1094
+	 *
1095
+	 * @return boolean
1096
+	 * @throws EE_Error
1097
+	 * @throws InvalidArgumentException
1098
+	 * @throws InvalidDataTypeException
1099
+	 * @throws InvalidInterfaceException
1100
+	 * @throws ReflectionException
1101
+	 */
1102
+	public function is_cancellation()
1103
+	{
1104
+		return EEM_Line_Item::type_cancellation === $this->type();
1105
+	}
1106
+
1107
+
1108
+	/**
1109
+	 * @return bool
1110
+	 * @throws EE_Error
1111
+	 * @throws InvalidArgumentException
1112
+	 * @throws InvalidDataTypeException
1113
+	 * @throws InvalidInterfaceException
1114
+	 * @throws ReflectionException
1115
+	 */
1116
+	public function is_total()
1117
+	{
1118
+		return $this->type() === EEM_Line_Item::type_total;
1119
+	}
1120
+
1121
+
1122
+	/**
1123
+	 * @return bool
1124
+	 * @throws EE_Error
1125
+	 * @throws InvalidArgumentException
1126
+	 * @throws InvalidDataTypeException
1127
+	 * @throws InvalidInterfaceException
1128
+	 * @throws ReflectionException
1129
+	 */
1130
+	public function is_cancelled()
1131
+	{
1132
+		return $this->type() === EEM_Line_Item::type_cancellation;
1133
+	}
1134
+
1135
+
1136
+	/**
1137
+	 * @return string like '2, 004.00', formatted according to the localized currency
1138
+	 * @throws EE_Error
1139
+	 * @throws InvalidArgumentException
1140
+	 * @throws InvalidDataTypeException
1141
+	 * @throws InvalidInterfaceException
1142
+	 * @throws ReflectionException
1143
+	 */
1144
+	public function unit_price_no_code()
1145
+	{
1146
+		return $this->get_pretty('LIN_unit_price', 'no_currency_code');
1147
+	}
1148
+
1149
+
1150
+	/**
1151
+	 * @return string like '2, 004.00', formatted according to the localized currency
1152
+	 * @throws EE_Error
1153
+	 * @throws InvalidArgumentException
1154
+	 * @throws InvalidDataTypeException
1155
+	 * @throws InvalidInterfaceException
1156
+	 * @throws ReflectionException
1157
+	 */
1158
+	public function total_no_code()
1159
+	{
1160
+		return $this->get_pretty('LIN_total', 'no_currency_code');
1161
+	}
1162
+
1163
+
1164
+	/**
1165
+	 * Gets the final total on this item, taking taxes into account.
1166
+	 * Has the side-effect of setting the sub-total as it was just calculated.
1167
+	 * If this is used on a grand-total line item, also updates the transaction's
1168
+	 * TXN_total (provided this line item is allowed to persist, otherwise we don't
1169
+	 * want to change a persistable transaction with info from a non-persistent line item)
1170
+	 *
1171
+	 * @param bool $update_txn_status
1172
+	 * @return float
1173
+	 * @throws EE_Error
1174
+	 * @throws InvalidArgumentException
1175
+	 * @throws InvalidDataTypeException
1176
+	 * @throws InvalidInterfaceException
1177
+	 * @throws ReflectionException
1178
+	 * @throws RuntimeException
1179
+	 */
1180
+	public function recalculate_total_including_taxes($update_txn_status = false)
1181
+	{
1182
+		$pre_tax_total = $this->recalculate_pre_tax_total();
1183
+		$tax_total = $this->recalculate_taxes_and_tax_total();
1184
+		$total = $pre_tax_total + $tax_total;
1185
+		// no negative totals plz
1186
+		$total = max($total, 0);
1187
+		$this->set_total($total);
1188
+		// only update the related transaction's total
1189
+		// if we intend to save this line item and its a grand total
1190
+		if ($this->allow_persist() && $this->type() === EEM_Line_Item::type_total
1191
+			&& $this->transaction()
1192
+			   instanceof
1193
+			   EE_Transaction
1194
+		) {
1195
+			$this->transaction()->set_total($total);
1196
+			if ($update_txn_status) {
1197
+				// don't save the TXN because that will be done below
1198
+				// and the following method only saves if the status changes
1199
+				$this->transaction()->update_status_based_on_total_paid(false);
1200
+			}
1201
+			if ($this->transaction()->ID()) {
1202
+				$this->transaction()->save();
1203
+			}
1204
+		}
1205
+		$this->maybe_save();
1206
+		return $total;
1207
+	}
1208
+
1209
+
1210
+	/**
1211
+	 * Recursively goes through all the children and recalculates sub-totals EXCEPT for
1212
+	 * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
1213
+	 * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
1214
+	 * when this is called on the grand total
1215
+	 *
1216
+	 * @return float
1217
+	 * @throws EE_Error
1218
+	 * @throws InvalidArgumentException
1219
+	 * @throws InvalidDataTypeException
1220
+	 * @throws InvalidInterfaceException
1221
+	 * @throws ReflectionException
1222
+	 */
1223
+	public function recalculate_pre_tax_total()
1224
+	{
1225
+		$total = 0;
1226
+		$my_children = $this->children();
1227
+		$has_children = ! empty($my_children);
1228
+		if ($has_children && $this->is_line_item()) {
1229
+			$total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
1230
+		} elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
1231
+			$total = $this->unit_price() * $this->quantity();
1232
+		} elseif ($this->is_sub_total() || $this->is_total()) {
1233
+			$total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
1234
+		} elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
1235
+			// completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
1236
+			return 0;
1237
+		}
1238
+		// ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
1239
+		if (! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
1240
+		) {
1241
+			if ($this->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_EVENT) {
1242
+				$this->set_quantity(1);
1243
+			}
1244
+			if (! $this->is_percent()) {
1245
+				$this->set_unit_price($total);
1246
+			}
1247
+		}
1248
+		// we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1249
+		// so it ought to be
1250
+		if (! $this->is_total()) {
1251
+			$this->set_total($total);
1252
+			// if not a percent line item, make sure we keep the unit price in sync
1253
+			if ($has_children
1254
+				&& $this->is_line_item()
1255
+				&& ! $this->is_percent()
1256
+			) {
1257
+				if ($this->quantity() === 0) {
1258
+					$new_unit_price = 0;
1259
+				} else {
1260
+					$new_unit_price = $this->total() / $this->quantity();
1261
+				}
1262
+				$this->set_unit_price($new_unit_price);
1263
+			}
1264
+			$this->maybe_save();
1265
+		}
1266
+		return $total;
1267
+	}
1268
+
1269
+
1270
+	/**
1271
+	 * Calculates the pretax total when this line item is a subtotal or total line item.
1272
+	 * Basically does a sum-then-round approach (ie, any percent line item that are children
1273
+	 * will calculate their total based on the un-rounded total we're working with so far, and
1274
+	 * THEN round the result; instead of rounding as we go like with sub-line-items)
1275
+	 *
1276
+	 * @param float          $calculated_total_so_far
1277
+	 * @param EE_Line_Item[] $my_children
1278
+	 * @return float
1279
+	 * @throws EE_Error
1280
+	 * @throws InvalidArgumentException
1281
+	 * @throws InvalidDataTypeException
1282
+	 * @throws InvalidInterfaceException
1283
+	 * @throws ReflectionException
1284
+	 */
1285
+	protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1286
+	{
1287
+		if ($my_children === null) {
1288
+			$my_children = $this->children();
1289
+		}
1290
+		$subtotal_quantity = 0;
1291
+		// get the total of all its children
1292
+		foreach ($my_children as $child_line_item) {
1293
+			if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1294
+				// percentage line items are based on total so far
1295
+				if ($child_line_item->is_percent()) {
1296
+					// round as we go so that the line items add up ok
1297
+					$percent_total = round(
1298
+						$calculated_total_so_far * $child_line_item->percent() / 100,
1299
+						EE_Registry::instance()->CFG->currency->dec_plc
1300
+					);
1301
+					$child_line_item->set_total($percent_total);
1302
+					// so far all percent line items should have a quantity of 1
1303
+					// (ie, no double percent discounts. Although that might be requested someday)
1304
+					$child_line_item->set_quantity(1);
1305
+					$child_line_item->maybe_save();
1306
+					$calculated_total_so_far += $percent_total;
1307
+				} else {
1308
+					// verify flat sub-line-item quantities match their parent
1309
+					if ($child_line_item->is_sub_line_item()) {
1310
+						$child_line_item->set_quantity($this->quantity());
1311
+					}
1312
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1313
+					$subtotal_quantity += $child_line_item->quantity();
1314
+				}
1315
+			}
1316
+		}
1317
+		if ($this->is_sub_total()) {
1318
+			// no negative totals plz
1319
+			$calculated_total_so_far = max($calculated_total_so_far, 0);
1320
+			$subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1321
+			$this->set_quantity($subtotal_quantity);
1322
+			$this->maybe_save();
1323
+		}
1324
+		return $calculated_total_so_far;
1325
+	}
1326
+
1327
+
1328
+	/**
1329
+	 * Calculates the pretax total for a normal line item, in a round-then-sum approach
1330
+	 * (where each sub-line-item is applied to the base price for the line item
1331
+	 * and the result is immediately rounded, rather than summing all the sub-line-items
1332
+	 * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1333
+	 *
1334
+	 * @param float          $calculated_total_so_far
1335
+	 * @param EE_Line_Item[] $my_children
1336
+	 * @return float
1337
+	 * @throws EE_Error
1338
+	 * @throws InvalidArgumentException
1339
+	 * @throws InvalidDataTypeException
1340
+	 * @throws InvalidInterfaceException
1341
+	 * @throws ReflectionException
1342
+	 */
1343
+	protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1344
+	{
1345
+		if ($my_children === null) {
1346
+			$my_children = $this->children();
1347
+		}
1348
+		// we need to keep track of the running total for a single item,
1349
+		// because we need to round as we go
1350
+		$unit_price_for_total = 0;
1351
+		$quantity_for_total = 1;
1352
+		// get the total of all its children
1353
+		foreach ($my_children as $child_line_item) {
1354
+			if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1355
+				if ($child_line_item->is_percent()) {
1356
+					// it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1357
+					// not total multiplied by percent, because that ignores rounding along-the-way
1358
+					$percent_unit_price = round(
1359
+						$unit_price_for_total * $child_line_item->percent() / 100,
1360
+						EE_Registry::instance()->CFG->currency->dec_plc
1361
+					);
1362
+					$percent_total = $percent_unit_price * $quantity_for_total;
1363
+					$child_line_item->set_total($percent_total);
1364
+					// so far all percent line items should have a quantity of 1
1365
+					// (ie, no double percent discounts. Although that might be requested someday)
1366
+					$child_line_item->set_quantity(1);
1367
+					$child_line_item->maybe_save();
1368
+					$calculated_total_so_far += $percent_total;
1369
+					$unit_price_for_total += $percent_unit_price;
1370
+				} else {
1371
+					// verify flat sub-line-item quantities match their parent
1372
+					if ($child_line_item->is_sub_line_item()) {
1373
+						$child_line_item->set_quantity($this->quantity());
1374
+					}
1375
+					$quantity_for_total = $child_line_item->quantity();
1376
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1377
+					$unit_price_for_total += $child_line_item->unit_price();
1378
+				}
1379
+			}
1380
+		}
1381
+		return $calculated_total_so_far;
1382
+	}
1383
+
1384
+
1385
+	/**
1386
+	 * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1387
+	 * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1388
+	 * and tax sub-total if already in the DB
1389
+	 *
1390
+	 * @return float
1391
+	 * @throws EE_Error
1392
+	 * @throws InvalidArgumentException
1393
+	 * @throws InvalidDataTypeException
1394
+	 * @throws InvalidInterfaceException
1395
+	 * @throws ReflectionException
1396
+	 */
1397
+	public function recalculate_taxes_and_tax_total()
1398
+	{
1399
+		// get all taxes
1400
+		$taxes = $this->tax_descendants();
1401
+		// calculate the pretax total
1402
+		$taxable_total = $this->taxable_total();
1403
+		$tax_total = 0;
1404
+		foreach ($taxes as $tax) {
1405
+			$total_on_this_tax = $taxable_total * $tax->percent() / 100;
1406
+			// remember the total on this line item
1407
+			$tax->set_total($total_on_this_tax);
1408
+			$tax->maybe_save();
1409
+			$tax_total += $tax->total();
1410
+		}
1411
+		$this->_recalculate_tax_sub_total();
1412
+		return $tax_total;
1413
+	}
1414
+
1415
+
1416
+	/**
1417
+	 * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1418
+	 *
1419
+	 * @return void
1420
+	 * @throws EE_Error
1421
+	 * @throws InvalidArgumentException
1422
+	 * @throws InvalidDataTypeException
1423
+	 * @throws InvalidInterfaceException
1424
+	 * @throws ReflectionException
1425
+	 */
1426
+	private function _recalculate_tax_sub_total()
1427
+	{
1428
+		if ($this->is_tax_sub_total()) {
1429
+			$total = 0;
1430
+			$total_percent = 0;
1431
+			// simply loop through all its children (which should be taxes) and sum their total
1432
+			foreach ($this->children() as $child_tax) {
1433
+				if ($child_tax instanceof EE_Line_Item) {
1434
+					$total += $child_tax->total();
1435
+					$total_percent += $child_tax->percent();
1436
+				}
1437
+			}
1438
+			$this->set_total($total);
1439
+			$this->set_percent($total_percent);
1440
+			$this->maybe_save();
1441
+		} elseif ($this->is_total()) {
1442
+			foreach ($this->children() as $maybe_tax_subtotal) {
1443
+				if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1444
+					$maybe_tax_subtotal->_recalculate_tax_sub_total();
1445
+				}
1446
+			}
1447
+		}
1448
+	}
1449
+
1450
+
1451
+	/**
1452
+	 * Gets the total tax on this line item. Assumes taxes have already been calculated using
1453
+	 * recalculate_taxes_and_total
1454
+	 *
1455
+	 * @return float
1456
+	 * @throws EE_Error
1457
+	 * @throws InvalidArgumentException
1458
+	 * @throws InvalidDataTypeException
1459
+	 * @throws InvalidInterfaceException
1460
+	 * @throws ReflectionException
1461
+	 */
1462
+	public function get_total_tax()
1463
+	{
1464
+		$this->_recalculate_tax_sub_total();
1465
+		$total = 0;
1466
+		foreach ($this->tax_descendants() as $tax_line_item) {
1467
+			if ($tax_line_item instanceof EE_Line_Item) {
1468
+				$total += $tax_line_item->total();
1469
+			}
1470
+		}
1471
+		return $total;
1472
+	}
1473
+
1474
+
1475
+	/**
1476
+	 * Gets the total for all the items purchased only
1477
+	 *
1478
+	 * @return float
1479
+	 * @throws EE_Error
1480
+	 * @throws InvalidArgumentException
1481
+	 * @throws InvalidDataTypeException
1482
+	 * @throws InvalidInterfaceException
1483
+	 * @throws ReflectionException
1484
+	 */
1485
+	public function get_items_total()
1486
+	{
1487
+		// by default, let's make sure we're consistent with the existing line item
1488
+		if ($this->is_total()) {
1489
+			$pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1490
+			if ($pretax_subtotal_li instanceof EE_Line_Item) {
1491
+				return $pretax_subtotal_li->total();
1492
+			}
1493
+		}
1494
+		$total = 0;
1495
+		foreach ($this->get_items() as $item) {
1496
+			if ($item instanceof EE_Line_Item) {
1497
+				$total += $item->total();
1498
+			}
1499
+		}
1500
+		return $total;
1501
+	}
1502
+
1503
+
1504
+	/**
1505
+	 * Gets all the descendants (ie, children or children of children etc) that
1506
+	 * are of the type 'tax'
1507
+	 *
1508
+	 * @return EE_Line_Item[]
1509
+	 * @throws EE_Error
1510
+	 */
1511
+	public function tax_descendants()
1512
+	{
1513
+		return EEH_Line_Item::get_tax_descendants($this);
1514
+	}
1515
+
1516
+
1517
+	/**
1518
+	 * Gets all the real items purchased which are children of this item
1519
+	 *
1520
+	 * @return EE_Line_Item[]
1521
+	 * @throws EE_Error
1522
+	 */
1523
+	public function get_items()
1524
+	{
1525
+		return EEH_Line_Item::get_line_item_descendants($this);
1526
+	}
1527
+
1528
+
1529
+	/**
1530
+	 * Returns the amount taxable among this line item's children (or if it has no children,
1531
+	 * how much of it is taxable). Does not recalculate totals or subtotals.
1532
+	 * If the taxable total is negative, (eg, if none of the tickets were taxable,
1533
+	 * but there is a "Taxable" discount), returns 0.
1534
+	 *
1535
+	 * @return float
1536
+	 * @throws EE_Error
1537
+	 * @throws InvalidArgumentException
1538
+	 * @throws InvalidDataTypeException
1539
+	 * @throws InvalidInterfaceException
1540
+	 * @throws ReflectionException
1541
+	 */
1542
+	public function taxable_total()
1543
+	{
1544
+		$total = 0;
1545
+		if ($this->children()) {
1546
+			foreach ($this->children() as $child_line_item) {
1547
+				if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1548
+					// if it's a percent item, only take into account the percent
1549
+					// that's taxable too (the taxable total so far)
1550
+					if ($child_line_item->is_percent()) {
1551
+						$total += ($total * $child_line_item->percent() / 100);
1552
+					} else {
1553
+						$total += $child_line_item->total();
1554
+					}
1555
+				} elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1556
+					$total += $child_line_item->taxable_total();
1557
+				} elseif ($child_line_item->is_percent()) {
1558
+					// Taxes should be charged on the discounted price.
1559
+					$total += $child_line_item->total();
1560
+				}
1561
+			}
1562
+		}
1563
+		return max($total, 0);
1564
+	}
1565
+
1566
+
1567
+	/**
1568
+	 * Gets the transaction for this line item
1569
+	 *
1570
+	 * @return EE_Base_Class|EE_Transaction
1571
+	 * @throws EE_Error
1572
+	 * @throws InvalidArgumentException
1573
+	 * @throws InvalidDataTypeException
1574
+	 * @throws InvalidInterfaceException
1575
+	 * @throws ReflectionException
1576
+	 */
1577
+	public function transaction()
1578
+	{
1579
+		return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TRANSACTION);
1580
+	}
1581
+
1582
+
1583
+	/**
1584
+	 * Saves this line item to the DB, and recursively saves its descendants.
1585
+	 * Because there currently is no proper parent-child relation on the model,
1586
+	 * save_this_and_cached() will NOT save the descendants.
1587
+	 * Also sets the transaction on this line item and all its descendants before saving
1588
+	 *
1589
+	 * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1590
+	 * @return int count of items saved
1591
+	 * @throws EE_Error
1592
+	 * @throws InvalidArgumentException
1593
+	 * @throws InvalidDataTypeException
1594
+	 * @throws InvalidInterfaceException
1595
+	 * @throws ReflectionException
1596
+	 */
1597
+	public function save_this_and_descendants_to_txn($txn_id = null)
1598
+	{
1599
+		$count = 0;
1600
+		if (! $txn_id) {
1601
+			$txn_id = $this->TXN_ID();
1602
+		}
1603
+		$this->set_TXN_ID($txn_id);
1604
+		$children = $this->children();
1605
+		$count += $this->save()
1606
+			? 1
1607
+			: 0;
1608
+		foreach ($children as $child_line_item) {
1609
+			if ($child_line_item instanceof EE_Line_Item) {
1610
+				$child_line_item->set_parent_ID($this->ID());
1611
+				$count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1612
+			}
1613
+		}
1614
+		return $count;
1615
+	}
1616
+
1617
+
1618
+	/**
1619
+	 * Saves this line item to the DB, and recursively saves its descendants.
1620
+	 *
1621
+	 * @return int count of items saved
1622
+	 * @throws EE_Error
1623
+	 * @throws InvalidArgumentException
1624
+	 * @throws InvalidDataTypeException
1625
+	 * @throws InvalidInterfaceException
1626
+	 * @throws ReflectionException
1627
+	 */
1628
+	public function save_this_and_descendants()
1629
+	{
1630
+		$count = 0;
1631
+		$children = $this->children();
1632
+		$count += $this->save()
1633
+			? 1
1634
+			: 0;
1635
+		foreach ($children as $child_line_item) {
1636
+			if ($child_line_item instanceof EE_Line_Item) {
1637
+				$child_line_item->set_parent_ID($this->ID());
1638
+				$count += $child_line_item->save_this_and_descendants();
1639
+			}
1640
+		}
1641
+		return $count;
1642
+	}
1643
+
1644
+
1645
+	/**
1646
+	 * returns the cancellation line item if this item was cancelled
1647
+	 *
1648
+	 * @return EE_Line_Item[]
1649
+	 * @throws InvalidArgumentException
1650
+	 * @throws InvalidInterfaceException
1651
+	 * @throws InvalidDataTypeException
1652
+	 * @throws ReflectionException
1653
+	 * @throws EE_Error
1654
+	 */
1655
+	public function get_cancellations()
1656
+	{
1657
+		EE_Registry::instance()->load_helper('Line_Item');
1658
+		return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1659
+	}
1660
+
1661
+
1662
+	/**
1663
+	 * If this item has an ID, then this saves it again to update the db
1664
+	 *
1665
+	 * @return int count of items saved
1666
+	 * @throws EE_Error
1667
+	 * @throws InvalidArgumentException
1668
+	 * @throws InvalidDataTypeException
1669
+	 * @throws InvalidInterfaceException
1670
+	 * @throws ReflectionException
1671
+	 */
1672
+	public function maybe_save()
1673
+	{
1674
+		if ($this->ID()) {
1675
+			return $this->save();
1676
+		}
1677
+		return false;
1678
+	}
1679
+
1680
+
1681
+	/**
1682
+	 * clears the cached children and parent from the line item
1683
+	 *
1684
+	 * @return void
1685
+	 */
1686
+	public function clear_related_line_item_cache()
1687
+	{
1688
+		$this->_children = array();
1689
+		$this->_parent = null;
1690
+	}
1691
+
1692
+
1693
+	/**
1694
+	 * @param bool $raw
1695
+	 * @return int
1696
+	 * @throws EE_Error
1697
+	 * @throws InvalidArgumentException
1698
+	 * @throws InvalidDataTypeException
1699
+	 * @throws InvalidInterfaceException
1700
+	 * @throws ReflectionException
1701
+	 */
1702
+	public function timestamp($raw = false)
1703
+	{
1704
+		return $raw
1705
+			? $this->get_raw('LIN_timestamp')
1706
+			: $this->get('LIN_timestamp');
1707
+	}
1708
+
1709
+
1710
+
1711
+
1712
+	/************************* DEPRECATED *************************/
1713
+	/**
1714
+	 * @deprecated 4.6.0
1715
+	 * @param string $type one of the constants on EEM_Line_Item
1716
+	 * @return EE_Line_Item[]
1717
+	 * @throws EE_Error
1718
+	 */
1719
+	protected function _get_descendants_of_type($type)
1720
+	{
1721
+		EE_Error::doing_it_wrong(
1722
+			'EE_Line_Item::_get_descendants_of_type()',
1723
+			sprintf(
1724
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
1725
+				'EEH_Line_Item::get_descendants_of_type()'
1726
+			),
1727
+			'4.6.0'
1728
+		);
1729
+		return EEH_Line_Item::get_descendants_of_type($this, $type);
1730
+	}
1731
+
1732
+
1733
+	/**
1734
+	 * @deprecated 4.6.0
1735
+	 * @param string $type like one of the EEM_Line_Item::type_*
1736
+	 * @return EE_Line_Item
1737
+	 * @throws EE_Error
1738
+	 * @throws InvalidArgumentException
1739
+	 * @throws InvalidDataTypeException
1740
+	 * @throws InvalidInterfaceException
1741
+	 * @throws ReflectionException
1742
+	 */
1743
+	public function get_nearest_descendant_of_type($type)
1744
+	{
1745
+		EE_Error::doing_it_wrong(
1746
+			'EE_Line_Item::get_nearest_descendant_of_type()',
1747
+			sprintf(
1748
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
1749
+				'EEH_Line_Item::get_nearest_descendant_of_type()'
1750
+			),
1751
+			'4.6.0'
1752
+		);
1753
+		return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1754
+	}
1755 1755
 }
Please login to merge, or discard this patch.