Completed
Branch dev (d8fe7d)
by
unknown
21:50 queued 14:45
created
core/admin/templates/admin_wrapper_ajax.template.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -17,11 +17,11 @@
 block discarded – undo
17 17
 
18 18
     <div class="ee-notices"><?php echo isset($ajax_notices) ? $ajax_notices  : '';// already escaped ?></div>
19 19
     <?php
20
-    do_action('AHEE__admin_wrapper__template__before_admin_page_content');
21
-    echo wp_kses($before_admin_page_content, AllowedTags::getWithFormTags());
22
-    echo wp_kses($admin_page_content, AllowedTags::getWithFullTags());
23
-    echo wp_kses($after_admin_page_content, AllowedTags::getWithFormTags());
24
-    do_action('AHEE__admin_wrapper__template__after_admin_page_content');
25
-    ?>
20
+	do_action('AHEE__admin_wrapper__template__before_admin_page_content');
21
+	echo wp_kses($before_admin_page_content, AllowedTags::getWithFormTags());
22
+	echo wp_kses($admin_page_content, AllowedTags::getWithFullTags());
23
+	echo wp_kses($after_admin_page_content, AllowedTags::getWithFormTags());
24
+	do_action('AHEE__admin_wrapper__template__after_admin_page_content');
25
+	?>
26 26
 </div>
27 27
 <!-- espresso-admin -->
28 28
\ No newline at end of file
Please login to merge, or discard this patch.
core/admin/templates/admin_wrapper.template.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -21,11 +21,11 @@
 block discarded – undo
21 21
     <?php echo wp_kses($nav_tabs, AllowedTags::getWithFormTags()); ?>
22 22
 
23 23
     <?php
24
-    do_action('AHEE__admin_wrapper__template__before_admin_page_content');
25
-    echo wp_kses($before_admin_page_content, AllowedTags::getWithFormTags());
26
-    echo wp_kses($admin_page_content, AllowedTags::getWithFullTags());
27
-    echo wp_kses($after_admin_page_content, AllowedTags::getWithFormTags());
28
-    do_action('AHEE__admin_wrapper__template__after_admin_page_content');
29
-    ?>
24
+	do_action('AHEE__admin_wrapper__template__before_admin_page_content');
25
+	echo wp_kses($before_admin_page_content, AllowedTags::getWithFormTags());
26
+	echo wp_kses($admin_page_content, AllowedTags::getWithFullTags());
27
+	echo wp_kses($after_admin_page_content, AllowedTags::getWithFormTags());
28
+	do_action('AHEE__admin_wrapper__template__after_admin_page_content');
29
+	?>
30 30
 
31 31
 </div>
Please login to merge, or discard this patch.
core/admin/templates/admin_details_wrapper_no_sidebar.template.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -24,19 +24,19 @@
 block discarded – undo
24 24
         <?php endif; ?>
25 25
 
26 26
         <?php
27
-        if (! empty($post_body_content)) :
28
-            if ($add_page_frame) {
29
-                if (
30
-                    strpos($post_body_content, 'class="padding"') === false
31
-                    || strpos($post_body_content, 'class="padding"') < 120
32
-                ) {
33
-                    $post_body_content = '<div class="padding">' . $post_body_content . '</div>';
34
-                }
35
-                if (strpos($post_body_content, '<div class="ee-admin-container">') === false) {
36
-                    $post_body_content = '<div class="ee-admin-container">' . $post_body_content . '</div>';
37
-                }
38
-            }
39
-            ?>
27
+		if (! empty($post_body_content)) :
28
+			if ($add_page_frame) {
29
+				if (
30
+					strpos($post_body_content, 'class="padding"') === false
31
+					|| strpos($post_body_content, 'class="padding"') < 120
32
+				) {
33
+					$post_body_content = '<div class="padding">' . $post_body_content . '</div>';
34
+				}
35
+				if (strpos($post_body_content, '<div class="ee-admin-container">') === false) {
36
+					$post_body_content = '<div class="ee-admin-container">' . $post_body_content . '</div>';
37
+				}
38
+			}
39
+			?>
40 40
             <div id="post-body-content">
41 41
                 <?php echo wp_kses($post_body_content, AllowedTags::getWithFullTags()); ?>
42 42
             </div>
Please login to merge, or discard this patch.
core/admin/templates/admin_details_wrapper.template.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -26,19 +26,19 @@
 block discarded – undo
26 26
     <div id="post-body" class="metabox-holder columns-2">
27 27
 
28 28
         <?php
29
-        if (! empty($post_body_content)) :
30
-            if ($add_page_frame) {
31
-                if (
32
-                    strpos($post_body_content, 'class="padding"') === false
33
-                    || strpos($post_body_content, 'class="padding"') < 120
34
-                ) {
35
-                    $post_body_content = '<div class="padding">' . $post_body_content . '</div>';
36
-                }
37
-                if (strpos($post_body_content, '<div class="ee-admin-container">') === false) {
38
-                    $post_body_content = '<div class="ee-admin-container">' . $post_body_content . '</div>';
39
-                }
40
-            }
41
-            ?>
29
+		if (! empty($post_body_content)) :
30
+			if ($add_page_frame) {
31
+				if (
32
+					strpos($post_body_content, 'class="padding"') === false
33
+					|| strpos($post_body_content, 'class="padding"') < 120
34
+				) {
35
+					$post_body_content = '<div class="padding">' . $post_body_content . '</div>';
36
+				}
37
+				if (strpos($post_body_content, '<div class="ee-admin-container">') === false) {
38
+					$post_body_content = '<div class="ee-admin-container">' . $post_body_content . '</div>';
39
+				}
40
+			}
41
+			?>
42 42
             <div id="post-body-content">
43 43
                 <?php echo wp_kses($post_body_content, AllowedTags::getWithFullTags()); ?>
44 44
             </div>
Please login to merge, or discard this patch.
caffeinated/admin/new/pricing/espresso_events_Pricing_Hooks.class.php 1 patch
Indentation   +2169 added lines, -2169 removed lines patch added patch discarded remove patch
@@ -15,2187 +15,2187 @@
 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
