Completed
Branch master (16095c)
by
unknown
09:17 queued 04:49
created
core/db_classes/EE_Ticket.class.php 1 patch
Indentation   +2140 added lines, -2140 removed lines patch added patch discarded remove patch
@@ -14,2148 +14,2148 @@
 block discarded – undo
14 14
  */
15 15
 class EE_Ticket extends EE_Soft_Delete_Base_Class implements EEI_Line_Item_Object, EEI_Event_Relation, EEI_Has_Icon
16 16
 {
17
-    /**
18
-     * TicKet Archived:
19
-     * constant used by ticket_status() to indicate that a ticket is archived
20
-     * and no longer available for purchase
21
-     */
22
-    const archived = 'TKA';
23
-
24
-    /**
25
-     * TicKet Expired:
26
-     * constant used by ticket_status() to indicate that a ticket is expired
27
-     * and no longer available for purchase
28
-     */
29
-    const expired = 'TKE';
30
-
31
-    /**
32
-     * TicKet On sale:
33
-     * constant used by ticket_status() to indicate that a ticket is On Sale
34
-     * and IS available for purchase
35
-     */
36
-    const onsale = 'TKO';
37
-
38
-    /**
39
-     * TicKet Pending:
40
-     * constant used by ticket_status() to indicate that a ticket is pending
41
-     * and is NOT YET available for purchase
42
-     */
43
-    const pending = 'TKP';
44
-
45
-    /**
46
-     * TicKet Sold out:
47
-     * constant used by ticket_status() to indicate that a ticket is sold out
48
-     * and no longer available for purchases
49
-     */
50
-    const sold_out = 'TKS';
51
-
52
-    /**
53
-     * extra meta key for tracking ticket reservations
54
-     *
55
-     * @type string
56
-     */
57
-    const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
58
-
59
-    /**
60
-     * override of parent property
61
-     *
62
-     * @var EEM_Ticket
63
-     */
64
-    protected $_model;
65
-
66
-    /**
67
-     * cached result from method of the same name
68
-     *
69
-     * @var float $_ticket_total_with_taxes
70
-     */
71
-    private $_ticket_total_with_taxes;
72
-
73
-    /**
74
-     * @var TicketPriceModifiers
75
-     */
76
-    protected $ticket_price_modifiers;
77
-
78
-
79
-    /**
80
-     * @param array  $props_n_values          incoming values
81
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
82
-     *                                        used.)
83
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
84
-     *                                        date_format and the second value is the time format
85
-     * @return EE_Ticket
86
-     * @throws EE_Error
87
-     * @throws ReflectionException
88
-     */
89
-    public static function new_instance($props_n_values = [], $timezone = null, $date_formats = [])
90
-    {
91
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
92
-        return $has_object ?: new self($props_n_values, false, $timezone, $date_formats);
93
-    }
94
-
95
-
96
-    /**
97
-     * @param array  $props_n_values  incoming values from the database
98
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
99
-     *                                the website will be used.
100
-     * @return EE_Ticket
101
-     * @throws EE_Error
102
-     * @throws ReflectionException
103
-     */
104
-    public static function new_instance_from_db($props_n_values = [], $timezone = null)
105
-    {
106
-        return new self($props_n_values, true, $timezone);
107
-    }
108
-
109
-
110
-    /**
111
-     * @param array  $fieldValues
112
-     * @param false  $bydb
113
-     * @param string $timezone
114
-     * @param array  $date_formats
115
-     * @throws EE_Error
116
-     * @throws ReflectionException
117
-     */
118
-    public function __construct($fieldValues = [], $bydb = false, $timezone = '', $date_formats = [])
119
-    {
120
-        parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
121
-        $this->ticket_price_modifiers = new TicketPriceModifiers($this);
122
-    }
123
-
124
-
125
-    /**
126
-     * @return bool
127
-     * @throws EE_Error
128
-     * @throws ReflectionException
129
-     */
130
-    public function parent()
131
-    {
132
-        return $this->get('TKT_parent');
133
-    }
134
-
135
-
136
-    /**
137
-     * return if a ticket has quantities available for purchase
138
-     *
139
-     * @param int $DTT_ID the primary key for a particular datetime
140
-     * @return boolean
141
-     * @throws EE_Error
142
-     * @throws ReflectionException
143
-     */
144
-    public function available($DTT_ID = 0)
145
-    {
146
-        // are we checking availability for a particular datetime ?
147
-        if ($DTT_ID) {
148
-            // get that datetime object
149
-            $datetime = $this->get_first_related('Datetime', [['DTT_ID' => $DTT_ID]]);
150
-            // if  ticket sales for this datetime have exceeded the reg limit...
151
-            if ($datetime instanceof EE_Datetime && $datetime->sold_out()) {
152
-                return false;
153
-            }
154
-        }
155
-        // datetime is still open for registration, but is this ticket sold out ?
156
-        return $this->qty() < 1 || $this->qty() > $this->sold();
157
-    }
158
-
159
-
160
-    /**
161
-     * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
162
-     *
163
-     * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
164
-     *                               relevant status const
165
-     * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
166
-     *                               further processing
167
-     * @return mixed status int if the display string isn't requested
168
-     * @throws EE_Error
169
-     * @throws ReflectionException
170
-     */
171
-    public function ticket_status($display = false, $remaining = null)
172
-    {
173
-        $remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
174
-        if (! $remaining) {
175
-            return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
176
-        }
177
-        if ($this->get('TKT_deleted')) {
178
-            return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
179
-        }
180
-        if ($this->is_expired()) {
181
-            return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
182
-        }
183
-        if ($this->is_pending()) {
184
-            return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
185
-        }
186
-        if ($this->is_on_sale()) {
187
-            return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
188
-        }
189
-        return '';
190
-    }
191
-
192
-
193
-    /**
194
-     * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
195
-     * considering ALL the factors used for figuring that out.
196
-     *
197
-     * @param int $DTT_ID if an int above 0 is included here then we get a specific dtt.
198
-     * @return boolean         true = tickets remaining, false not.
199
-     * @throws EE_Error
200
-     * @throws ReflectionException
201
-     */
202
-    public function is_remaining($DTT_ID = 0)
203
-    {
204
-        $num_remaining = $this->remaining($DTT_ID);
205
-        if ($num_remaining === 0) {
206
-            return false;
207
-        }
208
-        if ($num_remaining > 0 && $num_remaining < $this->min()) {
209
-            return false;
210
-        }
211
-        return true;
212
-    }
213
-
214
-
215
-    /**
216
-     * return the total number of tickets available for purchase
217
-     *
218
-     * @param int $DTT_ID  the primary key for a particular datetime.
219
-     *                     set to 0 for all related datetimes
220
-     * @return int
221
-     * @throws EE_Error
222
-     * @throws ReflectionException
223
-     */
224
-    public function remaining($DTT_ID = 0)
225
-    {
226
-        return $this->real_quantity_on_ticket('saleable', $DTT_ID);
227
-    }
228
-
229
-
230
-    /**
231
-     * Gets min
232
-     *
233
-     * @return int
234
-     * @throws EE_Error
235
-     * @throws ReflectionException
236
-     */
237
-    public function min()
238
-    {
239
-        return $this->get('TKT_min');
240
-    }
241
-
242
-
243
-    /**
244
-     * return if a ticket is no longer available cause its available dates have expired.
245
-     *
246
-     * @return boolean
247
-     * @throws EE_Error
248
-     * @throws ReflectionException
249
-     */
250
-    public function is_expired()
251
-    {
252
-        return ($this->get_raw('TKT_end_date') < time());
253
-    }
254
-
255
-
256
-    /**
257
-     * Return if a ticket is yet to go on sale or not
258
-     *
259
-     * @return boolean
260
-     * @throws EE_Error
261
-     * @throws ReflectionException
262
-     */
263
-    public function is_pending()
264
-    {
265
-        return ($this->get_raw('TKT_start_date') >= time());
266
-    }
267
-
268
-
269
-    /**
270
-     * Return if a ticket is on sale or not
271
-     *
272
-     * @return boolean
273
-     * @throws EE_Error
274
-     * @throws ReflectionException
275
-     */
276
-    public function is_on_sale()
277
-    {
278
-        return ($this->get_raw('TKT_start_date') <= time() && $this->get_raw('TKT_end_date') >= time());
279
-    }
280
-
281
-
282
-    /**
283
-     * This returns the chronologically last datetime that this ticket is associated with
284
-     *
285
-     * @param string $date_format
286
-     * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
287
-     *                            the end date ie: Jan 01 "to" Dec 31
288
-     * @return string
289
-     * @throws EE_Error
290
-     * @throws ReflectionException
291
-     */
292
-    public function date_range($date_format = '', $conjunction = ' - ')
293
-    {
294
-        $date_format = ! empty($date_format) ? $date_format : $this->_dt_frmt;
295
-        $first_date  = $this->first_datetime() instanceof EE_Datetime
296
-            ? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $date_format)
297
-            : '';
298
-        $last_date   = $this->last_datetime() instanceof EE_Datetime
299
-            ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format)
300
-            : '';
301
-
302
-        return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
303
-    }
304
-
305
-
306
-    /**
307
-     * This returns the chronologically first datetime that this ticket is associated with
308
-     *
309
-     * @return EE_Datetime
310
-     * @throws EE_Error
311
-     * @throws ReflectionException
312
-     */
313
-    public function first_datetime()
314
-    {
315
-        $datetimes = $this->datetimes(['limit' => 1]);
316
-        return reset($datetimes);
317
-    }
318
-
319
-
320
-    /**
321
-     * Gets all the datetimes this ticket can be used for attending.
322
-     * Unless otherwise specified, orders datetimes by start date.
323
-     *
324
-     * @param array $query_params
325
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
326
-     * @return EE_Datetime[]|EE_Base_Class[]
327
-     * @throws EE_Error
328
-     * @throws ReflectionException
329
-     */
330
-    public function datetimes($query_params = [])
331
-    {
332
-        if (! isset($query_params['order_by'])) {
333
-            $query_params['order_by']['DTT_order'] = 'ASC';
334
-        }
335
-        return $this->get_many_related('Datetime', $query_params);
336
-    }
337
-
338
-
339
-    /**
340
-     * This returns the chronologically last datetime that this ticket is associated with
341
-     *
342
-     * @return EE_Datetime
343
-     * @throws EE_Error
344
-     * @throws ReflectionException
345
-     */
346
-    public function last_datetime()
347
-    {
348
-        $datetimes = $this->datetimes(['limit' => 1, 'order_by' => ['DTT_EVT_start' => 'DESC']]);
349
-        return end($datetimes);
350
-    }
351
-
352
-
353
-    /**
354
-     * This returns the total tickets sold depending on the given parameters.
355
-     *
356
-     * @param string $what    Can be one of two options: 'ticket', 'datetime'.
357
-     *                        'ticket' = total ticket sales for all datetimes this ticket is related to
358
-     *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
359
-     *                        'datetime' = total ticket sales in the datetime_ticket table.
360
-     *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
361
-     *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
362
-     * @param int    $dtt_id  [optional] include the dtt_id with $what = 'datetime'.
363
-     * @return mixed (array|int)          how many tickets have sold
364
-     * @throws EE_Error
365
-     * @throws ReflectionException
366
-     */
367
-    public function tickets_sold($what = 'ticket', $dtt_id = null)
368
-    {
369
-        $total        = 0;
370
-        $tickets_sold = $this->_all_tickets_sold();
371
-        switch ($what) {
372
-            case 'ticket':
373
-                return $tickets_sold['ticket'];
374
-
375
-            case 'datetime':
376
-                if (empty($tickets_sold['datetime'])) {
377
-                    return $total;
378
-                }
379
-                if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
380
-                    EE_Error::add_error(
381
-                        esc_html__(
382
-                            'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included.  Are you SURE that is a datetime related to this ticket?',
383
-                            'event_espresso'
384
-                        ),
385
-                        __FILE__,
386
-                        __FUNCTION__,
387
-                        __LINE__
388
-                    );
389
-                    return $total;
390
-                }
391
-                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
392
-
393
-            default:
394
-                return $total;
395
-        }
396
-    }
397
-
398
-
399
-    /**
400
-     * This returns an array indexed by datetime_id for tickets sold with this ticket.
401
-     *
402
-     * @return EE_Ticket[]
403
-     * @throws EE_Error
404
-     * @throws ReflectionException
405
-     */
406
-    protected function _all_tickets_sold()
407
-    {
408
-        $datetimes    = $this->get_many_related('Datetime');
409
-        $tickets_sold = [];
410
-        if (! empty($datetimes)) {
411
-            foreach ($datetimes as $datetime) {
412
-                $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
413
-            }
414
-        }
415
-        // Tickets sold
416
-        $tickets_sold['ticket'] = $this->sold();
417
-        return $tickets_sold;
418
-    }
419
-
420
-
421
-    /**
422
-     * This returns the base price object for the ticket.
423
-     *
424
-     * @param bool $return_array whether to return as an array indexed by price id or just the object.
425
-     * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
426
-     * @throws EE_Error
427
-     * @throws ReflectionException
428
-     */
429
-    public function base_price(bool $return_array = false)
430
-    {
431
-        $base_price = $this->ticket_price_modifiers->getBasePrice();
432
-        if (! empty($base_price)) {
433
-            return $return_array ? $base_price : reset($base_price);
434
-        }
435
-        $_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price];
436
-        return $return_array
437
-            ? $this->get_many_related('Price', [$_where])
438
-            : $this->get_first_related('Price', [$_where]);
439
-    }
440
-
441
-
442
-    /**
443
-     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
444
-     *
445
-     * @return EE_Price[]
446
-     * @throws EE_Error
447
-     * @throws ReflectionException
448
-     */
449
-    public function price_modifiers(): array
450
-    {
451
-        $price_modifiers = $this->usesGlobalTaxes()
452
-            ? $this->ticket_price_modifiers->getAllDiscountAndSurchargeModifiersForTicket()
453
-            : $this->ticket_price_modifiers ->getAllModifiersForTicket();
454
-        if (! empty($price_modifiers)) {
455
-            return $price_modifiers;
456
-        }
457
-        return $this->prices(
458
-            [
459
-                [
460
-                    'Price_Type.PBT_ID' => [
461
-                        'NOT IN',
462
-                        [EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax],
463
-                    ]
464
-                ]
465
-            ]
466
-        );
467
-    }
468
-
469
-
470
-    /**
471
-     * This returns ONLY the TAX price modifiers for the ticket
472
-     *
473
-     * @return EE_Price[]
474
-     * @throws EE_Error
475
-     * @throws ReflectionException
476
-     */
477
-    public function tax_price_modifiers(): array
478
-    {
479
-        $tax_price_modifiers = $this->ticket_price_modifiers->getAllTaxesForTicket();
480
-        if (! empty($tax_price_modifiers)) {
481
-            return $tax_price_modifiers;
482
-        }
483
-        return $this->prices([['Price_Type.PBT_ID' => EEM_Price_Type::base_type_tax]]);
484
-    }
485
-
486
-
487
-    /**
488
-     * Gets all the prices that combine to form the final price of this ticket
489
-     *
490
-     * @param array $query_params
491
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
492
-     * @return EE_Price[]|EE_Base_Class[]
493
-     * @throws EE_Error
494
-     * @throws ReflectionException
495
-     */
496
-    public function prices(array $query_params = []): array
497
-    {
498
-        if (! isset($query_params['order_by'])) {
499
-            $query_params['order_by']['PRC_order'] = 'ASC';
500
-        }
501
-        return $this->get_many_related('Price', $query_params);
502
-    }
503
-
504
-
505
-    /**
506
-     * Gets all the ticket datetimes (ie, relations between datetimes and tickets)
507
-     *
508
-     * @param array $query_params
509
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
510
-     * @return EE_Datetime_Ticket|EE_Base_Class[]
511
-     * @throws EE_Error
512
-     * @throws ReflectionException
513
-     */
514
-    public function datetime_tickets($query_params = [])
515
-    {
516
-        return $this->get_many_related('Datetime_Ticket', $query_params);
517
-    }
518
-
519
-
520
-    /**
521
-     * Gets all the datetimes from the db ordered by DTT_order
522
-     *
523
-     * @param boolean $show_expired
524
-     * @param boolean $show_deleted
525
-     * @return EE_Datetime[]
526
-     * @throws EE_Error
527
-     * @throws ReflectionException
528
-     */
529
-    public function datetimes_ordered($show_expired = true, $show_deleted = false)
530
-    {
531
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
532
-            $this->ID(),
533
-            $show_expired,
534
-            $show_deleted
535
-        );
536
-    }
537
-
538
-
539
-    /**
540
-     * Gets ID
541
-     *
542
-     * @return int
543
-     * @throws EE_Error
544
-     * @throws ReflectionException
545
-     */
546
-    public function ID()
547
-    {
548
-        return (int) $this->get('TKT_ID');
549
-    }
550
-
551
-
552
-    /**
553
-     * get the author of the ticket.
554
-     *
555
-     * @return int
556
-     * @throws EE_Error
557
-     * @throws ReflectionException
558
-     * @since 4.5.0
559
-     */
560
-    public function wp_user()
561
-    {
562
-        return $this->get('TKT_wp_user');
563
-    }
564
-
565
-
566
-    /**
567
-     * Gets the template for the ticket
568
-     *
569
-     * @return EE_Ticket_Template|EE_Base_Class
570
-     * @throws EE_Error
571
-     * @throws ReflectionException
572
-     */
573
-    public function template()
574
-    {
575
-        return $this->get_first_related('Ticket_Template');
576
-    }
577
-
578
-
579
-    /**
580
-     * Simply returns an array of EE_Price objects that are taxes.
581
-     *
582
-     * @return EE_Price[]
583
-     * @throws EE_Error
584
-     * @throws ReflectionException
585
-     */
586
-    public function get_ticket_taxes_for_admin(): array
587
-    {
588
-        return $this->usesGlobalTaxes() ? EE_Taxes::get_taxes_for_admin() : $this->tax_price_modifiers();
589
-    }
590
-
591
-
592
-    /**
593
-     * alias of taxable() to better indicate that ticket uses the legacy method of applying default "global" taxes
594
-     * as opposed to having tax price modifiers added directly to each ticket
595
-     *
596
-     * @return bool
597
-     * @throws EE_Error
598
-     * @throws ReflectionException
599
-     * @since   5.0.0.p
600
-     */
601
-    public function usesGlobalTaxes(): bool
602
-    {
603
-        return $this->taxable();
604
-    }
605
-
606
-
607
-    /**
608
-     * @return float
609
-     * @throws EE_Error
610
-     * @throws ReflectionException
611
-     */
612
-    public function ticket_price()
613
-    {
614
-        return $this->get('TKT_price');
615
-    }
616
-
617
-
618
-    /**
619
-     * @return mixed
620
-     * @throws EE_Error
621
-     * @throws ReflectionException
622
-     */
623
-    public function pretty_price()
624
-    {
625
-        return $this->get_pretty('TKT_price');
626
-    }
627
-
628
-
629
-    /**
630
-     * @return bool
631
-     * @throws EE_Error
632
-     * @throws ReflectionException
633
-     */
634
-    public function is_free()
635
-    {
636
-        return $this->get_ticket_total_with_taxes() === (float) 0;
637
-    }
638
-
639
-
640
-    /**
641
-     * get_ticket_total_with_taxes
642
-     *
643
-     * @param bool $no_cache
644
-     * @return float
645
-     * @throws EE_Error
646
-     * @throws ReflectionException
647
-     */
648
-    public function get_ticket_total_with_taxes($no_cache = false)
649
-    {
650
-        if ($this->_ticket_total_with_taxes === null || $no_cache) {
651
-            $this->_ticket_total_with_taxes = $this->usesGlobalTaxes()
652
-                ? $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin()
653
-                : $this->ticket_price();
654
-        }
655
-        return (float) $this->_ticket_total_with_taxes;
656
-    }
657
-
658
-
659
-    /**
660
-     * @throws EE_Error
661
-     * @throws ReflectionException
662
-     */
663
-    public function ensure_TKT_Price_correct()
664
-    {
665
-        $this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
666
-        $this->save();
667
-    }
668
-
669
-
670
-    /**
671
-     * @return float
672
-     * @throws EE_Error
673
-     * @throws ReflectionException
674
-     */
675
-    public function get_ticket_subtotal()
676
-    {
677
-        return EE_Taxes::get_subtotal_for_admin($this);
678
-    }
679
-
680
-
681
-    /**
682
-     * Returns the total taxes applied to this ticket
683
-     *
684
-     * @return float
685
-     * @throws EE_Error
686
-     * @throws ReflectionException
687
-     */
688
-    public function get_ticket_taxes_total_for_admin()
689
-    {
690
-        return EE_Taxes::get_total_taxes_for_admin($this);
691
-    }
692
-
693
-
694
-    /**
695
-     * Sets name
696
-     *
697
-     * @param string $name
698
-     * @throws EE_Error
699
-     * @throws ReflectionException
700
-     */
701
-    public function set_name($name)
702
-    {
703
-        $this->set('TKT_name', $name);
704
-    }
705
-
706
-
707
-    /**
708
-     * Gets description
709
-     *
710
-     * @return string
711
-     * @throws EE_Error
712
-     * @throws ReflectionException
713
-     */
714
-    public function description()
715
-    {
716
-        return $this->get('TKT_description');
717
-    }
718
-
719
-
720
-    /**
721
-     * Sets description
722
-     *
723
-     * @param string $description
724
-     * @throws EE_Error
725
-     * @throws ReflectionException
726
-     */
727
-    public function set_description($description)
728
-    {
729
-        $this->set('TKT_description', $description);
730
-    }
731
-
732
-
733
-    /**
734
-     * Gets start_date
735
-     *
736
-     * @param string|null $date_format
737
-     * @param string|null $time_format
738
-     * @return string
739
-     * @throws EE_Error
740
-     * @throws ReflectionException
741
-     */
742
-    public function start_date(?string $date_format = '', ?string $time_format = ''): string
743
-    {
744
-        return $this->_get_datetime('TKT_start_date', $date_format, $time_format);
745
-    }
746
-
747
-
748
-    /**
749
-     * Sets start_date
750
-     *
751
-     * @param string $start_date
752
-     * @return void
753
-     * @throws EE_Error
754
-     * @throws ReflectionException
755
-     */
756
-    public function set_start_date($start_date)
757
-    {
758
-        $this->_set_date_time('B', $start_date, 'TKT_start_date');
759
-    }
760
-
761
-
762
-    /**
763
-     * Gets end_date
764
-     *
765
-     * @param string|null $date_format
766
-     * @param string|null $time_format
767
-     * @return string
768
-     * @throws EE_Error
769
-     * @throws ReflectionException
770
-     */
771
-    public function end_date(?string $date_format = '', ?string $time_format = ''): string
772
-    {
773
-        return $this->_get_datetime('TKT_end_date', $date_format, $time_format);
774
-    }
775
-
776
-
777
-    /**
778
-     * Sets end_date
779
-     *
780
-     * @param string $end_date
781
-     * @return void
782
-     * @throws EE_Error
783
-     * @throws ReflectionException
784
-     */
785
-    public function set_end_date($end_date)
786
-    {
787
-        $this->_set_date_time('B', $end_date, 'TKT_end_date');
788
-    }
789
-
790
-
791
-    /**
792
-     * Sets sell until time
793
-     *
794
-     * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
795
-     * @throws EE_Error
796
-     * @throws ReflectionException
797
-     * @since 4.5.0
798
-     */
799
-    public function set_end_time($time)
800
-    {
801
-        $this->_set_time_for($time, 'TKT_end_date');
802
-    }
803
-
804
-
805
-    /**
806
-     * Sets min
807
-     *
808
-     * @param int $min
809
-     * @return void
810
-     * @throws EE_Error
811
-     * @throws ReflectionException
812
-     */
813
-    public function set_min($min)
814
-    {
815
-        $this->set('TKT_min', $min);
816
-    }
817
-
818
-
819
-    /**
820
-     * Gets max
821
-     *
822
-     * @return int
823
-     * @throws EE_Error
824
-     * @throws ReflectionException
825
-     */
826
-    public function max()
827
-    {
828
-        return $this->get('TKT_max');
829
-    }
830
-
831
-
832
-    /**
833
-     * Sets max
834
-     *
835
-     * @param int $max
836
-     * @return void
837
-     * @throws EE_Error
838
-     * @throws ReflectionException
839
-     */
840
-    public function set_max($max)
841
-    {
842
-        $this->set('TKT_max', $max);
843
-    }
844
-
845
-
846
-    /**
847
-     * Sets price
848
-     *
849
-     * @param float $price
850
-     * @return void
851
-     * @throws EE_Error
852
-     * @throws ReflectionException
853
-     */
854
-    public function set_price($price)
855
-    {
856
-        $this->set('TKT_price', $price);
857
-    }
858
-
859
-
860
-    /**
861
-     * Gets sold
862
-     *
863
-     * @return int
864
-     * @throws EE_Error
865
-     * @throws ReflectionException
866
-     */
867
-    public function sold(): int
868
-    {
869
-        return (int) $this->get_raw('TKT_sold');
870
-    }
871
-
872
-
873
-    /**
874
-     * Sets sold
875
-     *
876
-     * @param int $sold
877
-     * @return void
878
-     * @throws EE_Error
879
-     * @throws ReflectionException
880
-     */
881
-    public function set_sold($sold)
882
-    {
883
-        // sold can not go below zero
884
-        $sold = max(0, $sold);
885
-        $this->set('TKT_sold', $sold);
886
-    }
887
-
888
-
889
-    /**
890
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
891
-     * associated datetimes.
892
-     *
893
-     * @param int $qty
894
-     * @return boolean
895
-     * @throws EE_Error
896
-     * @throws InvalidArgumentException
897
-     * @throws InvalidDataTypeException
898
-     * @throws InvalidInterfaceException
899
-     * @throws ReflectionException
900
-     * @since 4.9.80.p
901
-     */
902
-    public function increaseSold($qty = 1)
903
-    {
904
-        $qty = absint($qty);
905
-        // increment sold and decrement reserved datetime quantities simultaneously
906
-        // don't worry about failures, because they must have already had a spot reserved
907
-        $this->increaseSoldForDatetimes($qty);
908
-        // Increment and decrement ticket quantities simultaneously
909
-        $success = $this->adjustNumericFieldsInDb(
910
-            [
911
-                'TKT_reserved' => $qty * -1,
912
-                'TKT_sold'     => $qty,
913
-            ]
914
-        );
915
-        do_action(
916
-            'AHEE__EE_Ticket__increase_sold',
917
-            $this,
918
-            $qty,
919
-            $this->sold(),
920
-            $success
921
-        );
922
-        return $success;
923
-    }
924
-
925
-
926
-    /**
927
-     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
928
-     *
929
-     * @param int           $qty positive or negative. Positive means to increase sold counts (and decrease reserved
930
-     *                           counts), Negative means to decreases old counts (and increase reserved counts).
931
-     * @param EE_Datetime[] $datetimes
932
-     * @throws EE_Error
933
-     * @throws InvalidArgumentException
934
-     * @throws InvalidDataTypeException
935
-     * @throws InvalidInterfaceException
936
-     * @throws ReflectionException
937
-     * @since 4.9.80.p
938
-     */
939
-    protected function increaseSoldForDatetimes($qty, array $datetimes = [])
940
-    {
941
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
942
-        foreach ($datetimes as $datetime) {
943
-            $datetime->increaseSold($qty);
944
-        }
945
-    }
946
-
947
-
948
-    /**
949
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
950
-     * DB and then updates the model objects.
951
-     * Does not affect the reserved counts.
952
-     *
953
-     * @param int $qty
954
-     * @return boolean
955
-     * @throws EE_Error
956
-     * @throws InvalidArgumentException
957
-     * @throws InvalidDataTypeException
958
-     * @throws InvalidInterfaceException
959
-     * @throws ReflectionException
960
-     * @since 4.9.80.p
961
-     */
962
-    public function decreaseSold($qty = 1)
963
-    {
964
-        $qty = absint($qty);
965
-        $this->decreaseSoldForDatetimes($qty);
966
-        $success = $this->adjustNumericFieldsInDb(
967
-            [
968
-                'TKT_sold' => $qty * -1,
969
-            ]
970
-        );
971
-        do_action(
972
-            'AHEE__EE_Ticket__decrease_sold',
973
-            $this,
974
-            $qty,
975
-            $this->sold(),
976
-            $success
977
-        );
978
-        return $success;
979
-    }
980
-
981
-
982
-    /**
983
-     * Decreases sold on related datetimes
984
-     *
985
-     * @param int           $qty
986
-     * @param EE_Datetime[] $datetimes
987
-     * @return void
988
-     * @throws EE_Error
989
-     * @throws InvalidArgumentException
990
-     * @throws InvalidDataTypeException
991
-     * @throws InvalidInterfaceException
992
-     * @throws ReflectionException
993
-     * @since 4.9.80.p
994
-     */
995
-    protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
996
-    {
997
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
998
-        if (is_array($datetimes)) {
999
-            foreach ($datetimes as $datetime) {
1000
-                if ($datetime instanceof EE_Datetime) {
1001
-                    $datetime->decreaseSold($qty);
1002
-                }
1003
-            }
1004
-        }
1005
-    }
1006
-
1007
-
1008
-    /**
1009
-     * Gets qty of reserved tickets
1010
-     *
1011
-     * @return int
1012
-     * @throws EE_Error
1013
-     * @throws ReflectionException
1014
-     */
1015
-    public function reserved(): int
1016
-    {
1017
-        return (int) $this->get_raw('TKT_reserved');
1018
-    }
1019
-
1020
-
1021
-    /**
1022
-     * Sets reserved
1023
-     *
1024
-     * @param int $reserved
1025
-     * @return void
1026
-     * @throws EE_Error
1027
-     * @throws ReflectionException
1028
-     */
1029
-    public function set_reserved($reserved)
1030
-    {
1031
-        // reserved can not go below zero
1032
-        $reserved = max(0, (int) $reserved);
1033
-        $this->set('TKT_reserved', $reserved);
1034
-    }
1035
-
1036
-
1037
-    /**
1038
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1039
-     *
1040
-     * @param int    $qty
1041
-     * @param string $source
1042
-     * @return bool whether we successfully reserved the ticket or not.
1043
-     * @throws EE_Error
1044
-     * @throws InvalidArgumentException
1045
-     * @throws ReflectionException
1046
-     * @throws InvalidDataTypeException
1047
-     * @throws InvalidInterfaceException
1048
-     * @since 4.9.80.p
1049
-     */
1050
-    public function increaseReserved($qty = 1, $source = 'unknown')
1051
-    {
1052
-        $qty = absint($qty);
1053
-        do_action(
1054
-            'AHEE__EE_Ticket__increase_reserved__begin',
1055
-            $this,
1056
-            $qty,
1057
-            $source
1058
-        );
1059
-        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
1060
-        $success                         = false;
1061
-        $datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
1062
-        if ($datetimes_adjusted_successfully) {
1063
-            $success = $this->incrementFieldConditionallyInDb(
1064
-                'TKT_reserved',
1065
-                'TKT_sold',
1066
-                'TKT_qty',
1067
-                $qty
1068
-            );
1069
-            if (! $success) {
1070
-                // The datetimes were successfully bumped, but not the
1071
-                // ticket. So we need to manually rollback the datetimes.
1072
-                $this->decreaseReservedForDatetimes($qty);
1073
-            }
1074
-        }
1075
-        do_action(
1076
-            'AHEE__EE_Ticket__increase_reserved',
1077
-            $this,
1078
-            $qty,
1079
-            $this->reserved(),
1080
-            $success
1081
-        );
1082
-        return $success;
1083
-    }
1084
-
1085
-
1086
-    /**
1087
-     * Increases reserved counts on related datetimes
1088
-     *
1089
-     * @param int           $qty
1090
-     * @param EE_Datetime[] $datetimes
1091
-     * @return boolean indicating success
1092
-     * @throws EE_Error
1093
-     * @throws InvalidArgumentException
1094
-     * @throws InvalidDataTypeException
1095
-     * @throws InvalidInterfaceException
1096
-     * @throws ReflectionException
1097
-     * @since 4.9.80.p
1098
-     */
1099
-    protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
1100
-    {
1101
-        $datetimes         = ! empty($datetimes) ? $datetimes : $this->datetimes();
1102
-        $datetimes_updated = [];
1103
-        $limit_exceeded    = false;
1104
-        if (is_array($datetimes)) {
1105
-            foreach ($datetimes as $datetime) {
1106
-                if ($datetime instanceof EE_Datetime) {
1107
-                    if ($datetime->increaseReserved($qty)) {
1108
-                        $datetimes_updated[] = $datetime;
1109
-                    } else {
1110
-                        $limit_exceeded = true;
1111
-                        break;
1112
-                    }
1113
-                }
1114
-            }
1115
-            // If somewhere along the way we detected a datetime whose
1116
-            // limit was exceeded, do a manual rollback.
1117
-            if ($limit_exceeded) {
1118
-                $this->decreaseReservedForDatetimes($qty, $datetimes_updated);
1119
-                return false;
1120
-            }
1121
-        }
1122
-        return true;
1123
-    }
1124
-
1125
-
1126
-    /**
1127
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1128
-     *
1129
-     * @param int    $qty
1130
-     * @param bool   $adjust_datetimes
1131
-     * @param string $source
1132
-     * @return boolean
1133
-     * @throws EE_Error
1134
-     * @throws InvalidArgumentException
1135
-     * @throws ReflectionException
1136
-     * @throws InvalidDataTypeException
1137
-     * @throws InvalidInterfaceException
1138
-     * @since 4.9.80.p
1139
-     */
1140
-    public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1141
-    {
1142
-        $qty = absint($qty);
1143
-        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1144
-        if ($adjust_datetimes) {
1145
-            $this->decreaseReservedForDatetimes($qty);
1146
-        }
1147
-        $success = $this->adjustNumericFieldsInDb(
1148
-            [
1149
-                'TKT_reserved' => $qty * -1,
1150
-            ]
1151
-        );
1152
-        do_action(
1153
-            'AHEE__EE_Ticket__decrease_reserved',
1154
-            $this,
1155
-            $qty,
1156
-            $this->reserved(),
1157
-            $success
1158
-        );
1159
-        return $success;
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * Decreases the reserved count on the specified datetimes.
1165
-     *
1166
-     * @param int           $qty
1167
-     * @param EE_Datetime[] $datetimes
1168
-     * @throws EE_Error
1169
-     * @throws InvalidArgumentException
1170
-     * @throws ReflectionException
1171
-     * @throws InvalidDataTypeException
1172
-     * @throws InvalidInterfaceException
1173
-     * @since 4.9.80.p
1174
-     */
1175
-    protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1176
-    {
1177
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1178
-        foreach ($datetimes as $datetime) {
1179
-            if ($datetime instanceof EE_Datetime) {
1180
-                $datetime->decreaseReserved($qty);
1181
-            }
1182
-        }
1183
-    }
1184
-
1185
-
1186
-    /**
1187
-     * Gets ticket quantity
1188
-     *
1189
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1190
-     *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1191
-     *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1192
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1193
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1194
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1195
-     * @return int
1196
-     * @throws EE_Error
1197
-     * @throws ReflectionException
1198
-     */
1199
-    public function qty($context = '')
1200
-    {
1201
-        switch ($context) {
1202
-            case 'reg_limit':
1203
-                return $this->real_quantity_on_ticket();
1204
-            case 'saleable':
1205
-                return $this->real_quantity_on_ticket('saleable');
1206
-            default:
1207
-                return $this->get_raw('TKT_qty');
1208
-        }
1209
-    }
1210
-
1211
-
1212
-    /**
1213
-     * Gets ticket quantity
1214
-     *
1215
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1216
-     *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1217
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1218
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1219
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1220
-     * @param int    $DTT_ID      the primary key for a particular datetime.
1221
-     *                            set to 0 for all related datetimes
1222
-     * @return int|float          int for finite quantity or float for INF
1223
-     * @throws EE_Error
1224
-     * @throws ReflectionException
1225
-     */
1226
-    public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1227
-    {
1228
-        $raw = $this->get_raw('TKT_qty');
1229
-        // return immediately if it's zero
1230
-        if ($raw === 0) {
1231
-            return $raw;
1232
-        }
1233
-        // echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1234
-        // ensure qty doesn't exceed raw value for THIS ticket
1235
-        $qty = min(EE_INF, $raw);
1236
-        // echo "\n . qty: " . $qty . '<br />';
1237
-        // calculate this ticket's total sales and reservations
1238
-        $sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1239
-        // echo "\n . sold: " . $this->sold() . '<br />';
1240
-        // echo "\n . reserved: " . $this->reserved() . '<br />';
1241
-        // echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1242
-        // first we need to calculate the maximum number of tickets available for the datetime
1243
-        // do we want data for one datetime or all of them ?
1244
-        $query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : [];
1245
-        $datetimes    = $this->datetimes($query_params);
1246
-        if (is_array($datetimes) && ! empty($datetimes)) {
1247
-            foreach ($datetimes as $datetime) {
1248
-                if ($datetime instanceof EE_Datetime) {
1249
-                    $datetime->refresh_from_db();
1250
-                    // echo "\n . . datetime name: " . $datetime->name() . '<br />';
1251
-                    // echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1252
-                    // initialize with no restrictions for each datetime
1253
-                    // but adjust datetime qty based on datetime reg limit
1254
-                    $datetime_qty = min(EE_INF, $datetime->reg_limit());
1255
-                    // echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1256
-                    // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1257
-                    // if we want the actual saleable amount, then we need to consider OTHER ticket sales
1258
-                    // and reservations for this datetime, that do NOT include sales and reservations
1259
-                    // for this ticket (so we add $this->sold() and $this->reserved() back in)
1260
-                    if ($context === 'saleable') {
1261
-                        $datetime_qty = max(
1262
-                            $datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1263
-                            0
1264
-                        );
1265
-                        // echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1266
-                        // echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1267
-                        // echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1268
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1269
-                        $datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1270
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1271
-                    }
1272
-                    $qty = min($datetime_qty, $qty);
1273
-                    // echo "\n . . qty: " . $qty . '<br />';
1274
-                }
1275
-            }
1276
-        }
1277
-        // NOW that we know the  maximum number of tickets available for the datetime
1278
-        // we can finally factor in the details for this specific ticket
1279
-        if ($qty > 0 && $context === 'saleable') {
1280
-            // and subtract the sales for THIS ticket
1281
-            $qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1282
-            // echo "\n . qty: " . $qty . '<br />';
1283
-        }
1284
-        // echo "\nFINAL QTY: " . $qty . "<br /><br />";
1285
-        return $qty;
1286
-    }
1287
-
1288
-
1289
-    /**
1290
-     * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1291
-     *
1292
-     * @param int $qty
1293
-     * @return void
1294
-     * @throws EE_Error
1295
-     * @throws ReflectionException
1296
-     */
1297
-    public function set_qty($qty)
1298
-    {
1299
-        $datetimes = $this->datetimes();
1300
-        foreach ($datetimes as $datetime) {
1301
-            if ($datetime instanceof EE_Datetime) {
1302
-                $qty = min($qty, $datetime->reg_limit());
1303
-            }
1304
-        }
1305
-        $this->set('TKT_qty', $qty);
1306
-    }
1307
-
1308
-
1309
-    /**
1310
-     * Gets uses
1311
-     *
1312
-     * @return int
1313
-     * @throws EE_Error
1314
-     * @throws ReflectionException
1315
-     */
1316
-    public function uses()
1317
-    {
1318
-        return $this->get('TKT_uses');
1319
-    }
1320
-
1321
-
1322
-    /**
1323
-     * Sets uses
1324
-     *
1325
-     * @param int $uses
1326
-     * @return void
1327
-     * @throws EE_Error
1328
-     * @throws ReflectionException
1329
-     */
1330
-    public function set_uses($uses)
1331
-    {
1332
-        $this->set('TKT_uses', $uses);
1333
-    }
1334
-
1335
-
1336
-    /**
1337
-     * returns whether ticket is required or not.
1338
-     *
1339
-     * @return boolean
1340
-     * @throws EE_Error
1341
-     * @throws ReflectionException
1342
-     */
1343
-    public function required()
1344
-    {
1345
-        return $this->get('TKT_required');
1346
-    }
1347
-
1348
-
1349
-    /**
1350
-     * sets the TKT_required property
1351
-     *
1352
-     * @param boolean $required
1353
-     * @return void
1354
-     * @throws EE_Error
1355
-     * @throws ReflectionException
1356
-     */
1357
-    public function set_required($required)
1358
-    {
1359
-        $this->set('TKT_required', $required);
1360
-    }
1361
-
1362
-
1363
-    /**
1364
-     * Gets taxable
1365
-     *
1366
-     * @return boolean
1367
-     * @throws EE_Error
1368
-     * @throws ReflectionException
1369
-     */
1370
-    public function taxable()
1371
-    {
1372
-        return $this->get('TKT_taxable');
1373
-    }
1374
-
1375
-
1376
-    /**
1377
-     * Sets taxable
1378
-     *
1379
-     * @param boolean $taxable
1380
-     * @return void
1381
-     * @throws EE_Error
1382
-     * @throws ReflectionException
1383
-     */
1384
-    public function set_taxable($taxable)
1385
-    {
1386
-        $this->set('TKT_taxable', $taxable);
1387
-    }
1388
-
1389
-
1390
-    /**
1391
-     * Gets is_default
1392
-     *
1393
-     * @return boolean
1394
-     * @throws EE_Error
1395
-     * @throws ReflectionException
1396
-     */
1397
-    public function is_default()
1398
-    {
1399
-        return $this->get('TKT_is_default');
1400
-    }
1401
-
1402
-
1403
-    /**
1404
-     * Sets is_default
1405
-     *
1406
-     * @param boolean $is_default
1407
-     * @return void
1408
-     * @throws EE_Error
1409
-     * @throws ReflectionException
1410
-     */
1411
-    public function set_is_default($is_default)
1412
-    {
1413
-        $this->set('TKT_is_default', $is_default);
1414
-    }
1415
-
1416
-
1417
-    /**
1418
-     * Gets order
1419
-     *
1420
-     * @return int
1421
-     * @throws EE_Error
1422
-     * @throws ReflectionException
1423
-     */
1424
-    public function order()
1425
-    {
1426
-        return $this->get('TKT_order');
1427
-    }
1428
-
1429
-
1430
-    /**
1431
-     * Sets order
1432
-     *
1433
-     * @param int $order
1434
-     * @return void
1435
-     * @throws EE_Error
1436
-     * @throws ReflectionException
1437
-     */
1438
-    public function set_order($order)
1439
-    {
1440
-        $this->set('TKT_order', $order);
1441
-    }
1442
-
1443
-
1444
-    /**
1445
-     * Gets row
1446
-     *
1447
-     * @return int
1448
-     * @throws EE_Error
1449
-     * @throws ReflectionException
1450
-     */
1451
-    public function row()
1452
-    {
1453
-        return $this->get('TKT_row');
1454
-    }
1455
-
1456
-
1457
-    /**
1458
-     * Sets row
1459
-     *
1460
-     * @param int $row
1461
-     * @return void
1462
-     * @throws EE_Error
1463
-     * @throws ReflectionException
1464
-     */
1465
-    public function set_row($row)
1466
-    {
1467
-        $this->set('TKT_row', $row);
1468
-    }
1469
-
1470
-
1471
-    /**
1472
-     * Gets deleted
1473
-     *
1474
-     * @return boolean
1475
-     * @throws EE_Error
1476
-     * @throws ReflectionException
1477
-     */
1478
-    public function deleted()
1479
-    {
1480
-        return $this->get('TKT_deleted');
1481
-    }
1482
-
1483
-
1484
-    /**
1485
-     * Sets deleted
1486
-     *
1487
-     * @param boolean $deleted
1488
-     * @return void
1489
-     * @throws EE_Error
1490
-     * @throws ReflectionException
1491
-     */
1492
-    public function set_deleted($deleted)
1493
-    {
1494
-        $this->set('TKT_deleted', $deleted);
1495
-    }
1496
-
1497
-
1498
-    /**
1499
-     * Gets parent
1500
-     *
1501
-     * @return int
1502
-     * @throws EE_Error
1503
-     * @throws ReflectionException
1504
-     */
1505
-    public function parent_ID()
1506
-    {
1507
-        return $this->get('TKT_parent');
1508
-    }
1509
-
1510
-
1511
-    /**
1512
-     * Sets parent
1513
-     *
1514
-     * @param int $parent
1515
-     * @return void
1516
-     * @throws EE_Error
1517
-     * @throws ReflectionException
1518
-     */
1519
-    public function set_parent_ID($parent)
1520
-    {
1521
-        $this->set('TKT_parent', $parent);
1522
-    }
1523
-
1524
-
1525
-    /**
1526
-     * @return boolean
1527
-     * @throws EE_Error
1528
-     * @throws InvalidArgumentException
1529
-     * @throws InvalidDataTypeException
1530
-     * @throws InvalidInterfaceException
1531
-     * @throws ReflectionException
1532
-     */
1533
-    public function reverse_calculate()
1534
-    {
1535
-        return $this->get('TKT_reverse_calculate');
1536
-    }
1537
-
1538
-
1539
-    /**
1540
-     * @param boolean $reverse_calculate
1541
-     * @throws EE_Error
1542
-     * @throws InvalidArgumentException
1543
-     * @throws InvalidDataTypeException
1544
-     * @throws InvalidInterfaceException
1545
-     * @throws ReflectionException
1546
-     */
1547
-    public function set_reverse_calculate($reverse_calculate)
1548
-    {
1549
-        $this->set('TKT_reverse_calculate', $reverse_calculate);
1550
-    }
1551
-
1552
-
1553
-    /**
1554
-     * Gets a string which is handy for showing in gateways etc that describes the ticket.
1555
-     *
1556
-     * @return string
1557
-     * @throws EE_Error
1558
-     * @throws ReflectionException
1559
-     */
1560
-    public function name_and_info()
1561
-    {
1562
-        $times = [];
1563
-        foreach ($this->datetimes() as $datetime) {
1564
-            $times[] = $datetime->start_date_and_time();
1565
-        }
1566
-        /* translators: %1$s ticket name, %2$s start datetimes separated by comma, %3$s ticket price */
1567
-        return sprintf(
1568
-            esc_html__('%1$s @ %2$s for %3$s', 'event_espresso'),
1569
-            $this->name(),
1570
-            implode(', ', $times),
1571
-            $this->pretty_price()
1572
-        );
1573
-    }
1574
-
1575
-
1576
-    /**
1577
-     * Gets name
1578
-     *
1579
-     * @return string
1580
-     * @throws EE_Error
1581
-     * @throws ReflectionException
1582
-     */
1583
-    public function name()
1584
-    {
1585
-        return $this->get('TKT_name');
1586
-    }
1587
-
1588
-
1589
-    /**
1590
-     * Gets price
1591
-     *
1592
-     * @return float
1593
-     * @throws EE_Error
1594
-     * @throws ReflectionException
1595
-     */
1596
-    public function price()
1597
-    {
1598
-        return $this->get('TKT_price');
1599
-    }
1600
-
1601
-
1602
-    /**
1603
-     * Gets all the registrations for this ticket
1604
-     *
1605
-     * @param array $query_params
1606
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1607
-     * @return EE_Registration[]|EE_Base_Class[]
1608
-     * @throws EE_Error
1609
-     * @throws ReflectionException
1610
-     */
1611
-    public function registrations($query_params = [])
1612
-    {
1613
-        return $this->get_many_related('Registration', $query_params);
1614
-    }
1615
-
1616
-
1617
-    /**
1618
-     * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1619
-     *
1620
-     * @return int
1621
-     * @throws EE_Error
1622
-     * @throws ReflectionException
1623
-     */
1624
-    public function update_tickets_sold()
1625
-    {
1626
-        $count_regs_for_this_ticket = $this->count_registrations(
1627
-            [
1628
-                [
1629
-                    'STS_ID'      => EEM_Registration::status_id_approved,
1630
-                    'REG_deleted' => 0,
1631
-                ],
1632
-            ]
1633
-        );
1634
-        $this->set_sold($count_regs_for_this_ticket);
1635
-        $this->save();
1636
-        return $count_regs_for_this_ticket;
1637
-    }
1638
-
1639
-
1640
-    /**
1641
-     * Counts the registrations for this ticket
1642
-     *
1643
-     * @param array $query_params
1644
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1645
-     * @return int
1646
-     * @throws EE_Error
1647
-     * @throws ReflectionException
1648
-     */
1649
-    public function count_registrations($query_params = [])
1650
-    {
1651
-        return $this->count_related('Registration', $query_params);
1652
-    }
1653
-
1654
-
1655
-    /**
1656
-     * Implementation for EEI_Has_Icon interface method.
1657
-     *
1658
-     * @return string
1659
-     * @see EEI_Visual_Representation for comments
1660
-     */
1661
-    public function get_icon()
1662
-    {
1663
-        return '<span class="dashicons dashicons-tickets-alt"></span>';
1664
-    }
1665
-
1666
-
1667
-    /**
1668
-     * Implementation of the EEI_Event_Relation interface method
1669
-     *
1670
-     * @return EE_Event
1671
-     * @throws EE_Error
1672
-     * @throws UnexpectedEntityException
1673
-     * @throws ReflectionException
1674
-     * @see EEI_Event_Relation for comments
1675
-     */
1676
-    public function get_related_event()
1677
-    {
1678
-        // get one datetime to use for getting the event
1679
-        $datetime = $this->first_datetime();
1680
-        if (! $datetime instanceof EE_Datetime) {
1681
-            throw new UnexpectedEntityException(
1682
-                $datetime,
1683
-                'EE_Datetime',
1684
-                sprintf(
1685
-                    esc_html__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1686
-                    $this->name()
1687
-                )
1688
-            );
1689
-        }
1690
-        $event = $datetime->event();
1691
-        if (! $event instanceof EE_Event) {
1692
-            throw new UnexpectedEntityException(
1693
-                $event,
1694
-                'EE_Event',
1695
-                sprintf(
1696
-                    esc_html__('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1697
-                    $this->name()
1698
-                )
1699
-            );
1700
-        }
1701
-        return $event;
1702
-    }
1703
-
1704
-
1705
-    /**
1706
-     * Implementation of the EEI_Event_Relation interface method
1707
-     *
1708
-     * @return string
1709
-     * @throws UnexpectedEntityException
1710
-     * @throws EE_Error
1711
-     * @throws ReflectionException
1712
-     * @see EEI_Event_Relation for comments
1713
-     */
1714
-    public function get_event_name()
1715
-    {
1716
-        $event = $this->get_related_event();
1717
-        return $event instanceof EE_Event ? $event->name() : '';
1718
-    }
1719
-
1720
-
1721
-    /**
1722
-     * Implementation of the EEI_Event_Relation interface method
1723
-     *
1724
-     * @return int
1725
-     * @throws UnexpectedEntityException
1726
-     * @throws EE_Error
1727
-     * @throws ReflectionException
1728
-     * @see EEI_Event_Relation for comments
1729
-     */
1730
-    public function get_event_ID()
1731
-    {
1732
-        $event = $this->get_related_event();
1733
-        return $event instanceof EE_Event ? $event->ID() : 0;
1734
-    }
1735
-
1736
-
1737
-    /**
1738
-     * This simply returns whether a ticket can be permanently deleted or not.
1739
-     * The criteria for determining this is whether the ticket has any related registrations.
1740
-     * If there are none then it can be permanently deleted.
1741
-     *
1742
-     * @return bool
1743
-     * @throws EE_Error
1744
-     * @throws ReflectionException
1745
-     */
1746
-    public function is_permanently_deleteable()
1747
-    {
1748
-        return $this->count_registrations() === 0;
1749
-    }
1750
-
1751
-
1752
-    /**
1753
-     * @return int
1754
-     * @throws EE_Error
1755
-     * @throws ReflectionException
1756
-     * @since   5.0.0.p
1757
-     */
1758
-    public function visibility(): int
1759
-    {
1760
-        return $this->get('TKT_visibility');
1761
-    }
1762
-
1763
-
1764
-    /**
1765
-     * @return bool
1766
-     * @throws EE_Error
1767
-     * @throws ReflectionException
1768
-     * @since   5.0.0.p
1769
-     */
1770
-    public function isHidden(): bool
1771
-    {
1772
-        return $this->visibility() === EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1773
-    }
1774
-
1775
-
1776
-    /**
1777
-     * @return bool
1778
-     * @throws EE_Error
1779
-     * @throws ReflectionException
1780
-     * @since   5.0.0.p
1781
-     */
1782
-    public function isNotHidden(): bool
1783
-    {
1784
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1785
-    }
1786
-
1787
-
1788
-    /**
1789
-     * @return bool
1790
-     * @throws EE_Error
1791
-     * @throws ReflectionException
1792
-     * @since   5.0.0.p
1793
-     */
1794
-    public function isPublicOnly(): bool
1795
-    {
1796
-        return $this->isNotHidden() && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE;
1797
-    }
1798
-
1799
-
1800
-    /**
1801
-     * @return bool
1802
-     * @throws EE_Error
1803
-     * @throws ReflectionException
1804
-     * @since   5.0.0.p
1805
-     */
1806
-    public function isMembersOnly(): bool
1807
-    {
1808
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE
1809
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE;
1810
-    }
1811
-
1812
-
1813
-    /**
1814
-     * @return bool
1815
-     * @throws EE_Error
1816
-     * @throws ReflectionException
1817
-     * @since   5.0.0.p
1818
-     */
1819
-    public function isAdminsOnly(): bool
1820
-    {
1821
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE
1822
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE;
1823
-    }
1824
-
1825
-
1826
-    /**
1827
-     * @return bool
1828
-     * @throws EE_Error
1829
-     * @throws ReflectionException
1830
-     * @since   5.0.0.p
1831
-     */
1832
-    public function isAdminUiOnly(): bool
1833
-    {
1834
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE
1835
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE;
1836
-    }
1837
-
1838
-
1839
-    /**
1840
-     * @param int $visibility
1841
-     * @throws EE_Error
1842
-     * @throws ReflectionException
1843
-     * @since   5.0.0.p
1844
-     */
1845
-    public function set_visibility(int $visibility)
1846
-    {
1847
-
1848
-        $ticket_visibility_options = $this->_model->ticketVisibilityOptions();
1849
-        $ticket_visibility         = -1;
1850
-        foreach ($ticket_visibility_options as $ticket_visibility_option) {
1851
-            if ($visibility === $ticket_visibility_option) {
1852
-                $ticket_visibility = $visibility;
1853
-            }
1854
-        }
1855
-        if ($ticket_visibility === -1) {
1856
-            throw new DomainException(
1857
-                sprintf(
1858
-                    esc_html__(
1859
-                        'The supplied ticket visibility setting of "%1$s" is not valid. It needs to match one of the keys in the following array:%2$s %3$s ',
1860
-                        'event_espresso'
1861
-                    ),
1862
-                    $visibility,
1863
-                    '<br />',
1864
-                    var_export($ticket_visibility_options, true)
1865
-                )
1866
-            );
1867
-        }
1868
-        $this->set('TKT_visibility', $ticket_visibility);
1869
-    }
1870
-
1871
-
1872
-    /**
1873
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1874
-     * @param string                   $relationName
1875
-     * @param array                    $extra_join_model_fields_n_values
1876
-     * @param string|null              $cache_id
1877
-     * @return EE_Base_Class
1878
-     * @throws EE_Error
1879
-     * @throws ReflectionException
1880
-     * @since   5.0.0.p
1881
-     */
1882
-    public function _add_relation_to(
1883
-        $otherObjectModelObjectOrID,
1884
-        $relationName,
1885
-        $extra_join_model_fields_n_values = [],
1886
-        $cache_id = null
1887
-    ) {
1888
-        if ($relationName === 'Datetime' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1889
-            /** @var EE_Datetime $datetime */
1890
-            $datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1891
-            $datetime->increaseSold($this->sold(), false);
1892
-            $datetime->increaseReserved($this->reserved());
1893
-            $datetime->save();
1894
-            $otherObjectModelObjectOrID = $datetime;
1895
-        }
1896
-        return parent::_add_relation_to(
1897
-            $otherObjectModelObjectOrID,
1898
-            $relationName,
1899
-            $extra_join_model_fields_n_values,
1900
-            $cache_id
1901
-        );
1902
-    }
1903
-
1904
-
1905
-    /**
1906
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1907
-     * @param string                   $relationName
1908
-     * @param array                    $where_query
1909
-     * @return bool|EE_Base_Class|null
1910
-     * @throws EE_Error
1911
-     * @throws ReflectionException
1912
-     * @since   5.0.0.p
1913
-     */
1914
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1915
-    {
1916
-        // if we're adding a new relation to a datetime
1917
-        if ($relationName === 'Datetime' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1918
-            /** @var EE_Datetime $datetime */
1919
-            $datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1920
-            $datetime->decreaseSold($this->sold());
1921
-            $datetime->decreaseReserved($this->reserved());
1922
-            $datetime->save();
1923
-            $otherObjectModelObjectOrID = $datetime;
1924
-        }
1925
-        return parent::_remove_relation_to(
1926
-            $otherObjectModelObjectOrID,
1927
-            $relationName,
1928
-            $where_query
1929
-        );
1930
-    }
1931
-
1932
-
1933
-    /**
1934
-     * Removes ALL the related things for the $relationName.
1935
-     *
1936
-     * @param string $relationName
1937
-     * @param array  $where_query_params
1938
-     * @return EE_Base_Class
1939
-     * @throws ReflectionException
1940
-     * @throws InvalidArgumentException
1941
-     * @throws InvalidInterfaceException
1942
-     * @throws InvalidDataTypeException
1943
-     * @throws EE_Error
1944
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1945
-     */
1946
-    public function _remove_relations($relationName, $where_query_params = [])
1947
-    {
1948
-        if ($relationName === 'Datetime') {
1949
-            $datetimes = $this->datetimes();
1950
-            foreach ($datetimes as $datetime) {
1951
-                $datetime->decreaseSold($this->sold());
1952
-                $datetime->decreaseReserved($this->reserved());
1953
-                $datetime->save();
1954
-            }
1955
-        }
1956
-        return parent::_remove_relations($relationName, $where_query_params);
1957
-    }
1958
-
1959
-
1960
-    /*******************************************************************
17
+	/**
18
+	 * TicKet Archived:
19
+	 * constant used by ticket_status() to indicate that a ticket is archived
20
+	 * and no longer available for purchase
21
+	 */
22
+	const archived = 'TKA';
23
+
24
+	/**
25
+	 * TicKet Expired:
26
+	 * constant used by ticket_status() to indicate that a ticket is expired
27
+	 * and no longer available for purchase
28
+	 */
29
+	const expired = 'TKE';
30
+
31
+	/**
32
+	 * TicKet On sale:
33
+	 * constant used by ticket_status() to indicate that a ticket is On Sale
34
+	 * and IS available for purchase
35
+	 */
36
+	const onsale = 'TKO';
37
+
38
+	/**
39
+	 * TicKet Pending:
40
+	 * constant used by ticket_status() to indicate that a ticket is pending
41
+	 * and is NOT YET available for purchase
42
+	 */
43
+	const pending = 'TKP';
44
+
45
+	/**
46
+	 * TicKet Sold out:
47
+	 * constant used by ticket_status() to indicate that a ticket is sold out
48
+	 * and no longer available for purchases
49
+	 */
50
+	const sold_out = 'TKS';
51
+
52
+	/**
53
+	 * extra meta key for tracking ticket reservations
54
+	 *
55
+	 * @type string
56
+	 */
57
+	const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
58
+
59
+	/**
60
+	 * override of parent property
61
+	 *
62
+	 * @var EEM_Ticket
63
+	 */
64
+	protected $_model;
65
+
66
+	/**
67
+	 * cached result from method of the same name
68
+	 *
69
+	 * @var float $_ticket_total_with_taxes
70
+	 */
71
+	private $_ticket_total_with_taxes;
72
+
73
+	/**
74
+	 * @var TicketPriceModifiers
75
+	 */
76
+	protected $ticket_price_modifiers;
77
+
78
+
79
+	/**
80
+	 * @param array  $props_n_values          incoming values
81
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
82
+	 *                                        used.)
83
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
84
+	 *                                        date_format and the second value is the time format
85
+	 * @return EE_Ticket
86
+	 * @throws EE_Error
87
+	 * @throws ReflectionException
88
+	 */
89
+	public static function new_instance($props_n_values = [], $timezone = null, $date_formats = [])
90
+	{
91
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
92
+		return $has_object ?: new self($props_n_values, false, $timezone, $date_formats);
93
+	}
94
+
95
+
96
+	/**
97
+	 * @param array  $props_n_values  incoming values from the database
98
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
99
+	 *                                the website will be used.
100
+	 * @return EE_Ticket
101
+	 * @throws EE_Error
102
+	 * @throws ReflectionException
103
+	 */
104
+	public static function new_instance_from_db($props_n_values = [], $timezone = null)
105
+	{
106
+		return new self($props_n_values, true, $timezone);
107
+	}
108
+
109
+
110
+	/**
111
+	 * @param array  $fieldValues
112
+	 * @param false  $bydb
113
+	 * @param string $timezone
114
+	 * @param array  $date_formats
115
+	 * @throws EE_Error
116
+	 * @throws ReflectionException
117
+	 */
118
+	public function __construct($fieldValues = [], $bydb = false, $timezone = '', $date_formats = [])
119
+	{
120
+		parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
121
+		$this->ticket_price_modifiers = new TicketPriceModifiers($this);
122
+	}
123
+
124
+
125
+	/**
126
+	 * @return bool
127
+	 * @throws EE_Error
128
+	 * @throws ReflectionException
129
+	 */
130
+	public function parent()
131
+	{
132
+		return $this->get('TKT_parent');
133
+	}
134
+
135
+
136
+	/**
137
+	 * return if a ticket has quantities available for purchase
138
+	 *
139
+	 * @param int $DTT_ID the primary key for a particular datetime
140
+	 * @return boolean
141
+	 * @throws EE_Error
142
+	 * @throws ReflectionException
143
+	 */
144
+	public function available($DTT_ID = 0)
145
+	{
146
+		// are we checking availability for a particular datetime ?
147
+		if ($DTT_ID) {
148
+			// get that datetime object
149
+			$datetime = $this->get_first_related('Datetime', [['DTT_ID' => $DTT_ID]]);
150
+			// if  ticket sales for this datetime have exceeded the reg limit...
151
+			if ($datetime instanceof EE_Datetime && $datetime->sold_out()) {
152
+				return false;
153
+			}
154
+		}
155
+		// datetime is still open for registration, but is this ticket sold out ?
156
+		return $this->qty() < 1 || $this->qty() > $this->sold();
157
+	}
158
+
159
+
160
+	/**
161
+	 * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
162
+	 *
163
+	 * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
164
+	 *                               relevant status const
165
+	 * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
166
+	 *                               further processing
167
+	 * @return mixed status int if the display string isn't requested
168
+	 * @throws EE_Error
169
+	 * @throws ReflectionException
170
+	 */
171
+	public function ticket_status($display = false, $remaining = null)
172
+	{
173
+		$remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
174
+		if (! $remaining) {
175
+			return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
176
+		}
177
+		if ($this->get('TKT_deleted')) {
178
+			return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
179
+		}
180
+		if ($this->is_expired()) {
181
+			return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
182
+		}
183
+		if ($this->is_pending()) {
184
+			return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
185
+		}
186
+		if ($this->is_on_sale()) {
187
+			return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
188
+		}
189
+		return '';
190
+	}
191
+
192
+
193
+	/**
194
+	 * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
195
+	 * considering ALL the factors used for figuring that out.
196
+	 *
197
+	 * @param int $DTT_ID if an int above 0 is included here then we get a specific dtt.
198
+	 * @return boolean         true = tickets remaining, false not.
199
+	 * @throws EE_Error
200
+	 * @throws ReflectionException
201
+	 */
202
+	public function is_remaining($DTT_ID = 0)
203
+	{
204
+		$num_remaining = $this->remaining($DTT_ID);
205
+		if ($num_remaining === 0) {
206
+			return false;
207
+		}
208
+		if ($num_remaining > 0 && $num_remaining < $this->min()) {
209
+			return false;
210
+		}
211
+		return true;
212
+	}
213
+
214
+
215
+	/**
216
+	 * return the total number of tickets available for purchase
217
+	 *
218
+	 * @param int $DTT_ID  the primary key for a particular datetime.
219
+	 *                     set to 0 for all related datetimes
220
+	 * @return int
221
+	 * @throws EE_Error
222
+	 * @throws ReflectionException
223
+	 */
224
+	public function remaining($DTT_ID = 0)
225
+	{
226
+		return $this->real_quantity_on_ticket('saleable', $DTT_ID);
227
+	}
228
+
229
+
230
+	/**
231
+	 * Gets min
232
+	 *
233
+	 * @return int
234
+	 * @throws EE_Error
235
+	 * @throws ReflectionException
236
+	 */
237
+	public function min()
238
+	{
239
+		return $this->get('TKT_min');
240
+	}
241
+
242
+
243
+	/**
244
+	 * return if a ticket is no longer available cause its available dates have expired.
245
+	 *
246
+	 * @return boolean
247
+	 * @throws EE_Error
248
+	 * @throws ReflectionException
249
+	 */
250
+	public function is_expired()
251
+	{
252
+		return ($this->get_raw('TKT_end_date') < time());
253
+	}
254
+
255
+
256
+	/**
257
+	 * Return if a ticket is yet to go on sale or not
258
+	 *
259
+	 * @return boolean
260
+	 * @throws EE_Error
261
+	 * @throws ReflectionException
262
+	 */
263
+	public function is_pending()
264
+	{
265
+		return ($this->get_raw('TKT_start_date') >= time());
266
+	}
267
+
268
+
269
+	/**
270
+	 * Return if a ticket is on sale or not
271
+	 *
272
+	 * @return boolean
273
+	 * @throws EE_Error
274
+	 * @throws ReflectionException
275
+	 */
276
+	public function is_on_sale()
277
+	{
278
+		return ($this->get_raw('TKT_start_date') <= time() && $this->get_raw('TKT_end_date') >= time());
279
+	}
280
+
281
+
282
+	/**
283
+	 * This returns the chronologically last datetime that this ticket is associated with
284
+	 *
285
+	 * @param string $date_format
286
+	 * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
287
+	 *                            the end date ie: Jan 01 "to" Dec 31
288
+	 * @return string
289
+	 * @throws EE_Error
290
+	 * @throws ReflectionException
291
+	 */
292
+	public function date_range($date_format = '', $conjunction = ' - ')
293
+	{
294
+		$date_format = ! empty($date_format) ? $date_format : $this->_dt_frmt;
295
+		$first_date  = $this->first_datetime() instanceof EE_Datetime
296
+			? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $date_format)
297
+			: '';
298
+		$last_date   = $this->last_datetime() instanceof EE_Datetime
299
+			? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format)
300
+			: '';
301
+
302
+		return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
303
+	}
304
+
305
+
306
+	/**
307
+	 * This returns the chronologically first datetime that this ticket is associated with
308
+	 *
309
+	 * @return EE_Datetime
310
+	 * @throws EE_Error
311
+	 * @throws ReflectionException
312
+	 */
313
+	public function first_datetime()
314
+	{
315
+		$datetimes = $this->datetimes(['limit' => 1]);
316
+		return reset($datetimes);
317
+	}
318
+
319
+
320
+	/**
321
+	 * Gets all the datetimes this ticket can be used for attending.
322
+	 * Unless otherwise specified, orders datetimes by start date.
323
+	 *
324
+	 * @param array $query_params
325
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
326
+	 * @return EE_Datetime[]|EE_Base_Class[]
327
+	 * @throws EE_Error
328
+	 * @throws ReflectionException
329
+	 */
330
+	public function datetimes($query_params = [])
331
+	{
332
+		if (! isset($query_params['order_by'])) {
333
+			$query_params['order_by']['DTT_order'] = 'ASC';
334
+		}
335
+		return $this->get_many_related('Datetime', $query_params);
336
+	}
337
+
338
+
339
+	/**
340
+	 * This returns the chronologically last datetime that this ticket is associated with
341
+	 *
342
+	 * @return EE_Datetime
343
+	 * @throws EE_Error
344
+	 * @throws ReflectionException
345
+	 */
346
+	public function last_datetime()
347
+	{
348
+		$datetimes = $this->datetimes(['limit' => 1, 'order_by' => ['DTT_EVT_start' => 'DESC']]);
349
+		return end($datetimes);
350
+	}
351
+
352
+
353
+	/**
354
+	 * This returns the total tickets sold depending on the given parameters.
355
+	 *
356
+	 * @param string $what    Can be one of two options: 'ticket', 'datetime'.
357
+	 *                        'ticket' = total ticket sales for all datetimes this ticket is related to
358
+	 *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
359
+	 *                        'datetime' = total ticket sales in the datetime_ticket table.
360
+	 *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
361
+	 *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
362
+	 * @param int    $dtt_id  [optional] include the dtt_id with $what = 'datetime'.
363
+	 * @return mixed (array|int)          how many tickets have sold
364
+	 * @throws EE_Error
365
+	 * @throws ReflectionException
366
+	 */
367
+	public function tickets_sold($what = 'ticket', $dtt_id = null)
368
+	{
369
+		$total        = 0;
370
+		$tickets_sold = $this->_all_tickets_sold();
371
+		switch ($what) {
372
+			case 'ticket':
373
+				return $tickets_sold['ticket'];
374
+
375
+			case 'datetime':
376
+				if (empty($tickets_sold['datetime'])) {
377
+					return $total;
378
+				}
379
+				if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
380
+					EE_Error::add_error(
381
+						esc_html__(
382
+							'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included.  Are you SURE that is a datetime related to this ticket?',
383
+							'event_espresso'
384
+						),
385
+						__FILE__,
386
+						__FUNCTION__,
387
+						__LINE__
388
+					);
389
+					return $total;
390
+				}
391
+				return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
392
+
393
+			default:
394
+				return $total;
395
+		}
396
+	}
397
+
398
+
399
+	/**
400
+	 * This returns an array indexed by datetime_id for tickets sold with this ticket.
401
+	 *
402
+	 * @return EE_Ticket[]
403
+	 * @throws EE_Error
404
+	 * @throws ReflectionException
405
+	 */
406
+	protected function _all_tickets_sold()
407
+	{
408
+		$datetimes    = $this->get_many_related('Datetime');
409
+		$tickets_sold = [];
410
+		if (! empty($datetimes)) {
411
+			foreach ($datetimes as $datetime) {
412
+				$tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
413
+			}
414
+		}
415
+		// Tickets sold
416
+		$tickets_sold['ticket'] = $this->sold();
417
+		return $tickets_sold;
418
+	}
419
+
420
+
421
+	/**
422
+	 * This returns the base price object for the ticket.
423
+	 *
424
+	 * @param bool $return_array whether to return as an array indexed by price id or just the object.
425
+	 * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
426
+	 * @throws EE_Error
427
+	 * @throws ReflectionException
428
+	 */
429
+	public function base_price(bool $return_array = false)
430
+	{
431
+		$base_price = $this->ticket_price_modifiers->getBasePrice();
432
+		if (! empty($base_price)) {
433
+			return $return_array ? $base_price : reset($base_price);
434
+		}
435
+		$_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price];
436
+		return $return_array
437
+			? $this->get_many_related('Price', [$_where])
438
+			: $this->get_first_related('Price', [$_where]);
439
+	}
440
+
441
+
442
+	/**
443
+	 * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
444
+	 *
445
+	 * @return EE_Price[]
446
+	 * @throws EE_Error
447
+	 * @throws ReflectionException
448
+	 */
449
+	public function price_modifiers(): array
450
+	{
451
+		$price_modifiers = $this->usesGlobalTaxes()
452
+			? $this->ticket_price_modifiers->getAllDiscountAndSurchargeModifiersForTicket()
453
+			: $this->ticket_price_modifiers ->getAllModifiersForTicket();
454
+		if (! empty($price_modifiers)) {
455
+			return $price_modifiers;
456
+		}
457
+		return $this->prices(
458
+			[
459
+				[
460
+					'Price_Type.PBT_ID' => [
461
+						'NOT IN',
462
+						[EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax],
463
+					]
464
+				]
465
+			]
466
+		);
467
+	}
468
+
469
+
470
+	/**
471
+	 * This returns ONLY the TAX price modifiers for the ticket
472
+	 *
473
+	 * @return EE_Price[]
474
+	 * @throws EE_Error
475
+	 * @throws ReflectionException
476
+	 */
477
+	public function tax_price_modifiers(): array
478
+	{
479
+		$tax_price_modifiers = $this->ticket_price_modifiers->getAllTaxesForTicket();
480
+		if (! empty($tax_price_modifiers)) {
481
+			return $tax_price_modifiers;
482
+		}
483
+		return $this->prices([['Price_Type.PBT_ID' => EEM_Price_Type::base_type_tax]]);
484
+	}
485
+
486
+
487
+	/**
488
+	 * Gets all the prices that combine to form the final price of this ticket
489
+	 *
490
+	 * @param array $query_params
491
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
492
+	 * @return EE_Price[]|EE_Base_Class[]
493
+	 * @throws EE_Error
494
+	 * @throws ReflectionException
495
+	 */
496
+	public function prices(array $query_params = []): array
497
+	{
498
+		if (! isset($query_params['order_by'])) {
499
+			$query_params['order_by']['PRC_order'] = 'ASC';
500
+		}
501
+		return $this->get_many_related('Price', $query_params);
502
+	}
503
+
504
+
505
+	/**
506
+	 * Gets all the ticket datetimes (ie, relations between datetimes and tickets)
507
+	 *
508
+	 * @param array $query_params
509
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
510
+	 * @return EE_Datetime_Ticket|EE_Base_Class[]
511
+	 * @throws EE_Error
512
+	 * @throws ReflectionException
513
+	 */
514
+	public function datetime_tickets($query_params = [])
515
+	{
516
+		return $this->get_many_related('Datetime_Ticket', $query_params);
517
+	}
518
+
519
+
520
+	/**
521
+	 * Gets all the datetimes from the db ordered by DTT_order
522
+	 *
523
+	 * @param boolean $show_expired
524
+	 * @param boolean $show_deleted
525
+	 * @return EE_Datetime[]
526
+	 * @throws EE_Error
527
+	 * @throws ReflectionException
528
+	 */
529
+	public function datetimes_ordered($show_expired = true, $show_deleted = false)
530
+	{
531
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
532
+			$this->ID(),
533
+			$show_expired,
534
+			$show_deleted
535
+		);
536
+	}
537
+
538
+
539
+	/**
540
+	 * Gets ID
541
+	 *
542
+	 * @return int
543
+	 * @throws EE_Error
544
+	 * @throws ReflectionException
545
+	 */
546
+	public function ID()
547
+	{
548
+		return (int) $this->get('TKT_ID');
549
+	}
550
+
551
+
552
+	/**
553
+	 * get the author of the ticket.
554
+	 *
555
+	 * @return int
556
+	 * @throws EE_Error
557
+	 * @throws ReflectionException
558
+	 * @since 4.5.0
559
+	 */
560
+	public function wp_user()
561
+	{
562
+		return $this->get('TKT_wp_user');
563
+	}
564
+
565
+
566
+	/**
567
+	 * Gets the template for the ticket
568
+	 *
569
+	 * @return EE_Ticket_Template|EE_Base_Class
570
+	 * @throws EE_Error
571
+	 * @throws ReflectionException
572
+	 */
573
+	public function template()
574
+	{
575
+		return $this->get_first_related('Ticket_Template');
576
+	}
577
+
578
+
579
+	/**
580
+	 * Simply returns an array of EE_Price objects that are taxes.
581
+	 *
582
+	 * @return EE_Price[]
583
+	 * @throws EE_Error
584
+	 * @throws ReflectionException
585
+	 */
586
+	public function get_ticket_taxes_for_admin(): array
587
+	{
588
+		return $this->usesGlobalTaxes() ? EE_Taxes::get_taxes_for_admin() : $this->tax_price_modifiers();
589
+	}
590
+
591
+
592
+	/**
593
+	 * alias of taxable() to better indicate that ticket uses the legacy method of applying default "global" taxes
594
+	 * as opposed to having tax price modifiers added directly to each ticket
595
+	 *
596
+	 * @return bool
597
+	 * @throws EE_Error
598
+	 * @throws ReflectionException
599
+	 * @since   5.0.0.p
600
+	 */
601
+	public function usesGlobalTaxes(): bool
602
+	{
603
+		return $this->taxable();
604
+	}
605
+
606
+
607
+	/**
608
+	 * @return float
609
+	 * @throws EE_Error
610
+	 * @throws ReflectionException
611
+	 */
612
+	public function ticket_price()
613
+	{
614
+		return $this->get('TKT_price');
615
+	}
616
+
617
+
618
+	/**
619
+	 * @return mixed
620
+	 * @throws EE_Error
621
+	 * @throws ReflectionException
622
+	 */
623
+	public function pretty_price()
624
+	{
625
+		return $this->get_pretty('TKT_price');
626
+	}
627
+
628
+
629
+	/**
630
+	 * @return bool
631
+	 * @throws EE_Error
632
+	 * @throws ReflectionException
633
+	 */
634
+	public function is_free()
635
+	{
636
+		return $this->get_ticket_total_with_taxes() === (float) 0;
637
+	}
638
+
639
+
640
+	/**
641
+	 * get_ticket_total_with_taxes
642
+	 *
643
+	 * @param bool $no_cache
644
+	 * @return float
645
+	 * @throws EE_Error
646
+	 * @throws ReflectionException
647
+	 */
648
+	public function get_ticket_total_with_taxes($no_cache = false)
649
+	{
650
+		if ($this->_ticket_total_with_taxes === null || $no_cache) {
651
+			$this->_ticket_total_with_taxes = $this->usesGlobalTaxes()
652
+				? $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin()
653
+				: $this->ticket_price();
654
+		}
655
+		return (float) $this->_ticket_total_with_taxes;
656
+	}
657
+
658
+
659
+	/**
660
+	 * @throws EE_Error
661
+	 * @throws ReflectionException
662
+	 */
663
+	public function ensure_TKT_Price_correct()
664
+	{
665
+		$this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
666
+		$this->save();
667
+	}
668
+
669
+
670
+	/**
671
+	 * @return float
672
+	 * @throws EE_Error
673
+	 * @throws ReflectionException
674
+	 */
675
+	public function get_ticket_subtotal()
676
+	{
677
+		return EE_Taxes::get_subtotal_for_admin($this);
678
+	}
679
+
680
+
681
+	/**
682
+	 * Returns the total taxes applied to this ticket
683
+	 *
684
+	 * @return float
685
+	 * @throws EE_Error
686
+	 * @throws ReflectionException
687
+	 */
688
+	public function get_ticket_taxes_total_for_admin()
689
+	{
690
+		return EE_Taxes::get_total_taxes_for_admin($this);
691
+	}
692
+
693
+
694
+	/**
695
+	 * Sets name
696
+	 *
697
+	 * @param string $name
698
+	 * @throws EE_Error
699
+	 * @throws ReflectionException
700
+	 */
701
+	public function set_name($name)
702
+	{
703
+		$this->set('TKT_name', $name);
704
+	}
705
+
706
+
707
+	/**
708
+	 * Gets description
709
+	 *
710
+	 * @return string
711
+	 * @throws EE_Error
712
+	 * @throws ReflectionException
713
+	 */
714
+	public function description()
715
+	{
716
+		return $this->get('TKT_description');
717
+	}
718
+
719
+
720
+	/**
721
+	 * Sets description
722
+	 *
723
+	 * @param string $description
724
+	 * @throws EE_Error
725
+	 * @throws ReflectionException
726
+	 */
727
+	public function set_description($description)
728
+	{
729
+		$this->set('TKT_description', $description);
730
+	}
731
+
732
+
733
+	/**
734
+	 * Gets start_date
735
+	 *
736
+	 * @param string|null $date_format
737
+	 * @param string|null $time_format
738
+	 * @return string
739
+	 * @throws EE_Error
740
+	 * @throws ReflectionException
741
+	 */
742
+	public function start_date(?string $date_format = '', ?string $time_format = ''): string
743
+	{
744
+		return $this->_get_datetime('TKT_start_date', $date_format, $time_format);
745
+	}
746
+
747
+
748
+	/**
749
+	 * Sets start_date
750
+	 *
751
+	 * @param string $start_date
752
+	 * @return void
753
+	 * @throws EE_Error
754
+	 * @throws ReflectionException
755
+	 */
756
+	public function set_start_date($start_date)
757
+	{
758
+		$this->_set_date_time('B', $start_date, 'TKT_start_date');
759
+	}
760
+
761
+
762
+	/**
763
+	 * Gets end_date
764
+	 *
765
+	 * @param string|null $date_format
766
+	 * @param string|null $time_format
767
+	 * @return string
768
+	 * @throws EE_Error
769
+	 * @throws ReflectionException
770
+	 */
771
+	public function end_date(?string $date_format = '', ?string $time_format = ''): string
772
+	{
773
+		return $this->_get_datetime('TKT_end_date', $date_format, $time_format);
774
+	}
775
+
776
+
777
+	/**
778
+	 * Sets end_date
779
+	 *
780
+	 * @param string $end_date
781
+	 * @return void
782
+	 * @throws EE_Error
783
+	 * @throws ReflectionException
784
+	 */
785
+	public function set_end_date($end_date)
786
+	{
787
+		$this->_set_date_time('B', $end_date, 'TKT_end_date');
788
+	}
789
+
790
+
791
+	/**
792
+	 * Sets sell until time
793
+	 *
794
+	 * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
795
+	 * @throws EE_Error
796
+	 * @throws ReflectionException
797
+	 * @since 4.5.0
798
+	 */
799
+	public function set_end_time($time)
800
+	{
801
+		$this->_set_time_for($time, 'TKT_end_date');
802
+	}
803
+
804
+
805
+	/**
806
+	 * Sets min
807
+	 *
808
+	 * @param int $min
809
+	 * @return void
810
+	 * @throws EE_Error
811
+	 * @throws ReflectionException
812
+	 */
813
+	public function set_min($min)
814
+	{
815
+		$this->set('TKT_min', $min);
816
+	}
817
+
818
+
819
+	/**
820
+	 * Gets max
821
+	 *
822
+	 * @return int
823
+	 * @throws EE_Error
824
+	 * @throws ReflectionException
825
+	 */
826
+	public function max()
827
+	{
828
+		return $this->get('TKT_max');
829
+	}
830
+
831
+
832
+	/**
833
+	 * Sets max
834
+	 *
835
+	 * @param int $max
836
+	 * @return void
837
+	 * @throws EE_Error
838
+	 * @throws ReflectionException
839
+	 */
840
+	public function set_max($max)
841
+	{
842
+		$this->set('TKT_max', $max);
843
+	}
844
+
845
+
846
+	/**
847
+	 * Sets price
848
+	 *
849
+	 * @param float $price
850
+	 * @return void
851
+	 * @throws EE_Error
852
+	 * @throws ReflectionException
853
+	 */
854
+	public function set_price($price)
855
+	{
856
+		$this->set('TKT_price', $price);
857
+	}
858
+
859
+
860
+	/**
861
+	 * Gets sold
862
+	 *
863
+	 * @return int
864
+	 * @throws EE_Error
865
+	 * @throws ReflectionException
866
+	 */
867
+	public function sold(): int
868
+	{
869
+		return (int) $this->get_raw('TKT_sold');
870
+	}
871
+
872
+
873
+	/**
874
+	 * Sets sold
875
+	 *
876
+	 * @param int $sold
877
+	 * @return void
878
+	 * @throws EE_Error
879
+	 * @throws ReflectionException
880
+	 */
881
+	public function set_sold($sold)
882
+	{
883
+		// sold can not go below zero
884
+		$sold = max(0, $sold);
885
+		$this->set('TKT_sold', $sold);
886
+	}
887
+
888
+
889
+	/**
890
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
891
+	 * associated datetimes.
892
+	 *
893
+	 * @param int $qty
894
+	 * @return boolean
895
+	 * @throws EE_Error
896
+	 * @throws InvalidArgumentException
897
+	 * @throws InvalidDataTypeException
898
+	 * @throws InvalidInterfaceException
899
+	 * @throws ReflectionException
900
+	 * @since 4.9.80.p
901
+	 */
902
+	public function increaseSold($qty = 1)
903
+	{
904
+		$qty = absint($qty);
905
+		// increment sold and decrement reserved datetime quantities simultaneously
906
+		// don't worry about failures, because they must have already had a spot reserved
907
+		$this->increaseSoldForDatetimes($qty);
908
+		// Increment and decrement ticket quantities simultaneously
909
+		$success = $this->adjustNumericFieldsInDb(
910
+			[
911
+				'TKT_reserved' => $qty * -1,
912
+				'TKT_sold'     => $qty,
913
+			]
914
+		);
915
+		do_action(
916
+			'AHEE__EE_Ticket__increase_sold',
917
+			$this,
918
+			$qty,
919
+			$this->sold(),
920
+			$success
921
+		);
922
+		return $success;
923
+	}
924
+
925
+
926
+	/**
927
+	 * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
928
+	 *
929
+	 * @param int           $qty positive or negative. Positive means to increase sold counts (and decrease reserved
930
+	 *                           counts), Negative means to decreases old counts (and increase reserved counts).
931
+	 * @param EE_Datetime[] $datetimes
932
+	 * @throws EE_Error
933
+	 * @throws InvalidArgumentException
934
+	 * @throws InvalidDataTypeException
935
+	 * @throws InvalidInterfaceException
936
+	 * @throws ReflectionException
937
+	 * @since 4.9.80.p
938
+	 */
939
+	protected function increaseSoldForDatetimes($qty, array $datetimes = [])
940
+	{
941
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
942
+		foreach ($datetimes as $datetime) {
943
+			$datetime->increaseSold($qty);
944
+		}
945
+	}
946
+
947
+
948
+	/**
949
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
950
+	 * DB and then updates the model objects.
951
+	 * Does not affect the reserved counts.
952
+	 *
953
+	 * @param int $qty
954
+	 * @return boolean
955
+	 * @throws EE_Error
956
+	 * @throws InvalidArgumentException
957
+	 * @throws InvalidDataTypeException
958
+	 * @throws InvalidInterfaceException
959
+	 * @throws ReflectionException
960
+	 * @since 4.9.80.p
961
+	 */
962
+	public function decreaseSold($qty = 1)
963
+	{
964
+		$qty = absint($qty);
965
+		$this->decreaseSoldForDatetimes($qty);
966
+		$success = $this->adjustNumericFieldsInDb(
967
+			[
968
+				'TKT_sold' => $qty * -1,
969
+			]
970
+		);
971
+		do_action(
972
+			'AHEE__EE_Ticket__decrease_sold',
973
+			$this,
974
+			$qty,
975
+			$this->sold(),
976
+			$success
977
+		);
978
+		return $success;
979
+	}
980
+
981
+
982
+	/**
983
+	 * Decreases sold on related datetimes
984
+	 *
985
+	 * @param int           $qty
986
+	 * @param EE_Datetime[] $datetimes
987
+	 * @return void
988
+	 * @throws EE_Error
989
+	 * @throws InvalidArgumentException
990
+	 * @throws InvalidDataTypeException
991
+	 * @throws InvalidInterfaceException
992
+	 * @throws ReflectionException
993
+	 * @since 4.9.80.p
994
+	 */
995
+	protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
996
+	{
997
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
998
+		if (is_array($datetimes)) {
999
+			foreach ($datetimes as $datetime) {
1000
+				if ($datetime instanceof EE_Datetime) {
1001
+					$datetime->decreaseSold($qty);
1002
+				}
1003
+			}
1004
+		}
1005
+	}
1006
+
1007
+
1008
+	/**
1009
+	 * Gets qty of reserved tickets
1010
+	 *
1011
+	 * @return int
1012
+	 * @throws EE_Error
1013
+	 * @throws ReflectionException
1014
+	 */
1015
+	public function reserved(): int
1016
+	{
1017
+		return (int) $this->get_raw('TKT_reserved');
1018
+	}
1019
+
1020
+
1021
+	/**
1022
+	 * Sets reserved
1023
+	 *
1024
+	 * @param int $reserved
1025
+	 * @return void
1026
+	 * @throws EE_Error
1027
+	 * @throws ReflectionException
1028
+	 */
1029
+	public function set_reserved($reserved)
1030
+	{
1031
+		// reserved can not go below zero
1032
+		$reserved = max(0, (int) $reserved);
1033
+		$this->set('TKT_reserved', $reserved);
1034
+	}
1035
+
1036
+
1037
+	/**
1038
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1039
+	 *
1040
+	 * @param int    $qty
1041
+	 * @param string $source
1042
+	 * @return bool whether we successfully reserved the ticket or not.
1043
+	 * @throws EE_Error
1044
+	 * @throws InvalidArgumentException
1045
+	 * @throws ReflectionException
1046
+	 * @throws InvalidDataTypeException
1047
+	 * @throws InvalidInterfaceException
1048
+	 * @since 4.9.80.p
1049
+	 */
1050
+	public function increaseReserved($qty = 1, $source = 'unknown')
1051
+	{
1052
+		$qty = absint($qty);
1053
+		do_action(
1054
+			'AHEE__EE_Ticket__increase_reserved__begin',
1055
+			$this,
1056
+			$qty,
1057
+			$source
1058
+		);
1059
+		$this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
1060
+		$success                         = false;
1061
+		$datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
1062
+		if ($datetimes_adjusted_successfully) {
1063
+			$success = $this->incrementFieldConditionallyInDb(
1064
+				'TKT_reserved',
1065
+				'TKT_sold',
1066
+				'TKT_qty',
1067
+				$qty
1068
+			);
1069
+			if (! $success) {
1070
+				// The datetimes were successfully bumped, but not the
1071
+				// ticket. So we need to manually rollback the datetimes.
1072
+				$this->decreaseReservedForDatetimes($qty);
1073
+			}
1074
+		}
1075
+		do_action(
1076
+			'AHEE__EE_Ticket__increase_reserved',
1077
+			$this,
1078
+			$qty,
1079
+			$this->reserved(),
1080
+			$success
1081
+		);
1082
+		return $success;
1083
+	}
1084
+
1085
+
1086
+	/**
1087
+	 * Increases reserved counts on related datetimes
1088
+	 *
1089
+	 * @param int           $qty
1090
+	 * @param EE_Datetime[] $datetimes
1091
+	 * @return boolean indicating success
1092
+	 * @throws EE_Error
1093
+	 * @throws InvalidArgumentException
1094
+	 * @throws InvalidDataTypeException
1095
+	 * @throws InvalidInterfaceException
1096
+	 * @throws ReflectionException
1097
+	 * @since 4.9.80.p
1098
+	 */
1099
+	protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
1100
+	{
1101
+		$datetimes         = ! empty($datetimes) ? $datetimes : $this->datetimes();
1102
+		$datetimes_updated = [];
1103
+		$limit_exceeded    = false;
1104
+		if (is_array($datetimes)) {
1105
+			foreach ($datetimes as $datetime) {
1106
+				if ($datetime instanceof EE_Datetime) {
1107
+					if ($datetime->increaseReserved($qty)) {
1108
+						$datetimes_updated[] = $datetime;
1109
+					} else {
1110
+						$limit_exceeded = true;
1111
+						break;
1112
+					}
1113
+				}
1114
+			}
1115
+			// If somewhere along the way we detected a datetime whose
1116
+			// limit was exceeded, do a manual rollback.
1117
+			if ($limit_exceeded) {
1118
+				$this->decreaseReservedForDatetimes($qty, $datetimes_updated);
1119
+				return false;
1120
+			}
1121
+		}
1122
+		return true;
1123
+	}
1124
+
1125
+
1126
+	/**
1127
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1128
+	 *
1129
+	 * @param int    $qty
1130
+	 * @param bool   $adjust_datetimes
1131
+	 * @param string $source
1132
+	 * @return boolean
1133
+	 * @throws EE_Error
1134
+	 * @throws InvalidArgumentException
1135
+	 * @throws ReflectionException
1136
+	 * @throws InvalidDataTypeException
1137
+	 * @throws InvalidInterfaceException
1138
+	 * @since 4.9.80.p
1139
+	 */
1140
+	public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1141
+	{
1142
+		$qty = absint($qty);
1143
+		$this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1144
+		if ($adjust_datetimes) {
1145
+			$this->decreaseReservedForDatetimes($qty);
1146
+		}
1147
+		$success = $this->adjustNumericFieldsInDb(
1148
+			[
1149
+				'TKT_reserved' => $qty * -1,
1150
+			]
1151
+		);
1152
+		do_action(
1153
+			'AHEE__EE_Ticket__decrease_reserved',
1154
+			$this,
1155
+			$qty,
1156
+			$this->reserved(),
1157
+			$success
1158
+		);
1159
+		return $success;
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * Decreases the reserved count on the specified datetimes.
1165
+	 *
1166
+	 * @param int           $qty
1167
+	 * @param EE_Datetime[] $datetimes
1168
+	 * @throws EE_Error
1169
+	 * @throws InvalidArgumentException
1170
+	 * @throws ReflectionException
1171
+	 * @throws InvalidDataTypeException
1172
+	 * @throws InvalidInterfaceException
1173
+	 * @since 4.9.80.p
1174
+	 */
1175
+	protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1176
+	{
1177
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1178
+		foreach ($datetimes as $datetime) {
1179
+			if ($datetime instanceof EE_Datetime) {
1180
+				$datetime->decreaseReserved($qty);
1181
+			}
1182
+		}
1183
+	}
1184
+
1185
+
1186
+	/**
1187
+	 * Gets ticket quantity
1188
+	 *
1189
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1190
+	 *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1191
+	 *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1192
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1193
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1194
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1195
+	 * @return int
1196
+	 * @throws EE_Error
1197
+	 * @throws ReflectionException
1198
+	 */
1199
+	public function qty($context = '')
1200
+	{
1201
+		switch ($context) {
1202
+			case 'reg_limit':
1203
+				return $this->real_quantity_on_ticket();
1204
+			case 'saleable':
1205
+				return $this->real_quantity_on_ticket('saleable');
1206
+			default:
1207
+				return $this->get_raw('TKT_qty');
1208
+		}
1209
+	}
1210
+
1211
+
1212
+	/**
1213
+	 * Gets ticket quantity
1214
+	 *
1215
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1216
+	 *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1217
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1218
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1219
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1220
+	 * @param int    $DTT_ID      the primary key for a particular datetime.
1221
+	 *                            set to 0 for all related datetimes
1222
+	 * @return int|float          int for finite quantity or float for INF
1223
+	 * @throws EE_Error
1224
+	 * @throws ReflectionException
1225
+	 */
1226
+	public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1227
+	{
1228
+		$raw = $this->get_raw('TKT_qty');
1229
+		// return immediately if it's zero
1230
+		if ($raw === 0) {
1231
+			return $raw;
1232
+		}
1233
+		// echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1234
+		// ensure qty doesn't exceed raw value for THIS ticket
1235
+		$qty = min(EE_INF, $raw);
1236
+		// echo "\n . qty: " . $qty . '<br />';
1237
+		// calculate this ticket's total sales and reservations
1238
+		$sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1239
+		// echo "\n . sold: " . $this->sold() . '<br />';
1240
+		// echo "\n . reserved: " . $this->reserved() . '<br />';
1241
+		// echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1242
+		// first we need to calculate the maximum number of tickets available for the datetime
1243
+		// do we want data for one datetime or all of them ?
1244
+		$query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : [];
1245
+		$datetimes    = $this->datetimes($query_params);
1246
+		if (is_array($datetimes) && ! empty($datetimes)) {
1247
+			foreach ($datetimes as $datetime) {
1248
+				if ($datetime instanceof EE_Datetime) {
1249
+					$datetime->refresh_from_db();
1250
+					// echo "\n . . datetime name: " . $datetime->name() . '<br />';
1251
+					// echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1252
+					// initialize with no restrictions for each datetime
1253
+					// but adjust datetime qty based on datetime reg limit
1254
+					$datetime_qty = min(EE_INF, $datetime->reg_limit());
1255
+					// echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1256
+					// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1257
+					// if we want the actual saleable amount, then we need to consider OTHER ticket sales
1258
+					// and reservations for this datetime, that do NOT include sales and reservations
1259
+					// for this ticket (so we add $this->sold() and $this->reserved() back in)
1260
+					if ($context === 'saleable') {
1261
+						$datetime_qty = max(
1262
+							$datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1263
+							0
1264
+						);
1265
+						// echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1266
+						// echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1267
+						// echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1268
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1269
+						$datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1270
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1271
+					}
1272
+					$qty = min($datetime_qty, $qty);
1273
+					// echo "\n . . qty: " . $qty . '<br />';
1274
+				}
1275
+			}
1276
+		}
1277
+		// NOW that we know the  maximum number of tickets available for the datetime
1278
+		// we can finally factor in the details for this specific ticket
1279
+		if ($qty > 0 && $context === 'saleable') {
1280
+			// and subtract the sales for THIS ticket
1281
+			$qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1282
+			// echo "\n . qty: " . $qty . '<br />';
1283
+		}
1284
+		// echo "\nFINAL QTY: " . $qty . "<br /><br />";
1285
+		return $qty;
1286
+	}
1287
+
1288
+
1289
+	/**
1290
+	 * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1291
+	 *
1292
+	 * @param int $qty
1293
+	 * @return void
1294
+	 * @throws EE_Error
1295
+	 * @throws ReflectionException
1296
+	 */
1297
+	public function set_qty($qty)
1298
+	{
1299
+		$datetimes = $this->datetimes();
1300
+		foreach ($datetimes as $datetime) {
1301
+			if ($datetime instanceof EE_Datetime) {
1302
+				$qty = min($qty, $datetime->reg_limit());
1303
+			}
1304
+		}
1305
+		$this->set('TKT_qty', $qty);
1306
+	}
1307
+
1308
+
1309
+	/**
1310
+	 * Gets uses
1311
+	 *
1312
+	 * @return int
1313
+	 * @throws EE_Error
1314
+	 * @throws ReflectionException
1315
+	 */
1316
+	public function uses()
1317
+	{
1318
+		return $this->get('TKT_uses');
1319
+	}
1320
+
1321
+
1322
+	/**
1323
+	 * Sets uses
1324
+	 *
1325
+	 * @param int $uses
1326
+	 * @return void
1327
+	 * @throws EE_Error
1328
+	 * @throws ReflectionException
1329
+	 */
1330
+	public function set_uses($uses)
1331
+	{
1332
+		$this->set('TKT_uses', $uses);
1333
+	}
1334
+
1335
+
1336
+	/**
1337
+	 * returns whether ticket is required or not.
1338
+	 *
1339
+	 * @return boolean
1340
+	 * @throws EE_Error
1341
+	 * @throws ReflectionException
1342
+	 */
1343
+	public function required()
1344
+	{
1345
+		return $this->get('TKT_required');
1346
+	}
1347
+
1348
+
1349
+	/**
1350
+	 * sets the TKT_required property
1351
+	 *
1352
+	 * @param boolean $required
1353
+	 * @return void
1354
+	 * @throws EE_Error
1355
+	 * @throws ReflectionException
1356
+	 */
1357
+	public function set_required($required)
1358
+	{
1359
+		$this->set('TKT_required', $required);
1360
+	}
1361
+
1362
+
1363
+	/**
1364
+	 * Gets taxable
1365
+	 *
1366
+	 * @return boolean
1367
+	 * @throws EE_Error
1368
+	 * @throws ReflectionException
1369
+	 */
1370
+	public function taxable()
1371
+	{
1372
+		return $this->get('TKT_taxable');
1373
+	}
1374
+
1375
+
1376
+	/**
1377
+	 * Sets taxable
1378
+	 *
1379
+	 * @param boolean $taxable
1380
+	 * @return void
1381
+	 * @throws EE_Error
1382
+	 * @throws ReflectionException
1383
+	 */
1384
+	public function set_taxable($taxable)
1385
+	{
1386
+		$this->set('TKT_taxable', $taxable);
1387
+	}
1388
+
1389
+
1390
+	/**
1391
+	 * Gets is_default
1392
+	 *
1393
+	 * @return boolean
1394
+	 * @throws EE_Error
1395
+	 * @throws ReflectionException
1396
+	 */
1397
+	public function is_default()
1398
+	{
1399
+		return $this->get('TKT_is_default');
1400
+	}
1401
+
1402
+
1403
+	/**
1404
+	 * Sets is_default
1405
+	 *
1406
+	 * @param boolean $is_default
1407
+	 * @return void
1408
+	 * @throws EE_Error
1409
+	 * @throws ReflectionException
1410
+	 */
1411
+	public function set_is_default($is_default)
1412
+	{
1413
+		$this->set('TKT_is_default', $is_default);
1414
+	}
1415
+
1416
+
1417
+	/**
1418
+	 * Gets order
1419
+	 *
1420
+	 * @return int
1421
+	 * @throws EE_Error
1422
+	 * @throws ReflectionException
1423
+	 */
1424
+	public function order()
1425
+	{
1426
+		return $this->get('TKT_order');
1427
+	}
1428
+
1429
+
1430
+	/**
1431
+	 * Sets order
1432
+	 *
1433
+	 * @param int $order
1434
+	 * @return void
1435
+	 * @throws EE_Error
1436
+	 * @throws ReflectionException
1437
+	 */
1438
+	public function set_order($order)
1439
+	{
1440
+		$this->set('TKT_order', $order);
1441
+	}
1442
+
1443
+
1444
+	/**
1445
+	 * Gets row
1446
+	 *
1447
+	 * @return int
1448
+	 * @throws EE_Error
1449
+	 * @throws ReflectionException
1450
+	 */
1451
+	public function row()
1452
+	{
1453
+		return $this->get('TKT_row');
1454
+	}
1455
+
1456
+
1457
+	/**
1458
+	 * Sets row
1459
+	 *
1460
+	 * @param int $row
1461
+	 * @return void
1462
+	 * @throws EE_Error
1463
+	 * @throws ReflectionException
1464
+	 */
1465
+	public function set_row($row)
1466
+	{
1467
+		$this->set('TKT_row', $row);
1468
+	}
1469
+
1470
+
1471
+	/**
1472
+	 * Gets deleted
1473
+	 *
1474
+	 * @return boolean
1475
+	 * @throws EE_Error
1476
+	 * @throws ReflectionException
1477
+	 */
1478
+	public function deleted()
1479
+	{
1480
+		return $this->get('TKT_deleted');
1481
+	}
1482
+
1483
+
1484
+	/**
1485
+	 * Sets deleted
1486
+	 *
1487
+	 * @param boolean $deleted
1488
+	 * @return void
1489
+	 * @throws EE_Error
1490
+	 * @throws ReflectionException
1491
+	 */
1492
+	public function set_deleted($deleted)
1493
+	{
1494
+		$this->set('TKT_deleted', $deleted);
1495
+	}
1496
+
1497
+
1498
+	/**
1499
+	 * Gets parent
1500
+	 *
1501
+	 * @return int
1502
+	 * @throws EE_Error
1503
+	 * @throws ReflectionException
1504
+	 */
1505
+	public function parent_ID()
1506
+	{
1507
+		return $this->get('TKT_parent');
1508
+	}
1509
+
1510
+
1511
+	/**
1512
+	 * Sets parent
1513
+	 *
1514
+	 * @param int $parent
1515
+	 * @return void
1516
+	 * @throws EE_Error
1517
+	 * @throws ReflectionException
1518
+	 */
1519
+	public function set_parent_ID($parent)
1520
+	{
1521
+		$this->set('TKT_parent', $parent);
1522
+	}
1523
+
1524
+
1525
+	/**
1526
+	 * @return boolean
1527
+	 * @throws EE_Error
1528
+	 * @throws InvalidArgumentException
1529
+	 * @throws InvalidDataTypeException
1530
+	 * @throws InvalidInterfaceException
1531
+	 * @throws ReflectionException
1532
+	 */
1533
+	public function reverse_calculate()
1534
+	{
1535
+		return $this->get('TKT_reverse_calculate');
1536
+	}
1537
+
1538
+
1539
+	/**
1540
+	 * @param boolean $reverse_calculate
1541
+	 * @throws EE_Error
1542
+	 * @throws InvalidArgumentException
1543
+	 * @throws InvalidDataTypeException
1544
+	 * @throws InvalidInterfaceException
1545
+	 * @throws ReflectionException
1546
+	 */
1547
+	public function set_reverse_calculate($reverse_calculate)
1548
+	{
1549
+		$this->set('TKT_reverse_calculate', $reverse_calculate);
1550
+	}
1551
+
1552
+
1553
+	/**
1554
+	 * Gets a string which is handy for showing in gateways etc that describes the ticket.
1555
+	 *
1556
+	 * @return string
1557
+	 * @throws EE_Error
1558
+	 * @throws ReflectionException
1559
+	 */
1560
+	public function name_and_info()
1561
+	{
1562
+		$times = [];
1563
+		foreach ($this->datetimes() as $datetime) {
1564
+			$times[] = $datetime->start_date_and_time();
1565
+		}
1566
+		/* translators: %1$s ticket name, %2$s start datetimes separated by comma, %3$s ticket price */
1567
+		return sprintf(
1568
+			esc_html__('%1$s @ %2$s for %3$s', 'event_espresso'),
1569
+			$this->name(),
1570
+			implode(', ', $times),
1571
+			$this->pretty_price()
1572
+		);
1573
+	}
1574
+
1575
+
1576
+	/**
1577
+	 * Gets name
1578
+	 *
1579
+	 * @return string
1580
+	 * @throws EE_Error
1581
+	 * @throws ReflectionException
1582
+	 */
1583
+	public function name()
1584
+	{
1585
+		return $this->get('TKT_name');
1586
+	}
1587
+
1588
+
1589
+	/**
1590
+	 * Gets price
1591
+	 *
1592
+	 * @return float
1593
+	 * @throws EE_Error
1594
+	 * @throws ReflectionException
1595
+	 */
1596
+	public function price()
1597
+	{
1598
+		return $this->get('TKT_price');
1599
+	}
1600
+
1601
+
1602
+	/**
1603
+	 * Gets all the registrations for this ticket
1604
+	 *
1605
+	 * @param array $query_params
1606
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1607
+	 * @return EE_Registration[]|EE_Base_Class[]
1608
+	 * @throws EE_Error
1609
+	 * @throws ReflectionException
1610
+	 */
1611
+	public function registrations($query_params = [])
1612
+	{
1613
+		return $this->get_many_related('Registration', $query_params);
1614
+	}
1615
+
1616
+
1617
+	/**
1618
+	 * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1619
+	 *
1620
+	 * @return int
1621
+	 * @throws EE_Error
1622
+	 * @throws ReflectionException
1623
+	 */
1624
+	public function update_tickets_sold()
1625
+	{
1626
+		$count_regs_for_this_ticket = $this->count_registrations(
1627
+			[
1628
+				[
1629
+					'STS_ID'      => EEM_Registration::status_id_approved,
1630
+					'REG_deleted' => 0,
1631
+				],
1632
+			]
1633
+		);
1634
+		$this->set_sold($count_regs_for_this_ticket);
1635
+		$this->save();
1636
+		return $count_regs_for_this_ticket;
1637
+	}
1638
+
1639
+
1640
+	/**
1641
+	 * Counts the registrations for this ticket
1642
+	 *
1643
+	 * @param array $query_params
1644
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1645
+	 * @return int
1646
+	 * @throws EE_Error
1647
+	 * @throws ReflectionException
1648
+	 */
1649
+	public function count_registrations($query_params = [])
1650
+	{
1651
+		return $this->count_related('Registration', $query_params);
1652
+	}
1653
+
1654
+
1655
+	/**
1656
+	 * Implementation for EEI_Has_Icon interface method.
1657
+	 *
1658
+	 * @return string
1659
+	 * @see EEI_Visual_Representation for comments
1660
+	 */
1661
+	public function get_icon()
1662
+	{
1663
+		return '<span class="dashicons dashicons-tickets-alt"></span>';
1664
+	}
1665
+
1666
+
1667
+	/**
1668
+	 * Implementation of the EEI_Event_Relation interface method
1669
+	 *
1670
+	 * @return EE_Event
1671
+	 * @throws EE_Error
1672
+	 * @throws UnexpectedEntityException
1673
+	 * @throws ReflectionException
1674
+	 * @see EEI_Event_Relation for comments
1675
+	 */
1676
+	public function get_related_event()
1677
+	{
1678
+		// get one datetime to use for getting the event
1679
+		$datetime = $this->first_datetime();
1680
+		if (! $datetime instanceof EE_Datetime) {
1681
+			throw new UnexpectedEntityException(
1682
+				$datetime,
1683
+				'EE_Datetime',
1684
+				sprintf(
1685
+					esc_html__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1686
+					$this->name()
1687
+				)
1688
+			);
1689
+		}
1690
+		$event = $datetime->event();
1691
+		if (! $event instanceof EE_Event) {
1692
+			throw new UnexpectedEntityException(
1693
+				$event,
1694
+				'EE_Event',
1695
+				sprintf(
1696
+					esc_html__('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1697
+					$this->name()
1698
+				)
1699
+			);
1700
+		}
1701
+		return $event;
1702
+	}
1703
+
1704
+
1705
+	/**
1706
+	 * Implementation of the EEI_Event_Relation interface method
1707
+	 *
1708
+	 * @return string
1709
+	 * @throws UnexpectedEntityException
1710
+	 * @throws EE_Error
1711
+	 * @throws ReflectionException
1712
+	 * @see EEI_Event_Relation for comments
1713
+	 */
1714
+	public function get_event_name()
1715
+	{
1716
+		$event = $this->get_related_event();
1717
+		return $event instanceof EE_Event ? $event->name() : '';
1718
+	}
1719
+
1720
+
1721
+	/**
1722
+	 * Implementation of the EEI_Event_Relation interface method
1723
+	 *
1724
+	 * @return int
1725
+	 * @throws UnexpectedEntityException
1726
+	 * @throws EE_Error
1727
+	 * @throws ReflectionException
1728
+	 * @see EEI_Event_Relation for comments
1729
+	 */
1730
+	public function get_event_ID()
1731
+	{
1732
+		$event = $this->get_related_event();
1733
+		return $event instanceof EE_Event ? $event->ID() : 0;
1734
+	}
1735
+
1736
+
1737
+	/**
1738
+	 * This simply returns whether a ticket can be permanently deleted or not.
1739
+	 * The criteria for determining this is whether the ticket has any related registrations.
1740
+	 * If there are none then it can be permanently deleted.
1741
+	 *
1742
+	 * @return bool
1743
+	 * @throws EE_Error
1744
+	 * @throws ReflectionException
1745
+	 */
1746
+	public function is_permanently_deleteable()
1747
+	{
1748
+		return $this->count_registrations() === 0;
1749
+	}
1750
+
1751
+
1752
+	/**
1753
+	 * @return int
1754
+	 * @throws EE_Error
1755
+	 * @throws ReflectionException
1756
+	 * @since   5.0.0.p
1757
+	 */
1758
+	public function visibility(): int
1759
+	{
1760
+		return $this->get('TKT_visibility');
1761
+	}
1762
+
1763
+
1764
+	/**
1765
+	 * @return bool
1766
+	 * @throws EE_Error
1767
+	 * @throws ReflectionException
1768
+	 * @since   5.0.0.p
1769
+	 */
1770
+	public function isHidden(): bool
1771
+	{
1772
+		return $this->visibility() === EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1773
+	}
1774
+
1775
+
1776
+	/**
1777
+	 * @return bool
1778
+	 * @throws EE_Error
1779
+	 * @throws ReflectionException
1780
+	 * @since   5.0.0.p
1781
+	 */
1782
+	public function isNotHidden(): bool
1783
+	{
1784
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1785
+	}
1786
+
1787
+
1788
+	/**
1789
+	 * @return bool
1790
+	 * @throws EE_Error
1791
+	 * @throws ReflectionException
1792
+	 * @since   5.0.0.p
1793
+	 */
1794
+	public function isPublicOnly(): bool
1795
+	{
1796
+		return $this->isNotHidden() && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE;
1797
+	}
1798
+
1799
+
1800
+	/**
1801
+	 * @return bool
1802
+	 * @throws EE_Error
1803
+	 * @throws ReflectionException
1804
+	 * @since   5.0.0.p
1805
+	 */
1806
+	public function isMembersOnly(): bool
1807
+	{
1808
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE
1809
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE;
1810
+	}
1811
+
1812
+
1813
+	/**
1814
+	 * @return bool
1815
+	 * @throws EE_Error
1816
+	 * @throws ReflectionException
1817
+	 * @since   5.0.0.p
1818
+	 */
1819
+	public function isAdminsOnly(): bool
1820
+	{
1821
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE
1822
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE;
1823
+	}
1824
+
1825
+
1826
+	/**
1827
+	 * @return bool
1828
+	 * @throws EE_Error
1829
+	 * @throws ReflectionException
1830
+	 * @since   5.0.0.p
1831
+	 */
1832
+	public function isAdminUiOnly(): bool
1833
+	{
1834
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE
1835
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE;
1836
+	}
1837
+
1838
+
1839
+	/**
1840
+	 * @param int $visibility
1841
+	 * @throws EE_Error
1842
+	 * @throws ReflectionException
1843
+	 * @since   5.0.0.p
1844
+	 */
1845
+	public function set_visibility(int $visibility)
1846
+	{
1847
+
1848
+		$ticket_visibility_options = $this->_model->ticketVisibilityOptions();
1849
+		$ticket_visibility         = -1;
1850
+		foreach ($ticket_visibility_options as $ticket_visibility_option) {
1851
+			if ($visibility === $ticket_visibility_option) {
1852
+				$ticket_visibility = $visibility;
1853
+			}
1854
+		}
1855
+		if ($ticket_visibility === -1) {
1856
+			throw new DomainException(
1857
+				sprintf(
1858
+					esc_html__(
1859
+						'The supplied ticket visibility setting of "%1$s" is not valid. It needs to match one of the keys in the following array:%2$s %3$s ',
1860
+						'event_espresso'
1861
+					),
1862
+					$visibility,
1863
+					'<br />',
1864
+					var_export($ticket_visibility_options, true)
1865
+				)
1866
+			);
1867
+		}
1868
+		$this->set('TKT_visibility', $ticket_visibility);
1869
+	}
1870
+
1871
+
1872
+	/**
1873
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1874
+	 * @param string                   $relationName
1875
+	 * @param array                    $extra_join_model_fields_n_values
1876
+	 * @param string|null              $cache_id
1877
+	 * @return EE_Base_Class
1878
+	 * @throws EE_Error
1879
+	 * @throws ReflectionException
1880
+	 * @since   5.0.0.p
1881
+	 */
1882
+	public function _add_relation_to(
1883
+		$otherObjectModelObjectOrID,
1884
+		$relationName,
1885
+		$extra_join_model_fields_n_values = [],
1886
+		$cache_id = null
1887
+	) {
1888
+		if ($relationName === 'Datetime' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1889
+			/** @var EE_Datetime $datetime */
1890
+			$datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1891
+			$datetime->increaseSold($this->sold(), false);
1892
+			$datetime->increaseReserved($this->reserved());
1893
+			$datetime->save();
1894
+			$otherObjectModelObjectOrID = $datetime;
1895
+		}
1896
+		return parent::_add_relation_to(
1897
+			$otherObjectModelObjectOrID,
1898
+			$relationName,
1899
+			$extra_join_model_fields_n_values,
1900
+			$cache_id
1901
+		);
1902
+	}
1903
+
1904
+
1905
+	/**
1906
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1907
+	 * @param string                   $relationName
1908
+	 * @param array                    $where_query
1909
+	 * @return bool|EE_Base_Class|null
1910
+	 * @throws EE_Error
1911
+	 * @throws ReflectionException
1912
+	 * @since   5.0.0.p
1913
+	 */
1914
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1915
+	{
1916
+		// if we're adding a new relation to a datetime
1917
+		if ($relationName === 'Datetime' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1918
+			/** @var EE_Datetime $datetime */
1919
+			$datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1920
+			$datetime->decreaseSold($this->sold());
1921
+			$datetime->decreaseReserved($this->reserved());
1922
+			$datetime->save();
1923
+			$otherObjectModelObjectOrID = $datetime;
1924
+		}
1925
+		return parent::_remove_relation_to(
1926
+			$otherObjectModelObjectOrID,
1927
+			$relationName,
1928
+			$where_query
1929
+		);
1930
+	}
1931
+
1932
+
1933
+	/**
1934
+	 * Removes ALL the related things for the $relationName.
1935
+	 *
1936
+	 * @param string $relationName
1937
+	 * @param array  $where_query_params
1938
+	 * @return EE_Base_Class
1939
+	 * @throws ReflectionException
1940
+	 * @throws InvalidArgumentException
1941
+	 * @throws InvalidInterfaceException
1942
+	 * @throws InvalidDataTypeException
1943
+	 * @throws EE_Error
1944
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1945
+	 */
1946
+	public function _remove_relations($relationName, $where_query_params = [])
1947
+	{
1948
+		if ($relationName === 'Datetime') {
1949
+			$datetimes = $this->datetimes();
1950
+			foreach ($datetimes as $datetime) {
1951
+				$datetime->decreaseSold($this->sold());
1952
+				$datetime->decreaseReserved($this->reserved());
1953
+				$datetime->save();
1954
+			}
1955
+		}
1956
+		return parent::_remove_relations($relationName, $where_query_params);
1957
+	}
1958
+
1959
+
1960
+	/*******************************************************************
1961 1961
      ***********************  DEPRECATED METHODS  **********************
1962 1962
      *******************************************************************/
1963 1963
 
1964 1964
 
1965
-    /**
1966
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1967
-     * associated datetimes.
1968
-     *
1969
-     * @param int $qty
1970
-     * @return void
1971
-     * @throws EE_Error
1972
-     * @throws InvalidArgumentException
1973
-     * @throws InvalidDataTypeException
1974
-     * @throws InvalidInterfaceException
1975
-     * @throws ReflectionException
1976
-     * @deprecated 4.9.80.p
1977
-     */
1978
-    public function increase_sold($qty = 1)
1979
-    {
1980
-        EE_Error::doing_it_wrong(
1981
-            __FUNCTION__,
1982
-            esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1983
-            '4.9.80.p',
1984
-            '5.0.0.p'
1985
-        );
1986
-        $this->increaseSold($qty);
1987
-    }
1988
-
1989
-
1990
-    /**
1991
-     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1992
-     *
1993
-     * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
1994
-     *                 Negative means to decreases old counts (and increase reserved counts).
1995
-     * @throws EE_Error
1996
-     * @throws InvalidArgumentException
1997
-     * @throws InvalidDataTypeException
1998
-     * @throws InvalidInterfaceException
1999
-     * @throws ReflectionException
2000
-     * @deprecated 4.9.80.p
2001
-     */
2002
-    protected function _increase_sold_for_datetimes($qty)
2003
-    {
2004
-        EE_Error::doing_it_wrong(
2005
-            __FUNCTION__,
2006
-            esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
2007
-            '4.9.80.p',
2008
-            '5.0.0.p'
2009
-        );
2010
-        $this->increaseSoldForDatetimes($qty);
2011
-    }
2012
-
2013
-
2014
-    /**
2015
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
2016
-     * DB and then updates the model objects.
2017
-     * Does not affect the reserved counts.
2018
-     *
2019
-     * @param int $qty
2020
-     * @return void
2021
-     * @throws EE_Error
2022
-     * @throws InvalidArgumentException
2023
-     * @throws InvalidDataTypeException
2024
-     * @throws InvalidInterfaceException
2025
-     * @throws ReflectionException
2026
-     * @deprecated 4.9.80.p
2027
-     */
2028
-    public function decrease_sold($qty = 1)
2029
-    {
2030
-        EE_Error::doing_it_wrong(
2031
-            __FUNCTION__,
2032
-            esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
2033
-            '4.9.80.p',
2034
-            '5.0.0.p'
2035
-        );
2036
-        $this->decreaseSold($qty);
2037
-    }
2038
-
2039
-
2040
-    /**
2041
-     * Decreases sold on related datetimes
2042
-     *
2043
-     * @param int $qty
2044
-     * @return void
2045
-     * @throws EE_Error
2046
-     * @throws InvalidArgumentException
2047
-     * @throws InvalidDataTypeException
2048
-     * @throws InvalidInterfaceException
2049
-     * @throws ReflectionException
2050
-     * @deprecated 4.9.80.p
2051
-     */
2052
-    protected function _decrease_sold_for_datetimes($qty = 1)
2053
-    {
2054
-        EE_Error::doing_it_wrong(
2055
-            __FUNCTION__,
2056
-            esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
2057
-            '4.9.80.p',
2058
-            '5.0.0.p'
2059
-        );
2060
-        $this->decreaseSoldForDatetimes($qty);
2061
-    }
2062
-
2063
-
2064
-    /**
2065
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
2066
-     *
2067
-     * @param int    $qty
2068
-     * @param string $source
2069
-     * @return bool whether we successfully reserved the ticket or not.
2070
-     * @throws EE_Error
2071
-     * @throws InvalidArgumentException
2072
-     * @throws ReflectionException
2073
-     * @throws InvalidDataTypeException
2074
-     * @throws InvalidInterfaceException
2075
-     * @deprecated 4.9.80.p
2076
-     */
2077
-    public function increase_reserved($qty = 1, $source = 'unknown')
2078
-    {
2079
-        EE_Error::doing_it_wrong(
2080
-            __FUNCTION__,
2081
-            esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
2082
-            '4.9.80.p',
2083
-            '5.0.0.p'
2084
-        );
2085
-        return $this->increaseReserved($qty);
2086
-    }
2087
-
2088
-
2089
-    /**
2090
-     * Increases sold on related datetimes
2091
-     *
2092
-     * @param int $qty
2093
-     * @return boolean indicating success
2094
-     * @throws EE_Error
2095
-     * @throws InvalidArgumentException
2096
-     * @throws InvalidDataTypeException
2097
-     * @throws InvalidInterfaceException
2098
-     * @throws ReflectionException
2099
-     * @deprecated 4.9.80.p
2100
-     */
2101
-    protected function _increase_reserved_for_datetimes($qty = 1)
2102
-    {
2103
-        EE_Error::doing_it_wrong(
2104
-            __FUNCTION__,
2105
-            esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
2106
-            '4.9.80.p',
2107
-            '5.0.0.p'
2108
-        );
2109
-        return $this->increaseReservedForDatetimes($qty);
2110
-    }
2111
-
2112
-
2113
-    /**
2114
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
2115
-     *
2116
-     * @param int    $qty
2117
-     * @param bool   $adjust_datetimes
2118
-     * @param string $source
2119
-     * @return void
2120
-     * @throws EE_Error
2121
-     * @throws InvalidArgumentException
2122
-     * @throws ReflectionException
2123
-     * @throws InvalidDataTypeException
2124
-     * @throws InvalidInterfaceException
2125
-     * @deprecated 4.9.80.p
2126
-     */
2127
-    public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
2128
-    {
2129
-        EE_Error::doing_it_wrong(
2130
-            __FUNCTION__,
2131
-            esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
2132
-            '4.9.80.p',
2133
-            '5.0.0.p'
2134
-        );
2135
-        $this->decreaseReserved($qty);
2136
-    }
2137
-
2138
-
2139
-    /**
2140
-     * Decreases reserved on related datetimes
2141
-     *
2142
-     * @param int $qty
2143
-     * @return void
2144
-     * @throws EE_Error
2145
-     * @throws InvalidArgumentException
2146
-     * @throws ReflectionException
2147
-     * @throws InvalidDataTypeException
2148
-     * @throws InvalidInterfaceException
2149
-     * @deprecated 4.9.80.p
2150
-     */
2151
-    protected function _decrease_reserved_for_datetimes($qty = 1)
2152
-    {
2153
-        EE_Error::doing_it_wrong(
2154
-            __FUNCTION__,
2155
-            esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
2156
-            '4.9.80.p',
2157
-            '5.0.0.p'
2158
-        );
2159
-        $this->decreaseReservedForDatetimes($qty);
2160
-    }
1965
+	/**
1966
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1967
+	 * associated datetimes.
1968
+	 *
1969
+	 * @param int $qty
1970
+	 * @return void
1971
+	 * @throws EE_Error
1972
+	 * @throws InvalidArgumentException
1973
+	 * @throws InvalidDataTypeException
1974
+	 * @throws InvalidInterfaceException
1975
+	 * @throws ReflectionException
1976
+	 * @deprecated 4.9.80.p
1977
+	 */
1978
+	public function increase_sold($qty = 1)
1979
+	{
1980
+		EE_Error::doing_it_wrong(
1981
+			__FUNCTION__,
1982
+			esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1983
+			'4.9.80.p',
1984
+			'5.0.0.p'
1985
+		);
1986
+		$this->increaseSold($qty);
1987
+	}
1988
+
1989
+
1990
+	/**
1991
+	 * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1992
+	 *
1993
+	 * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
1994
+	 *                 Negative means to decreases old counts (and increase reserved counts).
1995
+	 * @throws EE_Error
1996
+	 * @throws InvalidArgumentException
1997
+	 * @throws InvalidDataTypeException
1998
+	 * @throws InvalidInterfaceException
1999
+	 * @throws ReflectionException
2000
+	 * @deprecated 4.9.80.p
2001
+	 */
2002
+	protected function _increase_sold_for_datetimes($qty)
2003
+	{
2004
+		EE_Error::doing_it_wrong(
2005
+			__FUNCTION__,
2006
+			esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
2007
+			'4.9.80.p',
2008
+			'5.0.0.p'
2009
+		);
2010
+		$this->increaseSoldForDatetimes($qty);
2011
+	}
2012
+
2013
+
2014
+	/**
2015
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
2016
+	 * DB and then updates the model objects.
2017
+	 * Does not affect the reserved counts.
2018
+	 *
2019
+	 * @param int $qty
2020
+	 * @return void
2021
+	 * @throws EE_Error
2022
+	 * @throws InvalidArgumentException
2023
+	 * @throws InvalidDataTypeException
2024
+	 * @throws InvalidInterfaceException
2025
+	 * @throws ReflectionException
2026
+	 * @deprecated 4.9.80.p
2027
+	 */
2028
+	public function decrease_sold($qty = 1)
2029
+	{
2030
+		EE_Error::doing_it_wrong(
2031
+			__FUNCTION__,
2032
+			esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
2033
+			'4.9.80.p',
2034
+			'5.0.0.p'
2035
+		);
2036
+		$this->decreaseSold($qty);
2037
+	}
2038
+
2039
+
2040
+	/**
2041
+	 * Decreases sold on related datetimes
2042
+	 *
2043
+	 * @param int $qty
2044
+	 * @return void
2045
+	 * @throws EE_Error
2046
+	 * @throws InvalidArgumentException
2047
+	 * @throws InvalidDataTypeException
2048
+	 * @throws InvalidInterfaceException
2049
+	 * @throws ReflectionException
2050
+	 * @deprecated 4.9.80.p
2051
+	 */
2052
+	protected function _decrease_sold_for_datetimes($qty = 1)
2053
+	{
2054
+		EE_Error::doing_it_wrong(
2055
+			__FUNCTION__,
2056
+			esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
2057
+			'4.9.80.p',
2058
+			'5.0.0.p'
2059
+		);
2060
+		$this->decreaseSoldForDatetimes($qty);
2061
+	}
2062
+
2063
+
2064
+	/**
2065
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
2066
+	 *
2067
+	 * @param int    $qty
2068
+	 * @param string $source
2069
+	 * @return bool whether we successfully reserved the ticket or not.
2070
+	 * @throws EE_Error
2071
+	 * @throws InvalidArgumentException
2072
+	 * @throws ReflectionException
2073
+	 * @throws InvalidDataTypeException
2074
+	 * @throws InvalidInterfaceException
2075
+	 * @deprecated 4.9.80.p
2076
+	 */
2077
+	public function increase_reserved($qty = 1, $source = 'unknown')
2078
+	{
2079
+		EE_Error::doing_it_wrong(
2080
+			__FUNCTION__,
2081
+			esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
2082
+			'4.9.80.p',
2083
+			'5.0.0.p'
2084
+		);
2085
+		return $this->increaseReserved($qty);
2086
+	}
2087
+
2088
+
2089
+	/**
2090
+	 * Increases sold on related datetimes
2091
+	 *
2092
+	 * @param int $qty
2093
+	 * @return boolean indicating success
2094
+	 * @throws EE_Error
2095
+	 * @throws InvalidArgumentException
2096
+	 * @throws InvalidDataTypeException
2097
+	 * @throws InvalidInterfaceException
2098
+	 * @throws ReflectionException
2099
+	 * @deprecated 4.9.80.p
2100
+	 */
2101
+	protected function _increase_reserved_for_datetimes($qty = 1)
2102
+	{
2103
+		EE_Error::doing_it_wrong(
2104
+			__FUNCTION__,
2105
+			esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
2106
+			'4.9.80.p',
2107
+			'5.0.0.p'
2108
+		);
2109
+		return $this->increaseReservedForDatetimes($qty);
2110
+	}
2111
+
2112
+
2113
+	/**
2114
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
2115
+	 *
2116
+	 * @param int    $qty
2117
+	 * @param bool   $adjust_datetimes
2118
+	 * @param string $source
2119
+	 * @return void
2120
+	 * @throws EE_Error
2121
+	 * @throws InvalidArgumentException
2122
+	 * @throws ReflectionException
2123
+	 * @throws InvalidDataTypeException
2124
+	 * @throws InvalidInterfaceException
2125
+	 * @deprecated 4.9.80.p
2126
+	 */
2127
+	public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
2128
+	{
2129
+		EE_Error::doing_it_wrong(
2130
+			__FUNCTION__,
2131
+			esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
2132
+			'4.9.80.p',
2133
+			'5.0.0.p'
2134
+		);
2135
+		$this->decreaseReserved($qty);
2136
+	}
2137
+
2138
+
2139
+	/**
2140
+	 * Decreases reserved on related datetimes
2141
+	 *
2142
+	 * @param int $qty
2143
+	 * @return void
2144
+	 * @throws EE_Error
2145
+	 * @throws InvalidArgumentException
2146
+	 * @throws ReflectionException
2147
+	 * @throws InvalidDataTypeException
2148
+	 * @throws InvalidInterfaceException
2149
+	 * @deprecated 4.9.80.p
2150
+	 */
2151
+	protected function _decrease_reserved_for_datetimes($qty = 1)
2152
+	{
2153
+		EE_Error::doing_it_wrong(
2154
+			__FUNCTION__,
2155
+			esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
2156
+			'4.9.80.p',
2157
+			'5.0.0.p'
2158
+		);
2159
+		$this->decreaseReservedForDatetimes($qty);
2160
+	}
2161 2161
 }
Please login to merge, or discard this patch.
core/interfaces/line_items/EEI_Line_Item_Filter.interface.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -15,11 +15,11 @@
 block discarded – undo
15 15
  */
16 16
 interface EEI_Line_Item_Filter
17 17
 {
18
-    /**
19
-     * process
20
-     *
21
-     * @param EE_Line_Item $line_item
22
-     * @return EE_Line_Item
23
-     */
24
-    public function process(EE_Line_Item $line_item);
18
+	/**
19
+	 * process
20
+	 *
21
+	 * @param EE_Line_Item $line_item
22
+	 * @return EE_Line_Item
23
+	 */
24
+	public function process(EE_Line_Item $line_item);
25 25
 }
Please login to merge, or discard this patch.
admin/new/pricing/templates/event_tickets_metabox_main.template.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -152,10 +152,10 @@
 block discarded – undo
152 152
     <div style="clear:both"></div>
153 153
 
154 154
     <?php
155
-    if (isset($status_change_notice)) {
156
-        echo $status_change_notice;
157
-    }
158
-    ?>
155
+	if (isset($status_change_notice)) {
156
+		echo $status_change_notice;
157
+	}
158
+	?>
159 159
 
160 160
     <div class="available-tickets-container">
161 161
         <div class="ee-layout-row ee-layout-row--fixed">
Please login to merge, or discard this patch.
pricing/templates/event_tickets_datetime_attached_tickets_row.template.php 2 patches
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -23,10 +23,10 @@  discard block
 block discarded – undo
23 23
             <div class="ee-editor-id-container">
24 24
                 <h3 class="ee-item-id">
25 25
                     <?php echo esc_html(
26
-                        $DTT_ID
27
-                            ? sprintf( __('Datetime ID: %d', 'event_espresso'), $DTT_ID )
28
-                            : ''
29
-                    ); ?>
26
+						$DTT_ID
27
+							? sprintf( __('Datetime ID: %d', 'event_espresso'), $DTT_ID )
28
+							: ''
29
+					); ?>
30 30
                 </h3>
31 31
             </div>
32 32
             <div class="datetime-description-container">
@@ -39,10 +39,10 @@  discard block
 block discarded – undo
39 39
                 ><?php echo esc_textarea($DTT_description); ?></textarea>
40 40
             </div>
41 41
             <?php do_action(
42
-                'AHEE__event_tickets_datetime_attached_tickets_row_template__advanced_details_after_dtt_description',
43
-                $dtt_row,
44
-                $DTT_ID
45
-            ); ?>
42
+				'AHEE__event_tickets_datetime_attached_tickets_row_template__advanced_details_after_dtt_description',
43
+				$dtt_row,
44
+				$DTT_ID
45
+			); ?>
46 46
             <h4 class="datetime-tickets-heading"><?php esc_html_e('Assigned Tickets', 'event_espresso'); ?></h4>
47 47
 
48 48
             <ul class="datetime-tickets-list">
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -24,17 +24,17 @@  discard block
 block discarded – undo
24 24
                 <h3 class="ee-item-id">
25 25
                     <?php echo esc_html(
26 26
                         $DTT_ID
27
-                            ? sprintf( __('Datetime ID: %d', 'event_espresso'), $DTT_ID )
27
+                            ? sprintf(__('Datetime ID: %d', 'event_espresso'), $DTT_ID)
28 28
                             : ''
29 29
                     ); ?>
30 30
                 </h3>
31 31
             </div>
32 32
             <div class="datetime-description-container">
33
-                <label for="<?php echo esc_attr($event_datetimes_name . '-' . $dtt_row); ?>-DTT_description">
33
+                <label for="<?php echo esc_attr($event_datetimes_name.'-'.$dtt_row); ?>-DTT_description">
34 34
                     <?php esc_html_e('Datetime Description', 'event_espresso') ?>
35 35
                 </label>
36 36
                 <textarea name="<?php echo esc_attr($event_datetimes_name); ?>[<?php echo esc_attr($dtt_row); ?>][]"
37
-                          id="<?php echo esc_attr($event_datetimes_name . '-' . $dtt_row); ?>-DTT_description"
37
+                          id="<?php echo esc_attr($event_datetimes_name.'-'.$dtt_row); ?>-DTT_description"
38 38
                           class="event-datetime-DTT_description ee-full-textarea-inp"
39 39
                 ><?php echo esc_textarea($DTT_description); ?></textarea>
40 40
             </div>
@@ -53,7 +53,7 @@  discard block
 block discarded – undo
53 53
             <div class="add-datetime-ticket-container">
54 54
                 <div class="ee-layout-row ee-layout-row--fixed">
55 55
                     <h4 class="datetime-tickets-heading">
56
-                        <?php esc_html_e( 'Add New Ticket', 'event_espresso' ); ?>
56
+                        <?php esc_html_e('Add New Ticket', 'event_espresso'); ?>
57 57
                     </h4>
58 58
                     <?php echo wp_kses($add_new_datetime_ticket_help_link, AllowedTags::getAllowedTags()); ?>
59 59
                 </div>
@@ -63,17 +63,17 @@  discard block
 block discarded – undo
63 63
                         <tr valign="top">
64 64
                             <td>
65 65
                                 <span class="ANT_TKT_name_label">
66
-                                    <?php esc_html_e( 'Ticket Name', 'event_espresso' ); ?>
66
+                                    <?php esc_html_e('Ticket Name', 'event_espresso'); ?>
67 67
                                 </span>
68 68
                             </td>
69 69
                             <td>
70 70
                                 <span class="ANT_TKT_goes_on_sale_label">
71
-                                    <?php esc_html_e('Sale Starts', 'event_espresso' ); ?>
71
+                                    <?php esc_html_e('Sale Starts', 'event_espresso'); ?>
72 72
                                 </span>
73 73
                             </td>
74 74
                             <td>
75 75
                                 <span class="ANT_TKT_sell_until_label">
76
-                                    <?php esc_html_e( 'Sell Until',  'event_espresso' ); ?>
76
+                                    <?php esc_html_e('Sell Until', 'event_espresso'); ?>
77 77
                                 </span>
78 78
                             </td>
79 79
                             <td>
Please login to merge, or discard this patch.
new/pricing/templates/event_tickets_datetime_ticket_price_row.template.php 1 patch
Braces   +10 added lines, -4 removed lines patch added patch discarded remove patch
@@ -64,9 +64,12 @@  discard block
 block discarded – undo
64 64
             <input type="text" size="1" class="edit-price-PRC_amount ee-numeric"
65 65
                    name="prices_archive[<?php echo esc_attr($tkt_row); ?>][<?php echo esc_attr($PRC_order); ?>][PRC_amount]"
66 66
                    value="<?php echo esc_attr($PRC_amount); ?>" disabled>
67
-        <?php else : ?>
67
+        <?php else {
68
+	: ?>
68 69
             <input type="text" size="1" class="edit-price-PRC_amount ee-numeric"
69
-                   name="<?php echo esc_attr($edit_prices_name); ?>[<?php echo esc_attr($tkt_row); ?>][<?php echo esc_attr($PRC_order); ?>][PRC_amount]"
70
+                   name="<?php echo esc_attr($edit_prices_name);
71
+}
72
+?>[<?php echo esc_attr($tkt_row); ?>][<?php echo esc_attr($PRC_order); ?>][PRC_amount]"
70 73
                    value="<?php echo esc_attr($PRC_amount); ?>">
71 74
         <?php endif; ?>
72 75
     </td>
@@ -77,9 +80,12 @@  discard block
 block discarded – undo
77 80
     <td>
78 81
         <?php if ($disabled) : ?>
79 82
             <span class="dashicons dashicons-lock"></span>
80
-        <?php else : ?>
83
+        <?php else {
84
+	: ?>
81 85
             <button aria-label="<?php esc_html_e('add a price modifier to this ticket', 'event_espresso') ?>"
82
-                    data-ticket-row="<?php echo esc_attr($tkt_row); ?>"
86
+                    data-ticket-row="<?php echo esc_attr($tkt_row);
87
+}
88
+?>"
83 89
                     data-price-row="<?php echo esc_attr($PRC_order); ?>"
84 90
                     data-context="price"
85 91
                     class="button button--icon-only button--small ee-create-button ee-aria-tooltip dashicons dashicons-plus
Please login to merge, or discard this patch.
admin/new/pricing/templates/event_tickets_datetime_ticket_row.template.php 1 patch
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -81,7 +81,7 @@  discard block
 block discarded – undo
81 81
                maxlength='245'
82 82
                name="<?php echo esc_attr($edit_tickets_name); ?>[<?php echo esc_attr($tkt_row); ?>][TKT_name]"
83 83
                placeholder="<?php
84
-               esc_html_e('Ticket Title', 'event_espresso') ?>"
84
+			   esc_html_e('Ticket Title', 'event_espresso') ?>"
85 85
                value="<?php echo esc_attr($TKT_name); ?>"
86 86
         />
87 87
     </td>
@@ -279,11 +279,11 @@  discard block
 block discarded – undo
279 279
             <div class="ee-editor-id-container">
280 280
                     <h3 class="ee-item-id">
281 281
                         <?php
282
-                        echo esc_html(
283
-                            $TKT_ID
284
-                                ? sprintf(__('Ticket ID: %d', 'event_espresso'), $TKT_ID)
285
-                                : ''
286
-                        ); ?>
282
+						echo esc_html(
283
+							$TKT_ID
284
+								? sprintf(__('Ticket ID: %d', 'event_espresso'), $TKT_ID)
285
+								: ''
286
+						); ?>
287 287
                     </h3>
288 288
             </div>
289 289
             <div class="basic-ticket-container">
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
                                        for='edit-ticket-TKT_uses-<?php echo esc_attr($tkt_row); ?>'
344 344
                                 >
345 345
                                     <?php
346
-                                    esc_html_e('Ticket Uses', 'event_espresso') ?>
346
+									esc_html_e('Ticket Uses', 'event_espresso') ?>
347 347
                                 </label>
348 348
                                 <input type="text"
349 349
                                        class="edit-ticket-TKT_uses ee-small-text-inp ee-numeric"
@@ -362,7 +362,7 @@  discard block
 block discarded – undo
362 362
                                 />
363 363
                                 <label class='screen-reader-text' for='disabled-ticket-TKT_min-<?php echo esc_attr($tkt_row);?>'>
364 364
                                     <?php
365
-                                    esc_html_e('Minimum Quantity', 'event_espresso') ?>
365
+									esc_html_e('Minimum Quantity', 'event_espresso') ?>
366 366
                                 </label>
367 367
                                 <input type="text" disabled
368 368
                                        class="edit-ticket-TKT_min ee-small-text-inp ee-numeric"
@@ -427,14 +427,14 @@  discard block
 block discarded – undo
427 427
                                id="edit-ticket-TKT_required-<?php echo esc_attr($tkt_row); ?>"
428 428
                                value="1"
429 429
                             <?php
430
-                                echo esc_attr($TKT_required ? ' checked' : '');
431
-                                echo esc_attr($disabled ? ' disabled' : '');
432
-                            ?>
430
+								echo esc_attr($TKT_required ? ' checked' : '');
431
+								echo esc_attr($disabled ? ' disabled' : '');
432
+							?>
433 433
                         />
434 434
                         <?php esc_html_e(
435
-                            'This ticket is required (will appear first in frontend ticket lists).',
436
-                            'event_espresso'
437
-                        ); ?>
435
+							'This ticket is required (will appear first in frontend ticket lists).',
436
+							'event_espresso'
437
+						); ?>
438 438
                     </label>
439 439
                 </div>
440 440
                 <div class="ticket-is-taxable-container">
@@ -537,26 +537,26 @@  discard block
 block discarded – undo
537 537
             <h4 class="tickets-heading"><?php esc_html_e('Event Datetimes', 'event_espresso'); ?></h4>
538 538
             <p>
539 539
             <?php  esc_html_e(
540
-                'This ticket will be usable (allow entrance) for the following selected event datetimes (click to select).  The "# Datetimes" amount (above) indicates how many of the assigned datetimes the ticket holder can gain access to:',
541
-                'event_espresso'
542
-            ); ?>
540
+				'This ticket will be usable (allow entrance) for the following selected event datetimes (click to select).  The "# Datetimes" amount (above) indicates how many of the assigned datetimes the ticket holder can gain access to:',
541
+				'event_espresso'
542
+			); ?>
543 543
             </p>
544 544
             <ul class="datetime-tickets-list">
545 545
                 <?php echo wp_kses($ticket_datetimes_list, AllowedTags::getWithFormTags()); ?>
546 546
             </ul>
547 547
 
548 548
             <?php do_action(
549
-                'AHEE__event_tickets_datetime_ticket_row_template__advanced_details_end',
550
-                $tkt_row,
551
-                $TKT_ID
552
-            ); ?>
549
+				'AHEE__event_tickets_datetime_ticket_row_template__advanced_details_end',
550
+				$tkt_row,
551
+				$TKT_ID
552
+			); ?>
553 553
             <div class="ee-editor-footer-container">
554 554
                 <div class="ee-layout-row ee-layout-row--spaced">
555 555
                     <label for="edit-ticket-TKT_is_default_selector-<?php echo esc_attr($tkt_row); ?>">
556 556
                         <?php esc_html_e(
557
-                            'use this new ticket as a default ticket for any new events',
558
-                            'event_espresso'
559
-                        ); ?>
557
+							'use this new ticket as a default ticket for any new events',
558
+							'event_espresso'
559
+						); ?>
560 560
                         <input type="checkbox"
561 561
                                name="<?php echo esc_attr($edit_tickets_name); ?>[<?php echo esc_attr($tkt_row); ?>][TKT_is_default_selector]"
562 562
                                class="edit-ticket-TKT_is_default_selector"
Please login to merge, or discard this patch.
caffeinated/admin/new/pricing/espresso_events_Pricing_Hooks.class.php 2 patches
Indentation   +2178 added lines, -2178 removed lines patch added patch discarded remove patch
@@ -15,2196 +15,2196 @@
 block discarded – undo
15 15
  */
16 16
 class espresso_events_Pricing_Hooks extends EE_Admin_Hooks
17 17
 {
18
-    /**
19
-     * This property is just used to hold the status of whether an event is currently being
20
-     * created (true) or edited (false)
21
-     *
22
-     * @access protected
23
-     * @var bool
24
-     */
25
-    protected $_is_creating_event;
26
-
27
-    /**
28
-     * Used to contain the format strings for date and time that will be used for php date and
29
-     * time.
30
-     * Is set in the _set_hooks_properties() method.
31
-     *
32
-     * @var array
33
-     */
34
-    protected $_date_format_strings;
35
-
36
-    /**
37
-     * @var string $_date_time_format
38
-     */
39
-    protected $_date_time_format;
40
-
41
-
42
-    /**
43
-     * @throws InvalidArgumentException
44
-     * @throws InvalidInterfaceException
45
-     * @throws InvalidDataTypeException
46
-     */
47
-    protected function _set_hooks_properties()
48
-    {
49
-        $this->_name = 'pricing';
50
-        // capability check
51
-        if (
52
-            $this->_adminpage_obj->adminConfig()->useAdvancedEditor()
53
-            || ! EE_Registry::instance()->CAP->current_user_can(
54
-                'ee_read_default_prices',
55
-                'advanced_ticket_datetime_metabox'
56
-            )
57
-        ) {
58
-            $this->_metaboxes      = [];
59
-            $this->_scripts_styles = [];
60
-            return;
61
-        }
62
-        $this->_setup_metaboxes();
63
-        $this->_set_date_time_formats();
64
-        $this->_validate_format_strings();
65
-        $this->_set_scripts_styles();
66
-        add_filter(
67
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
68
-            [$this, 'caf_updates']
69
-        );
70
-    }
71
-
72
-
73
-    /**
74
-     * @return void
75
-     */
76
-    protected function _setup_metaboxes()
77
-    {
78
-        // if we were going to add our own metaboxes we'd use the below.
79
-        $this->_metaboxes        = [
80
-            0 => [
81
-                'page_route' => ['edit', 'create_new'],
82
-                'func'       => [$this, 'pricing_metabox'],
83
-                'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
84
-                'priority'   => 'high',
85
-                'context'    => 'normal',
86
-            ],
87
-        ];
88
-        $this->_remove_metaboxes = [
89
-            0 => [
90
-                'page_route' => ['edit', 'create_new'],
91
-                'id'         => 'espresso_event_editor_tickets',
92
-                'context'    => 'normal',
93
-            ],
94
-        ];
95
-    }
96
-
97
-
98
-    /**
99
-     * @return void
100
-     */
101
-    protected function _set_date_time_formats()
102
-    {
103
-        /**
104
-         * Format strings for date and time.  Defaults are existing behaviour from 4.1.
105
-         * Note, that if you return null as the value for 'date', and 'time' in the array, then
106
-         * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
107
-         *
108
-         * @since 4.6.7
109
-         * @var array  Expected an array returned with 'date' and 'time' keys.
110
-         */
111
-        $this->_date_format_strings = apply_filters(
112
-            'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
113
-            [
114
-                'date' => 'Y-m-d',
115
-                'time' => 'h:i a',
116
-            ]
117
-        );
118
-        // validate
119
-        $this->_date_format_strings['date'] = $this->_date_format_strings['date'] ?? '';
120
-        $this->_date_format_strings['time'] = $this->_date_format_strings['time'] ?? '';
121
-
122
-        $this->_date_time_format = $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'];
123
-    }
124
-
125
-
126
-    /**
127
-     * @return void
128
-     */
129
-    protected function _validate_format_strings()
130
-    {
131
-        // validate format strings
132
-        $format_validation = EEH_DTT_Helper::validate_format_string(
133
-            $this->_date_time_format
134
-        );
135
-        if (is_array($format_validation)) {
136
-            $msg = '<p>';
137
-            $msg .= sprintf(
138
-                esc_html__(
139
-                    'The format "%s" was likely added via a filter and is invalid for the following reasons:',
140
-                    'event_espresso'
141
-                ),
142
-                $this->_date_time_format
143
-            );
144
-            $msg .= '</p><ul>';
145
-            foreach ($format_validation as $error) {
146
-                $msg .= '<li>' . $error . '</li>';
147
-            }
148
-            $msg .= '</ul><p>';
149
-            $msg .= sprintf(
150
-                esc_html__(
151
-                    '%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
152
-                    'event_espresso'
153
-                ),
154
-                '<span style="color:#D54E21;">',
155
-                '</span>'
156
-            );
157
-            $msg .= '</p>';
158
-            EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
159
-            $this->_date_format_strings = [
160
-                'date' => 'Y-m-d',
161
-                'time' => 'h:i a',
162
-            ];
163
-        }
164
-    }
165
-
166
-
167
-    /**
168
-     * @return void
169
-     */
170
-    protected function _set_scripts_styles()
171
-    {
172
-        $this->_scripts_styles = [
173
-            'registers'   => [
174
-                'ee-tickets-datetimes-css' => [
175
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
176
-                    'type' => 'css',
177
-                ],
178
-                'ee-dtt-ticket-metabox'    => [
179
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
180
-                    'depends' => ['ee-datepicker', 'ee-dialog', 'underscore'],
181
-                ],
182
-            ],
183
-            'deregisters' => [
184
-                'event-editor-css'       => ['type' => 'css'],
185
-                'event-datetime-metabox' => ['type' => 'js'],
186
-            ],
187
-            'enqueues'    => [
188
-                'ee-tickets-datetimes-css' => ['edit', 'create_new'],
189
-                'ee-dtt-ticket-metabox'    => ['edit', 'create_new'],
190
-            ],
191
-            'localize'    => [
192
-                'ee-dtt-ticket-metabox' => [
193
-                    'DTT_TRASH_BLOCK'       => [
194
-                        'main_warning'            => esc_html__(
195
-                            'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
196
-                            'event_espresso'
197
-                        ),
198
-                        'after_warning'           => esc_html__(
199
-                            'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
200
-                            'event_espresso'
201
-                        ),
202
-                        'cancel_button'           => '
18
+	/**
19
+	 * This property is just used to hold the status of whether an event is currently being
20
+	 * created (true) or edited (false)
21
+	 *
22
+	 * @access protected
23
+	 * @var bool
24
+	 */
25
+	protected $_is_creating_event;
26
+
27
+	/**
28
+	 * Used to contain the format strings for date and time that will be used for php date and
29
+	 * time.
30
+	 * Is set in the _set_hooks_properties() method.
31
+	 *
32
+	 * @var array
33
+	 */
34
+	protected $_date_format_strings;
35
+
36
+	/**
37
+	 * @var string $_date_time_format
38
+	 */
39
+	protected $_date_time_format;
40
+
41
+
42
+	/**
43
+	 * @throws InvalidArgumentException
44
+	 * @throws InvalidInterfaceException
45
+	 * @throws InvalidDataTypeException
46
+	 */
47
+	protected function _set_hooks_properties()
48
+	{
49
+		$this->_name = 'pricing';
50
+		// capability check
51
+		if (
52
+			$this->_adminpage_obj->adminConfig()->useAdvancedEditor()
53
+			|| ! EE_Registry::instance()->CAP->current_user_can(
54
+				'ee_read_default_prices',
55
+				'advanced_ticket_datetime_metabox'
56
+			)
57
+		) {
58
+			$this->_metaboxes      = [];
59
+			$this->_scripts_styles = [];
60
+			return;
61
+		}
62
+		$this->_setup_metaboxes();
63
+		$this->_set_date_time_formats();
64
+		$this->_validate_format_strings();
65
+		$this->_set_scripts_styles();
66
+		add_filter(
67
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
68
+			[$this, 'caf_updates']
69
+		);
70
+	}
71
+
72
+
73
+	/**
74
+	 * @return void
75
+	 */
76
+	protected function _setup_metaboxes()
77
+	{
78
+		// if we were going to add our own metaboxes we'd use the below.
79
+		$this->_metaboxes        = [
80
+			0 => [
81
+				'page_route' => ['edit', 'create_new'],
82
+				'func'       => [$this, 'pricing_metabox'],
83
+				'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
84
+				'priority'   => 'high',
85
+				'context'    => 'normal',
86
+			],
87
+		];
88
+		$this->_remove_metaboxes = [
89
+			0 => [
90
+				'page_route' => ['edit', 'create_new'],
91
+				'id'         => 'espresso_event_editor_tickets',
92
+				'context'    => 'normal',
93
+			],
94
+		];
95
+	}
96
+
97
+
98
+	/**
99
+	 * @return void
100
+	 */
101
+	protected function _set_date_time_formats()
102
+	{
103
+		/**
104
+		 * Format strings for date and time.  Defaults are existing behaviour from 4.1.
105
+		 * Note, that if you return null as the value for 'date', and 'time' in the array, then
106
+		 * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
107
+		 *
108
+		 * @since 4.6.7
109
+		 * @var array  Expected an array returned with 'date' and 'time' keys.
110
+		 */
111
+		$this->_date_format_strings = apply_filters(
112
+			'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
113
+			[
114
+				'date' => 'Y-m-d',
115
+				'time' => 'h:i a',
116
+			]
117
+		);
118
+		// validate
119
+		$this->_date_format_strings['date'] = $this->_date_format_strings['date'] ?? '';
120
+		$this->_date_format_strings['time'] = $this->_date_format_strings['time'] ?? '';
121
+
122
+		$this->_date_time_format = $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'];
123
+	}
124
+
125
+
126
+	/**
127
+	 * @return void
128
+	 */
129
+	protected function _validate_format_strings()
130
+	{
131
+		// validate format strings
132
+		$format_validation = EEH_DTT_Helper::validate_format_string(
133
+			$this->_date_time_format
134
+		);
135
+		if (is_array($format_validation)) {
136
+			$msg = '<p>';
137
+			$msg .= sprintf(
138
+				esc_html__(
139
+					'The format "%s" was likely added via a filter and is invalid for the following reasons:',
140
+					'event_espresso'
141
+				),
142
+				$this->_date_time_format
143
+			);
144
+			$msg .= '</p><ul>';
145
+			foreach ($format_validation as $error) {
146
+				$msg .= '<li>' . $error . '</li>';
147
+			}
148
+			$msg .= '</ul><p>';
149
+			$msg .= sprintf(
150
+				esc_html__(
151
+					'%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
152
+					'event_espresso'
153
+				),
154
+				'<span style="color:#D54E21;">',
155
+				'</span>'
156
+			);
157
+			$msg .= '</p>';
158
+			EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
159
+			$this->_date_format_strings = [
160
+				'date' => 'Y-m-d',
161
+				'time' => 'h:i a',
162
+			];
163
+		}
164
+	}
165
+
166
+
167
+	/**
168
+	 * @return void
169
+	 */
170
+	protected function _set_scripts_styles()
171
+	{
172
+		$this->_scripts_styles = [
173
+			'registers'   => [
174
+				'ee-tickets-datetimes-css' => [
175
+					'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
176
+					'type' => 'css',
177
+				],
178
+				'ee-dtt-ticket-metabox'    => [
179
+					'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
180
+					'depends' => ['ee-datepicker', 'ee-dialog', 'underscore'],
181
+				],
182
+			],
183
+			'deregisters' => [
184
+				'event-editor-css'       => ['type' => 'css'],
185
+				'event-datetime-metabox' => ['type' => 'js'],
186
+			],
187
+			'enqueues'    => [
188
+				'ee-tickets-datetimes-css' => ['edit', 'create_new'],
189
+				'ee-dtt-ticket-metabox'    => ['edit', 'create_new'],
190
+			],
191
+			'localize'    => [
192
+				'ee-dtt-ticket-metabox' => [
193
+					'DTT_TRASH_BLOCK'       => [
194
+						'main_warning'            => esc_html__(
195
+							'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
196
+							'event_espresso'
197
+						),
198
+						'after_warning'           => esc_html__(
199
+							'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
200
+							'event_espresso'
201
+						),
202
+						'cancel_button'           => '
203 203
                             <button class="button--secondary ee-modal-cancel">
204 204
                                 ' . esc_html__('Cancel', 'event_espresso') . '
205 205
                             </button>',
206
-                        'close_button'            => '
206
+						'close_button'            => '
207 207
                             <button class="button--secondary ee-modal-cancel">
208 208
                                 ' . esc_html__('Close', 'event_espresso') . '
209 209
                             </button>',
210
-                        'single_warning_from_tkt' => esc_html__(
211
-                            'The Datetime you are attempting to unassign from this ticket is the only remaining datetime for this ticket. Tickets must always have at least one datetime assigned to them.',
212
-                            'event_espresso'
213
-                        ),
214
-                        'single_warning_from_dtt' => esc_html__(
215
-                            'The ticket you are attempting to unassign from this datetime cannot be unassigned because the datetime is the only remaining datetime for the ticket.  Tickets must always have at least one datetime assigned to them.',
216
-                            'event_espresso'
217
-                        ),
218
-                        'dismiss_button'          => '
210
+						'single_warning_from_tkt' => esc_html__(
211
+							'The Datetime you are attempting to unassign from this ticket is the only remaining datetime for this ticket. Tickets must always have at least one datetime assigned to them.',
212
+							'event_espresso'
213
+						),
214
+						'single_warning_from_dtt' => esc_html__(
215
+							'The ticket you are attempting to unassign from this datetime cannot be unassigned because the datetime is the only remaining datetime for the ticket.  Tickets must always have at least one datetime assigned to them.',
216
+							'event_espresso'
217
+						),
218
+						'dismiss_button'          => '
219 219
                             <button class="button--secondary ee-modal-cancel">
220 220
                                 ' . esc_html__('Dismiss', 'event_espresso') . '
221 221
                             </button>',
222
-                    ],
223
-                    'DTT_ERROR_MSG'         => [
224
-                        'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
225
-                        'dismiss_button' => '
222
+					],
223
+					'DTT_ERROR_MSG'         => [
224
+						'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
225
+						'dismiss_button' => '
226 226
                             <div class="save-cancel-button-container">
227 227
                                 <button class="button--secondary ee-modal-cancel">
228 228
                                     ' . esc_html__('Dismiss', 'event_espresso') . '
229 229
                                 </button>
230 230
                             </div>',
231
-                    ],
232
-                    'DTT_OVERSELL_WARNING'  => [
233
-                        'datetime_ticket' => esc_html__(
234
-                            'You cannot add this ticket to this datetime because it has a sold amount that is greater than the amount of spots remaining for this datetime.',
235
-                            'event_espresso'
236
-                        ),
237
-                        'ticket_datetime' => esc_html__(
238
-                            'You cannot add this datetime to this ticket because the ticket has a sold amount that is greater than the amount of spots remaining on the datetime.',
239
-                            'event_espresso'
240
-                        ),
241
-                    ],
242
-                    'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
243
-                        $this->_date_format_strings['date'],
244
-                        $this->_date_format_strings['time']
245
-                    ),
246
-                    'DTT_START_OF_WEEK'     => ['dayValue' => (int) get_option('start_of_week')],
247
-                ],
248
-            ],
249
-        ];
250
-    }
251
-
252
-
253
-    /**
254
-     * @param array $update_callbacks
255
-     * @return array
256
-     */
257
-    public function caf_updates(array $update_callbacks): array
258
-    {
259
-        unset($update_callbacks['_default_tickets_update']);
260
-        $update_callbacks['datetime_and_tickets_caf_update'] = [$this, 'datetime_and_tickets_caf_update'];
261
-        return $update_callbacks;
262
-    }
263
-
264
-
265
-    /**
266
-     * Handles saving everything related to Tickets (datetimes, tickets, prices)
267
-     *
268
-     * @param EE_Event $event The Event object we're attaching data to
269
-     * @param array    $data  The request data from the form
270
-     * @throws ReflectionException
271
-     * @throws Exception
272
-     * @throws InvalidInterfaceException
273
-     * @throws InvalidDataTypeException
274
-     * @throws EE_Error
275
-     * @throws InvalidArgumentException
276
-     */
277
-    public function datetime_and_tickets_caf_update(EE_Event $event, array $data)
278
-    {
279
-        // first we need to start with datetimes cause they are the "root" items attached to events.
280
-        $saved_datetimes = $this->_update_datetimes($event, $data);
281
-        // next tackle the tickets (and prices?)
282
-        $this->_update_tickets($event, $saved_datetimes, $data);
283
-    }
284
-
285
-
286
-    /**
287
-     * update event_datetimes
288
-     *
289
-     * @param EE_Event $event Event being updated
290
-     * @param array    $data  the request data from the form
291
-     * @return EE_Datetime[]
292
-     * @throws Exception
293
-     * @throws ReflectionException
294
-     * @throws InvalidInterfaceException
295
-     * @throws InvalidDataTypeException
296
-     * @throws InvalidArgumentException
297
-     * @throws EE_Error
298
-     */
299
-    protected function _update_datetimes(EE_Event $event, array $data): array
300
-    {
301
-        $saved_datetime_ids  = [];
302
-        $saved_datetime_objs = [];
303
-        $timezone       = $data['timezone_string'] ?? null;
304
-        $datetime_model = EEM_Datetime::instance($timezone);
305
-
306
-        if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
307
-            throw new InvalidArgumentException(
308
-                esc_html__(
309
-                    'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
310
-                    'event_espresso'
311
-                )
312
-            );
313
-        }
314
-        foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315
-            // trim all values to ensure any excess whitespace is removed.
316
-            $datetime_data = array_map(
317
-                function ($datetime_data) {
318
-                    return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319
-                },
320
-                $datetime_data
321
-            );
322
-
323
-            $datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
324
-                                            && ! empty($datetime_data['DTT_EVT_end'])
325
-                ? $datetime_data['DTT_EVT_end']
326
-                : $datetime_data['DTT_EVT_start'];
327
-            $datetime_values              = [
328
-                'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
329
-                    ? $datetime_data['DTT_ID']
330
-                    : null,
331
-                'DTT_name'        => ! empty($datetime_data['DTT_name'])
332
-                    ? $datetime_data['DTT_name']
333
-                    : '',
334
-                'DTT_description' => ! empty($datetime_data['DTT_description'])
335
-                    ? $datetime_data['DTT_description']
336
-                    : '',
337
-                'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
338
-                'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
339
-                'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
340
-                    ? EE_INF
341
-                    : $datetime_data['DTT_reg_limit'],
342
-                'DTT_order'       => ! isset($datetime_data['DTT_order'])
343
-                    ? $row
344
-                    : $datetime_data['DTT_order'],
345
-            ];
346
-
347
-            // if we have an id then let's get existing object first and then set the new values.
348
-            // Otherwise we instantiate a new object for save.
349
-            if (! empty($datetime_data['DTT_ID'])) {
350
-                $datetime = EE_Registry::instance()
351
-                                       ->load_model('Datetime', [$timezone])
352
-                                       ->get_one_by_ID($datetime_data['DTT_ID']);
353
-                // set date and time format according to what is set in this class.
354
-                $datetime->set_date_format($this->_date_format_strings['date']);
355
-                $datetime->set_time_format($this->_date_format_strings['time']);
356
-                foreach ($datetime_values as $field => $value) {
357
-                    $datetime->set($field, $value);
358
-                }
359
-
360
-                // make sure the $datetime_id here is saved just in case
361
-                // after the add_relation_to() the autosave replaces it.
362
-                // We need to do this so we dont' TRASH the parent DTT.
363
-                // (save the ID for both key and value to avoid duplications)
364
-                $saved_datetime_ids[ $datetime->ID() ] = $datetime->ID();
365
-            } else {
366
-                $datetime = EE_Datetime::new_instance(
367
-                    $datetime_values,
368
-                    $timezone,
369
-                    [$this->_date_format_strings['date'], $this->_date_format_strings['time']]
370
-                );
371
-                foreach ($datetime_values as $field => $value) {
372
-                    $datetime->set($field, $value);
373
-                }
374
-            }
375
-            $datetime->save();
376
-            do_action(
377
-                'AHEE__espresso_events_Pricing_Hooks___update_datetimes_after_save',
378
-                $datetime,
379
-                $row,
380
-                $datetime_data,
381
-                $data
382
-            );
383
-            $datetime = $event->_add_relation_to($datetime, 'Datetime');
384
-            // before going any further make sure our dates are setup correctly
385
-            // so that the end date is always equal or greater than the start date.
386
-            if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
387
-                $datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
388
-                $datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
389
-                $datetime->save();
390
-            }
391
-            // now we have to make sure we add the new DTT_ID to the $saved_datetime_ids array
392
-            // because it is possible there was a new one created for the autosave.
393
-            // (save the ID for both key and value to avoid duplications)
394
-            $DTT_ID                        = $datetime->ID();
395
-            $saved_datetime_ids[ $DTT_ID ] = $DTT_ID;
396
-            $saved_datetime_objs[ $row ]   = $datetime;
397
-            // @todo if ANY of these updates fail then we want the appropriate global error message.
398
-        }
399
-        $event->save();
400
-        // now we need to REMOVE any datetimes that got deleted.
401
-        // Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
402
-        // So its safe to permanently delete at this point.
403
-        $old_datetimes = explode(',', $data['datetime_IDs']);
404
-        $old_datetimes = $old_datetimes[0] === '' ? [] : $old_datetimes;
405
-        if (is_array($old_datetimes)) {
406
-            $datetimes_to_delete = array_diff($old_datetimes, $saved_datetime_ids);
407
-            foreach ($datetimes_to_delete as $id) {
408
-                $id = absint($id);
409
-                if (empty($id)) {
410
-                    continue;
411
-                }
412
-                $dtt_to_remove = $datetime_model->get_one_by_ID($id);
413
-                // remove tkt relationships.
414
-                $related_tickets = $dtt_to_remove->get_many_related('Ticket');
415
-                foreach ($related_tickets as $ticket) {
416
-                    $dtt_to_remove->_remove_relation_to($ticket, 'Ticket');
417
-                }
418
-                $event->_remove_relation_to($id, 'Datetime');
419
-                $dtt_to_remove->refresh_cache_of_related_objects();
420
-            }
421
-        }
422
-        return $saved_datetime_objs;
423
-    }
424
-
425
-
426
-    /**
427
-     * update tickets
428
-     *
429
-     * @param EE_Event      $event           Event object being updated
430
-     * @param EE_Datetime[] $saved_datetimes an array of datetime ids being updated
431
-     * @param array         $data            incoming request data
432
-     * @return EE_Ticket[]
433
-     * @throws Exception
434
-     * @throws ReflectionException
435
-     * @throws InvalidInterfaceException
436
-     * @throws InvalidDataTypeException
437
-     * @throws InvalidArgumentException
438
-     * @throws EE_Error
439
-     */
440
-    protected function _update_tickets(EE_Event $event, array $saved_datetimes, array $data): array
441
-    {
442
-        $new_ticket = null;
443
-        // stripslashes because WP filtered the $_POST ($data) array to add slashes
444
-        $data          = stripslashes_deep($data);
445
-        $timezone      = $data['timezone_string'] ?? null;
446
-        $ticket_model = EEM_Ticket::instance($timezone);
447
-
448
-        $saved_tickets = [];
449
-        $old_tickets   = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
450
-        if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
451
-            throw new InvalidArgumentException(
452
-                esc_html__(
453
-                    'The "edit_tickets" array is invalid therefore the event can not be updated.',
454
-                    'event_espresso'
455
-                )
456
-            );
457
-        }
458
-        foreach ($data['edit_tickets'] as $row => $ticket_data) {
459
-            $update_prices = $create_new_TKT = false;
460
-            // figure out what datetimes were added to the ticket
461
-            // and what datetimes were removed from the ticket in the session.
462
-            $starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
463
-            $ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
464
-            $datetimes_added               = array_diff($ticket_datetime_rows, $starting_ticket_datetime_rows);
465
-            $datetimes_removed             = array_diff($starting_ticket_datetime_rows, $ticket_datetime_rows);
466
-            // trim inputs to ensure any excess whitespace is removed.
467
-            $ticket_data = array_map(
468
-                function ($ticket_data) {
469
-                    return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
470
-                },
471
-                $ticket_data
472
-            );
473
-            // note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
474
-            // because we're doing calculations prior to using the models.
475
-            // note incoming ['TKT_price'] value is already in standard notation (via js).
476
-            $ticket_price = isset($ticket_data['TKT_price'])
477
-                ? round((float) $ticket_data['TKT_price'], 3)
478
-                : 0;
479
-            // note incoming base price needs converted from localized value.
480
-            $base_price = isset($ticket_data['TKT_base_price'])
481
-                ? EEH_Money::convert_to_float_from_localized_money($ticket_data['TKT_base_price'])
482
-                : 0;
483
-            // if ticket price == 0 and $base_price != 0 then ticket price == base_price
484
-            $ticket_price  = $ticket_price === 0 && $base_price !== 0
485
-                ? $base_price
486
-                : $ticket_price;
487
-            $base_price_id = $ticket_data['TKT_base_price_ID'] ?? 0;
488
-            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
489
-                ? $data['edit_prices'][ $row ]
490
-                : [];
491
-            $now           = null;
492
-            if (empty($ticket_data['TKT_start_date'])) {
493
-                // lets' use now in the set timezone.
494
-                $now                           = new DateTime('now', new DateTimeZone($event->get_timezone()));
495
-                $ticket_data['TKT_start_date'] = $now->format($this->_date_time_format);
496
-            }
497
-            if (empty($ticket_data['TKT_end_date'])) {
498
-                /**
499
-                 * set the TKT_end_date to the first datetime attached to the ticket.
500
-                 */
501
-                $first_datetime              = $saved_datetimes[ reset($ticket_datetime_rows) ];
502
-                $ticket_data['TKT_end_date'] = $first_datetime->start_date_and_time($this->_date_time_format);
503
-            }
504
-            $TKT_values = [
505
-                'TKT_ID'          => ! empty($ticket_data['TKT_ID']) ? $ticket_data['TKT_ID'] : null,
506
-                'TTM_ID'          => ! empty($ticket_data['TTM_ID']) ? $ticket_data['TTM_ID'] : 0,
507
-                'TKT_name'        => ! empty($ticket_data['TKT_name']) ? $ticket_data['TKT_name'] : '',
508
-                'TKT_description' => ! empty($ticket_data['TKT_description'])
509
-                                     && $ticket_data['TKT_description'] !== esc_html__(
510
-                                         'You can modify this description',
511
-                                         'event_espresso'
512
-                                     )
513
-                    ? $ticket_data['TKT_description']
514
-                    : '',
515
-                'TKT_start_date'  => $ticket_data['TKT_start_date'],
516
-                'TKT_end_date'    => $ticket_data['TKT_end_date'],
517
-                'TKT_qty'         => ! isset($ticket_data['TKT_qty']) || $ticket_data['TKT_qty'] === ''
518
-                    ? EE_INF
519
-                    : $ticket_data['TKT_qty'],
520
-                'TKT_uses'        => ! isset($ticket_data['TKT_uses']) || $ticket_data['TKT_uses'] === ''
521
-                    ? EE_INF
522
-                    : $ticket_data['TKT_uses'],
523
-                'TKT_min'         => empty($ticket_data['TKT_min']) ? 0 : $ticket_data['TKT_min'],
524
-                'TKT_max'         => empty($ticket_data['TKT_max']) ? EE_INF : $ticket_data['TKT_max'],
525
-                'TKT_row'         => $row,
526
-                'TKT_order'       => $ticket_data['TKT_order'] ?? 0,
527
-                'TKT_taxable'     => ! empty($ticket_data['TKT_taxable']) ? 1 : 0,
528
-                'TKT_required'    => ! empty($ticket_data['TKT_required']) ? 1 : 0,
529
-                'TKT_price'       => $ticket_price,
530
-            ];
531
-            // if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
532
-            // which means in turn that the prices will become new prices as well.
533
-            if (isset($ticket_data['TKT_is_default']) && $ticket_data['TKT_is_default']) {
534
-                $TKT_values['TKT_ID']         = 0;
535
-                $TKT_values['TKT_is_default'] = 0;
536
-                $update_prices                = true;
537
-            }
538
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
539
-            // we actually do our saves ahead of doing any add_relations to
540
-            // because its entirely possible that this ticket wasn't removed or added to any datetime in the session
541
-            // but DID have it's items modified.
542
-            // keep in mind that if the TKT has been sold (and we have changed pricing information),
543
-            // then we won't be updating the ticket but instead a new ticket will be created and the old one archived.
544
-            if (absint($TKT_values['TKT_ID'])) {
545
-                $ticket = EE_Registry::instance()
546
-                                     ->load_model('Ticket', [$timezone])
547
-                                     ->get_one_by_ID($TKT_values['TKT_ID']);
548
-                if ($ticket instanceof EE_Ticket) {
549
-                    $ticket = $this->_update_ticket_datetimes(
550
-                        $ticket,
551
-                        $saved_datetimes,
552
-                        $datetimes_added,
553
-                        $datetimes_removed
554
-                    );
555
-                    // are there any registrations using this ticket ?
556
-                    $tickets_sold = $ticket->count_related(
557
-                        'Registration',
558
-                        [
559
-                            [
560
-                                'STS_ID' => ['NOT IN', [EEM_Registration::status_id_incomplete]],
561
-                            ],
562
-                        ]
563
-                    );
564
-                    // set ticket formats
565
-                    $ticket->set_date_format($this->_date_format_strings['date']);
566
-                    $ticket->set_time_format($this->_date_format_strings['time']);
567
-                    // let's just check the total price for the existing ticket
568
-                    // and determine if it matches the new total price.
569
-                    // if they are different then we create a new ticket (if tickets sold)
570
-                    // if they aren't different then we go ahead and modify existing ticket.
571
-                    $create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
572
-                    // set new values
573
-                    foreach ($TKT_values as $field => $value) {
574
-                        if ($field === 'TKT_qty') {
575
-                            $ticket->set_qty($value);
576
-                        } else {
577
-                            $ticket->set($field, $value);
578
-                        }
579
-                    }
580
-                    // if $create_new_TKT is false then we can safely update the existing ticket.
581
-                    // Otherwise we have to create a new ticket.
582
-                    if ($create_new_TKT) {
583
-                        $new_ticket = $this->_duplicate_ticket(
584
-                            $ticket,
585
-                            $price_rows,
586
-                            $ticket_price,
587
-                            $base_price,
588
-                            $base_price_id
589
-                        );
590
-                    }
591
-                }
592
-            } else {
593
-                // no TKT_id so a new TKT
594
-                $ticket = EE_Ticket::new_instance(
595
-                    $TKT_values,
596
-                    $timezone,
597
-                    [$this->_date_format_strings['date'], $this->_date_format_strings['time']]
598
-                );
599
-                if ($ticket instanceof EE_Ticket) {
600
-                    // make sure ticket has an ID of setting relations won't work
601
-                    $ticket->save();
602
-                    $ticket        = $this->_update_ticket_datetimes(
603
-                        $ticket,
604
-                        $saved_datetimes,
605
-                        $datetimes_added,
606
-                        $datetimes_removed
607
-                    );
608
-                    $update_prices = true;
609
-                }
610
-            }
611
-            // make sure any current values have been saved.
612
-            // $ticket->save();
613
-            // before going any further make sure our dates are setup correctly
614
-            // so that the end date is always equal or greater than the start date.
615
-            if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
616
-                $ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
617
-                $ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
618
-            }
619
-            // let's make sure the base price is handled
620
-            $ticket = ! $create_new_TKT
621
-                ? $this->_add_prices_to_ticket(
622
-                    [],
623
-                    $ticket,
624
-                    $update_prices,
625
-                    $base_price,
626
-                    $base_price_id
627
-                )
628
-                : $ticket;
629
-            // add/update price_modifiers
630
-            $ticket = ! $create_new_TKT
631
-                ? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
632
-                : $ticket;
633
-            // need to make sue that the TKT_price is accurate after saving the prices.
634
-            $ticket->ensure_TKT_Price_correct();
635
-            // handle CREATING a default ticket from the incoming ticket but ONLY if this isn't an autosave.
636
-            if (! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
637
-                $new_default = clone $ticket;
638
-                $new_default->set('TKT_ID', 0);
639
-                $new_default->set('TKT_is_default', 1);
640
-                $new_default->set('TKT_row', 1);
641
-                $new_default->set('TKT_price', $ticket_price);
642
-                // remove any datetime relations cause we DON'T want datetime relations attached
643
-                // (note this is just removing the cached relations in the object)
644
-                $new_default->_remove_relations('Datetime');
645
-                // @todo we need to add the current attached prices as new prices to the new default ticket.
646
-                $new_default = $this->_add_prices_to_ticket(
647
-                    $price_rows,
648
-                    $new_default,
649
-                    true
650
-                );
651
-                // don't forget the base price!
652
-                $new_default = $this->_add_prices_to_ticket(
653
-                    [],
654
-                    $new_default,
655
-                    true,
656
-                    $base_price,
657
-                    $base_price_id
658
-                );
659
-                $new_default->save();
660
-                do_action(
661
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
662
-                    $new_default,
663
-                    $row,
664
-                    $ticket,
665
-                    $data
666
-                );
667
-            }
668
-            // DO ALL datetime relationships for both current tickets and any archived tickets
669
-            // for the given datetime that are related to the current ticket.
670
-            // TODO... not sure exactly how we're going to do this considering we don't know
671
-            // what current ticket the archived tickets are related to
672
-            // (and TKT_parent is used for autosaves so that's not a field we can reliably use).
673
-            // let's assign any tickets that have been setup to the saved_tickets tracker
674
-            // save existing TKT
675
-            $ticket->save();
676
-            if ($create_new_TKT && $new_ticket instanceof EE_Ticket) {
677
-                // save new TKT
678
-                $new_ticket->save();
679
-                // add new ticket to array
680
-                $saved_tickets[ $new_ticket->ID() ] = $new_ticket;
681
-                do_action(
682
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
683
-                    $new_ticket,
684
-                    $row,
685
-                    $ticket_data,
686
-                    $data
687
-                );
688
-            } else {
689
-                // add ticket to saved tickets
690
-                $saved_tickets[ $ticket->ID() ] = $ticket;
691
-                do_action(
692
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
693
-                    $ticket,
694
-                    $row,
695
-                    $ticket_data,
696
-                    $data
697
-                );
698
-            }
699
-        }
700
-        // now we need to handle tickets actually "deleted permanently".
701
-        // There are cases where we'd want this to happen
702
-        // (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
703
-        // Or a draft event was saved and in the process of editing a ticket is trashed.
704
-        // No sense in keeping all the related data in the db!
705
-        $old_tickets     = isset($old_tickets[0]) && $old_tickets[0] === '' ? [] : $old_tickets;
706
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
707
-        foreach ($tickets_removed as $id) {
708
-            $id = absint($id);
709
-            // get the ticket for this id
710
-            $ticket_to_remove = $ticket_model->get_one_by_ID($id);
711
-            // if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
712
-            if ($ticket_to_remove->get('TKT_is_default')) {
713
-                continue;
714
-            }
715
-            // if this ticket has any registrations attached so then we just ARCHIVE
716
-            // because we don't actually permanently delete these tickets.
717
-            if ($ticket_to_remove->count_related('Registration') > 0) {
718
-                $ticket_to_remove->delete();
719
-                continue;
720
-            }
721
-            // need to get all the related datetimes on this ticket and remove from every single one of them
722
-            // (remember this process can ONLY kick off if there are NO tickets_sold)
723
-            $datetimes = $ticket_to_remove->get_many_related('Datetime');
724
-            foreach ($datetimes as $datetime) {
725
-                $ticket_to_remove->_remove_relation_to($datetime, 'Datetime');
726
-            }
727
-            // need to do the same for prices (except these prices can also be deleted because again,
728
-            // tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
729
-            $ticket_to_remove->delete_related('Price');
730
-            do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $ticket_to_remove);
731
-            // finally let's delete this ticket
732
-            // (which should not be blocked at this point b/c we've removed all our relationships)
733
-            $ticket_to_remove->delete_or_restore();
734
-        }
735
-        return $saved_tickets;
736
-    }
737
-
738
-
739
-    /**
740
-     * @access  protected
741
-     * @param EE_Ticket     $ticket
742
-     * @param EE_Datetime[] $saved_datetimes
743
-     * @param int[]         $added_datetimes
744
-     * @param int[]         $removed_datetimes
745
-     * @return EE_Ticket
746
-     * @throws EE_Error
747
-     * @throws ReflectionException
748
-     */
749
-    protected function _update_ticket_datetimes(
750
-        EE_Ticket $ticket,
751
-        array $saved_datetimes = [],
752
-        array $added_datetimes = [],
753
-        array $removed_datetimes = []
754
-    ): EE_Ticket {
755
-        // to start we have to add the ticket to all the datetimes its supposed to be with,
756
-        // and removing the ticket from datetimes it got removed from.
757
-        // first let's add datetimes
758
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
759
-            foreach ($added_datetimes as $row_id) {
760
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
761
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
762
-                    // Is this an existing ticket (has an ID) and does it have any sold?
763
-                    // If so, then we need to add that to the DTT sold because this DTT is getting added.
764
-                    if ($ticket->ID() && $ticket->sold() > 0) {
765
-                        $saved_datetimes[ $row_id ]->increaseSold($ticket->sold(), false);
766
-                    }
767
-                }
768
-            }
769
-        }
770
-        // then remove datetimes
771
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
772
-            foreach ($removed_datetimes as $row_id) {
773
-                // its entirely possible that a datetime got deleted (instead of just removed from relationship.
774
-                // So make sure we skip over this if the datetime isn't in the $saved_datetimes array)
775
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
777
-                }
778
-            }
779
-        }
780
-        // cap ticket qty by datetime reg limits
781
-        $ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
782
-        return $ticket;
783
-    }
784
-
785
-
786
-    /**
787
-     * @access  protected
788
-     * @param EE_Ticket $ticket
789
-     * @param array     $price_rows
790
-     * @param int|float $ticket_price
791
-     * @param int|float $base_price
792
-     * @param int       $base_price_id
793
-     * @return EE_Ticket
794
-     * @throws ReflectionException
795
-     * @throws InvalidArgumentException
796
-     * @throws InvalidInterfaceException
797
-     * @throws InvalidDataTypeException
798
-     * @throws EE_Error
799
-     */
800
-    protected function _duplicate_ticket(
801
-        EE_Ticket $ticket,
802
-        array $price_rows = [],
803
-        $ticket_price = 0,
804
-        $base_price = 0,
805
-        int $base_price_id = 0
806
-    ): EE_Ticket {
807
-        // create new ticket that's a copy of the existing
808
-        // except a new id of course (and not archived)
809
-        // AND has the new TKT_price associated with it.
810
-        $new_ticket = clone $ticket;
811
-        $new_ticket->set('TKT_ID', 0);
812
-        $new_ticket->set_deleted(0);
813
-        $new_ticket->set_price($ticket_price);
814
-        $new_ticket->set_sold(0);
815
-        // let's get a new ID for this ticket
816
-        $new_ticket->save();
817
-        // we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
818
-        $datetimes_on_existing = $ticket->datetimes();
819
-        $new_ticket            = $this->_update_ticket_datetimes(
820
-            $new_ticket,
821
-            $datetimes_on_existing,
822
-            array_keys($datetimes_on_existing)
823
-        );
824
-        // $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
825
-        // if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
826
-        // available.
827
-        if ($ticket->sold() > 0) {
828
-            $new_qty = $ticket->qty() - $ticket->sold();
829
-            $new_ticket->set_qty($new_qty);
830
-        }
831
-        // now we update the prices just for this ticket
832
-        $new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
833
-        // and we update the base price
834
-        return $this->_add_prices_to_ticket(
835
-            [],
836
-            $new_ticket,
837
-            true,
838
-            $base_price,
839
-            $base_price_id
840
-        );
841
-    }
842
-
843
-
844
-    /**
845
-     * This attaches a list of given prices to a ticket.
846
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
847
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
848
-     * price info and prices are automatically "archived" via the ticket.
849
-     *
850
-     * @access  private
851
-     * @param array     $prices        Array of prices from the form.
852
-     * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
853
-     * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
854
-     * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
855
-     * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
856
-     * @return EE_Ticket
857
-     * @throws ReflectionException
858
-     * @throws InvalidArgumentException
859
-     * @throws InvalidInterfaceException
860
-     * @throws InvalidDataTypeException
861
-     * @throws EE_Error
862
-     */
863
-    protected function _add_prices_to_ticket(
864
-        array $prices,
865
-        EE_Ticket $ticket,
866
-        bool $new_prices = false,
867
-        $base_price = false,
868
-        $base_price_id = false
869
-    ): EE_Ticket {
870
-        $price_model = EEM_Price::instance();
871
-        // let's just get any current prices that may exist on the given ticket
872
-        // so we can remove any prices that got trashed in this session.
873
-        $current_prices_on_ticket = $base_price !== false
874
-            ? $ticket->base_price(true)
875
-            : $ticket->price_modifiers();
876
-        $updated_prices           = [];
877
-        // if $base_price ! FALSE then updating a base price.
878
-        if ($base_price !== false) {
879
-            $prices[1] = [
880
-                'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
881
-                'PRT_ID'     => 1,
882
-                'PRC_amount' => $base_price,
883
-                'PRC_name'   => $ticket->get('TKT_name'),
884
-                'PRC_desc'   => $ticket->get('TKT_description'),
885
-            ];
886
-        }
887
-        // possibly need to save ticket
888
-        if (! $ticket->ID()) {
889
-            $ticket->save();
890
-        }
891
-        foreach ($prices as $row => $prc) {
892
-            $prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
893
-            if (empty($prt_id)) {
894
-                continue;
895
-            } //prices MUST have a price type id.
896
-            $PRC_values = [
897
-                'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
898
-                'PRT_ID'         => $prt_id,
899
-                'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
900
-                'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
901
-                'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
902
-                'PRC_is_default' => false,
903
-                // make sure we set PRC_is_default to false for all ticket saves from event_editor
904
-                'PRC_order'      => $row,
905
-            ];
906
-            if ($new_prices || empty($PRC_values['PRC_ID'])) {
907
-                $PRC_values['PRC_ID'] = 0;
908
-                $price                = EE_Registry::instance()->load_class(
909
-                    'Price',
910
-                    [$PRC_values],
911
-                    false,
912
-                    false
913
-                );
914
-            } else {
915
-                $price = $price_model->get_one_by_ID($prc['PRC_ID']);
916
-                // update this price with new values
917
-                foreach ($PRC_values as $field => $value) {
918
-                    $price->set($field, $value);
919
-                }
920
-            }
921
-            $price->save();
922
-            $updated_prices[ $price->ID() ] = $price;
923
-            $ticket->_add_relation_to($price, 'Price');
924
-        }
925
-        // now let's remove any prices that got removed from the ticket
926
-        if (! empty($current_prices_on_ticket)) {
927
-            $current          = array_keys($current_prices_on_ticket);
928
-            $updated          = array_keys($updated_prices);
929
-            $prices_to_remove = array_diff($current, $updated);
930
-            if (! empty($prices_to_remove)) {
931
-                foreach ($prices_to_remove as $prc_id) {
932
-                    $p = $current_prices_on_ticket[ $prc_id ];
933
-                    $ticket->_remove_relation_to($p, 'Price');
934
-                    // delete permanently the price
935
-                    $p->delete_or_restore();
936
-                }
937
-            }
938
-        }
939
-        return $ticket;
940
-    }
941
-
942
-
943
-    /**
944
-     * @throws ReflectionException
945
-     * @throws InvalidArgumentException
946
-     * @throws InvalidInterfaceException
947
-     * @throws InvalidDataTypeException
948
-     * @throws DomainException
949
-     * @throws EE_Error
950
-     */
951
-    public function pricing_metabox()
952
-    {
953
-        $event          = $this->_adminpage_obj->get_cpt_model_obj();
954
-        $timezone       = $event instanceof EE_Event ? $event->timezone_string() : null;
955
-        $price_model    = EEM_Price::instance($timezone);
956
-        $ticket_model   = EEM_Ticket::instance($timezone);
957
-        $datetime_model = EEM_Datetime::instance($timezone);
958
-        $all_tickets    = [];
959
-
960
-        // set is_creating_event property.
961
-        $EVT_ID                   = $event->ID();
962
-        $this->_is_creating_event = empty($this->_req_data['post']);
963
-        $existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = [];
964
-
965
-        // default main template args
966
-        $main_template_args = [
967
-            'event_datetime_help_link' => EEH_Template::get_help_tab_link(
968
-                'event_editor_event_datetimes_help_tab',
969
-                $this->_adminpage_obj->page_slug,
970
-                $this->_adminpage_obj->get_req_action()
971
-            ),
972
-
973
-            // todo need to add a filter to the template for the help text
974
-            // in the Events_Admin_Page core file so we can add further help
975
-            'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
976
-                'add_new_dtt_info',
977
-                $this->_adminpage_obj->page_slug,
978
-                $this->_adminpage_obj->get_req_action()
979
-            ),
980
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
981
-            'datetime_rows'            => '',
982
-            'show_tickets_container'   => '',
983
-            'ticket_rows'              => '',
984
-            'ee_collapsible_status'    => ' ee-collapsible-open'
985
-            // $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
986
-        ];
987
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
988
-
989
-        /**
990
-         * 1. Start with retrieving Datetimes
991
-         * 2. For each datetime get related tickets
992
-         * 3. For each ticket get related prices
993
-         */
994
-        $datetimes                            = $datetime_model->get_all_event_dates($EVT_ID);
995
-        $main_template_args['total_dtt_rows'] = count($datetimes);
996
-
997
-        /**
998
-         * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
999
-         * for why we are counting $datetime_row and then setting that on the Datetime object
1000
-         */
1001
-        $datetime_row = 1;
1002
-        foreach ($datetimes as $datetime) {
1003
-            $DTT_ID = $datetime->get('DTT_ID');
1004
-            $datetime->set('DTT_order', $datetime_row);
1005
-            $existing_datetime_ids[] = $DTT_ID;
1006
-            // tickets attached
1007
-            $related_tickets = $datetime->ID() > 0
1008
-                ? $datetime->get_many_related(
1009
-                    'Ticket',
1010
-                    [
1011
-                        [
1012
-                            'OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0],
1013
-                        ],
1014
-                        'default_where_conditions' => 'none',
1015
-                        'order_by'                 => ['TKT_order' => 'ASC'],
1016
-                    ]
1017
-                )
1018
-                : [];
1019
-            // if there are no related tickets this is likely a new event OR auto-draft
1020
-            // event so we need to generate the default tickets because datetimes
1021
-            // ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1022
-            // datetime on the event.
1023
-            if (empty($related_tickets) && count($datetimes) < 2) {
1024
-                $related_tickets = $ticket_model->get_all_default_tickets();
1025
-                // this should be ordered by TKT_ID, so let's grab the first default ticket
1026
-                // (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1027
-                $default_prices      = $price_model->get_all_default_prices();
1028
-                $main_default_ticket = reset($related_tickets);
1029
-                if ($main_default_ticket instanceof EE_Ticket) {
1030
-                    foreach ($default_prices as $default_price) {
1031
-                        if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1032
-                            continue;
1033
-                        }
1034
-                        $main_default_ticket->cache('Price', $default_price);
1035
-                    }
1036
-                }
1037
-            }
1038
-            // we can't actually setup rows in this loop yet cause we don't know all
1039
-            // the unique tickets for this event yet (tickets are linked through all datetimes).
1040
-            // So we're going to temporarily cache some of that information.
1041
-            // loop through and setup the ticket rows and make sure the order is set.
1042
-            foreach ($related_tickets as $ticket) {
1043
-                $TKT_ID     = $ticket->get('TKT_ID');
1044
-                $ticket_row = $ticket->get('TKT_row');
1045
-                // we only want unique tickets in our final display!!
1046
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1047
-                    $existing_ticket_ids[] = $TKT_ID;
1048
-                    $all_tickets[]         = $ticket;
1049
-                }
1050
-                // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1051
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1052
-                // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1053
-                if (
1054
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1055
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1056
-                ) {
1057
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1058
-                }
1059
-            }
1060
-            $datetime_row++;
1061
-        }
1062
-        $main_template_args['total_ticket_rows']     = count($existing_ticket_ids);
1063
-        $main_template_args['existing_ticket_ids']   = implode(',', $existing_ticket_ids);
1064
-        $main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1065
-        // sort $all_tickets by order
1066
-        usort(
1067
-            $all_tickets,
1068
-            function (EE_Ticket $a, EE_Ticket $b) {
1069
-                $a_order = (int) $a->get('TKT_order');
1070
-                $b_order = (int) $b->get('TKT_order');
1071
-                if ($a_order === $b_order) {
1072
-                    return 0;
1073
-                }
1074
-                return ($a_order < $b_order) ? -1 : 1;
1075
-            }
1076
-        );
1077
-        // k NOW we have all the data we need for setting up the datetime rows
1078
-        // and ticket rows so we start our datetime loop again.
1079
-        $datetime_row = 1;
1080
-        foreach ($datetimes as $datetime) {
1081
-            $main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1082
-                $datetime_row,
1083
-                $datetime,
1084
-                $datetime_tickets,
1085
-                $all_tickets,
1086
-                false,
1087
-                $datetimes
1088
-            );
1089
-            $datetime_row++;
1090
-        }
1091
-        // then loop through all tickets for the ticket rows.
1092
-        $ticket_row = 1;
1093
-        foreach ($all_tickets as $ticket) {
1094
-            $main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1095
-                $ticket_row,
1096
-                $ticket,
1097
-                $ticket_datetimes,
1098
-                $datetimes,
1099
-                false,
1100
-                $all_tickets
1101
-            );
1102
-            $ticket_row++;
1103
-        }
1104
-        $main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1105
-
1106
-        $status_change_notice = LoaderFactory::getLoader()->getShared(
1107
-            'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
1108
-        );
1109
-
1110
-        $main_template_args['status_change_notice'] = $status_change_notice->display(
1111
-            '__event-editor',
1112
-            'espresso-events'
1113
-        );
1114
-
1115
-        EEH_Template::display_template(
1116
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1117
-            $main_template_args
1118
-        );
1119
-    }
1120
-
1121
-
1122
-    /**
1123
-     * @param int|string  $datetime_row
1124
-     * @param EE_Datetime $datetime
1125
-     * @param array       $datetime_tickets
1126
-     * @param array       $all_tickets
1127
-     * @param bool        $default
1128
-     * @param array       $all_datetimes
1129
-     * @return string
1130
-     * @throws DomainException
1131
-     * @throws EE_Error
1132
-     * @throws ReflectionException
1133
-     */
1134
-    protected function _get_datetime_row(
1135
-        $datetime_row,
1136
-        EE_Datetime $datetime,
1137
-        array $datetime_tickets = [],
1138
-        array $all_tickets = [],
1139
-        bool $default = false,
1140
-        array $all_datetimes = []
1141
-    ): string {
1142
-        return EEH_Template::display_template(
1143
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1144
-            [
1145
-                'dtt_edit_row'             => $this->_get_dtt_edit_row(
1146
-                    $datetime_row,
1147
-                    $datetime,
1148
-                    $default,
1149
-                    $all_datetimes
1150
-                ),
1151
-                'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1152
-                    $datetime_row,
1153
-                    $datetime,
1154
-                    $datetime_tickets,
1155
-                    $all_tickets,
1156
-                    $default
1157
-                ),
1158
-                'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1159
-            ],
1160
-            true
1161
-        );
1162
-    }
1163
-
1164
-
1165
-    /**
1166
-     * This method is used to generate a datetime fields  edit row.
1167
-     * The same row is used to generate a row with valid DTT objects
1168
-     * and the default row that is used as the skeleton by the js.
1169
-     *
1170
-     * @param int|string       $datetime_row  The row number for the row being generated.
1171
-     * @param EE_Datetime|null $datetime
1172
-     * @param bool             $default       Whether a default row is being generated or not.
1173
-     * @param EE_Datetime[]    $all_datetimes This is the array of all datetimes used in the editor.
1174
-     * @return string
1175
-     * @throws EE_Error
1176
-     * @throws ReflectionException
1177
-     */
1178
-    protected function _get_dtt_edit_row(
1179
-        $datetime_row,
1180
-        ?EE_Datetime $datetime,
1181
-        bool $default,
1182
-        array $all_datetimes
1183
-    ): string {
1184
-        // if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1185
-        $default                     = ! $datetime instanceof EE_Datetime ? true : $default;
1186
-        $template_args               = [
1187
-            'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1188
-            'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1189
-            'edit_dtt_expanded'    => '',
1190
-            'DTT_ID'               => $default ? '' : $datetime->ID(),
1191
-            'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1192
-            'DTT_description'      => $default ? '' : $datetime->get_raw('DTT_description'),
1193
-            'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1194
-            'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1195
-            'DTT_reg_limit'        => $default
1196
-                ? ''
1197
-                : $datetime->get_pretty(
1198
-                    'DTT_reg_limit',
1199
-                    'input'
1200
-                ),
1201
-            'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1202
-            'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1203
-            'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1204
-            'can_clone'            => $datetime instanceof EE_Datetime && $datetime->get('DTT_sold'),
1205
-            'can_trash'            => count($all_datetimes) > 1
1206
-                                      && $datetime instanceof EE_Datetime
1207
-                                      && ! $datetime->get('DTT_sold'),
1208
-            'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1209
-                ? 'trash-entity dashicons dashicons-lock'
1210
-                : 'trash-entity dashicons dashicons-post-trash clickable',
1211
-            'reg_list_url'         => $default || ! $datetime->event() instanceof EE_Event
1212
-                ? ''
1213
-                : EE_Admin_Page::add_query_args_and_nonce(
1214
-                    [
1215
-                        'event_id'    => $datetime->event()->ID(),
1216
-                        'datetime_id' => $datetime->ID(),
1217
-                        'use_filters' => true,
1218
-                    ],
1219
-                    REG_ADMIN_URL
1220
-                ),
1221
-        ];
1222
-        $template_args['show_trash'] = count($all_datetimes) === 1
1223
-                                       && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1224
-            ? 'display:none'
1225
-            : '';
1226
-        // allow filtering of template args at this point.
1227
-        $template_args = apply_filters(
1228
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1229
-            $template_args,
1230
-            $datetime_row,
1231
-            $datetime,
1232
-            $default,
1233
-            $all_datetimes,
1234
-            $this->_is_creating_event
1235
-        );
1236
-        return EEH_Template::display_template(
1237
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1238
-            $template_args,
1239
-            true
1240
-        );
1241
-    }
1242
-
1243
-
1244
-    /**
1245
-     * @param int|string       $datetime_row
1246
-     * @param EE_Datetime|null $datetime
1247
-     * @param array            $datetime_tickets
1248
-     * @param array            $all_tickets
1249
-     * @param bool             $default
1250
-     * @return string
1251
-     * @throws DomainException
1252
-     * @throws EE_Error
1253
-     * @throws ReflectionException
1254
-     */
1255
-    protected function _get_dtt_attached_tickets_row(
1256
-        $datetime_row,
1257
-        ?EE_Datetime $datetime,
1258
-        array $datetime_tickets = [],
1259
-        array $all_tickets = [],
1260
-        bool $default = false
1261
-    ): string {
1262
-        $template_args = [
1263
-            'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1264
-            'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1265
-            'DTT_description'                   => $default ? '' : $datetime->get_raw('DTT_description'),
1266
-            'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1267
-            'show_tickets_row'                  => 'display:none;',
1268
-            'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1269
-                'add_new_ticket_via_datetime',
1270
-                $this->_adminpage_obj->page_slug,
1271
-                $this->_adminpage_obj->get_req_action()
1272
-            ),
1273
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1274
-            'DTT_ID'                            => $default ? '' : $datetime->ID(),
1275
-        ];
1276
-        // need to setup the list items (but only if this isn't a default skeleton setup)
1277
-        if (! $default) {
1278
-            $ticket_row = 1;
1279
-            foreach ($all_tickets as $ticket) {
1280
-                $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1281
-                    $datetime_row,
1282
-                    $ticket_row,
1283
-                    $datetime,
1284
-                    $ticket,
1285
-                    $datetime_tickets
1286
-                );
1287
-                $ticket_row++;
1288
-            }
1289
-        }
1290
-        // filter template args at this point
1291
-        $template_args = apply_filters(
1292
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1293
-            $template_args,
1294
-            $datetime_row,
1295
-            $datetime,
1296
-            $datetime_tickets,
1297
-            $all_tickets,
1298
-            $default,
1299
-            $this->_is_creating_event
1300
-        );
1301
-        return EEH_Template::display_template(
1302
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1303
-            $template_args,
1304
-            true
1305
-        );
1306
-    }
1307
-
1308
-
1309
-    /**
1310
-     * @param int|string       $datetime_row
1311
-     * @param int|string       $ticket_row
1312
-     * @param EE_Datetime|null $datetime
1313
-     * @param EE_Ticket|null   $ticket
1314
-     * @param array            $datetime_tickets
1315
-     * @param bool             $default
1316
-     * @return string
1317
-     * @throws EE_Error
1318
-     * @throws ReflectionException
1319
-     */
1320
-    protected function _get_datetime_tickets_list_item(
1321
-        $datetime_row,
1322
-        $ticket_row,
1323
-        ?EE_Datetime $datetime,
1324
-        ?EE_Ticket $ticket,
1325
-        array $datetime_tickets = [],
1326
-        bool $default = false
1327
-    ): string {
1328
-        $datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1329
-            ? $datetime_tickets[ $datetime->ID() ]
1330
-            : [];
1331
-        $display_row      = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1332
-        $no_ticket        = $default && empty($ticket);
1333
-        $template_args    = [
1334
-            'dtt_row'                 => $default
1335
-                ? 'DTTNUM'
1336
-                : $datetime_row,
1337
-            'tkt_row'                 => $no_ticket
1338
-                ? 'TICKETNUM'
1339
-                : $ticket_row,
1340
-            'datetime_ticket_checked' => in_array($display_row, $datetime_tickets, true)
1341
-                ? ' checked'
1342
-                : '',
1343
-            'ticket_selected'         => in_array($display_row, $datetime_tickets, true)
1344
-                ? ' ticket-selected'
1345
-                : '',
1346
-            'TKT_name'                => $no_ticket
1347
-                ? 'TKTNAME'
1348
-                : $ticket->get('TKT_name'),
1349
-            'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1350
-                ? ' tkt-status-' . EE_Ticket::onsale
1351
-                : ' tkt-status-' . $ticket->ticket_status(),
1352
-        ];
1353
-        // filter template args
1354
-        $template_args = apply_filters(
1355
-            'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1356
-            $template_args,
1357
-            $datetime_row,
1358
-            $ticket_row,
1359
-            $datetime,
1360
-            $ticket,
1361
-            $datetime_tickets,
1362
-            $default,
1363
-            $this->_is_creating_event
1364
-        );
1365
-        return EEH_Template::display_template(
1366
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1367
-            $template_args,
1368
-            true
1369
-        );
1370
-    }
1371
-
1372
-
1373
-    /**
1374
-     * This generates the ticket row for tickets.
1375
-     * This same method is used to generate both the actual rows and the js skeleton row
1376
-     * (when default === true)
1377
-     *
1378
-     * @param int|string     $ticket_row       Represents the row number being generated.
1379
-     * @param EE_Ticket|null $ticket
1380
-     * @param EE_Datetime[]  $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1381
-     *                                         or empty for default
1382
-     * @param EE_Datetime[]  $all_datetimes    All Datetimes on the event or empty for default.
1383
-     * @param bool           $default          Whether default row being generated or not.
1384
-     * @param EE_Ticket[]    $all_tickets      This is an array of all tickets attached to the event
1385
-     *                                         (or empty in the case of defaults)
1386
-     * @return string
1387
-     * @throws InvalidArgumentException
1388
-     * @throws InvalidInterfaceException
1389
-     * @throws InvalidDataTypeException
1390
-     * @throws DomainException
1391
-     * @throws EE_Error
1392
-     * @throws ReflectionException
1393
-     */
1394
-    protected function _get_ticket_row(
1395
-        $ticket_row,
1396
-        ?EE_Ticket $ticket,
1397
-        array $ticket_datetimes,
1398
-        array $all_datetimes,
1399
-        bool $default = false,
1400
-        array $all_tickets = []
1401
-    ): string {
1402
-        // if $ticket is not an instance of EE_Ticket then force default to true.
1403
-        $default = ! $ticket instanceof EE_Ticket ? true : $default;
1404
-        $prices  = ! empty($ticket) && ! $default
1405
-            ? $ticket->get_many_related(
1406
-                'Price',
1407
-                ['default_where_conditions' => 'none', 'order_by' => ['PRC_order' => 'ASC']]
1408
-            )
1409
-            : [];
1410
-        // if there is only one price (which would be the base price)
1411
-        // or NO prices and this ticket is a default ticket,
1412
-        // let's just make sure there are no cached default prices on the object.
1413
-        // This is done by not including any query_params.
1414
-        if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1415
-            $prices = $ticket->prices();
1416
-        }
1417
-        // check if we're dealing with a default ticket in which case
1418
-        // we don't want any starting_ticket_datetime_row values set
1419
-        // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1420
-        // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1421
-        $default_datetime = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1422
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1423
-            ? $ticket_datetimes[ $ticket->ID() ]
1424
-            : [];
1425
-        $ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1426
-        $base_price       = $default ? null : $ticket->base_price();
1427
-        $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1428
-        // breaking out complicated condition for ticket_status
1429
-        if ($default) {
1430
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1431
-        } else {
1432
-            $ticket_status_class = $ticket->is_default()
1433
-                ? ' tkt-status-' . EE_Ticket::onsale
1434
-                : ' tkt-status-' . $ticket->ticket_status();
1435
-        }
1436
-        // breaking out complicated condition for TKT_taxable
1437
-        if ($default) {
1438
-            $TKT_taxable = '';
1439
-        } else {
1440
-            $TKT_taxable = $ticket->taxable()
1441
-                ? 'checked'
1442
-                : '';
1443
-        }
1444
-        if ($default) {
1445
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1446
-        } elseif ($ticket->is_default()) {
1447
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1448
-        } else {
1449
-            $TKT_status = $ticket->ticket_status(true);
1450
-        }
1451
-        if ($default) {
1452
-            $TKT_min = '';
1453
-        } else {
1454
-            $TKT_min = $ticket->min();
1455
-            if ($TKT_min === -1 || $TKT_min === 0) {
1456
-                $TKT_min = '';
1457
-            }
1458
-        }
1459
-        $template_args                 = [
1460
-            'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1461
-            'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1462
-            // on initial page load this will always be the correct order.
1463
-            'tkt_status_class'              => $ticket_status_class,
1464
-            'display_edit_tkt_row'          => 'display:none;',
1465
-            'edit_tkt_expanded'             => '',
1466
-            'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1467
-            'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1468
-            'TKT_start_date'                => $default
1469
-                ? ''
1470
-                : $ticket->get_date('TKT_start_date', $this->_date_time_format),
1471
-            'TKT_end_date'                  => $default
1472
-                ? ''
1473
-                : $ticket->get_date('TKT_end_date', $this->_date_time_format),
1474
-            'TKT_status'                    => $TKT_status,
1475
-            'TKT_price'                     => $default
1476
-                ? ''
1477
-                : EEH_Template::format_currency(
1478
-                    $ticket->get_ticket_total_with_taxes(),
1479
-                    false,
1480
-                    false
1481
-                ),
1482
-            'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1483
-            'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1484
-            'TKT_qty'                       => $default
1485
-                ? ''
1486
-                : $ticket->get_pretty('TKT_qty', 'symbol'),
1487
-            'TKT_qty_for_input'             => $default
1488
-                ? ''
1489
-                : $ticket->get_pretty('TKT_qty', 'input'),
1490
-            'TKT_uses'                      => $default
1491
-                ? ''
1492
-                : $ticket->get_pretty('TKT_uses', 'input'),
1493
-            'TKT_min'                       => $TKT_min,
1494
-            'TKT_max'                       => $default
1495
-                ? ''
1496
-                : $ticket->get_pretty('TKT_max', 'input'),
1497
-            'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold(),
1498
-            'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1499
-            'TKT_registrations'             => $default
1500
-                ? 0
1501
-                : $ticket->count_registrations(
1502
-                    [
1503
-                        [
1504
-                            'STS_ID' => [
1505
-                                '!=',
1506
-                                EEM_Registration::status_id_incomplete,
1507
-                            ],
1508
-                        ],
1509
-                    ]
1510
-                ),
1511
-            'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1512
-            'TKT_description'               => $default ? '' : $ticket->get_raw('TKT_description'),
1513
-            'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1514
-            'TKT_required'                  => $default ? 0 : $ticket->required(),
1515
-            'TKT_is_default_selector'       => '',
1516
-            'ticket_price_rows'             => '',
1517
-            'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1518
-                ? ''
1519
-                : $base_price->get_pretty('PRC_amount', 'localized_float'),
1520
-            'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1521
-            'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1522
-                ? ''
1523
-                : 'display:none;',
1524
-            'show_price_mod_button'         => count($prices) > 1
1525
-                                               || ($default && $count_price_mods > 0)
1526
-                                               || (! $default && $ticket->deleted())
1527
-                ? 'display:none;'
1528
-                : '',
1529
-            'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1530
-            'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1531
-            'starting_ticket_datetime_rows' => $default || $default_datetime ? '' : implode(',', $tkt_datetimes),
1532
-            'ticket_datetime_rows'          => $default ? '' : implode(',', $tkt_datetimes),
1533
-            'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1534
-            'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1535
-            'TKT_taxable'                   => $TKT_taxable,
1536
-            'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1537
-                ? ''
1538
-                : 'display:none;',
1539
-            'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1540
-            'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1541
-                $ticket_subtotal,
1542
-                false,
1543
-                false
1544
-            ),
1545
-            'TKT_subtotal_amount'           => $ticket_subtotal,
1546
-            'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1547
-            'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1548
-            'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1549
-                ? ' ticket-archived'
1550
-                : '',
1551
-            'trash_icon'                    => $ticket instanceof EE_Ticket
1552
-                                               && $ticket->deleted()
1553
-                                               && ! $ticket->is_permanently_deleteable()
1554
-                ? 'dashicons dashicons-lock '
1555
-                : 'trash-entity dashicons dashicons-post-trash clickable',
1556
-
1557
-            'can_clone'            => $ticket instanceof EE_Ticket && ! $ticket->deleted(),
1558
-            'can_trash'            => $ticket instanceof EE_Ticket
1559
-                                      && (! $ticket->deleted() && $ticket->is_permanently_deleteable()),
1560
-        ];
1561
-        $template_args['trash_hidden'] = count($all_tickets) === 1
1562
-                                         && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1563
-            ? 'display:none'
1564
-            : '';
1565
-        // handle rows that should NOT be empty
1566
-        if (empty($template_args['TKT_start_date'])) {
1567
-            // if empty then the start date will be now.
1568
-            $template_args['TKT_start_date']   = date(
1569
-                $this->_date_time_format,
1570
-                current_time('timestamp')
1571
-            );
1572
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1573
-        }
1574
-        if (empty($template_args['TKT_end_date'])) {
1575
-            // get the earliest datetime (if present);
1576
-            $earliest_datetime = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1577
-                ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1578
-                    'Datetime',
1579
-                    ['order_by' => ['DTT_EVT_start' => 'ASC']]
1580
-                )
1581
-                : null;
1582
-            if (! empty($earliest_datetime)) {
1583
-                $template_args['TKT_end_date'] = $earliest_datetime->get_datetime(
1584
-                    'DTT_EVT_start',
1585
-                    $this->_date_time_format
1586
-                );
1587
-            } else {
1588
-                // default so let's just use what's been set for the default date-time which is 30 days from now.
1589
-                $template_args['TKT_end_date'] = date(
1590
-                    $this->_date_time_format,
1591
-                    mktime(
1592
-                        24,
1593
-                        0,
1594
-                        0,
1595
-                        date('m'),
1596
-                        date('d') + 29,
1597
-                        date('Y')
1598
-                    )
1599
-                );
1600
-            }
1601
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1602
-        }
1603
-        // generate ticket_datetime items
1604
-        if (! $default) {
1605
-            $datetime_row = 1;
1606
-            foreach ($all_datetimes as $datetime) {
1607
-                $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1608
-                    $datetime_row,
1609
-                    $ticket_row,
1610
-                    $datetime,
1611
-                    $ticket,
1612
-                    $ticket_datetimes,
1613
-                    $default
1614
-                );
1615
-                $datetime_row++;
1616
-            }
1617
-        }
1618
-        $price_row = 1;
1619
-        foreach ($prices as $price) {
1620
-            if (! $price instanceof EE_Price) {
1621
-                continue;
1622
-            }
1623
-            if ($price->is_base_price()) {
1624
-                $price_row++;
1625
-                continue;
1626
-            }
1627
-
1628
-            $show_trash  = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1629
-            $show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1630
-
1631
-            $template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1632
-                $ticket_row,
1633
-                $price_row,
1634
-                $price,
1635
-                $default,
1636
-                $ticket,
1637
-                $show_trash,
1638
-                $show_create
1639
-            );
1640
-            $price_row++;
1641
-        }
1642
-        // filter $template_args
1643
-        $template_args = apply_filters(
1644
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1645
-            $template_args,
1646
-            $ticket_row,
1647
-            $ticket,
1648
-            $ticket_datetimes,
1649
-            $all_datetimes,
1650
-            $default,
1651
-            $all_tickets,
1652
-            $this->_is_creating_event
1653
-        );
1654
-        return EEH_Template::display_template(
1655
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1656
-            $template_args,
1657
-            true
1658
-        );
1659
-    }
1660
-
1661
-
1662
-    /**
1663
-     * @param int|string     $ticket_row
1664
-     * @param EE_Ticket|null $ticket
1665
-     * @return string
1666
-     * @throws DomainException
1667
-     * @throws EE_Error
1668
-     * @throws ReflectionException
1669
-     */
1670
-    protected function _get_tax_rows($ticket_row, ?EE_Ticket $ticket): string
1671
-    {
1672
-        $tax_rows = '';
1673
-        /** @var EE_Price[] $taxes */
1674
-        $taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1675
-        foreach ($taxes as $tax) {
1676
-            $tax_added     = $this->_get_tax_added($tax, $ticket);
1677
-            $template_args = [
1678
-                'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1679
-                    ? ''
1680
-                    : 'display:none;',
1681
-                'tax_id'            => $tax->ID(),
1682
-                'tkt_row'           => $ticket_row,
1683
-                'tax_label'         => $tax->get('PRC_name'),
1684
-                'tax_added'         => $tax_added,
1685
-                'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1686
-                'tax_amount'        => $tax->get('PRC_amount'),
1687
-            ];
1688
-            $template_args = apply_filters(
1689
-                'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1690
-                $template_args,
1691
-                $ticket_row,
1692
-                $ticket,
1693
-                $this->_is_creating_event
1694
-            );
1695
-            $tax_rows      .= EEH_Template::display_template(
1696
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1697
-                $template_args,
1698
-                true
1699
-            );
1700
-        }
1701
-        return $tax_rows;
1702
-    }
1703
-
1704
-
1705
-    /**
1706
-     * @param EE_Price       $tax
1707
-     * @param EE_Ticket|null $ticket
1708
-     * @return float|int
1709
-     * @throws EE_Error
1710
-     * @throws ReflectionException
1711
-     */
1712
-    protected function _get_tax_added(EE_Price $tax, ?EE_Ticket $ticket)
1713
-    {
1714
-        $subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1715
-        return $subtotal * $tax->get('PRC_amount') / 100;
1716
-    }
1717
-
1718
-
1719
-    /**
1720
-     * @param int|string     $ticket_row
1721
-     * @param int|string     $price_row
1722
-     * @param EE_Price|null  $price
1723
-     * @param bool           $default
1724
-     * @param EE_Ticket|null $ticket
1725
-     * @param bool           $show_trash
1726
-     * @param bool           $show_create
1727
-     * @return string
1728
-     * @throws InvalidArgumentException
1729
-     * @throws InvalidInterfaceException
1730
-     * @throws InvalidDataTypeException
1731
-     * @throws DomainException
1732
-     * @throws EE_Error
1733
-     * @throws ReflectionException
1734
-     */
1735
-    protected function _get_ticket_price_row(
1736
-        $ticket_row,
1737
-        $price_row,
1738
-        ?EE_Price $price,
1739
-        bool $default,
1740
-        ?EE_Ticket $ticket,
1741
-        bool $show_trash = true,
1742
-        bool $show_create = true
1743
-    ): string {
1744
-        $send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1745
-        $template_args = [
1746
-            'tkt_row'               => $default && empty($ticket)
1747
-                ? 'TICKETNUM'
1748
-                : $ticket_row,
1749
-            'PRC_order'             => $default && empty($price)
1750
-                ? 'PRICENUM'
1751
-                : $price_row,
1752
-            'edit_prices_name'      => $default && empty($price)
1753
-                ? 'PRICENAMEATTR'
1754
-                : 'edit_prices',
1755
-            'price_type_selector'   => $default && empty($price)
1756
-                ? $this->_get_base_price_template($ticket_row, $price_row, $price, true)
1757
-                : $this->_get_price_type_selector(
1758
-                    $ticket_row,
1759
-                    $price_row,
1760
-                    $price,
1761
-                    $default,
1762
-                    $send_disabled
1763
-                ),
1764
-            'PRC_ID'                => $default && empty($price)
1765
-                ? 0
1766
-                : $price->ID(),
1767
-            'PRC_is_default'        => $default && empty($price)
1768
-                ? 0
1769
-                : $price->get('PRC_is_default'),
1770
-            'PRC_name'              => $default && empty($price)
1771
-                ? ''
1772
-                : $price->get('PRC_name'),
1773
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1774
-            'show_plus_or_minus'    => $default && empty($price)
1775
-                ? ''
1776
-                : 'display:none;',
1777
-            'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1778
-                ? 'display:none;'
1779
-                : '',
1780
-            'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1781
-                ? 'display:none;'
1782
-                : '',
1783
-            'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1784
-                ? 'display:none'
1785
-                : '',
1786
-            'PRC_amount'            => $default && empty($price)
1787
-                ? 0
1788
-                : $price->get_pretty('PRC_amount', 'localized_float'),
1789
-            'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1790
-                ? 'display:none;'
1791
-                : '',
1792
-            'show_trash_icon'       => $show_trash
1793
-                ? ''
1794
-                : ' style="display:none;"',
1795
-            'show_create_button'    => $show_create
1796
-                ? ''
1797
-                : ' style="display:none;"',
1798
-            'PRC_desc'              => $default && empty($price)
1799
-                ? ''
1800
-                : $price->get('PRC_desc'),
1801
-            'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1802
-        ];
1803
-        $template_args = apply_filters(
1804
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1805
-            $template_args,
1806
-            $ticket_row,
1807
-            $price_row,
1808
-            $price,
1809
-            $default,
1810
-            $ticket,
1811
-            $show_trash,
1812
-            $show_create,
1813
-            $this->_is_creating_event
1814
-        );
1815
-        return EEH_Template::display_template(
1816
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1817
-            $template_args,
1818
-            true
1819
-        );
1820
-    }
1821
-
1822
-
1823
-    /**
1824
-     * @param int|string    $ticket_row
1825
-     * @param int|string    $price_row
1826
-     * @param EE_Price|null $price
1827
-     * @param bool          $default
1828
-     * @param bool          $disabled
1829
-     * @return string
1830
-     * @throws ReflectionException
1831
-     * @throws InvalidArgumentException
1832
-     * @throws InvalidInterfaceException
1833
-     * @throws InvalidDataTypeException
1834
-     * @throws DomainException
1835
-     * @throws EE_Error
1836
-     */
1837
-    protected function _get_price_type_selector(
1838
-        $ticket_row,
1839
-        $price_row,
1840
-        ?EE_Price $price,
1841
-        bool $default,
1842
-        bool $disabled = false
1843
-    ): string {
1844
-        if ($price->is_base_price()) {
1845
-            return $this->_get_base_price_template(
1846
-                $ticket_row,
1847
-                $price_row,
1848
-                $price,
1849
-                $default
1850
-            );
1851
-        }
1852
-        return $this->_get_price_modifier_template(
1853
-            $ticket_row,
1854
-            $price_row,
1855
-            $price,
1856
-            $default,
1857
-            $disabled
1858
-        );
1859
-    }
1860
-
1861
-
1862
-    /**
1863
-     * @param int|string    $ticket_row
1864
-     * @param int|string    $price_row
1865
-     * @param EE_Price|null $price
1866
-     * @param bool          $default
1867
-     * @return string
1868
-     * @throws DomainException
1869
-     * @throws EE_Error
1870
-     * @throws ReflectionException
1871
-     */
1872
-    protected function _get_base_price_template(
1873
-        $ticket_row,
1874
-        $price_row,
1875
-        ?EE_Price $price,
1876
-        bool $default
1877
-    ): string {
1878
-        $template_args = [
1879
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1880
-            'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1881
-            'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1882
-            'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1883
-            'price_selected_operator'   => '+',
1884
-            'price_selected_is_percent' => 0,
1885
-        ];
1886
-        $template_args = apply_filters(
1887
-            'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1888
-            $template_args,
1889
-            $ticket_row,
1890
-            $price_row,
1891
-            $price,
1892
-            $default,
1893
-            $this->_is_creating_event
1894
-        );
1895
-        return EEH_Template::display_template(
1896
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1897
-            $template_args,
1898
-            true
1899
-        );
1900
-    }
1901
-
1902
-
1903
-    /**
1904
-     * @param int|string    $ticket_row
1905
-     * @param int|string    $price_row
1906
-     * @param EE_Price|null $price
1907
-     * @param bool          $default
1908
-     * @param bool          $disabled
1909
-     * @return string
1910
-     * @throws ReflectionException
1911
-     * @throws InvalidArgumentException
1912
-     * @throws InvalidInterfaceException
1913
-     * @throws InvalidDataTypeException
1914
-     * @throws DomainException
1915
-     * @throws EE_Error
1916
-     */
1917
-    protected function _get_price_modifier_template(
1918
-        $ticket_row,
1919
-        $price_row,
1920
-        ?EE_Price $price,
1921
-        bool $default,
1922
-        bool $disabled = false
1923
-    ): string {
1924
-        $select_name = $default && ! $price instanceof EE_Price
1925
-            ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1926
-            : 'edit_prices[' . esc_attr($ticket_row) . '][' . esc_attr($price_row) . '][PRT_ID]';
1927
-
1928
-        $price_type_model       = EEM_Price_Type::instance();
1929
-        $price_types            = $price_type_model->get_all(
1930
-            [
1931
-                [
1932
-                    'OR' => [
1933
-                        'PBT_ID'  => '2',
1934
-                        'PBT_ID*' => '3',
1935
-                    ],
1936
-                ],
1937
-            ]
1938
-        );
1939
-        $all_price_types        = $default && ! $price instanceof EE_Price
1940
-            ? [esc_html__('Select Modifier', 'event_espresso')]
1941
-            : [];
1942
-        $selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1943
-        $price_option_spans     = '';
1944
-        // setup price types for selector
1945
-        foreach ($price_types as $price_type) {
1946
-            if (! $price_type instanceof EE_Price_Type) {
1947
-                continue;
1948
-            }
1949
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1950
-            // while we're in the loop let's setup the option spans used by js
1951
-            $span_args          = [
1952
-                'PRT_ID'         => $price_type->ID(),
1953
-                'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1954
-                'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1955
-            ];
1956
-            $price_option_spans .= EEH_Template::display_template(
1957
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1958
-                $span_args,
1959
-                true
1960
-            );
1961
-        }
1962
-
1963
-        $select_name = $disabled
1964
-            ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1965
-            : $select_name;
1966
-
1967
-        $select_input = new EE_Select_Input(
1968
-            $all_price_types,
1969
-            [
1970
-                'default'               => $selected_price_type_id,
1971
-                'html_name'             => $select_name,
1972
-                'html_class'            => 'edit-price-PRT_ID',
1973
-                'other_html_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1974
-            ]
1975
-        );
1976
-
1977
-        $price_selected_operator   = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1978
-        $price_selected_operator   = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1979
-        $price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1980
-        $price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1981
-        $template_args             = [
1982
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1983
-            'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1984
-            'price_modifier_selector'   => $select_input->get_html_for_input(),
1985
-            'main_name'                 => $select_name,
1986
-            'selected_price_type_id'    => $selected_price_type_id,
1987
-            'price_option_spans'        => $price_option_spans,
1988
-            'price_selected_operator'   => $price_selected_operator,
1989
-            'price_selected_is_percent' => $price_selected_is_percent,
1990
-            'disabled'                  => $disabled,
1991
-        ];
1992
-        $template_args             = apply_filters(
1993
-            'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1994
-            $template_args,
1995
-            $ticket_row,
1996
-            $price_row,
1997
-            $price,
1998
-            $default,
1999
-            $disabled,
2000
-            $this->_is_creating_event
2001
-        );
2002
-        return EEH_Template::display_template(
2003
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2004
-            $template_args,
2005
-            true
2006
-        );
2007
-    }
2008
-
2009
-
2010
-    /**
2011
-     * @param int|string       $datetime_row
2012
-     * @param int|string       $ticket_row
2013
-     * @param EE_Datetime|null $datetime
2014
-     * @param EE_Ticket|null   $ticket
2015
-     * @param array            $ticket_datetimes
2016
-     * @param bool             $default
2017
-     * @return string
2018
-     * @throws DomainException
2019
-     * @throws EE_Error
2020
-     * @throws ReflectionException
2021
-     */
2022
-    protected function _get_ticket_datetime_list_item(
2023
-        $datetime_row,
2024
-        $ticket_row,
2025
-        ?EE_Datetime $datetime,
2026
-        ?EE_Ticket $ticket,
2027
-        array $ticket_datetimes = [],
2028
-        bool $default = false
2029
-    ): string {
2030
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2031
-            ? $ticket_datetimes[ $ticket->ID() ]
2032
-            : array();
2033
-        $template_args    = [
2034
-            'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2035
-                ? 'DTTNUM'
2036
-                : $datetime_row,
2037
-            'tkt_row'                  => $default
2038
-                ? 'TICKETNUM'
2039
-                : $ticket_row,
2040
-            'ticket_datetime_selected' => in_array($datetime_row, $tkt_datetimes, true)
2041
-                ? ' ticket-selected'
2042
-                : '',
2043
-            'ticket_datetime_checked'  => in_array($datetime_row, $tkt_datetimes, true)
2044
-                ? ' checked'
2045
-                : '',
2046
-            'DTT_name'                 => $default && empty($datetime)
2047
-                ? 'DTTNAME'
2048
-                : $datetime->get_dtt_display_name(true),
2049
-            'tkt_status_class'         => '',
2050
-        ];
2051
-        $template_args    = apply_filters(
2052
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2053
-            $template_args,
2054
-            $datetime_row,
2055
-            $ticket_row,
2056
-            $datetime,
2057
-            $ticket,
2058
-            $ticket_datetimes,
2059
-            $default,
2060
-            $this->_is_creating_event
2061
-        );
2062
-        return EEH_Template::display_template(
2063
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2064
-            $template_args,
2065
-            true
2066
-        );
2067
-    }
2068
-
2069
-
2070
-    /**
2071
-     * @param array $all_datetimes
2072
-     * @param array $all_tickets
2073
-     * @return string
2074
-     * @throws ReflectionException
2075
-     * @throws InvalidArgumentException
2076
-     * @throws InvalidInterfaceException
2077
-     * @throws InvalidDataTypeException
2078
-     * @throws DomainException
2079
-     * @throws EE_Error
2080
-     */
2081
-    protected function _get_ticket_js_structure(array $all_datetimes = [], array $all_tickets = []): string
2082
-    {
2083
-        $template_args = [
2084
-            'default_datetime_edit_row' => $this->_get_dtt_edit_row(
2085
-                'DTTNUM',
2086
-                null,
2087
-                true,
2088
-                $all_datetimes
2089
-            ),
2090
-            'default_ticket_row'        => $this->_get_ticket_row(
2091
-                'TICKETNUM',
2092
-                null,
2093
-                [],
2094
-                [],
2095
-                true
2096
-            ),
2097
-            'default_price_row'         => $this->_get_ticket_price_row(
2098
-                'TICKETNUM',
2099
-                'PRICENUM',
2100
-                null,
2101
-                true,
2102
-                null
2103
-            ),
2104
-
2105
-            'default_price_rows'                       => '',
2106
-            'default_base_price_amount'                => 0,
2107
-            'default_base_price_name'                  => '',
2108
-            'default_base_price_description'           => '',
2109
-            'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2110
-                'TICKETNUM',
2111
-                'PRICENUM',
2112
-                null,
2113
-                true
2114
-            ),
2115
-            'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2116
-                'DTTNUM',
2117
-                null,
2118
-                [],
2119
-                [],
2120
-                true
2121
-            ),
2122
-            'existing_available_datetime_tickets_list' => '',
2123
-            'existing_available_ticket_datetimes_list' => '',
2124
-            'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2125
-                'DTTNUM',
2126
-                'TICKETNUM',
2127
-                null,
2128
-                null,
2129
-                [],
2130
-                true
2131
-            ),
2132
-            'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2133
-                'DTTNUM',
2134
-                'TICKETNUM',
2135
-                null,
2136
-                null,
2137
-                [],
2138
-                true
2139
-            ),
2140
-        ];
2141
-        $ticket_row    = 1;
2142
-        foreach ($all_tickets as $ticket) {
2143
-            $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2144
-                'DTTNUM',
2145
-                $ticket_row,
2146
-                null,
2147
-                $ticket,
2148
-                [],
2149
-                true
2150
-            );
2151
-            $ticket_row++;
2152
-        }
2153
-        $datetime_row = 1;
2154
-        foreach ($all_datetimes as $datetime) {
2155
-            $template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2156
-                $datetime_row,
2157
-                'TICKETNUM',
2158
-                $datetime,
2159
-                null,
2160
-                [],
2161
-                true
2162
-            );
2163
-            $datetime_row++;
2164
-        }
2165
-        $price_model    = EEM_Price::instance();
2166
-        $default_prices = $price_model->get_all_default_prices();
2167
-        $price_row      = 1;
2168
-        foreach ($default_prices as $price) {
2169
-            if (! $price instanceof EE_Price) {
2170
-                continue;
2171
-            }
2172
-            if ($price->is_base_price()) {
2173
-                $template_args['default_base_price_amount']      = $price->get_pretty(
2174
-                    'PRC_amount',
2175
-                    'localized_float'
2176
-                );
2177
-                $template_args['default_base_price_name']        = $price->get('PRC_name');
2178
-                $template_args['default_base_price_description'] = $price->get('PRC_desc');
2179
-                $price_row++;
2180
-                continue;
2181
-            }
2182
-
2183
-            $show_trash  = ! ((count($default_prices) > 1 && $price_row === 1) || count($default_prices) === 1);
2184
-            $show_create = ! (count($default_prices) > 1 && count($default_prices) !== $price_row);
2185
-
2186
-            $template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2187
-                'TICKETNUM',
2188
-                $price_row,
2189
-                $price,
2190
-                true,
2191
-                null,
2192
-                $show_trash,
2193
-                $show_create
2194
-            );
2195
-            $price_row++;
2196
-        }
2197
-        $template_args = apply_filters(
2198
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2199
-            $template_args,
2200
-            $all_datetimes,
2201
-            $all_tickets,
2202
-            $this->_is_creating_event
2203
-        );
2204
-        return EEH_Template::display_template(
2205
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2206
-            $template_args,
2207
-            true
2208
-        );
2209
-    }
231
+					],
232
+					'DTT_OVERSELL_WARNING'  => [
233
+						'datetime_ticket' => esc_html__(
234
+							'You cannot add this ticket to this datetime because it has a sold amount that is greater than the amount of spots remaining for this datetime.',
235
+							'event_espresso'
236
+						),
237
+						'ticket_datetime' => esc_html__(
238
+							'You cannot add this datetime to this ticket because the ticket has a sold amount that is greater than the amount of spots remaining on the datetime.',
239
+							'event_espresso'
240
+						),
241
+					],
242
+					'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
243
+						$this->_date_format_strings['date'],
244
+						$this->_date_format_strings['time']
245
+					),
246
+					'DTT_START_OF_WEEK'     => ['dayValue' => (int) get_option('start_of_week')],
247
+				],
248
+			],
249
+		];
250
+	}
251
+
252
+
253
+	/**
254
+	 * @param array $update_callbacks
255
+	 * @return array
256
+	 */
257
+	public function caf_updates(array $update_callbacks): array
258
+	{
259
+		unset($update_callbacks['_default_tickets_update']);
260
+		$update_callbacks['datetime_and_tickets_caf_update'] = [$this, 'datetime_and_tickets_caf_update'];
261
+		return $update_callbacks;
262
+	}
263
+
264
+
265
+	/**
266
+	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
267
+	 *
268
+	 * @param EE_Event $event The Event object we're attaching data to
269
+	 * @param array    $data  The request data from the form
270
+	 * @throws ReflectionException
271
+	 * @throws Exception
272
+	 * @throws InvalidInterfaceException
273
+	 * @throws InvalidDataTypeException
274
+	 * @throws EE_Error
275
+	 * @throws InvalidArgumentException
276
+	 */
277
+	public function datetime_and_tickets_caf_update(EE_Event $event, array $data)
278
+	{
279
+		// first we need to start with datetimes cause they are the "root" items attached to events.
280
+		$saved_datetimes = $this->_update_datetimes($event, $data);
281
+		// next tackle the tickets (and prices?)
282
+		$this->_update_tickets($event, $saved_datetimes, $data);
283
+	}
284
+
285
+
286
+	/**
287
+	 * update event_datetimes
288
+	 *
289
+	 * @param EE_Event $event Event being updated
290
+	 * @param array    $data  the request data from the form
291
+	 * @return EE_Datetime[]
292
+	 * @throws Exception
293
+	 * @throws ReflectionException
294
+	 * @throws InvalidInterfaceException
295
+	 * @throws InvalidDataTypeException
296
+	 * @throws InvalidArgumentException
297
+	 * @throws EE_Error
298
+	 */
299
+	protected function _update_datetimes(EE_Event $event, array $data): array
300
+	{
301
+		$saved_datetime_ids  = [];
302
+		$saved_datetime_objs = [];
303
+		$timezone       = $data['timezone_string'] ?? null;
304
+		$datetime_model = EEM_Datetime::instance($timezone);
305
+
306
+		if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
307
+			throw new InvalidArgumentException(
308
+				esc_html__(
309
+					'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
310
+					'event_espresso'
311
+				)
312
+			);
313
+		}
314
+		foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315
+			// trim all values to ensure any excess whitespace is removed.
316
+			$datetime_data = array_map(
317
+				function ($datetime_data) {
318
+					return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319
+				},
320
+				$datetime_data
321
+			);
322
+
323
+			$datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
324
+											&& ! empty($datetime_data['DTT_EVT_end'])
325
+				? $datetime_data['DTT_EVT_end']
326
+				: $datetime_data['DTT_EVT_start'];
327
+			$datetime_values              = [
328
+				'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
329
+					? $datetime_data['DTT_ID']
330
+					: null,
331
+				'DTT_name'        => ! empty($datetime_data['DTT_name'])
332
+					? $datetime_data['DTT_name']
333
+					: '',
334
+				'DTT_description' => ! empty($datetime_data['DTT_description'])
335
+					? $datetime_data['DTT_description']
336
+					: '',
337
+				'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
338
+				'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
339
+				'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
340
+					? EE_INF
341
+					: $datetime_data['DTT_reg_limit'],
342
+				'DTT_order'       => ! isset($datetime_data['DTT_order'])
343
+					? $row
344
+					: $datetime_data['DTT_order'],
345
+			];
346
+
347
+			// if we have an id then let's get existing object first and then set the new values.
348
+			// Otherwise we instantiate a new object for save.
349
+			if (! empty($datetime_data['DTT_ID'])) {
350
+				$datetime = EE_Registry::instance()
351
+									   ->load_model('Datetime', [$timezone])
352
+									   ->get_one_by_ID($datetime_data['DTT_ID']);
353
+				// set date and time format according to what is set in this class.
354
+				$datetime->set_date_format($this->_date_format_strings['date']);
355
+				$datetime->set_time_format($this->_date_format_strings['time']);
356
+				foreach ($datetime_values as $field => $value) {
357
+					$datetime->set($field, $value);
358
+				}
359
+
360
+				// make sure the $datetime_id here is saved just in case
361
+				// after the add_relation_to() the autosave replaces it.
362
+				// We need to do this so we dont' TRASH the parent DTT.
363
+				// (save the ID for both key and value to avoid duplications)
364
+				$saved_datetime_ids[ $datetime->ID() ] = $datetime->ID();
365
+			} else {
366
+				$datetime = EE_Datetime::new_instance(
367
+					$datetime_values,
368
+					$timezone,
369
+					[$this->_date_format_strings['date'], $this->_date_format_strings['time']]
370
+				);
371
+				foreach ($datetime_values as $field => $value) {
372
+					$datetime->set($field, $value);
373
+				}
374
+			}
375
+			$datetime->save();
376
+			do_action(
377
+				'AHEE__espresso_events_Pricing_Hooks___update_datetimes_after_save',
378
+				$datetime,
379
+				$row,
380
+				$datetime_data,
381
+				$data
382
+			);
383
+			$datetime = $event->_add_relation_to($datetime, 'Datetime');
384
+			// before going any further make sure our dates are setup correctly
385
+			// so that the end date is always equal or greater than the start date.
386
+			if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
387
+				$datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
388
+				$datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
389
+				$datetime->save();
390
+			}
391
+			// now we have to make sure we add the new DTT_ID to the $saved_datetime_ids array
392
+			// because it is possible there was a new one created for the autosave.
393
+			// (save the ID for both key and value to avoid duplications)
394
+			$DTT_ID                        = $datetime->ID();
395
+			$saved_datetime_ids[ $DTT_ID ] = $DTT_ID;
396
+			$saved_datetime_objs[ $row ]   = $datetime;
397
+			// @todo if ANY of these updates fail then we want the appropriate global error message.
398
+		}
399
+		$event->save();
400
+		// now we need to REMOVE any datetimes that got deleted.
401
+		// Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
402
+		// So its safe to permanently delete at this point.
403
+		$old_datetimes = explode(',', $data['datetime_IDs']);
404
+		$old_datetimes = $old_datetimes[0] === '' ? [] : $old_datetimes;
405
+		if (is_array($old_datetimes)) {
406
+			$datetimes_to_delete = array_diff($old_datetimes, $saved_datetime_ids);
407
+			foreach ($datetimes_to_delete as $id) {
408
+				$id = absint($id);
409
+				if (empty($id)) {
410
+					continue;
411
+				}
412
+				$dtt_to_remove = $datetime_model->get_one_by_ID($id);
413
+				// remove tkt relationships.
414
+				$related_tickets = $dtt_to_remove->get_many_related('Ticket');
415
+				foreach ($related_tickets as $ticket) {
416
+					$dtt_to_remove->_remove_relation_to($ticket, 'Ticket');
417
+				}
418
+				$event->_remove_relation_to($id, 'Datetime');
419
+				$dtt_to_remove->refresh_cache_of_related_objects();
420
+			}
421
+		}
422
+		return $saved_datetime_objs;
423
+	}
424
+
425
+
426
+	/**
427
+	 * update tickets
428
+	 *
429
+	 * @param EE_Event      $event           Event object being updated
430
+	 * @param EE_Datetime[] $saved_datetimes an array of datetime ids being updated
431
+	 * @param array         $data            incoming request data
432
+	 * @return EE_Ticket[]
433
+	 * @throws Exception
434
+	 * @throws ReflectionException
435
+	 * @throws InvalidInterfaceException
436
+	 * @throws InvalidDataTypeException
437
+	 * @throws InvalidArgumentException
438
+	 * @throws EE_Error
439
+	 */
440
+	protected function _update_tickets(EE_Event $event, array $saved_datetimes, array $data): array
441
+	{
442
+		$new_ticket = null;
443
+		// stripslashes because WP filtered the $_POST ($data) array to add slashes
444
+		$data          = stripslashes_deep($data);
445
+		$timezone      = $data['timezone_string'] ?? null;
446
+		$ticket_model = EEM_Ticket::instance($timezone);
447
+
448
+		$saved_tickets = [];
449
+		$old_tickets   = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
450
+		if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
451
+			throw new InvalidArgumentException(
452
+				esc_html__(
453
+					'The "edit_tickets" array is invalid therefore the event can not be updated.',
454
+					'event_espresso'
455
+				)
456
+			);
457
+		}
458
+		foreach ($data['edit_tickets'] as $row => $ticket_data) {
459
+			$update_prices = $create_new_TKT = false;
460
+			// figure out what datetimes were added to the ticket
461
+			// and what datetimes were removed from the ticket in the session.
462
+			$starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
463
+			$ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
464
+			$datetimes_added               = array_diff($ticket_datetime_rows, $starting_ticket_datetime_rows);
465
+			$datetimes_removed             = array_diff($starting_ticket_datetime_rows, $ticket_datetime_rows);
466
+			// trim inputs to ensure any excess whitespace is removed.
467
+			$ticket_data = array_map(
468
+				function ($ticket_data) {
469
+					return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
470
+				},
471
+				$ticket_data
472
+			);
473
+			// note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
474
+			// because we're doing calculations prior to using the models.
475
+			// note incoming ['TKT_price'] value is already in standard notation (via js).
476
+			$ticket_price = isset($ticket_data['TKT_price'])
477
+				? round((float) $ticket_data['TKT_price'], 3)
478
+				: 0;
479
+			// note incoming base price needs converted from localized value.
480
+			$base_price = isset($ticket_data['TKT_base_price'])
481
+				? EEH_Money::convert_to_float_from_localized_money($ticket_data['TKT_base_price'])
482
+				: 0;
483
+			// if ticket price == 0 and $base_price != 0 then ticket price == base_price
484
+			$ticket_price  = $ticket_price === 0 && $base_price !== 0
485
+				? $base_price
486
+				: $ticket_price;
487
+			$base_price_id = $ticket_data['TKT_base_price_ID'] ?? 0;
488
+			$price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
489
+				? $data['edit_prices'][ $row ]
490
+				: [];
491
+			$now           = null;
492
+			if (empty($ticket_data['TKT_start_date'])) {
493
+				// lets' use now in the set timezone.
494
+				$now                           = new DateTime('now', new DateTimeZone($event->get_timezone()));
495
+				$ticket_data['TKT_start_date'] = $now->format($this->_date_time_format);
496
+			}
497
+			if (empty($ticket_data['TKT_end_date'])) {
498
+				/**
499
+				 * set the TKT_end_date to the first datetime attached to the ticket.
500
+				 */
501
+				$first_datetime              = $saved_datetimes[ reset($ticket_datetime_rows) ];
502
+				$ticket_data['TKT_end_date'] = $first_datetime->start_date_and_time($this->_date_time_format);
503
+			}
504
+			$TKT_values = [
505
+				'TKT_ID'          => ! empty($ticket_data['TKT_ID']) ? $ticket_data['TKT_ID'] : null,
506
+				'TTM_ID'          => ! empty($ticket_data['TTM_ID']) ? $ticket_data['TTM_ID'] : 0,
507
+				'TKT_name'        => ! empty($ticket_data['TKT_name']) ? $ticket_data['TKT_name'] : '',
508
+				'TKT_description' => ! empty($ticket_data['TKT_description'])
509
+									 && $ticket_data['TKT_description'] !== esc_html__(
510
+										 'You can modify this description',
511
+										 'event_espresso'
512
+									 )
513
+					? $ticket_data['TKT_description']
514
+					: '',
515
+				'TKT_start_date'  => $ticket_data['TKT_start_date'],
516
+				'TKT_end_date'    => $ticket_data['TKT_end_date'],
517
+				'TKT_qty'         => ! isset($ticket_data['TKT_qty']) || $ticket_data['TKT_qty'] === ''
518
+					? EE_INF
519
+					: $ticket_data['TKT_qty'],
520
+				'TKT_uses'        => ! isset($ticket_data['TKT_uses']) || $ticket_data['TKT_uses'] === ''
521
+					? EE_INF
522
+					: $ticket_data['TKT_uses'],
523
+				'TKT_min'         => empty($ticket_data['TKT_min']) ? 0 : $ticket_data['TKT_min'],
524
+				'TKT_max'         => empty($ticket_data['TKT_max']) ? EE_INF : $ticket_data['TKT_max'],
525
+				'TKT_row'         => $row,
526
+				'TKT_order'       => $ticket_data['TKT_order'] ?? 0,
527
+				'TKT_taxable'     => ! empty($ticket_data['TKT_taxable']) ? 1 : 0,
528
+				'TKT_required'    => ! empty($ticket_data['TKT_required']) ? 1 : 0,
529
+				'TKT_price'       => $ticket_price,
530
+			];
531
+			// if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
532
+			// which means in turn that the prices will become new prices as well.
533
+			if (isset($ticket_data['TKT_is_default']) && $ticket_data['TKT_is_default']) {
534
+				$TKT_values['TKT_ID']         = 0;
535
+				$TKT_values['TKT_is_default'] = 0;
536
+				$update_prices                = true;
537
+			}
538
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
539
+			// we actually do our saves ahead of doing any add_relations to
540
+			// because its entirely possible that this ticket wasn't removed or added to any datetime in the session
541
+			// but DID have it's items modified.
542
+			// keep in mind that if the TKT has been sold (and we have changed pricing information),
543
+			// then we won't be updating the ticket but instead a new ticket will be created and the old one archived.
544
+			if (absint($TKT_values['TKT_ID'])) {
545
+				$ticket = EE_Registry::instance()
546
+									 ->load_model('Ticket', [$timezone])
547
+									 ->get_one_by_ID($TKT_values['TKT_ID']);
548
+				if ($ticket instanceof EE_Ticket) {
549
+					$ticket = $this->_update_ticket_datetimes(
550
+						$ticket,
551
+						$saved_datetimes,
552
+						$datetimes_added,
553
+						$datetimes_removed
554
+					);
555
+					// are there any registrations using this ticket ?
556
+					$tickets_sold = $ticket->count_related(
557
+						'Registration',
558
+						[
559
+							[
560
+								'STS_ID' => ['NOT IN', [EEM_Registration::status_id_incomplete]],
561
+							],
562
+						]
563
+					);
564
+					// set ticket formats
565
+					$ticket->set_date_format($this->_date_format_strings['date']);
566
+					$ticket->set_time_format($this->_date_format_strings['time']);
567
+					// let's just check the total price for the existing ticket
568
+					// and determine if it matches the new total price.
569
+					// if they are different then we create a new ticket (if tickets sold)
570
+					// if they aren't different then we go ahead and modify existing ticket.
571
+					$create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
572
+					// set new values
573
+					foreach ($TKT_values as $field => $value) {
574
+						if ($field === 'TKT_qty') {
575
+							$ticket->set_qty($value);
576
+						} else {
577
+							$ticket->set($field, $value);
578
+						}
579
+					}
580
+					// if $create_new_TKT is false then we can safely update the existing ticket.
581
+					// Otherwise we have to create a new ticket.
582
+					if ($create_new_TKT) {
583
+						$new_ticket = $this->_duplicate_ticket(
584
+							$ticket,
585
+							$price_rows,
586
+							$ticket_price,
587
+							$base_price,
588
+							$base_price_id
589
+						);
590
+					}
591
+				}
592
+			} else {
593
+				// no TKT_id so a new TKT
594
+				$ticket = EE_Ticket::new_instance(
595
+					$TKT_values,
596
+					$timezone,
597
+					[$this->_date_format_strings['date'], $this->_date_format_strings['time']]
598
+				);
599
+				if ($ticket instanceof EE_Ticket) {
600
+					// make sure ticket has an ID of setting relations won't work
601
+					$ticket->save();
602
+					$ticket        = $this->_update_ticket_datetimes(
603
+						$ticket,
604
+						$saved_datetimes,
605
+						$datetimes_added,
606
+						$datetimes_removed
607
+					);
608
+					$update_prices = true;
609
+				}
610
+			}
611
+			// make sure any current values have been saved.
612
+			// $ticket->save();
613
+			// before going any further make sure our dates are setup correctly
614
+			// so that the end date is always equal or greater than the start date.
615
+			if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
616
+				$ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
617
+				$ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
618
+			}
619
+			// let's make sure the base price is handled
620
+			$ticket = ! $create_new_TKT
621
+				? $this->_add_prices_to_ticket(
622
+					[],
623
+					$ticket,
624
+					$update_prices,
625
+					$base_price,
626
+					$base_price_id
627
+				)
628
+				: $ticket;
629
+			// add/update price_modifiers
630
+			$ticket = ! $create_new_TKT
631
+				? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
632
+				: $ticket;
633
+			// need to make sue that the TKT_price is accurate after saving the prices.
634
+			$ticket->ensure_TKT_Price_correct();
635
+			// handle CREATING a default ticket from the incoming ticket but ONLY if this isn't an autosave.
636
+			if (! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
637
+				$new_default = clone $ticket;
638
+				$new_default->set('TKT_ID', 0);
639
+				$new_default->set('TKT_is_default', 1);
640
+				$new_default->set('TKT_row', 1);
641
+				$new_default->set('TKT_price', $ticket_price);
642
+				// remove any datetime relations cause we DON'T want datetime relations attached
643
+				// (note this is just removing the cached relations in the object)
644
+				$new_default->_remove_relations('Datetime');
645
+				// @todo we need to add the current attached prices as new prices to the new default ticket.
646
+				$new_default = $this->_add_prices_to_ticket(
647
+					$price_rows,
648
+					$new_default,
649
+					true
650
+				);
651
+				// don't forget the base price!
652
+				$new_default = $this->_add_prices_to_ticket(
653
+					[],
654
+					$new_default,
655
+					true,
656
+					$base_price,
657
+					$base_price_id
658
+				);
659
+				$new_default->save();
660
+				do_action(
661
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
662
+					$new_default,
663
+					$row,
664
+					$ticket,
665
+					$data
666
+				);
667
+			}
668
+			// DO ALL datetime relationships for both current tickets and any archived tickets
669
+			// for the given datetime that are related to the current ticket.
670
+			// TODO... not sure exactly how we're going to do this considering we don't know
671
+			// what current ticket the archived tickets are related to
672
+			// (and TKT_parent is used for autosaves so that's not a field we can reliably use).
673
+			// let's assign any tickets that have been setup to the saved_tickets tracker
674
+			// save existing TKT
675
+			$ticket->save();
676
+			if ($create_new_TKT && $new_ticket instanceof EE_Ticket) {
677
+				// save new TKT
678
+				$new_ticket->save();
679
+				// add new ticket to array
680
+				$saved_tickets[ $new_ticket->ID() ] = $new_ticket;
681
+				do_action(
682
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
683
+					$new_ticket,
684
+					$row,
685
+					$ticket_data,
686
+					$data
687
+				);
688
+			} else {
689
+				// add ticket to saved tickets
690
+				$saved_tickets[ $ticket->ID() ] = $ticket;
691
+				do_action(
692
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
693
+					$ticket,
694
+					$row,
695
+					$ticket_data,
696
+					$data
697
+				);
698
+			}
699
+		}
700
+		// now we need to handle tickets actually "deleted permanently".
701
+		// There are cases where we'd want this to happen
702
+		// (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
703
+		// Or a draft event was saved and in the process of editing a ticket is trashed.
704
+		// No sense in keeping all the related data in the db!
705
+		$old_tickets     = isset($old_tickets[0]) && $old_tickets[0] === '' ? [] : $old_tickets;
706
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
707
+		foreach ($tickets_removed as $id) {
708
+			$id = absint($id);
709
+			// get the ticket for this id
710
+			$ticket_to_remove = $ticket_model->get_one_by_ID($id);
711
+			// if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
712
+			if ($ticket_to_remove->get('TKT_is_default')) {
713
+				continue;
714
+			}
715
+			// if this ticket has any registrations attached so then we just ARCHIVE
716
+			// because we don't actually permanently delete these tickets.
717
+			if ($ticket_to_remove->count_related('Registration') > 0) {
718
+				$ticket_to_remove->delete();
719
+				continue;
720
+			}
721
+			// need to get all the related datetimes on this ticket and remove from every single one of them
722
+			// (remember this process can ONLY kick off if there are NO tickets_sold)
723
+			$datetimes = $ticket_to_remove->get_many_related('Datetime');
724
+			foreach ($datetimes as $datetime) {
725
+				$ticket_to_remove->_remove_relation_to($datetime, 'Datetime');
726
+			}
727
+			// need to do the same for prices (except these prices can also be deleted because again,
728
+			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
729
+			$ticket_to_remove->delete_related('Price');
730
+			do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $ticket_to_remove);
731
+			// finally let's delete this ticket
732
+			// (which should not be blocked at this point b/c we've removed all our relationships)
733
+			$ticket_to_remove->delete_or_restore();
734
+		}
735
+		return $saved_tickets;
736
+	}
737
+
738
+
739
+	/**
740
+	 * @access  protected
741
+	 * @param EE_Ticket     $ticket
742
+	 * @param EE_Datetime[] $saved_datetimes
743
+	 * @param int[]         $added_datetimes
744
+	 * @param int[]         $removed_datetimes
745
+	 * @return EE_Ticket
746
+	 * @throws EE_Error
747
+	 * @throws ReflectionException
748
+	 */
749
+	protected function _update_ticket_datetimes(
750
+		EE_Ticket $ticket,
751
+		array $saved_datetimes = [],
752
+		array $added_datetimes = [],
753
+		array $removed_datetimes = []
754
+	): EE_Ticket {
755
+		// to start we have to add the ticket to all the datetimes its supposed to be with,
756
+		// and removing the ticket from datetimes it got removed from.
757
+		// first let's add datetimes
758
+		if (! empty($added_datetimes) && is_array($added_datetimes)) {
759
+			foreach ($added_datetimes as $row_id) {
760
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
761
+					$ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
762
+					// Is this an existing ticket (has an ID) and does it have any sold?
763
+					// If so, then we need to add that to the DTT sold because this DTT is getting added.
764
+					if ($ticket->ID() && $ticket->sold() > 0) {
765
+						$saved_datetimes[ $row_id ]->increaseSold($ticket->sold(), false);
766
+					}
767
+				}
768
+			}
769
+		}
770
+		// then remove datetimes
771
+		if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
772
+			foreach ($removed_datetimes as $row_id) {
773
+				// its entirely possible that a datetime got deleted (instead of just removed from relationship.
774
+				// So make sure we skip over this if the datetime isn't in the $saved_datetimes array)
775
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
+					$ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
777
+				}
778
+			}
779
+		}
780
+		// cap ticket qty by datetime reg limits
781
+		$ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
782
+		return $ticket;
783
+	}
784
+
785
+
786
+	/**
787
+	 * @access  protected
788
+	 * @param EE_Ticket $ticket
789
+	 * @param array     $price_rows
790
+	 * @param int|float $ticket_price
791
+	 * @param int|float $base_price
792
+	 * @param int       $base_price_id
793
+	 * @return EE_Ticket
794
+	 * @throws ReflectionException
795
+	 * @throws InvalidArgumentException
796
+	 * @throws InvalidInterfaceException
797
+	 * @throws InvalidDataTypeException
798
+	 * @throws EE_Error
799
+	 */
800
+	protected function _duplicate_ticket(
801
+		EE_Ticket $ticket,
802
+		array $price_rows = [],
803
+		$ticket_price = 0,
804
+		$base_price = 0,
805
+		int $base_price_id = 0
806
+	): EE_Ticket {
807
+		// create new ticket that's a copy of the existing
808
+		// except a new id of course (and not archived)
809
+		// AND has the new TKT_price associated with it.
810
+		$new_ticket = clone $ticket;
811
+		$new_ticket->set('TKT_ID', 0);
812
+		$new_ticket->set_deleted(0);
813
+		$new_ticket->set_price($ticket_price);
814
+		$new_ticket->set_sold(0);
815
+		// let's get a new ID for this ticket
816
+		$new_ticket->save();
817
+		// we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
818
+		$datetimes_on_existing = $ticket->datetimes();
819
+		$new_ticket            = $this->_update_ticket_datetimes(
820
+			$new_ticket,
821
+			$datetimes_on_existing,
822
+			array_keys($datetimes_on_existing)
823
+		);
824
+		// $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
825
+		// if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
826
+		// available.
827
+		if ($ticket->sold() > 0) {
828
+			$new_qty = $ticket->qty() - $ticket->sold();
829
+			$new_ticket->set_qty($new_qty);
830
+		}
831
+		// now we update the prices just for this ticket
832
+		$new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
833
+		// and we update the base price
834
+		return $this->_add_prices_to_ticket(
835
+			[],
836
+			$new_ticket,
837
+			true,
838
+			$base_price,
839
+			$base_price_id
840
+		);
841
+	}
842
+
843
+
844
+	/**
845
+	 * This attaches a list of given prices to a ticket.
846
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
847
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
848
+	 * price info and prices are automatically "archived" via the ticket.
849
+	 *
850
+	 * @access  private
851
+	 * @param array     $prices        Array of prices from the form.
852
+	 * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
853
+	 * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
854
+	 * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
855
+	 * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
856
+	 * @return EE_Ticket
857
+	 * @throws ReflectionException
858
+	 * @throws InvalidArgumentException
859
+	 * @throws InvalidInterfaceException
860
+	 * @throws InvalidDataTypeException
861
+	 * @throws EE_Error
862
+	 */
863
+	protected function _add_prices_to_ticket(
864
+		array $prices,
865
+		EE_Ticket $ticket,
866
+		bool $new_prices = false,
867
+		$base_price = false,
868
+		$base_price_id = false
869
+	): EE_Ticket {
870
+		$price_model = EEM_Price::instance();
871
+		// let's just get any current prices that may exist on the given ticket
872
+		// so we can remove any prices that got trashed in this session.
873
+		$current_prices_on_ticket = $base_price !== false
874
+			? $ticket->base_price(true)
875
+			: $ticket->price_modifiers();
876
+		$updated_prices           = [];
877
+		// if $base_price ! FALSE then updating a base price.
878
+		if ($base_price !== false) {
879
+			$prices[1] = [
880
+				'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
881
+				'PRT_ID'     => 1,
882
+				'PRC_amount' => $base_price,
883
+				'PRC_name'   => $ticket->get('TKT_name'),
884
+				'PRC_desc'   => $ticket->get('TKT_description'),
885
+			];
886
+		}
887
+		// possibly need to save ticket
888
+		if (! $ticket->ID()) {
889
+			$ticket->save();
890
+		}
891
+		foreach ($prices as $row => $prc) {
892
+			$prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
893
+			if (empty($prt_id)) {
894
+				continue;
895
+			} //prices MUST have a price type id.
896
+			$PRC_values = [
897
+				'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
898
+				'PRT_ID'         => $prt_id,
899
+				'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
900
+				'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
901
+				'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
902
+				'PRC_is_default' => false,
903
+				// make sure we set PRC_is_default to false for all ticket saves from event_editor
904
+				'PRC_order'      => $row,
905
+			];
906
+			if ($new_prices || empty($PRC_values['PRC_ID'])) {
907
+				$PRC_values['PRC_ID'] = 0;
908
+				$price                = EE_Registry::instance()->load_class(
909
+					'Price',
910
+					[$PRC_values],
911
+					false,
912
+					false
913
+				);
914
+			} else {
915
+				$price = $price_model->get_one_by_ID($prc['PRC_ID']);
916
+				// update this price with new values
917
+				foreach ($PRC_values as $field => $value) {
918
+					$price->set($field, $value);
919
+				}
920
+			}
921
+			$price->save();
922
+			$updated_prices[ $price->ID() ] = $price;
923
+			$ticket->_add_relation_to($price, 'Price');
924
+		}
925
+		// now let's remove any prices that got removed from the ticket
926
+		if (! empty($current_prices_on_ticket)) {
927
+			$current          = array_keys($current_prices_on_ticket);
928
+			$updated          = array_keys($updated_prices);
929
+			$prices_to_remove = array_diff($current, $updated);
930
+			if (! empty($prices_to_remove)) {
931
+				foreach ($prices_to_remove as $prc_id) {
932
+					$p = $current_prices_on_ticket[ $prc_id ];
933
+					$ticket->_remove_relation_to($p, 'Price');
934
+					// delete permanently the price
935
+					$p->delete_or_restore();
936
+				}
937
+			}
938
+		}
939
+		return $ticket;
940
+	}
941
+
942
+
943
+	/**
944
+	 * @throws ReflectionException
945
+	 * @throws InvalidArgumentException
946
+	 * @throws InvalidInterfaceException
947
+	 * @throws InvalidDataTypeException
948
+	 * @throws DomainException
949
+	 * @throws EE_Error
950
+	 */
951
+	public function pricing_metabox()
952
+	{
953
+		$event          = $this->_adminpage_obj->get_cpt_model_obj();
954
+		$timezone       = $event instanceof EE_Event ? $event->timezone_string() : null;
955
+		$price_model    = EEM_Price::instance($timezone);
956
+		$ticket_model   = EEM_Ticket::instance($timezone);
957
+		$datetime_model = EEM_Datetime::instance($timezone);
958
+		$all_tickets    = [];
959
+
960
+		// set is_creating_event property.
961
+		$EVT_ID                   = $event->ID();
962
+		$this->_is_creating_event = empty($this->_req_data['post']);
963
+		$existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = [];
964
+
965
+		// default main template args
966
+		$main_template_args = [
967
+			'event_datetime_help_link' => EEH_Template::get_help_tab_link(
968
+				'event_editor_event_datetimes_help_tab',
969
+				$this->_adminpage_obj->page_slug,
970
+				$this->_adminpage_obj->get_req_action()
971
+			),
972
+
973
+			// todo need to add a filter to the template for the help text
974
+			// in the Events_Admin_Page core file so we can add further help
975
+			'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
976
+				'add_new_dtt_info',
977
+				$this->_adminpage_obj->page_slug,
978
+				$this->_adminpage_obj->get_req_action()
979
+			),
980
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
981
+			'datetime_rows'            => '',
982
+			'show_tickets_container'   => '',
983
+			'ticket_rows'              => '',
984
+			'ee_collapsible_status'    => ' ee-collapsible-open'
985
+			// $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
986
+		];
987
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
988
+
989
+		/**
990
+		 * 1. Start with retrieving Datetimes
991
+		 * 2. For each datetime get related tickets
992
+		 * 3. For each ticket get related prices
993
+		 */
994
+		$datetimes                            = $datetime_model->get_all_event_dates($EVT_ID);
995
+		$main_template_args['total_dtt_rows'] = count($datetimes);
996
+
997
+		/**
998
+		 * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
999
+		 * for why we are counting $datetime_row and then setting that on the Datetime object
1000
+		 */
1001
+		$datetime_row = 1;
1002
+		foreach ($datetimes as $datetime) {
1003
+			$DTT_ID = $datetime->get('DTT_ID');
1004
+			$datetime->set('DTT_order', $datetime_row);
1005
+			$existing_datetime_ids[] = $DTT_ID;
1006
+			// tickets attached
1007
+			$related_tickets = $datetime->ID() > 0
1008
+				? $datetime->get_many_related(
1009
+					'Ticket',
1010
+					[
1011
+						[
1012
+							'OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0],
1013
+						],
1014
+						'default_where_conditions' => 'none',
1015
+						'order_by'                 => ['TKT_order' => 'ASC'],
1016
+					]
1017
+				)
1018
+				: [];
1019
+			// if there are no related tickets this is likely a new event OR auto-draft
1020
+			// event so we need to generate the default tickets because datetimes
1021
+			// ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1022
+			// datetime on the event.
1023
+			if (empty($related_tickets) && count($datetimes) < 2) {
1024
+				$related_tickets = $ticket_model->get_all_default_tickets();
1025
+				// this should be ordered by TKT_ID, so let's grab the first default ticket
1026
+				// (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1027
+				$default_prices      = $price_model->get_all_default_prices();
1028
+				$main_default_ticket = reset($related_tickets);
1029
+				if ($main_default_ticket instanceof EE_Ticket) {
1030
+					foreach ($default_prices as $default_price) {
1031
+						if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1032
+							continue;
1033
+						}
1034
+						$main_default_ticket->cache('Price', $default_price);
1035
+					}
1036
+				}
1037
+			}
1038
+			// we can't actually setup rows in this loop yet cause we don't know all
1039
+			// the unique tickets for this event yet (tickets are linked through all datetimes).
1040
+			// So we're going to temporarily cache some of that information.
1041
+			// loop through and setup the ticket rows and make sure the order is set.
1042
+			foreach ($related_tickets as $ticket) {
1043
+				$TKT_ID     = $ticket->get('TKT_ID');
1044
+				$ticket_row = $ticket->get('TKT_row');
1045
+				// we only want unique tickets in our final display!!
1046
+				if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1047
+					$existing_ticket_ids[] = $TKT_ID;
1048
+					$all_tickets[]         = $ticket;
1049
+				}
1050
+				// temporary cache of this ticket info for this datetime for later processing of datetime rows.
1051
+				$datetime_tickets[ $DTT_ID ][] = $ticket_row;
1052
+				// temporary cache of this datetime info for this ticket for later processing of ticket rows.
1053
+				if (
1054
+					! isset($ticket_datetimes[ $TKT_ID ])
1055
+					|| ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1056
+				) {
1057
+					$ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1058
+				}
1059
+			}
1060
+			$datetime_row++;
1061
+		}
1062
+		$main_template_args['total_ticket_rows']     = count($existing_ticket_ids);
1063
+		$main_template_args['existing_ticket_ids']   = implode(',', $existing_ticket_ids);
1064
+		$main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1065
+		// sort $all_tickets by order
1066
+		usort(
1067
+			$all_tickets,
1068
+			function (EE_Ticket $a, EE_Ticket $b) {
1069
+				$a_order = (int) $a->get('TKT_order');
1070
+				$b_order = (int) $b->get('TKT_order');
1071
+				if ($a_order === $b_order) {
1072
+					return 0;
1073
+				}
1074
+				return ($a_order < $b_order) ? -1 : 1;
1075
+			}
1076
+		);
1077
+		// k NOW we have all the data we need for setting up the datetime rows
1078
+		// and ticket rows so we start our datetime loop again.
1079
+		$datetime_row = 1;
1080
+		foreach ($datetimes as $datetime) {
1081
+			$main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1082
+				$datetime_row,
1083
+				$datetime,
1084
+				$datetime_tickets,
1085
+				$all_tickets,
1086
+				false,
1087
+				$datetimes
1088
+			);
1089
+			$datetime_row++;
1090
+		}
1091
+		// then loop through all tickets for the ticket rows.
1092
+		$ticket_row = 1;
1093
+		foreach ($all_tickets as $ticket) {
1094
+			$main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1095
+				$ticket_row,
1096
+				$ticket,
1097
+				$ticket_datetimes,
1098
+				$datetimes,
1099
+				false,
1100
+				$all_tickets
1101
+			);
1102
+			$ticket_row++;
1103
+		}
1104
+		$main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1105
+
1106
+		$status_change_notice = LoaderFactory::getLoader()->getShared(
1107
+			'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
1108
+		);
1109
+
1110
+		$main_template_args['status_change_notice'] = $status_change_notice->display(
1111
+			'__event-editor',
1112
+			'espresso-events'
1113
+		);
1114
+
1115
+		EEH_Template::display_template(
1116
+			PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1117
+			$main_template_args
1118
+		);
1119
+	}
1120
+
1121
+
1122
+	/**
1123
+	 * @param int|string  $datetime_row
1124
+	 * @param EE_Datetime $datetime
1125
+	 * @param array       $datetime_tickets
1126
+	 * @param array       $all_tickets
1127
+	 * @param bool        $default
1128
+	 * @param array       $all_datetimes
1129
+	 * @return string
1130
+	 * @throws DomainException
1131
+	 * @throws EE_Error
1132
+	 * @throws ReflectionException
1133
+	 */
1134
+	protected function _get_datetime_row(
1135
+		$datetime_row,
1136
+		EE_Datetime $datetime,
1137
+		array $datetime_tickets = [],
1138
+		array $all_tickets = [],
1139
+		bool $default = false,
1140
+		array $all_datetimes = []
1141
+	): string {
1142
+		return EEH_Template::display_template(
1143
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1144
+			[
1145
+				'dtt_edit_row'             => $this->_get_dtt_edit_row(
1146
+					$datetime_row,
1147
+					$datetime,
1148
+					$default,
1149
+					$all_datetimes
1150
+				),
1151
+				'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1152
+					$datetime_row,
1153
+					$datetime,
1154
+					$datetime_tickets,
1155
+					$all_tickets,
1156
+					$default
1157
+				),
1158
+				'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1159
+			],
1160
+			true
1161
+		);
1162
+	}
1163
+
1164
+
1165
+	/**
1166
+	 * This method is used to generate a datetime fields  edit row.
1167
+	 * The same row is used to generate a row with valid DTT objects
1168
+	 * and the default row that is used as the skeleton by the js.
1169
+	 *
1170
+	 * @param int|string       $datetime_row  The row number for the row being generated.
1171
+	 * @param EE_Datetime|null $datetime
1172
+	 * @param bool             $default       Whether a default row is being generated or not.
1173
+	 * @param EE_Datetime[]    $all_datetimes This is the array of all datetimes used in the editor.
1174
+	 * @return string
1175
+	 * @throws EE_Error
1176
+	 * @throws ReflectionException
1177
+	 */
1178
+	protected function _get_dtt_edit_row(
1179
+		$datetime_row,
1180
+		?EE_Datetime $datetime,
1181
+		bool $default,
1182
+		array $all_datetimes
1183
+	): string {
1184
+		// if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1185
+		$default                     = ! $datetime instanceof EE_Datetime ? true : $default;
1186
+		$template_args               = [
1187
+			'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1188
+			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1189
+			'edit_dtt_expanded'    => '',
1190
+			'DTT_ID'               => $default ? '' : $datetime->ID(),
1191
+			'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1192
+			'DTT_description'      => $default ? '' : $datetime->get_raw('DTT_description'),
1193
+			'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1194
+			'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1195
+			'DTT_reg_limit'        => $default
1196
+				? ''
1197
+				: $datetime->get_pretty(
1198
+					'DTT_reg_limit',
1199
+					'input'
1200
+				),
1201
+			'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1202
+			'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1203
+			'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1204
+			'can_clone'            => $datetime instanceof EE_Datetime && $datetime->get('DTT_sold'),
1205
+			'can_trash'            => count($all_datetimes) > 1
1206
+									  && $datetime instanceof EE_Datetime
1207
+									  && ! $datetime->get('DTT_sold'),
1208
+			'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1209
+				? 'trash-entity dashicons dashicons-lock'
1210
+				: 'trash-entity dashicons dashicons-post-trash clickable',
1211
+			'reg_list_url'         => $default || ! $datetime->event() instanceof EE_Event
1212
+				? ''
1213
+				: EE_Admin_Page::add_query_args_and_nonce(
1214
+					[
1215
+						'event_id'    => $datetime->event()->ID(),
1216
+						'datetime_id' => $datetime->ID(),
1217
+						'use_filters' => true,
1218
+					],
1219
+					REG_ADMIN_URL
1220
+				),
1221
+		];
1222
+		$template_args['show_trash'] = count($all_datetimes) === 1
1223
+									   && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1224
+			? 'display:none'
1225
+			: '';
1226
+		// allow filtering of template args at this point.
1227
+		$template_args = apply_filters(
1228
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1229
+			$template_args,
1230
+			$datetime_row,
1231
+			$datetime,
1232
+			$default,
1233
+			$all_datetimes,
1234
+			$this->_is_creating_event
1235
+		);
1236
+		return EEH_Template::display_template(
1237
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1238
+			$template_args,
1239
+			true
1240
+		);
1241
+	}
1242
+
1243
+
1244
+	/**
1245
+	 * @param int|string       $datetime_row
1246
+	 * @param EE_Datetime|null $datetime
1247
+	 * @param array            $datetime_tickets
1248
+	 * @param array            $all_tickets
1249
+	 * @param bool             $default
1250
+	 * @return string
1251
+	 * @throws DomainException
1252
+	 * @throws EE_Error
1253
+	 * @throws ReflectionException
1254
+	 */
1255
+	protected function _get_dtt_attached_tickets_row(
1256
+		$datetime_row,
1257
+		?EE_Datetime $datetime,
1258
+		array $datetime_tickets = [],
1259
+		array $all_tickets = [],
1260
+		bool $default = false
1261
+	): string {
1262
+		$template_args = [
1263
+			'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1264
+			'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1265
+			'DTT_description'                   => $default ? '' : $datetime->get_raw('DTT_description'),
1266
+			'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1267
+			'show_tickets_row'                  => 'display:none;',
1268
+			'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1269
+				'add_new_ticket_via_datetime',
1270
+				$this->_adminpage_obj->page_slug,
1271
+				$this->_adminpage_obj->get_req_action()
1272
+			),
1273
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1274
+			'DTT_ID'                            => $default ? '' : $datetime->ID(),
1275
+		];
1276
+		// need to setup the list items (but only if this isn't a default skeleton setup)
1277
+		if (! $default) {
1278
+			$ticket_row = 1;
1279
+			foreach ($all_tickets as $ticket) {
1280
+				$template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1281
+					$datetime_row,
1282
+					$ticket_row,
1283
+					$datetime,
1284
+					$ticket,
1285
+					$datetime_tickets
1286
+				);
1287
+				$ticket_row++;
1288
+			}
1289
+		}
1290
+		// filter template args at this point
1291
+		$template_args = apply_filters(
1292
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1293
+			$template_args,
1294
+			$datetime_row,
1295
+			$datetime,
1296
+			$datetime_tickets,
1297
+			$all_tickets,
1298
+			$default,
1299
+			$this->_is_creating_event
1300
+		);
1301
+		return EEH_Template::display_template(
1302
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1303
+			$template_args,
1304
+			true
1305
+		);
1306
+	}
1307
+
1308
+
1309
+	/**
1310
+	 * @param int|string       $datetime_row
1311
+	 * @param int|string       $ticket_row
1312
+	 * @param EE_Datetime|null $datetime
1313
+	 * @param EE_Ticket|null   $ticket
1314
+	 * @param array            $datetime_tickets
1315
+	 * @param bool             $default
1316
+	 * @return string
1317
+	 * @throws EE_Error
1318
+	 * @throws ReflectionException
1319
+	 */
1320
+	protected function _get_datetime_tickets_list_item(
1321
+		$datetime_row,
1322
+		$ticket_row,
1323
+		?EE_Datetime $datetime,
1324
+		?EE_Ticket $ticket,
1325
+		array $datetime_tickets = [],
1326
+		bool $default = false
1327
+	): string {
1328
+		$datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1329
+			? $datetime_tickets[ $datetime->ID() ]
1330
+			: [];
1331
+		$display_row      = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1332
+		$no_ticket        = $default && empty($ticket);
1333
+		$template_args    = [
1334
+			'dtt_row'                 => $default
1335
+				? 'DTTNUM'
1336
+				: $datetime_row,
1337
+			'tkt_row'                 => $no_ticket
1338
+				? 'TICKETNUM'
1339
+				: $ticket_row,
1340
+			'datetime_ticket_checked' => in_array($display_row, $datetime_tickets, true)
1341
+				? ' checked'
1342
+				: '',
1343
+			'ticket_selected'         => in_array($display_row, $datetime_tickets, true)
1344
+				? ' ticket-selected'
1345
+				: '',
1346
+			'TKT_name'                => $no_ticket
1347
+				? 'TKTNAME'
1348
+				: $ticket->get('TKT_name'),
1349
+			'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1350
+				? ' tkt-status-' . EE_Ticket::onsale
1351
+				: ' tkt-status-' . $ticket->ticket_status(),
1352
+		];
1353
+		// filter template args
1354
+		$template_args = apply_filters(
1355
+			'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1356
+			$template_args,
1357
+			$datetime_row,
1358
+			$ticket_row,
1359
+			$datetime,
1360
+			$ticket,
1361
+			$datetime_tickets,
1362
+			$default,
1363
+			$this->_is_creating_event
1364
+		);
1365
+		return EEH_Template::display_template(
1366
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1367
+			$template_args,
1368
+			true
1369
+		);
1370
+	}
1371
+
1372
+
1373
+	/**
1374
+	 * This generates the ticket row for tickets.
1375
+	 * This same method is used to generate both the actual rows and the js skeleton row
1376
+	 * (when default === true)
1377
+	 *
1378
+	 * @param int|string     $ticket_row       Represents the row number being generated.
1379
+	 * @param EE_Ticket|null $ticket
1380
+	 * @param EE_Datetime[]  $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1381
+	 *                                         or empty for default
1382
+	 * @param EE_Datetime[]  $all_datetimes    All Datetimes on the event or empty for default.
1383
+	 * @param bool           $default          Whether default row being generated or not.
1384
+	 * @param EE_Ticket[]    $all_tickets      This is an array of all tickets attached to the event
1385
+	 *                                         (or empty in the case of defaults)
1386
+	 * @return string
1387
+	 * @throws InvalidArgumentException
1388
+	 * @throws InvalidInterfaceException
1389
+	 * @throws InvalidDataTypeException
1390
+	 * @throws DomainException
1391
+	 * @throws EE_Error
1392
+	 * @throws ReflectionException
1393
+	 */
1394
+	protected function _get_ticket_row(
1395
+		$ticket_row,
1396
+		?EE_Ticket $ticket,
1397
+		array $ticket_datetimes,
1398
+		array $all_datetimes,
1399
+		bool $default = false,
1400
+		array $all_tickets = []
1401
+	): string {
1402
+		// if $ticket is not an instance of EE_Ticket then force default to true.
1403
+		$default = ! $ticket instanceof EE_Ticket ? true : $default;
1404
+		$prices  = ! empty($ticket) && ! $default
1405
+			? $ticket->get_many_related(
1406
+				'Price',
1407
+				['default_where_conditions' => 'none', 'order_by' => ['PRC_order' => 'ASC']]
1408
+			)
1409
+			: [];
1410
+		// if there is only one price (which would be the base price)
1411
+		// or NO prices and this ticket is a default ticket,
1412
+		// let's just make sure there are no cached default prices on the object.
1413
+		// This is done by not including any query_params.
1414
+		if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1415
+			$prices = $ticket->prices();
1416
+		}
1417
+		// check if we're dealing with a default ticket in which case
1418
+		// we don't want any starting_ticket_datetime_row values set
1419
+		// (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1420
+		// This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1421
+		$default_datetime = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1422
+		$tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1423
+			? $ticket_datetimes[ $ticket->ID() ]
1424
+			: [];
1425
+		$ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1426
+		$base_price       = $default ? null : $ticket->base_price();
1427
+		$count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1428
+		// breaking out complicated condition for ticket_status
1429
+		if ($default) {
1430
+			$ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1431
+		} else {
1432
+			$ticket_status_class = $ticket->is_default()
1433
+				? ' tkt-status-' . EE_Ticket::onsale
1434
+				: ' tkt-status-' . $ticket->ticket_status();
1435
+		}
1436
+		// breaking out complicated condition for TKT_taxable
1437
+		if ($default) {
1438
+			$TKT_taxable = '';
1439
+		} else {
1440
+			$TKT_taxable = $ticket->taxable()
1441
+				? 'checked'
1442
+				: '';
1443
+		}
1444
+		if ($default) {
1445
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1446
+		} elseif ($ticket->is_default()) {
1447
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1448
+		} else {
1449
+			$TKT_status = $ticket->ticket_status(true);
1450
+		}
1451
+		if ($default) {
1452
+			$TKT_min = '';
1453
+		} else {
1454
+			$TKT_min = $ticket->min();
1455
+			if ($TKT_min === -1 || $TKT_min === 0) {
1456
+				$TKT_min = '';
1457
+			}
1458
+		}
1459
+		$template_args                 = [
1460
+			'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1461
+			'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1462
+			// on initial page load this will always be the correct order.
1463
+			'tkt_status_class'              => $ticket_status_class,
1464
+			'display_edit_tkt_row'          => 'display:none;',
1465
+			'edit_tkt_expanded'             => '',
1466
+			'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1467
+			'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1468
+			'TKT_start_date'                => $default
1469
+				? ''
1470
+				: $ticket->get_date('TKT_start_date', $this->_date_time_format),
1471
+			'TKT_end_date'                  => $default
1472
+				? ''
1473
+				: $ticket->get_date('TKT_end_date', $this->_date_time_format),
1474
+			'TKT_status'                    => $TKT_status,
1475
+			'TKT_price'                     => $default
1476
+				? ''
1477
+				: EEH_Template::format_currency(
1478
+					$ticket->get_ticket_total_with_taxes(),
1479
+					false,
1480
+					false
1481
+				),
1482
+			'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1483
+			'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1484
+			'TKT_qty'                       => $default
1485
+				? ''
1486
+				: $ticket->get_pretty('TKT_qty', 'symbol'),
1487
+			'TKT_qty_for_input'             => $default
1488
+				? ''
1489
+				: $ticket->get_pretty('TKT_qty', 'input'),
1490
+			'TKT_uses'                      => $default
1491
+				? ''
1492
+				: $ticket->get_pretty('TKT_uses', 'input'),
1493
+			'TKT_min'                       => $TKT_min,
1494
+			'TKT_max'                       => $default
1495
+				? ''
1496
+				: $ticket->get_pretty('TKT_max', 'input'),
1497
+			'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold(),
1498
+			'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1499
+			'TKT_registrations'             => $default
1500
+				? 0
1501
+				: $ticket->count_registrations(
1502
+					[
1503
+						[
1504
+							'STS_ID' => [
1505
+								'!=',
1506
+								EEM_Registration::status_id_incomplete,
1507
+							],
1508
+						],
1509
+					]
1510
+				),
1511
+			'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1512
+			'TKT_description'               => $default ? '' : $ticket->get_raw('TKT_description'),
1513
+			'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1514
+			'TKT_required'                  => $default ? 0 : $ticket->required(),
1515
+			'TKT_is_default_selector'       => '',
1516
+			'ticket_price_rows'             => '',
1517
+			'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1518
+				? ''
1519
+				: $base_price->get_pretty('PRC_amount', 'localized_float'),
1520
+			'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1521
+			'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1522
+				? ''
1523
+				: 'display:none;',
1524
+			'show_price_mod_button'         => count($prices) > 1
1525
+											   || ($default && $count_price_mods > 0)
1526
+											   || (! $default && $ticket->deleted())
1527
+				? 'display:none;'
1528
+				: '',
1529
+			'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1530
+			'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1531
+			'starting_ticket_datetime_rows' => $default || $default_datetime ? '' : implode(',', $tkt_datetimes),
1532
+			'ticket_datetime_rows'          => $default ? '' : implode(',', $tkt_datetimes),
1533
+			'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1534
+			'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1535
+			'TKT_taxable'                   => $TKT_taxable,
1536
+			'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1537
+				? ''
1538
+				: 'display:none;',
1539
+			'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1540
+			'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1541
+				$ticket_subtotal,
1542
+				false,
1543
+				false
1544
+			),
1545
+			'TKT_subtotal_amount'           => $ticket_subtotal,
1546
+			'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1547
+			'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1548
+			'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1549
+				? ' ticket-archived'
1550
+				: '',
1551
+			'trash_icon'                    => $ticket instanceof EE_Ticket
1552
+											   && $ticket->deleted()
1553
+											   && ! $ticket->is_permanently_deleteable()
1554
+				? 'dashicons dashicons-lock '
1555
+				: 'trash-entity dashicons dashicons-post-trash clickable',
1556
+
1557
+			'can_clone'            => $ticket instanceof EE_Ticket && ! $ticket->deleted(),
1558
+			'can_trash'            => $ticket instanceof EE_Ticket
1559
+									  && (! $ticket->deleted() && $ticket->is_permanently_deleteable()),
1560
+		];
1561
+		$template_args['trash_hidden'] = count($all_tickets) === 1
1562
+										 && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1563
+			? 'display:none'
1564
+			: '';
1565
+		// handle rows that should NOT be empty
1566
+		if (empty($template_args['TKT_start_date'])) {
1567
+			// if empty then the start date will be now.
1568
+			$template_args['TKT_start_date']   = date(
1569
+				$this->_date_time_format,
1570
+				current_time('timestamp')
1571
+			);
1572
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1573
+		}
1574
+		if (empty($template_args['TKT_end_date'])) {
1575
+			// get the earliest datetime (if present);
1576
+			$earliest_datetime = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1577
+				? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1578
+					'Datetime',
1579
+					['order_by' => ['DTT_EVT_start' => 'ASC']]
1580
+				)
1581
+				: null;
1582
+			if (! empty($earliest_datetime)) {
1583
+				$template_args['TKT_end_date'] = $earliest_datetime->get_datetime(
1584
+					'DTT_EVT_start',
1585
+					$this->_date_time_format
1586
+				);
1587
+			} else {
1588
+				// default so let's just use what's been set for the default date-time which is 30 days from now.
1589
+				$template_args['TKT_end_date'] = date(
1590
+					$this->_date_time_format,
1591
+					mktime(
1592
+						24,
1593
+						0,
1594
+						0,
1595
+						date('m'),
1596
+						date('d') + 29,
1597
+						date('Y')
1598
+					)
1599
+				);
1600
+			}
1601
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1602
+		}
1603
+		// generate ticket_datetime items
1604
+		if (! $default) {
1605
+			$datetime_row = 1;
1606
+			foreach ($all_datetimes as $datetime) {
1607
+				$template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1608
+					$datetime_row,
1609
+					$ticket_row,
1610
+					$datetime,
1611
+					$ticket,
1612
+					$ticket_datetimes,
1613
+					$default
1614
+				);
1615
+				$datetime_row++;
1616
+			}
1617
+		}
1618
+		$price_row = 1;
1619
+		foreach ($prices as $price) {
1620
+			if (! $price instanceof EE_Price) {
1621
+				continue;
1622
+			}
1623
+			if ($price->is_base_price()) {
1624
+				$price_row++;
1625
+				continue;
1626
+			}
1627
+
1628
+			$show_trash  = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1629
+			$show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1630
+
1631
+			$template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1632
+				$ticket_row,
1633
+				$price_row,
1634
+				$price,
1635
+				$default,
1636
+				$ticket,
1637
+				$show_trash,
1638
+				$show_create
1639
+			);
1640
+			$price_row++;
1641
+		}
1642
+		// filter $template_args
1643
+		$template_args = apply_filters(
1644
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1645
+			$template_args,
1646
+			$ticket_row,
1647
+			$ticket,
1648
+			$ticket_datetimes,
1649
+			$all_datetimes,
1650
+			$default,
1651
+			$all_tickets,
1652
+			$this->_is_creating_event
1653
+		);
1654
+		return EEH_Template::display_template(
1655
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1656
+			$template_args,
1657
+			true
1658
+		);
1659
+	}
1660
+
1661
+
1662
+	/**
1663
+	 * @param int|string     $ticket_row
1664
+	 * @param EE_Ticket|null $ticket
1665
+	 * @return string
1666
+	 * @throws DomainException
1667
+	 * @throws EE_Error
1668
+	 * @throws ReflectionException
1669
+	 */
1670
+	protected function _get_tax_rows($ticket_row, ?EE_Ticket $ticket): string
1671
+	{
1672
+		$tax_rows = '';
1673
+		/** @var EE_Price[] $taxes */
1674
+		$taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1675
+		foreach ($taxes as $tax) {
1676
+			$tax_added     = $this->_get_tax_added($tax, $ticket);
1677
+			$template_args = [
1678
+				'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1679
+					? ''
1680
+					: 'display:none;',
1681
+				'tax_id'            => $tax->ID(),
1682
+				'tkt_row'           => $ticket_row,
1683
+				'tax_label'         => $tax->get('PRC_name'),
1684
+				'tax_added'         => $tax_added,
1685
+				'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1686
+				'tax_amount'        => $tax->get('PRC_amount'),
1687
+			];
1688
+			$template_args = apply_filters(
1689
+				'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1690
+				$template_args,
1691
+				$ticket_row,
1692
+				$ticket,
1693
+				$this->_is_creating_event
1694
+			);
1695
+			$tax_rows      .= EEH_Template::display_template(
1696
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1697
+				$template_args,
1698
+				true
1699
+			);
1700
+		}
1701
+		return $tax_rows;
1702
+	}
1703
+
1704
+
1705
+	/**
1706
+	 * @param EE_Price       $tax
1707
+	 * @param EE_Ticket|null $ticket
1708
+	 * @return float|int
1709
+	 * @throws EE_Error
1710
+	 * @throws ReflectionException
1711
+	 */
1712
+	protected function _get_tax_added(EE_Price $tax, ?EE_Ticket $ticket)
1713
+	{
1714
+		$subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1715
+		return $subtotal * $tax->get('PRC_amount') / 100;
1716
+	}
1717
+
1718
+
1719
+	/**
1720
+	 * @param int|string     $ticket_row
1721
+	 * @param int|string     $price_row
1722
+	 * @param EE_Price|null  $price
1723
+	 * @param bool           $default
1724
+	 * @param EE_Ticket|null $ticket
1725
+	 * @param bool           $show_trash
1726
+	 * @param bool           $show_create
1727
+	 * @return string
1728
+	 * @throws InvalidArgumentException
1729
+	 * @throws InvalidInterfaceException
1730
+	 * @throws InvalidDataTypeException
1731
+	 * @throws DomainException
1732
+	 * @throws EE_Error
1733
+	 * @throws ReflectionException
1734
+	 */
1735
+	protected function _get_ticket_price_row(
1736
+		$ticket_row,
1737
+		$price_row,
1738
+		?EE_Price $price,
1739
+		bool $default,
1740
+		?EE_Ticket $ticket,
1741
+		bool $show_trash = true,
1742
+		bool $show_create = true
1743
+	): string {
1744
+		$send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1745
+		$template_args = [
1746
+			'tkt_row'               => $default && empty($ticket)
1747
+				? 'TICKETNUM'
1748
+				: $ticket_row,
1749
+			'PRC_order'             => $default && empty($price)
1750
+				? 'PRICENUM'
1751
+				: $price_row,
1752
+			'edit_prices_name'      => $default && empty($price)
1753
+				? 'PRICENAMEATTR'
1754
+				: 'edit_prices',
1755
+			'price_type_selector'   => $default && empty($price)
1756
+				? $this->_get_base_price_template($ticket_row, $price_row, $price, true)
1757
+				: $this->_get_price_type_selector(
1758
+					$ticket_row,
1759
+					$price_row,
1760
+					$price,
1761
+					$default,
1762
+					$send_disabled
1763
+				),
1764
+			'PRC_ID'                => $default && empty($price)
1765
+				? 0
1766
+				: $price->ID(),
1767
+			'PRC_is_default'        => $default && empty($price)
1768
+				? 0
1769
+				: $price->get('PRC_is_default'),
1770
+			'PRC_name'              => $default && empty($price)
1771
+				? ''
1772
+				: $price->get('PRC_name'),
1773
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1774
+			'show_plus_or_minus'    => $default && empty($price)
1775
+				? ''
1776
+				: 'display:none;',
1777
+			'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1778
+				? 'display:none;'
1779
+				: '',
1780
+			'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1781
+				? 'display:none;'
1782
+				: '',
1783
+			'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1784
+				? 'display:none'
1785
+				: '',
1786
+			'PRC_amount'            => $default && empty($price)
1787
+				? 0
1788
+				: $price->get_pretty('PRC_amount', 'localized_float'),
1789
+			'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1790
+				? 'display:none;'
1791
+				: '',
1792
+			'show_trash_icon'       => $show_trash
1793
+				? ''
1794
+				: ' style="display:none;"',
1795
+			'show_create_button'    => $show_create
1796
+				? ''
1797
+				: ' style="display:none;"',
1798
+			'PRC_desc'              => $default && empty($price)
1799
+				? ''
1800
+				: $price->get('PRC_desc'),
1801
+			'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1802
+		];
1803
+		$template_args = apply_filters(
1804
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1805
+			$template_args,
1806
+			$ticket_row,
1807
+			$price_row,
1808
+			$price,
1809
+			$default,
1810
+			$ticket,
1811
+			$show_trash,
1812
+			$show_create,
1813
+			$this->_is_creating_event
1814
+		);
1815
+		return EEH_Template::display_template(
1816
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1817
+			$template_args,
1818
+			true
1819
+		);
1820
+	}
1821
+
1822
+
1823
+	/**
1824
+	 * @param int|string    $ticket_row
1825
+	 * @param int|string    $price_row
1826
+	 * @param EE_Price|null $price
1827
+	 * @param bool          $default
1828
+	 * @param bool          $disabled
1829
+	 * @return string
1830
+	 * @throws ReflectionException
1831
+	 * @throws InvalidArgumentException
1832
+	 * @throws InvalidInterfaceException
1833
+	 * @throws InvalidDataTypeException
1834
+	 * @throws DomainException
1835
+	 * @throws EE_Error
1836
+	 */
1837
+	protected function _get_price_type_selector(
1838
+		$ticket_row,
1839
+		$price_row,
1840
+		?EE_Price $price,
1841
+		bool $default,
1842
+		bool $disabled = false
1843
+	): string {
1844
+		if ($price->is_base_price()) {
1845
+			return $this->_get_base_price_template(
1846
+				$ticket_row,
1847
+				$price_row,
1848
+				$price,
1849
+				$default
1850
+			);
1851
+		}
1852
+		return $this->_get_price_modifier_template(
1853
+			$ticket_row,
1854
+			$price_row,
1855
+			$price,
1856
+			$default,
1857
+			$disabled
1858
+		);
1859
+	}
1860
+
1861
+
1862
+	/**
1863
+	 * @param int|string    $ticket_row
1864
+	 * @param int|string    $price_row
1865
+	 * @param EE_Price|null $price
1866
+	 * @param bool          $default
1867
+	 * @return string
1868
+	 * @throws DomainException
1869
+	 * @throws EE_Error
1870
+	 * @throws ReflectionException
1871
+	 */
1872
+	protected function _get_base_price_template(
1873
+		$ticket_row,
1874
+		$price_row,
1875
+		?EE_Price $price,
1876
+		bool $default
1877
+	): string {
1878
+		$template_args = [
1879
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1880
+			'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1881
+			'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1882
+			'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1883
+			'price_selected_operator'   => '+',
1884
+			'price_selected_is_percent' => 0,
1885
+		];
1886
+		$template_args = apply_filters(
1887
+			'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1888
+			$template_args,
1889
+			$ticket_row,
1890
+			$price_row,
1891
+			$price,
1892
+			$default,
1893
+			$this->_is_creating_event
1894
+		);
1895
+		return EEH_Template::display_template(
1896
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1897
+			$template_args,
1898
+			true
1899
+		);
1900
+	}
1901
+
1902
+
1903
+	/**
1904
+	 * @param int|string    $ticket_row
1905
+	 * @param int|string    $price_row
1906
+	 * @param EE_Price|null $price
1907
+	 * @param bool          $default
1908
+	 * @param bool          $disabled
1909
+	 * @return string
1910
+	 * @throws ReflectionException
1911
+	 * @throws InvalidArgumentException
1912
+	 * @throws InvalidInterfaceException
1913
+	 * @throws InvalidDataTypeException
1914
+	 * @throws DomainException
1915
+	 * @throws EE_Error
1916
+	 */
1917
+	protected function _get_price_modifier_template(
1918
+		$ticket_row,
1919
+		$price_row,
1920
+		?EE_Price $price,
1921
+		bool $default,
1922
+		bool $disabled = false
1923
+	): string {
1924
+		$select_name = $default && ! $price instanceof EE_Price
1925
+			? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1926
+			: 'edit_prices[' . esc_attr($ticket_row) . '][' . esc_attr($price_row) . '][PRT_ID]';
1927
+
1928
+		$price_type_model       = EEM_Price_Type::instance();
1929
+		$price_types            = $price_type_model->get_all(
1930
+			[
1931
+				[
1932
+					'OR' => [
1933
+						'PBT_ID'  => '2',
1934
+						'PBT_ID*' => '3',
1935
+					],
1936
+				],
1937
+			]
1938
+		);
1939
+		$all_price_types        = $default && ! $price instanceof EE_Price
1940
+			? [esc_html__('Select Modifier', 'event_espresso')]
1941
+			: [];
1942
+		$selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1943
+		$price_option_spans     = '';
1944
+		// setup price types for selector
1945
+		foreach ($price_types as $price_type) {
1946
+			if (! $price_type instanceof EE_Price_Type) {
1947
+				continue;
1948
+			}
1949
+			$all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1950
+			// while we're in the loop let's setup the option spans used by js
1951
+			$span_args          = [
1952
+				'PRT_ID'         => $price_type->ID(),
1953
+				'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1954
+				'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1955
+			];
1956
+			$price_option_spans .= EEH_Template::display_template(
1957
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1958
+				$span_args,
1959
+				true
1960
+			);
1961
+		}
1962
+
1963
+		$select_name = $disabled
1964
+			? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1965
+			: $select_name;
1966
+
1967
+		$select_input = new EE_Select_Input(
1968
+			$all_price_types,
1969
+			[
1970
+				'default'               => $selected_price_type_id,
1971
+				'html_name'             => $select_name,
1972
+				'html_class'            => 'edit-price-PRT_ID',
1973
+				'other_html_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1974
+			]
1975
+		);
1976
+
1977
+		$price_selected_operator   = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1978
+		$price_selected_operator   = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1979
+		$price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1980
+		$price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1981
+		$template_args             = [
1982
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1983
+			'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1984
+			'price_modifier_selector'   => $select_input->get_html_for_input(),
1985
+			'main_name'                 => $select_name,
1986
+			'selected_price_type_id'    => $selected_price_type_id,
1987
+			'price_option_spans'        => $price_option_spans,
1988
+			'price_selected_operator'   => $price_selected_operator,
1989
+			'price_selected_is_percent' => $price_selected_is_percent,
1990
+			'disabled'                  => $disabled,
1991
+		];
1992
+		$template_args             = apply_filters(
1993
+			'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1994
+			$template_args,
1995
+			$ticket_row,
1996
+			$price_row,
1997
+			$price,
1998
+			$default,
1999
+			$disabled,
2000
+			$this->_is_creating_event
2001
+		);
2002
+		return EEH_Template::display_template(
2003
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2004
+			$template_args,
2005
+			true
2006
+		);
2007
+	}
2008
+
2009
+
2010
+	/**
2011
+	 * @param int|string       $datetime_row
2012
+	 * @param int|string       $ticket_row
2013
+	 * @param EE_Datetime|null $datetime
2014
+	 * @param EE_Ticket|null   $ticket
2015
+	 * @param array            $ticket_datetimes
2016
+	 * @param bool             $default
2017
+	 * @return string
2018
+	 * @throws DomainException
2019
+	 * @throws EE_Error
2020
+	 * @throws ReflectionException
2021
+	 */
2022
+	protected function _get_ticket_datetime_list_item(
2023
+		$datetime_row,
2024
+		$ticket_row,
2025
+		?EE_Datetime $datetime,
2026
+		?EE_Ticket $ticket,
2027
+		array $ticket_datetimes = [],
2028
+		bool $default = false
2029
+	): string {
2030
+		$tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2031
+			? $ticket_datetimes[ $ticket->ID() ]
2032
+			: array();
2033
+		$template_args    = [
2034
+			'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2035
+				? 'DTTNUM'
2036
+				: $datetime_row,
2037
+			'tkt_row'                  => $default
2038
+				? 'TICKETNUM'
2039
+				: $ticket_row,
2040
+			'ticket_datetime_selected' => in_array($datetime_row, $tkt_datetimes, true)
2041
+				? ' ticket-selected'
2042
+				: '',
2043
+			'ticket_datetime_checked'  => in_array($datetime_row, $tkt_datetimes, true)
2044
+				? ' checked'
2045
+				: '',
2046
+			'DTT_name'                 => $default && empty($datetime)
2047
+				? 'DTTNAME'
2048
+				: $datetime->get_dtt_display_name(true),
2049
+			'tkt_status_class'         => '',
2050
+		];
2051
+		$template_args    = apply_filters(
2052
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2053
+			$template_args,
2054
+			$datetime_row,
2055
+			$ticket_row,
2056
+			$datetime,
2057
+			$ticket,
2058
+			$ticket_datetimes,
2059
+			$default,
2060
+			$this->_is_creating_event
2061
+		);
2062
+		return EEH_Template::display_template(
2063
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2064
+			$template_args,
2065
+			true
2066
+		);
2067
+	}
2068
+
2069
+
2070
+	/**
2071
+	 * @param array $all_datetimes
2072
+	 * @param array $all_tickets
2073
+	 * @return string
2074
+	 * @throws ReflectionException
2075
+	 * @throws InvalidArgumentException
2076
+	 * @throws InvalidInterfaceException
2077
+	 * @throws InvalidDataTypeException
2078
+	 * @throws DomainException
2079
+	 * @throws EE_Error
2080
+	 */
2081
+	protected function _get_ticket_js_structure(array $all_datetimes = [], array $all_tickets = []): string
2082
+	{
2083
+		$template_args = [
2084
+			'default_datetime_edit_row' => $this->_get_dtt_edit_row(
2085
+				'DTTNUM',
2086
+				null,
2087
+				true,
2088
+				$all_datetimes
2089
+			),
2090
+			'default_ticket_row'        => $this->_get_ticket_row(
2091
+				'TICKETNUM',
2092
+				null,
2093
+				[],
2094
+				[],
2095
+				true
2096
+			),
2097
+			'default_price_row'         => $this->_get_ticket_price_row(
2098
+				'TICKETNUM',
2099
+				'PRICENUM',
2100
+				null,
2101
+				true,
2102
+				null
2103
+			),
2104
+
2105
+			'default_price_rows'                       => '',
2106
+			'default_base_price_amount'                => 0,
2107
+			'default_base_price_name'                  => '',
2108
+			'default_base_price_description'           => '',
2109
+			'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2110
+				'TICKETNUM',
2111
+				'PRICENUM',
2112
+				null,
2113
+				true
2114
+			),
2115
+			'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2116
+				'DTTNUM',
2117
+				null,
2118
+				[],
2119
+				[],
2120
+				true
2121
+			),
2122
+			'existing_available_datetime_tickets_list' => '',
2123
+			'existing_available_ticket_datetimes_list' => '',
2124
+			'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2125
+				'DTTNUM',
2126
+				'TICKETNUM',
2127
+				null,
2128
+				null,
2129
+				[],
2130
+				true
2131
+			),
2132
+			'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2133
+				'DTTNUM',
2134
+				'TICKETNUM',
2135
+				null,
2136
+				null,
2137
+				[],
2138
+				true
2139
+			),
2140
+		];
2141
+		$ticket_row    = 1;
2142
+		foreach ($all_tickets as $ticket) {
2143
+			$template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2144
+				'DTTNUM',
2145
+				$ticket_row,
2146
+				null,
2147
+				$ticket,
2148
+				[],
2149
+				true
2150
+			);
2151
+			$ticket_row++;
2152
+		}
2153
+		$datetime_row = 1;
2154
+		foreach ($all_datetimes as $datetime) {
2155
+			$template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2156
+				$datetime_row,
2157
+				'TICKETNUM',
2158
+				$datetime,
2159
+				null,
2160
+				[],
2161
+				true
2162
+			);
2163
+			$datetime_row++;
2164
+		}
2165
+		$price_model    = EEM_Price::instance();
2166
+		$default_prices = $price_model->get_all_default_prices();
2167
+		$price_row      = 1;
2168
+		foreach ($default_prices as $price) {
2169
+			if (! $price instanceof EE_Price) {
2170
+				continue;
2171
+			}
2172
+			if ($price->is_base_price()) {
2173
+				$template_args['default_base_price_amount']      = $price->get_pretty(
2174
+					'PRC_amount',
2175
+					'localized_float'
2176
+				);
2177
+				$template_args['default_base_price_name']        = $price->get('PRC_name');
2178
+				$template_args['default_base_price_description'] = $price->get('PRC_desc');
2179
+				$price_row++;
2180
+				continue;
2181
+			}
2182
+
2183
+			$show_trash  = ! ((count($default_prices) > 1 && $price_row === 1) || count($default_prices) === 1);
2184
+			$show_create = ! (count($default_prices) > 1 && count($default_prices) !== $price_row);
2185
+
2186
+			$template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2187
+				'TICKETNUM',
2188
+				$price_row,
2189
+				$price,
2190
+				true,
2191
+				null,
2192
+				$show_trash,
2193
+				$show_create
2194
+			);
2195
+			$price_row++;
2196
+		}
2197
+		$template_args = apply_filters(
2198
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2199
+			$template_args,
2200
+			$all_datetimes,
2201
+			$all_tickets,
2202
+			$this->_is_creating_event
2203
+		);
2204
+		return EEH_Template::display_template(
2205
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2206
+			$template_args,
2207
+			true
2208
+		);
2209
+	}
2210 2210
 }
Please login to merge, or discard this patch.
Spacing   +89 added lines, -89 removed lines patch added patch discarded remove patch
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
     protected function _setup_metaboxes()
77 77
     {
78 78
         // if we were going to add our own metaboxes we'd use the below.
79
-        $this->_metaboxes        = [
79
+        $this->_metaboxes = [
80 80
             0 => [
81 81
                 'page_route' => ['edit', 'create_new'],
82 82
                 'func'       => [$this, 'pricing_metabox'],
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
         $this->_date_format_strings['date'] = $this->_date_format_strings['date'] ?? '';
120 120
         $this->_date_format_strings['time'] = $this->_date_format_strings['time'] ?? '';
121 121
 
122
-        $this->_date_time_format = $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'];
122
+        $this->_date_time_format = $this->_date_format_strings['date'].' '.$this->_date_format_strings['time'];
123 123
     }
124 124
 
125 125
 
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
             );
144 144
             $msg .= '</p><ul>';
145 145
             foreach ($format_validation as $error) {
146
-                $msg .= '<li>' . $error . '</li>';
146
+                $msg .= '<li>'.$error.'</li>';
147 147
             }
148 148
             $msg .= '</ul><p>';
149 149
             $msg .= sprintf(
@@ -172,11 +172,11 @@  discard block
 block discarded – undo
172 172
         $this->_scripts_styles = [
173 173
             'registers'   => [
174 174
                 'ee-tickets-datetimes-css' => [
175
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
175
+                    'url'  => PRICING_ASSETS_URL.'event-tickets-datetimes.css',
176 176
                     'type' => 'css',
177 177
                 ],
178 178
                 'ee-dtt-ticket-metabox'    => [
179
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
179
+                    'url'     => PRICING_ASSETS_URL.'ee-datetime-ticket-metabox.js',
180 180
                     'depends' => ['ee-datepicker', 'ee-dialog', 'underscore'],
181 181
                 ],
182 182
             ],
@@ -201,11 +201,11 @@  discard block
 block discarded – undo
201 201
                         ),
202 202
                         'cancel_button'           => '
203 203
                             <button class="button--secondary ee-modal-cancel">
204
-                                ' . esc_html__('Cancel', 'event_espresso') . '
204
+                                ' . esc_html__('Cancel', 'event_espresso').'
205 205
                             </button>',
206 206
                         'close_button'            => '
207 207
                             <button class="button--secondary ee-modal-cancel">
208
-                                ' . esc_html__('Close', 'event_espresso') . '
208
+                                ' . esc_html__('Close', 'event_espresso').'
209 209
                             </button>',
210 210
                         'single_warning_from_tkt' => esc_html__(
211 211
                             'The Datetime you are attempting to unassign from this ticket is the only remaining datetime for this ticket. Tickets must always have at least one datetime assigned to them.',
@@ -217,7 +217,7 @@  discard block
 block discarded – undo
217 217
                         ),
218 218
                         'dismiss_button'          => '
219 219
                             <button class="button--secondary ee-modal-cancel">
220
-                                ' . esc_html__('Dismiss', 'event_espresso') . '
220
+                                ' . esc_html__('Dismiss', 'event_espresso').'
221 221
                             </button>',
222 222
                     ],
223 223
                     'DTT_ERROR_MSG'         => [
@@ -225,7 +225,7 @@  discard block
 block discarded – undo
225 225
                         'dismiss_button' => '
226 226
                             <div class="save-cancel-button-container">
227 227
                                 <button class="button--secondary ee-modal-cancel">
228
-                                    ' . esc_html__('Dismiss', 'event_espresso') . '
228
+                                    ' . esc_html__('Dismiss', 'event_espresso').'
229 229
                                 </button>
230 230
                             </div>',
231 231
                     ],
@@ -314,7 +314,7 @@  discard block
 block discarded – undo
314 314
         foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315 315
             // trim all values to ensure any excess whitespace is removed.
316 316
             $datetime_data = array_map(
317
-                function ($datetime_data) {
317
+                function($datetime_data) {
318 318
                     return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319 319
                 },
320 320
                 $datetime_data
@@ -324,7 +324,7 @@  discard block
 block discarded – undo
324 324
                                             && ! empty($datetime_data['DTT_EVT_end'])
325 325
                 ? $datetime_data['DTT_EVT_end']
326 326
                 : $datetime_data['DTT_EVT_start'];
327
-            $datetime_values              = [
327
+            $datetime_values = [
328 328
                 'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
329 329
                     ? $datetime_data['DTT_ID']
330 330
                     : null,
@@ -346,7 +346,7 @@  discard block
 block discarded – undo
346 346
 
347 347
             // if we have an id then let's get existing object first and then set the new values.
348 348
             // Otherwise we instantiate a new object for save.
349
-            if (! empty($datetime_data['DTT_ID'])) {
349
+            if ( ! empty($datetime_data['DTT_ID'])) {
350 350
                 $datetime = EE_Registry::instance()
351 351
                                        ->load_model('Datetime', [$timezone])
352 352
                                        ->get_one_by_ID($datetime_data['DTT_ID']);
@@ -361,7 +361,7 @@  discard block
 block discarded – undo
361 361
                 // after the add_relation_to() the autosave replaces it.
362 362
                 // We need to do this so we dont' TRASH the parent DTT.
363 363
                 // (save the ID for both key and value to avoid duplications)
364
-                $saved_datetime_ids[ $datetime->ID() ] = $datetime->ID();
364
+                $saved_datetime_ids[$datetime->ID()] = $datetime->ID();
365 365
             } else {
366 366
                 $datetime = EE_Datetime::new_instance(
367 367
                     $datetime_values,
@@ -392,8 +392,8 @@  discard block
 block discarded – undo
392 392
             // because it is possible there was a new one created for the autosave.
393 393
             // (save the ID for both key and value to avoid duplications)
394 394
             $DTT_ID                        = $datetime->ID();
395
-            $saved_datetime_ids[ $DTT_ID ] = $DTT_ID;
396
-            $saved_datetime_objs[ $row ]   = $datetime;
395
+            $saved_datetime_ids[$DTT_ID] = $DTT_ID;
396
+            $saved_datetime_objs[$row]   = $datetime;
397 397
             // @todo if ANY of these updates fail then we want the appropriate global error message.
398 398
         }
399 399
         $event->save();
@@ -459,13 +459,13 @@  discard block
 block discarded – undo
459 459
             $update_prices = $create_new_TKT = false;
460 460
             // figure out what datetimes were added to the ticket
461 461
             // and what datetimes were removed from the ticket in the session.
462
-            $starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
463
-            $ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
462
+            $starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][$row]);
463
+            $ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][$row]);
464 464
             $datetimes_added               = array_diff($ticket_datetime_rows, $starting_ticket_datetime_rows);
465 465
             $datetimes_removed             = array_diff($starting_ticket_datetime_rows, $ticket_datetime_rows);
466 466
             // trim inputs to ensure any excess whitespace is removed.
467 467
             $ticket_data = array_map(
468
-                function ($ticket_data) {
468
+                function($ticket_data) {
469 469
                     return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
470 470
                 },
471 471
                 $ticket_data
@@ -485,8 +485,8 @@  discard block
 block discarded – undo
485 485
                 ? $base_price
486 486
                 : $ticket_price;
487 487
             $base_price_id = $ticket_data['TKT_base_price_ID'] ?? 0;
488
-            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
489
-                ? $data['edit_prices'][ $row ]
488
+            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][$row])
489
+                ? $data['edit_prices'][$row]
490 490
                 : [];
491 491
             $now           = null;
492 492
             if (empty($ticket_data['TKT_start_date'])) {
@@ -498,7 +498,7 @@  discard block
 block discarded – undo
498 498
                 /**
499 499
                  * set the TKT_end_date to the first datetime attached to the ticket.
500 500
                  */
501
-                $first_datetime              = $saved_datetimes[ reset($ticket_datetime_rows) ];
501
+                $first_datetime              = $saved_datetimes[reset($ticket_datetime_rows)];
502 502
                 $ticket_data['TKT_end_date'] = $first_datetime->start_date_and_time($this->_date_time_format);
503 503
             }
504 504
             $TKT_values = [
@@ -599,7 +599,7 @@  discard block
 block discarded – undo
599 599
                 if ($ticket instanceof EE_Ticket) {
600 600
                     // make sure ticket has an ID of setting relations won't work
601 601
                     $ticket->save();
602
-                    $ticket        = $this->_update_ticket_datetimes(
602
+                    $ticket = $this->_update_ticket_datetimes(
603 603
                         $ticket,
604 604
                         $saved_datetimes,
605 605
                         $datetimes_added,
@@ -633,7 +633,7 @@  discard block
 block discarded – undo
633 633
             // need to make sue that the TKT_price is accurate after saving the prices.
634 634
             $ticket->ensure_TKT_Price_correct();
635 635
             // handle CREATING a default ticket from the incoming ticket but ONLY if this isn't an autosave.
636
-            if (! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
636
+            if ( ! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
637 637
                 $new_default = clone $ticket;
638 638
                 $new_default->set('TKT_ID', 0);
639 639
                 $new_default->set('TKT_is_default', 1);
@@ -677,7 +677,7 @@  discard block
 block discarded – undo
677 677
                 // save new TKT
678 678
                 $new_ticket->save();
679 679
                 // add new ticket to array
680
-                $saved_tickets[ $new_ticket->ID() ] = $new_ticket;
680
+                $saved_tickets[$new_ticket->ID()] = $new_ticket;
681 681
                 do_action(
682 682
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
683 683
                     $new_ticket,
@@ -687,7 +687,7 @@  discard block
 block discarded – undo
687 687
                 );
688 688
             } else {
689 689
                 // add ticket to saved tickets
690
-                $saved_tickets[ $ticket->ID() ] = $ticket;
690
+                $saved_tickets[$ticket->ID()] = $ticket;
691 691
                 do_action(
692 692
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
693 693
                     $ticket,
@@ -755,25 +755,25 @@  discard block
 block discarded – undo
755 755
         // to start we have to add the ticket to all the datetimes its supposed to be with,
756 756
         // and removing the ticket from datetimes it got removed from.
757 757
         // first let's add datetimes
758
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
758
+        if ( ! empty($added_datetimes) && is_array($added_datetimes)) {
759 759
             foreach ($added_datetimes as $row_id) {
760
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
761
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
760
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
761
+                    $ticket->_add_relation_to($saved_datetimes[$row_id], 'Datetime');
762 762
                     // Is this an existing ticket (has an ID) and does it have any sold?
763 763
                     // If so, then we need to add that to the DTT sold because this DTT is getting added.
764 764
                     if ($ticket->ID() && $ticket->sold() > 0) {
765
-                        $saved_datetimes[ $row_id ]->increaseSold($ticket->sold(), false);
765
+                        $saved_datetimes[$row_id]->increaseSold($ticket->sold(), false);
766 766
                     }
767 767
                 }
768 768
             }
769 769
         }
770 770
         // then remove datetimes
771
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
771
+        if ( ! empty($removed_datetimes) && is_array($removed_datetimes)) {
772 772
             foreach ($removed_datetimes as $row_id) {
773 773
                 // its entirely possible that a datetime got deleted (instead of just removed from relationship.
774 774
                 // So make sure we skip over this if the datetime isn't in the $saved_datetimes array)
775
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
775
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
776
+                    $ticket->_remove_relation_to($saved_datetimes[$row_id], 'Datetime');
777 777
                 }
778 778
             }
779 779
         }
@@ -885,7 +885,7 @@  discard block
 block discarded – undo
885 885
             ];
886 886
         }
887 887
         // possibly need to save ticket
888
-        if (! $ticket->ID()) {
888
+        if ( ! $ticket->ID()) {
889 889
             $ticket->save();
890 890
         }
891 891
         foreach ($prices as $row => $prc) {
@@ -919,17 +919,17 @@  discard block
 block discarded – undo
919 919
                 }
920 920
             }
921 921
             $price->save();
922
-            $updated_prices[ $price->ID() ] = $price;
922
+            $updated_prices[$price->ID()] = $price;
923 923
             $ticket->_add_relation_to($price, 'Price');
924 924
         }
925 925
         // now let's remove any prices that got removed from the ticket
926
-        if (! empty($current_prices_on_ticket)) {
926
+        if ( ! empty($current_prices_on_ticket)) {
927 927
             $current          = array_keys($current_prices_on_ticket);
928 928
             $updated          = array_keys($updated_prices);
929 929
             $prices_to_remove = array_diff($current, $updated);
930
-            if (! empty($prices_to_remove)) {
930
+            if ( ! empty($prices_to_remove)) {
931 931
                 foreach ($prices_to_remove as $prc_id) {
932
-                    $p = $current_prices_on_ticket[ $prc_id ];
932
+                    $p = $current_prices_on_ticket[$prc_id];
933 933
                     $ticket->_remove_relation_to($p, 'Price');
934 934
                     // delete permanently the price
935 935
                     $p->delete_or_restore();
@@ -1043,18 +1043,18 @@  discard block
 block discarded – undo
1043 1043
                 $TKT_ID     = $ticket->get('TKT_ID');
1044 1044
                 $ticket_row = $ticket->get('TKT_row');
1045 1045
                 // we only want unique tickets in our final display!!
1046
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1046
+                if ( ! in_array($TKT_ID, $existing_ticket_ids, true)) {
1047 1047
                     $existing_ticket_ids[] = $TKT_ID;
1048 1048
                     $all_tickets[]         = $ticket;
1049 1049
                 }
1050 1050
                 // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1051
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1051
+                $datetime_tickets[$DTT_ID][] = $ticket_row;
1052 1052
                 // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1053 1053
                 if (
1054
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1055
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1054
+                    ! isset($ticket_datetimes[$TKT_ID])
1055
+                    || ! in_array($datetime_row, $ticket_datetimes[$TKT_ID], true)
1056 1056
                 ) {
1057
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1057
+                    $ticket_datetimes[$TKT_ID][] = $datetime_row;
1058 1058
                 }
1059 1059
             }
1060 1060
             $datetime_row++;
@@ -1065,7 +1065,7 @@  discard block
 block discarded – undo
1065 1065
         // sort $all_tickets by order
1066 1066
         usort(
1067 1067
             $all_tickets,
1068
-            function (EE_Ticket $a, EE_Ticket $b) {
1068
+            function(EE_Ticket $a, EE_Ticket $b) {
1069 1069
                 $a_order = (int) $a->get('TKT_order');
1070 1070
                 $b_order = (int) $b->get('TKT_order');
1071 1071
                 if ($a_order === $b_order) {
@@ -1113,7 +1113,7 @@  discard block
 block discarded – undo
1113 1113
         );
1114 1114
 
1115 1115
         EEH_Template::display_template(
1116
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1116
+            PRICING_TEMPLATE_PATH.'event_tickets_metabox_main.template.php',
1117 1117
             $main_template_args
1118 1118
         );
1119 1119
     }
@@ -1140,7 +1140,7 @@  discard block
 block discarded – undo
1140 1140
         array $all_datetimes = []
1141 1141
     ): string {
1142 1142
         return EEH_Template::display_template(
1143
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1143
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_row_wrapper.template.php',
1144 1144
             [
1145 1145
                 'dtt_edit_row'             => $this->_get_dtt_edit_row(
1146 1146
                     $datetime_row,
@@ -1234,7 +1234,7 @@  discard block
 block discarded – undo
1234 1234
             $this->_is_creating_event
1235 1235
         );
1236 1236
         return EEH_Template::display_template(
1237
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1237
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_edit_row.template.php',
1238 1238
             $template_args,
1239 1239
             true
1240 1240
         );
@@ -1274,7 +1274,7 @@  discard block
 block discarded – undo
1274 1274
             'DTT_ID'                            => $default ? '' : $datetime->ID(),
1275 1275
         ];
1276 1276
         // need to setup the list items (but only if this isn't a default skeleton setup)
1277
-        if (! $default) {
1277
+        if ( ! $default) {
1278 1278
             $ticket_row = 1;
1279 1279
             foreach ($all_tickets as $ticket) {
1280 1280
                 $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
@@ -1299,7 +1299,7 @@  discard block
 block discarded – undo
1299 1299
             $this->_is_creating_event
1300 1300
         );
1301 1301
         return EEH_Template::display_template(
1302
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1302
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_attached_tickets_row.template.php',
1303 1303
             $template_args,
1304 1304
             true
1305 1305
         );
@@ -1325,8 +1325,8 @@  discard block
 block discarded – undo
1325 1325
         array $datetime_tickets = [],
1326 1326
         bool $default = false
1327 1327
     ): string {
1328
-        $datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1329
-            ? $datetime_tickets[ $datetime->ID() ]
1328
+        $datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[$datetime->ID()])
1329
+            ? $datetime_tickets[$datetime->ID()]
1330 1330
             : [];
1331 1331
         $display_row      = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1332 1332
         $no_ticket        = $default && empty($ticket);
@@ -1347,8 +1347,8 @@  discard block
 block discarded – undo
1347 1347
                 ? 'TKTNAME'
1348 1348
                 : $ticket->get('TKT_name'),
1349 1349
             'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1350
-                ? ' tkt-status-' . EE_Ticket::onsale
1351
-                : ' tkt-status-' . $ticket->ticket_status(),
1350
+                ? ' tkt-status-'.EE_Ticket::onsale
1351
+                : ' tkt-status-'.$ticket->ticket_status(),
1352 1352
         ];
1353 1353
         // filter template args
1354 1354
         $template_args = apply_filters(
@@ -1363,7 +1363,7 @@  discard block
 block discarded – undo
1363 1363
             $this->_is_creating_event
1364 1364
         );
1365 1365
         return EEH_Template::display_template(
1366
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1366
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_dtt_tickets_list.template.php',
1367 1367
             $template_args,
1368 1368
             true
1369 1369
         );
@@ -1419,19 +1419,19 @@  discard block
 block discarded – undo
1419 1419
         // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1420 1420
         // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1421 1421
         $default_datetime = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1422
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1423
-            ? $ticket_datetimes[ $ticket->ID() ]
1422
+        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
1423
+            ? $ticket_datetimes[$ticket->ID()]
1424 1424
             : [];
1425 1425
         $ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1426 1426
         $base_price       = $default ? null : $ticket->base_price();
1427 1427
         $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1428 1428
         // breaking out complicated condition for ticket_status
1429 1429
         if ($default) {
1430
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1430
+            $ticket_status_class = ' tkt-status-'.EE_Ticket::onsale;
1431 1431
         } else {
1432 1432
             $ticket_status_class = $ticket->is_default()
1433
-                ? ' tkt-status-' . EE_Ticket::onsale
1434
-                : ' tkt-status-' . $ticket->ticket_status();
1433
+                ? ' tkt-status-'.EE_Ticket::onsale
1434
+                : ' tkt-status-'.$ticket->ticket_status();
1435 1435
         }
1436 1436
         // breaking out complicated condition for TKT_taxable
1437 1437
         if ($default) {
@@ -1456,7 +1456,7 @@  discard block
 block discarded – undo
1456 1456
                 $TKT_min = '';
1457 1457
             }
1458 1458
         }
1459
-        $template_args                 = [
1459
+        $template_args = [
1460 1460
             'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1461 1461
             'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1462 1462
             // on initial page load this will always be the correct order.
@@ -1523,7 +1523,7 @@  discard block
 block discarded – undo
1523 1523
                 : 'display:none;',
1524 1524
             'show_price_mod_button'         => count($prices) > 1
1525 1525
                                                || ($default && $count_price_mods > 0)
1526
-                                               || (! $default && $ticket->deleted())
1526
+                                               || ( ! $default && $ticket->deleted())
1527 1527
                 ? 'display:none;'
1528 1528
                 : '',
1529 1529
             'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
@@ -1556,7 +1556,7 @@  discard block
 block discarded – undo
1556 1556
 
1557 1557
             'can_clone'            => $ticket instanceof EE_Ticket && ! $ticket->deleted(),
1558 1558
             'can_trash'            => $ticket instanceof EE_Ticket
1559
-                                      && (! $ticket->deleted() && $ticket->is_permanently_deleteable()),
1559
+                                      && ( ! $ticket->deleted() && $ticket->is_permanently_deleteable()),
1560 1560
         ];
1561 1561
         $template_args['trash_hidden'] = count($all_tickets) === 1
1562 1562
                                          && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
@@ -1565,11 +1565,11 @@  discard block
 block discarded – undo
1565 1565
         // handle rows that should NOT be empty
1566 1566
         if (empty($template_args['TKT_start_date'])) {
1567 1567
             // if empty then the start date will be now.
1568
-            $template_args['TKT_start_date']   = date(
1568
+            $template_args['TKT_start_date'] = date(
1569 1569
                 $this->_date_time_format,
1570 1570
                 current_time('timestamp')
1571 1571
             );
1572
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1572
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1573 1573
         }
1574 1574
         if (empty($template_args['TKT_end_date'])) {
1575 1575
             // get the earliest datetime (if present);
@@ -1579,7 +1579,7 @@  discard block
 block discarded – undo
1579 1579
                     ['order_by' => ['DTT_EVT_start' => 'ASC']]
1580 1580
                 )
1581 1581
                 : null;
1582
-            if (! empty($earliest_datetime)) {
1582
+            if ( ! empty($earliest_datetime)) {
1583 1583
                 $template_args['TKT_end_date'] = $earliest_datetime->get_datetime(
1584 1584
                     'DTT_EVT_start',
1585 1585
                     $this->_date_time_format
@@ -1598,10 +1598,10 @@  discard block
 block discarded – undo
1598 1598
                     )
1599 1599
                 );
1600 1600
             }
1601
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1601
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1602 1602
         }
1603 1603
         // generate ticket_datetime items
1604
-        if (! $default) {
1604
+        if ( ! $default) {
1605 1605
             $datetime_row = 1;
1606 1606
             foreach ($all_datetimes as $datetime) {
1607 1607
                 $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
@@ -1617,7 +1617,7 @@  discard block
 block discarded – undo
1617 1617
         }
1618 1618
         $price_row = 1;
1619 1619
         foreach ($prices as $price) {
1620
-            if (! $price instanceof EE_Price) {
1620
+            if ( ! $price instanceof EE_Price) {
1621 1621
                 continue;
1622 1622
             }
1623 1623
             if ($price->is_base_price()) {
@@ -1652,7 +1652,7 @@  discard block
 block discarded – undo
1652 1652
             $this->_is_creating_event
1653 1653
         );
1654 1654
         return EEH_Template::display_template(
1655
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1655
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_row.template.php',
1656 1656
             $template_args,
1657 1657
             true
1658 1658
         );
@@ -1692,8 +1692,8 @@  discard block
 block discarded – undo
1692 1692
                 $ticket,
1693 1693
                 $this->_is_creating_event
1694 1694
             );
1695
-            $tax_rows      .= EEH_Template::display_template(
1696
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1695
+            $tax_rows .= EEH_Template::display_template(
1696
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_tax_row.template.php',
1697 1697
                 $template_args,
1698 1698
                 true
1699 1699
             );
@@ -1813,7 +1813,7 @@  discard block
 block discarded – undo
1813 1813
             $this->_is_creating_event
1814 1814
         );
1815 1815
         return EEH_Template::display_template(
1816
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1816
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_price_row.template.php',
1817 1817
             $template_args,
1818 1818
             true
1819 1819
         );
@@ -1893,7 +1893,7 @@  discard block
 block discarded – undo
1893 1893
             $this->_is_creating_event
1894 1894
         );
1895 1895
         return EEH_Template::display_template(
1896
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1896
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_type_base.template.php',
1897 1897
             $template_args,
1898 1898
             true
1899 1899
         );
@@ -1923,7 +1923,7 @@  discard block
 block discarded – undo
1923 1923
     ): string {
1924 1924
         $select_name = $default && ! $price instanceof EE_Price
1925 1925
             ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1926
-            : 'edit_prices[' . esc_attr($ticket_row) . '][' . esc_attr($price_row) . '][PRT_ID]';
1926
+            : 'edit_prices['.esc_attr($ticket_row).']['.esc_attr($price_row).'][PRT_ID]';
1927 1927
 
1928 1928
         $price_type_model       = EEM_Price_Type::instance();
1929 1929
         $price_types            = $price_type_model->get_all(
@@ -1943,25 +1943,25 @@  discard block
 block discarded – undo
1943 1943
         $price_option_spans     = '';
1944 1944
         // setup price types for selector
1945 1945
         foreach ($price_types as $price_type) {
1946
-            if (! $price_type instanceof EE_Price_Type) {
1946
+            if ( ! $price_type instanceof EE_Price_Type) {
1947 1947
                 continue;
1948 1948
             }
1949
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1949
+            $all_price_types[$price_type->ID()] = $price_type->get('PRT_name');
1950 1950
             // while we're in the loop let's setup the option spans used by js
1951
-            $span_args          = [
1951
+            $span_args = [
1952 1952
                 'PRT_ID'         => $price_type->ID(),
1953 1953
                 'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1954 1954
                 'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1955 1955
             ];
1956 1956
             $price_option_spans .= EEH_Template::display_template(
1957
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1957
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_option_span.template.php',
1958 1958
                 $span_args,
1959 1959
                 true
1960 1960
             );
1961 1961
         }
1962 1962
 
1963 1963
         $select_name = $disabled
1964
-            ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1964
+            ? 'archive_price['.$ticket_row.']['.$price_row.'][PRT_ID]'
1965 1965
             : $select_name;
1966 1966
 
1967 1967
         $select_input = new EE_Select_Input(
@@ -1989,7 +1989,7 @@  discard block
 block discarded – undo
1989 1989
             'price_selected_is_percent' => $price_selected_is_percent,
1990 1990
             'disabled'                  => $disabled,
1991 1991
         ];
1992
-        $template_args             = apply_filters(
1992
+        $template_args = apply_filters(
1993 1993
             'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1994 1994
             $template_args,
1995 1995
             $ticket_row,
@@ -2000,7 +2000,7 @@  discard block
 block discarded – undo
2000 2000
             $this->_is_creating_event
2001 2001
         );
2002 2002
         return EEH_Template::display_template(
2003
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2003
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_modifier_selector.template.php',
2004 2004
             $template_args,
2005 2005
             true
2006 2006
         );
@@ -2027,10 +2027,10 @@  discard block
 block discarded – undo
2027 2027
         array $ticket_datetimes = [],
2028 2028
         bool $default = false
2029 2029
     ): string {
2030
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2031
-            ? $ticket_datetimes[ $ticket->ID() ]
2030
+        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
2031
+            ? $ticket_datetimes[$ticket->ID()]
2032 2032
             : array();
2033
-        $template_args    = [
2033
+        $template_args = [
2034 2034
             'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2035 2035
                 ? 'DTTNUM'
2036 2036
                 : $datetime_row,
@@ -2048,7 +2048,7 @@  discard block
 block discarded – undo
2048 2048
                 : $datetime->get_dtt_display_name(true),
2049 2049
             'tkt_status_class'         => '',
2050 2050
         ];
2051
-        $template_args    = apply_filters(
2051
+        $template_args = apply_filters(
2052 2052
             'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2053 2053
             $template_args,
2054 2054
             $datetime_row,
@@ -2060,7 +2060,7 @@  discard block
 block discarded – undo
2060 2060
             $this->_is_creating_event
2061 2061
         );
2062 2062
         return EEH_Template::display_template(
2063
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2063
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2064 2064
             $template_args,
2065 2065
             true
2066 2066
         );
@@ -2138,7 +2138,7 @@  discard block
 block discarded – undo
2138 2138
                 true
2139 2139
             ),
2140 2140
         ];
2141
-        $ticket_row    = 1;
2141
+        $ticket_row = 1;
2142 2142
         foreach ($all_tickets as $ticket) {
2143 2143
             $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2144 2144
                 'DTTNUM',
@@ -2166,11 +2166,11 @@  discard block
 block discarded – undo
2166 2166
         $default_prices = $price_model->get_all_default_prices();
2167 2167
         $price_row      = 1;
2168 2168
         foreach ($default_prices as $price) {
2169
-            if (! $price instanceof EE_Price) {
2169
+            if ( ! $price instanceof EE_Price) {
2170 2170
                 continue;
2171 2171
             }
2172 2172
             if ($price->is_base_price()) {
2173
-                $template_args['default_base_price_amount']      = $price->get_pretty(
2173
+                $template_args['default_base_price_amount'] = $price->get_pretty(
2174 2174
                     'PRC_amount',
2175 2175
                     'localized_float'
2176 2176
                 );
@@ -2202,7 +2202,7 @@  discard block
 block discarded – undo
2202 2202
             $this->_is_creating_event
2203 2203
         );
2204 2204
         return EEH_Template::display_template(
2205
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2205
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_js_structure.template.php',
2206 2206
             $template_args,
2207 2207
             true
2208 2208
         );
Please login to merge, or discard this patch.
admin/extend/events/templates/event_registration_options.template.php 2 patches
Indentation   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -19,12 +19,12 @@  discard block
 block discarded – undo
19 19
 
20 20
 <?php
21 21
 $settings_array                = [
22
-    'max_registrants'                 => '<p>
22
+	'max_registrants'                 => '<p>
23 23
         <label for="max-registrants">
24 24
         ' . esc_html__(
25
-        'Maximum number of tickets allowed per order for this event: ',
26
-        'event_espresso'
27
-    ) . '
25
+		'Maximum number of tickets allowed per order for this event: ',
26
+		'event_espresso'
27
+	) . '
28 28
         </label>
29 29
         <input class="ee-numeric ee-input-width--small"
30 30
                 type="text" 
@@ -34,33 +34,33 @@  discard block
 block discarded – undo
34 34
                 size="4" 
35 35
                 />
36 36
         </p>',
37
-    'additional_registration_options' => $additional_registration_options,
38
-    'display_ticket_selector'         => '<p>
37
+	'additional_registration_options' => $additional_registration_options,
38
+	'display_ticket_selector'         => '<p>
39 39
             <label>' . esc_html__('Display Ticket Selector', 'event_espresso') . '</label>'
40
-            . $display_ticket_selector // already escaped
41
-            . '</p>',
42
-    'alternative_registration_page'   => '<p>
40
+			. $display_ticket_selector // already escaped
41
+			. '</p>',
42
+	'alternative_registration_page'   => '<p>
43 43
             <label>' . esc_html__('Alternative Registration Page', 'event_espresso') . '</label>
44 44
             <input name="externalURL" class="ee-input-width--big" size="20" type="text" value="' . esc_url_raw
45
-        ($_event->external_url()) . '"> 
45
+		($_event->external_url()) . '"> 
46 46
             </p>',
47
-    'event_phone_number'              => '<p>
47
+	'event_phone_number'              => '<p>
48 48
             <label>' . esc_html__('Event Phone Number', 'event_espresso') . '</label>
49 49
             <input name="event_phone" class="ee-input-width--reg" size="20" type="text" value="' . esc_attr
50
-        ($_event->phone()) . '">
50
+		($_event->phone()) . '">
51 51
             </p>',
52
-    'default_registration_status'     => '<p>
52
+	'default_registration_status'     => '<p>
53 53
             <label>
54 54
             ' . esc_html__('Default Registration Status', 'event_espresso')
55
-             . EEH_Template::get_help_tab_link('event_editor_event_registration_options_help_tab')
56
-            . '</label>'
57
-             . $EVT_default_registration_status // already escaped
58
-             . '</p>',
55
+			 . EEH_Template::get_help_tab_link('event_editor_event_registration_options_help_tab')
56
+			. '</label>'
57
+			 . $EVT_default_registration_status // already escaped
58
+			 . '</p>',
59 59
 ];
60 60
 // filter
61 61
 $settings_array = apply_filters('FHEE__caffeinated_event_registration_options__template__settings', $settings_array);
62 62
 
63 63
 // echo
64 64
 foreach ($settings_array as $item) {
65
-    echo wp_kses($item, AllowedTags::getWithFormTags());
65
+	echo wp_kses($item, AllowedTags::getWithFormTags());
66 66
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -10 removed lines patch added patch discarded remove patch
@@ -18,36 +18,34 @@
 block discarded – undo
18 18
     </p>
19 19
 
20 20
 <?php
21
-$settings_array                = [
21
+$settings_array = [
22 22
     'max_registrants'                 => '<p>
23 23
         <label for="max-registrants">
24 24
         ' . esc_html__(
25 25
         'Maximum number of tickets allowed per order for this event: ',
26 26
         'event_espresso'
27
-    ) . '
27
+    ).'
28 28
         </label>
29 29
         <input class="ee-numeric ee-input-width--small"
30 30
                 type="text" 
31 31
                 id="max-registrants" 
32 32
                 name="additional_limit" 
33
-                value="' . esc_attr($additional_limit) . '"
33
+                value="' . esc_attr($additional_limit).'"
34 34
                 size="4" 
35 35
                 />
36 36
         </p>',
37 37
     'additional_registration_options' => $additional_registration_options,
38 38
     'display_ticket_selector'         => '<p>
39
-            <label>' . esc_html__('Display Ticket Selector', 'event_espresso') . '</label>'
39
+            <label>' . esc_html__('Display Ticket Selector', 'event_espresso').'</label>'
40 40
             . $display_ticket_selector // already escaped
41 41
             . '</p>',
42 42
     'alternative_registration_page'   => '<p>
43
-            <label>' . esc_html__('Alternative Registration Page', 'event_espresso') . '</label>
44
-            <input name="externalURL" class="ee-input-width--big" size="20" type="text" value="' . esc_url_raw
45
-        ($_event->external_url()) . '"> 
43
+            <label>' . esc_html__('Alternative Registration Page', 'event_espresso').'</label>
44
+            <input name="externalURL" class="ee-input-width--big" size="20" type="text" value="' . esc_url_raw($_event->external_url()).'"> 
46 45
             </p>',
47 46
     'event_phone_number'              => '<p>
48
-            <label>' . esc_html__('Event Phone Number', 'event_espresso') . '</label>
49
-            <input name="event_phone" class="ee-input-width--reg" size="20" type="text" value="' . esc_attr
50
-        ($_event->phone()) . '">
47
+            <label>' . esc_html__('Event Phone Number', 'event_espresso').'</label>
48
+            <input name="event_phone" class="ee-input-width--reg" size="20" type="text" value="' . esc_attr($_event->phone()).'">
51 49
             </p>',
52 50
     'default_registration_status'     => '<p>
53 51
             <label>
Please login to merge, or discard this patch.
caffeinated/admin/extend/events/qtips/EE_Event_Editor_Tips.lib.php 1 patch
Indentation   +124 added lines, -124 removed lines patch added patch discarded remove patch
@@ -13,133 +13,133 @@
 block discarded – undo
13 13
  */
14 14
 class EE_Event_Editor_Tips extends EE_Qtip_Config
15 15
 {
16
-    protected function _set_tips_array()
17
-    {
18
-        $this->_qtipsa = array(
19
-            0  => array(
20
-                'content_id' => 'about-taxable-toggle',
21
-                'target'     => '.TKT-taxable-checkbox',
22
-                'content'    => $this->_get_taxable_info_content(),
23
-                'options'    => array(
24
-                    'show_only_once' => true,
25
-                    'content'        => array(
26
-                        'title' => esc_html__('Taxable Ticket Toggle', 'event_espresso'),
27
-                        'button' => true,
28
-                    ),
29
-                    'show'           => array(
30
-                        'event' => 'click',
31
-                    ),
32
-                    'hide'           => array(
33
-                        'event' => false,
34
-                    ),
35
-                    'style'          => array(
36
-                        'classes' => '',
37
-                    ),
38
-                )// defaults
39
-            ),
40
-            7  => array(
41
-                'content_id' => 'tkt-status-archived',
42
-                'target'     => '.ticket-row .tkt-status-' . EE_Ticket::archived,
43
-                'content'    => $this->_ticket_status_legend(EE_Ticket::archived),
44
-                'options'    => array(
45
-                    'position' => array(
46
-                        'target' => 'mouse',
47
-                        'adjust' => array(
48
-                            'mouse' => false,
49
-                        ),
50
-                    ),
51
-                ),
52
-            ),
53
-            8  => array(
54
-                'content_id' => 'tkt-status-expired',
55
-                'target'     => '.ticket-row .tkt-status-' . EE_Ticket::expired,
56
-                'content'    => $this->_ticket_status_legend(EE_Ticket::expired),
57
-                'options'    => array(
58
-                    'position' => array(
59
-                        'target' => 'mouse',
60
-                        'adjust' => array(
61
-                            'mouse' => false,
62
-                        ),
63
-                    ),
64
-                ),
65
-            ),
66
-            9  => array(
67
-                'content_id' => 'tkt-status-sold_out',
68
-                'target'     => '.ticket-row .tkt-status-' . EE_Ticket::sold_out,
69
-                'content'    => $this->_ticket_status_legend(EE_Ticket::sold_out),
70
-                'options'    => array(
71
-                    'position' => array(
72
-                        'target' => 'mouse',
73
-                        'adjust' => array(
74
-                            'mouse' => false,
75
-                        ),
76
-                    ),
77
-                ),
78
-            ),
79
-            10 => array(
80
-                'content_id' => 'tkt-status-pending',
81
-                'target'     => '.ticket-row .tkt-status-' . EE_Ticket::pending,
82
-                'content'    => $this->_ticket_status_legend(EE_Ticket::pending),
83
-                'options'    => array(
84
-                    'position' => array(
85
-                        'target' => 'mouse',
86
-                        'adjust' => array(
87
-                            'mouse' => false,
88
-                        ),
89
-                    ),
90
-                ),
91
-            ),
92
-            11 => array(
93
-                'content_id' => 'tkt-status-onsale',
94
-                'target'     => '.ticket-row .tkt-status-' . EE_Ticket::onsale,
95
-                'content'    => $this->_ticket_status_legend(EE_Ticket::onsale),
96
-                'options'    => array(
97
-                    'position' => array(
98
-                        'target' => 'mouse',
99
-                        'adjust' => array(
100
-                            'mouse' => false,
101
-                        ),
102
-                    ),
103
-                ),
104
-            ),
105
-        );
106
-    }
16
+	protected function _set_tips_array()
17
+	{
18
+		$this->_qtipsa = array(
19
+			0  => array(
20
+				'content_id' => 'about-taxable-toggle',
21
+				'target'     => '.TKT-taxable-checkbox',
22
+				'content'    => $this->_get_taxable_info_content(),
23
+				'options'    => array(
24
+					'show_only_once' => true,
25
+					'content'        => array(
26
+						'title' => esc_html__('Taxable Ticket Toggle', 'event_espresso'),
27
+						'button' => true,
28
+					),
29
+					'show'           => array(
30
+						'event' => 'click',
31
+					),
32
+					'hide'           => array(
33
+						'event' => false,
34
+					),
35
+					'style'          => array(
36
+						'classes' => '',
37
+					),
38
+				)// defaults
39
+			),
40
+			7  => array(
41
+				'content_id' => 'tkt-status-archived',
42
+				'target'     => '.ticket-row .tkt-status-' . EE_Ticket::archived,
43
+				'content'    => $this->_ticket_status_legend(EE_Ticket::archived),
44
+				'options'    => array(
45
+					'position' => array(
46
+						'target' => 'mouse',
47
+						'adjust' => array(
48
+							'mouse' => false,
49
+						),
50
+					),
51
+				),
52
+			),
53
+			8  => array(
54
+				'content_id' => 'tkt-status-expired',
55
+				'target'     => '.ticket-row .tkt-status-' . EE_Ticket::expired,
56
+				'content'    => $this->_ticket_status_legend(EE_Ticket::expired),
57
+				'options'    => array(
58
+					'position' => array(
59
+						'target' => 'mouse',
60
+						'adjust' => array(
61
+							'mouse' => false,
62
+						),
63
+					),
64
+				),
65
+			),
66
+			9  => array(
67
+				'content_id' => 'tkt-status-sold_out',
68
+				'target'     => '.ticket-row .tkt-status-' . EE_Ticket::sold_out,
69
+				'content'    => $this->_ticket_status_legend(EE_Ticket::sold_out),
70
+				'options'    => array(
71
+					'position' => array(
72
+						'target' => 'mouse',
73
+						'adjust' => array(
74
+							'mouse' => false,
75
+						),
76
+					),
77
+				),
78
+			),
79
+			10 => array(
80
+				'content_id' => 'tkt-status-pending',
81
+				'target'     => '.ticket-row .tkt-status-' . EE_Ticket::pending,
82
+				'content'    => $this->_ticket_status_legend(EE_Ticket::pending),
83
+				'options'    => array(
84
+					'position' => array(
85
+						'target' => 'mouse',
86
+						'adjust' => array(
87
+							'mouse' => false,
88
+						),
89
+					),
90
+				),
91
+			),
92
+			11 => array(
93
+				'content_id' => 'tkt-status-onsale',
94
+				'target'     => '.ticket-row .tkt-status-' . EE_Ticket::onsale,
95
+				'content'    => $this->_ticket_status_legend(EE_Ticket::onsale),
96
+				'options'    => array(
97
+					'position' => array(
98
+						'target' => 'mouse',
99
+						'adjust' => array(
100
+							'mouse' => false,
101
+						),
102
+					),
103
+				),
104
+			),
105
+		);
106
+	}
107 107
 
108 108
 
109
-    private function _get_taxable_info_content()
110
-    {
111
-        $price_admin_link = EE_Admin_Page::add_query_args_and_nonce(array('action' => 'default'), PRICING_ADMIN_URL);
112
-        return '<p>'
113
-               . sprintf(
114
-                   esc_html__(
115
-                       'Clicking the taxable ticket toggle checkbox has enabled taxes for this ticket. What this means is that when a person purchases this ticket, the tax will be applied to all prices on this ticket. You can edit the existing tax price modifier that was setup in Event Espresso by going to  %sDefault Pricing Admin Page%s (labelled "Pricing" in the Event Espresso Menu)',
116
-                       'event_espresso'
117
-                   ),
118
-                   '<a href="' . $price_admin_link . '" aria-label="' . esc_attr__(
119
-                       'Pricing Admin Page',
120
-                       'event_espresso'
121
-                   ) . '">',
122
-                   '</a>'
123
-               ) . '</p>';
124
-    }
109
+	private function _get_taxable_info_content()
110
+	{
111
+		$price_admin_link = EE_Admin_Page::add_query_args_and_nonce(array('action' => 'default'), PRICING_ADMIN_URL);
112
+		return '<p>'
113
+			   . sprintf(
114
+				   esc_html__(
115
+					   'Clicking the taxable ticket toggle checkbox has enabled taxes for this ticket. What this means is that when a person purchases this ticket, the tax will be applied to all prices on this ticket. You can edit the existing tax price modifier that was setup in Event Espresso by going to  %sDefault Pricing Admin Page%s (labelled "Pricing" in the Event Espresso Menu)',
116
+					   'event_espresso'
117
+				   ),
118
+				   '<a href="' . $price_admin_link . '" aria-label="' . esc_attr__(
119
+					   'Pricing Admin Page',
120
+					   'event_espresso'
121
+				   ) . '">',
122
+				   '</a>'
123
+			   ) . '</p>';
124
+	}
125 125
 
126
-    /**
127
-     * output the relevant ee-status-legend with the designated status highlighted.
128
-     *
129
-     * @param  EE_Ticket constant $status What status is set (by class)
130
-     * @return string         The status legend with the related status highlighted
131
-     */
132
-    private function _ticket_status_legend($status)
133
-    {
126
+	/**
127
+	 * output the relevant ee-status-legend with the designated status highlighted.
128
+	 *
129
+	 * @param  EE_Ticket constant $status What status is set (by class)
130
+	 * @return string         The status legend with the related status highlighted
131
+	 */
132
+	private function _ticket_status_legend($status)
133
+	{
134 134
 
135
-        $status_array = array(
136
-            'archived' => EE_Ticket::archived,
137
-            'expired'  => EE_Ticket::expired,
138
-            'sold_out' => EE_Ticket::sold_out,
139
-            'pending'  => EE_Ticket::pending,
140
-            'onsale'   => EE_Ticket::onsale,
141
-        );
135
+		$status_array = array(
136
+			'archived' => EE_Ticket::archived,
137
+			'expired'  => EE_Ticket::expired,
138
+			'sold_out' => EE_Ticket::sold_out,
139
+			'pending'  => EE_Ticket::pending,
140
+			'onsale'   => EE_Ticket::onsale,
141
+		);
142 142
 
143
-        return EEH_Template::status_legend($status_array, $status);
144
-    }
143
+		return EEH_Template::status_legend($status_array, $status);
144
+	}
145 145
 }
Please login to merge, or discard this patch.