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