-        $timezone            = $data['timezone_string'] ?? null;
302
-        $saved_datetime_ids  = [];
303
-        $saved_datetime_objs = [];
304
-        if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
305
-            throw new InvalidArgumentException(
306
-                esc_html__(
307
-                    'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
308
-                    'event_espresso'
309
-                )
310
-            );
311
-        }
312
-        foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
313
-            // trim all values to ensure any excess whitespace is removed.
314
-            $datetime_data                = array_map(
315
-                function ($datetime_data) {
316
-                    return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
317
-                },
318
-                $datetime_data
319
-            );
320
-
321
-            $datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
322
-                                            && ! empty($datetime_data['DTT_EVT_end'])
323
-                ? $datetime_data['DTT_EVT_end']
324
-                : $datetime_data['DTT_EVT_start'];
325
-
326
-            $datetime_values              = [
327
-                'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
328
-                    ? $datetime_data['DTT_ID']
329
-                    : null,
330
-                'DTT_name'        => ! empty($datetime_data['DTT_name'])
331
-                    ? $datetime_data['DTT_name']
332
-                    : '',
333
-                'DTT_description' => ! empty($datetime_data['DTT_description'])
334
-                    ? $datetime_data['DTT_description']
335
-                    : '',
336
-                'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
337
-                'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
338
-                'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
339
-                    ? EE_INF
340
-                    : $datetime_data['DTT_reg_limit'],
341
-                'DTT_order'       => ! isset($datetime_data['DTT_order'])
342
-                    ? $row
343
-                    : $datetime_data['DTT_order'],
344
-            ];
345
-
346
-            // if we have an id then let's get existing object first and then set the new values.
347
-            // Otherwise we instantiate a new object for save.
348
-            if (! empty($datetime_data['DTT_ID'])) {
349
-                $datetime = EEM_Datetime::instance($timezone)->get_one_by_ID($datetime_data['DTT_ID']);
350
-                // set date and time format according to what is set in this class.
351
-                $datetime->set_date_format($this->_date_format_strings['date']);
352
-                $datetime->set_time_format($this->_date_format_strings['time']);
353
-                foreach ($datetime_values as $field => $value) {
354
-                    $datetime->set($field, $value);
355
-                }
356
-
357
-                // make sure the $datetime_id here is saved just in case
358
-                // after the add_relation_to() the autosave replaces it.
359
-                // We need to do this so we dont' TRASH the parent DTT.
360
-                // (save the ID for both key and value to avoid duplications)
361
-                $saved_datetime_ids[ $datetime->ID() ] = $datetime->ID();
362
-            } else {
363
-                $datetime = EE_Datetime::new_instance(
364
-                    $datetime_values,
365
-                    $timezone,
366
-                    [$this->_date_format_strings['date'], $this->_date_format_strings['time']]
367
-                );
368
-                foreach ($datetime_values as $field => $value) {
369
-                    $datetime->set($field, $value);
370
-                }
371
-            }
372
-            $datetime->save();
373
-            do_action(
374
-                'AHEE__espresso_events_Pricing_Hooks___update_datetimes_after_save',
375
-                $datetime,
376
-                $row,
377
-                $datetime_data,
378
-                $data
379
-            );
380
-            $datetime = $event->_add_relation_to($datetime, 'Datetime');
381
-            // before going any further make sure our dates are setup correctly
382
-            // so that the end date is always equal or greater than the start date.
383
-            if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
384
-                $datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
385
-                $datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
386
-                $datetime->save();
387
-            }
388
-            // now we have to make sure we add the new DTT_ID to the $saved_datetime_ids array
389
-            // because it is possible there was a new one created for the autosave.
390
-            // (save the ID for both key and value to avoid duplications)
391
-            $DTT_ID                        = $datetime->ID();
392
-            $saved_datetime_ids[ $DTT_ID ] = $DTT_ID;
393
-            $saved_datetime_objs[ $row ]   = $datetime;
394
-            // @todo if ANY of these updates fail then we want the appropriate global error message.
395
-        }
396
-        $event->save();
397
-        // now we need to REMOVE any datetimes that got deleted.
398
-        // Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
399
-        // So its safe to permanently delete at this point.
400
-        $old_datetimes = explode(',', $data['datetime_IDs']);
401
-        $old_datetimes = $old_datetimes[0] === '' ? [] : $old_datetimes;
402
-        if (is_array($old_datetimes)) {
403
-            $datetimes_to_delete = array_diff($old_datetimes, $saved_datetime_ids);
404
-            foreach ($datetimes_to_delete as $id) {
405
-                $id = absint($id);
406
-                if (empty($id)) {
407
-                    continue;
408
-                }
409
-                $datetime_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
410
-                // remove ticket relationships.
411
-                $related_tickets = $datetime_to_remove->get_many_related('Ticket');
412
-                foreach ($related_tickets as $ticket) {
413
-                    $datetime_to_remove->_remove_relation_to($ticket, 'Ticket');
414
-                }
415
-                $event->_remove_relation_to($id, 'Datetime');
416
-                $datetime_to_remove->refresh_cache_of_related_objects();
417
-            }
418
-        }
419
-        return $saved_datetime_objs;
420
-    }
421
-
422
-
423
-    /**
424
-     * update tickets
425
-     *
426
-     * @param EE_Event      $event           Event object being updated
427
-     * @param EE_Datetime[] $saved_datetimes an array of datetime ids being updated
428
-     * @param array         $data            incoming request data
429
-     * @return EE_Ticket[]
430
-     * @throws Exception
431
-     * @throws ReflectionException
432
-     * @throws InvalidInterfaceException
433
-     * @throws InvalidDataTypeException
434
-     * @throws InvalidArgumentException
435
-     * @throws EE_Error
436
-     */
437
-    protected function _update_tickets(EE_Event $event, array $saved_datetimes, array $data): array
438
-    {
439
-        $new_ticket = null;
440
-        // stripslashes because WP filtered the $_POST ($data) array to add slashes
441
-        $data          = stripslashes_deep($data);
442
-        $timezone      = $data['timezone_string'] ?? null;
443
-        $saved_tickets = [];
444
-        $old_tickets   = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
445
-        if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
446
-            throw new InvalidArgumentException(
447
-                esc_html__(
448
-                    'The "edit_tickets" array is invalid therefore the event can not be updated.',
449
-                    'event_espresso'
450
-                )
451
-            );
452
-        }
453
-        foreach ($data['edit_tickets'] as $row => $ticket_data) {
454
-            $update_prices = $create_new_TKT = false;
455
-            // figure out what datetimes were added to the ticket
456
-            // and what datetimes were removed from the ticket in the session.
457
-            $starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
458
-            $ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
459
-            $datetimes_added               = array_diff($ticket_datetime_rows, $starting_ticket_datetime_rows);
460
-            $datetimes_removed             = array_diff($starting_ticket_datetime_rows, $ticket_datetime_rows);
461
-            // trim inputs to ensure any excess whitespace is removed.
462
-            $ticket_data = array_map(
463
-                function ($ticket_data) {
464
-                    return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
465
-                },
466
-                $ticket_data
467
-            );
468
-            // note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
469
-            // because we're doing calculations prior to using the models.
470
-            // note incoming ['TKT_price'] value is already in standard notation (via js).
471
-            $ticket_price = isset($ticket_data['TKT_price'])
472
-                ? round((float) $ticket_data['TKT_price'], 3)
473
-                : 0;
474
-            // note incoming base price needs converted from localized value.
475
-            $base_price = isset($ticket_data['TKT_base_price'])
476
-                ? EEH_Money::convert_to_float_from_localized_money($ticket_data['TKT_base_price'])
477
-                : 0;
478
-            // if ticket price == 0 and $base_price != 0 then ticket price == base_price
479
-            $ticket_price  = $ticket_price === 0 && $base_price !== 0
480
-                ? $base_price
481
-                : $ticket_price;
482
-            $base_price_id = $ticket_data['TKT_base_price_ID'] ?? 0;
483
-            $price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
484
-                ? $data['edit_prices'][ $row ]
485
-                : [];
486
-            $now           = null;
487
-            if (empty($ticket_data['TKT_start_date'])) {
488
-                // lets' use now in the set timezone.
489
-                $now                           = new DateTime('now', new DateTimeZone($event->get_timezone()));
490
-                $ticket_data['TKT_start_date'] = $now->format($this->_date_time_format);
491
-            }
492
-            if (empty($ticket_data['TKT_end_date'])) {
493
-                /**
494
-                 * set the TKT_end_date to the first datetime attached to the ticket.
495
-                 */
496
-                $first_datetime              = $saved_datetimes[ reset($ticket_datetime_rows) ];
497
-                $ticket_data['TKT_end_date'] = $first_datetime->start_date_and_time($this->_date_time_format);
498
-            }
499
-            $TKT_values = [
500
-                'TKT_ID'          => ! empty($ticket_data['TKT_ID']) ? $ticket_data['TKT_ID'] : null,
501
-                'TTM_ID'          => ! empty($ticket_data['TTM_ID']) ? $ticket_data['TTM_ID'] : 0,
502
-                'TKT_name'        => ! empty($ticket_data['TKT_name']) ? $ticket_data['TKT_name'] : '',
503
-                'TKT_description' => ! empty($ticket_data['TKT_description'])
504
-                                     && $ticket_data['TKT_description'] !== esc_html__(
505
-                                         'You can modify this description',
506
-                                         'event_espresso'
507
-                                     )
508
-                    ? $ticket_data['TKT_description']
509
-                    : '',
510
-                'TKT_start_date'  => $ticket_data['TKT_start_date'],
511
-                'TKT_end_date'    => $ticket_data['TKT_end_date'],
512
-                'TKT_qty'         => ! isset($ticket_data['TKT_qty']) || $ticket_data['TKT_qty'] === ''
513
-                    ? EE_INF
514
-                    : $ticket_data['TKT_qty'],
515
-                'TKT_uses'        => ! isset($ticket_data['TKT_uses']) || $ticket_data['TKT_uses'] === ''
516
-                    ? EE_INF
517
-                    : $ticket_data['TKT_uses'],
518
-                'TKT_min'         => empty($ticket_data['TKT_min']) ? 0 : $ticket_data['TKT_min'],
519
-                'TKT_max'         => empty($ticket_data['TKT_max']) ? EE_INF : $ticket_data['TKT_max'],
520
-                'TKT_row'         => $row,
521
-                'TKT_order'       => $ticket_data['TKT_order'] ?? 0,
522
-                'TKT_taxable'     => ! empty($ticket_data['TKT_taxable']) ? 1 : 0,
523
-                'TKT_required'    => ! empty($ticket_data['TKT_required']) ? 1 : 0,
524
-                'TKT_price'       => $ticket_price,
525
-            ];
526
-            // if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
527
-            // which means in turn that the prices will become new prices as well.
528
-            if (isset($ticket_data['TKT_is_default']) && $ticket_data['TKT_is_default']) {
529
-                $TKT_values['TKT_ID']         = 0;
530
-                $TKT_values['TKT_is_default'] = 0;
531
-                $update_prices                = true;
532
-            }
533
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
534
-            // we actually do our saves ahead of doing any add_relations to
535
-            // because its entirely possible that this ticket wasn't removed or added to any datetime in the session
536
-            // but DID have it's items modified.
537
-            // keep in mind that if the TKT has been sold (and we have changed pricing information),
538
-            // then we won't be updating the ticket but instead a new ticket will be created and the old one archived.
539
-            if (absint($TKT_values['TKT_ID'])) {
540
-                $ticket = EEM_Ticket::instance($timezone)->get_one_by_ID($ticket_data['TKT_ID']);
541
-                if ($ticket instanceof EE_Ticket) {
542
-                    $ticket = $this->_update_ticket_datetimes(
543
-                        $ticket,
544
-                        $saved_datetimes,
545
-                        $datetimes_added,
546
-                        $datetimes_removed
547
-                    );
548
-                    // are there any registrations using this ticket ?
549
-                    $tickets_sold = $ticket->count_related(
550
-                        'Registration',
551
-                        [
552
-                            [
553
-                                'STS_ID' => ['NOT IN', [EEM_Registration::status_id_incomplete]],
554
-                            ],
555
-                        ]
556
-                    );
557
-                    // set ticket formats
558
-                    $ticket->set_date_format($this->_date_format_strings['date']);
559
-                    $ticket->set_time_format($this->_date_format_strings['time']);
560
-                    // let's just check the total price for the existing ticket
561
-                    // and determine if it matches the new total price.
562
-                    // if they are different then we create a new ticket (if tickets sold)
563
-                    // if they aren't different then we go ahead and modify existing ticket.
564
-                    $create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
565
-                    // set new values
566
-                    foreach ($TKT_values as $field => $value) {
567
-                        if ($field === 'TKT_qty') {
568
-                            $ticket->set_qty($value);
569
-                        } else {
570
-                            $ticket->set($field, $value);
571
-                        }
572
-                    }
573
-                    // if $create_new_TKT is false then we can safely update the existing ticket.
574
-                    // Otherwise we have to create a new ticket.
575
-                    if ($create_new_TKT) {
576
-                        $new_ticket = $this->_duplicate_ticket(
577
-                            $ticket,
578
-                            $price_rows,
579
-                            $ticket_price,
580
-                            $base_price,
581
-                            $base_price_id
582
-                        );
583
-                    }
584
-                }
585
-            } else {
586
-                // no TKT_id so a new TKT
587
-                $ticket = EE_Ticket::new_instance(
588
-                    $TKT_values,
589
-                    $timezone,
590
-                    [$this->_date_format_strings['date'], $this->_date_format_strings['time']]
591
-                );
592
-                if ($ticket instanceof EE_Ticket) {
593
-                    // make sure ticket has an ID of setting relations won't work
594
-                    $ticket->save();
595
-                    $ticket        = $this->_update_ticket_datetimes(
596
-                        $ticket,
597
-                        $saved_datetimes,
598
-                        $datetimes_added,
599
-                        $datetimes_removed
600
-                    );
601
-                    $update_prices = true;
602
-                }
603
-            }
604
-            // make sure any current values have been saved.
605
-            // $ticket->save();
606
-            // before going any further make sure our dates are setup correctly
607
-            // so that the end date is always equal or greater than the start date.
608
-            if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
609
-                $ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
610
-                $ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
611
-            }
612
-            // let's make sure the base price is handled
613
-            $ticket = ! $create_new_TKT
614
-                ? $this->_add_prices_to_ticket(
615
-                    [],
616
-                    $ticket,
617
-                    $update_prices,
618
-                    $base_price,
619
-                    $base_price_id
620
-                )
621
-                : $ticket;
622
-            // add/update price_modifiers
623
-            $ticket = ! $create_new_TKT
624
-                ? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
625
-                : $ticket;
626
-            // need to make sue that the TKT_price is accurate after saving the prices.
627
-            $ticket->ensure_TKT_Price_correct();
628
-            // handle CREATING a default ticket from the incoming ticket but ONLY if this isn't an autosave.
629
-            if (! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
630
-                $new_default = clone $ticket;
631
-                $new_default->set('TKT_ID', 0);
632
-                $new_default->set('TKT_is_default', 1);
633
-                $new_default->set('TKT_row', 1);
634
-                $new_default->set('TKT_price', $ticket_price);
635
-                // remove any datetime relations cause we DON'T want datetime relations attached
636
-                // (note this is just removing the cached relations in the object)
637
-                $new_default->_remove_relations('Datetime');
638
-                // @todo we need to add the current attached prices as new prices to the new default ticket.
639
-                $new_default = $this->_add_prices_to_ticket(
640
-                    $price_rows,
641
-                    $new_default,
642
-                    true
643
-                );
644
-                // don't forget the base price!
645
-                $new_default = $this->_add_prices_to_ticket(
646
-                    [],
647
-                    $new_default,
648
-                    true,
649
-                    $base_price,
650
-                    $base_price_id
651
-                );
652
-                $new_default->save();
653
-                do_action(
654
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
655
-                    $new_default,
656
-                    $row,
657
-                    $ticket,
658
-                    $data
659
-                );
660
-            }
661
-            // DO ALL datetime relationships for both current tickets and any archived tickets
662
-            // for the given datetime that are related to the current ticket.
663
-            // TODO... not sure exactly how we're going to do this considering we don't know
664
-            // what current ticket the archived tickets are related to
665
-            // (and TKT_parent is used for autosaves so that's not a field we can reliably use).
666
-            // let's assign any tickets that have been setup to the saved_tickets tracker
667
-            // save existing TKT
668
-            $ticket->save();
669
-            if ($create_new_TKT && $new_ticket instanceof EE_Ticket) {
670
-                // save new TKT
671
-                $new_ticket->save();
672
-                // add new ticket to array
673
-                $saved_tickets[ $new_ticket->ID() ] = $new_ticket;
674
-                do_action(
675
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
676
-                    $new_ticket,
677
-                    $row,
678
-                    $ticket_data,
679
-                    $data
680
-                );
681
-            } else {
682
-                // add ticket to saved tickets
683
-                $saved_tickets[ $ticket->ID() ] = $ticket;
684
-                do_action(
685
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
686
-                    $ticket,
687
-                    $row,
688
-                    $ticket_data,
689
-                    $data
690
-                );
691
-            }
692
-        }
693
-        // now we need to handle tickets actually "deleted permanently".
694
-        // There are cases where we'd want this to happen
695
-        // (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
696
-        // Or a draft event was saved and in the process of editing a ticket is trashed.
697
-        // No sense in keeping all the related data in the db!
698
-        $old_tickets     = isset($old_tickets[0]) && $old_tickets[0] === '' ? [] : $old_tickets;
699
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
700
-        foreach ($tickets_removed as $id) {
701
-            $id = absint($id);
702
-            // get the ticket for this id
703
-            $ticket_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
704
-            // if this ticket is a default ticket we leave it alone cause it won't be attached to the datetime
705
-            if ($ticket_to_remove->get('TKT_is_default')) {
706
-                continue;
707
-            }
708
-            // if this ticket has any registrations attached so then we just ARCHIVE
709
-            // because we don't actually permanently delete these tickets.
710
-            if ($ticket_to_remove->count_related('Registration') > 0) {
711
-                $ticket_to_remove->delete();
712
-                continue;
713
-            }
714
-            // need to get all the related datetimes on this ticket and remove from every single one of them
715
-            // (remember this process can ONLY kick off if there are NO tickets_sold)
716
-            $datetimes = $ticket_to_remove->get_many_related('Datetime');
717
-            foreach ($datetimes as $datetime) {
718
-                $ticket_to_remove->_remove_relation_to($datetime, 'Datetime');
719
-            }
720
-            // need to do the same for prices (except these prices can also be deleted because again,
721
-            // tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
722
-            $ticket_to_remove->delete_related_permanently('Price');
723
-            do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $ticket_to_remove);
724
-            // finally let's delete this ticket
725
-            // (which should not be blocked at this point b/c we've removed all our relationships)
726
-            $ticket_to_remove->delete_permanently();
727
-        }
728
-        return $saved_tickets;
729
-    }
730
-
731
-
732
-    /**
733
-     * @access  protected
734
-     * @param EE_Ticket     $ticket
735
-     * @param EE_Datetime[] $saved_datetimes
736
-     * @param int[]         $added_datetimes
737
-     * @param int[]         $removed_datetimes
738
-     * @return EE_Ticket
739
-     * @throws EE_Error
740
-     * @throws ReflectionException
741
-     */
742
-    protected function _update_ticket_datetimes(
743
-        EE_Ticket $ticket,
744
-        array $saved_datetimes = [],
745
-        array $added_datetimes = [],
746
-        array $removed_datetimes = []
747
-    ): EE_Ticket {
748
-        // to start we have to add the ticket to all the datetimes its supposed to be with,
749
-        // and removing the ticket from datetimes it got removed from.
750
-        // first let's add datetimes
751
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
752
-            foreach ($added_datetimes as $row_id) {
753
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
754
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
755
-                    // Is this an existing ticket (has an ID) and does it have any sold?
756
-                    // If so, then we need to add that to the DTT sold because this DTT is getting added.
757
-                    if ($ticket->ID() && $ticket->sold() > 0) {
758
-                        $saved_datetimes[ $row_id ]->increaseSold($ticket->sold(), false);
759
-                    }
760
-                }
761
-            }
762
-        }
763
-        // then remove datetimes
764
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
765
-            foreach ($removed_datetimes as $row_id) {
766
-                // its entirely possible that a datetime got deleted (instead of just removed from relationship.
767
-                // So make sure we skip over this if the datetime isn't in the $saved_datetimes array)
768
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
769
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
770
-                }
771
-            }
772
-        }
773
-        // cap ticket qty by datetime reg limits
774
-        $ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
775
-        return $ticket;
776
-    }
777
-
778
-
779
-    /**
780
-     * @access  protected
781
-     * @param EE_Ticket $ticket
782
-     * @param array     $price_rows
783
-     * @param int|float $ticket_price
784
-     * @param int|float $base_price
785
-     * @param int       $base_price_id
786
-     * @return EE_Ticket
787
-     * @throws ReflectionException
788
-     * @throws InvalidArgumentException
789
-     * @throws InvalidInterfaceException
790
-     * @throws InvalidDataTypeException
791
-     * @throws EE_Error
792
-     */
793
-    protected function _duplicate_ticket(
794
-        EE_Ticket $ticket,
795
-        array $price_rows = [],
796
-        $ticket_price = 0,
797
-        $base_price = 0,
798
-        int $base_price_id = 0
799
-    ): EE_Ticket {
800
-        // create new ticket that's a copy of the existing
801
-        // except a new id of course (and not archived)
802
-        // AND has the new TKT_price associated with it.
803
-        $new_ticket = clone $ticket;
804
-        $new_ticket->set('TKT_ID', 0);
805
-        $new_ticket->set_deleted(0);
806
-        $new_ticket->set_price($ticket_price);
807
-        $new_ticket->set_sold(0);
808
-        // let's get a new ID for this ticket
809
-        $new_ticket->save();
810
-        // we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
811
-        $datetimes_on_existing = $ticket->datetimes();
812
-        $new_ticket            = $this->_update_ticket_datetimes(
813
-            $new_ticket,
814
-            $datetimes_on_existing,
815
-            array_keys($datetimes_on_existing)
816
-        );
817
-        // $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
818
-        // if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
819
-        // available.
820
-        if ($ticket->sold() > 0) {
821
-            $new_qty = $ticket->qty() - $ticket->sold();
822
-            $new_ticket->set_qty($new_qty);
823
-        }
824
-        // now we update the prices just for this ticket
825
-        $new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
826
-        // and we update the base price
827
-        return $this->_add_prices_to_ticket(
828
-            [],
829
-            $new_ticket,
830
-            true,
831
-            $base_price,
832
-            $base_price_id
833
-        );
834
-    }
835
-
836
-
837
-    /**
838
-     * This attaches a list of given prices to a ticket.
839
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
840
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
841
-     * price info and prices are automatically "archived" via the ticket.
842
-     *
843
-     * @access  private
844
-     * @param array     $prices        Array of prices from the form.
845
-     * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
846
-     * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
847
-     * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
848
-     * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
849
-     * @return EE_Ticket
850
-     * @throws ReflectionException
851
-     * @throws InvalidArgumentException
852
-     * @throws InvalidInterfaceException
853
-     * @throws InvalidDataTypeException
854
-     * @throws EE_Error
855
-     */
856
-    protected function _add_prices_to_ticket(
857
-        array $prices,
858
-        EE_Ticket $ticket,
859
-        bool $new_prices = false,
860
-        $base_price = false,
861
-        $base_price_id = false
862
-    ): EE_Ticket {
863
-        // let's just get any current prices that may exist on the given ticket
864
-        // so we can remove any prices that got trashed in this session.
865
-        $current_prices_on_ticket = $base_price !== false
866
-            ? $ticket->base_price(true)
867
-            : $ticket->price_modifiers();
868
-        $updated_prices           = [];
869
-        // if $base_price ! FALSE then updating a base price.
870
-        if ($base_price !== false) {
871
-            $prices[1] = [
872
-                'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
873
-                'PRT_ID'     => 1,
874
-                'PRC_amount' => $base_price,
875
-                'PRC_name'   => $ticket->get('TKT_name'),
876
-                'PRC_desc'   => $ticket->get('TKT_description'),
877
-            ];
878
-        }
879
-        // possibly need to save ticket
880
-        if (! $ticket->ID()) {
881
-            $ticket->save();
882
-        }
883
-        foreach ($prices as $row => $prc) {
884
-            $prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
885
-            if (empty($prt_id)) {
886
-                continue;
887
-            } //prices MUST have a price type id.
888
-            $PRC_values = [
889
-                'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
890
-                'PRT_ID'         => $prt_id,
891
-                'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
892
-                'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
893
-                'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
894
-                'PRC_is_default' => false,
895
-                // make sure we set PRC_is_default to false for all ticket saves from event_editor
896
-                'PRC_order'      => $row,
897
-            ];
898
-            if ($new_prices || empty($PRC_values['PRC_ID'])) {
899
-                $PRC_values['PRC_ID'] = 0;
900
-                $price                = EE_Registry::instance()->load_class(
901
-                    'Price',
902
-                    [$PRC_values],
903
-                    false,
904
-                    false
905
-                );
906
-            } else {
907
-                $price = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
908
-                // update this price with new values
909
-                foreach ($PRC_values as $field => $value) {
910
-                    $price->set($field, $value);
911
-                }
912
-            }
913
-            $price->save();
914
-            $updated_prices[ $price->ID() ] = $price;
915
-            $ticket->_add_relation_to($price, 'Price');
916
-        }
917
-        // now let's remove any prices that got removed from the ticket
918
-        if (! empty($current_prices_on_ticket)) {
919
-            $current          = array_keys($current_prices_on_ticket);
920
-            $updated          = array_keys($updated_prices);
921
-            $prices_to_remove = array_diff($current, $updated);
922
-            if (! empty($prices_to_remove)) {
923
-                foreach ($prices_to_remove as $prc_id) {
924
-                    $p = $current_prices_on_ticket[ $prc_id ];
925
-                    $ticket->_remove_relation_to($p, 'Price');
926
-                    // delete permanently the price
927
-                    $p->delete_permanently();
928
-                }
929
-            }
930
-        }
931
-        return $ticket;
932
-    }
933
-
934
-
935
-    /**
936
-     * @throws ReflectionException
937
-     * @throws InvalidArgumentException
938
-     * @throws InvalidInterfaceException
939
-     * @throws InvalidDataTypeException
940
-     * @throws DomainException
941
-     * @throws EE_Error
942
-     */
943
-    public function pricing_metabox()
944
-    {
945
-        $existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = [];
946
-        $event                 = $this->_adminpage_obj->get_cpt_model_obj();
947
-
948
-        // set is_creating_event property.
949
-        $EVT_ID                   = $event->ID();
950
-        $this->_is_creating_event = empty($this->_req_data['post']);
951
-
952
-        // default main template args
953
-        $main_template_args = [
954
-            'event_datetime_help_link' => EEH_Template::get_help_tab_link(
955
-                'event_editor_event_datetimes_help_tab',
956
-                $this->_adminpage_obj->page_slug,
957
-                $this->_adminpage_obj->get_req_action()
958
-            ),
959
-
960
-            // todo need to add a filter to the template for the help text
961
-            // in the Events_Admin_Page core file so we can add further help
962
-            'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
963
-                'add_new_dtt_info',
964
-                $this->_adminpage_obj->page_slug,
965
-                $this->_adminpage_obj->get_req_action()
966
-            ),
967
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
968
-            'datetime_rows'            => '',
969
-            'show_tickets_container'   => '',
970
-            'ticket_rows'              => '',
971
-            'ee_collapsible_status'    => ' ee-collapsible-open',
972
-        ];
973
-        $timezone           = $event instanceof EE_Event ? $event->timezone_string() : null;
974
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
975
-
976
-        /**
977
-         * 1. Start with retrieving Datetimes
978
-         * 2. For each datetime get related tickets
979
-         * 3. For each ticket get related prices
980
-         */
981
-        /** @var EEM_Datetime $datetime_model */
982
-        $datetime_model                       = EE_Registry::instance()->load_model('Datetime', [$timezone]);
983
-        $datetimes                            = $datetime_model->get_all_event_dates($EVT_ID);
984
-        $main_template_args['total_dtt_rows'] = count($datetimes);
985
-
986
-        /**
987
-         * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
988
-         * for why we are counting $datetime_row and then setting that on the Datetime object
989
-         */
990
-        $datetime_row = 1;
991
-        foreach ($datetimes as $datetime) {
992
-            $DTT_ID = $datetime->get('DTT_ID');
993
-            $datetime->set('DTT_order', $datetime_row);
994
-            $existing_datetime_ids[] = $DTT_ID;
995
-            // tickets attached
996
-            $related_tickets = $datetime->ID() > 0
997
-                ? $datetime->get_many_related(
998
-                    'Ticket',
999
-                    [
1000
-                        [
1001
-                            'OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0],
1002
-                        ],
1003
-                        'default_where_conditions' => 'none',
1004
-                        'order_by'                 => ['TKT_order' => 'ASC'],
1005
-                    ]
1006
-                )
1007
-                : [];
1008
-            // if there are no related tickets this is likely a new event OR auto-draft
1009
-            // event so we need to generate the default tickets because datetimes
1010
-            // ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1011
-            // datetime on the event.
1012
-            if (empty($related_tickets) && count($datetimes) < 2) {
1013
-                /** @var EEM_Ticket $ticket_model */
1014
-                $ticket_model    = EE_Registry::instance()->load_model('Ticket');
1015
-                $related_tickets = $ticket_model->get_all_default_tickets();
1016
-                // this should be ordered by TKT_ID, so let's grab the first default ticket
1017
-                // (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1018
-                $default_prices      = EEM_Price::instance()->get_all_default_prices();
1019
-                $main_default_ticket = reset($related_tickets);
1020
-                if ($main_default_ticket instanceof EE_Ticket) {
1021
-                    foreach ($default_prices as $default_price) {
1022
-                        if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1023
-                            continue;
1024
-                        }
1025
-                        $main_default_ticket->cache('Price', $default_price);
1026
-                    }
1027
-                }
1028
-            }
1029
-            // we can't actually setup rows in this loop yet cause we don't know all
1030
-            // the unique tickets for this event yet (tickets are linked through all datetimes).
1031
-            // So we're going to temporarily cache some of that information.
1032
-            // loop through and setup the ticket rows and make sure the order is set.
1033
-            foreach ($related_tickets as $ticket) {
1034
-                $TKT_ID     = $ticket->get('TKT_ID');
1035
-                $ticket_row = $ticket->get('TKT_row');
1036
-                // we only want unique tickets in our final display!!
1037
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1038
-                    $existing_ticket_ids[] = $TKT_ID;
1039
-                    $all_tickets[]         = $ticket;
1040
-                }
1041
-                // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1042
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1043
-                // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1044
-                if (
1045
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1046
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1047
-                ) {
1048
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1049
-                }
1050
-            }
1051
-            $datetime_row++;
1052
-        }
1053
-        $main_template_args['total_ticket_rows']     = count($existing_ticket_ids);
1054
-        $main_template_args['existing_ticket_ids']   = implode(',', $existing_ticket_ids);
1055
-        $main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1056
-        // sort $all_tickets by order
1057
-        usort(
1058
-            $all_tickets,
1059
-            function (EE_Ticket $a, EE_Ticket $b) {
1060
-                $a_order = (int) $a->get('TKT_order');
1061
-                $b_order = (int) $b->get('TKT_order');
1062
-                if ($a_order === $b_order) {
1063
-                    return 0;
1064
-                }
1065
-                return ($a_order < $b_order) ? -1 : 1;
1066
-            }
1067
-        );
1068
-        // k NOW we have all the data we need for setting up the datetime rows
1069
-        // and ticket rows so we start our datetime loop again.
1070
-        $datetime_row = 1;
1071
-        foreach ($datetimes as $datetime) {
1072
-            $main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1073
-                $datetime_row,
1074
-                $datetime,
1075
-                $datetime_tickets,
1076
-                $all_tickets,
1077
-                false,
1078
-                $datetimes
1079
-            );
1080
-            $datetime_row++;
1081
-        }
1082
-        // then loop through all tickets for the ticket rows.
1083
-        $ticket_row = 1;
1084
-        foreach ($all_tickets as $ticket) {
1085
-            $main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1086
-                $ticket_row,
1087
-                $ticket,
1088
-                $ticket_datetimes,
1089
-                $datetimes,
1090
-                false,
1091
-                $all_tickets
1092
-            );
1093
-            $ticket_row++;
1094
-        }
1095
-        $main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1096
-
1097
-        $status_change_notice = LoaderFactory::getLoader()->getShared(
1098
-            'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
1099
-        );
1100
-
1101
-        $main_template_args['status_change_notice'] = $status_change_notice->display(
1102
-            '__event-editor',
1103
-            'espresso-events'
1104
-        );
1105
-
1106
-        EEH_Template::display_template(
1107
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1108
-            $main_template_args
1109
-        );
1110
-    }
1111
-
1112
-
1113
-    /**
1114
-     * @param int         $datetime_row
1115
-     * @param EE_Datetime $datetime
1116
-     * @param array       $datetime_tickets
1117
-     * @param array       $all_tickets
1118
-     * @param bool        $default
1119
-     * @param array       $all_datetimes
1120
-     * @return string
1121
-     * @throws DomainException
1122
-     * @throws EE_Error
1123
-     * @throws ReflectionException
1124
-     */
1125
-    protected function _get_datetime_row(
1126
-        int $datetime_row,
1127
-        EE_Datetime $datetime,
1128
-        array $datetime_tickets = [],
1129
-        array $all_tickets = [],
1130
-        bool $default = false,
1131
-        array $all_datetimes = []
1132
-    ): string {
1133
-        return EEH_Template::display_template(
1134
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1135
-            [
1136
-                'dtt_edit_row'             => $this->_get_dtt_edit_row(
1137
-                    $datetime_row,
1138
-                    $datetime,
1139
-                    $default,
1140
-                    $all_datetimes
1141
-                ),
1142
-                'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1143
-                    $datetime_row,
1144
-                    $datetime,
1145
-                    $datetime_tickets,
1146
-                    $all_tickets,
1147
-                    $default
1148
-                ),
1149
-                'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1150
-            ],
1151
-            true
1152
-        );
1153
-    }
1154
-
1155
-
1156
-    /**
1157
-     * This method is used to generate a datetime fields  edit row.
1158
-     * The same row is used to generate a row with valid DTT objects
1159
-     * and the default row that is used as the skeleton by the js.
1160
-     *
1161
-     * @param int              $datetime_row  The row number for the row being generated.
1162
-     * @param EE_Datetime|null $datetime
1163
-     * @param bool             $default       Whether a default row is being generated or not.
1164
-     * @param EE_Datetime[]    $all_datetimes This is the array of all datetimes used in the editor.
1165
-     * @return string
1166
-     * @throws EE_Error
1167
-     * @throws ReflectionException
1168
-     */
1169
-    protected function _get_dtt_edit_row(
1170
-        int $datetime_row,
1171
-        ?EE_Datetime $datetime,
1172
-        bool $default,
1173
-        array $all_datetimes
1174
-    ): string {
1175
-        // if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1176
-        $default                     = ! $datetime instanceof EE_Datetime ? true : $default;
1177
-        $template_args               = [
1178
-            'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1179
-            'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1180
-            'edit_dtt_expanded'    => '',
1181
-            'DTT_ID'               => $default ? '' : $datetime->ID(),
1182
-            'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1183
-            'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1184
-            'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1185
-            'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1186
-            'DTT_reg_limit'        => $default
1187
-                ? ''
1188
-                : $datetime->get_pretty(
1189
-                    'DTT_reg_limit',
1190
-                    'input'
1191
-                ),
1192
-            'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1193
-            'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1194
-            'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1195
-            'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1196
-                ? ''
1197
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1198
-            'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1199
-                ? 'dashicons dashicons-lock'
1200
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1201
-            'reg_list_url'         => $default || ! $datetime->event() instanceof EE_Event
1202
-                ? ''
1203
-                : EE_Admin_Page::add_query_args_and_nonce(
1204
-                    [
1205
-                        'event_id' => $datetime->event()->ID(),
1206
-                        'datetime_id' => $datetime->ID(),
1207
-                        'use_filters' => true
1208
-                    ],
1209
-                    REG_ADMIN_URL
1210
-                ),
1211
-        ];
1212
-        $template_args['show_trash'] = count($all_datetimes) === 1
1213
-                                       && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1214
-            ? 'display:none'
1215
-            : '';
1216
-        // allow filtering of template args at this point.
1217
-        $template_args = apply_filters(
1218
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1219
-            $template_args,
1220
-            $datetime_row,
1221
-            $datetime,
1222
-            $default,
1223
-            $all_datetimes,
1224
-            $this->_is_creating_event
1225
-        );
1226
-        return EEH_Template::display_template(
1227
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1228
-            $template_args,
1229
-            true
1230
-        );
1231
-    }
1232
-
1233
-
1234
-    /**
1235
-     * @param int         $datetime_row
1236
-     * @param EE_Datetime $datetime
1237
-     * @param array       $datetime_tickets
1238
-     * @param array       $all_tickets
1239
-     * @param bool        $default
1240
-     * @return string
1241
-     * @throws DomainException
1242
-     * @throws EE_Error
1243
-     * @throws ReflectionException
1244
-     */
1245
-    protected function _get_dtt_attached_tickets_row(
1246
-        int $datetime_row,
1247
-        EE_Datetime $datetime,
1248
-        array $datetime_tickets = [],
1249
-        array $all_tickets = [],
1250
-        bool $default = false
1251
-    ): string {
1252
-        $template_args = [
1253
-            'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1254
-            'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1255
-            'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1256
-            'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1257
-            'show_tickets_row'                  => 'display:none;',
1258
-            'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1259
-                'add_new_ticket_via_datetime',
1260
-                $this->_adminpage_obj->page_slug,
1261
-                $this->_adminpage_obj->get_req_action()
1262
-            ),
1263
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1264
-            'DTT_ID'                            => $default ? '' : $datetime->ID(),
1265
-        ];
1266
-        // need to setup the list items (but only if this isn't a default skeleton setup)
1267
-        if (! $default) {
1268
-            $ticket_row = 1;
1269
-            foreach ($all_tickets as $ticket) {
1270
-                $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1271
-                    $datetime_row,
1272
-                    $ticket_row,
1273
-                    $datetime,
1274
-                    $ticket,
1275
-                    $datetime_tickets,
1276
-                    $default
1277
-                );
1278
-                $ticket_row++;
1279
-            }
1280
-        }
1281
-        // filter template args at this point
1282
-        $template_args = apply_filters(
1283
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1284
-            $template_args,
1285
-            $datetime_row,
1286
-            $datetime,
1287
-            $datetime_tickets,
1288
-            $all_tickets,
1289
-            $default,
1290
-            $this->_is_creating_event
1291
-        );
1292
-        return EEH_Template::display_template(
1293
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1294
-            $template_args,
1295
-            true
1296
-        );
1297
-    }
1298
-
1299
-
1300
-    /**
1301
-     * @param int              $datetime_row
1302
-     * @param int              $ticket_row
1303
-     * @param EE_Datetime|null $datetime
1304
-     * @param EE_Ticket|null   $ticket
1305
-     * @param array            $datetime_tickets
1306
-     * @param bool             $default
1307
-     * @return string
1308
-     * @throws EE_Error
1309
-     * @throws ReflectionException
1310
-     */
1311
-    protected function _get_datetime_tickets_list_item(
1312
-        int $datetime_row,
1313
-        int $ticket_row,
1314
-        ?EE_Datetime $datetime,
1315
-        ?EE_Ticket $ticket,
1316
-        array $datetime_tickets = [],
1317
-        bool $default = false
1318
-    ): string {
1319
-        $datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1320
-            ? $datetime_tickets[ $datetime->ID() ]
1321
-            : [];
1322
-        $display_row      = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1323
-        $no_ticket        = $default && empty($ticket);
1324
-        $template_args    = [
1325
-            'dtt_row'                 => $default
1326
-                ? 'DTTNUM'
1327
-                : $datetime_row,
1328
-            'tkt_row'                 => $no_ticket
1329
-                ? 'TICKETNUM'
1330
-                : $ticket_row,
1331
-            'datetime_ticket_checked' => in_array($display_row, $datetime_tickets, true)
1332
-                ? ' checked'
1333
-                : '',
1334
-            'ticket_selected'         => in_array($display_row, $datetime_tickets, true)
1335
-                ? ' ticket-selected'
1336
-                : '',
1337
-            'TKT_name'                => $no_ticket
1338
-                ? 'TKTNAME'
1339
-                : $ticket->get('TKT_name'),
1340
-            'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1341
-                ? ' tkt-status-' . EE_Ticket::onsale
1342
-                : ' tkt-status-' . $ticket->ticket_status(),
1343
-        ];
1344
-        // filter template args
1345
-        $template_args = apply_filters(
1346
-            'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1347
-            $template_args,
1348
-            $datetime_row,
1349
-            $ticket_row,
1350
-            $datetime,
1351
-            $ticket,
1352
-            $datetime_tickets,
1353
-            $default,
1354
-            $this->_is_creating_event
1355
-        );
1356
-        return EEH_Template::display_template(
1357
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1358
-            $template_args,
1359
-            true
1360
-        );
1361
-    }
1362
-
1363
-
1364
-    /**
1365
-     * This generates the ticket row for tickets.
1366
-     * This same method is used to generate both the actual rows and the js skeleton row
1367
-     * (when default === true)
1368
-     *
1369
-     * @param int            $ticket_row       Represents the row number being generated.
1370
-     * @param EE_Ticket|null $ticket
1371
-     * @param EE_Datetime[]  $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1372
-     *                                         or empty for default
1373
-     * @param EE_Datetime[]  $all_datetimes    All Datetimes on the event or empty for default.
1374
-     * @param bool           $default          Whether default row being generated or not.
1375
-     * @param EE_Ticket[]    $all_tickets      This is an array of all tickets attached to the event
1376
-     *                                         (or empty in the case of defaults)
1377
-     * @return string
1378
-     * @throws InvalidArgumentException
1379
-     * @throws InvalidInterfaceException
1380
-     * @throws InvalidDataTypeException
1381
-     * @throws DomainException
1382
-     * @throws EE_Error
1383
-     * @throws ReflectionException
1384
-     */
1385
-    protected function _get_ticket_row(
1386
-        int $ticket_row,
1387
-        ?EE_Ticket $ticket,
1388
-        array $ticket_datetimes,
1389
-        array $all_datetimes,
1390
-        bool $default = false,
1391
-        array $all_tickets = []
1392
-    ): string {
1393
-        // if $ticket is not an instance of EE_Ticket then force default to true.
1394
-        $default = ! $ticket instanceof EE_Ticket ? true : $default;
1395
-        $prices  = ! empty($ticket) && ! $default
1396
-            ? $ticket->get_many_related(
1397
-                'Price',
1398
-                ['default_where_conditions' => 'none', 'order_by' => ['PRC_order' => 'ASC']]
1399
-            )
1400
-            : [];
1401
-        // if there is only one price (which would be the base price)
1402
-        // or NO prices and this ticket is a default ticket,
1403
-        // let's just make sure there are no cached default prices on the object.
1404
-        // This is done by not including any query_params.
1405
-        if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1406
-            $prices = $ticket->prices();
1407
-        }
1408
-        // check if we're dealing with a default ticket in which case
1409
-        // we don't want any starting_ticket_datetime_row values set
1410
-        // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1411
-        // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1412
-        $default_datetime = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1413
-        $ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1414
-            ? $ticket_datetimes[ $ticket->ID() ]
1415
-            : [];
1416
-        $ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1417
-        $base_price       = $default ? null : $ticket->base_price();
1418
-        $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1419
-        // breaking out complicated condition for ticket_status
1420
-        if ($default) {
1421
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1422
-        } else {
1423
-            $ticket_status_class = $ticket->is_default()
1424
-                ? ' tkt-status-' . EE_Ticket::onsale
1425
-                : ' tkt-status-' . $ticket->ticket_status();
1426
-        }
1427
-        // breaking out complicated condition for TKT_taxable
1428
-        if ($default) {
1429
-            $TKT_taxable = '';
1430
-        } else {
1431
-            $TKT_taxable = $ticket->taxable()
1432
-                ? 'checked'
1433
-                : '';
1434
-        }
1435
-        if ($default) {
1436
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1437
-        } elseif ($ticket->is_default()) {
1438
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1439
-        } else {
1440
-            $TKT_status = $ticket->ticket_status(true);
1441
-        }
1442
-        if ($default) {
1443
-            $TKT_min = '';
1444
-        } else {
1445
-            $TKT_min = $ticket->min();
1446
-            if ($TKT_min === -1 || $TKT_min === 0) {
1447
-                $TKT_min = '';
1448
-            }
1449
-        }
1450
-        $template_args                 = [
1451
-            'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1452
-            'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1453
-            // on initial page load this will always be the correct order.
1454
-            'tkt_status_class'              => $ticket_status_class,
1455
-            'display_edit_tkt_row'          => 'display:none;',
1456
-            'edit_tkt_expanded'             => '',
1457
-            'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1458
-            'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1459
-            'TKT_start_date'                => $default
1460
-                ? ''
1461
-                : $ticket->get_date('TKT_start_date', $this->_date_time_format),
1462
-            'TKT_end_date'                  => $default
1463
-                ? ''
1464
-                : $ticket->get_date('TKT_end_date', $this->_date_time_format),
1465
-            'TKT_status'                    => $TKT_status,
1466
-            'TKT_price'                     => $default
1467
-                ? ''
1468
-                : EEH_Template::format_currency(
1469
-                    $ticket->get_ticket_total_with_taxes(),
1470
-                    false,
1471
-                    false
1472
-                ),
1473
-            'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1474
-            'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1475
-            'TKT_qty'                       => $default
1476
-                ? ''
1477
-                : $ticket->get_pretty('TKT_qty', 'symbol'),
1478
-            'TKT_qty_for_input'             => $default
1479
-                ? ''
1480
-                : $ticket->get_pretty('TKT_qty', 'input'),
1481
-            'TKT_uses'                      => $default
1482
-                ? ''
1483
-                : $ticket->get_pretty('TKT_uses', 'input'),
1484
-            'TKT_min'                       => $TKT_min,
1485
-            'TKT_max'                       => $default
1486
-                ? ''
1487
-                : $ticket->get_pretty('TKT_max', 'input'),
1488
-            'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold(),
1489
-            'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1490
-            'TKT_registrations'             => $default
1491
-                ? 0
1492
-                : $ticket->count_registrations(
1493
-                    [
1494
-                        [
1495
-                            'STS_ID' => [
1496
-                                '!=',
1497
-                                EEM_Registration::status_id_incomplete,
1498
-                            ],
1499
-                        ],
1500
-                    ]
1501
-                ),
1502
-            'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1503
-            'TKT_description'               => $default ? '' : $ticket->get_f('TKT_description'),
1504
-            'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1505
-            'TKT_required'                  => $default ? 0 : $ticket->required(),
1506
-            'TKT_is_default_selector'       => '',
1507
-            'ticket_price_rows'             => '',
1508
-            'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1509
-                ? ''
1510
-                : $base_price->get_pretty('PRC_amount', 'localized_float'),
1511
-            'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1512
-            'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1513
-                ? ''
1514
-                : 'display:none;',
1515
-            'show_price_mod_button'         => count($prices) > 1
1516
-                                               || ($default && $count_price_mods > 0)
1517
-                                               || (! $default && $ticket->deleted())
1518
-                ? 'display:none;'
1519
-                : '',
1520
-            'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1521
-            'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1522
-            'starting_ticket_datetime_rows' => $default || $default_datetime ? '' : implode(',', $ticket_datetimes),
1523
-            'ticket_datetime_rows'          => $default ? '' : implode(',', $ticket_datetimes),
1524
-            'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1525
-            'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1526
-            'TKT_taxable'                   => $TKT_taxable,
1527
-            'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1528
-                ? ''
1529
-                : 'display:none;',
1530
-            'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1531
-            'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1532
-                $ticket_subtotal,
1533
-                false,
1534
-                false
1535
-            ),
1536
-            'TKT_subtotal_amount'           => $ticket_subtotal,
1537
-            'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1538
-            'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1539
-            'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1540
-                ? ' ticket-archived'
1541
-                : '',
1542
-            'trash_icon'                    => $ticket instanceof EE_Ticket
1543
-                                               && $ticket->deleted()
1544
-                                               && ! $ticket->is_permanently_deleteable()
1545
-                ? 'dashicons dashicons-lock '
1546
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1547
-            'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1548
-                ? ''
1549
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1550
-        ];
1551
-        $template_args['trash_hidden'] = count($all_tickets) === 1
1552
-                                         && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1553
-            ? 'display:none'
1554
-            : '';
1555
-        // handle rows that should NOT be empty
1556
-        if (empty($template_args['TKT_start_date'])) {
1557
-            // if empty then the start date will be now.
1558
-            $template_args['TKT_start_date']   = date(
1559
-                $this->_date_time_format,
1560
-                current_time('timestamp')
1561
-            );
1562
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1563
-        }
1564
-        if (empty($template_args['TKT_end_date'])) {
1565
-            // get the earliest datetime (if present);
1566
-            $earliest_datetime = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1567
-                ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1568
-                    'Datetime',
1569
-                    ['order_by' => ['DTT_EVT_start' => 'ASC']]
1570
-                )
1571
-                : null;
1572
-            if (! empty($earliest_datetime)) {
1573
-                $template_args['TKT_end_date'] = $earliest_datetime->get_datetime(
1574
-                    'DTT_EVT_start',
1575
-                    $this->_date_time_format
1576
-                );
1577
-            } else {
1578
-                // default so let's just use what's been set for the default date-time which is 30 days from now.
1579
-                $template_args['TKT_end_date'] = date(
1580
-                    $this->_date_time_format,
1581
-                    mktime(
1582
-                        24,
1583
-                        0,
1584
-                        0,
1585
-                        date('m'),
1586
-                        date('d') + 29,
1587
-                        date('Y')
1588
-                    )
1589
-                );
1590
-            }
1591
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1592
-        }
1593
-        // generate ticket_datetime items
1594
-        if (! $default) {
1595
-            $datetime_row = 1;
1596
-            foreach ($all_datetimes as $datetime) {
1597
-                $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1598
-                    $datetime_row,
1599
-                    $ticket_row,
1600
-                    $datetime,
1601
-                    $ticket,
1602
-                    $ticket_datetimes,
1603
-                    $default
1604
-                );
1605
-                $datetime_row++;
1606
-            }
1607
-        }
1608
-        $price_row = 1;
1609
-        foreach ($prices as $price) {
1610
-            if (! $price instanceof EE_Price) {
1611
-                continue;
1612
-            }
1613
-            if ($price->is_base_price()) {
1614
-                $price_row++;
1615
-                continue;
1616
-            }
1617
-
1618
-            $show_trash  = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1619
-            $show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1620
-
1621
-            $template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1622
-                $ticket_row,
1623
-                $price_row,
1624
-                $price,
1625
-                $default,
1626
-                $ticket,
1627
-                $show_trash,
1628
-                $show_create
1629
-            );
1630
-            $price_row++;
1631
-        }
1632
-        // filter $template_args
1633
-        $template_args = apply_filters(
1634
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1635
-            $template_args,
1636
-            $ticket_row,
1637
-            $ticket,
1638
-            $ticket_datetimes,
1639
-            $all_datetimes,
1640
-            $default,
1641
-            $all_tickets,
1642
-            $this->_is_creating_event
1643
-        );
1644
-        return EEH_Template::display_template(
1645
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1646
-            $template_args,
1647
-            true
1648
-        );
1649
-    }
1650
-
1651
-
1652
-    /**
1653
-     * @param int            $ticket_row
1654
-     * @param EE_Ticket|null $ticket
1655
-     * @return string
1656
-     * @throws DomainException
1657
-     * @throws EE_Error
1658
-     * @throws ReflectionException
1659
-     */
1660
-    protected function _get_tax_rows(int $ticket_row, ?EE_Ticket $ticket): string
1661
-    {
1662
-        $tax_rows = '';
1663
-        /** @var EE_Price[] $taxes */
1664
-        $taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1665
-        foreach ($taxes as $tax) {
1666
-            $tax_added     = $this->_get_tax_added($tax, $ticket);
1667
-            $template_args = [
1668
-                'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1669
-                    ? ''
1670
-                    : 'display:none;',
1671
-                'tax_id'            => $tax->ID(),
1672
-                'tkt_row'           => $ticket_row,
1673
-                'tax_label'         => $tax->get('PRC_name'),
1674
-                'tax_added'         => $tax_added,
1675
-                'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1676
-                'tax_amount'        => $tax->get('PRC_amount'),
1677
-            ];
1678
-            $template_args = apply_filters(
1679
-                'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1680
-                $template_args,
1681
-                $ticket_row,
1682
-                $ticket,
1683
-                $this->_is_creating_event
1684
-            );
1685
-            $tax_rows      .= EEH_Template::display_template(
1686
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1687
-                $template_args,
1688
-                true
1689
-            );
1690
-        }
1691
-        return $tax_rows;
1692
-    }
1693
-
1694
-
1695
-    /**
1696
-     * @param EE_Price       $tax
1697
-     * @param EE_Ticket|null $ticket
1698
-     * @return float|int
1699
-     * @throws EE_Error
1700
-     * @throws ReflectionException
1701
-     */
1702
-    protected function _get_tax_added(EE_Price $tax, ?EE_Ticket $ticket)
1703
-    {
1704
-        $subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1705
-        return $subtotal * $tax->get('PRC_amount') / 100;
1706
-    }
1707
-
1708
-
1709
-    /**
1710
-     * @param int            $ticket_row
1711
-     * @param int            $price_row
1712
-     * @param EE_Price|null  $price
1713
-     * @param bool           $default
1714
-     * @param EE_Ticket|null $ticket
1715
-     * @param bool           $show_trash
1716
-     * @param bool           $show_create
1717
-     * @return string
1718
-     * @throws InvalidArgumentException
1719
-     * @throws InvalidInterfaceException
1720
-     * @throws InvalidDataTypeException
1721
-     * @throws DomainException
1722
-     * @throws EE_Error
1723
-     * @throws ReflectionException
1724
-     */
1725
-    protected function _get_ticket_price_row(
1726
-        int $ticket_row,
1727
-        int $price_row,
1728
-        ?EE_Price $price,
1729
-        bool $default,
1730
-        ?EE_Ticket $ticket,
1731
-        bool $show_trash = true,
1732
-        bool $show_create = true
1733
-    ): string {
1734
-        $send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1735
-        $template_args = [
1736
-            'tkt_row'               => $default && empty($ticket)
1737
-                ? 'TICKETNUM'
1738
-                : $ticket_row,
1739
-            'PRC_order'             => $default && empty($price)
1740
-                ? 'PRICENUM'
1741
-                : $price_row,
1742
-            'edit_prices_name'      => $default && empty($price)
1743
-                ? 'PRICENAMEATTR'
1744
-                : 'edit_prices',
1745
-            'price_type_selector'   => $default && empty($price)
1746
-                ? $this->_get_base_price_template($ticket_row, $price_row, $price, true)
1747
-                : $this->_get_price_type_selector(
1748
-                    $ticket_row,
1749
-                    $price_row,
1750
-                    $price,
1751
-                    $default,
1752
-                    $send_disabled
1753
-                ),
1754
-            'PRC_ID'                => $default && empty($price)
1755
-                ? 0
1756
-                : $price->ID(),
1757
-            'PRC_is_default'        => $default && empty($price)
1758
-                ? 0
1759
-                : $price->get('PRC_is_default'),
1760
-            'PRC_name'              => $default && empty($price)
1761
-                ? ''
1762
-                : $price->get('PRC_name'),
1763
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1764
-            'show_plus_or_minus'    => $default && empty($price)
1765
-                ? ''
1766
-                : 'display:none;',
1767
-            'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1768
-                ? 'display:none;'
1769
-                : '',
1770
-            'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1771
-                ? 'display:none;'
1772
-                : '',
1773
-            'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1774
-                ? 'display:none'
1775
-                : '',
1776
-            'PRC_amount'            => $default && empty($price)
1777
-                ? 0
1778
-                : $price->get_pretty('PRC_amount', 'localized_float'),
1779
-            'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1780
-                ? 'display:none;'
1781
-                : '',
1782
-            'show_trash_icon'       => $show_trash
1783
-                ? ''
1784
-                : ' style="display:none;"',
1785
-            'show_create_button'    => $show_create
1786
-                ? ''
1787
-                : ' style="display:none;"',
1788
-            'PRC_desc'              => $default && empty($price)
1789
-                ? ''
1790
-                : $price->get('PRC_desc'),
1791
-            'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1792
-        ];
1793
-        $template_args = apply_filters(
1794
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1795
-            $template_args,
1796
-            $ticket_row,
1797
-            $price_row,
1798
-            $price,
1799
-            $default,
1800
-            $ticket,
1801
-            $show_trash,
1802
-            $show_create,
1803
-            $this->_is_creating_event
1804
-        );
1805
-        return EEH_Template::display_template(
1806
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1807
-            $template_args,
1808
-            true
1809
-        );
1810
-    }
1811
-
1812
-
1813
-    /**
1814
-     * @param int           $ticket_row
1815
-     * @param int           $price_row
1816
-     * @param EE_Price|null $price
1817
-     * @param bool          $default
1818
-     * @param bool          $disabled
1819
-     * @return string
1820
-     * @throws ReflectionException
1821
-     * @throws InvalidArgumentException
1822
-     * @throws InvalidInterfaceException
1823
-     * @throws InvalidDataTypeException
1824
-     * @throws DomainException
1825
-     * @throws EE_Error
1826
-     */
1827
-    protected function _get_price_type_selector(
1828
-        int $ticket_row,
1829
-        int $price_row,
1830
-        ?EE_Price $price,
1831
-        bool $default,
1832
-        bool $disabled = false
1833
-    ): string {
1834
-        if ($price->is_base_price()) {
1835
-            return $this->_get_base_price_template(
1836
-                $ticket_row,
1837
-                $price_row,
1838
-                $price,
1839
-                $default
1840
-            );
1841
-        }
1842
-        return $this->_get_price_modifier_template(
1843
-            $ticket_row,
1844
-            $price_row,
1845
-            $price,
1846
-            $default,
1847
-            $disabled
1848
-        );
1849
-    }
1850
-
1851
-
1852
-    /**
1853
-     * @param int           $ticket_row
1854
-     * @param int           $price_row
1855
-     * @param EE_Price|null $price
1856
-     * @param bool          $default
1857
-     * @return string
1858
-     * @throws DomainException
1859
-     * @throws EE_Error
1860
-     * @throws ReflectionException
1861
-     */
1862
-    protected function _get_base_price_template(
1863
-        int $ticket_row,
1864
-        int $price_row,
1865
-        ?EE_Price $price,
1866
-        bool $default
1867
-    ): string {
1868
-        $template_args = [
1869
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1870
-            'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1871
-            'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1872
-            'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1873
-            'price_selected_operator'   => '+',
1874
-            'price_selected_is_percent' => 0,
1875
-        ];
1876
-        $template_args = apply_filters(
1877
-            'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1878
-            $template_args,
1879
-            $ticket_row,
1880
-            $price_row,
1881
-            $price,
1882
-            $default,
1883
-            $this->_is_creating_event
1884
-        );
1885
-        return EEH_Template::display_template(
1886
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1887
-            $template_args,
1888
-            true
1889
-        );
1890
-    }
1891
-
1892
-
1893
-    /**
1894
-     * @param int           $ticket_row
1895
-     * @param int           $price_row
1896
-     * @param EE_Price|null $price
1897
-     * @param bool          $default
1898
-     * @param bool          $disabled
1899
-     * @return string
1900
-     * @throws ReflectionException
1901
-     * @throws InvalidArgumentException
1902
-     * @throws InvalidInterfaceException
1903
-     * @throws InvalidDataTypeException
1904
-     * @throws DomainException
1905
-     * @throws EE_Error
1906
-     */
1907
-    protected function _get_price_modifier_template(
1908
-        int $ticket_row,
1909
-        int $price_row,
1910
-        ?EE_Price $price,
1911
-        bool $default,
1912
-        bool $disabled = false
1913
-    ): string {
1914
-        $select_name = $default && ! $price instanceof EE_Price
1915
-            ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1916
-            : 'edit_prices[' . esc_attr($ticket_row) . '][' . esc_attr($price_row) . '][PRT_ID]';
1917
-        /** @var EEM_Price_Type $price_type_model */
1918
-        $price_type_model       = EE_Registry::instance()->load_model('Price_Type');
1919
-        $price_types            = $price_type_model->get_all(
1920
-            [
1921
-                [
1922
-                    'OR' => [
1923
-                        'PBT_ID'  => '2',
1924
-                        'PBT_ID*' => '3',
1925
-                    ],
1926
-                ],
1927
-            ]
1928
-        );
1929
-        $all_price_types        = $default && ! $price instanceof EE_Price
1930
-            ? [esc_html__('Select Modifier', 'event_espresso')]
1931
-            : [];
1932
-        $selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1933
-        $price_option_spans     = '';
1934
-        // setup price types for selector
1935
-        foreach ($price_types as $price_type) {
1936
-            if (! $price_type instanceof EE_Price_Type) {
1937
-                continue;
1938
-            }
1939
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1940
-            // while we're in the loop let's setup the option spans used by js
1941
-            $span_args          = [
1942
-                'PRT_ID'         => $price_type->ID(),
1943
-                'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1944
-                'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1945
-            ];
1946
-            $price_option_spans .= EEH_Template::display_template(
1947
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1948
-                $span_args,
1949
-                true
1950
-            );
1951
-        }
1952
-
1953
-        $select_name = $disabled
1954
-            ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1955
-            : $select_name;
1956
-
1957
-        $select_input = new EE_Select_Input(
1958
-            $all_price_types,
1959
-            [
1960
-                'default'               => $selected_price_type_id,
1961
-                'html_name'             => $select_name,
1962
-                'html_class'            => 'edit-price-PRT_ID',
1963
-                'other_html_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1964
-            ]
1965
-        );
1966
-
1967
-        $price_selected_operator   = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1968
-        $price_selected_operator   = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1969
-        $price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1970
-        $price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1971
-        $template_args             = [
1972
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1973
-            'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1974
-            'price_modifier_selector'   => $select_input->get_html_for_input(),
1975
-            'main_name'                 => $select_name,
1976
-            'selected_price_type_id'    => $selected_price_type_id,
1977
-            'price_option_spans'        => $price_option_spans,
1978
-            'price_selected_operator'   => $price_selected_operator,
1979
-            'price_selected_is_percent' => $price_selected_is_percent,
1980
-            'disabled'                  => $disabled,
1981
-        ];
1982
-        $template_args             = apply_filters(
1983
-            'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1984
-            $template_args,
1985
-            $ticket_row,
1986
-            $price_row,
1987
-            $price,
1988
-            $default,
1989
-            $disabled,
1990
-            $this->_is_creating_event
1991
-        );
1992
-        return EEH_Template::display_template(
1993
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
1994
-            $template_args,
1995
-            true
1996
-        );
1997
-    }
1998
-
1999
-
2000
-    /**
2001
-     * @param int              $datetime_row
2002
-     * @param int              $ticket_row
2003
-     * @param EE_Datetime|null $datetime
2004
-     * @param EE_Ticket|null   $ticket
2005
-     * @param array            $ticket_datetimes
2006
-     * @param bool             $default
2007
-     * @return string
2008
-     * @throws DomainException
2009
-     * @throws EE_Error
2010
-     * @throws ReflectionException
2011
-     */
2012
-    protected function _get_ticket_datetime_list_item(
2013
-        int $datetime_row,
2014
-        int $ticket_row,
2015
-        ?EE_Datetime $datetime,
2016
-        ?EE_Ticket $ticket,
2017
-        array $ticket_datetimes = [],
2018
-        bool $default = false
2019
-    ): string {
2020
-        $ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2021
-            ? $ticket_datetimes[ $ticket->ID() ]
2022
-            : [];
2023
-        $template_args    = [
2024
-            'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2025
-                ? 'DTTNUM'
2026
-                : $datetime_row,
2027
-            'tkt_row'                  => $default
2028
-                ? 'TICKETNUM'
2029
-                : $ticket_row,
2030
-            'ticket_datetime_selected' => in_array($datetime_row, $ticket_datetimes, true)
2031
-                ? ' ticket-selected'
2032
-                : '',
2033
-            'ticket_datetime_checked'  => in_array($datetime_row, $ticket_datetimes, true)
2034
-                ? ' checked'
2035
-                : '',
2036
-            'DTT_name'                 => $default && empty($datetime)
2037
-                ? 'DTTNAME'
2038
-                : $datetime->get_dtt_display_name(true),
2039
-            'tkt_status_class'         => '',
2040
-        ];
2041
-        $template_args    = apply_filters(
2042
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2043
-            $template_args,
2044
-            $datetime_row,
2045
-            $ticket_row,
2046
-            $datetime,
2047
-            $ticket,
2048
-            $ticket_datetimes,
2049
-            $default,
2050
-            $this->_is_creating_event
2051
-        );
2052
-        return EEH_Template::display_template(
2053
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2054
-            $template_args,
2055
-            true
2056
-        );
2057
-    }
2058
-
2059
-
2060
-    /**
2061
-     * @param array $all_datetimes
2062
-     * @param array $all_tickets
2063
-     * @return string
2064
-     * @throws ReflectionException
2065
-     * @throws InvalidArgumentException
2066
-     * @throws InvalidInterfaceException
2067
-     * @throws InvalidDataTypeException
2068
-     * @throws DomainException
2069
-     * @throws EE_Error
2070
-     */
2071
-    protected function _get_ticket_js_structure(array $all_datetimes = [], array $all_tickets = []): string
2072
-    {
2073
-        $template_args = [
2074
-            'default_datetime_edit_row' => $this->_get_dtt_edit_row(
2075
-                'DTTNUM',
2076
-                null,
2077
-                true,
2078
-                $all_datetimes
2079
-            ),
2080
-            'default_ticket_row'        => $this->_get_ticket_row(
2081
-                'TICKETNUM',
2082
-                null,
2083
-                [],
2084
-                [],
2085
-                true
2086
-            ),
2087
-            'default_price_row'         => $this->_get_ticket_price_row(
2088
-                'TICKETNUM',
2089
-                'PRICENUM',
2090
-                null,
2091
-                true,
2092
-                null
2093
-            ),
2094
-
2095
-            'default_price_rows'                       => '',
2096
-            'default_base_price_amount'                => 0,
2097
-            'default_base_price_name'                  => '',
2098
-            'default_base_price_description'           => '',
2099
-            'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2100
-                'TICKETNUM',
2101
-                'PRICENUM',
2102
-                null,
2103
-                true
2104
-            ),
2105
-            'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2106
-                'DTTNUM',
2107
-                null,
2108
-                [],
2109
-                [],
2110
-                true
2111
-            ),
2112
-            'existing_available_datetime_tickets_list' => '',
2113
-            'existing_available_ticket_datetimes_list' => '',
2114
-            'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2115
-                'DTTNUM',
2116
-                'TICKETNUM',
2117
-                null,
2118
-                null,
2119
-                [],
2120
-                true
2121
-            ),
2122
-            'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2123
-                'DTTNUM',
2124
-                'TICKETNUM',
2125
-                null,
2126
-                null,
2127
-                [],
2128
-                true
2129
-            ),
2130
-        ];
2131
-        $ticket_row    = 1;
2132
-        foreach ($all_tickets as $ticket) {
2133
-            $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2134
-                'DTTNUM',
2135
-                $ticket_row,
2136
-                null,
2137
-                $ticket,
2138
-                [],
2139
-                true
2140
-            );
2141
-            $ticket_row++;
2142
-        }
2143
-        $datetime_row = 1;
2144
-        foreach ($all_datetimes as $datetime) {
2145
-            $template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2146
-                $datetime_row,
2147
-                'TICKETNUM',
2148
-                $datetime,
2149
-                null,
2150
-                [],
2151
-                true
2152
-            );
2153
-            $datetime_row++;
2154
-        }
2155
-        /** @var EEM_Price $price_model */
2156
-        $price_model    = EE_Registry::instance()->load_model('Price');
2157
-        $default_prices = $price_model->get_all_default_prices();
2158
-        $price_row      = 1;
2159
-        foreach ($default_prices as $price) {
2160
-            if (! $price instanceof EE_Price) {
2161
-                continue;
2162
-            }
2163
-            if ($price->is_base_price()) {
2164
-                $template_args['default_base_price_amount']      = $price->get_pretty(
2165
-                    'PRC_amount',
2166
-                    'localized_float'
2167
-                );
2168
-                $template_args['default_base_price_name']        = $price->get('PRC_name');
2169
-                $template_args['default_base_price_description'] = $price->get('PRC_desc');
2170
-                $price_row++;
2171
-                continue;
2172
-            }
2173
-
2174
-            $show_trash  = ! ((count($default_prices) > 1 && $price_row === 1) || count($default_prices) === 1);
2175
-            $show_create = ! (count($default_prices) > 1 && count($default_prices) !== $price_row);
2176
-
2177
-            $template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2178
-                'TICKETNUM',
2179
-                $price_row,
2180
-                $price,
2181
-                true,
2182
-                null,
2183
-                $show_trash,
2184
-                $show_create
2185
-            );
2186
-            $price_row++;
2187
-        }
2188
-        $template_args = apply_filters(
2189
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2190
-            $template_args,
2191
-            $all_datetimes,
2192
-            $all_tickets,
2193
-            $this->_is_creating_event
2194
-        );
2195
-        return EEH_Template::display_template(
2196
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2197
-            $template_args,
2198
-            true
2199
-        );
2200
-    }
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
+		$timezone            = $data['timezone_string'] ?? null;
302
+		$saved_datetime_ids  = [];
303
+		$saved_datetime_objs = [];
304
+		if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
305
+			throw new InvalidArgumentException(
306
+				esc_html__(
307
+					'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
308
+					'event_espresso'
309
+				)
310
+			);
311
+		}
312
+		foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
313
+			// trim all values to ensure any excess whitespace is removed.
314
+			$datetime_data                = array_map(
315
+				function ($datetime_data) {
316
+					return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
317
+				},
318
+				$datetime_data
319
+			);
320
+
321
+			$datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
322
+											&& ! empty($datetime_data['DTT_EVT_end'])
323
+				? $datetime_data['DTT_EVT_end']
324
+				: $datetime_data['DTT_EVT_start'];
325
+
326
+			$datetime_values              = [
327
+				'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
328
+					? $datetime_data['DTT_ID']
329
+					: null,
330
+				'DTT_name'        => ! empty($datetime_data['DTT_name'])
331
+					? $datetime_data['DTT_name']
332
+					: '',
333
+				'DTT_description' => ! empty($datetime_data['DTT_description'])
334
+					? $datetime_data['DTT_description']
335
+					: '',
336
+				'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
337
+				'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
338
+				'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
339
+					? EE_INF
340
+					: $datetime_data['DTT_reg_limit'],
341
+				'DTT_order'       => ! isset($datetime_data['DTT_order'])
342
+					? $row
343
+					: $datetime_data['DTT_order'],
344
+			];
345
+
346
+			// if we have an id then let's get existing object first and then set the new values.
347
+			// Otherwise we instantiate a new object for save.
348
+			if (! empty($datetime_data['DTT_ID'])) {
349
+				$datetime = EEM_Datetime::instance($timezone)->get_one_by_ID($datetime_data['DTT_ID']);
350
+				// set date and time format according to what is set in this class.
351
+				$datetime->set_date_format($this->_date_format_strings['date']);
352
+				$datetime->set_time_format($this->_date_format_strings['time']);
353
+				foreach ($datetime_values as $field => $value) {
354
+					$datetime->set($field, $value);
355
+				}
356
+
357
+				// make sure the $datetime_id here is saved just in case
358
+				// after the add_relation_to() the autosave replaces it.
359
+				// We need to do this so we dont' TRASH the parent DTT.
360
+				// (save the ID for both key and value to avoid duplications)
361
+				$saved_datetime_ids[ $datetime->ID() ] = $datetime->ID();
362
+			} else {
363
+				$datetime = EE_Datetime::new_instance(
364
+					$datetime_values,
365
+					$timezone,
366
+					[$this->_date_format_strings['date'], $this->_date_format_strings['time']]
367
+				);
368
+				foreach ($datetime_values as $field => $value) {
369
+					$datetime->set($field, $value);
370
+				}
371
+			}
372
+			$datetime->save();
373
+			do_action(
374
+				'AHEE__espresso_events_Pricing_Hooks___update_datetimes_after_save',
375
+				$datetime,
376
+				$row,
377
+				$datetime_data,
378
+				$data
379
+			);
380
+			$datetime = $event->_add_relation_to($datetime, 'Datetime');
381
+			// before going any further make sure our dates are setup correctly
382
+			// so that the end date is always equal or greater than the start date.
383
+			if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
384
+				$datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
385
+				$datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
386
+				$datetime->save();
387
+			}
388
+			// now we have to make sure we add the new DTT_ID to the $saved_datetime_ids array
389
+			// because it is possible there was a new one created for the autosave.
390
+			// (save the ID for both key and value to avoid duplications)
391
+			$DTT_ID                        = $datetime->ID();
392
+			$saved_datetime_ids[ $DTT_ID ] = $DTT_ID;
393
+			$saved_datetime_objs[ $row ]   = $datetime;
394
+			// @todo if ANY of these updates fail then we want the appropriate global error message.
395
+		}
396
+		$event->save();
397
+		// now we need to REMOVE any datetimes that got deleted.
398
+		// Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
399
+		// So its safe to permanently delete at this point.
400
+		$old_datetimes = explode(',', $data['datetime_IDs']);
401
+		$old_datetimes = $old_datetimes[0] === '' ? [] : $old_datetimes;
402
+		if (is_array($old_datetimes)) {
403
+			$datetimes_to_delete = array_diff($old_datetimes, $saved_datetime_ids);
404
+			foreach ($datetimes_to_delete as $id) {
405
+				$id = absint($id);
406
+				if (empty($id)) {
407
+					continue;
408
+				}
409
+				$datetime_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
410
+				// remove ticket relationships.
411
+				$related_tickets = $datetime_to_remove->get_many_related('Ticket');
412
+				foreach ($related_tickets as $ticket) {
413
+					$datetime_to_remove->_remove_relation_to($ticket, 'Ticket');
414
+				}
415
+				$event->_remove_relation_to($id, 'Datetime');
416
+				$datetime_to_remove->refresh_cache_of_related_objects();
417
+			}
418
+		}
419
+		return $saved_datetime_objs;
420
+	}
421
+
422
+
423
+	/**
424
+	 * update tickets
425
+	 *
426
+	 * @param EE_Event      $event           Event object being updated
427
+	 * @param EE_Datetime[] $saved_datetimes an array of datetime ids being updated
428
+	 * @param array         $data            incoming request data
429
+	 * @return EE_Ticket[]
430
+	 * @throws Exception
431
+	 * @throws ReflectionException
432
+	 * @throws InvalidInterfaceException
433
+	 * @throws InvalidDataTypeException
434
+	 * @throws InvalidArgumentException
435
+	 * @throws EE_Error
436
+	 */
437
+	protected function _update_tickets(EE_Event $event, array $saved_datetimes, array $data): array
438
+	{
439
+		$new_ticket = null;
440
+		// stripslashes because WP filtered the $_POST ($data) array to add slashes
441
+		$data          = stripslashes_deep($data);
442
+		$timezone      = $data['timezone_string'] ?? null;
443
+		$saved_tickets = [];
444
+		$old_tickets   = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : [];
445
+		if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
446
+			throw new InvalidArgumentException(
447
+				esc_html__(
448
+					'The "edit_tickets" array is invalid therefore the event can not be updated.',
449
+					'event_espresso'
450
+				)
451
+			);
452
+		}
453
+		foreach ($data['edit_tickets'] as $row => $ticket_data) {
454
+			$update_prices = $create_new_TKT = false;
455
+			// figure out what datetimes were added to the ticket
456
+			// and what datetimes were removed from the ticket in the session.
457
+			$starting_ticket_datetime_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
458
+			$ticket_datetime_rows          = explode(',', $data['ticket_datetime_rows'][ $row ]);
459
+			$datetimes_added               = array_diff($ticket_datetime_rows, $starting_ticket_datetime_rows);
460
+			$datetimes_removed             = array_diff($starting_ticket_datetime_rows, $ticket_datetime_rows);
461
+			// trim inputs to ensure any excess whitespace is removed.
462
+			$ticket_data = array_map(
463
+				function ($ticket_data) {
464
+					return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
465
+				},
466
+				$ticket_data
467
+			);
468
+			// note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
469
+			// because we're doing calculations prior to using the models.
470
+			// note incoming ['TKT_price'] value is already in standard notation (via js).
471
+			$ticket_price = isset($ticket_data['TKT_price'])
472
+				? round((float) $ticket_data['TKT_price'], 3)
473
+				: 0;
474
+			// note incoming base price needs converted from localized value.
475
+			$base_price = isset($ticket_data['TKT_base_price'])
476
+				? EEH_Money::convert_to_float_from_localized_money($ticket_data['TKT_base_price'])
477
+				: 0;
478
+			// if ticket price == 0 and $base_price != 0 then ticket price == base_price
479
+			$ticket_price  = $ticket_price === 0 && $base_price !== 0
480
+				? $base_price
481
+				: $ticket_price;
482
+			$base_price_id = $ticket_data['TKT_base_price_ID'] ?? 0;
483
+			$price_rows    = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
484
+				? $data['edit_prices'][ $row ]
485
+				: [];
486
+			$now           = null;
487
+			if (empty($ticket_data['TKT_start_date'])) {
488
+				// lets' use now in the set timezone.
489
+				$now                           = new DateTime('now', new DateTimeZone($event->get_timezone()));
490
+				$ticket_data['TKT_start_date'] = $now->format($this->_date_time_format);
491
+			}
492
+			if (empty($ticket_data['TKT_end_date'])) {
493
+				/**
494
+				 * set the TKT_end_date to the first datetime attached to the ticket.
495
+				 */
496
+				$first_datetime              = $saved_datetimes[ reset($ticket_datetime_rows) ];
497
+				$ticket_data['TKT_end_date'] = $first_datetime->start_date_and_time($this->_date_time_format);
498
+			}
499
+			$TKT_values = [
500
+				'TKT_ID'          => ! empty($ticket_data['TKT_ID']) ? $ticket_data['TKT_ID'] : null,
501
+				'TTM_ID'          => ! empty($ticket_data['TTM_ID']) ? $ticket_data['TTM_ID'] : 0,
502
+				'TKT_name'        => ! empty($ticket_data['TKT_name']) ? $ticket_data['TKT_name'] : '',
503
+				'TKT_description' => ! empty($ticket_data['TKT_description'])
504
+									 && $ticket_data['TKT_description'] !== esc_html__(
505
+										 'You can modify this description',
506
+										 'event_espresso'
507
+									 )
508
+					? $ticket_data['TKT_description']
509
+					: '',
510
+				'TKT_start_date'  => $ticket_data['TKT_start_date'],
511
+				'TKT_end_date'    => $ticket_data['TKT_end_date'],
512
+				'TKT_qty'         => ! isset($ticket_data['TKT_qty']) || $ticket_data['TKT_qty'] === ''
513
+					? EE_INF
514
+					: $ticket_data['TKT_qty'],
515
+				'TKT_uses'        => ! isset($ticket_data['TKT_uses']) || $ticket_data['TKT_uses'] === ''
516
+					? EE_INF
517
+					: $ticket_data['TKT_uses'],
518
+				'TKT_min'         => empty($ticket_data['TKT_min']) ? 0 : $ticket_data['TKT_min'],
519
+				'TKT_max'         => empty($ticket_data['TKT_max']) ? EE_INF : $ticket_data['TKT_max'],
520
+				'TKT_row'         => $row,
521
+				'TKT_order'       => $ticket_data['TKT_order'] ?? 0,
522
+				'TKT_taxable'     => ! empty($ticket_data['TKT_taxable']) ? 1 : 0,
523
+				'TKT_required'    => ! empty($ticket_data['TKT_required']) ? 1 : 0,
524
+				'TKT_price'       => $ticket_price,
525
+			];
526
+			// if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
527
+			// which means in turn that the prices will become new prices as well.
528
+			if (isset($ticket_data['TKT_is_default']) && $ticket_data['TKT_is_default']) {
529
+				$TKT_values['TKT_ID']         = 0;
530
+				$TKT_values['TKT_is_default'] = 0;
531
+				$update_prices                = true;
532
+			}
533
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
534
+			// we actually do our saves ahead of doing any add_relations to
535
+			// because its entirely possible that this ticket wasn't removed or added to any datetime in the session
536
+			// but DID have it's items modified.
537
+			// keep in mind that if the TKT has been sold (and we have changed pricing information),
538
+			// then we won't be updating the ticket but instead a new ticket will be created and the old one archived.
539
+			if (absint($TKT_values['TKT_ID'])) {
540
+				$ticket = EEM_Ticket::instance($timezone)->get_one_by_ID($ticket_data['TKT_ID']);
541
+				if ($ticket instanceof EE_Ticket) {
542
+					$ticket = $this->_update_ticket_datetimes(
543
+						$ticket,
544
+						$saved_datetimes,
545
+						$datetimes_added,
546
+						$datetimes_removed
547
+					);
548
+					// are there any registrations using this ticket ?
549
+					$tickets_sold = $ticket->count_related(
550
+						'Registration',
551
+						[
552
+							[
553
+								'STS_ID' => ['NOT IN', [EEM_Registration::status_id_incomplete]],
554
+							],
555
+						]
556
+					);
557
+					// set ticket formats
558
+					$ticket->set_date_format($this->_date_format_strings['date']);
559
+					$ticket->set_time_format($this->_date_format_strings['time']);
560
+					// let's just check the total price for the existing ticket
561
+					// and determine if it matches the new total price.
562
+					// if they are different then we create a new ticket (if tickets sold)
563
+					// if they aren't different then we go ahead and modify existing ticket.
564
+					$create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
565
+					// set new values
566
+					foreach ($TKT_values as $field => $value) {
567
+						if ($field === 'TKT_qty') {
568
+							$ticket->set_qty($value);
569
+						} else {
570
+							$ticket->set($field, $value);
571
+						}
572
+					}
573
+					// if $create_new_TKT is false then we can safely update the existing ticket.
574
+					// Otherwise we have to create a new ticket.
575
+					if ($create_new_TKT) {
576
+						$new_ticket = $this->_duplicate_ticket(
577
+							$ticket,
578
+							$price_rows,
579
+							$ticket_price,
580
+							$base_price,
581
+							$base_price_id
582
+						);
583
+					}
584
+				}
585
+			} else {
586
+				// no TKT_id so a new TKT
587
+				$ticket = EE_Ticket::new_instance(
588
+					$TKT_values,
589
+					$timezone,
590
+					[$this->_date_format_strings['date'], $this->_date_format_strings['time']]
591
+				);
592
+				if ($ticket instanceof EE_Ticket) {
593
+					// make sure ticket has an ID of setting relations won't work
594
+					$ticket->save();
595
+					$ticket        = $this->_update_ticket_datetimes(
596
+						$ticket,
597
+						$saved_datetimes,
598
+						$datetimes_added,
599
+						$datetimes_removed
600
+					);
601
+					$update_prices = true;
602
+				}
603
+			}
604
+			// make sure any current values have been saved.
605
+			// $ticket->save();
606
+			// before going any further make sure our dates are setup correctly
607
+			// so that the end date is always equal or greater than the start date.
608
+			if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
609
+				$ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
610
+				$ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
611
+			}
612
+			// let's make sure the base price is handled
613
+			$ticket = ! $create_new_TKT
614
+				? $this->_add_prices_to_ticket(
615
+					[],
616
+					$ticket,
617
+					$update_prices,
618
+					$base_price,
619
+					$base_price_id
620
+				)
621
+				: $ticket;
622
+			// add/update price_modifiers
623
+			$ticket = ! $create_new_TKT
624
+				? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
625
+				: $ticket;
626
+			// need to make sue that the TKT_price is accurate after saving the prices.
627
+			$ticket->ensure_TKT_Price_correct();
628
+			// handle CREATING a default ticket from the incoming ticket but ONLY if this isn't an autosave.
629
+			if (! defined('DOING_AUTOSAVE') && ! empty($ticket_data['TKT_is_default_selector'])) {
630
+				$new_default = clone $ticket;
631
+				$new_default->set('TKT_ID', 0);
632
+				$new_default->set('TKT_is_default', 1);
633
+				$new_default->set('TKT_row', 1);
634
+				$new_default->set('TKT_price', $ticket_price);
635
+				// remove any datetime relations cause we DON'T want datetime relations attached
636
+				// (note this is just removing the cached relations in the object)
637
+				$new_default->_remove_relations('Datetime');
638
+				// @todo we need to add the current attached prices as new prices to the new default ticket.
639
+				$new_default = $this->_add_prices_to_ticket(
640
+					$price_rows,
641
+					$new_default,
642
+					true
643
+				);
644
+				// don't forget the base price!
645
+				$new_default = $this->_add_prices_to_ticket(
646
+					[],
647
+					$new_default,
648
+					true,
649
+					$base_price,
650
+					$base_price_id
651
+				);
652
+				$new_default->save();
653
+				do_action(
654
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
655
+					$new_default,
656
+					$row,
657
+					$ticket,
658
+					$data
659
+				);
660
+			}
661
+			// DO ALL datetime relationships for both current tickets and any archived tickets
662
+			// for the given datetime that are related to the current ticket.
663
+			// TODO... not sure exactly how we're going to do this considering we don't know
664
+			// what current ticket the archived tickets are related to
665
+			// (and TKT_parent is used for autosaves so that's not a field we can reliably use).
666
+			// let's assign any tickets that have been setup to the saved_tickets tracker
667
+			// save existing TKT
668
+			$ticket->save();
669
+			if ($create_new_TKT && $new_ticket instanceof EE_Ticket) {
670
+				// save new TKT
671
+				$new_ticket->save();
672
+				// add new ticket to array
673
+				$saved_tickets[ $new_ticket->ID() ] = $new_ticket;
674
+				do_action(
675
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
676
+					$new_ticket,
677
+					$row,
678
+					$ticket_data,
679
+					$data
680
+				);
681
+			} else {
682
+				// add ticket to saved tickets
683
+				$saved_tickets[ $ticket->ID() ] = $ticket;
684
+				do_action(
685
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
686
+					$ticket,
687
+					$row,
688
+					$ticket_data,
689
+					$data
690
+				);
691
+			}
692
+		}
693
+		// now we need to handle tickets actually "deleted permanently".
694
+		// There are cases where we'd want this to happen
695
+		// (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
696
+		// Or a draft event was saved and in the process of editing a ticket is trashed.
697
+		// No sense in keeping all the related data in the db!
698
+		$old_tickets     = isset($old_tickets[0]) && $old_tickets[0] === '' ? [] : $old_tickets;
699
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
700
+		foreach ($tickets_removed as $id) {
701
+			$id = absint($id);
702
+			// get the ticket for this id
703
+			$ticket_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
704
+			// if this ticket is a default ticket we leave it alone cause it won't be attached to the datetime
705
+			if ($ticket_to_remove->get('TKT_is_default')) {
706
+				continue;
707
+			}
708
+			// if this ticket has any registrations attached so then we just ARCHIVE
709
+			// because we don't actually permanently delete these tickets.
710
+			if ($ticket_to_remove->count_related('Registration') > 0) {
711
+				$ticket_to_remove->delete();
712
+				continue;
713
+			}
714
+			// need to get all the related datetimes on this ticket and remove from every single one of them
715
+			// (remember this process can ONLY kick off if there are NO tickets_sold)
716
+			$datetimes = $ticket_to_remove->get_many_related('Datetime');
717
+			foreach ($datetimes as $datetime) {
718
+				$ticket_to_remove->_remove_relation_to($datetime, 'Datetime');
719
+			}
720
+			// need to do the same for prices (except these prices can also be deleted because again,
721
+			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
722
+			$ticket_to_remove->delete_related_permanently('Price');
723
+			do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $ticket_to_remove);
724
+			// finally let's delete this ticket
725
+			// (which should not be blocked at this point b/c we've removed all our relationships)
726
+			$ticket_to_remove->delete_permanently();
727
+		}
728
+		return $saved_tickets;
729
+	}
730
+
731
+
732
+	/**
733
+	 * @access  protected
734
+	 * @param EE_Ticket     $ticket
735
+	 * @param EE_Datetime[] $saved_datetimes
736
+	 * @param int[]         $added_datetimes
737
+	 * @param int[]         $removed_datetimes
738
+	 * @return EE_Ticket
739
+	 * @throws EE_Error
740
+	 * @throws ReflectionException
741
+	 */
742
+	protected function _update_ticket_datetimes(
743
+		EE_Ticket $ticket,
744
+		array $saved_datetimes = [],
745
+		array $added_datetimes = [],
746
+		array $removed_datetimes = []
747
+	): EE_Ticket {
748
+		// to start we have to add the ticket to all the datetimes its supposed to be with,
749
+		// and removing the ticket from datetimes it got removed from.
750
+		// first let's add datetimes
751
+		if (! empty($added_datetimes) && is_array($added_datetimes)) {
752
+			foreach ($added_datetimes as $row_id) {
753
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
754
+					$ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
755
+					// Is this an existing ticket (has an ID) and does it have any sold?
756
+					// If so, then we need to add that to the DTT sold because this DTT is getting added.
757
+					if ($ticket->ID() && $ticket->sold() > 0) {
758
+						$saved_datetimes[ $row_id ]->increaseSold($ticket->sold(), false);
759
+					}
760
+				}
761
+			}
762
+		}
763
+		// then remove datetimes
764
+		if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
765
+			foreach ($removed_datetimes as $row_id) {
766
+				// its entirely possible that a datetime got deleted (instead of just removed from relationship.
767
+				// So make sure we skip over this if the datetime isn't in the $saved_datetimes array)
768
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
769
+					$ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
770
+				}
771
+			}
772
+		}
773
+		// cap ticket qty by datetime reg limits
774
+		$ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
775
+		return $ticket;
776
+	}
777
+
778
+
779
+	/**
780
+	 * @access  protected
781
+	 * @param EE_Ticket $ticket
782
+	 * @param array     $price_rows
783
+	 * @param int|float $ticket_price
784
+	 * @param int|float $base_price
785
+	 * @param int       $base_price_id
786
+	 * @return EE_Ticket
787
+	 * @throws ReflectionException
788
+	 * @throws InvalidArgumentException
789
+	 * @throws InvalidInterfaceException
790
+	 * @throws InvalidDataTypeException
791
+	 * @throws EE_Error
792
+	 */
793
+	protected function _duplicate_ticket(
794
+		EE_Ticket $ticket,
795
+		array $price_rows = [],
796
+		$ticket_price = 0,
797
+		$base_price = 0,
798
+		int $base_price_id = 0
799
+	): EE_Ticket {
800
+		// create new ticket that's a copy of the existing
801
+		// except a new id of course (and not archived)
802
+		// AND has the new TKT_price associated with it.
803
+		$new_ticket = clone $ticket;
804
+		$new_ticket->set('TKT_ID', 0);
805
+		$new_ticket->set_deleted(0);
806
+		$new_ticket->set_price($ticket_price);
807
+		$new_ticket->set_sold(0);
808
+		// let's get a new ID for this ticket
809
+		$new_ticket->save();
810
+		// we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
811
+		$datetimes_on_existing = $ticket->datetimes();
812
+		$new_ticket            = $this->_update_ticket_datetimes(
813
+			$new_ticket,
814
+			$datetimes_on_existing,
815
+			array_keys($datetimes_on_existing)
816
+		);
817
+		// $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
818
+		// if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
819
+		// available.
820
+		if ($ticket->sold() > 0) {
821
+			$new_qty = $ticket->qty() - $ticket->sold();
822
+			$new_ticket->set_qty($new_qty);
823
+		}
824
+		// now we update the prices just for this ticket
825
+		$new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
826
+		// and we update the base price
827
+		return $this->_add_prices_to_ticket(
828
+			[],
829
+			$new_ticket,
830
+			true,
831
+			$base_price,
832
+			$base_price_id
833
+		);
834
+	}
835
+
836
+
837
+	/**
838
+	 * This attaches a list of given prices to a ticket.
839
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
840
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
841
+	 * price info and prices are automatically "archived" via the ticket.
842
+	 *
843
+	 * @access  private
844
+	 * @param array     $prices        Array of prices from the form.
845
+	 * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
846
+	 * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
847
+	 * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
848
+	 * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
849
+	 * @return EE_Ticket
850
+	 * @throws ReflectionException
851
+	 * @throws InvalidArgumentException
852
+	 * @throws InvalidInterfaceException
853
+	 * @throws InvalidDataTypeException
854
+	 * @throws EE_Error
855
+	 */
856
+	protected function _add_prices_to_ticket(
857
+		array $prices,
858
+		EE_Ticket $ticket,
859
+		bool $new_prices = false,
860
+		$base_price = false,
861
+		$base_price_id = false
862
+	): EE_Ticket {
863
+		// let's just get any current prices that may exist on the given ticket
864
+		// so we can remove any prices that got trashed in this session.
865
+		$current_prices_on_ticket = $base_price !== false
866
+			? $ticket->base_price(true)
867
+			: $ticket->price_modifiers();
868
+		$updated_prices           = [];
869
+		// if $base_price ! FALSE then updating a base price.
870
+		if ($base_price !== false) {
871
+			$prices[1] = [
872
+				'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
873
+				'PRT_ID'     => 1,
874
+				'PRC_amount' => $base_price,
875
+				'PRC_name'   => $ticket->get('TKT_name'),
876
+				'PRC_desc'   => $ticket->get('TKT_description'),
877
+			];
878
+		}
879
+		// possibly need to save ticket
880
+		if (! $ticket->ID()) {
881
+			$ticket->save();
882
+		}
883
+		foreach ($prices as $row => $prc) {
884
+			$prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
885
+			if (empty($prt_id)) {
886
+				continue;
887
+			} //prices MUST have a price type id.
888
+			$PRC_values = [
889
+				'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
890
+				'PRT_ID'         => $prt_id,
891
+				'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
892
+				'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
893
+				'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
894
+				'PRC_is_default' => false,
895
+				// make sure we set PRC_is_default to false for all ticket saves from event_editor
896
+				'PRC_order'      => $row,
897
+			];
898
+			if ($new_prices || empty($PRC_values['PRC_ID'])) {
899
+				$PRC_values['PRC_ID'] = 0;
900
+				$price                = EE_Registry::instance()->load_class(
901
+					'Price',
902
+					[$PRC_values],
903
+					false,
904
+					false
905
+				);
906
+			} else {
907
+				$price = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
908
+				// update this price with new values
909
+				foreach ($PRC_values as $field => $value) {
910
+					$price->set($field, $value);
911
+				}
912
+			}
913
+			$price->save();
914
+			$updated_prices[ $price->ID() ] = $price;
915
+			$ticket->_add_relation_to($price, 'Price');
916
+		}
917
+		// now let's remove any prices that got removed from the ticket
918
+		if (! empty($current_prices_on_ticket)) {
919
+			$current          = array_keys($current_prices_on_ticket);
920
+			$updated          = array_keys($updated_prices);
921
+			$prices_to_remove = array_diff($current, $updated);
922
+			if (! empty($prices_to_remove)) {
923
+				foreach ($prices_to_remove as $prc_id) {
924
+					$p = $current_prices_on_ticket[ $prc_id ];
925
+					$ticket->_remove_relation_to($p, 'Price');
926
+					// delete permanently the price
927
+					$p->delete_permanently();
928
+				}
929
+			}
930
+		}
931
+		return $ticket;
932
+	}
933
+
934
+
935
+	/**
936
+	 * @throws ReflectionException
937
+	 * @throws InvalidArgumentException
938
+	 * @throws InvalidInterfaceException
939
+	 * @throws InvalidDataTypeException
940
+	 * @throws DomainException
941
+	 * @throws EE_Error
942
+	 */
943
+	public function pricing_metabox()
944
+	{
945
+		$existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = [];
946
+		$event                 = $this->_adminpage_obj->get_cpt_model_obj();
947
+
948
+		// set is_creating_event property.
949
+		$EVT_ID                   = $event->ID();
950
+		$this->_is_creating_event = empty($this->_req_data['post']);
951
+
952
+		// default main template args
953
+		$main_template_args = [
954
+			'event_datetime_help_link' => EEH_Template::get_help_tab_link(
955
+				'event_editor_event_datetimes_help_tab',
956
+				$this->_adminpage_obj->page_slug,
957
+				$this->_adminpage_obj->get_req_action()
958
+			),
959
+
960
+			// todo need to add a filter to the template for the help text
961
+			// in the Events_Admin_Page core file so we can add further help
962
+			'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
963
+				'add_new_dtt_info',
964
+				$this->_adminpage_obj->page_slug,
965
+				$this->_adminpage_obj->get_req_action()
966
+			),
967
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
968
+			'datetime_rows'            => '',
969
+			'show_tickets_container'   => '',
970
+			'ticket_rows'              => '',
971
+			'ee_collapsible_status'    => ' ee-collapsible-open',
972
+		];
973
+		$timezone           = $event instanceof EE_Event ? $event->timezone_string() : null;
974
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
975
+
976
+		/**
977
+		 * 1. Start with retrieving Datetimes
978
+		 * 2. For each datetime get related tickets
979
+		 * 3. For each ticket get related prices
980
+		 */
981
+		/** @var EEM_Datetime $datetime_model */
982
+		$datetime_model                       = EE_Registry::instance()->load_model('Datetime', [$timezone]);
983
+		$datetimes                            = $datetime_model->get_all_event_dates($EVT_ID);
984
+		$main_template_args['total_dtt_rows'] = count($datetimes);
985
+
986
+		/**
987
+		 * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
988
+		 * for why we are counting $datetime_row and then setting that on the Datetime object
989
+		 */
990
+		$datetime_row = 1;
991
+		foreach ($datetimes as $datetime) {
992
+			$DTT_ID = $datetime->get('DTT_ID');
993
+			$datetime->set('DTT_order', $datetime_row);
994
+			$existing_datetime_ids[] = $DTT_ID;
995
+			// tickets attached
996
+			$related_tickets = $datetime->ID() > 0
997
+				? $datetime->get_many_related(
998
+					'Ticket',
999
+					[
1000
+						[
1001
+							'OR' => ['TKT_deleted' => 1, 'TKT_deleted*' => 0],
1002
+						],
1003
+						'default_where_conditions' => 'none',
1004
+						'order_by'                 => ['TKT_order' => 'ASC'],
1005
+					]
1006
+				)
1007
+				: [];
1008
+			// if there are no related tickets this is likely a new event OR auto-draft
1009
+			// event so we need to generate the default tickets because datetimes
1010
+			// ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1011
+			// datetime on the event.
1012
+			if (empty($related_tickets) && count($datetimes) < 2) {
1013
+				/** @var EEM_Ticket $ticket_model */
1014
+				$ticket_model    = EE_Registry::instance()->load_model('Ticket');
1015
+				$related_tickets = $ticket_model->get_all_default_tickets();
1016
+				// this should be ordered by TKT_ID, so let's grab the first default ticket
1017
+				// (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1018
+				$default_prices      = EEM_Price::instance()->get_all_default_prices();
1019
+				$main_default_ticket = reset($related_tickets);
1020
+				if ($main_default_ticket instanceof EE_Ticket) {
1021
+					foreach ($default_prices as $default_price) {
1022
+						if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1023
+							continue;
1024
+						}
1025
+						$main_default_ticket->cache('Price', $default_price);
1026
+					}
1027
+				}
1028
+			}
1029
+			// we can't actually setup rows in this loop yet cause we don't know all
1030
+			// the unique tickets for this event yet (tickets are linked through all datetimes).
1031
+			// So we're going to temporarily cache some of that information.
1032
+			// loop through and setup the ticket rows and make sure the order is set.
1033
+			foreach ($related_tickets as $ticket) {
1034
+				$TKT_ID     = $ticket->get('TKT_ID');
1035
+				$ticket_row = $ticket->get('TKT_row');
1036
+				// we only want unique tickets in our final display!!
1037
+				if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1038
+					$existing_ticket_ids[] = $TKT_ID;
1039
+					$all_tickets[]         = $ticket;
1040
+				}
1041
+				// temporary cache of this ticket info for this datetime for later processing of datetime rows.
1042
+				$datetime_tickets[ $DTT_ID ][] = $ticket_row;
1043
+				// temporary cache of this datetime info for this ticket for later processing of ticket rows.
1044
+				if (
1045
+					! isset($ticket_datetimes[ $TKT_ID ])
1046
+					|| ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1047
+				) {
1048
+					$ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1049
+				}
1050
+			}
1051
+			$datetime_row++;
1052
+		}
1053
+		$main_template_args['total_ticket_rows']     = count($existing_ticket_ids);
1054
+		$main_template_args['existing_ticket_ids']   = implode(',', $existing_ticket_ids);
1055
+		$main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1056
+		// sort $all_tickets by order
1057
+		usort(
1058
+			$all_tickets,
1059
+			function (EE_Ticket $a, EE_Ticket $b) {
1060
+				$a_order = (int) $a->get('TKT_order');
1061
+				$b_order = (int) $b->get('TKT_order');
1062
+				if ($a_order === $b_order) {
1063
+					return 0;
1064
+				}
1065
+				return ($a_order < $b_order) ? -1 : 1;
1066
+			}
1067
+		);
1068
+		// k NOW we have all the data we need for setting up the datetime rows
1069
+		// and ticket rows so we start our datetime loop again.
1070
+		$datetime_row = 1;
1071
+		foreach ($datetimes as $datetime) {
1072
+			$main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1073
+				$datetime_row,
1074
+				$datetime,
1075
+				$datetime_tickets,
1076
+				$all_tickets,
1077
+				false,
1078
+				$datetimes
1079
+			);
1080
+			$datetime_row++;
1081
+		}
1082
+		// then loop through all tickets for the ticket rows.
1083
+		$ticket_row = 1;
1084
+		foreach ($all_tickets as $ticket) {
1085
+			$main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1086
+				$ticket_row,
1087
+				$ticket,
1088
+				$ticket_datetimes,
1089
+				$datetimes,
1090
+				false,
1091
+				$all_tickets
1092
+			);
1093
+			$ticket_row++;
1094
+		}
1095
+		$main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1096
+
1097
+		$status_change_notice = LoaderFactory::getLoader()->getShared(
1098
+			'EventEspresso\core\domain\services\admin\notices\status_change\StatusChangeNotice'
1099
+		);
1100
+
1101
+		$main_template_args['status_change_notice'] = $status_change_notice->display(
1102
+			'__event-editor',
1103
+			'espresso-events'
1104
+		);
1105
+
1106
+		EEH_Template::display_template(
1107
+			PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1108
+			$main_template_args
1109
+		);
1110
+	}
1111
+
1112
+
1113
+	/**
1114
+	 * @param int         $datetime_row
1115
+	 * @param EE_Datetime $datetime
1116
+	 * @param array       $datetime_tickets
1117
+	 * @param array       $all_tickets
1118
+	 * @param bool        $default
1119
+	 * @param array       $all_datetimes
1120
+	 * @return string
1121
+	 * @throws DomainException
1122
+	 * @throws EE_Error
1123
+	 * @throws ReflectionException
1124
+	 */
1125
+	protected function _get_datetime_row(
1126
+		int $datetime_row,
1127
+		EE_Datetime $datetime,
1128
+		array $datetime_tickets = [],
1129
+		array $all_tickets = [],
1130
+		bool $default = false,
1131
+		array $all_datetimes = []
1132
+	): string {
1133
+		return EEH_Template::display_template(
1134
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1135
+			[
1136
+				'dtt_edit_row'             => $this->_get_dtt_edit_row(
1137
+					$datetime_row,
1138
+					$datetime,
1139
+					$default,
1140
+					$all_datetimes
1141
+				),
1142
+				'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1143
+					$datetime_row,
1144
+					$datetime,
1145
+					$datetime_tickets,
1146
+					$all_tickets,
1147
+					$default
1148
+				),
1149
+				'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1150
+			],
1151
+			true
1152
+		);
1153
+	}
1154
+
1155
+
1156
+	/**
1157
+	 * This method is used to generate a datetime fields  edit row.
1158
+	 * The same row is used to generate a row with valid DTT objects
1159
+	 * and the default row that is used as the skeleton by the js.
1160
+	 *
1161
+	 * @param int              $datetime_row  The row number for the row being generated.
1162
+	 * @param EE_Datetime|null $datetime
1163
+	 * @param bool             $default       Whether a default row is being generated or not.
1164
+	 * @param EE_Datetime[]    $all_datetimes This is the array of all datetimes used in the editor.
1165
+	 * @return string
1166
+	 * @throws EE_Error
1167
+	 * @throws ReflectionException
1168
+	 */
1169
+	protected function _get_dtt_edit_row(
1170
+		int $datetime_row,
1171
+		?EE_Datetime $datetime,
1172
+		bool $default,
1173
+		array $all_datetimes
1174
+	): string {
1175
+		// if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1176
+		$default                     = ! $datetime instanceof EE_Datetime ? true : $default;
1177
+		$template_args               = [
1178
+			'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1179
+			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1180
+			'edit_dtt_expanded'    => '',
1181
+			'DTT_ID'               => $default ? '' : $datetime->ID(),
1182
+			'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1183
+			'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1184
+			'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1185
+			'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1186
+			'DTT_reg_limit'        => $default
1187
+				? ''
1188
+				: $datetime->get_pretty(
1189
+					'DTT_reg_limit',
1190
+					'input'
1191
+				),
1192
+			'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1193
+			'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1194
+			'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1195
+			'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1196
+				? ''
1197
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1198
+			'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1199
+				? 'dashicons dashicons-lock'
1200
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1201
+			'reg_list_url'         => $default || ! $datetime->event() instanceof EE_Event
1202
+				? ''
1203
+				: EE_Admin_Page::add_query_args_and_nonce(
1204
+					[
1205
+						'event_id' => $datetime->event()->ID(),
1206
+						'datetime_id' => $datetime->ID(),
1207
+						'use_filters' => true
1208
+					],
1209
+					REG_ADMIN_URL
1210
+				),
1211
+		];
1212
+		$template_args['show_trash'] = count($all_datetimes) === 1
1213
+									   && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1214
+			? 'display:none'
1215
+			: '';
1216
+		// allow filtering of template args at this point.
1217
+		$template_args = apply_filters(
1218
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1219
+			$template_args,
1220
+			$datetime_row,
1221
+			$datetime,
1222
+			$default,
1223
+			$all_datetimes,
1224
+			$this->_is_creating_event
1225
+		);
1226
+		return EEH_Template::display_template(
1227
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1228
+			$template_args,
1229
+			true
1230
+		);
1231
+	}
1232
+
1233
+
1234
+	/**
1235
+	 * @param int         $datetime_row
1236
+	 * @param EE_Datetime $datetime
1237
+	 * @param array       $datetime_tickets
1238
+	 * @param array       $all_tickets
1239
+	 * @param bool        $default
1240
+	 * @return string
1241
+	 * @throws DomainException
1242
+	 * @throws EE_Error
1243
+	 * @throws ReflectionException
1244
+	 */
1245
+	protected function _get_dtt_attached_tickets_row(
1246
+		int $datetime_row,
1247
+		EE_Datetime $datetime,
1248
+		array $datetime_tickets = [],
1249
+		array $all_tickets = [],
1250
+		bool $default = false
1251
+	): string {
1252
+		$template_args = [
1253
+			'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1254
+			'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1255
+			'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1256
+			'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1257
+			'show_tickets_row'                  => 'display:none;',
1258
+			'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1259
+				'add_new_ticket_via_datetime',
1260
+				$this->_adminpage_obj->page_slug,
1261
+				$this->_adminpage_obj->get_req_action()
1262
+			),
1263
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1264
+			'DTT_ID'                            => $default ? '' : $datetime->ID(),
1265
+		];
1266
+		// need to setup the list items (but only if this isn't a default skeleton setup)
1267
+		if (! $default) {
1268
+			$ticket_row = 1;
1269
+			foreach ($all_tickets as $ticket) {
1270
+				$template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1271
+					$datetime_row,
1272
+					$ticket_row,
1273
+					$datetime,
1274
+					$ticket,
1275
+					$datetime_tickets,
1276
+					$default
1277
+				);
1278
+				$ticket_row++;
1279
+			}
1280
+		}
1281
+		// filter template args at this point
1282
+		$template_args = apply_filters(
1283
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1284
+			$template_args,
1285
+			$datetime_row,
1286
+			$datetime,
1287
+			$datetime_tickets,
1288
+			$all_tickets,
1289
+			$default,
1290
+			$this->_is_creating_event
1291
+		);
1292
+		return EEH_Template::display_template(
1293
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1294
+			$template_args,
1295
+			true
1296
+		);
1297
+	}
1298
+
1299
+
1300
+	/**
1301
+	 * @param int              $datetime_row
1302
+	 * @param int              $ticket_row
1303
+	 * @param EE_Datetime|null $datetime
1304
+	 * @param EE_Ticket|null   $ticket
1305
+	 * @param array            $datetime_tickets
1306
+	 * @param bool             $default
1307
+	 * @return string
1308
+	 * @throws EE_Error
1309
+	 * @throws ReflectionException
1310
+	 */
1311
+	protected function _get_datetime_tickets_list_item(
1312
+		int $datetime_row,
1313
+		int $ticket_row,
1314
+		?EE_Datetime $datetime,
1315
+		?EE_Ticket $ticket,
1316
+		array $datetime_tickets = [],
1317
+		bool $default = false
1318
+	): string {
1319
+		$datetime_tickets = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1320
+			? $datetime_tickets[ $datetime->ID() ]
1321
+			: [];
1322
+		$display_row      = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1323
+		$no_ticket        = $default && empty($ticket);
1324
+		$template_args    = [
1325
+			'dtt_row'                 => $default
1326
+				? 'DTTNUM'
1327
+				: $datetime_row,
1328
+			'tkt_row'                 => $no_ticket
1329
+				? 'TICKETNUM'
1330
+				: $ticket_row,
1331
+			'datetime_ticket_checked' => in_array($display_row, $datetime_tickets, true)
1332
+				? ' checked'
1333
+				: '',
1334
+			'ticket_selected'         => in_array($display_row, $datetime_tickets, true)
1335
+				? ' ticket-selected'
1336
+				: '',
1337
+			'TKT_name'                => $no_ticket
1338
+				? 'TKTNAME'
1339
+				: $ticket->get('TKT_name'),
1340
+			'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1341
+				? ' tkt-status-' . EE_Ticket::onsale
1342
+				: ' tkt-status-' . $ticket->ticket_status(),
1343
+		];
1344
+		// filter template args
1345
+		$template_args = apply_filters(
1346
+			'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1347
+			$template_args,
1348
+			$datetime_row,
1349
+			$ticket_row,
1350
+			$datetime,
1351
+			$ticket,
1352
+			$datetime_tickets,
1353
+			$default,
1354
+			$this->_is_creating_event
1355
+		);
1356
+		return EEH_Template::display_template(
1357
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1358
+			$template_args,
1359
+			true
1360
+		);
1361
+	}
1362
+
1363
+
1364
+	/**
1365
+	 * This generates the ticket row for tickets.
1366
+	 * This same method is used to generate both the actual rows and the js skeleton row
1367
+	 * (when default === true)
1368
+	 *
1369
+	 * @param int            $ticket_row       Represents the row number being generated.
1370
+	 * @param EE_Ticket|null $ticket
1371
+	 * @param EE_Datetime[]  $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1372
+	 *                                         or empty for default
1373
+	 * @param EE_Datetime[]  $all_datetimes    All Datetimes on the event or empty for default.
1374
+	 * @param bool           $default          Whether default row being generated or not.
1375
+	 * @param EE_Ticket[]    $all_tickets      This is an array of all tickets attached to the event
1376
+	 *                                         (or empty in the case of defaults)
1377
+	 * @return string
1378
+	 * @throws InvalidArgumentException
1379
+	 * @throws InvalidInterfaceException
1380
+	 * @throws InvalidDataTypeException
1381
+	 * @throws DomainException
1382
+	 * @throws EE_Error
1383
+	 * @throws ReflectionException
1384
+	 */
1385
+	protected function _get_ticket_row(
1386
+		int $ticket_row,
1387
+		?EE_Ticket $ticket,
1388
+		array $ticket_datetimes,
1389
+		array $all_datetimes,
1390
+		bool $default = false,
1391
+		array $all_tickets = []
1392
+	): string {
1393
+		// if $ticket is not an instance of EE_Ticket then force default to true.
1394
+		$default = ! $ticket instanceof EE_Ticket ? true : $default;
1395
+		$prices  = ! empty($ticket) && ! $default
1396
+			? $ticket->get_many_related(
1397
+				'Price',
1398
+				['default_where_conditions' => 'none', 'order_by' => ['PRC_order' => 'ASC']]
1399
+			)
1400
+			: [];
1401
+		// if there is only one price (which would be the base price)
1402
+		// or NO prices and this ticket is a default ticket,
1403
+		// let's just make sure there are no cached default prices on the object.
1404
+		// This is done by not including any query_params.
1405
+		if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1406
+			$prices = $ticket->prices();
1407
+		}
1408
+		// check if we're dealing with a default ticket in which case
1409
+		// we don't want any starting_ticket_datetime_row values set
1410
+		// (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1411
+		// This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1412
+		$default_datetime = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1413
+		$ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1414
+			? $ticket_datetimes[ $ticket->ID() ]
1415
+			: [];
1416
+		$ticket_subtotal  = $default ? 0 : $ticket->get_ticket_subtotal();
1417
+		$base_price       = $default ? null : $ticket->base_price();
1418
+		$count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1419
+		// breaking out complicated condition for ticket_status
1420
+		if ($default) {
1421
+			$ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1422
+		} else {
1423
+			$ticket_status_class = $ticket->is_default()
1424
+				? ' tkt-status-' . EE_Ticket::onsale
1425
+				: ' tkt-status-' . $ticket->ticket_status();
1426
+		}
1427
+		// breaking out complicated condition for TKT_taxable
1428
+		if ($default) {
1429
+			$TKT_taxable = '';
1430
+		} else {
1431
+			$TKT_taxable = $ticket->taxable()
1432
+				? 'checked'
1433
+				: '';
1434
+		}
1435
+		if ($default) {
1436
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1437
+		} elseif ($ticket->is_default()) {
1438
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1439
+		} else {
1440
+			$TKT_status = $ticket->ticket_status(true);
1441
+		}
1442
+		if ($default) {
1443
+			$TKT_min = '';
1444
+		} else {
1445
+			$TKT_min = $ticket->min();
1446
+			if ($TKT_min === -1 || $TKT_min === 0) {
1447
+				$TKT_min = '';
1448
+			}
1449
+		}
1450
+		$template_args                 = [
1451
+			'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1452
+			'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1453
+			// on initial page load this will always be the correct order.
1454
+			'tkt_status_class'              => $ticket_status_class,
1455
+			'display_edit_tkt_row'          => 'display:none;',
1456
+			'edit_tkt_expanded'             => '',
1457
+			'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1458
+			'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1459
+			'TKT_start_date'                => $default
1460
+				? ''
1461
+				: $ticket->get_date('TKT_start_date', $this->_date_time_format),
1462
+			'TKT_end_date'                  => $default
1463
+				? ''
1464
+				: $ticket->get_date('TKT_end_date', $this->_date_time_format),
1465
+			'TKT_status'                    => $TKT_status,
1466
+			'TKT_price'                     => $default
1467
+				? ''
1468
+				: EEH_Template::format_currency(
1469
+					$ticket->get_ticket_total_with_taxes(),
1470
+					false,
1471
+					false
1472
+				),
1473
+			'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1474
+			'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1475
+			'TKT_qty'                       => $default
1476
+				? ''
1477
+				: $ticket->get_pretty('TKT_qty', 'symbol'),
1478
+			'TKT_qty_for_input'             => $default
1479
+				? ''
1480
+				: $ticket->get_pretty('TKT_qty', 'input'),
1481
+			'TKT_uses'                      => $default
1482
+				? ''
1483
+				: $ticket->get_pretty('TKT_uses', 'input'),
1484
+			'TKT_min'                       => $TKT_min,
1485
+			'TKT_max'                       => $default
1486
+				? ''
1487
+				: $ticket->get_pretty('TKT_max', 'input'),
1488
+			'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold(),
1489
+			'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1490
+			'TKT_registrations'             => $default
1491
+				? 0
1492
+				: $ticket->count_registrations(
1493
+					[
1494
+						[
1495
+							'STS_ID' => [
1496
+								'!=',
1497
+								EEM_Registration::status_id_incomplete,
1498
+							],
1499
+						],
1500
+					]
1501
+				),
1502
+			'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1503
+			'TKT_description'               => $default ? '' : $ticket->get_f('TKT_description'),
1504
+			'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1505
+			'TKT_required'                  => $default ? 0 : $ticket->required(),
1506
+			'TKT_is_default_selector'       => '',
1507
+			'ticket_price_rows'             => '',
1508
+			'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1509
+				? ''
1510
+				: $base_price->get_pretty('PRC_amount', 'localized_float'),
1511
+			'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1512
+			'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1513
+				? ''
1514
+				: 'display:none;',
1515
+			'show_price_mod_button'         => count($prices) > 1
1516
+											   || ($default && $count_price_mods > 0)
1517
+											   || (! $default && $ticket->deleted())
1518
+				? 'display:none;'
1519
+				: '',
1520
+			'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1521
+			'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1522
+			'starting_ticket_datetime_rows' => $default || $default_datetime ? '' : implode(',', $ticket_datetimes),
1523
+			'ticket_datetime_rows'          => $default ? '' : implode(',', $ticket_datetimes),
1524
+			'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1525
+			'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1526
+			'TKT_taxable'                   => $TKT_taxable,
1527
+			'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1528
+				? ''
1529
+				: 'display:none;',
1530
+			'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1531
+			'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1532
+				$ticket_subtotal,
1533
+				false,
1534
+				false
1535
+			),
1536
+			'TKT_subtotal_amount'           => $ticket_subtotal,
1537
+			'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1538
+			'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1539
+			'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1540
+				? ' ticket-archived'
1541
+				: '',
1542
+			'trash_icon'                    => $ticket instanceof EE_Ticket
1543
+											   && $ticket->deleted()
1544
+											   && ! $ticket->is_permanently_deleteable()
1545
+				? 'dashicons dashicons-lock '
1546
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1547
+			'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1548
+				? ''
1549
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1550
+		];
1551
+		$template_args['trash_hidden'] = count($all_tickets) === 1
1552
+										 && $template_args['trash_icon'] !== 'dashicons dashicons-lock'
1553
+			? 'display:none'
1554
+			: '';
1555
+		// handle rows that should NOT be empty
1556
+		if (empty($template_args['TKT_start_date'])) {
1557
+			// if empty then the start date will be now.
1558
+			$template_args['TKT_start_date']   = date(
1559
+				$this->_date_time_format,
1560
+				current_time('timestamp')
1561
+			);
1562
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1563
+		}
1564
+		if (empty($template_args['TKT_end_date'])) {
1565
+			// get the earliest datetime (if present);
1566
+			$earliest_datetime = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1567
+				? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1568
+					'Datetime',
1569
+					['order_by' => ['DTT_EVT_start' => 'ASC']]
1570
+				)
1571
+				: null;
1572
+			if (! empty($earliest_datetime)) {
1573
+				$template_args['TKT_end_date'] = $earliest_datetime->get_datetime(
1574
+					'DTT_EVT_start',
1575
+					$this->_date_time_format
1576
+				);
1577
+			} else {
1578
+				// default so let's just use what's been set for the default date-time which is 30 days from now.
1579
+				$template_args['TKT_end_date'] = date(
1580
+					$this->_date_time_format,
1581
+					mktime(
1582
+						24,
1583
+						0,
1584
+						0,
1585
+						date('m'),
1586
+						date('d') + 29,
1587
+						date('Y')
1588
+					)
1589
+				);
1590
+			}
1591
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1592
+		}
1593
+		// generate ticket_datetime items
1594
+		if (! $default) {
1595
+			$datetime_row = 1;
1596
+			foreach ($all_datetimes as $datetime) {
1597
+				$template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1598
+					$datetime_row,
1599
+					$ticket_row,
1600
+					$datetime,
1601
+					$ticket,
1602
+					$ticket_datetimes,
1603
+					$default
1604
+				);
1605
+				$datetime_row++;
1606
+			}
1607
+		}
1608
+		$price_row = 1;
1609
+		foreach ($prices as $price) {
1610
+			if (! $price instanceof EE_Price) {
1611
+				continue;
1612
+			}
1613
+			if ($price->is_base_price()) {
1614
+				$price_row++;
1615
+				continue;
1616
+			}
1617
+
1618
+			$show_trash  = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1619
+			$show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1620
+
1621
+			$template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1622
+				$ticket_row,
1623
+				$price_row,
1624
+				$price,
1625
+				$default,
1626
+				$ticket,
1627
+				$show_trash,
1628
+				$show_create
1629
+			);
1630
+			$price_row++;
1631
+		}
1632
+		// filter $template_args
1633
+		$template_args = apply_filters(
1634
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1635
+			$template_args,
1636
+			$ticket_row,
1637
+			$ticket,
1638
+			$ticket_datetimes,
1639
+			$all_datetimes,
1640
+			$default,
1641
+			$all_tickets,
1642
+			$this->_is_creating_event
1643
+		);
1644
+		return EEH_Template::display_template(
1645
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1646
+			$template_args,
1647
+			true
1648
+		);
1649
+	}
1650
+
1651
+
1652
+	/**
1653
+	 * @param int            $ticket_row
1654
+	 * @param EE_Ticket|null $ticket
1655
+	 * @return string
1656
+	 * @throws DomainException
1657
+	 * @throws EE_Error
1658
+	 * @throws ReflectionException
1659
+	 */
1660
+	protected function _get_tax_rows(int $ticket_row, ?EE_Ticket $ticket): string
1661
+	{
1662
+		$tax_rows = '';
1663
+		/** @var EE_Price[] $taxes */
1664
+		$taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1665
+		foreach ($taxes as $tax) {
1666
+			$tax_added     = $this->_get_tax_added($tax, $ticket);
1667
+			$template_args = [
1668
+				'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1669
+					? ''
1670
+					: 'display:none;',
1671
+				'tax_id'            => $tax->ID(),
1672
+				'tkt_row'           => $ticket_row,
1673
+				'tax_label'         => $tax->get('PRC_name'),
1674
+				'tax_added'         => $tax_added,
1675
+				'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1676
+				'tax_amount'        => $tax->get('PRC_amount'),
1677
+			];
1678
+			$template_args = apply_filters(
1679
+				'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1680
+				$template_args,
1681
+				$ticket_row,
1682
+				$ticket,
1683
+				$this->_is_creating_event
1684
+			);
1685
+			$tax_rows      .= EEH_Template::display_template(
1686
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1687
+				$template_args,
1688
+				true
1689
+			);
1690
+		}
1691
+		return $tax_rows;
1692
+	}
1693
+
1694
+
1695
+	/**
1696
+	 * @param EE_Price       $tax
1697
+	 * @param EE_Ticket|null $ticket
1698
+	 * @return float|int
1699
+	 * @throws EE_Error
1700
+	 * @throws ReflectionException
1701
+	 */
1702
+	protected function _get_tax_added(EE_Price $tax, ?EE_Ticket $ticket)
1703
+	{
1704
+		$subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1705
+		return $subtotal * $tax->get('PRC_amount') / 100;
1706
+	}
1707
+
1708
+
1709
+	/**
1710
+	 * @param int            $ticket_row
1711
+	 * @param int            $price_row
1712
+	 * @param EE_Price|null  $price
1713
+	 * @param bool           $default
1714
+	 * @param EE_Ticket|null $ticket
1715
+	 * @param bool           $show_trash
1716
+	 * @param bool           $show_create
1717
+	 * @return string
1718
+	 * @throws InvalidArgumentException
1719
+	 * @throws InvalidInterfaceException
1720
+	 * @throws InvalidDataTypeException
1721
+	 * @throws DomainException
1722
+	 * @throws EE_Error
1723
+	 * @throws ReflectionException
1724
+	 */
1725
+	protected function _get_ticket_price_row(
1726
+		int $ticket_row,
1727
+		int $price_row,
1728
+		?EE_Price $price,
1729
+		bool $default,
1730
+		?EE_Ticket $ticket,
1731
+		bool $show_trash = true,
1732
+		bool $show_create = true
1733
+	): string {
1734
+		$send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1735
+		$template_args = [
1736
+			'tkt_row'               => $default && empty($ticket)
1737
+				? 'TICKETNUM'
1738
+				: $ticket_row,
1739
+			'PRC_order'             => $default && empty($price)
1740
+				? 'PRICENUM'
1741
+				: $price_row,
1742
+			'edit_prices_name'      => $default && empty($price)
1743
+				? 'PRICENAMEATTR'
1744
+				: 'edit_prices',
1745
+			'price_type_selector'   => $default && empty($price)
1746
+				? $this->_get_base_price_template($ticket_row, $price_row, $price, true)
1747
+				: $this->_get_price_type_selector(
1748
+					$ticket_row,
1749
+					$price_row,
1750
+					$price,
1751
+					$default,
1752
+					$send_disabled
1753
+				),
1754
+			'PRC_ID'                => $default && empty($price)
1755
+				? 0
1756
+				: $price->ID(),
1757
+			'PRC_is_default'        => $default && empty($price)
1758
+				? 0
1759
+				: $price->get('PRC_is_default'),
1760
+			'PRC_name'              => $default && empty($price)
1761
+				? ''
1762
+				: $price->get('PRC_name'),
1763
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1764
+			'show_plus_or_minus'    => $default && empty($price)
1765
+				? ''
1766
+				: 'display:none;',
1767
+			'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1768
+				? 'display:none;'
1769
+				: '',
1770
+			'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1771
+				? 'display:none;'
1772
+				: '',
1773
+			'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1774
+				? 'display:none'
1775
+				: '',
1776
+			'PRC_amount'            => $default && empty($price)
1777
+				? 0
1778
+				: $price->get_pretty('PRC_amount', 'localized_float'),
1779
+			'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1780
+				? 'display:none;'
1781
+				: '',
1782
+			'show_trash_icon'       => $show_trash
1783
+				? ''
1784
+				: ' style="display:none;"',
1785
+			'show_create_button'    => $show_create
1786
+				? ''
1787
+				: ' style="display:none;"',
1788
+			'PRC_desc'              => $default && empty($price)
1789
+				? ''
1790
+				: $price->get('PRC_desc'),
1791
+			'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1792
+		];
1793
+		$template_args = apply_filters(
1794
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1795
+			$template_args,
1796
+			$ticket_row,
1797
+			$price_row,
1798
+			$price,
1799
+			$default,
1800
+			$ticket,
1801
+			$show_trash,
1802
+			$show_create,
1803
+			$this->_is_creating_event
1804
+		);
1805
+		return EEH_Template::display_template(
1806
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1807
+			$template_args,
1808
+			true
1809
+		);
1810
+	}
1811
+
1812
+
1813
+	/**
1814
+	 * @param int           $ticket_row
1815
+	 * @param int           $price_row
1816
+	 * @param EE_Price|null $price
1817
+	 * @param bool          $default
1818
+	 * @param bool          $disabled
1819
+	 * @return string
1820
+	 * @throws ReflectionException
1821
+	 * @throws InvalidArgumentException
1822
+	 * @throws InvalidInterfaceException
1823
+	 * @throws InvalidDataTypeException
1824
+	 * @throws DomainException
1825
+	 * @throws EE_Error
1826
+	 */
1827
+	protected function _get_price_type_selector(
1828
+		int $ticket_row,
1829
+		int $price_row,
1830
+		?EE_Price $price,
1831
+		bool $default,
1832
+		bool $disabled = false
1833
+	): string {
1834
+		if ($price->is_base_price()) {
1835
+			return $this->_get_base_price_template(
1836
+				$ticket_row,
1837
+				$price_row,
1838
+				$price,
1839
+				$default
1840
+			);
1841
+		}
1842
+		return $this->_get_price_modifier_template(
1843
+			$ticket_row,
1844
+			$price_row,
1845
+			$price,
1846
+			$default,
1847
+			$disabled
1848
+		);
1849
+	}
1850
+
1851
+
1852
+	/**
1853
+	 * @param int           $ticket_row
1854
+	 * @param int           $price_row
1855
+	 * @param EE_Price|null $price
1856
+	 * @param bool          $default
1857
+	 * @return string
1858
+	 * @throws DomainException
1859
+	 * @throws EE_Error
1860
+	 * @throws ReflectionException
1861
+	 */
1862
+	protected function _get_base_price_template(
1863
+		int $ticket_row,
1864
+		int $price_row,
1865
+		?EE_Price $price,
1866
+		bool $default
1867
+	): string {
1868
+		$template_args = [
1869
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1870
+			'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1871
+			'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1872
+			'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1873
+			'price_selected_operator'   => '+',
1874
+			'price_selected_is_percent' => 0,
1875
+		];
1876
+		$template_args = apply_filters(
1877
+			'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1878
+			$template_args,
1879
+			$ticket_row,
1880
+			$price_row,
1881
+			$price,
1882
+			$default,
1883
+			$this->_is_creating_event
1884
+		);
1885
+		return EEH_Template::display_template(
1886
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1887
+			$template_args,
1888
+			true
1889
+		);
1890
+	}
1891
+
1892
+
1893
+	/**
1894
+	 * @param int           $ticket_row
1895
+	 * @param int           $price_row
1896
+	 * @param EE_Price|null $price
1897
+	 * @param bool          $default
1898
+	 * @param bool          $disabled
1899
+	 * @return string
1900
+	 * @throws ReflectionException
1901
+	 * @throws InvalidArgumentException
1902
+	 * @throws InvalidInterfaceException
1903
+	 * @throws InvalidDataTypeException
1904
+	 * @throws DomainException
1905
+	 * @throws EE_Error
1906
+	 */
1907
+	protected function _get_price_modifier_template(
1908
+		int $ticket_row,
1909
+		int $price_row,
1910
+		?EE_Price $price,
1911
+		bool $default,
1912
+		bool $disabled = false
1913
+	): string {
1914
+		$select_name = $default && ! $price instanceof EE_Price
1915
+			? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1916
+			: 'edit_prices[' . esc_attr($ticket_row) . '][' . esc_attr($price_row) . '][PRT_ID]';
1917
+		/** @var EEM_Price_Type $price_type_model */
1918
+		$price_type_model       = EE_Registry::instance()->load_model('Price_Type');
1919
+		$price_types            = $price_type_model->get_all(
1920
+			[
1921
+				[
1922
+					'OR' => [
1923
+						'PBT_ID'  => '2',
1924
+						'PBT_ID*' => '3',
1925
+					],
1926
+				],
1927
+			]
1928
+		);
1929
+		$all_price_types        = $default && ! $price instanceof EE_Price
1930
+			? [esc_html__('Select Modifier', 'event_espresso')]
1931
+			: [];
1932
+		$selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1933
+		$price_option_spans     = '';
1934
+		// setup price types for selector
1935
+		foreach ($price_types as $price_type) {
1936
+			if (! $price_type instanceof EE_Price_Type) {
1937
+				continue;
1938
+			}
1939
+			$all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1940
+			// while we're in the loop let's setup the option spans used by js
1941
+			$span_args          = [
1942
+				'PRT_ID'         => $price_type->ID(),
1943
+				'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1944
+				'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1945
+			];
1946
+			$price_option_spans .= EEH_Template::display_template(
1947
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1948
+				$span_args,
1949
+				true
1950
+			);
1951
+		}
1952
+
1953
+		$select_name = $disabled
1954
+			? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1955
+			: $select_name;
1956
+
1957
+		$select_input = new EE_Select_Input(
1958
+			$all_price_types,
1959
+			[
1960
+				'default'               => $selected_price_type_id,
1961
+				'html_name'             => $select_name,
1962
+				'html_class'            => 'edit-price-PRT_ID',
1963
+				'other_html_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1964
+			]
1965
+		);
1966
+
1967
+		$price_selected_operator   = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1968
+		$price_selected_operator   = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1969
+		$price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1970
+		$price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1971
+		$template_args             = [
1972
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1973
+			'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1974
+			'price_modifier_selector'   => $select_input->get_html_for_input(),
1975
+			'main_name'                 => $select_name,
1976
+			'selected_price_type_id'    => $selected_price_type_id,
1977
+			'price_option_spans'        => $price_option_spans,
1978
+			'price_selected_operator'   => $price_selected_operator,
1979
+			'price_selected_is_percent' => $price_selected_is_percent,
1980
+			'disabled'                  => $disabled,
1981
+		];
1982
+		$template_args             = apply_filters(
1983
+			'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1984
+			$template_args,
1985
+			$ticket_row,
1986
+			$price_row,
1987
+			$price,
1988
+			$default,
1989
+			$disabled,
1990
+			$this->_is_creating_event
1991
+		);
1992
+		return EEH_Template::display_template(
1993
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
1994
+			$template_args,
1995
+			true
1996
+		);
1997
+	}
1998
+
1999
+
2000
+	/**
2001
+	 * @param int              $datetime_row
2002
+	 * @param int              $ticket_row
2003
+	 * @param EE_Datetime|null $datetime
2004
+	 * @param EE_Ticket|null   $ticket
2005
+	 * @param array            $ticket_datetimes
2006
+	 * @param bool             $default
2007
+	 * @return string
2008
+	 * @throws DomainException
2009
+	 * @throws EE_Error
2010
+	 * @throws ReflectionException
2011
+	 */
2012
+	protected function _get_ticket_datetime_list_item(
2013
+		int $datetime_row,
2014
+		int $ticket_row,
2015
+		?EE_Datetime $datetime,
2016
+		?EE_Ticket $ticket,
2017
+		array $ticket_datetimes = [],
2018
+		bool $default = false
2019
+	): string {
2020
+		$ticket_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2021
+			? $ticket_datetimes[ $ticket->ID() ]
2022
+			: [];
2023
+		$template_args    = [
2024
+			'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2025
+				? 'DTTNUM'
2026
+				: $datetime_row,
2027
+			'tkt_row'                  => $default
2028
+				? 'TICKETNUM'
2029
+				: $ticket_row,
2030
+			'ticket_datetime_selected' => in_array($datetime_row, $ticket_datetimes, true)
2031
+				? ' ticket-selected'
2032
+				: '',
2033
+			'ticket_datetime_checked'  => in_array($datetime_row, $ticket_datetimes, true)
2034
+				? ' checked'
2035
+				: '',
2036
+			'DTT_name'                 => $default && empty($datetime)
2037
+				? 'DTTNAME'
2038
+				: $datetime->get_dtt_display_name(true),
2039
+			'tkt_status_class'         => '',
2040
+		];
2041
+		$template_args    = apply_filters(
2042
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2043
+			$template_args,
2044
+			$datetime_row,
2045
+			$ticket_row,
2046
+			$datetime,
2047
+			$ticket,
2048
+			$ticket_datetimes,
2049
+			$default,
2050
+			$this->_is_creating_event
2051
+		);
2052
+		return EEH_Template::display_template(
2053
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2054
+			$template_args,
2055
+			true
2056
+		);
2057
+	}
2058
+
2059
+
2060
+	/**
2061
+	 * @param array $all_datetimes
2062
+	 * @param array $all_tickets
2063
+	 * @return string
2064
+	 * @throws ReflectionException
2065
+	 * @throws InvalidArgumentException
2066
+	 * @throws InvalidInterfaceException
2067
+	 * @throws InvalidDataTypeException
2068
+	 * @throws DomainException
2069
+	 * @throws EE_Error
2070
+	 */
2071
+	protected function _get_ticket_js_structure(array $all_datetimes = [], array $all_tickets = []): string
2072
+	{
2073
+		$template_args = [
2074
+			'default_datetime_edit_row' => $this->_get_dtt_edit_row(
2075
+				'DTTNUM',
2076
+				null,
2077
+				true,
2078
+				$all_datetimes
2079
+			),
2080
+			'default_ticket_row'        => $this->_get_ticket_row(
2081
+				'TICKETNUM',
2082
+				null,
2083
+				[],
2084
+				[],
2085
+				true
2086
+			),
2087
+			'default_price_row'         => $this->_get_ticket_price_row(
2088
+				'TICKETNUM',
2089
+				'PRICENUM',
2090
+				null,
2091
+				true,
2092
+				null
2093
+			),
2094
+
2095
+			'default_price_rows'                       => '',
2096
+			'default_base_price_amount'                => 0,
2097
+			'default_base_price_name'                  => '',
2098
+			'default_base_price_description'           => '',
2099
+			'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2100
+				'TICKETNUM',
2101
+				'PRICENUM',
2102
+				null,
2103
+				true
2104
+			),
2105
+			'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2106
+				'DTTNUM',
2107
+				null,
2108
+				[],
2109
+				[],
2110
+				true
2111
+			),
2112
+			'existing_available_datetime_tickets_list' => '',
2113
+			'existing_available_ticket_datetimes_list' => '',
2114
+			'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2115
+				'DTTNUM',
2116
+				'TICKETNUM',
2117
+				null,
2118
+				null,
2119
+				[],
2120
+				true
2121
+			),
2122
+			'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2123
+				'DTTNUM',
2124
+				'TICKETNUM',
2125
+				null,
2126
+				null,
2127
+				[],
2128
+				true
2129
+			),
2130
+		];
2131
+		$ticket_row    = 1;
2132
+		foreach ($all_tickets as $ticket) {
2133
+			$template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2134
+				'DTTNUM',
2135
+				$ticket_row,
2136
+				null,
2137
+				$ticket,
2138
+				[],
2139
+				true
2140
+			);
2141
+			$ticket_row++;
2142
+		}
2143
+		$datetime_row = 1;
2144
+		foreach ($all_datetimes as $datetime) {
2145
+			$template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2146
+				$datetime_row,
2147
+				'TICKETNUM',
2148
+				$datetime,
2149
+				null,
2150
+				[],
2151
+				true
2152
+			);
2153
+			$datetime_row++;
2154
+		}
2155
+		/** @var EEM_Price $price_model */
2156
+		$price_model    = EE_Registry::instance()->load_model('Price');
2157
+		$default_prices = $price_model->get_all_default_prices();
2158
+		$price_row      = 1;
2159
+		foreach ($default_prices as $price) {
2160
+			if (! $price instanceof EE_Price) {
2161
+				continue;
2162
+			}
2163
+			if ($price->is_base_price()) {
2164
+				$template_args['default_base_price_amount']      = $price->get_pretty(
2165
+					'PRC_amount',
2166
+					'localized_float'
2167
+				);
2168
+				$template_args['default_base_price_name']        = $price->get('PRC_name');
2169
+				$template_args['default_base_price_description'] = $price->get('PRC_desc');
2170
+				$price_row++;
2171
+				continue;
2172
+			}
2173
+
2174
+			$show_trash  = ! ((count($default_prices) > 1 && $price_row === 1) || count($default_prices) === 1);
2175
+			$show_create = ! (count($default_prices) > 1 && count($default_prices) !== $price_row);
2176
+
2177
+			$template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2178
+				'TICKETNUM',
2179
+				$price_row,
2180
+				$price,
2181
+				true,
2182
+				null,
2183
+				$show_trash,
2184
+				$show_create
2185
+			);
2186
+			$price_row++;
2187
+		}
2188
+		$template_args = apply_filters(
2189
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2190
+			$template_args,
2191
+			$all_datetimes,
2192
+			$all_tickets,
2193
+			$this->_is_creating_event
2194
+		);
2195
+		return EEH_Template::display_template(
2196
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2197
+			$template_args,
2198
+			true
2199
+		);
2200
+	}
2201 2201
 }
