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