Please login to merge, or discard this patch.
messages/templates/ee_msg_details_main_edit_meta_box.template.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -23,14 +23,14 @@  discard block
 block discarded – undo
23 23
     <input type="hidden" id="ee-msg-current-context" name="MTP_context" value="<?php echo esc_attr($context); ?>" />
24 24
     <!-- if this is not a global template then let's show the name and description fields -->
25 25
     <?php
26
-    if (! $MTP->is_global()) : ?>
26
+	if (! $MTP->is_global()) : ?>
27 27
         <div class="non-global-mtp-fields">
28 28
             <div class="ee-info-box">
29 29
                 <p>
30 30
                     <?php esc_html_e(
31
-                        'This is a custom template.  Custom Templates have an editable name and description to help you differentiate between templates.',
32
-                        'event_espresso'
33
-                    ); ?>
31
+						'This is a custom template.  Custom Templates have an editable name and description to help you differentiate between templates.',
32
+						'event_espresso'
33
+					); ?>
34 34
                 </p>
35 35
             </div>
36 36
             <div id="titlediv">
@@ -59,8 +59,8 @@  discard block
 block discarded – undo
59 59
             </p>
60 60
         </div>
61 61
         <?php
62
-    else :
63
-        ?>
62
+	else :
63
+		?>
64 64
         <input type="hidden" name="ee_msg_non_global_fields[MTP_name]" value="<?php echo esc_attr($MTP->name()); ?>">
65 65
         <input type="hidden"
66 66
                name="ee_msg_non_global_fields[MTP_description]"
@@ -69,18 +69,18 @@  discard block
 block discarded – undo
69 69
     <?php endif; ?>
70 70
     <!-- we need to loop through the template_fields so we know our structure -->
71 71
     <?php
72
-    if (isset($template_fields) && ! empty($template_fields) && ! is_wp_error($template_fields)) {
73
-        echo wp_kses($template_fields, AllowedTags::getWithFullTags());
74
-    } else {
75
-        ?>
72
+	if (isset($template_fields) && ! empty($template_fields) && ! is_wp_error($template_fields)) {
73
+		echo wp_kses($template_fields, AllowedTags::getWithFullTags());
74
+	} else {
75
+		?>
76 76
         <p>
77 77
             <?php esc_html_e(
78
-                'Something has gone wrong, there are no template fields to output.',
79
-                'event_espresso'
80
-            ); ?>
78
+				'Something has gone wrong, there are no template fields to output.',
79
+				'event_espresso'
80
+			); ?>
81 81
         </p>
82 82
         <?php
83
-    }
83
+	}
84 84
 
85
-    ?>
85
+	?>
86 86
 </div> <!-- end #admin-primary-mbox-dv -->
Please login to merge, or discard this patch.