Completed
Branch master (7421d0)
by
unknown
11:25 queued 06:55
created
core/libraries/shortcodes/EE_Payment_List_Shortcodes.lib.php 1 patch
Indentation   +69 added lines, -69 removed lines patch added patch discarded remove patch
@@ -18,87 +18,87 @@
 block discarded – undo
18 18
  */
19 19
 class EE_Payment_List_Shortcodes extends EE_Shortcodes
20 20
 {
21
-    protected function _init_props()
22
-    {
23
-        $this->label = esc_html__('Payment List Shortcodes', 'event_espresso');
24
-        $this->description = esc_html__('All shortcodes specific to payment lists', 'event_espresso');
25
-        $this->_shortcodes = array(
26
-            '[PAYMENT_LIST_*]' => esc_html__(
27
-                'Outputs a list of payment items. Note, this is a dynamic shortcode in that it accepts some attributes for setting certain defaults.  Attributes that are available are:',
28
-                'event_espresso'
29
-            )
30
-                                  . '<p><ul>' .
31
-                                  '<li><strong>no_payments</strong>:' . sprintf(
32
-                                      esc_html__(
33
-                                          'Indicate with this attribute what will be used if there are no payments present.  Default is: "%sNo approved payments have been received.%s"',
34
-                                          'event_espresso'
35
-                                      ),
36
-                                      htmlspecialchars('<td class="aln-cntr" colspan="6">'),
37
-                                      htmlspecialchars('</td>')
38
-                                  ) . '</li>' .
39
-                                  '</ul></p>',
40
-        );
41
-    }
21
+	protected function _init_props()
22
+	{
23
+		$this->label = esc_html__('Payment List Shortcodes', 'event_espresso');
24
+		$this->description = esc_html__('All shortcodes specific to payment lists', 'event_espresso');
25
+		$this->_shortcodes = array(
26
+			'[PAYMENT_LIST_*]' => esc_html__(
27
+				'Outputs a list of payment items. Note, this is a dynamic shortcode in that it accepts some attributes for setting certain defaults.  Attributes that are available are:',
28
+				'event_espresso'
29
+			)
30
+								  . '<p><ul>' .
31
+								  '<li><strong>no_payments</strong>:' . sprintf(
32
+									  esc_html__(
33
+										  'Indicate with this attribute what will be used if there are no payments present.  Default is: "%sNo approved payments have been received.%s"',
34
+										  'event_espresso'
35
+									  ),
36
+									  htmlspecialchars('<td class="aln-cntr" colspan="6">'),
37
+									  htmlspecialchars('</td>')
38
+								  ) . '</li>' .
39
+								  '</ul></p>',
40
+		);
41
+	}
42 42
 
43 43
 
44
-    protected function _parser($shortcode)
45
-    {
46
-        if (strpos($shortcode, '[PAYMENT_LIST_*') !== false) {
47
-            return $this->_get_payment_list($shortcode);
48
-        }
49
-        return '';
50
-    }
44
+	protected function _parser($shortcode)
45
+	{
46
+		if (strpos($shortcode, '[PAYMENT_LIST_*') !== false) {
47
+			return $this->_get_payment_list($shortcode);
48
+		}
49
+		return '';
50
+	}
51 51
 
52 52
 
53
-    /**
54
-     * verify incoming data contains what is needed for retrieving and parsing each payment for transaction.
55
-     *
56
-     * @since 4.5.0
57
-     *
58
-     * @param string $shortcode The incoming shortcode.
59
-     *
60
-     * @return string parsed ticket line item list.
61
-     */
62
-    private function _get_payment_list($shortcode)
63
-    {
64
-        $this->_validate_list_requirements();
53
+	/**
54
+	 * verify incoming data contains what is needed for retrieving and parsing each payment for transaction.
55
+	 *
56
+	 * @since 4.5.0
57
+	 *
58
+	 * @param string $shortcode The incoming shortcode.
59
+	 *
60
+	 * @return string parsed ticket line item list.
61
+	 */
62
+	private function _get_payment_list($shortcode)
63
+	{
64
+		$this->_validate_list_requirements();
65 65
 
66 66
 
67
-        if (! $this->_data['data'] instanceof EE_Messages_Addressee) {
68
-            return '';
69
-        }
67
+		if (! $this->_data['data'] instanceof EE_Messages_Addressee) {
68
+			return '';
69
+		}
70 70
 
71
-        $valid_shortcodes = array('payment');
71
+		$valid_shortcodes = array('payment');
72 72
 
73
-        $addressee_obj = $this->_data['data'];
74
-        $templates = $this->_extra_data['template'];
75
-        $payments = apply_filters(
76
-            'FHEE__Payment_List_Shortcodes___get_payments_list__payments',
77
-            $addressee_obj->payments
78
-        );
73
+		$addressee_obj = $this->_data['data'];
74
+		$templates = $this->_extra_data['template'];
75
+		$payments = apply_filters(
76
+			'FHEE__Payment_List_Shortcodes___get_payments_list__payments',
77
+			$addressee_obj->payments
78
+		);
79 79
 
80
-        // let's get any attributes that may be present and set the defaults.
81
-        $atts = $this->_get_shortcode_attrs($shortcode);
80
+		// let's get any attributes that may be present and set the defaults.
81
+		$atts = $this->_get_shortcode_attrs($shortcode);
82 82
 
83
-        $no_payments_msg = empty($atts['no_payments']) ? esc_html__(
84
-            'No approved payments have been received.',
85
-            'event_espresso'
86
-        ) : $atts['no_payments'];
83
+		$no_payments_msg = empty($atts['no_payments']) ? esc_html__(
84
+			'No approved payments have been received.',
85
+			'event_espresso'
86
+		) : $atts['no_payments'];
87 87
 
88
-        // made it here so we have an array of paymnets, so we should have what we need.
89
-        $payment_content = empty($payments) ? $no_payments_msg : '';
88
+		// made it here so we have an array of paymnets, so we should have what we need.
89
+		$payment_content = empty($payments) ? $no_payments_msg : '';
90 90
 
91
-        $payments = (array) $payments;
91
+		$payments = (array) $payments;
92 92
 
93
-        foreach ($payments as $payment) {
94
-            $payment_content .= $this->_shortcode_helper->parse_payment_list_template(
95
-                $templates['payment_list'],
96
-                $payment,
97
-                $valid_shortcodes,
98
-                $this->_extra_data
99
-            );
100
-        }
93
+		foreach ($payments as $payment) {
94
+			$payment_content .= $this->_shortcode_helper->parse_payment_list_template(
95
+				$templates['payment_list'],
96
+				$payment,
97
+				$valid_shortcodes,
98
+				$this->_extra_data
99
+			);
100
+		}
101 101
 
102
-        return $payment_content;
103
-    }
102
+		return $payment_content;
103
+	}
104 104
 }
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/RegistrationsReport.php 1 patch
Indentation   +640 added lines, -640 removed lines patch added patch discarded remove patch
@@ -47,644 +47,644 @@
 block discarded – undo
47 47
  */
48 48
 class RegistrationsReport extends JobHandlerFile
49 49
 {
50
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
51
-    // phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
52
-    /**
53
-     * Performs any necessary setup for starting the job. This is also a good
54
-     * place to set up the $job_arguments which will be used for subsequent HTTP requests
55
-     * when continue_job will be called
56
-     *
57
-     * @param JobParameters $job_parameters
58
-     * @return JobStepResponse
59
-     * @throws BatchRequestException
60
-     * @throws EE_Error
61
-     * @throws ReflectionException
62
-     * @throws Exception
63
-     */
64
-    public function create_job(JobParameters $job_parameters): JobStepResponse
65
-    {
66
-        $event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
67
-        $DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
68
-        if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
69
-            throw new BatchRequestException(
70
-                esc_html__('You do not have permission to view registrations', 'event_espresso')
71
-            );
72
-        }
73
-        $filepath = $this->create_file_from_job_with_name(
74
-            $job_parameters->job_id(),
75
-            $this->get_filename()
76
-        );
77
-        $job_parameters->add_extra_data('filepath', $filepath);
78
-
79
-        if ($job_parameters->request_datum('use_filters', false)) {
80
-            $query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
81
-        } else {
82
-            $query_params = [
83
-                [ 'Ticket.TKT_deleted' => ['IN', [true, false]] ],
84
-                'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
85
-                'force_join' => ['Transaction', 'Ticket', 'Attendee'],
86
-                'caps'       => EEM_Base::caps_read_admin,
87
-            ];
88
-            if ($event_id) {
89
-                $query_params[0]['EVT_ID'] = $event_id;
90
-            } else {
91
-                $query_params['force_join'][] = 'Event';
92
-            }
93
-        }
94
-        // unless the query params already include a status,
95
-        // we want to exclude registrations from failed or abandoned transactions
96
-        if (! isset($query_params[0]['Transaction.STS_ID'])) {
97
-            $query_params[0]['OR'] = [
98
-                // don't include registrations from failed or abandoned transactions...
99
-                'Transaction.STS_ID' => [
100
-                    'NOT IN',
101
-                    [
102
-                        EEM_Transaction::failed_status_code,
103
-                        EEM_Transaction::abandoned_status_code,
104
-                    ],
105
-                ],
106
-                // unless the registration is approved,
107
-                // in which case include it regardless of transaction status
108
-                'STS_ID' => RegStatus::APPROVED,
109
-            ];
110
-        }
111
-
112
-        if (! isset($query_params['force_join'])) {
113
-            $query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
114
-        }
115
-
116
-        $return_url_args = [];
117
-        parse_str(
118
-            parse_url(
119
-                $job_parameters->request_datum('return_url'),
120
-                PHP_URL_QUERY
121
-            ),
122
-            $return_url_args
123
-        );
124
-
125
-        if (
126
-            isset($return_url_args['orderby'], $return_url_args['order'])
127
-            && $return_url_args['orderby'] === 'ATT_lname'
128
-        ) {
129
-            $query_params['order_by'] = [
130
-                'Attendee.ATT_lname' => $return_url_args['order'],
131
-                'Attendee.ATT_fname' => $return_url_args['order'],
132
-                'REG_ID' => $return_url_args['order']
133
-            ];
134
-        }
135
-
136
-        $query_params = apply_filters(
137
-            'FHEE__EE_Export__report_registration_for_event',
138
-            $query_params,
139
-            $event_id
140
-        );
141
-
142
-        $utc_timezone = new DateTimeZone('UTC');
143
-        $site_timezone = new DateTimeZone(EEH_DTT_Helper::get_timezone());
144
-        $query_params = $this->convertDateStringsToObjects($query_params, $site_timezone, $utc_timezone);
145
-
146
-        $job_parameters->add_extra_data('query_params', $query_params);
147
-        $question_labels = $this->_get_question_labels($query_params);
148
-        $job_parameters->add_extra_data('question_labels', $question_labels);
149
-        $job_parameters->set_job_size($this->count_units_to_process($query_params));
150
-        // we need to set the header columns
151
-        // but to do that we need to process one row so that we can extract ALL the column headers
152
-        $csv_data_for_row = $this->get_csv_data_for(
153
-            $event_id,
154
-            0,
155
-            1,
156
-            $question_labels,
157
-            $query_params,
158
-            $DTT_ID
159
-        );
160
-        // but we don't want to write any actual data yet...
161
-        // so let's blank out all the values for that first row
162
-        array_walk(
163
-            $csv_data_for_row[0],
164
-            function (&$value) {
165
-                $value = null;
166
-            }
167
-        );
168
-
169
-        EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row, true, true);
170
-        $this->updateTextHeader(
171
-            esc_html__('Registrations report started successfully...', 'event_espresso')
172
-        );
173
-        return new JobStepResponse($job_parameters, $this->feedback);
174
-    }
175
-
176
-
177
-    /**
178
-     * Gets the filename
179
-     *
180
-     * @return string
181
-     */
182
-    protected function get_filename(): string
183
-    {
184
-        return apply_filters(
185
-            'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__get_filename',
186
-            sprintf(
187
-                'event-espresso-registrations-%s.csv',
188
-                str_replace([':', ' '], '-', current_time('mysql'))
189
-            )
190
-        );
191
-    }
192
-
193
-
194
-    /**
195
-     * Gets the questions which are to be used for this report,
196
-     * so they can be remembered for later
197
-     *
198
-     * @param array $registration_query_params
199
-     * @return array question admin labels to be used for this report
200
-     * @throws EE_Error
201
-     * @throws ReflectionException
202
-     */
203
-    protected function _get_question_labels(array $registration_query_params): array
204
-    {
205
-        $where                 = $registration_query_params[0] ?? null;
206
-        $question_query_params = [];
207
-        if ($where !== null) {
208
-            $question_query_params = [
209
-                $this->_change_registration_where_params_to_question_where_params($registration_query_params[0]),
210
-            ];
211
-        }
212
-        // Make sure it's not a system question
213
-        $question_query_params[0]['OR*not-system-questions'] = [
214
-            'QST_system'      => '',
215
-            'QST_system*null' => ['IS_NULL']
216
-        ];
217
-        if (
218
-            apply_filters(
219
-                'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport___get_question_labels__only_include_answered_questions',
220
-                false,
221
-                $registration_query_params
222
-            )
223
-        ) {
224
-            $question_query_params[0]['Answer.ANS_ID'] = ['IS_NOT_NULL'];
225
-        }
226
-        $question_query_params['order_by'] = [
227
-            'Question_Group_Question.QGQ_order' => 'ASC',
228
-            'QST_order' => 'ASC',
229
-            'QST_admin_label' => 'ASC'
230
-        ];
231
-        $question_query_params['group_by'] = ['QST_ID'];
232
-        return array_unique(EEM_Question::instance()->get_col($question_query_params, 'QST_admin_label'));
233
-    }
234
-
235
-
236
-    /**
237
-     * Takes where params meant for registrations and changes them to work for questions
238
-     *
239
-     * @param array $reg_where_params
240
-     * @return array
241
-     * @throws EE_Error
242
-     * @throws ReflectionException
243
-     */
244
-    protected function _change_registration_where_params_to_question_where_params(array $reg_where_params): array
245
-    {
246
-        $question_where_params = [];
247
-        foreach ($reg_where_params as $key => $val) {
248
-            if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
249
-                $question_where_params[ $key ] =
250
-                    $this->_change_registration_where_params_to_question_where_params($val);
251
-            } else {
252
-                // it's a normal where condition
253
-                $question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
254
-            }
255
-        }
256
-        return $question_where_params;
257
-    }
258
-
259
-
260
-    /**
261
-     * Performs another step of the job
262
-     *
263
-     * @param JobParameters $job_parameters
264
-     * @param int           $batch_size
265
-     * @return JobStepResponse
266
-     * @throws EE_Error
267
-     * @throws ReflectionException
268
-     */
269
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
270
-    {
271
-        if ($job_parameters->units_processed() < $job_parameters->job_size()) {
272
-            $csv_data = $this->get_csv_data_for(
273
-                (int) $job_parameters->request_datum('EVT_ID', '0'),
274
-                $job_parameters->units_processed(),
275
-                $batch_size,
276
-                $job_parameters->extra_datum('question_labels'),
277
-                $job_parameters->extra_datum('query_params'),
278
-                (int) $job_parameters->request_datum('DTT_ID', '0')
279
-            );
280
-            EEH_Export::write_data_array_to_csv(
281
-                $job_parameters->extra_datum('filepath'),
282
-                $csv_data,
283
-                false
284
-            );
285
-            $units_processed = count($csv_data);
286
-            if ($units_processed) {
287
-                $job_parameters->mark_processed($units_processed);
288
-                $this->updateText(
289
-                    sprintf(
290
-                        esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
291
-                        $units_processed
292
-                    )
293
-                );
294
-            }
295
-        }
296
-        $extra_response_data = ['file_url' => ''];
297
-        if ($job_parameters->units_processed() >= $job_parameters->job_size()) {
298
-            $job_parameters->set_status(JobParameters::status_complete);
299
-            $extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
300
-            $this->displayJobFinalResults($job_parameters);
301
-        } else {
302
-            $job_parameters->set_status(JobParameters::status_continue);
303
-        }
304
-        return new JobStepResponse($job_parameters, $this->feedback, $extra_response_data);
305
-    }
306
-
307
-
308
-    /**
309
-     * Gets the csv data for a batch of registrations
310
-     *
311
-     * @param int|null $event_id
312
-     * @param int      $offset
313
-     * @param int      $limit
314
-     * @param array    $question_labels the IDs for all the questions which were answered by someone in this selection
315
-     * @param array    $query_params    for using where querying the model
316
-     * @param int      $DTT_ID
317
-     * @return array top-level keys are numeric, next-level keys are column headers
318
-     * @throws EE_Error
319
-     * @throws ReflectionException
320
-     */
321
-    public function get_csv_data_for(
322
-        ?int $event_id,
323
-        int $offset,
324
-        int $limit,
325
-        array $question_labels,
326
-        array $query_params,
327
-        int $DTT_ID = 0
328
-    ): array {
329
-        $reg_fields_to_include = [
330
-            'TXN_ID',
331
-            'ATT_ID',
332
-            'REG_ID',
333
-            'REG_date',
334
-            'REG_code',
335
-            'REG_count',
336
-            'REG_final_price',
337
-        ];
338
-        $att_fields_to_include = [
339
-            'ATT_fname',
340
-            'ATT_lname',
341
-            'ATT_email',
342
-            'ATT_address',
343
-            'ATT_address2',
344
-            'ATT_city',
345
-            'STA_ID',
346
-            'CNT_ISO',
347
-            'ATT_zip',
348
-            'ATT_phone',
349
-        ];
350
-
351
-        // get models
352
-        $event_model   = EEM_Event::instance();
353
-        $date_model    = EEM_Datetime::instance();
354
-        $ticket_model  = EEM_Ticket::instance();
355
-        $txn_model     = EEM_Transaction::instance();
356
-        $reg_model     = EEM_Registration::instance();
357
-        $pay_model     = EEM_Payment::instance();
358
-        $status_model  = EEM_Status::instance();
359
-
360
-        $registrations_csv_ready_array = [];
361
-        $query_params['limit']         = [$offset, $limit];
362
-        $registration_rows             = $reg_model->get_all_wpdb_results($query_params);
363
-
364
-        foreach ($registration_rows as $reg_row) {
365
-            if (! is_array($reg_row)) {
366
-                continue;
367
-            }
368
-            $reg_csv_array = [];
369
-            // registration ID
370
-            $reg_id_field = $reg_model->field_settings_for('REG_ID');
371
-            $reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
372
-                EEH_Export::prepare_value_from_db_for_display(
373
-                    $reg_model,
374
-                    'REG_ID',
375
-                    $reg_row[ $reg_id_field->get_qualified_column() ]
376
-                );
377
-            // ALL registrations, or is list filtered to just one?
378
-            if (! $event_id) {
379
-                // ALL registrations, so get each event's name and ID
380
-                $reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
381
-                    /* translators: 1: event name, 2: event ID */
382
-                    esc_html__('%1$s (%2$s)', 'event_espresso'),
383
-                    EEH_Export::prepare_value_from_db_for_display(
384
-                        $event_model,
385
-                        'EVT_name',
386
-                        $reg_row['Event_CPT.post_title']
387
-                    ),
388
-                    $reg_row['Event_CPT.ID']
389
-                );
390
-            }
391
-            // add attendee columns
392
-            $reg_csv_array = AttendeeCSV::addAttendeeColumns($att_fields_to_include, $reg_row, $reg_csv_array);
393
-            // add registration columns
394
-            $reg_csv_array = RegistrationCSV::addRegistrationColumns($reg_fields_to_include, $reg_row, $reg_csv_array);
395
-            // get pretty status
396
-            $stati = $status_model->localized_status(
397
-                [
398
-                    $reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
399
-                    $reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
400
-                ],
401
-                false,
402
-                'sentence'
403
-            );
404
-            $is_primary_reg = $reg_row['Registration.REG_count'] == '1';
405
-
406
-            $reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
407
-                $stati[ $reg_row['Registration.STS_ID'] ];
408
-            // get pretty transaction status
409
-            $reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
410
-                $stati[ $reg_row['TransactionTable.STS_ID'] ];
411
-            $reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
412
-                ? EEH_Export::prepare_value_from_db_for_display(
413
-                    $txn_model,
414
-                    'TXN_total',
415
-                    $reg_row['TransactionTable.TXN_total'],
416
-                    'localized_float'
417
-                )
418
-                : '0.00';
419
-
420
-            $reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
421
-                ? EEH_Export::prepare_value_from_db_for_display(
422
-                    $txn_model,
423
-                    'TXN_paid',
424
-                    $reg_row['TransactionTable.TXN_paid'],
425
-                    'localized_float'
426
-                )
427
-                : '0.00';
428
-
429
-            $payment_methods                                                                  = [];
430
-            $gateway_txn_ids_etc                                                              = [];
431
-            $payment_times                                                                    = [];
432
-            if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
433
-                $payments_info = $pay_model->get_all_wpdb_results(
434
-                    [
435
-                        [
436
-                            'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
437
-                            'STS_ID' => EEM_Payment::status_id_approved,
438
-                        ],
439
-                        'force_join' => ['Payment_Method'],
440
-                    ],
441
-                    ARRAY_A,
442
-                    'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
443
-                );
444
-                [$payment_methods, $gateway_txn_ids_etc, $payment_times] = PaymentsInfoCSV::extractPaymentInfo(
445
-                    $payments_info
446
-                );
447
-            }
448
-
449
-            $reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
450
-                ',',
451
-                $payment_times
452
-            );
453
-
454
-            $reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
455
-                ',',
456
-                $payment_methods
457
-            );
458
-
459
-            $reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
460
-                ',',
461
-                $gateway_txn_ids_etc
462
-            );
463
-
464
-            $ticket_name      = esc_html__('Unknown', 'event_espresso');
465
-            $datetime_strings = [esc_html__('Unknown', 'event_espresso')];
466
-            if ($reg_row['Ticket.TKT_ID']) {
467
-                $ticket_name       = EEH_Export::prepare_value_from_db_for_display(
468
-                    $ticket_model,
469
-                    'TKT_name',
470
-                    $reg_row['Ticket.TKT_name']
471
-                );
472
-                $datetime_strings = [];
473
-                $datetimes        = $date_model->get_all_wpdb_results(
474
-                    [
475
-                        ['Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']],
476
-                        'order_by'                 => ['DTT_EVT_start' => 'ASC'],
477
-                        'default_where_conditions' => 'none',
478
-                    ]
479
-                );
480
-                foreach ($datetimes as $datetime) {
481
-                    $datetime_strings[] = EEH_Export::prepare_value_from_db_for_display(
482
-                        $date_model,
483
-                        'DTT_EVT_start',
484
-                        $datetime['Datetime.DTT_EVT_start']
485
-                    );
486
-                }
487
-            }
488
-
489
-            $reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
490
-
491
-
492
-            $reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
493
-                ', ',
494
-                $datetime_strings
495
-            );
496
-
497
-            // add answer columns
498
-            $reg_csv_array = AnswersCSV::addAnswerColumns($reg_row, $reg_csv_array, $question_labels);
499
-            // Include check-in data
500
-            if ($event_id && $DTT_ID) {
501
-                // get whether the user has checked in
502
-                $reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
503
-                    $reg_model->count_related(
504
-                        $reg_row['Registration.REG_ID'],
505
-                        'Checkin',
506
-                        [
507
-                            [
508
-                                'DTT_ID' => $DTT_ID
509
-                            ]
510
-                        ]
511
-                    );
512
-                $datetime     = $date_model->get_one_by_ID($DTT_ID);
513
-                $checkin_rows = EEM_Checkin::instance()->get_all(
514
-                    [
515
-                        [
516
-                            'REG_ID' => $reg_row['Registration.REG_ID'],
517
-                            'DTT_ID' => $datetime->get('DTT_ID'),
518
-                        ],
519
-                    ]
520
-                );
521
-                $checkins     = [];
522
-                foreach ($checkin_rows as $checkin_row) {
523
-                    /** @var EE_Checkin $checkin_row */
524
-                    $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
525
-                    if ($checkin_value) {
526
-                        $checkins[] = $checkin_value;
527
-                    }
528
-                }
529
-                $datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
530
-                $reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
531
-            } elseif ($event_id) {
532
-                // get whether the user has checked in
533
-                $reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
534
-                    $reg_model->count_related(
535
-                        $reg_row['Registration.REG_ID'],
536
-                        'Checkin'
537
-                    );
538
-
539
-                $datetimes = $date_model->get_all(
540
-                    [
541
-                        [
542
-                            'Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID'],
543
-                        ],
544
-                        'order_by'                 => ['DTT_EVT_start' => 'ASC'],
545
-                        'default_where_conditions' => 'none',
546
-                    ]
547
-                );
548
-                foreach ($datetimes as $datetime) {
549
-                    if (! $datetime instanceof EE_Datetime) {
550
-                        continue;
551
-                    }
552
-
553
-                    /** @var EE_Checkin $checkin_row */
554
-                    $checkin_row = EEM_Checkin::instance()->get_one(
555
-                        [
556
-                            [
557
-                                'REG_ID' => $reg_row['Registration.REG_ID'],
558
-                                'DTT_ID' => $datetime->get('DTT_ID'),
559
-                            ],
560
-                            'limit'    => 1,
561
-                            'order_by' => [
562
-                                'CHK_ID' => 'DESC'
563
-                            ]
564
-                        ]
565
-                    );
566
-
567
-                    $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
568
-                    $datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
569
-
570
-                    $reg_csv_array[ $datetime_name ] = $checkin_value;
571
-                }
572
-            }
573
-            /**
574
-             * Filter to change the contents of each row of the registrations report CSV file.
575
-             * This can be used to add or remote columns from the CSV file, or change their values.
576
-             * Note when using: all rows in the CSV should have the same columns.
577
-             *
578
-             * @param array $reg_csv_array keys are the column names, values are their cell values
579
-             * @param array $reg_row       one entry from EEM_Registration::get_all_wpdb_results()
580
-             */
581
-            $registrations_csv_ready_array[] = apply_filters(
582
-                'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
583
-                $reg_csv_array,
584
-                $reg_row
585
-            );
586
-        }
587
-        // if we couldn't export anything, we want to at least show the column headers
588
-        if (empty($registrations_csv_ready_array)) {
589
-            $reg_csv_array               = [];
590
-            $model_and_fields_to_include = [
591
-                'Registration' => $reg_fields_to_include,
592
-                'Attendee'     => $att_fields_to_include,
593
-            ];
594
-            foreach ($model_and_fields_to_include as $model_name => $field_list) {
595
-                $model = EE_Registry::instance()->load_model($model_name);
596
-                foreach ($field_list as $field_name) {
597
-                    $field                                                          =
598
-                        $model->field_settings_for($field_name);
599
-                    $reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
600
-                }
601
-            }
602
-            $registrations_csv_ready_array[] = $reg_csv_array;
603
-        }
604
-        return $registrations_csv_ready_array;
605
-    }
606
-
607
-
608
-    /**
609
-     * recursively convert MySQL format date strings in query params array to Datetime objects
610
-     *
611
-     * @param array        $query_params
612
-     * @param DateTimeZone $site_timezone
613
-     * @param DateTimeZone $utc_timezone
614
-     * @return array
615
-     * @throws Exception
616
-     * @since 5.0.19.p
617
-     */
618
-    private function convertDateStringsToObjects(
619
-        array $query_params,
620
-        DateTimeZone $site_timezone,
621
-        DateTimeZone $utc_timezone
622
-    ): array {
623
-        foreach ($query_params as $key => $value) {
624
-            if (is_array($value)) {
625
-                $query_params[$key] = $this->convertDateStringsToObjects($value, $site_timezone, $utc_timezone);
626
-                continue;
627
-            }
628
-            if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $value)) {
629
-                $query_params[$key] = DbSafeDateTime::createFromFormat('Y-m-d H:i:s', $value, $site_timezone);
630
-                $query_params[$key] = $query_params[$key]->setTimezone($utc_timezone);
631
-            }
632
-        }
633
-        return $query_params;
634
-    }
635
-
636
-
637
-    /**
638
-     * Counts total unit to process
639
-     *
640
-     * @param array $query_params
641
-     * @return int
642
-     * @throws EE_Error
643
-     * @throws ReflectionException
644
-     */
645
-    public function count_units_to_process(array $query_params): int
646
-    {
647
-        return EEM_Registration::instance()->count(
648
-            array_diff_key(
649
-                $query_params,
650
-                array_flip(
651
-                    ['limit']
652
-                )
653
-            )
654
-        );
655
-    }
656
-
657
-
658
-    /**
659
-     * Performs any clean-up logic when we know the job is completed.
660
-     * In this case, we delete the temporary file
661
-     *
662
-     * @param JobParameters $job_parameters
663
-     * @return JobStepResponse
664
-     */
665
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse
666
-    {
667
-        $this->updateText(esc_html__('File Generation complete and downloaded', 'event_espresso'));
668
-
669
-        $this->_file_helper->delete(
670
-            EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
671
-            true,
672
-            'd'
673
-        );
674
-        $this->updateText(esc_html__('Cleaned up temporary file', 'event_espresso'));
675
-        $this->updateText(
676
-            $this->infoWrapper(
677
-                sprintf(
678
-                    esc_html__(
679
-                        'If not automatically redirected in %1$s seconds, click here to return to the %2$sRegistrations List Table%3$s',
680
-                        'event_espresso'
681
-                    ),
682
-                    '<span id="ee-redirect-timer">10</span>',
683
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
684
-                    '</a>'
685
-                )
686
-            )
687
-        );
688
-        return new JobStepResponse($job_parameters, $this->feedback);
689
-    }
50
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
51
+	// phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
52
+	/**
53
+	 * Performs any necessary setup for starting the job. This is also a good
54
+	 * place to set up the $job_arguments which will be used for subsequent HTTP requests
55
+	 * when continue_job will be called
56
+	 *
57
+	 * @param JobParameters $job_parameters
58
+	 * @return JobStepResponse
59
+	 * @throws BatchRequestException
60
+	 * @throws EE_Error
61
+	 * @throws ReflectionException
62
+	 * @throws Exception
63
+	 */
64
+	public function create_job(JobParameters $job_parameters): JobStepResponse
65
+	{
66
+		$event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
67
+		$DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
68
+		if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
69
+			throw new BatchRequestException(
70
+				esc_html__('You do not have permission to view registrations', 'event_espresso')
71
+			);
72
+		}
73
+		$filepath = $this->create_file_from_job_with_name(
74
+			$job_parameters->job_id(),
75
+			$this->get_filename()
76
+		);
77
+		$job_parameters->add_extra_data('filepath', $filepath);
78
+
79
+		if ($job_parameters->request_datum('use_filters', false)) {
80
+			$query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
81
+		} else {
82
+			$query_params = [
83
+				[ 'Ticket.TKT_deleted' => ['IN', [true, false]] ],
84
+				'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
85
+				'force_join' => ['Transaction', 'Ticket', 'Attendee'],
86
+				'caps'       => EEM_Base::caps_read_admin,
87
+			];
88
+			if ($event_id) {
89
+				$query_params[0]['EVT_ID'] = $event_id;
90
+			} else {
91
+				$query_params['force_join'][] = 'Event';
92
+			}
93
+		}
94
+		// unless the query params already include a status,
95
+		// we want to exclude registrations from failed or abandoned transactions
96
+		if (! isset($query_params[0]['Transaction.STS_ID'])) {
97
+			$query_params[0]['OR'] = [
98
+				// don't include registrations from failed or abandoned transactions...
99
+				'Transaction.STS_ID' => [
100
+					'NOT IN',
101
+					[
102
+						EEM_Transaction::failed_status_code,
103
+						EEM_Transaction::abandoned_status_code,
104
+					],
105
+				],
106
+				// unless the registration is approved,
107
+				// in which case include it regardless of transaction status
108
+				'STS_ID' => RegStatus::APPROVED,
109
+			];
110
+		}
111
+
112
+		if (! isset($query_params['force_join'])) {
113
+			$query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
114
+		}
115
+
116
+		$return_url_args = [];
117
+		parse_str(
118
+			parse_url(
119
+				$job_parameters->request_datum('return_url'),
120
+				PHP_URL_QUERY
121
+			),
122
+			$return_url_args
123
+		);
124
+
125
+		if (
126
+			isset($return_url_args['orderby'], $return_url_args['order'])
127
+			&& $return_url_args['orderby'] === 'ATT_lname'
128
+		) {
129
+			$query_params['order_by'] = [
130
+				'Attendee.ATT_lname' => $return_url_args['order'],
131
+				'Attendee.ATT_fname' => $return_url_args['order'],
132
+				'REG_ID' => $return_url_args['order']
133
+			];
134
+		}
135
+
136
+		$query_params = apply_filters(
137
+			'FHEE__EE_Export__report_registration_for_event',
138
+			$query_params,
139
+			$event_id
140
+		);
141
+
142
+		$utc_timezone = new DateTimeZone('UTC');
143
+		$site_timezone = new DateTimeZone(EEH_DTT_Helper::get_timezone());
144
+		$query_params = $this->convertDateStringsToObjects($query_params, $site_timezone, $utc_timezone);
145
+
146
+		$job_parameters->add_extra_data('query_params', $query_params);
147
+		$question_labels = $this->_get_question_labels($query_params);
148
+		$job_parameters->add_extra_data('question_labels', $question_labels);
149
+		$job_parameters->set_job_size($this->count_units_to_process($query_params));
150
+		// we need to set the header columns
151
+		// but to do that we need to process one row so that we can extract ALL the column headers
152
+		$csv_data_for_row = $this->get_csv_data_for(
153
+			$event_id,
154
+			0,
155
+			1,
156
+			$question_labels,
157
+			$query_params,
158
+			$DTT_ID
159
+		);
160
+		// but we don't want to write any actual data yet...
161
+		// so let's blank out all the values for that first row
162
+		array_walk(
163
+			$csv_data_for_row[0],
164
+			function (&$value) {
165
+				$value = null;
166
+			}
167
+		);
168
+
169
+		EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row, true, true);
170
+		$this->updateTextHeader(
171
+			esc_html__('Registrations report started successfully...', 'event_espresso')
172
+		);
173
+		return new JobStepResponse($job_parameters, $this->feedback);
174
+	}
175
+
176
+
177
+	/**
178
+	 * Gets the filename
179
+	 *
180
+	 * @return string
181
+	 */
182
+	protected function get_filename(): string
183
+	{
184
+		return apply_filters(
185
+			'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__get_filename',
186
+			sprintf(
187
+				'event-espresso-registrations-%s.csv',
188
+				str_replace([':', ' '], '-', current_time('mysql'))
189
+			)
190
+		);
191
+	}
192
+
193
+
194
+	/**
195
+	 * Gets the questions which are to be used for this report,
196
+	 * so they can be remembered for later
197
+	 *
198
+	 * @param array $registration_query_params
199
+	 * @return array question admin labels to be used for this report
200
+	 * @throws EE_Error
201
+	 * @throws ReflectionException
202
+	 */
203
+	protected function _get_question_labels(array $registration_query_params): array
204
+	{
205
+		$where                 = $registration_query_params[0] ?? null;
206
+		$question_query_params = [];
207
+		if ($where !== null) {
208
+			$question_query_params = [
209
+				$this->_change_registration_where_params_to_question_where_params($registration_query_params[0]),
210
+			];
211
+		}
212
+		// Make sure it's not a system question
213
+		$question_query_params[0]['OR*not-system-questions'] = [
214
+			'QST_system'      => '',
215
+			'QST_system*null' => ['IS_NULL']
216
+		];
217
+		if (
218
+			apply_filters(
219
+				'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport___get_question_labels__only_include_answered_questions',
220
+				false,
221
+				$registration_query_params
222
+			)
223
+		) {
224
+			$question_query_params[0]['Answer.ANS_ID'] = ['IS_NOT_NULL'];
225
+		}
226
+		$question_query_params['order_by'] = [
227
+			'Question_Group_Question.QGQ_order' => 'ASC',
228
+			'QST_order' => 'ASC',
229
+			'QST_admin_label' => 'ASC'
230
+		];
231
+		$question_query_params['group_by'] = ['QST_ID'];
232
+		return array_unique(EEM_Question::instance()->get_col($question_query_params, 'QST_admin_label'));
233
+	}
234
+
235
+
236
+	/**
237
+	 * Takes where params meant for registrations and changes them to work for questions
238
+	 *
239
+	 * @param array $reg_where_params
240
+	 * @return array
241
+	 * @throws EE_Error
242
+	 * @throws ReflectionException
243
+	 */
244
+	protected function _change_registration_where_params_to_question_where_params(array $reg_where_params): array
245
+	{
246
+		$question_where_params = [];
247
+		foreach ($reg_where_params as $key => $val) {
248
+			if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
249
+				$question_where_params[ $key ] =
250
+					$this->_change_registration_where_params_to_question_where_params($val);
251
+			} else {
252
+				// it's a normal where condition
253
+				$question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
254
+			}
255
+		}
256
+		return $question_where_params;
257
+	}
258
+
259
+
260
+	/**
261
+	 * Performs another step of the job
262
+	 *
263
+	 * @param JobParameters $job_parameters
264
+	 * @param int           $batch_size
265
+	 * @return JobStepResponse
266
+	 * @throws EE_Error
267
+	 * @throws ReflectionException
268
+	 */
269
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
270
+	{
271
+		if ($job_parameters->units_processed() < $job_parameters->job_size()) {
272
+			$csv_data = $this->get_csv_data_for(
273
+				(int) $job_parameters->request_datum('EVT_ID', '0'),
274
+				$job_parameters->units_processed(),
275
+				$batch_size,
276
+				$job_parameters->extra_datum('question_labels'),
277
+				$job_parameters->extra_datum('query_params'),
278
+				(int) $job_parameters->request_datum('DTT_ID', '0')
279
+			);
280
+			EEH_Export::write_data_array_to_csv(
281
+				$job_parameters->extra_datum('filepath'),
282
+				$csv_data,
283
+				false
284
+			);
285
+			$units_processed = count($csv_data);
286
+			if ($units_processed) {
287
+				$job_parameters->mark_processed($units_processed);
288
+				$this->updateText(
289
+					sprintf(
290
+						esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
291
+						$units_processed
292
+					)
293
+				);
294
+			}
295
+		}
296
+		$extra_response_data = ['file_url' => ''];
297
+		if ($job_parameters->units_processed() >= $job_parameters->job_size()) {
298
+			$job_parameters->set_status(JobParameters::status_complete);
299
+			$extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
300
+			$this->displayJobFinalResults($job_parameters);
301
+		} else {
302
+			$job_parameters->set_status(JobParameters::status_continue);
303
+		}
304
+		return new JobStepResponse($job_parameters, $this->feedback, $extra_response_data);
305
+	}
306
+
307
+
308
+	/**
309
+	 * Gets the csv data for a batch of registrations
310
+	 *
311
+	 * @param int|null $event_id
312
+	 * @param int      $offset
313
+	 * @param int      $limit
314
+	 * @param array    $question_labels the IDs for all the questions which were answered by someone in this selection
315
+	 * @param array    $query_params    for using where querying the model
316
+	 * @param int      $DTT_ID
317
+	 * @return array top-level keys are numeric, next-level keys are column headers
318
+	 * @throws EE_Error
319
+	 * @throws ReflectionException
320
+	 */
321
+	public function get_csv_data_for(
322
+		?int $event_id,
323
+		int $offset,
324
+		int $limit,
325
+		array $question_labels,
326
+		array $query_params,
327
+		int $DTT_ID = 0
328
+	): array {
329
+		$reg_fields_to_include = [
330
+			'TXN_ID',
331
+			'ATT_ID',
332
+			'REG_ID',
333
+			'REG_date',
334
+			'REG_code',
335
+			'REG_count',
336
+			'REG_final_price',
337
+		];
338
+		$att_fields_to_include = [
339
+			'ATT_fname',
340
+			'ATT_lname',
341
+			'ATT_email',
342
+			'ATT_address',
343
+			'ATT_address2',
344
+			'ATT_city',
345
+			'STA_ID',
346
+			'CNT_ISO',
347
+			'ATT_zip',
348
+			'ATT_phone',
349
+		];
350
+
351
+		// get models
352
+		$event_model   = EEM_Event::instance();
353
+		$date_model    = EEM_Datetime::instance();
354
+		$ticket_model  = EEM_Ticket::instance();
355
+		$txn_model     = EEM_Transaction::instance();
356
+		$reg_model     = EEM_Registration::instance();
357
+		$pay_model     = EEM_Payment::instance();
358
+		$status_model  = EEM_Status::instance();
359
+
360
+		$registrations_csv_ready_array = [];
361
+		$query_params['limit']         = [$offset, $limit];
362
+		$registration_rows             = $reg_model->get_all_wpdb_results($query_params);
363
+
364
+		foreach ($registration_rows as $reg_row) {
365
+			if (! is_array($reg_row)) {
366
+				continue;
367
+			}
368
+			$reg_csv_array = [];
369
+			// registration ID
370
+			$reg_id_field = $reg_model->field_settings_for('REG_ID');
371
+			$reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
372
+				EEH_Export::prepare_value_from_db_for_display(
373
+					$reg_model,
374
+					'REG_ID',
375
+					$reg_row[ $reg_id_field->get_qualified_column() ]
376
+				);
377
+			// ALL registrations, or is list filtered to just one?
378
+			if (! $event_id) {
379
+				// ALL registrations, so get each event's name and ID
380
+				$reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
381
+					/* translators: 1: event name, 2: event ID */
382
+					esc_html__('%1$s (%2$s)', 'event_espresso'),
383
+					EEH_Export::prepare_value_from_db_for_display(
384
+						$event_model,
385
+						'EVT_name',
386
+						$reg_row['Event_CPT.post_title']
387
+					),
388
+					$reg_row['Event_CPT.ID']
389
+				);
390
+			}
391
+			// add attendee columns
392
+			$reg_csv_array = AttendeeCSV::addAttendeeColumns($att_fields_to_include, $reg_row, $reg_csv_array);
393
+			// add registration columns
394
+			$reg_csv_array = RegistrationCSV::addRegistrationColumns($reg_fields_to_include, $reg_row, $reg_csv_array);
395
+			// get pretty status
396
+			$stati = $status_model->localized_status(
397
+				[
398
+					$reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
399
+					$reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
400
+				],
401
+				false,
402
+				'sentence'
403
+			);
404
+			$is_primary_reg = $reg_row['Registration.REG_count'] == '1';
405
+
406
+			$reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
407
+				$stati[ $reg_row['Registration.STS_ID'] ];
408
+			// get pretty transaction status
409
+			$reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
410
+				$stati[ $reg_row['TransactionTable.STS_ID'] ];
411
+			$reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
412
+				? EEH_Export::prepare_value_from_db_for_display(
413
+					$txn_model,
414
+					'TXN_total',
415
+					$reg_row['TransactionTable.TXN_total'],
416
+					'localized_float'
417
+				)
418
+				: '0.00';
419
+
420
+			$reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
421
+				? EEH_Export::prepare_value_from_db_for_display(
422
+					$txn_model,
423
+					'TXN_paid',
424
+					$reg_row['TransactionTable.TXN_paid'],
425
+					'localized_float'
426
+				)
427
+				: '0.00';
428
+
429
+			$payment_methods                                                                  = [];
430
+			$gateway_txn_ids_etc                                                              = [];
431
+			$payment_times                                                                    = [];
432
+			if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
433
+				$payments_info = $pay_model->get_all_wpdb_results(
434
+					[
435
+						[
436
+							'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
437
+							'STS_ID' => EEM_Payment::status_id_approved,
438
+						],
439
+						'force_join' => ['Payment_Method'],
440
+					],
441
+					ARRAY_A,
442
+					'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
443
+				);
444
+				[$payment_methods, $gateway_txn_ids_etc, $payment_times] = PaymentsInfoCSV::extractPaymentInfo(
445
+					$payments_info
446
+				);
447
+			}
448
+
449
+			$reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
450
+				',',
451
+				$payment_times
452
+			);
453
+
454
+			$reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
455
+				',',
456
+				$payment_methods
457
+			);
458
+
459
+			$reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
460
+				',',
461
+				$gateway_txn_ids_etc
462
+			);
463
+
464
+			$ticket_name      = esc_html__('Unknown', 'event_espresso');
465
+			$datetime_strings = [esc_html__('Unknown', 'event_espresso')];
466
+			if ($reg_row['Ticket.TKT_ID']) {
467
+				$ticket_name       = EEH_Export::prepare_value_from_db_for_display(
468
+					$ticket_model,
469
+					'TKT_name',
470
+					$reg_row['Ticket.TKT_name']
471
+				);
472
+				$datetime_strings = [];
473
+				$datetimes        = $date_model->get_all_wpdb_results(
474
+					[
475
+						['Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']],
476
+						'order_by'                 => ['DTT_EVT_start' => 'ASC'],
477
+						'default_where_conditions' => 'none',
478
+					]
479
+				);
480
+				foreach ($datetimes as $datetime) {
481
+					$datetime_strings[] = EEH_Export::prepare_value_from_db_for_display(
482
+						$date_model,
483
+						'DTT_EVT_start',
484
+						$datetime['Datetime.DTT_EVT_start']
485
+					);
486
+				}
487
+			}
488
+
489
+			$reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
490
+
491
+
492
+			$reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
493
+				', ',
494
+				$datetime_strings
495
+			);
496
+
497
+			// add answer columns
498
+			$reg_csv_array = AnswersCSV::addAnswerColumns($reg_row, $reg_csv_array, $question_labels);
499
+			// Include check-in data
500
+			if ($event_id && $DTT_ID) {
501
+				// get whether the user has checked in
502
+				$reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
503
+					$reg_model->count_related(
504
+						$reg_row['Registration.REG_ID'],
505
+						'Checkin',
506
+						[
507
+							[
508
+								'DTT_ID' => $DTT_ID
509
+							]
510
+						]
511
+					);
512
+				$datetime     = $date_model->get_one_by_ID($DTT_ID);
513
+				$checkin_rows = EEM_Checkin::instance()->get_all(
514
+					[
515
+						[
516
+							'REG_ID' => $reg_row['Registration.REG_ID'],
517
+							'DTT_ID' => $datetime->get('DTT_ID'),
518
+						],
519
+					]
520
+				);
521
+				$checkins     = [];
522
+				foreach ($checkin_rows as $checkin_row) {
523
+					/** @var EE_Checkin $checkin_row */
524
+					$checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
525
+					if ($checkin_value) {
526
+						$checkins[] = $checkin_value;
527
+					}
528
+				}
529
+				$datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
530
+				$reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
531
+			} elseif ($event_id) {
532
+				// get whether the user has checked in
533
+				$reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
534
+					$reg_model->count_related(
535
+						$reg_row['Registration.REG_ID'],
536
+						'Checkin'
537
+					);
538
+
539
+				$datetimes = $date_model->get_all(
540
+					[
541
+						[
542
+							'Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID'],
543
+						],
544
+						'order_by'                 => ['DTT_EVT_start' => 'ASC'],
545
+						'default_where_conditions' => 'none',
546
+					]
547
+				);
548
+				foreach ($datetimes as $datetime) {
549
+					if (! $datetime instanceof EE_Datetime) {
550
+						continue;
551
+					}
552
+
553
+					/** @var EE_Checkin $checkin_row */
554
+					$checkin_row = EEM_Checkin::instance()->get_one(
555
+						[
556
+							[
557
+								'REG_ID' => $reg_row['Registration.REG_ID'],
558
+								'DTT_ID' => $datetime->get('DTT_ID'),
559
+							],
560
+							'limit'    => 1,
561
+							'order_by' => [
562
+								'CHK_ID' => 'DESC'
563
+							]
564
+						]
565
+					);
566
+
567
+					$checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
568
+					$datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
569
+
570
+					$reg_csv_array[ $datetime_name ] = $checkin_value;
571
+				}
572
+			}
573
+			/**
574
+			 * Filter to change the contents of each row of the registrations report CSV file.
575
+			 * This can be used to add or remote columns from the CSV file, or change their values.
576
+			 * Note when using: all rows in the CSV should have the same columns.
577
+			 *
578
+			 * @param array $reg_csv_array keys are the column names, values are their cell values
579
+			 * @param array $reg_row       one entry from EEM_Registration::get_all_wpdb_results()
580
+			 */
581
+			$registrations_csv_ready_array[] = apply_filters(
582
+				'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
583
+				$reg_csv_array,
584
+				$reg_row
585
+			);
586
+		}
587
+		// if we couldn't export anything, we want to at least show the column headers
588
+		if (empty($registrations_csv_ready_array)) {
589
+			$reg_csv_array               = [];
590
+			$model_and_fields_to_include = [
591
+				'Registration' => $reg_fields_to_include,
592
+				'Attendee'     => $att_fields_to_include,
593
+			];
594
+			foreach ($model_and_fields_to_include as $model_name => $field_list) {
595
+				$model = EE_Registry::instance()->load_model($model_name);
596
+				foreach ($field_list as $field_name) {
597
+					$field                                                          =
598
+						$model->field_settings_for($field_name);
599
+					$reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
600
+				}
601
+			}
602
+			$registrations_csv_ready_array[] = $reg_csv_array;
603
+		}
604
+		return $registrations_csv_ready_array;
605
+	}
606
+
607
+
608
+	/**
609
+	 * recursively convert MySQL format date strings in query params array to Datetime objects
610
+	 *
611
+	 * @param array        $query_params
612
+	 * @param DateTimeZone $site_timezone
613
+	 * @param DateTimeZone $utc_timezone
614
+	 * @return array
615
+	 * @throws Exception
616
+	 * @since 5.0.19.p
617
+	 */
618
+	private function convertDateStringsToObjects(
619
+		array $query_params,
620
+		DateTimeZone $site_timezone,
621
+		DateTimeZone $utc_timezone
622
+	): array {
623
+		foreach ($query_params as $key => $value) {
624
+			if (is_array($value)) {
625
+				$query_params[$key] = $this->convertDateStringsToObjects($value, $site_timezone, $utc_timezone);
626
+				continue;
627
+			}
628
+			if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $value)) {
629
+				$query_params[$key] = DbSafeDateTime::createFromFormat('Y-m-d H:i:s', $value, $site_timezone);
630
+				$query_params[$key] = $query_params[$key]->setTimezone($utc_timezone);
631
+			}
632
+		}
633
+		return $query_params;
634
+	}
635
+
636
+
637
+	/**
638
+	 * Counts total unit to process
639
+	 *
640
+	 * @param array $query_params
641
+	 * @return int
642
+	 * @throws EE_Error
643
+	 * @throws ReflectionException
644
+	 */
645
+	public function count_units_to_process(array $query_params): int
646
+	{
647
+		return EEM_Registration::instance()->count(
648
+			array_diff_key(
649
+				$query_params,
650
+				array_flip(
651
+					['limit']
652
+				)
653
+			)
654
+		);
655
+	}
656
+
657
+
658
+	/**
659
+	 * Performs any clean-up logic when we know the job is completed.
660
+	 * In this case, we delete the temporary file
661
+	 *
662
+	 * @param JobParameters $job_parameters
663
+	 * @return JobStepResponse
664
+	 */
665
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse
666
+	{
667
+		$this->updateText(esc_html__('File Generation complete and downloaded', 'event_espresso'));
668
+
669
+		$this->_file_helper->delete(
670
+			EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
671
+			true,
672
+			'd'
673
+		);
674
+		$this->updateText(esc_html__('Cleaned up temporary file', 'event_espresso'));
675
+		$this->updateText(
676
+			$this->infoWrapper(
677
+				sprintf(
678
+					esc_html__(
679
+						'If not automatically redirected in %1$s seconds, click here to return to the %2$sRegistrations List Table%3$s',
680
+						'event_espresso'
681
+					),
682
+					'<span id="ee-redirect-timer">10</span>',
683
+					'<a href="' . $job_parameters->request_datum('return_url') . '">',
684
+					'</a>'
685
+				)
686
+			)
687
+		);
688
+		return new JobStepResponse($job_parameters, $this->feedback);
689
+	}
690 690
 }
Please login to merge, or discard this patch.
libraries/line_item_display/EE_SPCO_Line_Item_Display_Strategy.strategy.php 1 patch
Indentation   +721 added lines, -721 removed lines patch added patch discarded remove patch
@@ -13,725 +13,725 @@
 block discarded – undo
13 13
  */
14 14
 class EE_SPCO_Line_Item_Display_Strategy implements EEI_Line_Item_Display
15 15
 {
16
-    use DebugDisplay;
17
-
18
-    protected bool $prices_include_taxes = false;
19
-
20
-    /**
21
-     * array of events
22
-     *
23
-     * @type EE_Line_Item[] $_events
24
-     */
25
-    private array $_events = [];
26
-
27
-    /**
28
-     * whether to display the taxes row or not
29
-     *
30
-     * @type bool $_show_taxes
31
-     */
32
-    private bool $_show_taxes = false;
33
-
34
-    /**
35
-     * html for any tax rows
36
-     *
37
-     * @type string $_show_taxes
38
-     */
39
-    private string $_taxes_html = '';
40
-
41
-    /**
42
-     * total amount including tax we can bill for at this time
43
-     *
44
-     * @type float $_grand_total
45
-     */
46
-    private float $_grand_total = 0.00;
47
-
48
-    /**
49
-     * total number of items being billed for
50
-     *
51
-     * @type int $_total_items
52
-     */
53
-    private int $_total_items = 0;
54
-
55
-    private bool $debug = false;   //  true  false
56
-
57
-
58
-    public function __construct()
59
-    {
60
-        $this->prices_include_taxes = EE_Registry::instance()->CFG->tax_settings->prices_displayed_including_taxes;
61
-        $this->initializeDebugDisplay();
62
-    }
63
-
64
-
65
-    /**
66
-     * @return float
67
-     */
68
-    public function grand_total(): float
69
-    {
70
-        return $this->_grand_total;
71
-    }
72
-
73
-
74
-    /**
75
-     * @return int
76
-     */
77
-    public function total_items(): int
78
-    {
79
-        return $this->_total_items;
80
-    }
81
-
82
-
83
-    /**
84
-     * @param EE_Line_Item      $line_item
85
-     * @param array             $options
86
-     * @param EE_Line_Item|null $parent_line_item
87
-     * @return string
88
-     * @throws EE_Error
89
-     * @throws ReflectionException
90
-     */
91
-    public function display_line_item(
92
-        EE_Line_Item $line_item,
93
-        $options = [],
94
-        ?EE_Line_Item $parent_line_item = null
95
-    ): string {
96
-        $html = '';
97
-        // set some default options and merge with incoming
98
-        $options += [
99
-            'show_desc' => true,  //    true        false
100
-            'odd'       => false,
101
-        ];
102
-
103
-        $this->debugLog('', 2);
104
-        $this->debugLog(__FUNCTION__);
105
-        $this->debugLog($line_item->name() . ': ' . $line_item->code() . ' (' . $line_item->type() . ')');
106
-        if ($line_item->type() === EEM_Line_Item::type_total) {
107
-            $this->debugLog('******************************************************************', 2);
108
-        }
109
-
110
-        switch ($line_item->type()) {
111
-            case EEM_Line_Item::type_line_item:
112
-                $sub_taxes         = EEH_Line_Item::get_nearest_descendant_of_type(
113
-                    $line_item,
114
-                    EEM_Line_Item::type_sub_tax
115
-                );
116
-                $show_taxes = $line_item->is_taxable() || $sub_taxes;
117
-                $this->_show_taxes = $show_taxes ? true : $this->_show_taxes;
118
-                $html              .= $line_item->OBJ_type() === 'Ticket'
119
-                    ? $this->ticketRow($line_item, $options, $show_taxes)
120
-                    : $this->itemRow($line_item, $options, $show_taxes);
121
-                if (
122
-                    apply_filters(
123
-                        'FHEE__EE_SPCO_Line_Item_Display_Strategy__display_line_item__display_sub_line_items',
124
-                        true
125
-                    )
126
-                ) {
127
-                    // got any kids?
128
-                    $child_line_items = $line_item->children();
129
-                    if ($child_line_items) {
130
-                        foreach ($child_line_items as $child_line_item) {
131
-                            $html .= $this->display_line_item($child_line_item, $options, $line_item);
132
-                        }
133
-                    }
134
-                }
135
-                break;
136
-
137
-            case EEM_Line_Item::type_sub_line_item:
138
-                $html .= $this->subItemRow($line_item, $options, $parent_line_item);
139
-                break;
140
-
141
-            case EEM_Line_Item::type_sub_tax:
142
-                $this->_show_taxes = true;
143
-                break;
144
-
145
-            case EEM_Line_Item::type_sub_total:
146
-                static $sub_total = 0;
147
-                $event_sub_total = 0;
148
-                $text            = esc_html__('Sub-Total', 'event_espresso');
149
-                if ($line_item->OBJ_type() === 'Event') {
150
-                    $options['event_id'] = $line_item->OBJ_ID();
151
-                    if (! isset($this->_events[ $options['event_id'] ])) {
152
-                        $this->_events[ $options['event_id'] ] = 0;
153
-                        $event                                 = EEM_Event::instance()->get_one_by_ID(
154
-                            $options['event_id']
155
-                        );
156
-                        $event_name                            = $event instanceof EE_Event ? $event->name() . ' ' : '';
157
-                        // if event has default reg status of Not Approved, then don't display info on it
158
-                        if (
159
-                            $event instanceof EE_Event
160
-                            && $event->default_registration_status() === RegStatus::AWAITING_REVIEW
161
-                        ) {
162
-                            // unless display is forced
163
-                            $display_event = false;
164
-                            // unless there are registrations for it that are returning to pay
165
-                            if (isset($options['registrations']) && is_array($options['registrations'])) {
166
-                                foreach ($options['registrations'] as $registration) {
167
-                                    if (! $registration instanceof EE_Registration) {
168
-                                        continue;
169
-                                    }
170
-                                    $display_event = $registration->event_ID() === $options['event_id']
171
-                                    && $registration->status_ID() !== RegStatus::AWAITING_REVIEW
172
-                                        ? true
173
-                                        : $display_event;
174
-                                }
175
-                            }
176
-                            if (! $display_event) {
177
-                                return '';
178
-                            }
179
-                        }
180
-                        $this->_events[ $options['event_id'] ] = 0;
181
-
182
-                        $html .= $this->eventRow($line_item);
183
-                        $text = $event_name . esc_html__('Event Sub-Total', 'event_espresso');
184
-                    }
185
-                }
186
-                $child_line_items = $line_item->children();
187
-                // loop thru children
188
-                foreach ($child_line_items as $child_line_item) {
189
-                    // recursively feed children back into this method
190
-                    $html .= $this->display_line_item($child_line_item, $options, $line_item);
191
-                }
192
-                if (isset($options['event_id'], $this->_events[ $options['event_id'] ])) {
193
-                    $event_sub_total += $this->_events[ $options['event_id'] ];
194
-                }
195
-                $sub_total += $event_sub_total;
196
-                $this->debugLog(' = count($child_line_items): ' . count($child_line_items), 3);
197
-                $this->debugLog(' = count($this->_events): ' . count($this->_events), 3);
198
-                if (
199
-                    (
200
-                        // event subtotals
201
-                        $line_item->code() !== 'pre-tax-subtotal'
202
-                        && count($child_line_items) > 1
203
-                    ) || (
204
-                        // pre-tax subtotals
205
-                        $line_item->code() === 'pre-tax-subtotal'
206
-                        && count($this->_events) > 1
207
-                    )
208
-                ) {
209
-                    $options['sub_total'] = $line_item->OBJ_type() === 'Event' ? $event_sub_total : $sub_total;
210
-                    $html                 .= $this->subTotalRow($line_item, $text, $options);
211
-                } else {
212
-                    $this->debugLog('NO EVENT SUBTOTAL', 3);
213
-                }
214
-                break;
215
-
216
-            case EEM_Line_Item::type_tax:
217
-                if ($this->_show_taxes) {
218
-                    $this->_taxes_html .= $this->taxRow($line_item, $options);
219
-                }
220
-                break;
221
-
222
-            case EEM_Line_Item::type_tax_sub_total:
223
-                if ($this->_show_taxes) {
224
-                    $child_line_items = $line_item->children();
225
-                    // loop thru children
226
-                    foreach ($child_line_items as $child_line_item) {
227
-                        // recursively feed children back into this method
228
-                        $html .= $this->display_line_item($child_line_item, $options, $line_item);
229
-                    }
230
-                    if ($child_line_items) {
231
-                        $this->_taxes_html .= $this->totalTaxRow(
232
-                            $line_item,
233
-                            esc_html__('Tax Total', 'event_espresso')
234
-                        );
235
-                    }
236
-                }
237
-                break;
238
-
239
-            case EEM_Line_Item::type_total:
240
-                // get all child line items
241
-                $children = $line_item->children();
242
-                // loop thru all non-tax child line items
243
-                foreach ($children as $child_line_item) {
244
-                    if ($child_line_item->type() !== EEM_Line_Item::type_tax_sub_total) {
245
-                        // recursively feed children back into this method
246
-                        $html .= $this->display_line_item($child_line_item, $options, $line_item);
247
-                    }
248
-                }
249
-                // now loop thru  tax child line items
250
-                foreach ($children as $child_line_item) {
251
-                    if ($child_line_item->type() === EEM_Line_Item::type_tax_sub_total) {
252
-                        // recursively feed children back into this method
253
-                        $html .= $this->display_line_item($child_line_item, $options, $line_item);
254
-                    }
255
-                }
256
-                $html .= $this->_taxes_html;
257
-                $html .= $this->totalRow($line_item, esc_html__('Total', 'event_espresso'));
258
-                $html .= $this->paymentsAndAmountOwingRows($line_item, $options);
259
-                break;
260
-        }
261
-        return $html;
262
-    }
263
-
264
-
265
-    /**
266
-     * _event_row - basically a Heading row displayed once above each event's ticket rows
267
-     *
268
-     * @param EE_Line_Item $line_item
269
-     * @return string
270
-     * @throws EE_Error
271
-     * @throws ReflectionException
272
-     */
273
-    private function eventRow(EE_Line_Item $line_item): string
274
-    {
275
-        $this->debugLog(' - ' . __FUNCTION__);
276
-        // start of row
277
-        $html = EEH_HTML::tr('', 'event-cart-total-row', 'total_tr odd');
278
-        // event name td
279
-        $html .= EEH_HTML::td(
280
-            EEH_HTML::strong($line_item->name()),
281
-            '',
282
-            'event-header',
283
-            '',
284
-            ' colspan="4"'
285
-        );
286
-        // end of row
287
-        $html .= EEH_HTML::trx();
288
-        return $html;
289
-    }
290
-
291
-
292
-    /**
293
-     * _ticket_row
294
-     *
295
-     * @param EE_Line_Item $line_item
296
-     * @param array        $options
297
-     * @param bool         $show_taxes
298
-     * @return string
299
-     * @throws EE_Error
300
-     * @throws ReflectionException
301
-     */
302
-    private function ticketRow(EE_Line_Item $line_item, array $options = [], bool $show_taxes = false): string
303
-    {
304
-        $this->debugLog(' - ' . __FUNCTION__);
305
-        // start of row
306
-        $row_class = $options['odd'] ? 'item odd' : 'item';
307
-        $html      = EEH_HTML::tr('', '', $row_class);
308
-        // name && desc
309
-        $name_and_desc = apply_filters(
310
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy__item_row__name',
311
-            $line_item->name(),
312
-            $line_item
313
-        );
314
-        $name_and_desc .= apply_filters(
315
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy__item_row__desc',
316
-            $options['show_desc']
317
-                ? '<span class="line-item-desc-spn smaller-text">: ' . $line_item->desc() . '</span>'
318
-                : '',
319
-            $line_item,
320
-            $options
321
-        );
322
-        $name_and_desc .= $show_taxes ? ' * ' : '';
323
-        $name_and_desc = apply_filters(
324
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy___ticket_row__name_and_desc',
325
-            $name_and_desc,
326
-            $line_item,
327
-            $options
328
-        );
329
-
330
-        // name td
331
-        $html .= EEH_HTML::td(
332
-            $name_and_desc,
333
-            '',
334
-            'item_l'
335
-        );
336
-        // price td
337
-        $price = apply_filters(
338
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy___ticket_row__price',
339
-            $line_item->unit_price_no_code(),
340
-            $line_item
341
-        );
342
-        $html  .= EEH_HTML::td($price, '', 'spco-nowrap item_c jst-rght');
343
-        // quantity td
344
-        $html               .= EEH_HTML::td($line_item->quantity(), '', 'spco-nowrap item_l jst-rght');
345
-        $this->_total_items += $line_item->quantity();
346
-        // determine total for line item
347
-        $total = apply_filters(
348
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy___ticket_row__total',
349
-            $line_item->pretaxTotal(),
350
-            $line_item
351
-        );
352
-        if (isset($this->_events[ $options['event_id'] ])) {
353
-            $this->_events[ $options['event_id'] ] += $total;
354
-        }
355
-        // total td
356
-        $html .= EEH_HTML::td(
357
-            EEH_Template::format_currency($total, false, false),
358
-            '',
359
-            'spco-nowrap item_r jst-rght'
360
-        );
361
-        // end of row
362
-        $html .= EEH_HTML::trx();
363
-        return $html;
364
-    }
365
-
366
-
367
-    /**
368
-     * _item_row
369
-     *
370
-     * @param EE_Line_Item $line_item
371
-     * @param array        $options
372
-     * @param bool         $show_taxes
373
-     * @return string
374
-     * @throws EE_Error
375
-     * @throws ReflectionException
376
-     */
377
-    private function itemRow(EE_Line_Item $line_item, array $options = [], bool $show_taxes = false): string
378
-    {
379
-        $this->debugLog(' - ' . __FUNCTION__);
380
-        // start of row
381
-        $row_class = $options['odd'] ? 'item odd' : 'item';
382
-        $html      = EEH_HTML::tr('', '', $row_class);
383
-        $obj_name  = $line_item->OBJ_type() ? $line_item->OBJ_type_i18n() . ': ' : '';
384
-        // name && desc
385
-        $name_and_desc = apply_filters(
386
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy__item_row__name',
387
-            $obj_name . $line_item->name(),
388
-            $line_item
389
-        );
390
-        $name_and_desc .= apply_filters(
391
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy__item_row__desc',
392
-            (
393
-            $options['show_desc']
394
-                ? '<span class="line-item-desc-spn smaller-text">: ' . $line_item->desc() . '</span>'
395
-                : ''
396
-            ),
397
-            $line_item,
398
-            $options
399
-        );
400
-        $name_and_desc .= $show_taxes ? ' * ' : '';
401
-        $name_and_desc = apply_filters(
402
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy___item_row__name_and_desc',
403
-            $name_and_desc,
404
-            $line_item,
405
-            $options
406
-        );
407
-
408
-        // name td
409
-        $html .= EEH_HTML::td($name_and_desc, '', 'item_l');
410
-        // price td
411
-        if ($line_item->is_percent()) {
412
-            $html .= EEH_HTML::td($line_item->percent() . '%', '', 'spco-nowrap item_c jst-rght');
413
-        } else {
414
-            $html .= EEH_HTML::td($line_item->unit_price_no_code(), '', 'spco-nowrap item_c jst-rght');
415
-        }
416
-        // quantity td
417
-        $html .= EEH_HTML::td($line_item->quantity(), '', 'spco-nowrap item_l jst-rght');
418
-        // $total = $line_item->total() * $line_item->quantity();
419
-        $total = $line_item->total();
420
-        if (isset($options['event_id'], $this->_events[ $options['event_id'] ])) {
421
-            $this->_events[ $options['event_id'] ] += $total;
422
-        }
423
-        // total td
424
-        $html .= EEH_HTML::td(
425
-            EEH_Template::format_currency($total, false, false),
426
-            '',
427
-            'spco-nowrap item_r jst-rght'
428
-        );
429
-        // end of row
430
-        $html .= EEH_HTML::trx();
431
-        return $html;
432
-    }
433
-
434
-
435
-    /**
436
-     * _sub_item_row
437
-     *
438
-     * @param EE_Line_Item      $line_item
439
-     * @param array             $options
440
-     * @param EE_Line_Item|null $parent_line_item
441
-     * @return string
442
-     * @throws EE_Error
443
-     * @throws ReflectionException
444
-     */
445
-    private function subItemRow(
446
-        EE_Line_Item $line_item,
447
-        array $options = [],
448
-        ?EE_Line_Item $parent_line_item = null
449
-    ): string {
450
-        if (
451
-            $parent_line_item instanceof EE_Line_Item
452
-            && $line_item->children() === []
453
-            && $line_item->name() === $parent_line_item->name()
454
-            && apply_filters(
455
-                'FHEE__EE_SPCO_Line_Item_Display_Strategy___sub_item_row__hide_main_sub_line_item',
456
-                true
457
-            )
458
-        ) {
459
-            return '';
460
-        }
461
-        $this->debugLog(' - ' . __FUNCTION__);
462
-        // start of row
463
-        $html = EEH_HTML::tr('', '', 'item sub-item-row');
464
-        // name && desc
465
-        $name_and_desc = EEH_HTML::span('', '', 'sub-item-row-bullet dashicons dashicons-arrow-right');
466
-        $name_and_desc .= $line_item->name();
467
-        $name_and_desc .= $options['show_desc']
468
-            ? '<span class="line-sub-item-desc-spn smaller-text">: ' . $line_item->desc() . '</span>'
469
-            : '';
470
-        // name td
471
-        $html .= EEH_HTML::td($name_and_desc, '', 'item_l sub-item');
472
-        $qty  = $parent_line_item instanceof EE_Line_Item ? $parent_line_item->quantity() : 1;
473
-        // discount/surcharge td
474
-        if ($line_item->is_percent()) {
475
-            $html .= EEH_HTML::td(
476
-                EEH_Template::format_currency(
477
-                    $line_item->total() / $qty,
478
-                    false,
479
-                    false
480
-                ),
481
-                '',
482
-                'spco-nowrap item_c jst-rght'
483
-            );
484
-        } else {
485
-            $html .= EEH_HTML::td($line_item->unit_price_no_code(), '', 'spco-nowrap item_c jst-rght');
486
-        }
487
-        // no quantity td
488
-        $html .= EEH_HTML::td();
489
-        // no total td
490
-        $html .= EEH_HTML::td();
491
-        // end of row
492
-        $html .= EEH_HTML::trx();
493
-        return apply_filters(
494
-            'FHEE__EE_SPCO_Line_Item_Display_Strategy___sub_item_row__html',
495
-            $html,
496
-            $line_item,
497
-            $options,
498
-            $parent_line_item
499
-        );
500
-    }
501
-
502
-
503
-    /**
504
-     * _tax_row
505
-     *
506
-     * @param EE_Line_Item $line_item
507
-     * @param array        $options
508
-     * @return string
509
-     * @throws EE_Error
510
-     * @throws ReflectionException
511
-     */
512
-    private function taxRow(EE_Line_Item $line_item, array $options = []): string
513
-    {
514
-        $this->debugLog(' - ' . __FUNCTION__);
515
-        $total = $line_item->total();
516
-        if (! $total) {
517
-            return '';
518
-        }
519
-        // start of row
520
-        $html = EEH_HTML::tr('', 'item sub-item tax-total');
521
-        // name && desc
522
-        $name_and_desc = $line_item->name();
523
-        $name_and_desc .= '<span class="smaller-text lt-grey-text" style="margin:0 0 0 2em;">';
524
-        $name_and_desc .= esc_html__(' * taxable items', 'event_espresso');
525
-        $name_and_desc .= '</span>';
526
-        $name_and_desc .= $options['show_desc'] ? '<br/>' . $line_item->desc() : '';
527
-        // name td
528
-        $html .= EEH_HTML::td( /*__FUNCTION__ .*/
529
-            $name_and_desc,
530
-            '',
531
-            'item_l sub-item'
532
-        );
533
-        // percent td
534
-        $html .= EEH_HTML::td($line_item->percent() . '%', '', 'spco-nowrap jst-rght');
535
-        // empty td (price)
536
-        $html .= EEH_HTML::td(EEH_HTML::nbsp());
537
-        // total td
538
-        $html .= EEH_HTML::td(
539
-            EEH_Template::format_currency($total, false, false),
540
-            '',
541
-            'spco-nowrap item_r jst-rght'
542
-        );
543
-        // end of row
544
-        $html .= EEH_HTML::trx();
545
-        return $html;
546
-    }
547
-
548
-
549
-    /**
550
-     * _total_row
551
-     *
552
-     * @param EE_Line_Item $line_item
553
-     * @param string       $text
554
-     * @return string
555
-     * @throws EE_Error
556
-     * @throws ReflectionException
557
-     */
558
-    private function totalTaxRow(EE_Line_Item $line_item, string $text = ''): string
559
-    {
560
-        $this->debugLog(' - ' . __FUNCTION__);
561
-        $html = '';
562
-        if ($line_item->total()) {
563
-            // start of row
564
-            $html = EEH_HTML::tr('', '', 'total_tr odd');
565
-            // total td
566
-            $html .= EEH_HTML::td(
567
-                $text,
568
-                '',
569
-                'total_currency total jst-rght',
570
-                '',
571
-                ' colspan="2"'
572
-            );
573
-            // empty td (price)
574
-            $html .= EEH_HTML::td(EEH_HTML::nbsp());
575
-            // total td
576
-            $html .= EEH_HTML::td(
577
-                EEH_Template::format_currency($line_item->total(), false, false),
578
-                '',
579
-                'spco-nowrap total jst-rght'
580
-            );
581
-            // end of row
582
-            $html .= EEH_HTML::trx();
583
-        }
584
-        return $html;
585
-    }
586
-
587
-
588
-    /**
589
-     * _total_row
590
-     *
591
-     * @param EE_Line_Item $line_item
592
-     * @param string       $text
593
-     * @param array        $options
594
-     * @return string
595
-     * @throws EE_Error
596
-     * @throws ReflectionException
597
-     */
598
-    private function subTotalRow(EE_Line_Item $line_item, string $text = '', array $options = []): string
599
-    {
600
-        $this->debugLog(' - ' . __FUNCTION__);
601
-        $html = '';
602
-        if ($line_item->total()) {
603
-            // start of row
604
-            $html = EEH_HTML::tr('', '', 'total_tr odd');
605
-            // total td
606
-            $html .= EEH_HTML::td(
607
-                $text,
608
-                '',
609
-                'total_currency total jst-rght',
610
-                '',
611
-                ' colspan="3"'
612
-            );
613
-            // total td
614
-            $html .= EEH_HTML::td(
615
-                EEH_Template::format_currency($options['sub_total'], false, false),
616
-                '',
617
-                'spco-nowrap total jst-rght'
618
-            );
619
-            // end of row
620
-            $html .= EEH_HTML::trx();
621
-        }
622
-        return $html;
623
-    }
624
-
625
-
626
-    /**
627
-     * _total_row
628
-     *
629
-     * @param EE_Line_Item $line_item
630
-     * @param string       $text
631
-     * @return string
632
-     * @throws EE_Error
633
-     * @throws ReflectionException
634
-     */
635
-    private function totalRow(EE_Line_Item $line_item, string $text = ''): string
636
-    {
637
-        $this->debugLog(' - ' . __FUNCTION__);
638
-        // start of row
639
-        $html = EEH_HTML::tr('', '', 'spco-grand-total total_tr odd');
640
-        // total td
641
-        $html .= EEH_HTML::td($text, '', 'total_currency total jst-rght', '', ' colspan="3"');
642
-        // total td
643
-        $html .= EEH_HTML::td(
644
-            EEH_Template::format_currency($line_item->total(), false, false),
645
-            '',
646
-            'spco-nowrap total jst-rght'
647
-        );
648
-        // end of row
649
-        $html .= EEH_HTML::trx();
650
-        return $html;
651
-    }
652
-
653
-
654
-    /**
655
-     * _payments_and_amount_owing_rows
656
-     *
657
-     * @param EE_Line_Item $line_item
658
-     * @param array        $options
659
-     * @return string
660
-     * @throws EE_Error
661
-     * @throws ReflectionException
662
-     */
663
-    private function paymentsAndAmountOwingRows(EE_Line_Item $line_item, array $options = []): string
664
-    {
665
-        $this->debugLog(' - ' . __FUNCTION__);
666
-        $html        = '';
667
-        $owing       = $line_item->total();
668
-        $transaction = EEM_Transaction::instance()->get_one_by_ID($line_item->TXN_ID());
669
-        if ($transaction instanceof EE_Transaction) {
670
-            $registration_payments = [];
671
-            $registrations         = ! empty($options['registrations'])
672
-                ? $options['registrations']
673
-                : $transaction->registrations();
674
-            foreach ($registrations as $registration) {
675
-                if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
676
-                    $registration_payments += $registration->registration_payments();
677
-                }
678
-            }
679
-            if (! empty($registration_payments)) {
680
-                foreach ($registration_payments as $registration_payment) {
681
-                    if ($registration_payment instanceof EE_Registration_Payment) {
682
-                        $owing        -= $registration_payment->amount();
683
-                        $payment      = $registration_payment->payment();
684
-                        $payment_desc = '';
685
-                        if ($payment instanceof EE_Payment) {
686
-                            $payment_desc = sprintf(
687
-                                esc_html__('Payment%1$s Received: %2$s', 'event_espresso'),
688
-                                $payment->txn_id_chq_nmbr() !== ''
689
-                                    ? ' <span class="small-text">(#' . $payment->txn_id_chq_nmbr() . ')</span> '
690
-                                    : '',
691
-                                $payment->timestamp()
692
-                            );
693
-                        }
694
-                        // start of row
695
-                        $html .= EEH_HTML::tr('', '', 'total_tr odd');
696
-                        // payment desc
697
-                        $html .= EEH_HTML::td($payment_desc, '', '', '', ' colspan="3"');
698
-                        // total td
699
-                        $html .= EEH_HTML::td(
700
-                            EEH_Template::format_currency(
701
-                                $registration_payment->amount(),
702
-                                false,
703
-                                false
704
-                            ),
705
-                            '',
706
-                            'spco-nowrap total jst-rght'
707
-                        );
708
-                        // end of row
709
-                        $html .= EEH_HTML::trx();
710
-                    }
711
-                }
712
-                if ($line_item->total()) {
713
-                    // start of row
714
-                    $html .= EEH_HTML::tr('', '', 'total_tr odd');
715
-                    // total td
716
-                    $html .= EEH_HTML::td(
717
-                        esc_html__('Amount Owing', 'event_espresso'),
718
-                        '',
719
-                        'total_currency total jst-rght',
720
-                        '',
721
-                        ' colspan="3"'
722
-                    );
723
-                    // total td
724
-                    $html .= EEH_HTML::td(
725
-                        EEH_Template::format_currency($owing, false, false),
726
-                        '',
727
-                        'spco-nowrap total jst-rght'
728
-                    );
729
-                    // end of row
730
-                    $html .= EEH_HTML::trx();
731
-                }
732
-            }
733
-        }
734
-        $this->_grand_total = $owing;
735
-        return $html;
736
-    }
16
+	use DebugDisplay;
17
+
18
+	protected bool $prices_include_taxes = false;
19
+
20
+	/**
21
+	 * array of events
22
+	 *
23
+	 * @type EE_Line_Item[] $_events
24
+	 */
25
+	private array $_events = [];
26
+
27
+	/**
28
+	 * whether to display the taxes row or not
29
+	 *
30
+	 * @type bool $_show_taxes
31
+	 */
32
+	private bool $_show_taxes = false;
33
+
34
+	/**
35
+	 * html for any tax rows
36
+	 *
37
+	 * @type string $_show_taxes
38
+	 */
39
+	private string $_taxes_html = '';
40
+
41
+	/**
42
+	 * total amount including tax we can bill for at this time
43
+	 *
44
+	 * @type float $_grand_total
45
+	 */
46
+	private float $_grand_total = 0.00;
47
+
48
+	/**
49
+	 * total number of items being billed for
50
+	 *
51
+	 * @type int $_total_items
52
+	 */
53
+	private int $_total_items = 0;
54
+
55
+	private bool $debug = false;   //  true  false
56
+
57
+
58
+	public function __construct()
59
+	{
60
+		$this->prices_include_taxes = EE_Registry::instance()->CFG->tax_settings->prices_displayed_including_taxes;
61
+		$this->initializeDebugDisplay();
62
+	}
63
+
64
+
65
+	/**
66
+	 * @return float
67
+	 */
68
+	public function grand_total(): float
69
+	{
70
+		return $this->_grand_total;
71
+	}
72
+
73
+
74
+	/**
75
+	 * @return int
76
+	 */
77
+	public function total_items(): int
78
+	{
79
+		return $this->_total_items;
80
+	}
81
+
82
+
83
+	/**
84
+	 * @param EE_Line_Item      $line_item
85
+	 * @param array             $options
86
+	 * @param EE_Line_Item|null $parent_line_item
87
+	 * @return string
88
+	 * @throws EE_Error
89
+	 * @throws ReflectionException
90
+	 */
91
+	public function display_line_item(
92
+		EE_Line_Item $line_item,
93
+		$options = [],
94
+		?EE_Line_Item $parent_line_item = null
95
+	): string {
96
+		$html = '';
97
+		// set some default options and merge with incoming
98
+		$options += [
99
+			'show_desc' => true,  //    true        false
100
+			'odd'       => false,
101
+		];
102
+
103
+		$this->debugLog('', 2);
104
+		$this->debugLog(__FUNCTION__);
105
+		$this->debugLog($line_item->name() . ': ' . $line_item->code() . ' (' . $line_item->type() . ')');
106
+		if ($line_item->type() === EEM_Line_Item::type_total) {
107
+			$this->debugLog('******************************************************************', 2);
108
+		}
109
+
110
+		switch ($line_item->type()) {
111
+			case EEM_Line_Item::type_line_item:
112
+				$sub_taxes         = EEH_Line_Item::get_nearest_descendant_of_type(
113
+					$line_item,
114
+					EEM_Line_Item::type_sub_tax
115
+				);
116
+				$show_taxes = $line_item->is_taxable() || $sub_taxes;
117
+				$this->_show_taxes = $show_taxes ? true : $this->_show_taxes;
118
+				$html              .= $line_item->OBJ_type() === 'Ticket'
119
+					? $this->ticketRow($line_item, $options, $show_taxes)
120
+					: $this->itemRow($line_item, $options, $show_taxes);
121
+				if (
122
+					apply_filters(
123
+						'FHEE__EE_SPCO_Line_Item_Display_Strategy__display_line_item__display_sub_line_items',
124
+						true
125
+					)
126
+				) {
127
+					// got any kids?
128
+					$child_line_items = $line_item->children();
129
+					if ($child_line_items) {
130
+						foreach ($child_line_items as $child_line_item) {
131
+							$html .= $this->display_line_item($child_line_item, $options, $line_item);
132
+						}
133
+					}
134
+				}
135
+				break;
136
+
137
+			case EEM_Line_Item::type_sub_line_item:
138
+				$html .= $this->subItemRow($line_item, $options, $parent_line_item);
139
+				break;
140
+
141
+			case EEM_Line_Item::type_sub_tax:
142
+				$this->_show_taxes = true;
143
+				break;
144
+
145
+			case EEM_Line_Item::type_sub_total:
146
+				static $sub_total = 0;
147
+				$event_sub_total = 0;
148
+				$text            = esc_html__('Sub-Total', 'event_espresso');
149
+				if ($line_item->OBJ_type() === 'Event') {
150
+					$options['event_id'] = $line_item->OBJ_ID();
151
+					if (! isset($this->_events[ $options['event_id'] ])) {
152
+						$this->_events[ $options['event_id'] ] = 0;
153
+						$event                                 = EEM_Event::instance()->get_one_by_ID(
154
+							$options['event_id']
155
+						);
156
+						$event_name                            = $event instanceof EE_Event ? $event->name() . ' ' : '';
157
+						// if event has default reg status of Not Approved, then don't display info on it
158
+						if (
159
+							$event instanceof EE_Event
160
+							&& $event->default_registration_status() === RegStatus::AWAITING_REVIEW
161
+						) {
162
+							// unless display is forced
163
+							$display_event = false;
164
+							// unless there are registrations for it that are returning to pay
165
+							if (isset($options['registrations']) && is_array($options['registrations'])) {
166
+								foreach ($options['registrations'] as $registration) {
167
+									if (! $registration instanceof EE_Registration) {
168
+										continue;
169
+									}
170
+									$display_event = $registration->event_ID() === $options['event_id']
171
+									&& $registration->status_ID() !== RegStatus::AWAITING_REVIEW
172
+										? true
173
+										: $display_event;
174
+								}
175
+							}
176
+							if (! $display_event) {
177
+								return '';
178
+							}
179
+						}
180
+						$this->_events[ $options['event_id'] ] = 0;
181
+
182
+						$html .= $this->eventRow($line_item);
183
+						$text = $event_name . esc_html__('Event Sub-Total', 'event_espresso');
184
+					}
185
+				}
186
+				$child_line_items = $line_item->children();
187
+				// loop thru children
188
+				foreach ($child_line_items as $child_line_item) {
189
+					// recursively feed children back into this method
190
+					$html .= $this->display_line_item($child_line_item, $options, $line_item);
191
+				}
192
+				if (isset($options['event_id'], $this->_events[ $options['event_id'] ])) {
193
+					$event_sub_total += $this->_events[ $options['event_id'] ];
194
+				}
195
+				$sub_total += $event_sub_total;
196
+				$this->debugLog(' = count($child_line_items): ' . count($child_line_items), 3);
197
+				$this->debugLog(' = count($this->_events): ' . count($this->_events), 3);
198
+				if (
199
+					(
200
+						// event subtotals
201
+						$line_item->code() !== 'pre-tax-subtotal'
202
+						&& count($child_line_items) > 1
203
+					) || (
204
+						// pre-tax subtotals
205
+						$line_item->code() === 'pre-tax-subtotal'
206
+						&& count($this->_events) > 1
207
+					)
208
+				) {
209
+					$options['sub_total'] = $line_item->OBJ_type() === 'Event' ? $event_sub_total : $sub_total;
210
+					$html                 .= $this->subTotalRow($line_item, $text, $options);
211
+				} else {
212
+					$this->debugLog('NO EVENT SUBTOTAL', 3);
213
+				}
214
+				break;
215
+
216
+			case EEM_Line_Item::type_tax:
217
+				if ($this->_show_taxes) {
218
+					$this->_taxes_html .= $this->taxRow($line_item, $options);
219
+				}
220
+				break;
221
+
222
+			case EEM_Line_Item::type_tax_sub_total:
223
+				if ($this->_show_taxes) {
224
+					$child_line_items = $line_item->children();
225
+					// loop thru children
226
+					foreach ($child_line_items as $child_line_item) {
227
+						// recursively feed children back into this method
228
+						$html .= $this->display_line_item($child_line_item, $options, $line_item);
229
+					}
230
+					if ($child_line_items) {
231
+						$this->_taxes_html .= $this->totalTaxRow(
232
+							$line_item,
233
+							esc_html__('Tax Total', 'event_espresso')
234
+						);
235
+					}
236
+				}
237
+				break;
238
+
239
+			case EEM_Line_Item::type_total:
240
+				// get all child line items
241
+				$children = $line_item->children();
242
+				// loop thru all non-tax child line items
243
+				foreach ($children as $child_line_item) {
244
+					if ($child_line_item->type() !== EEM_Line_Item::type_tax_sub_total) {
245
+						// recursively feed children back into this method
246
+						$html .= $this->display_line_item($child_line_item, $options, $line_item);
247
+					}
248
+				}
249
+				// now loop thru  tax child line items
250
+				foreach ($children as $child_line_item) {
251
+					if ($child_line_item->type() === EEM_Line_Item::type_tax_sub_total) {
252
+						// recursively feed children back into this method
253
+						$html .= $this->display_line_item($child_line_item, $options, $line_item);
254
+					}
255
+				}
256
+				$html .= $this->_taxes_html;
257
+				$html .= $this->totalRow($line_item, esc_html__('Total', 'event_espresso'));
258
+				$html .= $this->paymentsAndAmountOwingRows($line_item, $options);
259
+				break;
260
+		}
261
+		return $html;
262
+	}
263
+
264
+
265
+	/**
266
+	 * _event_row - basically a Heading row displayed once above each event's ticket rows
267
+	 *
268
+	 * @param EE_Line_Item $line_item
269
+	 * @return string
270
+	 * @throws EE_Error
271
+	 * @throws ReflectionException
272
+	 */
273
+	private function eventRow(EE_Line_Item $line_item): string
274
+	{
275
+		$this->debugLog(' - ' . __FUNCTION__);
276
+		// start of row
277
+		$html = EEH_HTML::tr('', 'event-cart-total-row', 'total_tr odd');
278
+		// event name td
279
+		$html .= EEH_HTML::td(
280
+			EEH_HTML::strong($line_item->name()),
281
+			'',
282
+			'event-header',
283
+			'',
284
+			' colspan="4"'
285
+		);
286
+		// end of row
287
+		$html .= EEH_HTML::trx();
288
+		return $html;
289
+	}
290
+
291
+
292
+	/**
293
+	 * _ticket_row
294
+	 *
295
+	 * @param EE_Line_Item $line_item
296
+	 * @param array        $options
297
+	 * @param bool         $show_taxes
298
+	 * @return string
299
+	 * @throws EE_Error
300
+	 * @throws ReflectionException
301
+	 */
302
+	private function ticketRow(EE_Line_Item $line_item, array $options = [], bool $show_taxes = false): string
303
+	{
304
+		$this->debugLog(' - ' . __FUNCTION__);
305
+		// start of row
306
+		$row_class = $options['odd'] ? 'item odd' : 'item';
307
+		$html      = EEH_HTML::tr('', '', $row_class);
308
+		// name && desc
309
+		$name_and_desc = apply_filters(
310
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy__item_row__name',
311
+			$line_item->name(),
312
+			$line_item
313
+		);
314
+		$name_and_desc .= apply_filters(
315
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy__item_row__desc',
316
+			$options['show_desc']
317
+				? '<span class="line-item-desc-spn smaller-text">: ' . $line_item->desc() . '</span>'
318
+				: '',
319
+			$line_item,
320
+			$options
321
+		);
322
+		$name_and_desc .= $show_taxes ? ' * ' : '';
323
+		$name_and_desc = apply_filters(
324
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy___ticket_row__name_and_desc',
325
+			$name_and_desc,
326
+			$line_item,
327
+			$options
328
+		);
329
+
330
+		// name td
331
+		$html .= EEH_HTML::td(
332
+			$name_and_desc,
333
+			'',
334
+			'item_l'
335
+		);
336
+		// price td
337
+		$price = apply_filters(
338
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy___ticket_row__price',
339
+			$line_item->unit_price_no_code(),
340
+			$line_item
341
+		);
342
+		$html  .= EEH_HTML::td($price, '', 'spco-nowrap item_c jst-rght');
343
+		// quantity td
344
+		$html               .= EEH_HTML::td($line_item->quantity(), '', 'spco-nowrap item_l jst-rght');
345
+		$this->_total_items += $line_item->quantity();
346
+		// determine total for line item
347
+		$total = apply_filters(
348
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy___ticket_row__total',
349
+			$line_item->pretaxTotal(),
350
+			$line_item
351
+		);
352
+		if (isset($this->_events[ $options['event_id'] ])) {
353
+			$this->_events[ $options['event_id'] ] += $total;
354
+		}
355
+		// total td
356
+		$html .= EEH_HTML::td(
357
+			EEH_Template::format_currency($total, false, false),
358
+			'',
359
+			'spco-nowrap item_r jst-rght'
360
+		);
361
+		// end of row
362
+		$html .= EEH_HTML::trx();
363
+		return $html;
364
+	}
365
+
366
+
367
+	/**
368
+	 * _item_row
369
+	 *
370
+	 * @param EE_Line_Item $line_item
371
+	 * @param array        $options
372
+	 * @param bool         $show_taxes
373
+	 * @return string
374
+	 * @throws EE_Error
375
+	 * @throws ReflectionException
376
+	 */
377
+	private function itemRow(EE_Line_Item $line_item, array $options = [], bool $show_taxes = false): string
378
+	{
379
+		$this->debugLog(' - ' . __FUNCTION__);
380
+		// start of row
381
+		$row_class = $options['odd'] ? 'item odd' : 'item';
382
+		$html      = EEH_HTML::tr('', '', $row_class);
383
+		$obj_name  = $line_item->OBJ_type() ? $line_item->OBJ_type_i18n() . ': ' : '';
384
+		// name && desc
385
+		$name_and_desc = apply_filters(
386
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy__item_row__name',
387
+			$obj_name . $line_item->name(),
388
+			$line_item
389
+		);
390
+		$name_and_desc .= apply_filters(
391
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy__item_row__desc',
392
+			(
393
+			$options['show_desc']
394
+				? '<span class="line-item-desc-spn smaller-text">: ' . $line_item->desc() . '</span>'
395
+				: ''
396
+			),
397
+			$line_item,
398
+			$options
399
+		);
400
+		$name_and_desc .= $show_taxes ? ' * ' : '';
401
+		$name_and_desc = apply_filters(
402
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy___item_row__name_and_desc',
403
+			$name_and_desc,
404
+			$line_item,
405
+			$options
406
+		);
407
+
408
+		// name td
409
+		$html .= EEH_HTML::td($name_and_desc, '', 'item_l');
410
+		// price td
411
+		if ($line_item->is_percent()) {
412
+			$html .= EEH_HTML::td($line_item->percent() . '%', '', 'spco-nowrap item_c jst-rght');
413
+		} else {
414
+			$html .= EEH_HTML::td($line_item->unit_price_no_code(), '', 'spco-nowrap item_c jst-rght');
415
+		}
416
+		// quantity td
417
+		$html .= EEH_HTML::td($line_item->quantity(), '', 'spco-nowrap item_l jst-rght');
418
+		// $total = $line_item->total() * $line_item->quantity();
419
+		$total = $line_item->total();
420
+		if (isset($options['event_id'], $this->_events[ $options['event_id'] ])) {
421
+			$this->_events[ $options['event_id'] ] += $total;
422
+		}
423
+		// total td
424
+		$html .= EEH_HTML::td(
425
+			EEH_Template::format_currency($total, false, false),
426
+			'',
427
+			'spco-nowrap item_r jst-rght'
428
+		);
429
+		// end of row
430
+		$html .= EEH_HTML::trx();
431
+		return $html;
432
+	}
433
+
434
+
435
+	/**
436
+	 * _sub_item_row
437
+	 *
438
+	 * @param EE_Line_Item      $line_item
439
+	 * @param array             $options
440
+	 * @param EE_Line_Item|null $parent_line_item
441
+	 * @return string
442
+	 * @throws EE_Error
443
+	 * @throws ReflectionException
444
+	 */
445
+	private function subItemRow(
446
+		EE_Line_Item $line_item,
447
+		array $options = [],
448
+		?EE_Line_Item $parent_line_item = null
449
+	): string {
450
+		if (
451
+			$parent_line_item instanceof EE_Line_Item
452
+			&& $line_item->children() === []
453
+			&& $line_item->name() === $parent_line_item->name()
454
+			&& apply_filters(
455
+				'FHEE__EE_SPCO_Line_Item_Display_Strategy___sub_item_row__hide_main_sub_line_item',
456
+				true
457
+			)
458
+		) {
459
+			return '';
460
+		}
461
+		$this->debugLog(' - ' . __FUNCTION__);
462
+		// start of row
463
+		$html = EEH_HTML::tr('', '', 'item sub-item-row');
464
+		// name && desc
465
+		$name_and_desc = EEH_HTML::span('', '', 'sub-item-row-bullet dashicons dashicons-arrow-right');
466
+		$name_and_desc .= $line_item->name();
467
+		$name_and_desc .= $options['show_desc']
468
+			? '<span class="line-sub-item-desc-spn smaller-text">: ' . $line_item->desc() . '</span>'
469
+			: '';
470
+		// name td
471
+		$html .= EEH_HTML::td($name_and_desc, '', 'item_l sub-item');
472
+		$qty  = $parent_line_item instanceof EE_Line_Item ? $parent_line_item->quantity() : 1;
473
+		// discount/surcharge td
474
+		if ($line_item->is_percent()) {
475
+			$html .= EEH_HTML::td(
476
+				EEH_Template::format_currency(
477
+					$line_item->total() / $qty,
478
+					false,
479
+					false
480
+				),
481
+				'',
482
+				'spco-nowrap item_c jst-rght'
483
+			);
484
+		} else {
485
+			$html .= EEH_HTML::td($line_item->unit_price_no_code(), '', 'spco-nowrap item_c jst-rght');
486
+		}
487
+		// no quantity td
488
+		$html .= EEH_HTML::td();
489
+		// no total td
490
+		$html .= EEH_HTML::td();
491
+		// end of row
492
+		$html .= EEH_HTML::trx();
493
+		return apply_filters(
494
+			'FHEE__EE_SPCO_Line_Item_Display_Strategy___sub_item_row__html',
495
+			$html,
496
+			$line_item,
497
+			$options,
498
+			$parent_line_item
499
+		);
500
+	}
501
+
502
+
503
+	/**
504
+	 * _tax_row
505
+	 *
506
+	 * @param EE_Line_Item $line_item
507
+	 * @param array        $options
508
+	 * @return string
509
+	 * @throws EE_Error
510
+	 * @throws ReflectionException
511
+	 */
512
+	private function taxRow(EE_Line_Item $line_item, array $options = []): string
513
+	{
514
+		$this->debugLog(' - ' . __FUNCTION__);
515
+		$total = $line_item->total();
516
+		if (! $total) {
517
+			return '';
518
+		}
519
+		// start of row
520
+		$html = EEH_HTML::tr('', 'item sub-item tax-total');
521
+		// name && desc
522
+		$name_and_desc = $line_item->name();
523
+		$name_and_desc .= '<span class="smaller-text lt-grey-text" style="margin:0 0 0 2em;">';
524
+		$name_and_desc .= esc_html__(' * taxable items', 'event_espresso');
525
+		$name_and_desc .= '</span>';
526
+		$name_and_desc .= $options['show_desc'] ? '<br/>' . $line_item->desc() : '';
527
+		// name td
528
+		$html .= EEH_HTML::td( /*__FUNCTION__ .*/
529
+			$name_and_desc,
530
+			'',
531
+			'item_l sub-item'
532
+		);
533
+		// percent td
534
+		$html .= EEH_HTML::td($line_item->percent() . '%', '', 'spco-nowrap jst-rght');
535
+		// empty td (price)
536
+		$html .= EEH_HTML::td(EEH_HTML::nbsp());
537
+		// total td
538
+		$html .= EEH_HTML::td(
539
+			EEH_Template::format_currency($total, false, false),
540
+			'',
541
+			'spco-nowrap item_r jst-rght'
542
+		);
543
+		// end of row
544
+		$html .= EEH_HTML::trx();
545
+		return $html;
546
+	}
547
+
548
+
549
+	/**
550
+	 * _total_row
551
+	 *
552
+	 * @param EE_Line_Item $line_item
553
+	 * @param string       $text
554
+	 * @return string
555
+	 * @throws EE_Error
556
+	 * @throws ReflectionException
557
+	 */
558
+	private function totalTaxRow(EE_Line_Item $line_item, string $text = ''): string
559
+	{
560
+		$this->debugLog(' - ' . __FUNCTION__);
561
+		$html = '';
562
+		if ($line_item->total()) {
563
+			// start of row
564
+			$html = EEH_HTML::tr('', '', 'total_tr odd');
565
+			// total td
566
+			$html .= EEH_HTML::td(
567
+				$text,
568
+				'',
569
+				'total_currency total jst-rght',
570
+				'',
571
+				' colspan="2"'
572
+			);
573
+			// empty td (price)
574
+			$html .= EEH_HTML::td(EEH_HTML::nbsp());
575
+			// total td
576
+			$html .= EEH_HTML::td(
577
+				EEH_Template::format_currency($line_item->total(), false, false),
578
+				'',
579
+				'spco-nowrap total jst-rght'
580
+			);
581
+			// end of row
582
+			$html .= EEH_HTML::trx();
583
+		}
584
+		return $html;
585
+	}
586
+
587
+
588
+	/**
589
+	 * _total_row
590
+	 *
591
+	 * @param EE_Line_Item $line_item
592
+	 * @param string       $text
593
+	 * @param array        $options
594
+	 * @return string
595
+	 * @throws EE_Error
596
+	 * @throws ReflectionException
597
+	 */
598
+	private function subTotalRow(EE_Line_Item $line_item, string $text = '', array $options = []): string
599
+	{
600
+		$this->debugLog(' - ' . __FUNCTION__);
601
+		$html = '';
602
+		if ($line_item->total()) {
603
+			// start of row
604
+			$html = EEH_HTML::tr('', '', 'total_tr odd');
605
+			// total td
606
+			$html .= EEH_HTML::td(
607
+				$text,
608
+				'',
609
+				'total_currency total jst-rght',
610
+				'',
611
+				' colspan="3"'
612
+			);
613
+			// total td
614
+			$html .= EEH_HTML::td(
615
+				EEH_Template::format_currency($options['sub_total'], false, false),
616
+				'',
617
+				'spco-nowrap total jst-rght'
618
+			);
619
+			// end of row
620
+			$html .= EEH_HTML::trx();
621
+		}
622
+		return $html;
623
+	}
624
+
625
+
626
+	/**
627
+	 * _total_row
628
+	 *
629
+	 * @param EE_Line_Item $line_item
630
+	 * @param string       $text
631
+	 * @return string
632
+	 * @throws EE_Error
633
+	 * @throws ReflectionException
634
+	 */
635
+	private function totalRow(EE_Line_Item $line_item, string $text = ''): string
636
+	{
637
+		$this->debugLog(' - ' . __FUNCTION__);
638
+		// start of row
639
+		$html = EEH_HTML::tr('', '', 'spco-grand-total total_tr odd');
640
+		// total td
641
+		$html .= EEH_HTML::td($text, '', 'total_currency total jst-rght', '', ' colspan="3"');
642
+		// total td
643
+		$html .= EEH_HTML::td(
644
+			EEH_Template::format_currency($line_item->total(), false, false),
645
+			'',
646
+			'spco-nowrap total jst-rght'
647
+		);
648
+		// end of row
649
+		$html .= EEH_HTML::trx();
650
+		return $html;
651
+	}
652
+
653
+
654
+	/**
655
+	 * _payments_and_amount_owing_rows
656
+	 *
657
+	 * @param EE_Line_Item $line_item
658
+	 * @param array        $options
659
+	 * @return string
660
+	 * @throws EE_Error
661
+	 * @throws ReflectionException
662
+	 */
663
+	private function paymentsAndAmountOwingRows(EE_Line_Item $line_item, array $options = []): string
664
+	{
665
+		$this->debugLog(' - ' . __FUNCTION__);
666
+		$html        = '';
667
+		$owing       = $line_item->total();
668
+		$transaction = EEM_Transaction::instance()->get_one_by_ID($line_item->TXN_ID());
669
+		if ($transaction instanceof EE_Transaction) {
670
+			$registration_payments = [];
671
+			$registrations         = ! empty($options['registrations'])
672
+				? $options['registrations']
673
+				: $transaction->registrations();
674
+			foreach ($registrations as $registration) {
675
+				if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
676
+					$registration_payments += $registration->registration_payments();
677
+				}
678
+			}
679
+			if (! empty($registration_payments)) {
680
+				foreach ($registration_payments as $registration_payment) {
681
+					if ($registration_payment instanceof EE_Registration_Payment) {
682
+						$owing        -= $registration_payment->amount();
683
+						$payment      = $registration_payment->payment();
684
+						$payment_desc = '';
685
+						if ($payment instanceof EE_Payment) {
686
+							$payment_desc = sprintf(
687
+								esc_html__('Payment%1$s Received: %2$s', 'event_espresso'),
688
+								$payment->txn_id_chq_nmbr() !== ''
689
+									? ' <span class="small-text">(#' . $payment->txn_id_chq_nmbr() . ')</span> '
690
+									: '',
691
+								$payment->timestamp()
692
+							);
693
+						}
694
+						// start of row
695
+						$html .= EEH_HTML::tr('', '', 'total_tr odd');
696
+						// payment desc
697
+						$html .= EEH_HTML::td($payment_desc, '', '', '', ' colspan="3"');
698
+						// total td
699
+						$html .= EEH_HTML::td(
700
+							EEH_Template::format_currency(
701
+								$registration_payment->amount(),
702
+								false,
703
+								false
704
+							),
705
+							'',
706
+							'spco-nowrap total jst-rght'
707
+						);
708
+						// end of row
709
+						$html .= EEH_HTML::trx();
710
+					}
711
+				}
712
+				if ($line_item->total()) {
713
+					// start of row
714
+					$html .= EEH_HTML::tr('', '', 'total_tr odd');
715
+					// total td
716
+					$html .= EEH_HTML::td(
717
+						esc_html__('Amount Owing', 'event_espresso'),
718
+						'',
719
+						'total_currency total jst-rght',
720
+						'',
721
+						' colspan="3"'
722
+					);
723
+					// total td
724
+					$html .= EEH_HTML::td(
725
+						EEH_Template::format_currency($owing, false, false),
726
+						'',
727
+						'spco-nowrap total jst-rght'
728
+					);
729
+					// end of row
730
+					$html .= EEH_HTML::trx();
731
+				}
732
+			}
733
+		}
734
+		$this->_grand_total = $owing;
735
+		return $html;
736
+	}
737 737
 }
Please login to merge, or discard this patch.
core/EE_Network_Config.core.php 1 patch
Indentation   +198 added lines, -198 removed lines patch added patch discarded remove patch
@@ -13,180 +13,180 @@  discard block
 block discarded – undo
13 13
  */
14 14
 final class EE_Network_Config
15 15
 {
16
-    /**
17
-     * @var EE_Network_Config $_instance
18
-     */
19
-    private static $_instance;
20
-
21
-    /**
22
-     * addons can add their specific network_config objects to this property
23
-     *
24
-     * @var EE_Config_Base[] $addons
25
-     */
26
-    public $addons;
27
-
28
-    /**
29
-     * @var EE_Network_Core_Config $core
30
-     */
31
-    public $core;
32
-
33
-
34
-    /**
35
-     * @singleton method used to instantiate class object
36
-     * @return EE_Network_Config instance
37
-     */
38
-    public static function instance()
39
-    {
40
-        // check if class object is instantiated, and instantiated properly
41
-        if (! self::$_instance instanceof EE_Network_Config) {
42
-            self::$_instance = new self();
43
-        }
44
-        return self::$_instance;
45
-    }
46
-
47
-
48
-    /**
49
-     * class constructor
50
-     */
51
-    private function __construct()
52
-    {
53
-        do_action('AHEE__EE_Network_Config__construct__begin', $this);
54
-        // set defaults
55
-        $this->core = apply_filters('FHEE__EE_Network_Config___construct__core', new EE_Network_Core_Config());
56
-        $this->addons = array();
57
-
58
-        $this->_load_config();
59
-
60
-        // construct__end hook
61
-        do_action('AHEE__EE_Network_Config__construct__end', $this);
62
-    }
63
-
64
-
65
-    /**
66
-     * load EE Network Config options
67
-     *
68
-     * @return void
69
-     */
70
-    private function _load_config()
71
-    {
72
-        // load network config start hook
73
-        do_action('AHEE__EE_Network_Config___load_config__start', $this);
74
-        $config = $this->get_config();
75
-        foreach ($config as $config_prop => $settings) {
76
-            if ($config_prop === 'core' && ! $settings instanceof EE_Network_Core_Config) {
77
-                $core = new EE_Network_Core_Config();
78
-                foreach ($settings as $prop => $setting) {
79
-                    if (property_exists($core, $prop)) {
80
-                        $core->{$prop} = $setting;
81
-                    }
82
-                }
83
-                $settings = $core;
84
-                add_filter('FHEE__EE_Network_Config___load_config__update_network_config', '__return_true');
85
-            }
86
-            if (is_object($settings) && property_exists($this, $config_prop)) {
87
-                $this->{$config_prop} = apply_filters(
88
-                    'FHEE__EE_Network_Config___load_config__config_settings',
89
-                    $settings,
90
-                    $config_prop,
91
-                    $this
92
-                );
93
-                if (method_exists($settings, 'populate')) {
94
-                    $this->{$config_prop}->populate();
95
-                }
96
-                if (method_exists($settings, 'do_hooks')) {
97
-                    $this->{$config_prop}->do_hooks();
98
-                }
99
-            }
100
-        }
101
-        if (apply_filters('FHEE__EE_Network_Config___load_config__update_network_config', false)) {
102
-            $this->update_config();
103
-        }
104
-
105
-        // load network config end hook
106
-        do_action('AHEE__EE_Network_Config___load_config__end', $this);
107
-    }
108
-
109
-
110
-    /**
111
-     * get_config
112
-     *
113
-     * @return array of network config stuff
114
-     */
115
-    public function get_config()
116
-    {
117
-        // grab network configuration
118
-        $CFG = get_site_option('ee_network_config', array());
119
-        $CFG = apply_filters('FHEE__EE_Network_Config__get_config__CFG', $CFG);
120
-        return $CFG;
121
-    }
122
-
123
-
124
-    /**
125
-     * update_config
126
-     *
127
-     * @param bool $add_success
128
-     * @param bool $add_error
129
-     * @return bool success
130
-     */
131
-    public function update_config($add_success = false, $add_error = true)
132
-    {
133
-        do_action('AHEE__EE_Network_Config__update_config__begin', $this);
134
-
135
-        // need to bust cache for comparing original if this is a multisite install
136
-        if (is_multisite()) {
137
-            global $current_site;
138
-            $cache_key = $current_site->id . ':ee_network_config';
139
-            wp_cache_delete($cache_key, 'site-options');
140
-        }
141
-
142
-        // we have to compare existing saved config with config in memory because if there is no difference that means
143
-        // that the method executed fine but there just was no update.  WordPress doesn't distinguish between false because
144
-        // there were 0 records updated because of no change vs false because some error produced problems with the update.
145
-        $original = get_site_option('ee_network_config');
146
-
147
-        if ($original == $this) {
148
-            return true;
149
-        }
150
-        // update
151
-        $saved = update_site_option('ee_network_config', $this);
152
-
153
-        do_action('AHEE__EE_Network_Config__update_config__end', $this, $saved);
154
-        // if config remains the same or was updated successfully
155
-        if ($saved) {
156
-            if ($add_success) {
157
-                $msg = is_multisite() ? esc_html__(
158
-                    'The Event Espresso Network Configuration Settings have been successfully updated.',
159
-                    'event_espresso'
160
-                ) : esc_html__('Extra Event Espresso Configuration settings were successfully updated.', 'event_espresso');
161
-                EE_Error::add_success($msg);
162
-            }
163
-            return true;
164
-        }
165
-        if ($add_error) {
166
-            $msg = is_multisite() ? esc_html__(
167
-                'The Event Espresso Network Configuration Settings were not updated.',
168
-                'event_espresso'
169
-            ) : esc_html__('Extra Event Espresso Network Configuration settings were not updated.', 'event_espresso');
170
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
171
-        }
172
-        return false;
173
-    }
174
-
175
-
176
-    /**
177
-     * __sleep
178
-     *
179
-     * @return array
180
-     */
181
-    public function __sleep()
182
-    {
183
-        return apply_filters(
184
-            'FHEE__EE_Network_Config__sleep',
185
-            array(
186
-                'core',
187
-            )
188
-        );
189
-    }
16
+	/**
17
+	 * @var EE_Network_Config $_instance
18
+	 */
19
+	private static $_instance;
20
+
21
+	/**
22
+	 * addons can add their specific network_config objects to this property
23
+	 *
24
+	 * @var EE_Config_Base[] $addons
25
+	 */
26
+	public $addons;
27
+
28
+	/**
29
+	 * @var EE_Network_Core_Config $core
30
+	 */
31
+	public $core;
32
+
33
+
34
+	/**
35
+	 * @singleton method used to instantiate class object
36
+	 * @return EE_Network_Config instance
37
+	 */
38
+	public static function instance()
39
+	{
40
+		// check if class object is instantiated, and instantiated properly
41
+		if (! self::$_instance instanceof EE_Network_Config) {
42
+			self::$_instance = new self();
43
+		}
44
+		return self::$_instance;
45
+	}
46
+
47
+
48
+	/**
49
+	 * class constructor
50
+	 */
51
+	private function __construct()
52
+	{
53
+		do_action('AHEE__EE_Network_Config__construct__begin', $this);
54
+		// set defaults
55
+		$this->core = apply_filters('FHEE__EE_Network_Config___construct__core', new EE_Network_Core_Config());
56
+		$this->addons = array();
57
+
58
+		$this->_load_config();
59
+
60
+		// construct__end hook
61
+		do_action('AHEE__EE_Network_Config__construct__end', $this);
62
+	}
63
+
64
+
65
+	/**
66
+	 * load EE Network Config options
67
+	 *
68
+	 * @return void
69
+	 */
70
+	private function _load_config()
71
+	{
72
+		// load network config start hook
73
+		do_action('AHEE__EE_Network_Config___load_config__start', $this);
74
+		$config = $this->get_config();
75
+		foreach ($config as $config_prop => $settings) {
76
+			if ($config_prop === 'core' && ! $settings instanceof EE_Network_Core_Config) {
77
+				$core = new EE_Network_Core_Config();
78
+				foreach ($settings as $prop => $setting) {
79
+					if (property_exists($core, $prop)) {
80
+						$core->{$prop} = $setting;
81
+					}
82
+				}
83
+				$settings = $core;
84
+				add_filter('FHEE__EE_Network_Config___load_config__update_network_config', '__return_true');
85
+			}
86
+			if (is_object($settings) && property_exists($this, $config_prop)) {
87
+				$this->{$config_prop} = apply_filters(
88
+					'FHEE__EE_Network_Config___load_config__config_settings',
89
+					$settings,
90
+					$config_prop,
91
+					$this
92
+				);
93
+				if (method_exists($settings, 'populate')) {
94
+					$this->{$config_prop}->populate();
95
+				}
96
+				if (method_exists($settings, 'do_hooks')) {
97
+					$this->{$config_prop}->do_hooks();
98
+				}
99
+			}
100
+		}
101
+		if (apply_filters('FHEE__EE_Network_Config___load_config__update_network_config', false)) {
102
+			$this->update_config();
103
+		}
104
+
105
+		// load network config end hook
106
+		do_action('AHEE__EE_Network_Config___load_config__end', $this);
107
+	}
108
+
109
+
110
+	/**
111
+	 * get_config
112
+	 *
113
+	 * @return array of network config stuff
114
+	 */
115
+	public function get_config()
116
+	{
117
+		// grab network configuration
118
+		$CFG = get_site_option('ee_network_config', array());
119
+		$CFG = apply_filters('FHEE__EE_Network_Config__get_config__CFG', $CFG);
120
+		return $CFG;
121
+	}
122
+
123
+
124
+	/**
125
+	 * update_config
126
+	 *
127
+	 * @param bool $add_success
128
+	 * @param bool $add_error
129
+	 * @return bool success
130
+	 */
131
+	public function update_config($add_success = false, $add_error = true)
132
+	{
133
+		do_action('AHEE__EE_Network_Config__update_config__begin', $this);
134
+
135
+		// need to bust cache for comparing original if this is a multisite install
136
+		if (is_multisite()) {
137
+			global $current_site;
138
+			$cache_key = $current_site->id . ':ee_network_config';
139
+			wp_cache_delete($cache_key, 'site-options');
140
+		}
141
+
142
+		// we have to compare existing saved config with config in memory because if there is no difference that means
143
+		// that the method executed fine but there just was no update.  WordPress doesn't distinguish between false because
144
+		// there were 0 records updated because of no change vs false because some error produced problems with the update.
145
+		$original = get_site_option('ee_network_config');
146
+
147
+		if ($original == $this) {
148
+			return true;
149
+		}
150
+		// update
151
+		$saved = update_site_option('ee_network_config', $this);
152
+
153
+		do_action('AHEE__EE_Network_Config__update_config__end', $this, $saved);
154
+		// if config remains the same or was updated successfully
155
+		if ($saved) {
156
+			if ($add_success) {
157
+				$msg = is_multisite() ? esc_html__(
158
+					'The Event Espresso Network Configuration Settings have been successfully updated.',
159
+					'event_espresso'
160
+				) : esc_html__('Extra Event Espresso Configuration settings were successfully updated.', 'event_espresso');
161
+				EE_Error::add_success($msg);
162
+			}
163
+			return true;
164
+		}
165
+		if ($add_error) {
166
+			$msg = is_multisite() ? esc_html__(
167
+				'The Event Espresso Network Configuration Settings were not updated.',
168
+				'event_espresso'
169
+			) : esc_html__('Extra Event Espresso Network Configuration settings were not updated.', 'event_espresso');
170
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
171
+		}
172
+		return false;
173
+	}
174
+
175
+
176
+	/**
177
+	 * __sleep
178
+	 *
179
+	 * @return array
180
+	 */
181
+	public function __sleep()
182
+	{
183
+		return apply_filters(
184
+			'FHEE__EE_Network_Config__sleep',
185
+			array(
186
+				'core',
187
+			)
188
+		);
189
+	}
190 190
 }
191 191
 
192 192
 
@@ -195,28 +195,28 @@  discard block
 block discarded – undo
195 195
  */
196 196
 class EE_Network_Core_Config extends EE_Config_Base
197 197
 {
198
-    /**
199
-     * PUE site license key
200
-     *
201
-     * @var string $site_license_key
202
-     * @deprecated 5.0.20.p
203
-     */
204
-    public $site_license_key;
205
-
206
-    /**
207
-     * This indicates whether messages system processing should be done on the same request or not.
208
-     *
209
-     * @var boolean $do_messages_on_same_request
210
-     */
211
-    public $do_messages_on_same_request;
212
-
213
-
214
-    /**
215
-     * EE_Network_Core_Config constructor.
216
-     */
217
-    public function __construct()
218
-    {
219
-        $this->site_license_key = '';
220
-        $this->do_messages_on_same_request = false;
221
-    }
198
+	/**
199
+	 * PUE site license key
200
+	 *
201
+	 * @var string $site_license_key
202
+	 * @deprecated 5.0.20.p
203
+	 */
204
+	public $site_license_key;
205
+
206
+	/**
207
+	 * This indicates whether messages system processing should be done on the same request or not.
208
+	 *
209
+	 * @var boolean $do_messages_on_same_request
210
+	 */
211
+	public $do_messages_on_same_request;
212
+
213
+
214
+	/**
215
+	 * EE_Network_Core_Config constructor.
216
+	 */
217
+	public function __construct()
218
+	{
219
+		$this->site_license_key = '';
220
+		$this->do_messages_on_same_request = false;
221
+	}
222 222
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Ticket.class.php 1 patch
Indentation   +2146 added lines, -2146 removed lines patch added patch discarded remove patch
@@ -15,2154 +15,2154 @@
 block discarded – undo
15 15
  */
16 16
 class EE_Ticket extends EE_Soft_Delete_Base_Class implements EEI_Line_Item_Object, EEI_Event_Relation, EEI_Has_Icon
17 17
 {
18
-    /**
19
-     * TicKet Archived:
20
-     * constant used by ticket_status() to indicate that a ticket is archived
21
-     * and no longer available for purchase
22
-     */
23
-    const archived = 'TKA';
24
-
25
-    /**
26
-     * TicKet Expired:
27
-     * constant used by ticket_status() to indicate that a ticket is expired
28
-     * and no longer available for purchase
29
-     */
30
-    const expired = 'TKE';
31
-
32
-    /**
33
-     * TicKet On sale:
34
-     * constant used by ticket_status() to indicate that a ticket is On Sale
35
-     * and IS available for purchase
36
-     */
37
-    const onsale = 'TKO';
38
-
39
-    /**
40
-     * TicKet Pending:
41
-     * constant used by ticket_status() to indicate that a ticket is pending
42
-     * and is NOT YET available for purchase
43
-     */
44
-    const pending = 'TKP';
45
-
46
-    /**
47
-     * TicKet Sold out:
48
-     * constant used by ticket_status() to indicate that a ticket is sold out
49
-     * and no longer available for purchases
50
-     */
51
-    const sold_out = 'TKS';
52
-
53
-    /**
54
-     * extra meta key for tracking ticket reservations
55
-     *
56
-     * @type string
57
-     */
58
-    const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
59
-
60
-    /**
61
-     * override of parent property
62
-     *
63
-     * @var EEM_Ticket
64
-     */
65
-    protected $_model;
66
-
67
-    /**
68
-     * cached result from method of the same name
69
-     *
70
-     * @var float $_ticket_total_with_taxes
71
-     */
72
-    private $_ticket_total_with_taxes;
73
-
74
-    /**
75
-     * @var TicketPriceModifiers
76
-     */
77
-    protected $ticket_price_modifiers;
78
-
79
-
80
-    /**
81
-     * @param array  $props_n_values          incoming values
82
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
83
-     *                                        used.)
84
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
85
-     *                                        date_format and the second value is the time format
86
-     * @return EE_Ticket
87
-     * @throws EE_Error
88
-     * @throws ReflectionException
89
-     */
90
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
91
-    {
92
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
93
-        return $has_object ?: new self($props_n_values, false, $timezone, $date_formats);
94
-    }
95
-
96
-
97
-    /**
98
-     * @param array  $props_n_values  incoming values from the database
99
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
100
-     *                                the website will be used.
101
-     * @return EE_Ticket
102
-     * @throws EE_Error
103
-     * @throws ReflectionException
104
-     */
105
-    public static function new_instance_from_db($props_n_values = [], $timezone = '')
106
-    {
107
-        return new self($props_n_values, true, $timezone);
108
-    }
109
-
110
-
111
-    /**
112
-     * @param array  $fieldValues
113
-     * @param false  $bydb
114
-     * @param string $timezone
115
-     * @param array  $date_formats
116
-     * @throws EE_Error
117
-     * @throws ReflectionException
118
-     */
119
-    public function __construct($fieldValues = [], $bydb = false, $timezone = '', $date_formats = [])
120
-    {
121
-        parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
122
-        $this->ticket_price_modifiers = new TicketPriceModifiers($this);
123
-    }
124
-
125
-
126
-    /**
127
-     * @return bool
128
-     * @throws EE_Error
129
-     * @throws ReflectionException
130
-     */
131
-    public function parent()
132
-    {
133
-        return $this->get('TKT_parent');
134
-    }
135
-
136
-
137
-    /**
138
-     * return if a ticket has quantities available for purchase
139
-     *
140
-     * @param int $DTT_ID the primary key for a particular datetime
141
-     * @return boolean
142
-     * @throws EE_Error
143
-     * @throws ReflectionException
144
-     */
145
-    public function available($DTT_ID = 0)
146
-    {
147
-        // are we checking availability for a particular datetime ?
148
-        if ($DTT_ID) {
149
-            // get that datetime object
150
-            $datetime = $this->get_first_related('Datetime', [['DTT_ID' => $DTT_ID]]);
151
-            // if  ticket sales for this datetime have exceeded the reg limit...
152
-            if ($datetime instanceof EE_Datetime && $datetime->sold_out()) {
153
-                return false;
154
-            }
155
-        }
156
-        // datetime is still open for registration, but is this ticket sold out ?
157
-        return $this->qty() < 1 || $this->qty() > $this->sold();
158
-    }
159
-
160
-
161
-    /**
162
-     * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
163
-     *
164
-     * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
165
-     *                               relevant status const
166
-     * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
167
-     *                               further processing
168
-     * @return mixed status int if the display string isn't requested
169
-     * @throws EE_Error
170
-     * @throws ReflectionException
171
-     */
172
-    public function ticket_status($display = false, $remaining = null)
173
-    {
174
-        $remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
175
-        if (! $remaining) {
176
-            return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
177
-        }
178
-        if ($this->get('TKT_deleted')) {
179
-            return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
180
-        }
181
-        if ($this->is_expired()) {
182
-            return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
183
-        }
184
-        if ($this->is_pending()) {
185
-            return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
186
-        }
187
-        if ($this->is_on_sale()) {
188
-            return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
189
-        }
190
-        return '';
191
-    }
192
-
193
-
194
-    /**
195
-     * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
196
-     * considering ALL the factors used for figuring that out.
197
-     *
198
-     * @param int $DTT_ID if an int above 0 is included here then we get a specific dtt.
199
-     * @return boolean         true = tickets remaining, false not.
200
-     * @throws EE_Error
201
-     * @throws ReflectionException
202
-     */
203
-    public function is_remaining($DTT_ID = 0)
204
-    {
205
-        $num_remaining = $this->remaining($DTT_ID);
206
-        if ($num_remaining === 0) {
207
-            return false;
208
-        }
209
-        if ($num_remaining > 0 && $num_remaining < $this->min()) {
210
-            return false;
211
-        }
212
-        return true;
213
-    }
214
-
215
-
216
-    /**
217
-     * return the total number of tickets available for purchase
218
-     *
219
-     * @param int $DTT_ID  the primary key for a particular datetime.
220
-     *                     set to 0 for all related datetimes
221
-     * @return int
222
-     * @throws EE_Error
223
-     * @throws ReflectionException
224
-     */
225
-    public function remaining($DTT_ID = 0)
226
-    {
227
-        return $this->real_quantity_on_ticket('saleable', $DTT_ID);
228
-    }
229
-
230
-
231
-    /**
232
-     * Gets min
233
-     *
234
-     * @return int
235
-     * @throws EE_Error
236
-     * @throws ReflectionException
237
-     */
238
-    public function min()
239
-    {
240
-        return $this->get('TKT_min');
241
-    }
242
-
243
-
244
-    /**
245
-     * return if a ticket is no longer available cause its available dates have expired.
246
-     *
247
-     * @return boolean
248
-     * @throws EE_Error
249
-     * @throws ReflectionException
250
-     */
251
-    public function is_expired()
252
-    {
253
-        return ($this->get_raw('TKT_end_date') < time());
254
-    }
255
-
256
-
257
-    /**
258
-     * Return if a ticket is yet to go on sale or not
259
-     *
260
-     * @return boolean
261
-     * @throws EE_Error
262
-     * @throws ReflectionException
263
-     */
264
-    public function is_pending()
265
-    {
266
-        return ($this->get_raw('TKT_start_date') >= time());
267
-    }
268
-
269
-
270
-    /**
271
-     * Return if a ticket is on sale or not
272
-     *
273
-     * @return boolean
274
-     * @throws EE_Error
275
-     * @throws ReflectionException
276
-     */
277
-    public function is_on_sale()
278
-    {
279
-        return ($this->get_raw('TKT_start_date') <= time() && $this->get_raw('TKT_end_date') >= time());
280
-    }
281
-
282
-
283
-    /**
284
-     * This returns the chronologically last datetime that this ticket is associated with
285
-     *
286
-     * @param string $date_format
287
-     * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
288
-     *                            the end date ie: Jan 01 "to" Dec 31
289
-     * @return string
290
-     * @throws EE_Error
291
-     * @throws ReflectionException
292
-     */
293
-    public function date_range($date_format = '', $conjunction = ' - ')
294
-    {
295
-        $date_format = ! empty($date_format) ? $date_format : $this->_dt_frmt;
296
-        $first_date  = $this->first_datetime() instanceof EE_Datetime
297
-            ? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $date_format)
298
-            : '';
299
-        $last_date   = $this->last_datetime() instanceof EE_Datetime
300
-            ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format)
301
-            : '';
302
-
303
-        return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
304
-    }
305
-
306
-
307
-    /**
308
-     * This returns the chronologically first datetime that this ticket is associated with
309
-     *
310
-     * @return EE_Datetime
311
-     * @throws EE_Error
312
-     * @throws ReflectionException
313
-     */
314
-    public function first_datetime()
315
-    {
316
-        $datetimes = $this->datetimes(['limit' => 1]);
317
-        return reset($datetimes);
318
-    }
319
-
320
-
321
-    /**
322
-     * Gets all the datetimes this ticket can be used for attending.
323
-     * Unless otherwise specified, orders datetimes by start date.
324
-     *
325
-     * @param array $query_params
326
-     * @return EE_Datetime[]|EE_Base_Class[]
327
-     * @throws EE_Error
328
-     * @throws ReflectionException
329
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
330
-     */
331
-    public function datetimes($query_params = [])
332
-    {
333
-        if (! isset($query_params['order_by'])) {
334
-            $query_params['order_by']['DTT_order'] = 'ASC';
335
-        }
336
-        return $this->get_many_related('Datetime', $query_params);
337
-    }
338
-
339
-
340
-    /**
341
-     * This returns the chronologically last datetime that this ticket is associated with
342
-     *
343
-     * @return EE_Datetime
344
-     * @throws EE_Error
345
-     * @throws ReflectionException
346
-     */
347
-    public function last_datetime()
348
-    {
349
-        $datetimes = $this->datetimes(['limit' => 1, 'order_by' => ['DTT_EVT_start' => 'DESC']]);
350
-        return end($datetimes);
351
-    }
352
-
353
-
354
-    /**
355
-     * This returns the total tickets sold depending on the given parameters.
356
-     *
357
-     * @param string $what    Can be one of two options: 'ticket', 'datetime'.
358
-     *                        'ticket' = total ticket sales for all datetimes this ticket is related to
359
-     *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
360
-     *                        'datetime' = total ticket sales in the datetime_ticket table.
361
-     *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
362
-     *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
363
-     * @param int    $dtt_id  [optional] include the dtt_id with $what = 'datetime'.
364
-     * @return mixed (array|int)          how many tickets have sold
365
-     * @throws EE_Error
366
-     * @throws ReflectionException
367
-     */
368
-    public function tickets_sold($what = 'ticket', $dtt_id = null)
369
-    {
370
-        $total        = 0;
371
-        $tickets_sold = $this->_all_tickets_sold();
372
-        switch ($what) {
373
-            case 'ticket':
374
-                return $tickets_sold['ticket'];
375
-
376
-            case 'datetime':
377
-                if (empty($tickets_sold['datetime'])) {
378
-                    return $total;
379
-                }
380
-                if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
381
-                    EE_Error::add_error(
382
-                        esc_html__(
383
-                            'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included.  Are you SURE that is a datetime related to this ticket?',
384
-                            'event_espresso'
385
-                        ),
386
-                        __FILE__,
387
-                        __FUNCTION__,
388
-                        __LINE__
389
-                    );
390
-                    return $total;
391
-                }
392
-                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
393
-
394
-            default:
395
-                return $total;
396
-        }
397
-    }
398
-
399
-
400
-    /**
401
-     * This returns an array indexed by datetime_id for tickets sold with this ticket.
402
-     *
403
-     * @return EE_Ticket[]
404
-     * @throws EE_Error
405
-     * @throws ReflectionException
406
-     */
407
-    protected function _all_tickets_sold()
408
-    {
409
-        $datetimes    = $this->get_many_related('Datetime');
410
-        $tickets_sold = [];
411
-        if (! empty($datetimes)) {
412
-            foreach ($datetimes as $datetime) {
413
-                $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
414
-            }
415
-        }
416
-        // Tickets sold
417
-        $tickets_sold['ticket'] = $this->sold();
418
-        return $tickets_sold;
419
-    }
420
-
421
-
422
-    /**
423
-     * This returns the base price object for the ticket.
424
-     *
425
-     * @param bool $return_array whether to return as an array indexed by price id or just the object.
426
-     * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
427
-     * @throws EE_Error
428
-     * @throws ReflectionException
429
-     */
430
-    public function base_price(bool $return_array = false)
431
-    {
432
-        $base_price = $this->ticket_price_modifiers->getBasePrice();
433
-        if (! empty($base_price)) {
434
-            return $return_array ? $base_price : reset($base_price);
435
-        }
436
-        $_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price];
437
-        return $return_array
438
-            ? $this->get_many_related('Price', [$_where])
439
-            : $this->get_first_related('Price', [$_where]);
440
-    }
441
-
442
-
443
-    /**
444
-     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
445
-     *
446
-     * @return EE_Price[]
447
-     * @throws EE_Error
448
-     * @throws ReflectionException
449
-     */
450
-    public function price_modifiers(): array
451
-    {
452
-        $price_modifiers = $this->usesGlobalTaxes()
453
-            ? $this->ticket_price_modifiers->getAllDiscountAndSurchargeModifiersForTicket()
454
-            : $this->ticket_price_modifiers->getAllModifiersForTicket();
455
-        if (! empty($price_modifiers)) {
456
-            return $price_modifiers;
457
-        }
458
-        return $this->prices(
459
-            [
460
-                [
461
-                    'Price_Type.PBT_ID' => [
462
-                        'NOT IN',
463
-                        [EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax],
464
-                    ],
465
-                ],
466
-            ]
467
-        );
468
-    }
469
-
470
-
471
-    /**
472
-     * This returns ONLY the TAX price modifiers for the ticket
473
-     *
474
-     * @return EE_Price[]
475
-     * @throws EE_Error
476
-     * @throws ReflectionException
477
-     */
478
-    public function tax_price_modifiers(): array
479
-    {
480
-        $tax_price_modifiers = $this->ticket_price_modifiers->getAllTaxesForTicket();
481
-        if (! empty($tax_price_modifiers)) {
482
-            return $tax_price_modifiers;
483
-        }
484
-        return $this->prices([['Price_Type.PBT_ID' => EEM_Price_Type::base_type_tax]]);
485
-    }
486
-
487
-
488
-    /**
489
-     * Gets all the prices that combine to form the final price of this ticket
490
-     *
491
-     * @param array $query_params
492
-     * @return EE_Price[]|EE_Base_Class[]
493
-     * @throws EE_Error
494
-     * @throws ReflectionException
495
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
496
-     */
497
-    public function prices(array $query_params = []): array
498
-    {
499
-        if (! isset($query_params['order_by'])) {
500
-            $query_params['order_by']['PRC_order'] = 'ASC';
501
-        }
502
-        return $this->get_many_related('Price', $query_params);
503
-    }
504
-
505
-
506
-    /**
507
-     * Gets all the ticket datetimes (ie, relations between datetimes and tickets)
508
-     *
509
-     * @param array $query_params
510
-     * @return EE_Datetime_Ticket|EE_Base_Class[]
511
-     * @throws EE_Error
512
-     * @throws ReflectionException
513
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
514
-     */
515
-    public function datetime_tickets($query_params = [])
516
-    {
517
-        return $this->get_many_related('Datetime_Ticket', $query_params);
518
-    }
519
-
520
-
521
-    /**
522
-     * Gets all the datetimes from the db ordered by DTT_order
523
-     *
524
-     * @param boolean $show_expired
525
-     * @param boolean $show_deleted
526
-     * @return EE_Datetime[]
527
-     * @throws EE_Error
528
-     * @throws ReflectionException
529
-     */
530
-    public function datetimes_ordered($show_expired = true, $show_deleted = false)
531
-    {
532
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
533
-            $this->ID(),
534
-            $show_expired,
535
-            $show_deleted
536
-        );
537
-    }
538
-
539
-
540
-    /**
541
-     * Gets ID
542
-     *
543
-     * @return int
544
-     * @throws EE_Error
545
-     * @throws ReflectionException
546
-     */
547
-    public function ID()
548
-    {
549
-        return (int) $this->get('TKT_ID');
550
-    }
551
-
552
-
553
-    /**
554
-     * get the author of the ticket.
555
-     *
556
-     * @return int
557
-     * @throws EE_Error
558
-     * @throws ReflectionException
559
-     * @since 4.5.0
560
-     */
561
-    public function wp_user()
562
-    {
563
-        return $this->get('TKT_wp_user');
564
-    }
565
-
566
-
567
-    /**
568
-     * Gets the template for the ticket
569
-     *
570
-     * @return EE_Ticket_Template|EE_Base_Class
571
-     * @throws EE_Error
572
-     * @throws ReflectionException
573
-     */
574
-    public function template()
575
-    {
576
-        return $this->get_first_related('Ticket_Template');
577
-    }
578
-
579
-
580
-    /**
581
-     * Simply returns an array of EE_Price objects that are taxes.
582
-     *
583
-     * @return EE_Price[]
584
-     * @throws EE_Error
585
-     * @throws ReflectionException
586
-     */
587
-    public function get_ticket_taxes_for_admin(): array
588
-    {
589
-        return $this->usesGlobalTaxes() ? EE_Taxes::get_taxes_for_admin() : $this->tax_price_modifiers();
590
-    }
591
-
592
-
593
-    /**
594
-     * alias of taxable() to better indicate that ticket uses the legacy method of applying default "global" taxes
595
-     * as opposed to having tax price modifiers added directly to each ticket
596
-     *
597
-     * @return bool
598
-     * @throws EE_Error
599
-     * @throws ReflectionException
600
-     * @since   5.0.0.p
601
-     */
602
-    public function usesGlobalTaxes(): bool
603
-    {
604
-        return $this->taxable();
605
-    }
606
-
607
-
608
-    /**
609
-     * @return float
610
-     * @throws EE_Error
611
-     * @throws ReflectionException
612
-     */
613
-    public function ticket_price()
614
-    {
615
-        return $this->get('TKT_price');
616
-    }
617
-
618
-
619
-    /**
620
-     * @return mixed
621
-     * @throws EE_Error
622
-     * @throws ReflectionException
623
-     */
624
-    public function pretty_price()
625
-    {
626
-        return $this->get_pretty('TKT_price');
627
-    }
628
-
629
-
630
-    /**
631
-     * @return bool
632
-     * @throws EE_Error
633
-     * @throws ReflectionException
634
-     */
635
-    public function is_free()
636
-    {
637
-        return $this->get_ticket_total_with_taxes() === (float) 0;
638
-    }
639
-
640
-
641
-    /**
642
-     * get_ticket_total_with_taxes
643
-     *
644
-     * @param bool $no_cache
645
-     * @return float
646
-     * @throws EE_Error
647
-     * @throws ReflectionException
648
-     */
649
-    public function get_ticket_total_with_taxes(bool $no_cache = false): float
650
-    {
651
-        if ($this->_ticket_total_with_taxes === null || $no_cache) {
652
-            $this->_ticket_total_with_taxes = $this->get_ticket_subtotal();
653
-            // add taxes
654
-            if ($this->usesGlobalTaxes()) {
655
-                $this->_ticket_total_with_taxes += $this->get_ticket_taxes_total_for_admin();
656
-            } else {
657
-                $subtotal = $this->_ticket_total_with_taxes;
658
-                foreach ($this->tax_price_modifiers() as $tax) {
659
-                    $this->_ticket_total_with_taxes += $subtotal * $tax->amount() / 100;
660
-                }
661
-            }
662
-        }
663
-        return (float) $this->_ticket_total_with_taxes;
664
-    }
665
-
666
-
667
-    /**
668
-     * @throws EE_Error
669
-     * @throws ReflectionException
670
-     */
671
-    public function ensure_TKT_Price_correct()
672
-    {
673
-        $this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
674
-        $this->save();
675
-    }
676
-
677
-
678
-    /**
679
-     * @return float
680
-     * @throws EE_Error
681
-     * @throws ReflectionException
682
-     */
683
-    public function get_ticket_subtotal()
684
-    {
685
-        return EE_Taxes::get_subtotal_for_admin($this);
686
-    }
687
-
688
-
689
-    /**
690
-     * Returns the total taxes applied to this ticket
691
-     *
692
-     * @return float
693
-     * @throws EE_Error
694
-     * @throws ReflectionException
695
-     */
696
-    public function get_ticket_taxes_total_for_admin()
697
-    {
698
-        return EE_Taxes::get_total_taxes_for_admin($this);
699
-    }
700
-
701
-
702
-    /**
703
-     * Sets name
704
-     *
705
-     * @param string $name
706
-     * @throws EE_Error
707
-     * @throws ReflectionException
708
-     */
709
-    public function set_name($name)
710
-    {
711
-        $this->set('TKT_name', $name);
712
-    }
713
-
714
-
715
-    /**
716
-     * Gets description
717
-     *
718
-     * @return string
719
-     * @throws EE_Error
720
-     * @throws ReflectionException
721
-     */
722
-    public function description()
723
-    {
724
-        return $this->get('TKT_description');
725
-    }
726
-
727
-
728
-    /**
729
-     * Sets description
730
-     *
731
-     * @param string $description
732
-     * @throws EE_Error
733
-     * @throws ReflectionException
734
-     */
735
-    public function set_description($description)
736
-    {
737
-        $this->set('TKT_description', $description);
738
-    }
739
-
740
-
741
-    /**
742
-     * Gets start_date
743
-     *
744
-     * @param string|null $date_format
745
-     * @param string|null $time_format
746
-     * @return string
747
-     * @throws EE_Error
748
-     * @throws ReflectionException
749
-     */
750
-    public function start_date(?string $date_format = '', ?string $time_format = ''): string
751
-    {
752
-        return $this->_get_datetime('TKT_start_date', $date_format, $time_format);
753
-    }
754
-
755
-
756
-    /**
757
-     * Sets start_date
758
-     *
759
-     * @param string $start_date
760
-     * @return void
761
-     * @throws EE_Error
762
-     * @throws ReflectionException
763
-     */
764
-    public function set_start_date($start_date)
765
-    {
766
-        $this->_set_date_time('B', $start_date, 'TKT_start_date');
767
-    }
768
-
769
-
770
-    /**
771
-     * Gets end_date
772
-     *
773
-     * @param string|null $date_format
774
-     * @param string|null $time_format
775
-     * @return string
776
-     * @throws EE_Error
777
-     * @throws ReflectionException
778
-     */
779
-    public function end_date(?string $date_format = '', ?string $time_format = ''): string
780
-    {
781
-        return $this->_get_datetime('TKT_end_date', $date_format, $time_format);
782
-    }
783
-
784
-
785
-    /**
786
-     * Sets end_date
787
-     *
788
-     * @param string $end_date
789
-     * @return void
790
-     * @throws EE_Error
791
-     * @throws ReflectionException
792
-     */
793
-    public function set_end_date($end_date)
794
-    {
795
-        $this->_set_date_time('B', $end_date, 'TKT_end_date');
796
-    }
797
-
798
-
799
-    /**
800
-     * Sets sell until time
801
-     *
802
-     * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
803
-     * @throws EE_Error
804
-     * @throws ReflectionException
805
-     * @since 4.5.0
806
-     */
807
-    public function set_end_time($time)
808
-    {
809
-        $this->_set_time_for($time, 'TKT_end_date');
810
-    }
811
-
812
-
813
-    /**
814
-     * Sets min
815
-     *
816
-     * @param int $min
817
-     * @return void
818
-     * @throws EE_Error
819
-     * @throws ReflectionException
820
-     */
821
-    public function set_min($min)
822
-    {
823
-        $this->set('TKT_min', $min);
824
-    }
825
-
826
-
827
-    /**
828
-     * Gets max
829
-     *
830
-     * @return int
831
-     * @throws EE_Error
832
-     * @throws ReflectionException
833
-     */
834
-    public function max()
835
-    {
836
-        return $this->get('TKT_max');
837
-    }
838
-
839
-
840
-    /**
841
-     * Sets max
842
-     *
843
-     * @param int $max
844
-     * @return void
845
-     * @throws EE_Error
846
-     * @throws ReflectionException
847
-     */
848
-    public function set_max($max)
849
-    {
850
-        $this->set('TKT_max', $max);
851
-    }
852
-
853
-
854
-    /**
855
-     * Sets price
856
-     *
857
-     * @param float $price
858
-     * @return void
859
-     * @throws EE_Error
860
-     * @throws ReflectionException
861
-     */
862
-    public function set_price($price)
863
-    {
864
-        $this->set('TKT_price', $price);
865
-    }
866
-
867
-
868
-    /**
869
-     * Gets sold
870
-     *
871
-     * @return int
872
-     * @throws EE_Error
873
-     * @throws ReflectionException
874
-     */
875
-    public function sold(): int
876
-    {
877
-        return (int) $this->get_raw('TKT_sold');
878
-    }
879
-
880
-
881
-    /**
882
-     * Sets sold
883
-     *
884
-     * @param int $sold
885
-     * @return void
886
-     * @throws EE_Error
887
-     * @throws ReflectionException
888
-     */
889
-    public function set_sold($sold)
890
-    {
891
-        // sold can not go below zero
892
-        $sold = max(0, $sold);
893
-        $this->set('TKT_sold', $sold);
894
-    }
895
-
896
-
897
-    /**
898
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
899
-     * associated datetimes.
900
-     *
901
-     * @param int $qty
902
-     * @return boolean
903
-     * @throws EE_Error
904
-     * @throws InvalidArgumentException
905
-     * @throws InvalidDataTypeException
906
-     * @throws InvalidInterfaceException
907
-     * @throws ReflectionException
908
-     * @since 4.9.80.p
909
-     */
910
-    public function increaseSold($qty = 1)
911
-    {
912
-        $qty = absint($qty);
913
-        // increment sold and decrement reserved datetime quantities simultaneously
914
-        // don't worry about failures, because they must have already had a spot reserved
915
-        $this->increaseSoldForDatetimes($qty);
916
-        // Increment and decrement ticket quantities simultaneously
917
-        $success = $this->adjustNumericFieldsInDb(
918
-            [
919
-                'TKT_reserved' => $qty * -1,
920
-                'TKT_sold'     => $qty,
921
-            ]
922
-        );
923
-        do_action(
924
-            'AHEE__EE_Ticket__increase_sold',
925
-            $this,
926
-            $qty,
927
-            $this->sold(),
928
-            $success
929
-        );
930
-        return $success;
931
-    }
932
-
933
-
934
-    /**
935
-     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
936
-     *
937
-     * @param int           $qty positive or negative. Positive means to increase sold counts (and decrease reserved
938
-     *                           counts), Negative means to decreases old counts (and increase reserved counts).
939
-     * @param EE_Datetime[] $datetimes
940
-     * @throws EE_Error
941
-     * @throws InvalidArgumentException
942
-     * @throws InvalidDataTypeException
943
-     * @throws InvalidInterfaceException
944
-     * @throws ReflectionException
945
-     * @since 4.9.80.p
946
-     */
947
-    protected function increaseSoldForDatetimes($qty, array $datetimes = [])
948
-    {
949
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
950
-        foreach ($datetimes as $datetime) {
951
-            $datetime->increaseSold($qty);
952
-        }
953
-    }
954
-
955
-
956
-    /**
957
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
958
-     * DB and then updates the model objects.
959
-     * Does not affect the reserved counts.
960
-     *
961
-     * @param int $qty
962
-     * @return boolean
963
-     * @throws EE_Error
964
-     * @throws InvalidArgumentException
965
-     * @throws InvalidDataTypeException
966
-     * @throws InvalidInterfaceException
967
-     * @throws ReflectionException
968
-     * @since 4.9.80.p
969
-     */
970
-    public function decreaseSold($qty = 1)
971
-    {
972
-        $qty = absint($qty);
973
-        $this->decreaseSoldForDatetimes($qty);
974
-        $success = $this->adjustNumericFieldsInDb(
975
-            [
976
-                'TKT_sold' => $qty * -1,
977
-            ]
978
-        );
979
-        do_action(
980
-            'AHEE__EE_Ticket__decrease_sold',
981
-            $this,
982
-            $qty,
983
-            $this->sold(),
984
-            $success
985
-        );
986
-        return $success;
987
-    }
988
-
989
-
990
-    /**
991
-     * Decreases sold on related datetimes
992
-     *
993
-     * @param int           $qty
994
-     * @param EE_Datetime[] $datetimes
995
-     * @return void
996
-     * @throws EE_Error
997
-     * @throws InvalidArgumentException
998
-     * @throws InvalidDataTypeException
999
-     * @throws InvalidInterfaceException
1000
-     * @throws ReflectionException
1001
-     * @since 4.9.80.p
1002
-     */
1003
-    protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
1004
-    {
1005
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1006
-        if (is_array($datetimes)) {
1007
-            foreach ($datetimes as $datetime) {
1008
-                if ($datetime instanceof EE_Datetime) {
1009
-                    $datetime->decreaseSold($qty);
1010
-                }
1011
-            }
1012
-        }
1013
-    }
1014
-
1015
-
1016
-    /**
1017
-     * Gets qty of reserved tickets
1018
-     *
1019
-     * @return int
1020
-     * @throws EE_Error
1021
-     * @throws ReflectionException
1022
-     */
1023
-    public function reserved(): int
1024
-    {
1025
-        return (int) $this->get_raw('TKT_reserved');
1026
-    }
1027
-
1028
-
1029
-    /**
1030
-     * Sets reserved
1031
-     *
1032
-     * @param int $reserved
1033
-     * @return void
1034
-     * @throws EE_Error
1035
-     * @throws ReflectionException
1036
-     */
1037
-    public function set_reserved($reserved)
1038
-    {
1039
-        // reserved can not go below zero
1040
-        $reserved = max(0, (int) $reserved);
1041
-        $this->set('TKT_reserved', $reserved);
1042
-    }
1043
-
1044
-
1045
-    /**
1046
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1047
-     *
1048
-     * @param int    $qty
1049
-     * @param string $source
1050
-     * @return bool whether we successfully reserved the ticket or not.
1051
-     * @throws EE_Error
1052
-     * @throws InvalidArgumentException
1053
-     * @throws ReflectionException
1054
-     * @throws InvalidDataTypeException
1055
-     * @throws InvalidInterfaceException
1056
-     * @since 4.9.80.p
1057
-     */
1058
-    public function increaseReserved($qty = 1, $source = 'unknown')
1059
-    {
1060
-        $qty = absint($qty);
1061
-        do_action(
1062
-            'AHEE__EE_Ticket__increase_reserved__begin',
1063
-            $this,
1064
-            $qty,
1065
-            $source
1066
-        );
1067
-        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
1068
-        $success                         = false;
1069
-        $datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
1070
-        if ($datetimes_adjusted_successfully) {
1071
-            $success = $this->incrementFieldConditionallyInDb(
1072
-                'TKT_reserved',
1073
-                'TKT_sold',
1074
-                'TKT_qty',
1075
-                $qty
1076
-            );
1077
-            if (! $success) {
1078
-                // The datetimes were successfully bumped, but not the
1079
-                // ticket. So we need to manually rollback the datetimes.
1080
-                $this->decreaseReservedForDatetimes($qty);
1081
-            }
1082
-        }
1083
-        do_action(
1084
-            'AHEE__EE_Ticket__increase_reserved',
1085
-            $this,
1086
-            $qty,
1087
-            $this->reserved(),
1088
-            $success
1089
-        );
1090
-        return $success;
1091
-    }
1092
-
1093
-
1094
-    /**
1095
-     * Increases reserved counts on related datetimes
1096
-     *
1097
-     * @param int           $qty
1098
-     * @param EE_Datetime[] $datetimes
1099
-     * @return boolean indicating success
1100
-     * @throws EE_Error
1101
-     * @throws InvalidArgumentException
1102
-     * @throws InvalidDataTypeException
1103
-     * @throws InvalidInterfaceException
1104
-     * @throws ReflectionException
1105
-     * @since 4.9.80.p
1106
-     */
1107
-    protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
1108
-    {
1109
-        $datetimes         = ! empty($datetimes) ? $datetimes : $this->datetimes();
1110
-        $datetimes_updated = [];
1111
-        $limit_exceeded    = false;
1112
-        if (is_array($datetimes)) {
1113
-            foreach ($datetimes as $datetime) {
1114
-                if ($datetime instanceof EE_Datetime) {
1115
-                    if ($datetime->increaseReserved($qty)) {
1116
-                        $datetimes_updated[] = $datetime;
1117
-                    } else {
1118
-                        $limit_exceeded = true;
1119
-                        break;
1120
-                    }
1121
-                }
1122
-            }
1123
-            // If somewhere along the way we detected a datetime whose
1124
-            // limit was exceeded, do a manual rollback.
1125
-            if ($limit_exceeded) {
1126
-                $this->decreaseReservedForDatetimes($qty, $datetimes_updated);
1127
-                return false;
1128
-            }
1129
-        }
1130
-        return true;
1131
-    }
1132
-
1133
-
1134
-    /**
1135
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1136
-     *
1137
-     * @param int    $qty
1138
-     * @param bool   $adjust_datetimes
1139
-     * @param string $source
1140
-     * @return boolean
1141
-     * @throws EE_Error
1142
-     * @throws InvalidArgumentException
1143
-     * @throws ReflectionException
1144
-     * @throws InvalidDataTypeException
1145
-     * @throws InvalidInterfaceException
1146
-     * @since 4.9.80.p
1147
-     */
1148
-    public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1149
-    {
1150
-        $qty = absint($qty);
1151
-        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1152
-        if ($adjust_datetimes) {
1153
-            $this->decreaseReservedForDatetimes($qty);
1154
-        }
1155
-        $success = $this->adjustNumericFieldsInDb(
1156
-            [
1157
-                'TKT_reserved' => $qty * -1,
1158
-            ]
1159
-        );
1160
-        do_action(
1161
-            'AHEE__EE_Ticket__decrease_reserved',
1162
-            $this,
1163
-            $qty,
1164
-            $this->reserved(),
1165
-            $success
1166
-        );
1167
-        return $success;
1168
-    }
1169
-
1170
-
1171
-    /**
1172
-     * Decreases the reserved count on the specified datetimes.
1173
-     *
1174
-     * @param int           $qty
1175
-     * @param EE_Datetime[] $datetimes
1176
-     * @throws EE_Error
1177
-     * @throws InvalidArgumentException
1178
-     * @throws ReflectionException
1179
-     * @throws InvalidDataTypeException
1180
-     * @throws InvalidInterfaceException
1181
-     * @since 4.9.80.p
1182
-     */
1183
-    protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1184
-    {
1185
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1186
-        foreach ($datetimes as $datetime) {
1187
-            if ($datetime instanceof EE_Datetime) {
1188
-                $datetime->decreaseReserved($qty);
1189
-            }
1190
-        }
1191
-    }
1192
-
1193
-
1194
-    /**
1195
-     * Gets ticket quantity
1196
-     *
1197
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1198
-     *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1199
-     *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1200
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1201
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1202
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1203
-     * @return int
1204
-     * @throws EE_Error
1205
-     * @throws ReflectionException
1206
-     */
1207
-    public function qty($context = '')
1208
-    {
1209
-        switch ($context) {
1210
-            case 'reg_limit':
1211
-                return $this->real_quantity_on_ticket();
1212
-            case 'saleable':
1213
-                return $this->real_quantity_on_ticket('saleable');
1214
-            default:
1215
-                return $this->get_raw('TKT_qty');
1216
-        }
1217
-    }
1218
-
1219
-
1220
-    /**
1221
-     * Gets ticket quantity
1222
-     *
1223
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1224
-     *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1225
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1226
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1227
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1228
-     * @param int    $DTT_ID      the primary key for a particular datetime.
1229
-     *                            set to 0 for all related datetimes
1230
-     * @return int|float          int for finite quantity or float for INF
1231
-     * @throws EE_Error
1232
-     * @throws ReflectionException
1233
-     */
1234
-    public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1235
-    {
1236
-        $raw = $this->get_raw('TKT_qty');
1237
-        // return immediately if it's zero
1238
-        if ($raw === 0) {
1239
-            return $raw;
1240
-        }
1241
-        // echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1242
-        // ensure qty doesn't exceed raw value for THIS ticket
1243
-        $qty = min(EE_INF, $raw);
1244
-        // echo "\n . qty: " . $qty . '<br />';
1245
-        // calculate this ticket's total sales and reservations
1246
-        $sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1247
-        // echo "\n . sold: " . $this->sold() . '<br />';
1248
-        // echo "\n . reserved: " . $this->reserved() . '<br />';
1249
-        // echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1250
-        // first we need to calculate the maximum number of tickets available for the datetime
1251
-        // do we want data for one datetime or all of them ?
1252
-        $query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : [];
1253
-        $datetimes    = $this->get_many_related('Datetime', $query_params);
1254
-        if (is_array($datetimes) && ! empty($datetimes)) {
1255
-            foreach ($datetimes as $datetime) {
1256
-                if ($datetime instanceof EE_Datetime) {
1257
-                    // $datetime->refresh_from_db();
1258
-                    // echo "\n . . datetime name: " . $datetime->name() . '<br />';
1259
-                    // echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1260
-                    // initialize with no restrictions for each datetime
1261
-                    // but adjust datetime qty based on datetime reg limit
1262
-                    $datetime_qty = min(EE_INF, $datetime->reg_limit());
1263
-                    // echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1264
-                    // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1265
-                    // if we want the actual saleable amount, then we need to consider OTHER ticket sales
1266
-                    // and reservations for this datetime, that do NOT include sales and reservations
1267
-                    // for this ticket (so we add $this->sold() and $this->reserved() back in)
1268
-                    if ($context === 'saleable') {
1269
-                        $datetime_qty = max(
1270
-                            $datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1271
-                            0
1272
-                        );
1273
-                        // echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1274
-                        // echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1275
-                        // echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1276
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1277
-                        $datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1278
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1279
-                    }
1280
-                    $qty = min($datetime_qty, $qty);
1281
-                    // echo "\n . . qty: " . $qty . '<br />';
1282
-                }
1283
-            }
1284
-        }
1285
-        // NOW that we know the  maximum number of tickets available for the datetime
1286
-        // we can finally factor in the details for this specific ticket
1287
-        if ($qty > 0 && $context === 'saleable') {
1288
-            // and subtract the sales for THIS ticket
1289
-            $qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1290
-            // echo "\n . qty: " . $qty . '<br />';
1291
-        }
1292
-        // echo "\nFINAL QTY: " . $qty . "<br /><br />";
1293
-        return $qty;
1294
-    }
1295
-
1296
-
1297
-    /**
1298
-     * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1299
-     *
1300
-     * @param int $qty
1301
-     * @return void
1302
-     * @throws EE_Error
1303
-     * @throws ReflectionException
1304
-     */
1305
-    public function set_qty($qty)
1306
-    {
1307
-        $datetimes = $this->datetimes();
1308
-        foreach ($datetimes as $datetime) {
1309
-            if ($datetime instanceof EE_Datetime) {
1310
-                $qty = min($qty, $datetime->reg_limit());
1311
-            }
1312
-        }
1313
-        $this->set('TKT_qty', $qty);
1314
-    }
1315
-
1316
-
1317
-    /**
1318
-     * Gets uses
1319
-     *
1320
-     * @return int
1321
-     * @throws EE_Error
1322
-     * @throws ReflectionException
1323
-     */
1324
-    public function uses()
1325
-    {
1326
-        return $this->get('TKT_uses');
1327
-    }
1328
-
1329
-
1330
-    /**
1331
-     * Sets uses
1332
-     *
1333
-     * @param int $uses
1334
-     * @return void
1335
-     * @throws EE_Error
1336
-     * @throws ReflectionException
1337
-     */
1338
-    public function set_uses($uses)
1339
-    {
1340
-        $this->set('TKT_uses', $uses);
1341
-    }
1342
-
1343
-
1344
-    /**
1345
-     * returns whether ticket is required or not.
1346
-     *
1347
-     * @return boolean
1348
-     * @throws EE_Error
1349
-     * @throws ReflectionException
1350
-     */
1351
-    public function required()
1352
-    {
1353
-        return $this->get('TKT_required');
1354
-    }
1355
-
1356
-
1357
-    /**
1358
-     * sets the TKT_required property
1359
-     *
1360
-     * @param boolean $required
1361
-     * @return void
1362
-     * @throws EE_Error
1363
-     * @throws ReflectionException
1364
-     */
1365
-    public function set_required($required)
1366
-    {
1367
-        $this->set('TKT_required', $required);
1368
-    }
1369
-
1370
-
1371
-    /**
1372
-     * Gets taxable
1373
-     *
1374
-     * @return boolean
1375
-     * @throws EE_Error
1376
-     * @throws ReflectionException
1377
-     */
1378
-    public function taxable()
1379
-    {
1380
-        return $this->get('TKT_taxable');
1381
-    }
1382
-
1383
-
1384
-    /**
1385
-     * Sets taxable
1386
-     *
1387
-     * @param boolean $taxable
1388
-     * @return void
1389
-     * @throws EE_Error
1390
-     * @throws ReflectionException
1391
-     */
1392
-    public function set_taxable($taxable)
1393
-    {
1394
-        $this->set('TKT_taxable', $taxable);
1395
-    }
1396
-
1397
-
1398
-    /**
1399
-     * Gets is_default
1400
-     *
1401
-     * @return boolean
1402
-     * @throws EE_Error
1403
-     * @throws ReflectionException
1404
-     */
1405
-    public function is_default()
1406
-    {
1407
-        return $this->get('TKT_is_default');
1408
-    }
1409
-
1410
-
1411
-    /**
1412
-     * Sets is_default
1413
-     *
1414
-     * @param boolean $is_default
1415
-     * @return void
1416
-     * @throws EE_Error
1417
-     * @throws ReflectionException
1418
-     */
1419
-    public function set_is_default($is_default)
1420
-    {
1421
-        $this->set('TKT_is_default', $is_default);
1422
-    }
1423
-
1424
-
1425
-    /**
1426
-     * Gets order
1427
-     *
1428
-     * @return int
1429
-     * @throws EE_Error
1430
-     * @throws ReflectionException
1431
-     */
1432
-    public function order()
1433
-    {
1434
-        return $this->get('TKT_order');
1435
-    }
1436
-
1437
-
1438
-    /**
1439
-     * Sets order
1440
-     *
1441
-     * @param int $order
1442
-     * @return void
1443
-     * @throws EE_Error
1444
-     * @throws ReflectionException
1445
-     */
1446
-    public function set_order($order)
1447
-    {
1448
-        $this->set('TKT_order', $order);
1449
-    }
1450
-
1451
-
1452
-    /**
1453
-     * Gets row
1454
-     *
1455
-     * @return int
1456
-     * @throws EE_Error
1457
-     * @throws ReflectionException
1458
-     */
1459
-    public function row()
1460
-    {
1461
-        return $this->get('TKT_row');
1462
-    }
1463
-
1464
-
1465
-    /**
1466
-     * Sets row
1467
-     *
1468
-     * @param int $row
1469
-     * @return void
1470
-     * @throws EE_Error
1471
-     * @throws ReflectionException
1472
-     */
1473
-    public function set_row($row)
1474
-    {
1475
-        $this->set('TKT_row', $row);
1476
-    }
1477
-
1478
-
1479
-    /**
1480
-     * Gets deleted
1481
-     *
1482
-     * @return boolean
1483
-     * @throws EE_Error
1484
-     * @throws ReflectionException
1485
-     */
1486
-    public function deleted()
1487
-    {
1488
-        return $this->get('TKT_deleted');
1489
-    }
1490
-
1491
-
1492
-    /**
1493
-     * Sets deleted
1494
-     *
1495
-     * @param boolean $deleted
1496
-     * @return void
1497
-     * @throws EE_Error
1498
-     * @throws ReflectionException
1499
-     */
1500
-    public function set_deleted($deleted)
1501
-    {
1502
-        $this->set('TKT_deleted', $deleted);
1503
-    }
1504
-
1505
-
1506
-    /**
1507
-     * Gets parent
1508
-     *
1509
-     * @return int
1510
-     * @throws EE_Error
1511
-     * @throws ReflectionException
1512
-     */
1513
-    public function parent_ID()
1514
-    {
1515
-        return $this->get('TKT_parent');
1516
-    }
1517
-
1518
-
1519
-    /**
1520
-     * Sets parent
1521
-     *
1522
-     * @param int $parent
1523
-     * @return void
1524
-     * @throws EE_Error
1525
-     * @throws ReflectionException
1526
-     */
1527
-    public function set_parent_ID($parent)
1528
-    {
1529
-        $this->set('TKT_parent', $parent);
1530
-    }
1531
-
1532
-
1533
-    /**
1534
-     * @return boolean
1535
-     * @throws EE_Error
1536
-     * @throws InvalidArgumentException
1537
-     * @throws InvalidDataTypeException
1538
-     * @throws InvalidInterfaceException
1539
-     * @throws ReflectionException
1540
-     */
1541
-    public function reverse_calculate()
1542
-    {
1543
-        return $this->get('TKT_reverse_calculate');
1544
-    }
1545
-
1546
-
1547
-    /**
1548
-     * @param boolean $reverse_calculate
1549
-     * @throws EE_Error
1550
-     * @throws InvalidArgumentException
1551
-     * @throws InvalidDataTypeException
1552
-     * @throws InvalidInterfaceException
1553
-     * @throws ReflectionException
1554
-     */
1555
-    public function set_reverse_calculate($reverse_calculate)
1556
-    {
1557
-        $this->set('TKT_reverse_calculate', $reverse_calculate);
1558
-    }
1559
-
1560
-
1561
-    /**
1562
-     * Gets a string which is handy for showing in gateways etc that describes the ticket.
1563
-     *
1564
-     * @return string
1565
-     * @throws EE_Error
1566
-     * @throws ReflectionException
1567
-     */
1568
-    public function name_and_info()
1569
-    {
1570
-        $times = [];
1571
-        foreach ($this->datetimes() as $datetime) {
1572
-            $times[] = $datetime->start_date_and_time();
1573
-        }
1574
-        /* translators: %1$s ticket name, %2$s start datetimes separated by comma, %3$s ticket price */
1575
-        return sprintf(
1576
-            esc_html__('%1$s @ %2$s for %3$s', 'event_espresso'),
1577
-            $this->name(),
1578
-            implode(', ', $times),
1579
-            $this->pretty_price()
1580
-        );
1581
-    }
1582
-
1583
-
1584
-    /**
1585
-     * Gets name
1586
-     *
1587
-     * @return string
1588
-     * @throws EE_Error
1589
-     * @throws ReflectionException
1590
-     */
1591
-    public function name()
1592
-    {
1593
-        return $this->get('TKT_name');
1594
-    }
1595
-
1596
-
1597
-    /**
1598
-     * Gets price
1599
-     *
1600
-     * @return float
1601
-     * @throws EE_Error
1602
-     * @throws ReflectionException
1603
-     */
1604
-    public function price()
1605
-    {
1606
-        return $this->get('TKT_price');
1607
-    }
1608
-
1609
-
1610
-    /**
1611
-     * Gets all the registrations for this ticket
1612
-     *
1613
-     * @param array $query_params
1614
-     * @return EE_Registration[]|EE_Base_Class[]
1615
-     * @throws EE_Error
1616
-     * @throws ReflectionException
1617
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1618
-     */
1619
-    public function registrations($query_params = [])
1620
-    {
1621
-        return $this->get_many_related('Registration', $query_params);
1622
-    }
1623
-
1624
-
1625
-    /**
1626
-     * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1627
-     *
1628
-     * @return int
1629
-     * @throws EE_Error
1630
-     * @throws ReflectionException
1631
-     */
1632
-    public function update_tickets_sold()
1633
-    {
1634
-        $count_regs_for_this_ticket = $this->count_registrations(
1635
-            [
1636
-                [
1637
-                    'STS_ID'      => RegStatus::APPROVED,
1638
-                    'REG_deleted' => 0,
1639
-                ],
1640
-            ]
1641
-        );
1642
-        $this->set_sold($count_regs_for_this_ticket);
1643
-        $this->save();
1644
-        return $count_regs_for_this_ticket;
1645
-    }
1646
-
1647
-
1648
-    /**
1649
-     * Counts the registrations for this ticket
1650
-     *
1651
-     * @param array $query_params
1652
-     * @return int
1653
-     * @throws EE_Error
1654
-     * @throws ReflectionException
1655
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1656
-     */
1657
-    public function count_registrations($query_params = [])
1658
-    {
1659
-        return $this->count_related('Registration', $query_params);
1660
-    }
1661
-
1662
-
1663
-    /**
1664
-     * Implementation for EEI_Has_Icon interface method.
1665
-     *
1666
-     * @return string
1667
-     * @see EEI_Visual_Representation for comments
1668
-     */
1669
-    public function get_icon()
1670
-    {
1671
-        return '<span class="dashicons dashicons-tickets-alt"></span>';
1672
-    }
1673
-
1674
-
1675
-    /**
1676
-     * Implementation of the EEI_Event_Relation interface method
1677
-     *
1678
-     * @return EE_Event
1679
-     * @throws EE_Error
1680
-     * @throws UnexpectedEntityException
1681
-     * @throws ReflectionException
1682
-     * @see EEI_Event_Relation for comments
1683
-     */
1684
-    public function get_related_event()
1685
-    {
1686
-        // get one datetime to use for getting the event
1687
-        $datetime = $this->first_datetime();
1688
-        if (! $datetime instanceof EE_Datetime) {
1689
-            throw new UnexpectedEntityException(
1690
-                $datetime,
1691
-                'EE_Datetime',
1692
-                sprintf(
1693
-                    esc_html__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1694
-                    $this->name()
1695
-                )
1696
-            );
1697
-        }
1698
-        $event = $datetime->event();
1699
-        if (! $event instanceof EE_Event) {
1700
-            throw new UnexpectedEntityException(
1701
-                $event,
1702
-                'EE_Event',
1703
-                sprintf(
1704
-                    esc_html__('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1705
-                    $this->name()
1706
-                )
1707
-            );
1708
-        }
1709
-        return $event;
1710
-    }
1711
-
1712
-
1713
-    /**
1714
-     * Implementation of the EEI_Event_Relation interface method
1715
-     *
1716
-     * @return string
1717
-     * @throws UnexpectedEntityException
1718
-     * @throws EE_Error
1719
-     * @throws ReflectionException
1720
-     * @see EEI_Event_Relation for comments
1721
-     */
1722
-    public function get_event_name()
1723
-    {
1724
-        $event = $this->get_related_event();
1725
-        return $event instanceof EE_Event ? $event->name() : '';
1726
-    }
1727
-
1728
-
1729
-    /**
1730
-     * Implementation of the EEI_Event_Relation interface method
1731
-     *
1732
-     * @return int
1733
-     * @throws UnexpectedEntityException
1734
-     * @throws EE_Error
1735
-     * @throws ReflectionException
1736
-     * @see EEI_Event_Relation for comments
1737
-     */
1738
-    public function get_event_ID()
1739
-    {
1740
-        $event = $this->get_related_event();
1741
-        return $event instanceof EE_Event ? $event->ID() : 0;
1742
-    }
1743
-
1744
-
1745
-    /**
1746
-     * This simply returns whether a ticket can be permanently deleted or not.
1747
-     * The criteria for determining this is whether the ticket has any related registrations.
1748
-     * If there are none then it can be permanently deleted.
1749
-     *
1750
-     * @return bool
1751
-     * @throws EE_Error
1752
-     * @throws ReflectionException
1753
-     */
1754
-    public function is_permanently_deleteable()
1755
-    {
1756
-        return $this->count_registrations() === 0;
1757
-    }
1758
-
1759
-
1760
-    /**
1761
-     * @return int
1762
-     * @throws EE_Error
1763
-     * @throws ReflectionException
1764
-     * @since   5.0.0.p
1765
-     */
1766
-    public function visibility(): int
1767
-    {
1768
-        return $this->get('TKT_visibility');
1769
-    }
1770
-
1771
-
1772
-    /**
1773
-     * @return bool
1774
-     * @throws EE_Error
1775
-     * @throws ReflectionException
1776
-     * @since   5.0.0.p
1777
-     */
1778
-    public function isHidden(): bool
1779
-    {
1780
-        return $this->visibility() === EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1781
-    }
1782
-
1783
-
1784
-    /**
1785
-     * @return bool
1786
-     * @throws EE_Error
1787
-     * @throws ReflectionException
1788
-     * @since   5.0.0.p
1789
-     */
1790
-    public function isNotHidden(): bool
1791
-    {
1792
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1793
-    }
1794
-
1795
-
1796
-    /**
1797
-     * @return bool
1798
-     * @throws EE_Error
1799
-     * @throws ReflectionException
1800
-     * @since   5.0.0.p
1801
-     */
1802
-    public function isPublicOnly(): bool
1803
-    {
1804
-        return $this->isNotHidden() && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE;
1805
-    }
1806
-
1807
-
1808
-    /**
1809
-     * @return bool
1810
-     * @throws EE_Error
1811
-     * @throws ReflectionException
1812
-     * @since   5.0.0.p
1813
-     */
1814
-    public function isMembersOnly(): bool
1815
-    {
1816
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE
1817
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE;
1818
-    }
1819
-
1820
-
1821
-    /**
1822
-     * @return bool
1823
-     * @throws EE_Error
1824
-     * @throws ReflectionException
1825
-     * @since   5.0.0.p
1826
-     */
1827
-    public function isAdminsOnly(): bool
1828
-    {
1829
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE
1830
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE;
1831
-    }
1832
-
1833
-
1834
-    /**
1835
-     * @return bool
1836
-     * @throws EE_Error
1837
-     * @throws ReflectionException
1838
-     * @since   5.0.0.p
1839
-     */
1840
-    public function isAdminUiOnly(): bool
1841
-    {
1842
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE
1843
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE;
1844
-    }
1845
-
1846
-
1847
-    /**
1848
-     * @param int $visibility
1849
-     * @throws EE_Error
1850
-     * @throws ReflectionException
1851
-     * @since   5.0.0.p
1852
-     */
1853
-    public function set_visibility(int $visibility)
1854
-    {
1855
-        $ticket_visibility_options = $this->_model->ticketVisibilityOptions();
1856
-        $ticket_visibility         = -1;
1857
-        foreach ($ticket_visibility_options as $ticket_visibility_option) {
1858
-            if ($visibility === $ticket_visibility_option) {
1859
-                $ticket_visibility = $visibility;
1860
-            }
1861
-        }
1862
-        if ($ticket_visibility === -1) {
1863
-            throw new DomainException(
1864
-                sprintf(
1865
-                    esc_html__(
1866
-                        'The supplied ticket visibility setting of "%1$s" is not valid. It needs to match one of the keys in the following array:%2$s %3$s ',
1867
-                        'event_espresso'
1868
-                    ),
1869
-                    $visibility,
1870
-                    '<br />',
1871
-                    var_export($ticket_visibility_options, true)
1872
-                )
1873
-            );
1874
-        }
1875
-        $this->set('TKT_visibility', $ticket_visibility);
1876
-    }
1877
-
1878
-
1879
-    /**
1880
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1881
-     * @param string                   $relationName
1882
-     * @param array                    $extra_join_model_fields_n_values
1883
-     * @param string|null              $cache_id
1884
-     * @return EE_Base_Class
1885
-     * @throws EE_Error
1886
-     * @throws ReflectionException
1887
-     * @since   5.0.0.p
1888
-     */
1889
-    public function _add_relation_to(
1890
-        $otherObjectModelObjectOrID,
1891
-        $relationName,
1892
-        $extra_join_model_fields_n_values = [],
1893
-        $cache_id = null
1894
-    ) {
1895
-        if ($relationName === 'Datetime' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1896
-            /** @var EE_Datetime $datetime */
1897
-            $datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1898
-            $datetime->increaseSold($this->sold(), false);
1899
-            $datetime->increaseReserved($this->reserved());
1900
-            $datetime->save();
1901
-            $otherObjectModelObjectOrID = $datetime;
1902
-        }
1903
-        return parent::_add_relation_to(
1904
-            $otherObjectModelObjectOrID,
1905
-            $relationName,
1906
-            $extra_join_model_fields_n_values,
1907
-            $cache_id
1908
-        );
1909
-    }
1910
-
1911
-
1912
-    /**
1913
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1914
-     * @param string                   $relationName
1915
-     * @param array                    $where_query
1916
-     * @return bool|EE_Base_Class|null
1917
-     * @throws EE_Error
1918
-     * @throws ReflectionException
1919
-     * @since   5.0.0.p
1920
-     */
1921
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1922
-    {
1923
-        // if we're adding a new relation to a datetime
1924
-        if ($relationName === 'Datetime' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1925
-            /** @var EE_Datetime $datetime */
1926
-            $datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1927
-            $datetime->decreaseSold($this->sold());
1928
-            $datetime->decreaseReserved($this->reserved());
1929
-            $datetime->save();
1930
-            $otherObjectModelObjectOrID = $datetime;
1931
-        }
1932
-        return parent::_remove_relation_to(
1933
-            $otherObjectModelObjectOrID,
1934
-            $relationName,
1935
-            $where_query
1936
-        );
1937
-    }
1938
-
1939
-
1940
-    /**
1941
-     * Removes ALL the related things for the $relationName.
1942
-     *
1943
-     * @param string $relationName
1944
-     * @param array  $where_query_params
1945
-     * @return EE_Base_Class
1946
-     * @throws ReflectionException
1947
-     * @throws InvalidArgumentException
1948
-     * @throws InvalidInterfaceException
1949
-     * @throws InvalidDataTypeException
1950
-     * @throws EE_Error
1951
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1952
-     */
1953
-    public function _remove_relations($relationName, $where_query_params = [])
1954
-    {
1955
-        if ($relationName === 'Datetime') {
1956
-            $datetimes = $this->datetimes();
1957
-            foreach ($datetimes as $datetime) {
1958
-                $datetime->decreaseSold($this->sold());
1959
-                $datetime->decreaseReserved($this->reserved());
1960
-                $datetime->save();
1961
-            }
1962
-        }
1963
-        return parent::_remove_relations($relationName, $where_query_params);
1964
-    }
1965
-
1966
-
1967
-    /*******************************************************************
18
+	/**
19
+	 * TicKet Archived:
20
+	 * constant used by ticket_status() to indicate that a ticket is archived
21
+	 * and no longer available for purchase
22
+	 */
23
+	const archived = 'TKA';
24
+
25
+	/**
26
+	 * TicKet Expired:
27
+	 * constant used by ticket_status() to indicate that a ticket is expired
28
+	 * and no longer available for purchase
29
+	 */
30
+	const expired = 'TKE';
31
+
32
+	/**
33
+	 * TicKet On sale:
34
+	 * constant used by ticket_status() to indicate that a ticket is On Sale
35
+	 * and IS available for purchase
36
+	 */
37
+	const onsale = 'TKO';
38
+
39
+	/**
40
+	 * TicKet Pending:
41
+	 * constant used by ticket_status() to indicate that a ticket is pending
42
+	 * and is NOT YET available for purchase
43
+	 */
44
+	const pending = 'TKP';
45
+
46
+	/**
47
+	 * TicKet Sold out:
48
+	 * constant used by ticket_status() to indicate that a ticket is sold out
49
+	 * and no longer available for purchases
50
+	 */
51
+	const sold_out = 'TKS';
52
+
53
+	/**
54
+	 * extra meta key for tracking ticket reservations
55
+	 *
56
+	 * @type string
57
+	 */
58
+	const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
59
+
60
+	/**
61
+	 * override of parent property
62
+	 *
63
+	 * @var EEM_Ticket
64
+	 */
65
+	protected $_model;
66
+
67
+	/**
68
+	 * cached result from method of the same name
69
+	 *
70
+	 * @var float $_ticket_total_with_taxes
71
+	 */
72
+	private $_ticket_total_with_taxes;
73
+
74
+	/**
75
+	 * @var TicketPriceModifiers
76
+	 */
77
+	protected $ticket_price_modifiers;
78
+
79
+
80
+	/**
81
+	 * @param array  $props_n_values          incoming values
82
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
83
+	 *                                        used.)
84
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
85
+	 *                                        date_format and the second value is the time format
86
+	 * @return EE_Ticket
87
+	 * @throws EE_Error
88
+	 * @throws ReflectionException
89
+	 */
90
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
91
+	{
92
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
93
+		return $has_object ?: new self($props_n_values, false, $timezone, $date_formats);
94
+	}
95
+
96
+
97
+	/**
98
+	 * @param array  $props_n_values  incoming values from the database
99
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
100
+	 *                                the website will be used.
101
+	 * @return EE_Ticket
102
+	 * @throws EE_Error
103
+	 * @throws ReflectionException
104
+	 */
105
+	public static function new_instance_from_db($props_n_values = [], $timezone = '')
106
+	{
107
+		return new self($props_n_values, true, $timezone);
108
+	}
109
+
110
+
111
+	/**
112
+	 * @param array  $fieldValues
113
+	 * @param false  $bydb
114
+	 * @param string $timezone
115
+	 * @param array  $date_formats
116
+	 * @throws EE_Error
117
+	 * @throws ReflectionException
118
+	 */
119
+	public function __construct($fieldValues = [], $bydb = false, $timezone = '', $date_formats = [])
120
+	{
121
+		parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
122
+		$this->ticket_price_modifiers = new TicketPriceModifiers($this);
123
+	}
124
+
125
+
126
+	/**
127
+	 * @return bool
128
+	 * @throws EE_Error
129
+	 * @throws ReflectionException
130
+	 */
131
+	public function parent()
132
+	{
133
+		return $this->get('TKT_parent');
134
+	}
135
+
136
+
137
+	/**
138
+	 * return if a ticket has quantities available for purchase
139
+	 *
140
+	 * @param int $DTT_ID the primary key for a particular datetime
141
+	 * @return boolean
142
+	 * @throws EE_Error
143
+	 * @throws ReflectionException
144
+	 */
145
+	public function available($DTT_ID = 0)
146
+	{
147
+		// are we checking availability for a particular datetime ?
148
+		if ($DTT_ID) {
149
+			// get that datetime object
150
+			$datetime = $this->get_first_related('Datetime', [['DTT_ID' => $DTT_ID]]);
151
+			// if  ticket sales for this datetime have exceeded the reg limit...
152
+			if ($datetime instanceof EE_Datetime && $datetime->sold_out()) {
153
+				return false;
154
+			}
155
+		}
156
+		// datetime is still open for registration, but is this ticket sold out ?
157
+		return $this->qty() < 1 || $this->qty() > $this->sold();
158
+	}
159
+
160
+
161
+	/**
162
+	 * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
163
+	 *
164
+	 * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
165
+	 *                               relevant status const
166
+	 * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
167
+	 *                               further processing
168
+	 * @return mixed status int if the display string isn't requested
169
+	 * @throws EE_Error
170
+	 * @throws ReflectionException
171
+	 */
172
+	public function ticket_status($display = false, $remaining = null)
173
+	{
174
+		$remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
175
+		if (! $remaining) {
176
+			return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
177
+		}
178
+		if ($this->get('TKT_deleted')) {
179
+			return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
180
+		}
181
+		if ($this->is_expired()) {
182
+			return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
183
+		}
184
+		if ($this->is_pending()) {
185
+			return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
186
+		}
187
+		if ($this->is_on_sale()) {
188
+			return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
189
+		}
190
+		return '';
191
+	}
192
+
193
+
194
+	/**
195
+	 * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
196
+	 * considering ALL the factors used for figuring that out.
197
+	 *
198
+	 * @param int $DTT_ID if an int above 0 is included here then we get a specific dtt.
199
+	 * @return boolean         true = tickets remaining, false not.
200
+	 * @throws EE_Error
201
+	 * @throws ReflectionException
202
+	 */
203
+	public function is_remaining($DTT_ID = 0)
204
+	{
205
+		$num_remaining = $this->remaining($DTT_ID);
206
+		if ($num_remaining === 0) {
207
+			return false;
208
+		}
209
+		if ($num_remaining > 0 && $num_remaining < $this->min()) {
210
+			return false;
211
+		}
212
+		return true;
213
+	}
214
+
215
+
216
+	/**
217
+	 * return the total number of tickets available for purchase
218
+	 *
219
+	 * @param int $DTT_ID  the primary key for a particular datetime.
220
+	 *                     set to 0 for all related datetimes
221
+	 * @return int
222
+	 * @throws EE_Error
223
+	 * @throws ReflectionException
224
+	 */
225
+	public function remaining($DTT_ID = 0)
226
+	{
227
+		return $this->real_quantity_on_ticket('saleable', $DTT_ID);
228
+	}
229
+
230
+
231
+	/**
232
+	 * Gets min
233
+	 *
234
+	 * @return int
235
+	 * @throws EE_Error
236
+	 * @throws ReflectionException
237
+	 */
238
+	public function min()
239
+	{
240
+		return $this->get('TKT_min');
241
+	}
242
+
243
+
244
+	/**
245
+	 * return if a ticket is no longer available cause its available dates have expired.
246
+	 *
247
+	 * @return boolean
248
+	 * @throws EE_Error
249
+	 * @throws ReflectionException
250
+	 */
251
+	public function is_expired()
252
+	{
253
+		return ($this->get_raw('TKT_end_date') < time());
254
+	}
255
+
256
+
257
+	/**
258
+	 * Return if a ticket is yet to go on sale or not
259
+	 *
260
+	 * @return boolean
261
+	 * @throws EE_Error
262
+	 * @throws ReflectionException
263
+	 */
264
+	public function is_pending()
265
+	{
266
+		return ($this->get_raw('TKT_start_date') >= time());
267
+	}
268
+
269
+
270
+	/**
271
+	 * Return if a ticket is on sale or not
272
+	 *
273
+	 * @return boolean
274
+	 * @throws EE_Error
275
+	 * @throws ReflectionException
276
+	 */
277
+	public function is_on_sale()
278
+	{
279
+		return ($this->get_raw('TKT_start_date') <= time() && $this->get_raw('TKT_end_date') >= time());
280
+	}
281
+
282
+
283
+	/**
284
+	 * This returns the chronologically last datetime that this ticket is associated with
285
+	 *
286
+	 * @param string $date_format
287
+	 * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
288
+	 *                            the end date ie: Jan 01 "to" Dec 31
289
+	 * @return string
290
+	 * @throws EE_Error
291
+	 * @throws ReflectionException
292
+	 */
293
+	public function date_range($date_format = '', $conjunction = ' - ')
294
+	{
295
+		$date_format = ! empty($date_format) ? $date_format : $this->_dt_frmt;
296
+		$first_date  = $this->first_datetime() instanceof EE_Datetime
297
+			? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $date_format)
298
+			: '';
299
+		$last_date   = $this->last_datetime() instanceof EE_Datetime
300
+			? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format)
301
+			: '';
302
+
303
+		return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
304
+	}
305
+
306
+
307
+	/**
308
+	 * This returns the chronologically first datetime that this ticket is associated with
309
+	 *
310
+	 * @return EE_Datetime
311
+	 * @throws EE_Error
312
+	 * @throws ReflectionException
313
+	 */
314
+	public function first_datetime()
315
+	{
316
+		$datetimes = $this->datetimes(['limit' => 1]);
317
+		return reset($datetimes);
318
+	}
319
+
320
+
321
+	/**
322
+	 * Gets all the datetimes this ticket can be used for attending.
323
+	 * Unless otherwise specified, orders datetimes by start date.
324
+	 *
325
+	 * @param array $query_params
326
+	 * @return EE_Datetime[]|EE_Base_Class[]
327
+	 * @throws EE_Error
328
+	 * @throws ReflectionException
329
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
330
+	 */
331
+	public function datetimes($query_params = [])
332
+	{
333
+		if (! isset($query_params['order_by'])) {
334
+			$query_params['order_by']['DTT_order'] = 'ASC';
335
+		}
336
+		return $this->get_many_related('Datetime', $query_params);
337
+	}
338
+
339
+
340
+	/**
341
+	 * This returns the chronologically last datetime that this ticket is associated with
342
+	 *
343
+	 * @return EE_Datetime
344
+	 * @throws EE_Error
345
+	 * @throws ReflectionException
346
+	 */
347
+	public function last_datetime()
348
+	{
349
+		$datetimes = $this->datetimes(['limit' => 1, 'order_by' => ['DTT_EVT_start' => 'DESC']]);
350
+		return end($datetimes);
351
+	}
352
+
353
+
354
+	/**
355
+	 * This returns the total tickets sold depending on the given parameters.
356
+	 *
357
+	 * @param string $what    Can be one of two options: 'ticket', 'datetime'.
358
+	 *                        'ticket' = total ticket sales for all datetimes this ticket is related to
359
+	 *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
360
+	 *                        'datetime' = total ticket sales in the datetime_ticket table.
361
+	 *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
362
+	 *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
363
+	 * @param int    $dtt_id  [optional] include the dtt_id with $what = 'datetime'.
364
+	 * @return mixed (array|int)          how many tickets have sold
365
+	 * @throws EE_Error
366
+	 * @throws ReflectionException
367
+	 */
368
+	public function tickets_sold($what = 'ticket', $dtt_id = null)
369
+	{
370
+		$total        = 0;
371
+		$tickets_sold = $this->_all_tickets_sold();
372
+		switch ($what) {
373
+			case 'ticket':
374
+				return $tickets_sold['ticket'];
375
+
376
+			case 'datetime':
377
+				if (empty($tickets_sold['datetime'])) {
378
+					return $total;
379
+				}
380
+				if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
381
+					EE_Error::add_error(
382
+						esc_html__(
383
+							'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included.  Are you SURE that is a datetime related to this ticket?',
384
+							'event_espresso'
385
+						),
386
+						__FILE__,
387
+						__FUNCTION__,
388
+						__LINE__
389
+					);
390
+					return $total;
391
+				}
392
+				return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
393
+
394
+			default:
395
+				return $total;
396
+		}
397
+	}
398
+
399
+
400
+	/**
401
+	 * This returns an array indexed by datetime_id for tickets sold with this ticket.
402
+	 *
403
+	 * @return EE_Ticket[]
404
+	 * @throws EE_Error
405
+	 * @throws ReflectionException
406
+	 */
407
+	protected function _all_tickets_sold()
408
+	{
409
+		$datetimes    = $this->get_many_related('Datetime');
410
+		$tickets_sold = [];
411
+		if (! empty($datetimes)) {
412
+			foreach ($datetimes as $datetime) {
413
+				$tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
414
+			}
415
+		}
416
+		// Tickets sold
417
+		$tickets_sold['ticket'] = $this->sold();
418
+		return $tickets_sold;
419
+	}
420
+
421
+
422
+	/**
423
+	 * This returns the base price object for the ticket.
424
+	 *
425
+	 * @param bool $return_array whether to return as an array indexed by price id or just the object.
426
+	 * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
427
+	 * @throws EE_Error
428
+	 * @throws ReflectionException
429
+	 */
430
+	public function base_price(bool $return_array = false)
431
+	{
432
+		$base_price = $this->ticket_price_modifiers->getBasePrice();
433
+		if (! empty($base_price)) {
434
+			return $return_array ? $base_price : reset($base_price);
435
+		}
436
+		$_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price];
437
+		return $return_array
438
+			? $this->get_many_related('Price', [$_where])
439
+			: $this->get_first_related('Price', [$_where]);
440
+	}
441
+
442
+
443
+	/**
444
+	 * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
445
+	 *
446
+	 * @return EE_Price[]
447
+	 * @throws EE_Error
448
+	 * @throws ReflectionException
449
+	 */
450
+	public function price_modifiers(): array
451
+	{
452
+		$price_modifiers = $this->usesGlobalTaxes()
453
+			? $this->ticket_price_modifiers->getAllDiscountAndSurchargeModifiersForTicket()
454
+			: $this->ticket_price_modifiers->getAllModifiersForTicket();
455
+		if (! empty($price_modifiers)) {
456
+			return $price_modifiers;
457
+		}
458
+		return $this->prices(
459
+			[
460
+				[
461
+					'Price_Type.PBT_ID' => [
462
+						'NOT IN',
463
+						[EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax],
464
+					],
465
+				],
466
+			]
467
+		);
468
+	}
469
+
470
+
471
+	/**
472
+	 * This returns ONLY the TAX price modifiers for the ticket
473
+	 *
474
+	 * @return EE_Price[]
475
+	 * @throws EE_Error
476
+	 * @throws ReflectionException
477
+	 */
478
+	public function tax_price_modifiers(): array
479
+	{
480
+		$tax_price_modifiers = $this->ticket_price_modifiers->getAllTaxesForTicket();
481
+		if (! empty($tax_price_modifiers)) {
482
+			return $tax_price_modifiers;
483
+		}
484
+		return $this->prices([['Price_Type.PBT_ID' => EEM_Price_Type::base_type_tax]]);
485
+	}
486
+
487
+
488
+	/**
489
+	 * Gets all the prices that combine to form the final price of this ticket
490
+	 *
491
+	 * @param array $query_params
492
+	 * @return EE_Price[]|EE_Base_Class[]
493
+	 * @throws EE_Error
494
+	 * @throws ReflectionException
495
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
496
+	 */
497
+	public function prices(array $query_params = []): array
498
+	{
499
+		if (! isset($query_params['order_by'])) {
500
+			$query_params['order_by']['PRC_order'] = 'ASC';
501
+		}
502
+		return $this->get_many_related('Price', $query_params);
503
+	}
504
+
505
+
506
+	/**
507
+	 * Gets all the ticket datetimes (ie, relations between datetimes and tickets)
508
+	 *
509
+	 * @param array $query_params
510
+	 * @return EE_Datetime_Ticket|EE_Base_Class[]
511
+	 * @throws EE_Error
512
+	 * @throws ReflectionException
513
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
514
+	 */
515
+	public function datetime_tickets($query_params = [])
516
+	{
517
+		return $this->get_many_related('Datetime_Ticket', $query_params);
518
+	}
519
+
520
+
521
+	/**
522
+	 * Gets all the datetimes from the db ordered by DTT_order
523
+	 *
524
+	 * @param boolean $show_expired
525
+	 * @param boolean $show_deleted
526
+	 * @return EE_Datetime[]
527
+	 * @throws EE_Error
528
+	 * @throws ReflectionException
529
+	 */
530
+	public function datetimes_ordered($show_expired = true, $show_deleted = false)
531
+	{
532
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
533
+			$this->ID(),
534
+			$show_expired,
535
+			$show_deleted
536
+		);
537
+	}
538
+
539
+
540
+	/**
541
+	 * Gets ID
542
+	 *
543
+	 * @return int
544
+	 * @throws EE_Error
545
+	 * @throws ReflectionException
546
+	 */
547
+	public function ID()
548
+	{
549
+		return (int) $this->get('TKT_ID');
550
+	}
551
+
552
+
553
+	/**
554
+	 * get the author of the ticket.
555
+	 *
556
+	 * @return int
557
+	 * @throws EE_Error
558
+	 * @throws ReflectionException
559
+	 * @since 4.5.0
560
+	 */
561
+	public function wp_user()
562
+	{
563
+		return $this->get('TKT_wp_user');
564
+	}
565
+
566
+
567
+	/**
568
+	 * Gets the template for the ticket
569
+	 *
570
+	 * @return EE_Ticket_Template|EE_Base_Class
571
+	 * @throws EE_Error
572
+	 * @throws ReflectionException
573
+	 */
574
+	public function template()
575
+	{
576
+		return $this->get_first_related('Ticket_Template');
577
+	}
578
+
579
+
580
+	/**
581
+	 * Simply returns an array of EE_Price objects that are taxes.
582
+	 *
583
+	 * @return EE_Price[]
584
+	 * @throws EE_Error
585
+	 * @throws ReflectionException
586
+	 */
587
+	public function get_ticket_taxes_for_admin(): array
588
+	{
589
+		return $this->usesGlobalTaxes() ? EE_Taxes::get_taxes_for_admin() : $this->tax_price_modifiers();
590
+	}
591
+
592
+
593
+	/**
594
+	 * alias of taxable() to better indicate that ticket uses the legacy method of applying default "global" taxes
595
+	 * as opposed to having tax price modifiers added directly to each ticket
596
+	 *
597
+	 * @return bool
598
+	 * @throws EE_Error
599
+	 * @throws ReflectionException
600
+	 * @since   5.0.0.p
601
+	 */
602
+	public function usesGlobalTaxes(): bool
603
+	{
604
+		return $this->taxable();
605
+	}
606
+
607
+
608
+	/**
609
+	 * @return float
610
+	 * @throws EE_Error
611
+	 * @throws ReflectionException
612
+	 */
613
+	public function ticket_price()
614
+	{
615
+		return $this->get('TKT_price');
616
+	}
617
+
618
+
619
+	/**
620
+	 * @return mixed
621
+	 * @throws EE_Error
622
+	 * @throws ReflectionException
623
+	 */
624
+	public function pretty_price()
625
+	{
626
+		return $this->get_pretty('TKT_price');
627
+	}
628
+
629
+
630
+	/**
631
+	 * @return bool
632
+	 * @throws EE_Error
633
+	 * @throws ReflectionException
634
+	 */
635
+	public function is_free()
636
+	{
637
+		return $this->get_ticket_total_with_taxes() === (float) 0;
638
+	}
639
+
640
+
641
+	/**
642
+	 * get_ticket_total_with_taxes
643
+	 *
644
+	 * @param bool $no_cache
645
+	 * @return float
646
+	 * @throws EE_Error
647
+	 * @throws ReflectionException
648
+	 */
649
+	public function get_ticket_total_with_taxes(bool $no_cache = false): float
650
+	{
651
+		if ($this->_ticket_total_with_taxes === null || $no_cache) {
652
+			$this->_ticket_total_with_taxes = $this->get_ticket_subtotal();
653
+			// add taxes
654
+			if ($this->usesGlobalTaxes()) {
655
+				$this->_ticket_total_with_taxes += $this->get_ticket_taxes_total_for_admin();
656
+			} else {
657
+				$subtotal = $this->_ticket_total_with_taxes;
658
+				foreach ($this->tax_price_modifiers() as $tax) {
659
+					$this->_ticket_total_with_taxes += $subtotal * $tax->amount() / 100;
660
+				}
661
+			}
662
+		}
663
+		return (float) $this->_ticket_total_with_taxes;
664
+	}
665
+
666
+
667
+	/**
668
+	 * @throws EE_Error
669
+	 * @throws ReflectionException
670
+	 */
671
+	public function ensure_TKT_Price_correct()
672
+	{
673
+		$this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
674
+		$this->save();
675
+	}
676
+
677
+
678
+	/**
679
+	 * @return float
680
+	 * @throws EE_Error
681
+	 * @throws ReflectionException
682
+	 */
683
+	public function get_ticket_subtotal()
684
+	{
685
+		return EE_Taxes::get_subtotal_for_admin($this);
686
+	}
687
+
688
+
689
+	/**
690
+	 * Returns the total taxes applied to this ticket
691
+	 *
692
+	 * @return float
693
+	 * @throws EE_Error
694
+	 * @throws ReflectionException
695
+	 */
696
+	public function get_ticket_taxes_total_for_admin()
697
+	{
698
+		return EE_Taxes::get_total_taxes_for_admin($this);
699
+	}
700
+
701
+
702
+	/**
703
+	 * Sets name
704
+	 *
705
+	 * @param string $name
706
+	 * @throws EE_Error
707
+	 * @throws ReflectionException
708
+	 */
709
+	public function set_name($name)
710
+	{
711
+		$this->set('TKT_name', $name);
712
+	}
713
+
714
+
715
+	/**
716
+	 * Gets description
717
+	 *
718
+	 * @return string
719
+	 * @throws EE_Error
720
+	 * @throws ReflectionException
721
+	 */
722
+	public function description()
723
+	{
724
+		return $this->get('TKT_description');
725
+	}
726
+
727
+
728
+	/**
729
+	 * Sets description
730
+	 *
731
+	 * @param string $description
732
+	 * @throws EE_Error
733
+	 * @throws ReflectionException
734
+	 */
735
+	public function set_description($description)
736
+	{
737
+		$this->set('TKT_description', $description);
738
+	}
739
+
740
+
741
+	/**
742
+	 * Gets start_date
743
+	 *
744
+	 * @param string|null $date_format
745
+	 * @param string|null $time_format
746
+	 * @return string
747
+	 * @throws EE_Error
748
+	 * @throws ReflectionException
749
+	 */
750
+	public function start_date(?string $date_format = '', ?string $time_format = ''): string
751
+	{
752
+		return $this->_get_datetime('TKT_start_date', $date_format, $time_format);
753
+	}
754
+
755
+
756
+	/**
757
+	 * Sets start_date
758
+	 *
759
+	 * @param string $start_date
760
+	 * @return void
761
+	 * @throws EE_Error
762
+	 * @throws ReflectionException
763
+	 */
764
+	public function set_start_date($start_date)
765
+	{
766
+		$this->_set_date_time('B', $start_date, 'TKT_start_date');
767
+	}
768
+
769
+
770
+	/**
771
+	 * Gets end_date
772
+	 *
773
+	 * @param string|null $date_format
774
+	 * @param string|null $time_format
775
+	 * @return string
776
+	 * @throws EE_Error
777
+	 * @throws ReflectionException
778
+	 */
779
+	public function end_date(?string $date_format = '', ?string $time_format = ''): string
780
+	{
781
+		return $this->_get_datetime('TKT_end_date', $date_format, $time_format);
782
+	}
783
+
784
+
785
+	/**
786
+	 * Sets end_date
787
+	 *
788
+	 * @param string $end_date
789
+	 * @return void
790
+	 * @throws EE_Error
791
+	 * @throws ReflectionException
792
+	 */
793
+	public function set_end_date($end_date)
794
+	{
795
+		$this->_set_date_time('B', $end_date, 'TKT_end_date');
796
+	}
797
+
798
+
799
+	/**
800
+	 * Sets sell until time
801
+	 *
802
+	 * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
803
+	 * @throws EE_Error
804
+	 * @throws ReflectionException
805
+	 * @since 4.5.0
806
+	 */
807
+	public function set_end_time($time)
808
+	{
809
+		$this->_set_time_for($time, 'TKT_end_date');
810
+	}
811
+
812
+
813
+	/**
814
+	 * Sets min
815
+	 *
816
+	 * @param int $min
817
+	 * @return void
818
+	 * @throws EE_Error
819
+	 * @throws ReflectionException
820
+	 */
821
+	public function set_min($min)
822
+	{
823
+		$this->set('TKT_min', $min);
824
+	}
825
+
826
+
827
+	/**
828
+	 * Gets max
829
+	 *
830
+	 * @return int
831
+	 * @throws EE_Error
832
+	 * @throws ReflectionException
833
+	 */
834
+	public function max()
835
+	{
836
+		return $this->get('TKT_max');
837
+	}
838
+
839
+
840
+	/**
841
+	 * Sets max
842
+	 *
843
+	 * @param int $max
844
+	 * @return void
845
+	 * @throws EE_Error
846
+	 * @throws ReflectionException
847
+	 */
848
+	public function set_max($max)
849
+	{
850
+		$this->set('TKT_max', $max);
851
+	}
852
+
853
+
854
+	/**
855
+	 * Sets price
856
+	 *
857
+	 * @param float $price
858
+	 * @return void
859
+	 * @throws EE_Error
860
+	 * @throws ReflectionException
861
+	 */
862
+	public function set_price($price)
863
+	{
864
+		$this->set('TKT_price', $price);
865
+	}
866
+
867
+
868
+	/**
869
+	 * Gets sold
870
+	 *
871
+	 * @return int
872
+	 * @throws EE_Error
873
+	 * @throws ReflectionException
874
+	 */
875
+	public function sold(): int
876
+	{
877
+		return (int) $this->get_raw('TKT_sold');
878
+	}
879
+
880
+
881
+	/**
882
+	 * Sets sold
883
+	 *
884
+	 * @param int $sold
885
+	 * @return void
886
+	 * @throws EE_Error
887
+	 * @throws ReflectionException
888
+	 */
889
+	public function set_sold($sold)
890
+	{
891
+		// sold can not go below zero
892
+		$sold = max(0, $sold);
893
+		$this->set('TKT_sold', $sold);
894
+	}
895
+
896
+
897
+	/**
898
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
899
+	 * associated datetimes.
900
+	 *
901
+	 * @param int $qty
902
+	 * @return boolean
903
+	 * @throws EE_Error
904
+	 * @throws InvalidArgumentException
905
+	 * @throws InvalidDataTypeException
906
+	 * @throws InvalidInterfaceException
907
+	 * @throws ReflectionException
908
+	 * @since 4.9.80.p
909
+	 */
910
+	public function increaseSold($qty = 1)
911
+	{
912
+		$qty = absint($qty);
913
+		// increment sold and decrement reserved datetime quantities simultaneously
914
+		// don't worry about failures, because they must have already had a spot reserved
915
+		$this->increaseSoldForDatetimes($qty);
916
+		// Increment and decrement ticket quantities simultaneously
917
+		$success = $this->adjustNumericFieldsInDb(
918
+			[
919
+				'TKT_reserved' => $qty * -1,
920
+				'TKT_sold'     => $qty,
921
+			]
922
+		);
923
+		do_action(
924
+			'AHEE__EE_Ticket__increase_sold',
925
+			$this,
926
+			$qty,
927
+			$this->sold(),
928
+			$success
929
+		);
930
+		return $success;
931
+	}
932
+
933
+
934
+	/**
935
+	 * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
936
+	 *
937
+	 * @param int           $qty positive or negative. Positive means to increase sold counts (and decrease reserved
938
+	 *                           counts), Negative means to decreases old counts (and increase reserved counts).
939
+	 * @param EE_Datetime[] $datetimes
940
+	 * @throws EE_Error
941
+	 * @throws InvalidArgumentException
942
+	 * @throws InvalidDataTypeException
943
+	 * @throws InvalidInterfaceException
944
+	 * @throws ReflectionException
945
+	 * @since 4.9.80.p
946
+	 */
947
+	protected function increaseSoldForDatetimes($qty, array $datetimes = [])
948
+	{
949
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
950
+		foreach ($datetimes as $datetime) {
951
+			$datetime->increaseSold($qty);
952
+		}
953
+	}
954
+
955
+
956
+	/**
957
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
958
+	 * DB and then updates the model objects.
959
+	 * Does not affect the reserved counts.
960
+	 *
961
+	 * @param int $qty
962
+	 * @return boolean
963
+	 * @throws EE_Error
964
+	 * @throws InvalidArgumentException
965
+	 * @throws InvalidDataTypeException
966
+	 * @throws InvalidInterfaceException
967
+	 * @throws ReflectionException
968
+	 * @since 4.9.80.p
969
+	 */
970
+	public function decreaseSold($qty = 1)
971
+	{
972
+		$qty = absint($qty);
973
+		$this->decreaseSoldForDatetimes($qty);
974
+		$success = $this->adjustNumericFieldsInDb(
975
+			[
976
+				'TKT_sold' => $qty * -1,
977
+			]
978
+		);
979
+		do_action(
980
+			'AHEE__EE_Ticket__decrease_sold',
981
+			$this,
982
+			$qty,
983
+			$this->sold(),
984
+			$success
985
+		);
986
+		return $success;
987
+	}
988
+
989
+
990
+	/**
991
+	 * Decreases sold on related datetimes
992
+	 *
993
+	 * @param int           $qty
994
+	 * @param EE_Datetime[] $datetimes
995
+	 * @return void
996
+	 * @throws EE_Error
997
+	 * @throws InvalidArgumentException
998
+	 * @throws InvalidDataTypeException
999
+	 * @throws InvalidInterfaceException
1000
+	 * @throws ReflectionException
1001
+	 * @since 4.9.80.p
1002
+	 */
1003
+	protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
1004
+	{
1005
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1006
+		if (is_array($datetimes)) {
1007
+			foreach ($datetimes as $datetime) {
1008
+				if ($datetime instanceof EE_Datetime) {
1009
+					$datetime->decreaseSold($qty);
1010
+				}
1011
+			}
1012
+		}
1013
+	}
1014
+
1015
+
1016
+	/**
1017
+	 * Gets qty of reserved tickets
1018
+	 *
1019
+	 * @return int
1020
+	 * @throws EE_Error
1021
+	 * @throws ReflectionException
1022
+	 */
1023
+	public function reserved(): int
1024
+	{
1025
+		return (int) $this->get_raw('TKT_reserved');
1026
+	}
1027
+
1028
+
1029
+	/**
1030
+	 * Sets reserved
1031
+	 *
1032
+	 * @param int $reserved
1033
+	 * @return void
1034
+	 * @throws EE_Error
1035
+	 * @throws ReflectionException
1036
+	 */
1037
+	public function set_reserved($reserved)
1038
+	{
1039
+		// reserved can not go below zero
1040
+		$reserved = max(0, (int) $reserved);
1041
+		$this->set('TKT_reserved', $reserved);
1042
+	}
1043
+
1044
+
1045
+	/**
1046
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1047
+	 *
1048
+	 * @param int    $qty
1049
+	 * @param string $source
1050
+	 * @return bool whether we successfully reserved the ticket or not.
1051
+	 * @throws EE_Error
1052
+	 * @throws InvalidArgumentException
1053
+	 * @throws ReflectionException
1054
+	 * @throws InvalidDataTypeException
1055
+	 * @throws InvalidInterfaceException
1056
+	 * @since 4.9.80.p
1057
+	 */
1058
+	public function increaseReserved($qty = 1, $source = 'unknown')
1059
+	{
1060
+		$qty = absint($qty);
1061
+		do_action(
1062
+			'AHEE__EE_Ticket__increase_reserved__begin',
1063
+			$this,
1064
+			$qty,
1065
+			$source
1066
+		);
1067
+		$this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
1068
+		$success                         = false;
1069
+		$datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
1070
+		if ($datetimes_adjusted_successfully) {
1071
+			$success = $this->incrementFieldConditionallyInDb(
1072
+				'TKT_reserved',
1073
+				'TKT_sold',
1074
+				'TKT_qty',
1075
+				$qty
1076
+			);
1077
+			if (! $success) {
1078
+				// The datetimes were successfully bumped, but not the
1079
+				// ticket. So we need to manually rollback the datetimes.
1080
+				$this->decreaseReservedForDatetimes($qty);
1081
+			}
1082
+		}
1083
+		do_action(
1084
+			'AHEE__EE_Ticket__increase_reserved',
1085
+			$this,
1086
+			$qty,
1087
+			$this->reserved(),
1088
+			$success
1089
+		);
1090
+		return $success;
1091
+	}
1092
+
1093
+
1094
+	/**
1095
+	 * Increases reserved counts on related datetimes
1096
+	 *
1097
+	 * @param int           $qty
1098
+	 * @param EE_Datetime[] $datetimes
1099
+	 * @return boolean indicating success
1100
+	 * @throws EE_Error
1101
+	 * @throws InvalidArgumentException
1102
+	 * @throws InvalidDataTypeException
1103
+	 * @throws InvalidInterfaceException
1104
+	 * @throws ReflectionException
1105
+	 * @since 4.9.80.p
1106
+	 */
1107
+	protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
1108
+	{
1109
+		$datetimes         = ! empty($datetimes) ? $datetimes : $this->datetimes();
1110
+		$datetimes_updated = [];
1111
+		$limit_exceeded    = false;
1112
+		if (is_array($datetimes)) {
1113
+			foreach ($datetimes as $datetime) {
1114
+				if ($datetime instanceof EE_Datetime) {
1115
+					if ($datetime->increaseReserved($qty)) {
1116
+						$datetimes_updated[] = $datetime;
1117
+					} else {
1118
+						$limit_exceeded = true;
1119
+						break;
1120
+					}
1121
+				}
1122
+			}
1123
+			// If somewhere along the way we detected a datetime whose
1124
+			// limit was exceeded, do a manual rollback.
1125
+			if ($limit_exceeded) {
1126
+				$this->decreaseReservedForDatetimes($qty, $datetimes_updated);
1127
+				return false;
1128
+			}
1129
+		}
1130
+		return true;
1131
+	}
1132
+
1133
+
1134
+	/**
1135
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1136
+	 *
1137
+	 * @param int    $qty
1138
+	 * @param bool   $adjust_datetimes
1139
+	 * @param string $source
1140
+	 * @return boolean
1141
+	 * @throws EE_Error
1142
+	 * @throws InvalidArgumentException
1143
+	 * @throws ReflectionException
1144
+	 * @throws InvalidDataTypeException
1145
+	 * @throws InvalidInterfaceException
1146
+	 * @since 4.9.80.p
1147
+	 */
1148
+	public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1149
+	{
1150
+		$qty = absint($qty);
1151
+		$this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1152
+		if ($adjust_datetimes) {
1153
+			$this->decreaseReservedForDatetimes($qty);
1154
+		}
1155
+		$success = $this->adjustNumericFieldsInDb(
1156
+			[
1157
+				'TKT_reserved' => $qty * -1,
1158
+			]
1159
+		);
1160
+		do_action(
1161
+			'AHEE__EE_Ticket__decrease_reserved',
1162
+			$this,
1163
+			$qty,
1164
+			$this->reserved(),
1165
+			$success
1166
+		);
1167
+		return $success;
1168
+	}
1169
+
1170
+
1171
+	/**
1172
+	 * Decreases the reserved count on the specified datetimes.
1173
+	 *
1174
+	 * @param int           $qty
1175
+	 * @param EE_Datetime[] $datetimes
1176
+	 * @throws EE_Error
1177
+	 * @throws InvalidArgumentException
1178
+	 * @throws ReflectionException
1179
+	 * @throws InvalidDataTypeException
1180
+	 * @throws InvalidInterfaceException
1181
+	 * @since 4.9.80.p
1182
+	 */
1183
+	protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1184
+	{
1185
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1186
+		foreach ($datetimes as $datetime) {
1187
+			if ($datetime instanceof EE_Datetime) {
1188
+				$datetime->decreaseReserved($qty);
1189
+			}
1190
+		}
1191
+	}
1192
+
1193
+
1194
+	/**
1195
+	 * Gets ticket quantity
1196
+	 *
1197
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1198
+	 *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1199
+	 *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1200
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1201
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1202
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1203
+	 * @return int
1204
+	 * @throws EE_Error
1205
+	 * @throws ReflectionException
1206
+	 */
1207
+	public function qty($context = '')
1208
+	{
1209
+		switch ($context) {
1210
+			case 'reg_limit':
1211
+				return $this->real_quantity_on_ticket();
1212
+			case 'saleable':
1213
+				return $this->real_quantity_on_ticket('saleable');
1214
+			default:
1215
+				return $this->get_raw('TKT_qty');
1216
+		}
1217
+	}
1218
+
1219
+
1220
+	/**
1221
+	 * Gets ticket quantity
1222
+	 *
1223
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1224
+	 *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1225
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1226
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1227
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1228
+	 * @param int    $DTT_ID      the primary key for a particular datetime.
1229
+	 *                            set to 0 for all related datetimes
1230
+	 * @return int|float          int for finite quantity or float for INF
1231
+	 * @throws EE_Error
1232
+	 * @throws ReflectionException
1233
+	 */
1234
+	public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1235
+	{
1236
+		$raw = $this->get_raw('TKT_qty');
1237
+		// return immediately if it's zero
1238
+		if ($raw === 0) {
1239
+			return $raw;
1240
+		}
1241
+		// echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1242
+		// ensure qty doesn't exceed raw value for THIS ticket
1243
+		$qty = min(EE_INF, $raw);
1244
+		// echo "\n . qty: " . $qty . '<br />';
1245
+		// calculate this ticket's total sales and reservations
1246
+		$sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1247
+		// echo "\n . sold: " . $this->sold() . '<br />';
1248
+		// echo "\n . reserved: " . $this->reserved() . '<br />';
1249
+		// echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1250
+		// first we need to calculate the maximum number of tickets available for the datetime
1251
+		// do we want data for one datetime or all of them ?
1252
+		$query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : [];
1253
+		$datetimes    = $this->get_many_related('Datetime', $query_params);
1254
+		if (is_array($datetimes) && ! empty($datetimes)) {
1255
+			foreach ($datetimes as $datetime) {
1256
+				if ($datetime instanceof EE_Datetime) {
1257
+					// $datetime->refresh_from_db();
1258
+					// echo "\n . . datetime name: " . $datetime->name() . '<br />';
1259
+					// echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1260
+					// initialize with no restrictions for each datetime
1261
+					// but adjust datetime qty based on datetime reg limit
1262
+					$datetime_qty = min(EE_INF, $datetime->reg_limit());
1263
+					// echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1264
+					// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1265
+					// if we want the actual saleable amount, then we need to consider OTHER ticket sales
1266
+					// and reservations for this datetime, that do NOT include sales and reservations
1267
+					// for this ticket (so we add $this->sold() and $this->reserved() back in)
1268
+					if ($context === 'saleable') {
1269
+						$datetime_qty = max(
1270
+							$datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1271
+							0
1272
+						);
1273
+						// echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1274
+						// echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1275
+						// echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1276
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1277
+						$datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1278
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1279
+					}
1280
+					$qty = min($datetime_qty, $qty);
1281
+					// echo "\n . . qty: " . $qty . '<br />';
1282
+				}
1283
+			}
1284
+		}
1285
+		// NOW that we know the  maximum number of tickets available for the datetime
1286
+		// we can finally factor in the details for this specific ticket
1287
+		if ($qty > 0 && $context === 'saleable') {
1288
+			// and subtract the sales for THIS ticket
1289
+			$qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1290
+			// echo "\n . qty: " . $qty . '<br />';
1291
+		}
1292
+		// echo "\nFINAL QTY: " . $qty . "<br /><br />";
1293
+		return $qty;
1294
+	}
1295
+
1296
+
1297
+	/**
1298
+	 * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1299
+	 *
1300
+	 * @param int $qty
1301
+	 * @return void
1302
+	 * @throws EE_Error
1303
+	 * @throws ReflectionException
1304
+	 */
1305
+	public function set_qty($qty)
1306
+	{
1307
+		$datetimes = $this->datetimes();
1308
+		foreach ($datetimes as $datetime) {
1309
+			if ($datetime instanceof EE_Datetime) {
1310
+				$qty = min($qty, $datetime->reg_limit());
1311
+			}
1312
+		}
1313
+		$this->set('TKT_qty', $qty);
1314
+	}
1315
+
1316
+
1317
+	/**
1318
+	 * Gets uses
1319
+	 *
1320
+	 * @return int
1321
+	 * @throws EE_Error
1322
+	 * @throws ReflectionException
1323
+	 */
1324
+	public function uses()
1325
+	{
1326
+		return $this->get('TKT_uses');
1327
+	}
1328
+
1329
+
1330
+	/**
1331
+	 * Sets uses
1332
+	 *
1333
+	 * @param int $uses
1334
+	 * @return void
1335
+	 * @throws EE_Error
1336
+	 * @throws ReflectionException
1337
+	 */
1338
+	public function set_uses($uses)
1339
+	{
1340
+		$this->set('TKT_uses', $uses);
1341
+	}
1342
+
1343
+
1344
+	/**
1345
+	 * returns whether ticket is required or not.
1346
+	 *
1347
+	 * @return boolean
1348
+	 * @throws EE_Error
1349
+	 * @throws ReflectionException
1350
+	 */
1351
+	public function required()
1352
+	{
1353
+		return $this->get('TKT_required');
1354
+	}
1355
+
1356
+
1357
+	/**
1358
+	 * sets the TKT_required property
1359
+	 *
1360
+	 * @param boolean $required
1361
+	 * @return void
1362
+	 * @throws EE_Error
1363
+	 * @throws ReflectionException
1364
+	 */
1365
+	public function set_required($required)
1366
+	{
1367
+		$this->set('TKT_required', $required);
1368
+	}
1369
+
1370
+
1371
+	/**
1372
+	 * Gets taxable
1373
+	 *
1374
+	 * @return boolean
1375
+	 * @throws EE_Error
1376
+	 * @throws ReflectionException
1377
+	 */
1378
+	public function taxable()
1379
+	{
1380
+		return $this->get('TKT_taxable');
1381
+	}
1382
+
1383
+
1384
+	/**
1385
+	 * Sets taxable
1386
+	 *
1387
+	 * @param boolean $taxable
1388
+	 * @return void
1389
+	 * @throws EE_Error
1390
+	 * @throws ReflectionException
1391
+	 */
1392
+	public function set_taxable($taxable)
1393
+	{
1394
+		$this->set('TKT_taxable', $taxable);
1395
+	}
1396
+
1397
+
1398
+	/**
1399
+	 * Gets is_default
1400
+	 *
1401
+	 * @return boolean
1402
+	 * @throws EE_Error
1403
+	 * @throws ReflectionException
1404
+	 */
1405
+	public function is_default()
1406
+	{
1407
+		return $this->get('TKT_is_default');
1408
+	}
1409
+
1410
+
1411
+	/**
1412
+	 * Sets is_default
1413
+	 *
1414
+	 * @param boolean $is_default
1415
+	 * @return void
1416
+	 * @throws EE_Error
1417
+	 * @throws ReflectionException
1418
+	 */
1419
+	public function set_is_default($is_default)
1420
+	{
1421
+		$this->set('TKT_is_default', $is_default);
1422
+	}
1423
+
1424
+
1425
+	/**
1426
+	 * Gets order
1427
+	 *
1428
+	 * @return int
1429
+	 * @throws EE_Error
1430
+	 * @throws ReflectionException
1431
+	 */
1432
+	public function order()
1433
+	{
1434
+		return $this->get('TKT_order');
1435
+	}
1436
+
1437
+
1438
+	/**
1439
+	 * Sets order
1440
+	 *
1441
+	 * @param int $order
1442
+	 * @return void
1443
+	 * @throws EE_Error
1444
+	 * @throws ReflectionException
1445
+	 */
1446
+	public function set_order($order)
1447
+	{
1448
+		$this->set('TKT_order', $order);
1449
+	}
1450
+
1451
+
1452
+	/**
1453
+	 * Gets row
1454
+	 *
1455
+	 * @return int
1456
+	 * @throws EE_Error
1457
+	 * @throws ReflectionException
1458
+	 */
1459
+	public function row()
1460
+	{
1461
+		return $this->get('TKT_row');
1462
+	}
1463
+
1464
+
1465
+	/**
1466
+	 * Sets row
1467
+	 *
1468
+	 * @param int $row
1469
+	 * @return void
1470
+	 * @throws EE_Error
1471
+	 * @throws ReflectionException
1472
+	 */
1473
+	public function set_row($row)
1474
+	{
1475
+		$this->set('TKT_row', $row);
1476
+	}
1477
+
1478
+
1479
+	/**
1480
+	 * Gets deleted
1481
+	 *
1482
+	 * @return boolean
1483
+	 * @throws EE_Error
1484
+	 * @throws ReflectionException
1485
+	 */
1486
+	public function deleted()
1487
+	{
1488
+		return $this->get('TKT_deleted');
1489
+	}
1490
+
1491
+
1492
+	/**
1493
+	 * Sets deleted
1494
+	 *
1495
+	 * @param boolean $deleted
1496
+	 * @return void
1497
+	 * @throws EE_Error
1498
+	 * @throws ReflectionException
1499
+	 */
1500
+	public function set_deleted($deleted)
1501
+	{
1502
+		$this->set('TKT_deleted', $deleted);
1503
+	}
1504
+
1505
+
1506
+	/**
1507
+	 * Gets parent
1508
+	 *
1509
+	 * @return int
1510
+	 * @throws EE_Error
1511
+	 * @throws ReflectionException
1512
+	 */
1513
+	public function parent_ID()
1514
+	{
1515
+		return $this->get('TKT_parent');
1516
+	}
1517
+
1518
+
1519
+	/**
1520
+	 * Sets parent
1521
+	 *
1522
+	 * @param int $parent
1523
+	 * @return void
1524
+	 * @throws EE_Error
1525
+	 * @throws ReflectionException
1526
+	 */
1527
+	public function set_parent_ID($parent)
1528
+	{
1529
+		$this->set('TKT_parent', $parent);
1530
+	}
1531
+
1532
+
1533
+	/**
1534
+	 * @return boolean
1535
+	 * @throws EE_Error
1536
+	 * @throws InvalidArgumentException
1537
+	 * @throws InvalidDataTypeException
1538
+	 * @throws InvalidInterfaceException
1539
+	 * @throws ReflectionException
1540
+	 */
1541
+	public function reverse_calculate()
1542
+	{
1543
+		return $this->get('TKT_reverse_calculate');
1544
+	}
1545
+
1546
+
1547
+	/**
1548
+	 * @param boolean $reverse_calculate
1549
+	 * @throws EE_Error
1550
+	 * @throws InvalidArgumentException
1551
+	 * @throws InvalidDataTypeException
1552
+	 * @throws InvalidInterfaceException
1553
+	 * @throws ReflectionException
1554
+	 */
1555
+	public function set_reverse_calculate($reverse_calculate)
1556
+	{
1557
+		$this->set('TKT_reverse_calculate', $reverse_calculate);
1558
+	}
1559
+
1560
+
1561
+	/**
1562
+	 * Gets a string which is handy for showing in gateways etc that describes the ticket.
1563
+	 *
1564
+	 * @return string
1565
+	 * @throws EE_Error
1566
+	 * @throws ReflectionException
1567
+	 */
1568
+	public function name_and_info()
1569
+	{
1570
+		$times = [];
1571
+		foreach ($this->datetimes() as $datetime) {
1572
+			$times[] = $datetime->start_date_and_time();
1573
+		}
1574
+		/* translators: %1$s ticket name, %2$s start datetimes separated by comma, %3$s ticket price */
1575
+		return sprintf(
1576
+			esc_html__('%1$s @ %2$s for %3$s', 'event_espresso'),
1577
+			$this->name(),
1578
+			implode(', ', $times),
1579
+			$this->pretty_price()
1580
+		);
1581
+	}
1582
+
1583
+
1584
+	/**
1585
+	 * Gets name
1586
+	 *
1587
+	 * @return string
1588
+	 * @throws EE_Error
1589
+	 * @throws ReflectionException
1590
+	 */
1591
+	public function name()
1592
+	{
1593
+		return $this->get('TKT_name');
1594
+	}
1595
+
1596
+
1597
+	/**
1598
+	 * Gets price
1599
+	 *
1600
+	 * @return float
1601
+	 * @throws EE_Error
1602
+	 * @throws ReflectionException
1603
+	 */
1604
+	public function price()
1605
+	{
1606
+		return $this->get('TKT_price');
1607
+	}
1608
+
1609
+
1610
+	/**
1611
+	 * Gets all the registrations for this ticket
1612
+	 *
1613
+	 * @param array $query_params
1614
+	 * @return EE_Registration[]|EE_Base_Class[]
1615
+	 * @throws EE_Error
1616
+	 * @throws ReflectionException
1617
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1618
+	 */
1619
+	public function registrations($query_params = [])
1620
+	{
1621
+		return $this->get_many_related('Registration', $query_params);
1622
+	}
1623
+
1624
+
1625
+	/**
1626
+	 * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1627
+	 *
1628
+	 * @return int
1629
+	 * @throws EE_Error
1630
+	 * @throws ReflectionException
1631
+	 */
1632
+	public function update_tickets_sold()
1633
+	{
1634
+		$count_regs_for_this_ticket = $this->count_registrations(
1635
+			[
1636
+				[
1637
+					'STS_ID'      => RegStatus::APPROVED,
1638
+					'REG_deleted' => 0,
1639
+				],
1640
+			]
1641
+		);
1642
+		$this->set_sold($count_regs_for_this_ticket);
1643
+		$this->save();
1644
+		return $count_regs_for_this_ticket;
1645
+	}
1646
+
1647
+
1648
+	/**
1649
+	 * Counts the registrations for this ticket
1650
+	 *
1651
+	 * @param array $query_params
1652
+	 * @return int
1653
+	 * @throws EE_Error
1654
+	 * @throws ReflectionException
1655
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1656
+	 */
1657
+	public function count_registrations($query_params = [])
1658
+	{
1659
+		return $this->count_related('Registration', $query_params);
1660
+	}
1661
+
1662
+
1663
+	/**
1664
+	 * Implementation for EEI_Has_Icon interface method.
1665
+	 *
1666
+	 * @return string
1667
+	 * @see EEI_Visual_Representation for comments
1668
+	 */
1669
+	public function get_icon()
1670
+	{
1671
+		return '<span class="dashicons dashicons-tickets-alt"></span>';
1672
+	}
1673
+
1674
+
1675
+	/**
1676
+	 * Implementation of the EEI_Event_Relation interface method
1677
+	 *
1678
+	 * @return EE_Event
1679
+	 * @throws EE_Error
1680
+	 * @throws UnexpectedEntityException
1681
+	 * @throws ReflectionException
1682
+	 * @see EEI_Event_Relation for comments
1683
+	 */
1684
+	public function get_related_event()
1685
+	{
1686
+		// get one datetime to use for getting the event
1687
+		$datetime = $this->first_datetime();
1688
+		if (! $datetime instanceof EE_Datetime) {
1689
+			throw new UnexpectedEntityException(
1690
+				$datetime,
1691
+				'EE_Datetime',
1692
+				sprintf(
1693
+					esc_html__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1694
+					$this->name()
1695
+				)
1696
+			);
1697
+		}
1698
+		$event = $datetime->event();
1699
+		if (! $event instanceof EE_Event) {
1700
+			throw new UnexpectedEntityException(
1701
+				$event,
1702
+				'EE_Event',
1703
+				sprintf(
1704
+					esc_html__('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1705
+					$this->name()
1706
+				)
1707
+			);
1708
+		}
1709
+		return $event;
1710
+	}
1711
+
1712
+
1713
+	/**
1714
+	 * Implementation of the EEI_Event_Relation interface method
1715
+	 *
1716
+	 * @return string
1717
+	 * @throws UnexpectedEntityException
1718
+	 * @throws EE_Error
1719
+	 * @throws ReflectionException
1720
+	 * @see EEI_Event_Relation for comments
1721
+	 */
1722
+	public function get_event_name()
1723
+	{
1724
+		$event = $this->get_related_event();
1725
+		return $event instanceof EE_Event ? $event->name() : '';
1726
+	}
1727
+
1728
+
1729
+	/**
1730
+	 * Implementation of the EEI_Event_Relation interface method
1731
+	 *
1732
+	 * @return int
1733
+	 * @throws UnexpectedEntityException
1734
+	 * @throws EE_Error
1735
+	 * @throws ReflectionException
1736
+	 * @see EEI_Event_Relation for comments
1737
+	 */
1738
+	public function get_event_ID()
1739
+	{
1740
+		$event = $this->get_related_event();
1741
+		return $event instanceof EE_Event ? $event->ID() : 0;
1742
+	}
1743
+
1744
+
1745
+	/**
1746
+	 * This simply returns whether a ticket can be permanently deleted or not.
1747
+	 * The criteria for determining this is whether the ticket has any related registrations.
1748
+	 * If there are none then it can be permanently deleted.
1749
+	 *
1750
+	 * @return bool
1751
+	 * @throws EE_Error
1752
+	 * @throws ReflectionException
1753
+	 */
1754
+	public function is_permanently_deleteable()
1755
+	{
1756
+		return $this->count_registrations() === 0;
1757
+	}
1758
+
1759
+
1760
+	/**
1761
+	 * @return int
1762
+	 * @throws EE_Error
1763
+	 * @throws ReflectionException
1764
+	 * @since   5.0.0.p
1765
+	 */
1766
+	public function visibility(): int
1767
+	{
1768
+		return $this->get('TKT_visibility');
1769
+	}
1770
+
1771
+
1772
+	/**
1773
+	 * @return bool
1774
+	 * @throws EE_Error
1775
+	 * @throws ReflectionException
1776
+	 * @since   5.0.0.p
1777
+	 */
1778
+	public function isHidden(): bool
1779
+	{
1780
+		return $this->visibility() === EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1781
+	}
1782
+
1783
+
1784
+	/**
1785
+	 * @return bool
1786
+	 * @throws EE_Error
1787
+	 * @throws ReflectionException
1788
+	 * @since   5.0.0.p
1789
+	 */
1790
+	public function isNotHidden(): bool
1791
+	{
1792
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1793
+	}
1794
+
1795
+
1796
+	/**
1797
+	 * @return bool
1798
+	 * @throws EE_Error
1799
+	 * @throws ReflectionException
1800
+	 * @since   5.0.0.p
1801
+	 */
1802
+	public function isPublicOnly(): bool
1803
+	{
1804
+		return $this->isNotHidden() && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE;
1805
+	}
1806
+
1807
+
1808
+	/**
1809
+	 * @return bool
1810
+	 * @throws EE_Error
1811
+	 * @throws ReflectionException
1812
+	 * @since   5.0.0.p
1813
+	 */
1814
+	public function isMembersOnly(): bool
1815
+	{
1816
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE
1817
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE;
1818
+	}
1819
+
1820
+
1821
+	/**
1822
+	 * @return bool
1823
+	 * @throws EE_Error
1824
+	 * @throws ReflectionException
1825
+	 * @since   5.0.0.p
1826
+	 */
1827
+	public function isAdminsOnly(): bool
1828
+	{
1829
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE
1830
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE;
1831
+	}
1832
+
1833
+
1834
+	/**
1835
+	 * @return bool
1836
+	 * @throws EE_Error
1837
+	 * @throws ReflectionException
1838
+	 * @since   5.0.0.p
1839
+	 */
1840
+	public function isAdminUiOnly(): bool
1841
+	{
1842
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE
1843
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE;
1844
+	}
1845
+
1846
+
1847
+	/**
1848
+	 * @param int $visibility
1849
+	 * @throws EE_Error
1850
+	 * @throws ReflectionException
1851
+	 * @since   5.0.0.p
1852
+	 */
1853
+	public function set_visibility(int $visibility)
1854
+	{
1855
+		$ticket_visibility_options = $this->_model->ticketVisibilityOptions();
1856
+		$ticket_visibility         = -1;
1857
+		foreach ($ticket_visibility_options as $ticket_visibility_option) {
1858
+			if ($visibility === $ticket_visibility_option) {
1859
+				$ticket_visibility = $visibility;
1860
+			}
1861
+		}
1862
+		if ($ticket_visibility === -1) {
1863
+			throw new DomainException(
1864
+				sprintf(
1865
+					esc_html__(
1866
+						'The supplied ticket visibility setting of "%1$s" is not valid. It needs to match one of the keys in the following array:%2$s %3$s ',
1867
+						'event_espresso'
1868
+					),
1869
+					$visibility,
1870
+					'<br />',
1871
+					var_export($ticket_visibility_options, true)
1872
+				)
1873
+			);
1874
+		}
1875
+		$this->set('TKT_visibility', $ticket_visibility);
1876
+	}
1877
+
1878
+
1879
+	/**
1880
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1881
+	 * @param string                   $relationName
1882
+	 * @param array                    $extra_join_model_fields_n_values
1883
+	 * @param string|null              $cache_id
1884
+	 * @return EE_Base_Class
1885
+	 * @throws EE_Error
1886
+	 * @throws ReflectionException
1887
+	 * @since   5.0.0.p
1888
+	 */
1889
+	public function _add_relation_to(
1890
+		$otherObjectModelObjectOrID,
1891
+		$relationName,
1892
+		$extra_join_model_fields_n_values = [],
1893
+		$cache_id = null
1894
+	) {
1895
+		if ($relationName === 'Datetime' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1896
+			/** @var EE_Datetime $datetime */
1897
+			$datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1898
+			$datetime->increaseSold($this->sold(), false);
1899
+			$datetime->increaseReserved($this->reserved());
1900
+			$datetime->save();
1901
+			$otherObjectModelObjectOrID = $datetime;
1902
+		}
1903
+		return parent::_add_relation_to(
1904
+			$otherObjectModelObjectOrID,
1905
+			$relationName,
1906
+			$extra_join_model_fields_n_values,
1907
+			$cache_id
1908
+		);
1909
+	}
1910
+
1911
+
1912
+	/**
1913
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1914
+	 * @param string                   $relationName
1915
+	 * @param array                    $where_query
1916
+	 * @return bool|EE_Base_Class|null
1917
+	 * @throws EE_Error
1918
+	 * @throws ReflectionException
1919
+	 * @since   5.0.0.p
1920
+	 */
1921
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1922
+	{
1923
+		// if we're adding a new relation to a datetime
1924
+		if ($relationName === 'Datetime' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1925
+			/** @var EE_Datetime $datetime */
1926
+			$datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1927
+			$datetime->decreaseSold($this->sold());
1928
+			$datetime->decreaseReserved($this->reserved());
1929
+			$datetime->save();
1930
+			$otherObjectModelObjectOrID = $datetime;
1931
+		}
1932
+		return parent::_remove_relation_to(
1933
+			$otherObjectModelObjectOrID,
1934
+			$relationName,
1935
+			$where_query
1936
+		);
1937
+	}
1938
+
1939
+
1940
+	/**
1941
+	 * Removes ALL the related things for the $relationName.
1942
+	 *
1943
+	 * @param string $relationName
1944
+	 * @param array  $where_query_params
1945
+	 * @return EE_Base_Class
1946
+	 * @throws ReflectionException
1947
+	 * @throws InvalidArgumentException
1948
+	 * @throws InvalidInterfaceException
1949
+	 * @throws InvalidDataTypeException
1950
+	 * @throws EE_Error
1951
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1952
+	 */
1953
+	public function _remove_relations($relationName, $where_query_params = [])
1954
+	{
1955
+		if ($relationName === 'Datetime') {
1956
+			$datetimes = $this->datetimes();
1957
+			foreach ($datetimes as $datetime) {
1958
+				$datetime->decreaseSold($this->sold());
1959
+				$datetime->decreaseReserved($this->reserved());
1960
+				$datetime->save();
1961
+			}
1962
+		}
1963
+		return parent::_remove_relations($relationName, $where_query_params);
1964
+	}
1965
+
1966
+
1967
+	/*******************************************************************
1968 1968
      ***********************  DEPRECATED METHODS  **********************
1969 1969
      *******************************************************************/
1970 1970
 
1971 1971
 
1972
-    /**
1973
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1974
-     * associated datetimes.
1975
-     *
1976
-     * @param int $qty
1977
-     * @return void
1978
-     * @throws EE_Error
1979
-     * @throws InvalidArgumentException
1980
-     * @throws InvalidDataTypeException
1981
-     * @throws InvalidInterfaceException
1982
-     * @throws ReflectionException
1983
-     * @deprecated 4.9.80.p
1984
-     */
1985
-    public function increase_sold($qty = 1)
1986
-    {
1987
-        EE_Error::doing_it_wrong(
1988
-            __FUNCTION__,
1989
-            esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1990
-            '4.9.80.p',
1991
-            '5.0.0.p'
1992
-        );
1993
-        $this->increaseSold($qty);
1994
-    }
1995
-
1996
-
1997
-    /**
1998
-     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1999
-     *
2000
-     * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
2001
-     *                 Negative means to decreases old counts (and increase reserved counts).
2002
-     * @throws EE_Error
2003
-     * @throws InvalidArgumentException
2004
-     * @throws InvalidDataTypeException
2005
-     * @throws InvalidInterfaceException
2006
-     * @throws ReflectionException
2007
-     * @deprecated 4.9.80.p
2008
-     */
2009
-    protected function _increase_sold_for_datetimes($qty)
2010
-    {
2011
-        EE_Error::doing_it_wrong(
2012
-            __FUNCTION__,
2013
-            esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
2014
-            '4.9.80.p',
2015
-            '5.0.0.p'
2016
-        );
2017
-        $this->increaseSoldForDatetimes($qty);
2018
-    }
2019
-
2020
-
2021
-    /**
2022
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
2023
-     * DB and then updates the model objects.
2024
-     * Does not affect the reserved counts.
2025
-     *
2026
-     * @param int $qty
2027
-     * @return void
2028
-     * @throws EE_Error
2029
-     * @throws InvalidArgumentException
2030
-     * @throws InvalidDataTypeException
2031
-     * @throws InvalidInterfaceException
2032
-     * @throws ReflectionException
2033
-     * @deprecated 4.9.80.p
2034
-     */
2035
-    public function decrease_sold($qty = 1)
2036
-    {
2037
-        EE_Error::doing_it_wrong(
2038
-            __FUNCTION__,
2039
-            esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
2040
-            '4.9.80.p',
2041
-            '5.0.0.p'
2042
-        );
2043
-        $this->decreaseSold($qty);
2044
-    }
2045
-
2046
-
2047
-    /**
2048
-     * Decreases sold on related datetimes
2049
-     *
2050
-     * @param int $qty
2051
-     * @return void
2052
-     * @throws EE_Error
2053
-     * @throws InvalidArgumentException
2054
-     * @throws InvalidDataTypeException
2055
-     * @throws InvalidInterfaceException
2056
-     * @throws ReflectionException
2057
-     * @deprecated 4.9.80.p
2058
-     */
2059
-    protected function _decrease_sold_for_datetimes($qty = 1)
2060
-    {
2061
-        EE_Error::doing_it_wrong(
2062
-            __FUNCTION__,
2063
-            esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
2064
-            '4.9.80.p',
2065
-            '5.0.0.p'
2066
-        );
2067
-        $this->decreaseSoldForDatetimes($qty);
2068
-    }
2069
-
2070
-
2071
-    /**
2072
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
2073
-     *
2074
-     * @param int    $qty
2075
-     * @param string $source
2076
-     * @return bool whether we successfully reserved the ticket or not.
2077
-     * @throws EE_Error
2078
-     * @throws InvalidArgumentException
2079
-     * @throws ReflectionException
2080
-     * @throws InvalidDataTypeException
2081
-     * @throws InvalidInterfaceException
2082
-     * @deprecated 4.9.80.p
2083
-     */
2084
-    public function increase_reserved($qty = 1, $source = 'unknown')
2085
-    {
2086
-        EE_Error::doing_it_wrong(
2087
-            __FUNCTION__,
2088
-            esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
2089
-            '4.9.80.p',
2090
-            '5.0.0.p'
2091
-        );
2092
-        return $this->increaseReserved($qty);
2093
-    }
2094
-
2095
-
2096
-    /**
2097
-     * Increases sold on related datetimes
2098
-     *
2099
-     * @param int $qty
2100
-     * @return boolean indicating success
2101
-     * @throws EE_Error
2102
-     * @throws InvalidArgumentException
2103
-     * @throws InvalidDataTypeException
2104
-     * @throws InvalidInterfaceException
2105
-     * @throws ReflectionException
2106
-     * @deprecated 4.9.80.p
2107
-     */
2108
-    protected function _increase_reserved_for_datetimes($qty = 1)
2109
-    {
2110
-        EE_Error::doing_it_wrong(
2111
-            __FUNCTION__,
2112
-            esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
2113
-            '4.9.80.p',
2114
-            '5.0.0.p'
2115
-        );
2116
-        return $this->increaseReservedForDatetimes($qty);
2117
-    }
2118
-
2119
-
2120
-    /**
2121
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
2122
-     *
2123
-     * @param int    $qty
2124
-     * @param bool   $adjust_datetimes
2125
-     * @param string $source
2126
-     * @return void
2127
-     * @throws EE_Error
2128
-     * @throws InvalidArgumentException
2129
-     * @throws ReflectionException
2130
-     * @throws InvalidDataTypeException
2131
-     * @throws InvalidInterfaceException
2132
-     * @deprecated 4.9.80.p
2133
-     */
2134
-    public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
2135
-    {
2136
-        EE_Error::doing_it_wrong(
2137
-            __FUNCTION__,
2138
-            esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
2139
-            '4.9.80.p',
2140
-            '5.0.0.p'
2141
-        );
2142
-        $this->decreaseReserved($qty);
2143
-    }
2144
-
2145
-
2146
-    /**
2147
-     * Decreases reserved on related datetimes
2148
-     *
2149
-     * @param int $qty
2150
-     * @return void
2151
-     * @throws EE_Error
2152
-     * @throws InvalidArgumentException
2153
-     * @throws ReflectionException
2154
-     * @throws InvalidDataTypeException
2155
-     * @throws InvalidInterfaceException
2156
-     * @deprecated 4.9.80.p
2157
-     */
2158
-    protected function _decrease_reserved_for_datetimes($qty = 1)
2159
-    {
2160
-        EE_Error::doing_it_wrong(
2161
-            __FUNCTION__,
2162
-            esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
2163
-            '4.9.80.p',
2164
-            '5.0.0.p'
2165
-        );
2166
-        $this->decreaseReservedForDatetimes($qty);
2167
-    }
1972
+	/**
1973
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1974
+	 * associated datetimes.
1975
+	 *
1976
+	 * @param int $qty
1977
+	 * @return void
1978
+	 * @throws EE_Error
1979
+	 * @throws InvalidArgumentException
1980
+	 * @throws InvalidDataTypeException
1981
+	 * @throws InvalidInterfaceException
1982
+	 * @throws ReflectionException
1983
+	 * @deprecated 4.9.80.p
1984
+	 */
1985
+	public function increase_sold($qty = 1)
1986
+	{
1987
+		EE_Error::doing_it_wrong(
1988
+			__FUNCTION__,
1989
+			esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1990
+			'4.9.80.p',
1991
+			'5.0.0.p'
1992
+		);
1993
+		$this->increaseSold($qty);
1994
+	}
1995
+
1996
+
1997
+	/**
1998
+	 * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1999
+	 *
2000
+	 * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
2001
+	 *                 Negative means to decreases old counts (and increase reserved counts).
2002
+	 * @throws EE_Error
2003
+	 * @throws InvalidArgumentException
2004
+	 * @throws InvalidDataTypeException
2005
+	 * @throws InvalidInterfaceException
2006
+	 * @throws ReflectionException
2007
+	 * @deprecated 4.9.80.p
2008
+	 */
2009
+	protected function _increase_sold_for_datetimes($qty)
2010
+	{
2011
+		EE_Error::doing_it_wrong(
2012
+			__FUNCTION__,
2013
+			esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
2014
+			'4.9.80.p',
2015
+			'5.0.0.p'
2016
+		);
2017
+		$this->increaseSoldForDatetimes($qty);
2018
+	}
2019
+
2020
+
2021
+	/**
2022
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
2023
+	 * DB and then updates the model objects.
2024
+	 * Does not affect the reserved counts.
2025
+	 *
2026
+	 * @param int $qty
2027
+	 * @return void
2028
+	 * @throws EE_Error
2029
+	 * @throws InvalidArgumentException
2030
+	 * @throws InvalidDataTypeException
2031
+	 * @throws InvalidInterfaceException
2032
+	 * @throws ReflectionException
2033
+	 * @deprecated 4.9.80.p
2034
+	 */
2035
+	public function decrease_sold($qty = 1)
2036
+	{
2037
+		EE_Error::doing_it_wrong(
2038
+			__FUNCTION__,
2039
+			esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
2040
+			'4.9.80.p',
2041
+			'5.0.0.p'
2042
+		);
2043
+		$this->decreaseSold($qty);
2044
+	}
2045
+
2046
+
2047
+	/**
2048
+	 * Decreases sold on related datetimes
2049
+	 *
2050
+	 * @param int $qty
2051
+	 * @return void
2052
+	 * @throws EE_Error
2053
+	 * @throws InvalidArgumentException
2054
+	 * @throws InvalidDataTypeException
2055
+	 * @throws InvalidInterfaceException
2056
+	 * @throws ReflectionException
2057
+	 * @deprecated 4.9.80.p
2058
+	 */
2059
+	protected function _decrease_sold_for_datetimes($qty = 1)
2060
+	{
2061
+		EE_Error::doing_it_wrong(
2062
+			__FUNCTION__,
2063
+			esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
2064
+			'4.9.80.p',
2065
+			'5.0.0.p'
2066
+		);
2067
+		$this->decreaseSoldForDatetimes($qty);
2068
+	}
2069
+
2070
+
2071
+	/**
2072
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
2073
+	 *
2074
+	 * @param int    $qty
2075
+	 * @param string $source
2076
+	 * @return bool whether we successfully reserved the ticket or not.
2077
+	 * @throws EE_Error
2078
+	 * @throws InvalidArgumentException
2079
+	 * @throws ReflectionException
2080
+	 * @throws InvalidDataTypeException
2081
+	 * @throws InvalidInterfaceException
2082
+	 * @deprecated 4.9.80.p
2083
+	 */
2084
+	public function increase_reserved($qty = 1, $source = 'unknown')
2085
+	{
2086
+		EE_Error::doing_it_wrong(
2087
+			__FUNCTION__,
2088
+			esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
2089
+			'4.9.80.p',
2090
+			'5.0.0.p'
2091
+		);
2092
+		return $this->increaseReserved($qty);
2093
+	}
2094
+
2095
+
2096
+	/**
2097
+	 * Increases sold on related datetimes
2098
+	 *
2099
+	 * @param int $qty
2100
+	 * @return boolean indicating success
2101
+	 * @throws EE_Error
2102
+	 * @throws InvalidArgumentException
2103
+	 * @throws InvalidDataTypeException
2104
+	 * @throws InvalidInterfaceException
2105
+	 * @throws ReflectionException
2106
+	 * @deprecated 4.9.80.p
2107
+	 */
2108
+	protected function _increase_reserved_for_datetimes($qty = 1)
2109
+	{
2110
+		EE_Error::doing_it_wrong(
2111
+			__FUNCTION__,
2112
+			esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
2113
+			'4.9.80.p',
2114
+			'5.0.0.p'
2115
+		);
2116
+		return $this->increaseReservedForDatetimes($qty);
2117
+	}
2118
+
2119
+
2120
+	/**
2121
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
2122
+	 *
2123
+	 * @param int    $qty
2124
+	 * @param bool   $adjust_datetimes
2125
+	 * @param string $source
2126
+	 * @return void
2127
+	 * @throws EE_Error
2128
+	 * @throws InvalidArgumentException
2129
+	 * @throws ReflectionException
2130
+	 * @throws InvalidDataTypeException
2131
+	 * @throws InvalidInterfaceException
2132
+	 * @deprecated 4.9.80.p
2133
+	 */
2134
+	public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
2135
+	{
2136
+		EE_Error::doing_it_wrong(
2137
+			__FUNCTION__,
2138
+			esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
2139
+			'4.9.80.p',
2140
+			'5.0.0.p'
2141
+		);
2142
+		$this->decreaseReserved($qty);
2143
+	}
2144
+
2145
+
2146
+	/**
2147
+	 * Decreases reserved on related datetimes
2148
+	 *
2149
+	 * @param int $qty
2150
+	 * @return void
2151
+	 * @throws EE_Error
2152
+	 * @throws InvalidArgumentException
2153
+	 * @throws ReflectionException
2154
+	 * @throws InvalidDataTypeException
2155
+	 * @throws InvalidInterfaceException
2156
+	 * @deprecated 4.9.80.p
2157
+	 */
2158
+	protected function _decrease_reserved_for_datetimes($qty = 1)
2159
+	{
2160
+		EE_Error::doing_it_wrong(
2161
+			__FUNCTION__,
2162
+			esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
2163
+			'4.9.80.p',
2164
+			'5.0.0.p'
2165
+		);
2166
+		$this->decreaseReservedForDatetimes($qty);
2167
+	}
2168 2168
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Price.class.php 1 patch
Indentation   +369 added lines, -369 removed lines patch added patch discarded remove patch
@@ -14,373 +14,373 @@
 block discarded – undo
14 14
  */
15 15
 class EE_Price extends EE_Soft_Delete_Base_Class
16 16
 {
17
-    /**
18
-     * @param array  $props_n_values          incoming values
19
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
20
-     *                                        used.)
21
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
22
-     *                                        date_format and the second value is the time format
23
-     * @return EE_Price
24
-     * @throws EE_Error
25
-     * @throws ReflectionException
26
-     */
27
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
28
-    {
29
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
30
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
31
-    }
32
-
33
-
34
-    /**
35
-     * @param array  $props_n_values  incoming values from the database
36
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
37
-     *                                the website will be used.
38
-     * @return EE_Price
39
-     * @throws EE_Error
40
-     * @throws ReflectionException
41
-     */
42
-    public static function new_instance_from_db($props_n_values = [], $timezone = '')
43
-    {
44
-        return new self($props_n_values, true, $timezone);
45
-    }
46
-
47
-
48
-    /**
49
-     * Set Price type ID
50
-     *
51
-     * @param int $PRT_ID
52
-     * @throws EE_Error
53
-     * @throws ReflectionException
54
-     */
55
-    public function set_type($PRT_ID = 0)
56
-    {
57
-        $this->set('PRT_ID', $PRT_ID);
58
-    }
59
-
60
-
61
-    /**
62
-     * Set Price Amount
63
-     *
64
-     * @param float $PRC_amount
65
-     * @throws EE_Error
66
-     * @throws ReflectionException
67
-     */
68
-    public function set_amount($PRC_amount = 0.00)
69
-    {
70
-        $this->set('PRC_amount', $PRC_amount);
71
-    }
72
-
73
-
74
-    /**
75
-     * Set Price Name
76
-     *
77
-     * @param string $PRC_name
78
-     * @throws EE_Error
79
-     * @throws ReflectionException
80
-     */
81
-    public function set_name($PRC_name = '')
82
-    {
83
-        $this->set('PRC_name', $PRC_name);
84
-    }
85
-
86
-
87
-    /**
88
-     * Set Price Description
89
-     *
90
-     * @param string $PRC_desc
91
-     * @throws EE_Error
92
-     * @throws ReflectionException
93
-     */
94
-    public function set_description($PRC_desc = '')
95
-    {
96
-        $this->Set('PRC_desc', $PRC_desc);
97
-    }
98
-
99
-
100
-    /**
101
-     * set is_default
102
-     *
103
-     * @param bool $PRC_is_default
104
-     * @throws EE_Error
105
-     * @throws ReflectionException
106
-     */
107
-    public function set_is_default($PRC_is_default = false)
108
-    {
109
-        $this->set('PRC_is_default', $PRC_is_default);
110
-    }
111
-
112
-
113
-    /**
114
-     * set deleted
115
-     *
116
-     * @param bool $PRC_deleted
117
-     * @throws EE_Error
118
-     * @throws ReflectionException
119
-     */
120
-    public function set_deleted($PRC_deleted = null)
121
-    {
122
-        $this->set('PRC_deleted', $PRC_deleted);
123
-    }
124
-
125
-
126
-    /**
127
-     * get Price type
128
-     *
129
-     * @return int
130
-     * @throws EE_Error
131
-     * @throws ReflectionException
132
-     */
133
-    public function type(): int
134
-    {
135
-        return (int) $this->get('PRT_ID');
136
-    }
137
-
138
-
139
-    /**
140
-     * get Price Amount
141
-     *
142
-     * @return float
143
-     * @throws EE_Error
144
-     * @throws ReflectionException
145
-     */
146
-    public function amount(): float
147
-    {
148
-        return (float) $this->get('PRC_amount');
149
-    }
150
-
151
-
152
-    /**
153
-     * get Price Name
154
-     *
155
-     * @return        string
156
-     * @throws EE_Error
157
-     * @throws ReflectionException
158
-     */
159
-    public function name()
160
-    {
161
-        return $this->get('PRC_name');
162
-    }
163
-
164
-
165
-    /**
166
-     * get Price description
167
-     *
168
-     * @return        string
169
-     * @throws EE_Error
170
-     * @throws ReflectionException
171
-     */
172
-    public function desc()
173
-    {
174
-        return $this->get('PRC_desc');
175
-    }
176
-
177
-
178
-    /**
179
-     * get overrides
180
-     *
181
-     * @return        int
182
-     * @throws EE_Error
183
-     * @throws ReflectionException
184
-     */
185
-    public function overrides()
186
-    {
187
-        return $this->get('PRC_overrides');
188
-    }
189
-
190
-
191
-    /**
192
-     * get order
193
-     *
194
-     * @return int
195
-     * @throws EE_Error
196
-     * @throws ReflectionException
197
-     */
198
-    public function order()
199
-    {
200
-        return $this->get('PRC_order');
201
-    }
202
-
203
-
204
-    /**
205
-     * get the author of the price
206
-     *
207
-     * @return int
208
-     * @throws EE_Error
209
-     * @throws ReflectionException
210
-     * @since 4.5.0
211
-     */
212
-    public function wp_user()
213
-    {
214
-        return $this->get('PRC_wp_user');
215
-    }
216
-
217
-
218
-    /**
219
-     * get is_default
220
-     *
221
-     * @return bool
222
-     * @throws EE_Error
223
-     * @throws ReflectionException
224
-     */
225
-    public function is_default()
226
-    {
227
-        return $this->get('PRC_is_default');
228
-    }
229
-
230
-
231
-    /**
232
-     * get deleted
233
-     *
234
-     * @return bool
235
-     * @throws EE_Error
236
-     * @throws ReflectionException
237
-     */
238
-    public function deleted()
239
-    {
240
-        return $this->get('PRC_deleted');
241
-    }
242
-
243
-
244
-    /**
245
-     * @return bool
246
-     * @throws EE_Error
247
-     * @throws ReflectionException
248
-     */
249
-    public function parent()
250
-    {
251
-        return $this->get('PRC_parent');
252
-    }
253
-
254
-
255
-    // some helper methods for getting info on the price_type for this price
256
-
257
-    /**
258
-     * @return EE_Price_Type|null
259
-     * @throws EE_Error
260
-     * @throws ReflectionException
261
-     */
262
-    public function type_obj(): ?EE_Price_Type
263
-    {
264
-        return $this->get_first_related('Price_Type');
265
-    }
266
-
267
-
268
-    /**
269
-     * @return int
270
-     * @throws EE_Error
271
-     * @throws ReflectionException
272
-     */
273
-    public function type_order(): int
274
-    {
275
-        $price_type = $this->type_obj();
276
-        return $price_type instanceof EE_Price_Type ? $price_type->order() : 0;
277
-    }
278
-
279
-
280
-    /**
281
-     * return whether the price is a base price or not
282
-     *
283
-     * @return bool
284
-     * @throws EE_Error
285
-     * @throws InvalidArgumentException
286
-     * @throws InvalidDataTypeException
287
-     * @throws InvalidInterfaceException
288
-     * @throws ReflectionException
289
-     */
290
-    public function is_base_price(): bool
291
-    {
292
-        $price_type = $this->type_obj();
293
-        return $price_type instanceof EE_Price_Type && $price_type->is_base_price();
294
-    }
295
-
296
-
297
-    /**
298
-     * Simply indicates whether this price increases or decreases the total
299
-     *
300
-     * @return bool true = discount, otherwise adds to the total
301
-     * @throws EE_Error
302
-     * @throws ReflectionException
303
-     */
304
-    public function is_discount(): bool
305
-    {
306
-        $price_type = $this->type_obj();
307
-        return $price_type instanceof EE_Price_Type && $price_type->is_discount();
308
-    }
309
-
310
-
311
-    /**
312
-     * whether the price is a percentage or not
313
-     *
314
-     * @return bool
315
-     * @throws EE_Error
316
-     * @throws InvalidArgumentException
317
-     * @throws InvalidDataTypeException
318
-     * @throws InvalidInterfaceException
319
-     * @throws ReflectionException
320
-     */
321
-    public function is_percent(): bool
322
-    {
323
-        $price_type = $this->type_obj();
324
-        return $price_type instanceof EE_Price_Type && $price_type->is_percent();
325
-    }
326
-
327
-
328
-    /**
329
-     * whether the price is a percentage or not
330
-     *
331
-     * @return bool
332
-     * @throws EE_Error
333
-     * @throws ReflectionException
334
-     */
335
-    public function is_surcharge(): bool
336
-    {
337
-        $price_type = $this->type_obj();
338
-        return $price_type instanceof EE_Price_Type && $price_type->is_surcharge();
339
-    }
340
-
341
-
342
-
343
-    /**
344
-     * whether the price is a percentage or not
345
-     *
346
-     * @return bool
347
-     * @throws EE_Error
348
-     * @throws ReflectionException
349
-     */
350
-    public function is_tax(): bool
351
-    {
352
-        $price_type = $this->type_obj();
353
-        return $price_type instanceof EE_Price_Type && $price_type->is_tax();
354
-    }
355
-
356
-
357
-    /**
358
-     * return pretty price dependant on whether its a dollar or percent.
359
-     *
360
-     * @return string
361
-     * @throws EE_Error
362
-     * @throws ReflectionException
363
-     * @since 4.4.0
364
-     */
365
-    public function pretty_price(): string
366
-    {
367
-        return ! $this->is_percent()
368
-            ? $this->get_pretty('PRC_amount')
369
-            : $this->get('PRC_amount') . '%';
370
-    }
371
-
372
-
373
-    /**
374
-     * @return mixed
375
-     * @throws EE_Error
376
-     * @throws ReflectionException
377
-     */
378
-    public function get_price_without_currency_symbol()
379
-    {
380
-        return str_replace(
381
-            EE_Registry::instance()->CFG->currency->sign,
382
-            '',
383
-            $this->get_pretty('PRC_amount')
384
-        );
385
-    }
17
+	/**
18
+	 * @param array  $props_n_values          incoming values
19
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
20
+	 *                                        used.)
21
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
22
+	 *                                        date_format and the second value is the time format
23
+	 * @return EE_Price
24
+	 * @throws EE_Error
25
+	 * @throws ReflectionException
26
+	 */
27
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
28
+	{
29
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
30
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
31
+	}
32
+
33
+
34
+	/**
35
+	 * @param array  $props_n_values  incoming values from the database
36
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
37
+	 *                                the website will be used.
38
+	 * @return EE_Price
39
+	 * @throws EE_Error
40
+	 * @throws ReflectionException
41
+	 */
42
+	public static function new_instance_from_db($props_n_values = [], $timezone = '')
43
+	{
44
+		return new self($props_n_values, true, $timezone);
45
+	}
46
+
47
+
48
+	/**
49
+	 * Set Price type ID
50
+	 *
51
+	 * @param int $PRT_ID
52
+	 * @throws EE_Error
53
+	 * @throws ReflectionException
54
+	 */
55
+	public function set_type($PRT_ID = 0)
56
+	{
57
+		$this->set('PRT_ID', $PRT_ID);
58
+	}
59
+
60
+
61
+	/**
62
+	 * Set Price Amount
63
+	 *
64
+	 * @param float $PRC_amount
65
+	 * @throws EE_Error
66
+	 * @throws ReflectionException
67
+	 */
68
+	public function set_amount($PRC_amount = 0.00)
69
+	{
70
+		$this->set('PRC_amount', $PRC_amount);
71
+	}
72
+
73
+
74
+	/**
75
+	 * Set Price Name
76
+	 *
77
+	 * @param string $PRC_name
78
+	 * @throws EE_Error
79
+	 * @throws ReflectionException
80
+	 */
81
+	public function set_name($PRC_name = '')
82
+	{
83
+		$this->set('PRC_name', $PRC_name);
84
+	}
85
+
86
+
87
+	/**
88
+	 * Set Price Description
89
+	 *
90
+	 * @param string $PRC_desc
91
+	 * @throws EE_Error
92
+	 * @throws ReflectionException
93
+	 */
94
+	public function set_description($PRC_desc = '')
95
+	{
96
+		$this->Set('PRC_desc', $PRC_desc);
97
+	}
98
+
99
+
100
+	/**
101
+	 * set is_default
102
+	 *
103
+	 * @param bool $PRC_is_default
104
+	 * @throws EE_Error
105
+	 * @throws ReflectionException
106
+	 */
107
+	public function set_is_default($PRC_is_default = false)
108
+	{
109
+		$this->set('PRC_is_default', $PRC_is_default);
110
+	}
111
+
112
+
113
+	/**
114
+	 * set deleted
115
+	 *
116
+	 * @param bool $PRC_deleted
117
+	 * @throws EE_Error
118
+	 * @throws ReflectionException
119
+	 */
120
+	public function set_deleted($PRC_deleted = null)
121
+	{
122
+		$this->set('PRC_deleted', $PRC_deleted);
123
+	}
124
+
125
+
126
+	/**
127
+	 * get Price type
128
+	 *
129
+	 * @return int
130
+	 * @throws EE_Error
131
+	 * @throws ReflectionException
132
+	 */
133
+	public function type(): int
134
+	{
135
+		return (int) $this->get('PRT_ID');
136
+	}
137
+
138
+
139
+	/**
140
+	 * get Price Amount
141
+	 *
142
+	 * @return float
143
+	 * @throws EE_Error
144
+	 * @throws ReflectionException
145
+	 */
146
+	public function amount(): float
147
+	{
148
+		return (float) $this->get('PRC_amount');
149
+	}
150
+
151
+
152
+	/**
153
+	 * get Price Name
154
+	 *
155
+	 * @return        string
156
+	 * @throws EE_Error
157
+	 * @throws ReflectionException
158
+	 */
159
+	public function name()
160
+	{
161
+		return $this->get('PRC_name');
162
+	}
163
+
164
+
165
+	/**
166
+	 * get Price description
167
+	 *
168
+	 * @return        string
169
+	 * @throws EE_Error
170
+	 * @throws ReflectionException
171
+	 */
172
+	public function desc()
173
+	{
174
+		return $this->get('PRC_desc');
175
+	}
176
+
177
+
178
+	/**
179
+	 * get overrides
180
+	 *
181
+	 * @return        int
182
+	 * @throws EE_Error
183
+	 * @throws ReflectionException
184
+	 */
185
+	public function overrides()
186
+	{
187
+		return $this->get('PRC_overrides');
188
+	}
189
+
190
+
191
+	/**
192
+	 * get order
193
+	 *
194
+	 * @return int
195
+	 * @throws EE_Error
196
+	 * @throws ReflectionException
197
+	 */
198
+	public function order()
199
+	{
200
+		return $this->get('PRC_order');
201
+	}
202
+
203
+
204
+	/**
205
+	 * get the author of the price
206
+	 *
207
+	 * @return int
208
+	 * @throws EE_Error
209
+	 * @throws ReflectionException
210
+	 * @since 4.5.0
211
+	 */
212
+	public function wp_user()
213
+	{
214
+		return $this->get('PRC_wp_user');
215
+	}
216
+
217
+
218
+	/**
219
+	 * get is_default
220
+	 *
221
+	 * @return bool
222
+	 * @throws EE_Error
223
+	 * @throws ReflectionException
224
+	 */
225
+	public function is_default()
226
+	{
227
+		return $this->get('PRC_is_default');
228
+	}
229
+
230
+
231
+	/**
232
+	 * get deleted
233
+	 *
234
+	 * @return bool
235
+	 * @throws EE_Error
236
+	 * @throws ReflectionException
237
+	 */
238
+	public function deleted()
239
+	{
240
+		return $this->get('PRC_deleted');
241
+	}
242
+
243
+
244
+	/**
245
+	 * @return bool
246
+	 * @throws EE_Error
247
+	 * @throws ReflectionException
248
+	 */
249
+	public function parent()
250
+	{
251
+		return $this->get('PRC_parent');
252
+	}
253
+
254
+
255
+	// some helper methods for getting info on the price_type for this price
256
+
257
+	/**
258
+	 * @return EE_Price_Type|null
259
+	 * @throws EE_Error
260
+	 * @throws ReflectionException
261
+	 */
262
+	public function type_obj(): ?EE_Price_Type
263
+	{
264
+		return $this->get_first_related('Price_Type');
265
+	}
266
+
267
+
268
+	/**
269
+	 * @return int
270
+	 * @throws EE_Error
271
+	 * @throws ReflectionException
272
+	 */
273
+	public function type_order(): int
274
+	{
275
+		$price_type = $this->type_obj();
276
+		return $price_type instanceof EE_Price_Type ? $price_type->order() : 0;
277
+	}
278
+
279
+
280
+	/**
281
+	 * return whether the price is a base price or not
282
+	 *
283
+	 * @return bool
284
+	 * @throws EE_Error
285
+	 * @throws InvalidArgumentException
286
+	 * @throws InvalidDataTypeException
287
+	 * @throws InvalidInterfaceException
288
+	 * @throws ReflectionException
289
+	 */
290
+	public function is_base_price(): bool
291
+	{
292
+		$price_type = $this->type_obj();
293
+		return $price_type instanceof EE_Price_Type && $price_type->is_base_price();
294
+	}
295
+
296
+
297
+	/**
298
+	 * Simply indicates whether this price increases or decreases the total
299
+	 *
300
+	 * @return bool true = discount, otherwise adds to the total
301
+	 * @throws EE_Error
302
+	 * @throws ReflectionException
303
+	 */
304
+	public function is_discount(): bool
305
+	{
306
+		$price_type = $this->type_obj();
307
+		return $price_type instanceof EE_Price_Type && $price_type->is_discount();
308
+	}
309
+
310
+
311
+	/**
312
+	 * whether the price is a percentage or not
313
+	 *
314
+	 * @return bool
315
+	 * @throws EE_Error
316
+	 * @throws InvalidArgumentException
317
+	 * @throws InvalidDataTypeException
318
+	 * @throws InvalidInterfaceException
319
+	 * @throws ReflectionException
320
+	 */
321
+	public function is_percent(): bool
322
+	{
323
+		$price_type = $this->type_obj();
324
+		return $price_type instanceof EE_Price_Type && $price_type->is_percent();
325
+	}
326
+
327
+
328
+	/**
329
+	 * whether the price is a percentage or not
330
+	 *
331
+	 * @return bool
332
+	 * @throws EE_Error
333
+	 * @throws ReflectionException
334
+	 */
335
+	public function is_surcharge(): bool
336
+	{
337
+		$price_type = $this->type_obj();
338
+		return $price_type instanceof EE_Price_Type && $price_type->is_surcharge();
339
+	}
340
+
341
+
342
+
343
+	/**
344
+	 * whether the price is a percentage or not
345
+	 *
346
+	 * @return bool
347
+	 * @throws EE_Error
348
+	 * @throws ReflectionException
349
+	 */
350
+	public function is_tax(): bool
351
+	{
352
+		$price_type = $this->type_obj();
353
+		return $price_type instanceof EE_Price_Type && $price_type->is_tax();
354
+	}
355
+
356
+
357
+	/**
358
+	 * return pretty price dependant on whether its a dollar or percent.
359
+	 *
360
+	 * @return string
361
+	 * @throws EE_Error
362
+	 * @throws ReflectionException
363
+	 * @since 4.4.0
364
+	 */
365
+	public function pretty_price(): string
366
+	{
367
+		return ! $this->is_percent()
368
+			? $this->get_pretty('PRC_amount')
369
+			: $this->get('PRC_amount') . '%';
370
+	}
371
+
372
+
373
+	/**
374
+	 * @return mixed
375
+	 * @throws EE_Error
376
+	 * @throws ReflectionException
377
+	 */
378
+	public function get_price_without_currency_symbol()
379
+	{
380
+		return str_replace(
381
+			EE_Registry::instance()->CFG->currency->sign,
382
+			'',
383
+			$this->get_pretty('PRC_amount')
384
+		);
385
+	}
386 386
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Export.class.php 1 patch
Indentation   +780 added lines, -780 removed lines patch added patch discarded remove patch
@@ -18,784 +18,784 @@
 block discarded – undo
18 18
  */
19 19
 class EE_Export
20 20
 {
21
-    const option_prefix = 'ee_report_job_';
22
-
23
-
24
-    // instance of the EE_Export object
25
-    private static $_instance = null;
26
-
27
-    // instance of the EE_CSV object
28
-    /**
29
-     *
30
-     * @var EE_CSV
31
-     */
32
-    public $EE_CSV = null;
33
-
34
-
35
-    private $_req_data = array();
36
-
37
-
38
-    /**
39
-     *        private constructor to prevent direct creation
40
-     *
41
-     * @Constructor
42
-     * @access private
43
-     * @param array $request_data
44
-     */
45
-    private function __construct($request_data = array())
46
-    {
47
-        $this->_req_data = $request_data;
48
-        $this->today = date("Y-m-d", time());
49
-        require_once(EE_CLASSES . 'EE_CSV.class.php');
50
-        $this->EE_CSV = EE_CSV::instance();
51
-    }
52
-
53
-
54
-    /**
55
-     *        @ singleton method used to instantiate class object
56
-     *        @ access public
57
-     *
58
-     * @param array $request_data
59
-     * @return \EE_Export
60
-     */
61
-    public static function instance($request_data = array())
62
-    {
63
-        // check if class object is instantiated
64
-        if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Export)) {
65
-            self::$_instance = new self($request_data);
66
-        }
67
-        return self::$_instance;
68
-    }
69
-
70
-
71
-    /**
72
-     * @Export Event Espresso data - routes export requests
73
-     * @access public
74
-     * @return void | bool
75
-     */
76
-    public function export()
77
-    {
78
-        // in case of bulk exports, the "actual" action will be in action2, but first check regular action for "export" keyword
79
-        if (isset($this->_req_data['action']) && strpos($this->_req_data['action'], 'export') === false) {
80
-            // check if action2 has export action
81
-            if (isset($this->_req_data['action2']) && strpos($this->_req_data['action2'], 'export') !== false) {
82
-                // whoop! there it is!
83
-                $this->_req_data['action'] = $this->_req_data['action2'];
84
-            }
85
-        }
86
-
87
-        $this->_req_data['export'] = isset($this->_req_data['export']) ? $this->_req_data['export'] : '';
88
-
89
-        switch ($this->_req_data['export']) {
90
-            case 'report':
91
-                switch ($this->_req_data['action']) {
92
-                    case "event":
93
-                    case "export_events":
94
-                    case 'all_event_data':
95
-                        $this->export_all_event_data();
96
-                        break;
97
-
98
-                    case 'registrations_report_for_event':
99
-                        $this->report_registrations_for_event($this->_req_data['EVT_ID']);
100
-                        break;
101
-
102
-                    case 'attendees':
103
-                        $this->export_attendees();
104
-                        break;
105
-
106
-                    case 'categories':
107
-                        $this->export_categories();
108
-                        break;
109
-
110
-                    default:
111
-                        EE_Error::add_error(
112
-                            esc_html__('An error occurred! The requested export report could not be found.', 'event_espresso'),
113
-                            __FILE__,
114
-                            __FUNCTION__,
115
-                            __LINE__
116
-                        );
117
-                        return false;
118
-                        break;
119
-                }
120
-                break; // end of switch export : report
121
-            default:
122
-                break;
123
-        } // end of switch export
124
-
125
-        exit;
126
-    }
127
-
128
-    /**
129
-     * Downloads a CSV file with all the columns, but no data. This should be used for importing
130
-     *
131
-     * @return void kills execution
132
-     * @throws EE_Error
133
-     * @throws ReflectionException
134
-     */
135
-    public function export_sample()
136
-    {
137
-        $event = EEM_Event::instance()->get_one();
138
-        $this->_req_data['EVT_ID'] = $event->ID();
139
-        $this->export_all_event_data();
140
-    }
141
-
142
-
143
-    /**
144
-     * @Export data for ALL events
145
-     * @access public
146
-     * @return void
147
-     * @throws EE_Error
148
-     * @throws ReflectionException
149
-     */
150
-    public function export_all_event_data()
151
-    {
152
-        // are any Event IDs set?
153
-        $event_query_params = array();
154
-        $related_models_query_params = array();
155
-        $related_through_reg_query_params = array();
156
-        $datetime_ticket_query_params = array();
157
-        $price_query_params = array();
158
-        $price_type_query_params = array();
159
-        $term_query_params = array();
160
-        $state_country_query_params = array();
161
-        $question_group_query_params = array();
162
-        $question_query_params = array();
163
-        if (isset($this->_req_data['EVT_ID'])) {
164
-            // do we have an array of IDs ?
165
-
166
-            if (is_array($this->_req_data['EVT_ID'])) {
167
-                $EVT_IDs = array_map('sanitize_text_field', $this->_req_data['EVT_ID']);
168
-                $value_to_equal = array('IN', $EVT_IDs);
169
-                $filename = 'events';
170
-            } else {
171
-                // generate regular where = clause
172
-                $EVT_ID = absint($this->_req_data['EVT_ID']);
173
-                $value_to_equal = $EVT_ID;
174
-                $event = EE_Registry::instance()->load_model('Event')->get_one_by_ID($EVT_ID);
175
-
176
-                $filename = 'event-' . ($event instanceof EE_Event ? $event->slug() : esc_html__('unknown', 'event_espresso'));
177
-            }
178
-            $event_query_params[0]['EVT_ID'] = $value_to_equal;
179
-            $related_models_query_params[0]['Event.EVT_ID'] = $value_to_equal;
180
-            $related_through_reg_query_params[0]['Registration.EVT_ID'] = $value_to_equal;
181
-            $datetime_ticket_query_params[0]['Datetime.EVT_ID'] = $value_to_equal;
182
-            $price_query_params[0]['Ticket.Datetime.EVT_ID'] = $value_to_equal;
183
-            $price_type_query_params[0]['Price.Ticket.Datetime.EVT_ID'] = $value_to_equal;
184
-            $term_query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $value_to_equal;
185
-            $state_country_query_params[0]['Venue.Event.EVT_ID'] = $value_to_equal;
186
-            $question_group_query_params[0]['Event.EVT_ID'] = $value_to_equal;
187
-            $question_query_params[0]['Question_Group.Event.EVT_ID'] = $value_to_equal;
188
-        } else {
189
-            $filename = 'all-events';
190
-        }
191
-
192
-
193
-        // array in the format:  table name =>  query where clause
194
-        $models_to_export = array(
195
-            'Event'                   => $event_query_params,
196
-            'Datetime'                => $related_models_query_params,
197
-            'Ticket_Template'         => $price_query_params,
198
-            'Ticket'                  => $datetime_ticket_query_params,
199
-            'Datetime_Ticket'         => $datetime_ticket_query_params,
200
-            'Price_Type'              => $price_type_query_params,
201
-            'Price'                   => $price_query_params,
202
-            'Ticket_Price'            => $price_query_params,
203
-            'Term'                    => $term_query_params,
204
-            'Term_Taxonomy'           => $related_models_query_params,
205
-            'Term_Relationship'       => $related_models_query_params, // model has NO primary key...
206
-            'Country'                 => $state_country_query_params,
207
-            'State'                   => $state_country_query_params,
208
-            'Venue'                   => $related_models_query_params,
209
-            'Event_Venue'             => $related_models_query_params,
210
-            'Question_Group'          => $question_group_query_params,
211
-            'Event_Question_Group'    => $question_group_query_params,
212
-            'Question'                => $question_query_params,
213
-            'Question_Group_Question' => $question_query_params,
214
-            // 'Transaction'=>$related_through_reg_query_params,
215
-            // 'Registration'=>$related_models_query_params,
216
-            // 'Attendee'=>$related_through_reg_query_params,
217
-            // 'Line_Item'=>
218
-
219
-        );
220
-
221
-        $model_data = $this->_get_export_data_for_models($models_to_export);
222
-
223
-        $filename = $this->generate_filename($filename);
224
-
225
-        if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $model_data)) {
226
-            EE_Error::add_error(
227
-                esc_html__(
228
-                    "'An error occurred and the Event details could not be exported from the database.'",
229
-                    "event_espresso"
230
-                ),
231
-                __FILE__,
232
-                __FUNCTION__,
233
-                __LINE__
234
-            );
235
-        }
236
-    }
237
-
238
-    public function report_attendees()
239
-    {
240
-        $attendee_rows = EEM_Attendee::instance()->get_all_wpdb_results(
241
-            array(
242
-                'force_join' => array('State', 'Country'),
243
-                'caps'       => EEM_Base::caps_read_admin,
244
-            )
245
-        );
246
-        $csv_data = array();
247
-        foreach ($attendee_rows as $attendee_row) {
248
-            $csv_row = array();
249
-            foreach (EEM_Attendee::instance()->field_settings() as $field_name => $field_obj) {
250
-                if ($field_name == 'STA_ID') {
251
-                    $state_name_field = EEM_State::instance()->field_settings_for('STA_name');
252
-                    $csv_row[ esc_html__('State', 'event_espresso') ] = $attendee_row[ $state_name_field->get_qualified_column(
253
-                    ) ];
254
-                } elseif ($field_name == 'CNT_ISO') {
255
-                    $country_name_field = EEM_Country::instance()->field_settings_for('CNT_name');
256
-                    $csv_row[ esc_html__(
257
-                        'Country',
258
-                        'event_espresso'
259
-                    ) ] = $attendee_row[ $country_name_field->get_qualified_column() ];
260
-                } else {
261
-                    $csv_row[ $field_obj->get_nicename() ] = $attendee_row[ $field_obj->get_qualified_column() ];
262
-                }
263
-            }
264
-            $csv_data[] = $csv_row;
265
-        }
266
-
267
-        $filename = $this->generate_filename('contact-list-report');
268
-
269
-        $handle = $this->EE_CSV->begin_sending_csv($filename);
270
-        $this->EE_CSV->write_data_array_to_csv($handle, $csv_data);
271
-        $this->EE_CSV->end_sending_csv($handle);
272
-    }
273
-
274
-
275
-    /**
276
-     * @Export data for ALL attendees
277
-     * @access public
278
-     * @return void
279
-     * @throws EE_Error
280
-     */
281
-    public function export_attendees()
282
-    {
283
-
284
-        $states_that_have_an_attendee = EEM_State::instance()->get_all(
285
-            array(0 => array('Attendee.ATT_ID' => array('IS NOT NULL')))
286
-        );
287
-        $countries_that_have_an_attendee = EEM_Country::instance()->get_all(
288
-            array(0 => array('Attendee.ATT_ID' => array('IS NOT NULL')))
289
-        );
290
-        // $states_to_export_query_params
291
-        $models_to_export = array(
292
-            'Country'  => array(array('CNT_ISO' => array('IN', array_keys($countries_that_have_an_attendee)))),
293
-            'State'    => array(array('STA_ID' => array('IN', array_keys($states_that_have_an_attendee)))),
294
-            'Attendee' => array(),
295
-        );
296
-
297
-
298
-        $model_data = $this->_get_export_data_for_models($models_to_export);
299
-        $filename = $this->generate_filename('all-attendees');
300
-
301
-        if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $model_data)) {
302
-            EE_Error::add_error(
303
-                esc_html__(
304
-                    'An error occurred and the Attendee data could not be exported from the database.',
305
-                    'event_espresso'
306
-                ),
307
-                __FILE__,
308
-                __FUNCTION__,
309
-                __LINE__
310
-            );
311
-        }
312
-    }
313
-
314
-    /**
315
-     * Shortcut for preparing a database result for display
316
-     *
317
-     * @param EEM_Base $model
318
-     * @param string $field_name
319
-     * @param string $raw_db_value
320
-     * @param bool|string $pretty_schema true to display pretty, a string to use a specific "Schema", or false to
321
-     *                                      NOT display pretty
322
-     * @return string
323
-     * @throws EE_Error
324
-     */
325
-    protected function _prepare_value_from_db_for_display($model, $field_name, $raw_db_value, $pretty_schema = true)
326
-    {
327
-        $field_obj = $model->field_settings_for($field_name);
328
-        $value_on_model_obj = $field_obj->prepare_for_set_from_db($raw_db_value);
329
-        if ($field_obj instanceof EE_Datetime_Field) {
330
-            $field_obj->set_date_format(
331
-                EE_CSV::instance()->get_date_format_for_csv($field_obj->get_date_format($pretty_schema)),
332
-                $pretty_schema
333
-            );
334
-            $field_obj->set_time_format(
335
-                EE_CSV::instance()->get_time_format_for_csv($field_obj->get_time_format($pretty_schema)),
336
-                $pretty_schema
337
-            );
338
-        }
339
-        if ($pretty_schema === true) {
340
-            return $field_obj->prepare_for_pretty_echoing($value_on_model_obj);
341
-        } elseif (is_string($pretty_schema)) {
342
-            return $field_obj->prepare_for_pretty_echoing($value_on_model_obj, $pretty_schema);
343
-        } else {
344
-            return $field_obj->prepare_for_get($value_on_model_obj);
345
-        }
346
-    }
347
-
348
-    /**
349
-     * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price
350
-     * name, and the questions associated with the registrations
351
-     *
352
-     * @param int $event_id
353
-     * @throws EE_Error
354
-     * @throws ReflectionException
355
-     */
356
-    public function report_registrations_for_event($event_id = null)
357
-    {
358
-        $reg_fields_to_include = array(
359
-            'TXN_ID',
360
-            'ATT_ID',
361
-            'REG_ID',
362
-            'REG_date',
363
-            'REG_code',
364
-            'REG_count',
365
-            'REG_final_price',
366
-
367
-        );
368
-        $att_fields_to_include = array(
369
-            'ATT_fname',
370
-            'ATT_lname',
371
-            'ATT_email',
372
-            'ATT_address',
373
-            'ATT_address2',
374
-            'ATT_city',
375
-            'STA_ID',
376
-            'CNT_ISO',
377
-            'ATT_zip',
378
-            'ATT_phone',
379
-        );
380
-
381
-        $registrations_csv_ready_array = array();
382
-        $reg_model = EE_Registry::instance()->load_model('Registration');
383
-        $query_params = apply_filters(
384
-            'FHEE__EE_Export__report_registration_for_event',
385
-            array(
386
-                array(
387
-                    'OR'                 => array(
388
-                        // don't include registrations from failed or abandoned transactions...
389
-                        'Transaction.STS_ID' => array(
390
-                            'NOT IN',
391
-                            array(EEM_Transaction::failed_status_code, EEM_Transaction::abandoned_status_code),
392
-                        ),
393
-                        // unless the registration is approved, in which case include it regardless of transaction status
394
-                        'STS_ID'             => RegStatus::APPROVED,
395
-                    ),
396
-                    'Ticket.TKT_deleted' => array('IN', array(true, false)),
397
-                ),
398
-                'order_by'   => array('Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'),
399
-                'force_join' => array('Transaction', 'Ticket', 'Attendee'),
400
-                'caps'       => EEM_Base::caps_read_admin,
401
-            ),
402
-            $event_id
403
-        );
404
-        if ($event_id) {
405
-            $query_params[0]['EVT_ID'] = $event_id;
406
-        } else {
407
-            $query_params['force_join'][] = 'Event';
408
-        }
409
-        $registration_rows = $reg_model->get_all_wpdb_results($query_params);
410
-        // get all questions which relate to someone in this group
411
-        $registration_ids = array();
412
-        foreach ($registration_rows as $reg_row) {
413
-            $registration_ids[] = intval($reg_row['Registration.REG_ID']);
414
-        }
415
-        // EEM_Question::instance()->show_next_x_db_queries();
416
-        $questions_for_these_regs_rows = EEM_Question::instance()->get_all_wpdb_results(
417
-            array(array('Answer.REG_ID' => array('IN', $registration_ids)))
418
-        );
419
-        foreach ($registration_rows as $reg_row) {
420
-            if (is_array($reg_row)) {
421
-                $reg_csv_array = array();
422
-                if (! $event_id) {
423
-                    // get the event's name and Id
424
-                    $reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
425
-                        esc_html__('%1$s (%2$s)', 'event_espresso'),
426
-                        $this->_prepare_value_from_db_for_display(
427
-                            EEM_Event::instance(),
428
-                            'EVT_name',
429
-                            $reg_row['Event_CPT.post_title']
430
-                        ),
431
-                        $reg_row['Event_CPT.ID']
432
-                    );
433
-                }
434
-                $is_primary_reg = $reg_row['Registration.REG_count'] == '1' ? true : false;
435
-                /*@var $reg_row EE_Registration */
436
-                foreach ($reg_fields_to_include as $field_name) {
437
-                    $field = $reg_model->field_settings_for($field_name);
438
-                    if ($field_name == 'REG_final_price') {
439
-                        $value = $this->_prepare_value_from_db_for_display(
440
-                            $reg_model,
441
-                            $field_name,
442
-                            $reg_row['Registration.REG_final_price'],
443
-                            'localized_float'
444
-                        );
445
-                    } elseif ($field_name == 'REG_count') {
446
-                        $value = sprintf(
447
-                            esc_html__('%s of %s', 'event_espresso'),
448
-                            $this->_prepare_value_from_db_for_display(
449
-                                $reg_model,
450
-                                'REG_count',
451
-                                $reg_row['Registration.REG_count']
452
-                            ),
453
-                            $this->_prepare_value_from_db_for_display(
454
-                                $reg_model,
455
-                                'REG_group_size',
456
-                                $reg_row['Registration.REG_group_size']
457
-                            )
458
-                        );
459
-                    } elseif ($field_name == 'REG_date') {
460
-                        $value = $this->_prepare_value_from_db_for_display(
461
-                            $reg_model,
462
-                            $field_name,
463
-                            $reg_row['Registration.REG_date'],
464
-                            'no_html'
465
-                        );
466
-                    } else {
467
-                        $value = $this->_prepare_value_from_db_for_display(
468
-                            $reg_model,
469
-                            $field_name,
470
-                            $reg_row[ $field->get_qualified_column() ]
471
-                        );
472
-                    }
473
-                    $reg_csv_array[ $this->_get_column_name_for_field($field) ] = $value;
474
-                    if ($field_name == 'REG_final_price') {
475
-                        // add a column named Currency after the final price
476
-                        $reg_csv_array[ esc_html__("Currency", "event_espresso") ] = EE_Config::instance()->currency->code;
477
-                    }
478
-                }
479
-                // get pretty status
480
-                $stati = EEM_Status::instance()->localized_status(
481
-                    array(
482
-                        $reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
483
-                        $reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
484
-                    ),
485
-                    false,
486
-                    'sentence'
487
-                );
488
-                $reg_csv_array[ esc_html__(
489
-                    "Registration Status",
490
-                    'event_espresso'
491
-                ) ] = $stati[ $reg_row['Registration.STS_ID'] ];
492
-                // get pretty trnasaction status
493
-                $reg_csv_array[ esc_html__(
494
-                    "Transaction Status",
495
-                    'event_espresso'
496
-                ) ] = $stati[ $reg_row['TransactionTable.STS_ID'] ];
497
-                $reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
498
-                    ? $this->_prepare_value_from_db_for_display(
499
-                        EEM_Transaction::instance(),
500
-                        'TXN_total',
501
-                        $reg_row['TransactionTable.TXN_total'],
502
-                        'localized_float'
503
-                    ) : '0.00';
504
-                $reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ] = $is_primary_reg
505
-                    ? $this->_prepare_value_from_db_for_display(
506
-                        EEM_Transaction::instance(),
507
-                        'TXN_paid',
508
-                        $reg_row['TransactionTable.TXN_paid'],
509
-                        'localized_float'
510
-                    ) : '0.00';
511
-                $payment_methods = array();
512
-                $gateway_txn_ids_etc = array();
513
-                $payment_times = array();
514
-                if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
515
-                    $payments_info = EEM_Payment::instance()->get_all_wpdb_results(
516
-                        array(
517
-                            array(
518
-                                'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
519
-                                'STS_ID' => EEM_Payment::status_id_approved,
520
-                            ),
521
-                            'force_join' => array('Payment_Method'),
522
-                        ),
523
-                        ARRAY_A,
524
-                        'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
525
-                    );
526
-                    list($payment_methods, $gateway_txn_ids_etc, $payment_times) = PaymentsInfoCSV::extractPaymentInfo($payments_info);
527
-                }
528
-                $reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(',', $payment_times);
529
-                $reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(",", $payment_methods);
530
-                $reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
531
-                    ',',
532
-                    $gateway_txn_ids_etc
533
-                );
534
-
535
-                // get whether or not the user has checked in
536
-                $reg_csv_array[ esc_html__("Check-Ins", "event_espresso") ] = $reg_model->count_related(
537
-                    $reg_row['Registration.REG_ID'],
538
-                    'Checkin'
539
-                );
540
-                // get ticket of registration and its price
541
-                $ticket_model = EE_Registry::instance()->load_model('Ticket');
542
-                if ($reg_row['Ticket.TKT_ID']) {
543
-                    $ticket_name = $this->_prepare_value_from_db_for_display(
544
-                        $ticket_model,
545
-                        'TKT_name',
546
-                        $reg_row['Ticket.TKT_name']
547
-                    );
548
-                    $datetimes_strings = array();
549
-                    foreach (
550
-                        EEM_Datetime::instance()->get_all_wpdb_results(
551
-                            array(
552
-                            array('Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']),
553
-                            'order_by'                 => array('DTT_EVT_start' => 'ASC'),
554
-                            'default_where_conditions' => 'none',
555
-                            )
556
-                        ) as $datetime
557
-                    ) {
558
-                        $datetimes_strings[] = $this->_prepare_value_from_db_for_display(
559
-                            EEM_Datetime::instance(),
560
-                            'DTT_EVT_start',
561
-                            $datetime['Datetime.DTT_EVT_start']
562
-                        );
563
-                    }
564
-                } else {
565
-                    $ticket_name = esc_html__('Unknown', 'event_espresso');
566
-                    $datetimes_strings = array(esc_html__('Unknown', 'event_espresso'));
567
-                }
568
-                $reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
569
-                $reg_csv_array[ esc_html__("Datetimes of Ticket", "event_espresso") ] = implode(", ", $datetimes_strings);
570
-                // get datetime(s) of registration
571
-
572
-                // add attendee columns
573
-                foreach ($att_fields_to_include as $att_field_name) {
574
-                    $field_obj = EEM_Attendee::instance()->field_settings_for($att_field_name);
575
-                    if ($reg_row['Attendee_CPT.ID']) {
576
-                        if ($att_field_name == 'STA_ID') {
577
-                            $value = EEM_State::instance()->get_var(
578
-                                array(array('STA_ID' => $reg_row['Attendee_Meta.STA_ID'])),
579
-                                'STA_name'
580
-                            );
581
-                        } elseif ($att_field_name == 'CNT_ISO') {
582
-                            $value = EEM_Country::instance()->get_var(
583
-                                array(array('CNT_ISO' => $reg_row['Attendee_Meta.CNT_ISO'])),
584
-                                'CNT_name'
585
-                            );
586
-                        } else {
587
-                            $value = $this->_prepare_value_from_db_for_display(
588
-                                EEM_Attendee::instance(),
589
-                                $att_field_name,
590
-                                $reg_row[ $field_obj->get_qualified_column() ]
591
-                            );
592
-                        }
593
-                    } else {
594
-                        $value = '';
595
-                    }
596
-
597
-                    $reg_csv_array[ $this->_get_column_name_for_field($field_obj) ] = $value;
598
-                }
599
-
600
-                // make sure each registration has the same questions in the same order
601
-                foreach ($questions_for_these_regs_rows as $question_row) {
602
-                    if (! isset($reg_csv_array[ $question_row['Question.QST_admin_label'] ])) {
603
-                        $reg_csv_array[ $question_row['Question.QST_admin_label'] ] = null;
604
-                    }
605
-                }
606
-                // now fill out the questions THEY answered
607
-                foreach (
608
-                    EEM_Answer::instance()->get_all_wpdb_results(
609
-                        array(array('REG_ID' => $reg_row['Registration.REG_ID']), 'force_join' => array('Question'))
610
-                    ) as $answer_row
611
-                ) {
612
-                    /* @var $answer EE_Answer */
613
-                    if ($answer_row['Question.QST_ID']) {
614
-                        $question_label = $this->_prepare_value_from_db_for_display(
615
-                            EEM_Question::instance(),
616
-                            'QST_admin_label',
617
-                            $answer_row['Question.QST_admin_label']
618
-                        );
619
-                    } else {
620
-                        $question_label = sprintf(esc_html__('Question $s', 'event_espresso'), $answer_row['Answer.QST_ID']);
621
-                    }
622
-                    if (isset($answer_row['Question.QST_type']) && $answer_row['Question.QST_type'] == EEM_Question::QST_type_state) {
623
-                        $reg_csv_array[ $question_label ] = EEM_State::instance()->get_state_name_by_ID(
624
-                            $answer_row['Answer.ANS_value']
625
-                        );
626
-                    } else {
627
-                        $reg_csv_array[ $question_label ] = $this->_prepare_value_from_db_for_display(
628
-                            EEM_Answer::instance(),
629
-                            'ANS_value',
630
-                            $answer_row['Answer.ANS_value']
631
-                        );
632
-                    }
633
-                }
634
-                $registrations_csv_ready_array[] = apply_filters(
635
-                    'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
636
-                    $reg_csv_array,
637
-                    $reg_row
638
-                );
639
-            }
640
-        }
641
-
642
-        // if we couldn't export anything, we want to at least show the column headers
643
-        if (empty($registrations_csv_ready_array)) {
644
-            $reg_csv_array = array();
645
-            $model_and_fields_to_include = array(
646
-                'Registration' => $reg_fields_to_include,
647
-                'Attendee'     => $att_fields_to_include,
648
-            );
649
-            foreach ($model_and_fields_to_include as $model_name => $field_list) {
650
-                $model = EE_Registry::instance()->load_model($model_name);
651
-                foreach ($field_list as $field_name) {
652
-                    $field = $model->field_settings_for($field_name);
653
-                    $reg_csv_array[ $this->_get_column_name_for_field(
654
-                        $field
655
-                    ) ] = null;// $registration->get($field->get_name());
656
-                }
657
-            }
658
-            $registrations_csv_ready_array [] = $reg_csv_array;
659
-        }
660
-        if ($event_id) {
661
-            $event_slug = EEM_Event::instance()->get_var(array(array('EVT_ID' => $event_id)), 'EVT_slug');
662
-            if (! $event_slug) {
663
-                $event_slug = esc_html__('unknown', 'event_espresso');
664
-            }
665
-        } else {
666
-            $event_slug = esc_html__('all', 'event_espresso');
667
-        }
668
-        $filename = sprintf("registrations-for-%s", $event_slug);
669
-
670
-        $handle = $this->EE_CSV->begin_sending_csv($filename);
671
-        $this->EE_CSV->write_data_array_to_csv($handle, $registrations_csv_ready_array);
672
-        $this->EE_CSV->end_sending_csv($handle);
673
-    }
674
-
675
-    /**
676
-     * Gets the 'normal' column named for fields
677
-     *
678
-     * @param EE_Model_Field_Base $field
679
-     * @return string
680
-     * @throws EE_Error
681
-     */
682
-    protected function _get_column_name_for_field(EE_Model_Field_Base $field)
683
-    {
684
-        return $field->get_nicename() . "[" . $field->get_name() . "]";
685
-    }
686
-
687
-
688
-    /**
689
-     * @Export data for ALL events
690
-     * @access public
691
-     * @return void
692
-     */
693
-    public function export_categories()
694
-    {
695
-        // are any Event IDs set?
696
-        $query_params = array();
697
-        if (isset($this->_req_data['EVT_CAT_ID'])) {
698
-            // do we have an array of IDs ?
699
-            if (is_array($this->_req_data['EVT_CAT_ID'])) {
700
-                // generate an "IN (CSV)" where clause
701
-                $EVT_CAT_IDs = array_map('sanitize_text_field', $this->_req_data['EVT_CAT_ID']);
702
-                $filename = 'event-categories';
703
-                $query_params[0]['term_taxonomy_id'] = array('IN', $EVT_CAT_IDs);
704
-            } else {
705
-                // generate regular where = clause
706
-                $EVT_CAT_ID = absint($this->_req_data['EVT_CAT_ID']);
707
-                $filename = 'event-category#' . $EVT_CAT_ID;
708
-                $query_params[0]['term_taxonomy_id'] = $EVT_CAT_ID;
709
-            }
710
-        } else {
711
-            // no IDs means we will d/l the entire table
712
-            $filename = 'all-categories';
713
-        }
714
-
715
-        $tables_to_export = array(
716
-            'Term_Taxonomy' => $query_params,
717
-        );
718
-
719
-        $table_data = $this->_get_export_data_for_models($tables_to_export);
720
-        $filename = $this->generate_filename($filename);
721
-
722
-        if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $table_data)) {
723
-            EE_Error::add_error(
724
-                esc_html__(
725
-                    'An error occurred and the Category details could not be exported from the database.',
726
-                    'event_espresso'
727
-                ),
728
-                __FILE__,
729
-                __FUNCTION__,
730
-                __LINE__
731
-            );
732
-        }
733
-    }
734
-
735
-
736
-    /**
737
-     * @process export name to create a suitable filename
738
-     * @access  private
739
-     * @param string - export_name
740
-     * @return string on success, FALSE on fail
741
-     */
742
-    private function generate_filename($export_name = '')
743
-    {
744
-        if ($export_name != '') {
745
-            $filename = get_bloginfo('name') . '-' . $export_name;
746
-            $filename = sanitize_key($filename) . '-' . $this->today;
747
-            return $filename;
748
-        } else {
749
-            EE_Error::add_error(esc_html__("No filename was provided", "event_espresso"), __FILE__, __FUNCTION__, __LINE__);
750
-        }
751
-        return false;
752
-    }
753
-
754
-
755
-    /**
756
-     * @recursive function for exporting table data and merging the results with the next results
757
-     * @access    private
758
-     * @param array $models_to_export keys are model names (eg 'Event', 'Attendee', etc.) and values are arrays of
759
-     *                                query params @return bool on success, FALSE on fail
760
-     * @throws EE_Error
761
-     * @throws ReflectionException
762
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
763
-     */
764
-    private function _get_export_data_for_models($models_to_export = array())
765
-    {
766
-        $table_data = false;
767
-        if (is_array($models_to_export)) {
768
-            foreach ($models_to_export as $model_name => $query_params) {
769
-                // check for a numerically-indexed array. in that case, $model_name is the value!!
770
-                if (is_int($model_name)) {
771
-                    $model_name = $query_params;
772
-                    $query_params = array();
773
-                }
774
-                $model = EE_Registry::instance()->load_model($model_name);
775
-                $model_objects = $model->get_all($query_params);
776
-
777
-                $table_data[ $model_name ] = array();
778
-                foreach ($model_objects as $model_object) {
779
-                    $model_data_array = array();
780
-                    $fields = $model->field_settings();
781
-                    foreach ($fields as $field) {
782
-                        $column_name = $field->get_nicename() . "[" . $field->get_name() . "]";
783
-                        if ($field instanceof EE_Datetime_Field) {
784
-                            // $field->set_date_format('Y-m-d');
785
-                            // $field->set_time_format('H:i:s');
786
-                            $model_data_array[ $column_name ] = $model_object->get_datetime(
787
-                                $field->get_name(),
788
-                                'Y-m-d',
789
-                                'H:i:s'
790
-                            );
791
-                        } else {
792
-                            $model_data_array[ $column_name ] = $model_object->get($field->get_name());
793
-                        }
794
-                    }
795
-                    $table_data[ $model_name ][] = $model_data_array;
796
-                }
797
-            }
798
-        }
799
-        return $table_data;
800
-    }
21
+	const option_prefix = 'ee_report_job_';
22
+
23
+
24
+	// instance of the EE_Export object
25
+	private static $_instance = null;
26
+
27
+	// instance of the EE_CSV object
28
+	/**
29
+	 *
30
+	 * @var EE_CSV
31
+	 */
32
+	public $EE_CSV = null;
33
+
34
+
35
+	private $_req_data = array();
36
+
37
+
38
+	/**
39
+	 *        private constructor to prevent direct creation
40
+	 *
41
+	 * @Constructor
42
+	 * @access private
43
+	 * @param array $request_data
44
+	 */
45
+	private function __construct($request_data = array())
46
+	{
47
+		$this->_req_data = $request_data;
48
+		$this->today = date("Y-m-d", time());
49
+		require_once(EE_CLASSES . 'EE_CSV.class.php');
50
+		$this->EE_CSV = EE_CSV::instance();
51
+	}
52
+
53
+
54
+	/**
55
+	 *        @ singleton method used to instantiate class object
56
+	 *        @ access public
57
+	 *
58
+	 * @param array $request_data
59
+	 * @return \EE_Export
60
+	 */
61
+	public static function instance($request_data = array())
62
+	{
63
+		// check if class object is instantiated
64
+		if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Export)) {
65
+			self::$_instance = new self($request_data);
66
+		}
67
+		return self::$_instance;
68
+	}
69
+
70
+
71
+	/**
72
+	 * @Export Event Espresso data - routes export requests
73
+	 * @access public
74
+	 * @return void | bool
75
+	 */
76
+	public function export()
77
+	{
78
+		// in case of bulk exports, the "actual" action will be in action2, but first check regular action for "export" keyword
79
+		if (isset($this->_req_data['action']) && strpos($this->_req_data['action'], 'export') === false) {
80
+			// check if action2 has export action
81
+			if (isset($this->_req_data['action2']) && strpos($this->_req_data['action2'], 'export') !== false) {
82
+				// whoop! there it is!
83
+				$this->_req_data['action'] = $this->_req_data['action2'];
84
+			}
85
+		}
86
+
87
+		$this->_req_data['export'] = isset($this->_req_data['export']) ? $this->_req_data['export'] : '';
88
+
89
+		switch ($this->_req_data['export']) {
90
+			case 'report':
91
+				switch ($this->_req_data['action']) {
92
+					case "event":
93
+					case "export_events":
94
+					case 'all_event_data':
95
+						$this->export_all_event_data();
96
+						break;
97
+
98
+					case 'registrations_report_for_event':
99
+						$this->report_registrations_for_event($this->_req_data['EVT_ID']);
100
+						break;
101
+
102
+					case 'attendees':
103
+						$this->export_attendees();
104
+						break;
105
+
106
+					case 'categories':
107
+						$this->export_categories();
108
+						break;
109
+
110
+					default:
111
+						EE_Error::add_error(
112
+							esc_html__('An error occurred! The requested export report could not be found.', 'event_espresso'),
113
+							__FILE__,
114
+							__FUNCTION__,
115
+							__LINE__
116
+						);
117
+						return false;
118
+						break;
119
+				}
120
+				break; // end of switch export : report
121
+			default:
122
+				break;
123
+		} // end of switch export
124
+
125
+		exit;
126
+	}
127
+
128
+	/**
129
+	 * Downloads a CSV file with all the columns, but no data. This should be used for importing
130
+	 *
131
+	 * @return void kills execution
132
+	 * @throws EE_Error
133
+	 * @throws ReflectionException
134
+	 */
135
+	public function export_sample()
136
+	{
137
+		$event = EEM_Event::instance()->get_one();
138
+		$this->_req_data['EVT_ID'] = $event->ID();
139
+		$this->export_all_event_data();
140
+	}
141
+
142
+
143
+	/**
144
+	 * @Export data for ALL events
145
+	 * @access public
146
+	 * @return void
147
+	 * @throws EE_Error
148
+	 * @throws ReflectionException
149
+	 */
150
+	public function export_all_event_data()
151
+	{
152
+		// are any Event IDs set?
153
+		$event_query_params = array();
154
+		$related_models_query_params = array();
155
+		$related_through_reg_query_params = array();
156
+		$datetime_ticket_query_params = array();
157
+		$price_query_params = array();
158
+		$price_type_query_params = array();
159
+		$term_query_params = array();
160
+		$state_country_query_params = array();
161
+		$question_group_query_params = array();
162
+		$question_query_params = array();
163
+		if (isset($this->_req_data['EVT_ID'])) {
164
+			// do we have an array of IDs ?
165
+
166
+			if (is_array($this->_req_data['EVT_ID'])) {
167
+				$EVT_IDs = array_map('sanitize_text_field', $this->_req_data['EVT_ID']);
168
+				$value_to_equal = array('IN', $EVT_IDs);
169
+				$filename = 'events';
170
+			} else {
171
+				// generate regular where = clause
172
+				$EVT_ID = absint($this->_req_data['EVT_ID']);
173
+				$value_to_equal = $EVT_ID;
174
+				$event = EE_Registry::instance()->load_model('Event')->get_one_by_ID($EVT_ID);
175
+
176
+				$filename = 'event-' . ($event instanceof EE_Event ? $event->slug() : esc_html__('unknown', 'event_espresso'));
177
+			}
178
+			$event_query_params[0]['EVT_ID'] = $value_to_equal;
179
+			$related_models_query_params[0]['Event.EVT_ID'] = $value_to_equal;
180
+			$related_through_reg_query_params[0]['Registration.EVT_ID'] = $value_to_equal;
181
+			$datetime_ticket_query_params[0]['Datetime.EVT_ID'] = $value_to_equal;
182
+			$price_query_params[0]['Ticket.Datetime.EVT_ID'] = $value_to_equal;
183
+			$price_type_query_params[0]['Price.Ticket.Datetime.EVT_ID'] = $value_to_equal;
184
+			$term_query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $value_to_equal;
185
+			$state_country_query_params[0]['Venue.Event.EVT_ID'] = $value_to_equal;
186
+			$question_group_query_params[0]['Event.EVT_ID'] = $value_to_equal;
187
+			$question_query_params[0]['Question_Group.Event.EVT_ID'] = $value_to_equal;
188
+		} else {
189
+			$filename = 'all-events';
190
+		}
191
+
192
+
193
+		// array in the format:  table name =>  query where clause
194
+		$models_to_export = array(
195
+			'Event'                   => $event_query_params,
196
+			'Datetime'                => $related_models_query_params,
197
+			'Ticket_Template'         => $price_query_params,
198
+			'Ticket'                  => $datetime_ticket_query_params,
199
+			'Datetime_Ticket'         => $datetime_ticket_query_params,
200
+			'Price_Type'              => $price_type_query_params,
201
+			'Price'                   => $price_query_params,
202
+			'Ticket_Price'            => $price_query_params,
203
+			'Term'                    => $term_query_params,
204
+			'Term_Taxonomy'           => $related_models_query_params,
205
+			'Term_Relationship'       => $related_models_query_params, // model has NO primary key...
206
+			'Country'                 => $state_country_query_params,
207
+			'State'                   => $state_country_query_params,
208
+			'Venue'                   => $related_models_query_params,
209
+			'Event_Venue'             => $related_models_query_params,
210
+			'Question_Group'          => $question_group_query_params,
211
+			'Event_Question_Group'    => $question_group_query_params,
212
+			'Question'                => $question_query_params,
213
+			'Question_Group_Question' => $question_query_params,
214
+			// 'Transaction'=>$related_through_reg_query_params,
215
+			// 'Registration'=>$related_models_query_params,
216
+			// 'Attendee'=>$related_through_reg_query_params,
217
+			// 'Line_Item'=>
218
+
219
+		);
220
+
221
+		$model_data = $this->_get_export_data_for_models($models_to_export);
222
+
223
+		$filename = $this->generate_filename($filename);
224
+
225
+		if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $model_data)) {
226
+			EE_Error::add_error(
227
+				esc_html__(
228
+					"'An error occurred and the Event details could not be exported from the database.'",
229
+					"event_espresso"
230
+				),
231
+				__FILE__,
232
+				__FUNCTION__,
233
+				__LINE__
234
+			);
235
+		}
236
+	}
237
+
238
+	public function report_attendees()
239
+	{
240
+		$attendee_rows = EEM_Attendee::instance()->get_all_wpdb_results(
241
+			array(
242
+				'force_join' => array('State', 'Country'),
243
+				'caps'       => EEM_Base::caps_read_admin,
244
+			)
245
+		);
246
+		$csv_data = array();
247
+		foreach ($attendee_rows as $attendee_row) {
248
+			$csv_row = array();
249
+			foreach (EEM_Attendee::instance()->field_settings() as $field_name => $field_obj) {
250
+				if ($field_name == 'STA_ID') {
251
+					$state_name_field = EEM_State::instance()->field_settings_for('STA_name');
252
+					$csv_row[ esc_html__('State', 'event_espresso') ] = $attendee_row[ $state_name_field->get_qualified_column(
253
+					) ];
254
+				} elseif ($field_name == 'CNT_ISO') {
255
+					$country_name_field = EEM_Country::instance()->field_settings_for('CNT_name');
256
+					$csv_row[ esc_html__(
257
+						'Country',
258
+						'event_espresso'
259
+					) ] = $attendee_row[ $country_name_field->get_qualified_column() ];
260
+				} else {
261
+					$csv_row[ $field_obj->get_nicename() ] = $attendee_row[ $field_obj->get_qualified_column() ];
262
+				}
263
+			}
264
+			$csv_data[] = $csv_row;
265
+		}
266
+
267
+		$filename = $this->generate_filename('contact-list-report');
268
+
269
+		$handle = $this->EE_CSV->begin_sending_csv($filename);
270
+		$this->EE_CSV->write_data_array_to_csv($handle, $csv_data);
271
+		$this->EE_CSV->end_sending_csv($handle);
272
+	}
273
+
274
+
275
+	/**
276
+	 * @Export data for ALL attendees
277
+	 * @access public
278
+	 * @return void
279
+	 * @throws EE_Error
280
+	 */
281
+	public function export_attendees()
282
+	{
283
+
284
+		$states_that_have_an_attendee = EEM_State::instance()->get_all(
285
+			array(0 => array('Attendee.ATT_ID' => array('IS NOT NULL')))
286
+		);
287
+		$countries_that_have_an_attendee = EEM_Country::instance()->get_all(
288
+			array(0 => array('Attendee.ATT_ID' => array('IS NOT NULL')))
289
+		);
290
+		// $states_to_export_query_params
291
+		$models_to_export = array(
292
+			'Country'  => array(array('CNT_ISO' => array('IN', array_keys($countries_that_have_an_attendee)))),
293
+			'State'    => array(array('STA_ID' => array('IN', array_keys($states_that_have_an_attendee)))),
294
+			'Attendee' => array(),
295
+		);
296
+
297
+
298
+		$model_data = $this->_get_export_data_for_models($models_to_export);
299
+		$filename = $this->generate_filename('all-attendees');
300
+
301
+		if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $model_data)) {
302
+			EE_Error::add_error(
303
+				esc_html__(
304
+					'An error occurred and the Attendee data could not be exported from the database.',
305
+					'event_espresso'
306
+				),
307
+				__FILE__,
308
+				__FUNCTION__,
309
+				__LINE__
310
+			);
311
+		}
312
+	}
313
+
314
+	/**
315
+	 * Shortcut for preparing a database result for display
316
+	 *
317
+	 * @param EEM_Base $model
318
+	 * @param string $field_name
319
+	 * @param string $raw_db_value
320
+	 * @param bool|string $pretty_schema true to display pretty, a string to use a specific "Schema", or false to
321
+	 *                                      NOT display pretty
322
+	 * @return string
323
+	 * @throws EE_Error
324
+	 */
325
+	protected function _prepare_value_from_db_for_display($model, $field_name, $raw_db_value, $pretty_schema = true)
326
+	{
327
+		$field_obj = $model->field_settings_for($field_name);
328
+		$value_on_model_obj = $field_obj->prepare_for_set_from_db($raw_db_value);
329
+		if ($field_obj instanceof EE_Datetime_Field) {
330
+			$field_obj->set_date_format(
331
+				EE_CSV::instance()->get_date_format_for_csv($field_obj->get_date_format($pretty_schema)),
332
+				$pretty_schema
333
+			);
334
+			$field_obj->set_time_format(
335
+				EE_CSV::instance()->get_time_format_for_csv($field_obj->get_time_format($pretty_schema)),
336
+				$pretty_schema
337
+			);
338
+		}
339
+		if ($pretty_schema === true) {
340
+			return $field_obj->prepare_for_pretty_echoing($value_on_model_obj);
341
+		} elseif (is_string($pretty_schema)) {
342
+			return $field_obj->prepare_for_pretty_echoing($value_on_model_obj, $pretty_schema);
343
+		} else {
344
+			return $field_obj->prepare_for_get($value_on_model_obj);
345
+		}
346
+	}
347
+
348
+	/**
349
+	 * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price
350
+	 * name, and the questions associated with the registrations
351
+	 *
352
+	 * @param int $event_id
353
+	 * @throws EE_Error
354
+	 * @throws ReflectionException
355
+	 */
356
+	public function report_registrations_for_event($event_id = null)
357
+	{
358
+		$reg_fields_to_include = array(
359
+			'TXN_ID',
360
+			'ATT_ID',
361
+			'REG_ID',
362
+			'REG_date',
363
+			'REG_code',
364
+			'REG_count',
365
+			'REG_final_price',
366
+
367
+		);
368
+		$att_fields_to_include = array(
369
+			'ATT_fname',
370
+			'ATT_lname',
371
+			'ATT_email',
372
+			'ATT_address',
373
+			'ATT_address2',
374
+			'ATT_city',
375
+			'STA_ID',
376
+			'CNT_ISO',
377
+			'ATT_zip',
378
+			'ATT_phone',
379
+		);
380
+
381
+		$registrations_csv_ready_array = array();
382
+		$reg_model = EE_Registry::instance()->load_model('Registration');
383
+		$query_params = apply_filters(
384
+			'FHEE__EE_Export__report_registration_for_event',
385
+			array(
386
+				array(
387
+					'OR'                 => array(
388
+						// don't include registrations from failed or abandoned transactions...
389
+						'Transaction.STS_ID' => array(
390
+							'NOT IN',
391
+							array(EEM_Transaction::failed_status_code, EEM_Transaction::abandoned_status_code),
392
+						),
393
+						// unless the registration is approved, in which case include it regardless of transaction status
394
+						'STS_ID'             => RegStatus::APPROVED,
395
+					),
396
+					'Ticket.TKT_deleted' => array('IN', array(true, false)),
397
+				),
398
+				'order_by'   => array('Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'),
399
+				'force_join' => array('Transaction', 'Ticket', 'Attendee'),
400
+				'caps'       => EEM_Base::caps_read_admin,
401
+			),
402
+			$event_id
403
+		);
404
+		if ($event_id) {
405
+			$query_params[0]['EVT_ID'] = $event_id;
406
+		} else {
407
+			$query_params['force_join'][] = 'Event';
408
+		}
409
+		$registration_rows = $reg_model->get_all_wpdb_results($query_params);
410
+		// get all questions which relate to someone in this group
411
+		$registration_ids = array();
412
+		foreach ($registration_rows as $reg_row) {
413
+			$registration_ids[] = intval($reg_row['Registration.REG_ID']);
414
+		}
415
+		// EEM_Question::instance()->show_next_x_db_queries();
416
+		$questions_for_these_regs_rows = EEM_Question::instance()->get_all_wpdb_results(
417
+			array(array('Answer.REG_ID' => array('IN', $registration_ids)))
418
+		);
419
+		foreach ($registration_rows as $reg_row) {
420
+			if (is_array($reg_row)) {
421
+				$reg_csv_array = array();
422
+				if (! $event_id) {
423
+					// get the event's name and Id
424
+					$reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
425
+						esc_html__('%1$s (%2$s)', 'event_espresso'),
426
+						$this->_prepare_value_from_db_for_display(
427
+							EEM_Event::instance(),
428
+							'EVT_name',
429
+							$reg_row['Event_CPT.post_title']
430
+						),
431
+						$reg_row['Event_CPT.ID']
432
+					);
433
+				}
434
+				$is_primary_reg = $reg_row['Registration.REG_count'] == '1' ? true : false;
435
+				/*@var $reg_row EE_Registration */
436
+				foreach ($reg_fields_to_include as $field_name) {
437
+					$field = $reg_model->field_settings_for($field_name);
438
+					if ($field_name == 'REG_final_price') {
439
+						$value = $this->_prepare_value_from_db_for_display(
440
+							$reg_model,
441
+							$field_name,
442
+							$reg_row['Registration.REG_final_price'],
443
+							'localized_float'
444
+						);
445
+					} elseif ($field_name == 'REG_count') {
446
+						$value = sprintf(
447
+							esc_html__('%s of %s', 'event_espresso'),
448
+							$this->_prepare_value_from_db_for_display(
449
+								$reg_model,
450
+								'REG_count',
451
+								$reg_row['Registration.REG_count']
452
+							),
453
+							$this->_prepare_value_from_db_for_display(
454
+								$reg_model,
455
+								'REG_group_size',
456
+								$reg_row['Registration.REG_group_size']
457
+							)
458
+						);
459
+					} elseif ($field_name == 'REG_date') {
460
+						$value = $this->_prepare_value_from_db_for_display(
461
+							$reg_model,
462
+							$field_name,
463
+							$reg_row['Registration.REG_date'],
464
+							'no_html'
465
+						);
466
+					} else {
467
+						$value = $this->_prepare_value_from_db_for_display(
468
+							$reg_model,
469
+							$field_name,
470
+							$reg_row[ $field->get_qualified_column() ]
471
+						);
472
+					}
473
+					$reg_csv_array[ $this->_get_column_name_for_field($field) ] = $value;
474
+					if ($field_name == 'REG_final_price') {
475
+						// add a column named Currency after the final price
476
+						$reg_csv_array[ esc_html__("Currency", "event_espresso") ] = EE_Config::instance()->currency->code;
477
+					}
478
+				}
479
+				// get pretty status
480
+				$stati = EEM_Status::instance()->localized_status(
481
+					array(
482
+						$reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
483
+						$reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
484
+					),
485
+					false,
486
+					'sentence'
487
+				);
488
+				$reg_csv_array[ esc_html__(
489
+					"Registration Status",
490
+					'event_espresso'
491
+				) ] = $stati[ $reg_row['Registration.STS_ID'] ];
492
+				// get pretty trnasaction status
493
+				$reg_csv_array[ esc_html__(
494
+					"Transaction Status",
495
+					'event_espresso'
496
+				) ] = $stati[ $reg_row['TransactionTable.STS_ID'] ];
497
+				$reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
498
+					? $this->_prepare_value_from_db_for_display(
499
+						EEM_Transaction::instance(),
500
+						'TXN_total',
501
+						$reg_row['TransactionTable.TXN_total'],
502
+						'localized_float'
503
+					) : '0.00';
504
+				$reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ] = $is_primary_reg
505
+					? $this->_prepare_value_from_db_for_display(
506
+						EEM_Transaction::instance(),
507
+						'TXN_paid',
508
+						$reg_row['TransactionTable.TXN_paid'],
509
+						'localized_float'
510
+					) : '0.00';
511
+				$payment_methods = array();
512
+				$gateway_txn_ids_etc = array();
513
+				$payment_times = array();
514
+				if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
515
+					$payments_info = EEM_Payment::instance()->get_all_wpdb_results(
516
+						array(
517
+							array(
518
+								'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
519
+								'STS_ID' => EEM_Payment::status_id_approved,
520
+							),
521
+							'force_join' => array('Payment_Method'),
522
+						),
523
+						ARRAY_A,
524
+						'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
525
+					);
526
+					list($payment_methods, $gateway_txn_ids_etc, $payment_times) = PaymentsInfoCSV::extractPaymentInfo($payments_info);
527
+				}
528
+				$reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(',', $payment_times);
529
+				$reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(",", $payment_methods);
530
+				$reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
531
+					',',
532
+					$gateway_txn_ids_etc
533
+				);
534
+
535
+				// get whether or not the user has checked in
536
+				$reg_csv_array[ esc_html__("Check-Ins", "event_espresso") ] = $reg_model->count_related(
537
+					$reg_row['Registration.REG_ID'],
538
+					'Checkin'
539
+				);
540
+				// get ticket of registration and its price
541
+				$ticket_model = EE_Registry::instance()->load_model('Ticket');
542
+				if ($reg_row['Ticket.TKT_ID']) {
543
+					$ticket_name = $this->_prepare_value_from_db_for_display(
544
+						$ticket_model,
545
+						'TKT_name',
546
+						$reg_row['Ticket.TKT_name']
547
+					);
548
+					$datetimes_strings = array();
549
+					foreach (
550
+						EEM_Datetime::instance()->get_all_wpdb_results(
551
+							array(
552
+							array('Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']),
553
+							'order_by'                 => array('DTT_EVT_start' => 'ASC'),
554
+							'default_where_conditions' => 'none',
555
+							)
556
+						) as $datetime
557
+					) {
558
+						$datetimes_strings[] = $this->_prepare_value_from_db_for_display(
559
+							EEM_Datetime::instance(),
560
+							'DTT_EVT_start',
561
+							$datetime['Datetime.DTT_EVT_start']
562
+						);
563
+					}
564
+				} else {
565
+					$ticket_name = esc_html__('Unknown', 'event_espresso');
566
+					$datetimes_strings = array(esc_html__('Unknown', 'event_espresso'));
567
+				}
568
+				$reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
569
+				$reg_csv_array[ esc_html__("Datetimes of Ticket", "event_espresso") ] = implode(", ", $datetimes_strings);
570
+				// get datetime(s) of registration
571
+
572
+				// add attendee columns
573
+				foreach ($att_fields_to_include as $att_field_name) {
574
+					$field_obj = EEM_Attendee::instance()->field_settings_for($att_field_name);
575
+					if ($reg_row['Attendee_CPT.ID']) {
576
+						if ($att_field_name == 'STA_ID') {
577
+							$value = EEM_State::instance()->get_var(
578
+								array(array('STA_ID' => $reg_row['Attendee_Meta.STA_ID'])),
579
+								'STA_name'
580
+							);
581
+						} elseif ($att_field_name == 'CNT_ISO') {
582
+							$value = EEM_Country::instance()->get_var(
583
+								array(array('CNT_ISO' => $reg_row['Attendee_Meta.CNT_ISO'])),
584
+								'CNT_name'
585
+							);
586
+						} else {
587
+							$value = $this->_prepare_value_from_db_for_display(
588
+								EEM_Attendee::instance(),
589
+								$att_field_name,
590
+								$reg_row[ $field_obj->get_qualified_column() ]
591
+							);
592
+						}
593
+					} else {
594
+						$value = '';
595
+					}
596
+
597
+					$reg_csv_array[ $this->_get_column_name_for_field($field_obj) ] = $value;
598
+				}
599
+
600
+				// make sure each registration has the same questions in the same order
601
+				foreach ($questions_for_these_regs_rows as $question_row) {
602
+					if (! isset($reg_csv_array[ $question_row['Question.QST_admin_label'] ])) {
603
+						$reg_csv_array[ $question_row['Question.QST_admin_label'] ] = null;
604
+					}
605
+				}
606
+				// now fill out the questions THEY answered
607
+				foreach (
608
+					EEM_Answer::instance()->get_all_wpdb_results(
609
+						array(array('REG_ID' => $reg_row['Registration.REG_ID']), 'force_join' => array('Question'))
610
+					) as $answer_row
611
+				) {
612
+					/* @var $answer EE_Answer */
613
+					if ($answer_row['Question.QST_ID']) {
614
+						$question_label = $this->_prepare_value_from_db_for_display(
615
+							EEM_Question::instance(),
616
+							'QST_admin_label',
617
+							$answer_row['Question.QST_admin_label']
618
+						);
619
+					} else {
620
+						$question_label = sprintf(esc_html__('Question $s', 'event_espresso'), $answer_row['Answer.QST_ID']);
621
+					}
622
+					if (isset($answer_row['Question.QST_type']) && $answer_row['Question.QST_type'] == EEM_Question::QST_type_state) {
623
+						$reg_csv_array[ $question_label ] = EEM_State::instance()->get_state_name_by_ID(
624
+							$answer_row['Answer.ANS_value']
625
+						);
626
+					} else {
627
+						$reg_csv_array[ $question_label ] = $this->_prepare_value_from_db_for_display(
628
+							EEM_Answer::instance(),
629
+							'ANS_value',
630
+							$answer_row['Answer.ANS_value']
631
+						);
632
+					}
633
+				}
634
+				$registrations_csv_ready_array[] = apply_filters(
635
+					'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
636
+					$reg_csv_array,
637
+					$reg_row
638
+				);
639
+			}
640
+		}
641
+
642
+		// if we couldn't export anything, we want to at least show the column headers
643
+		if (empty($registrations_csv_ready_array)) {
644
+			$reg_csv_array = array();
645
+			$model_and_fields_to_include = array(
646
+				'Registration' => $reg_fields_to_include,
647
+				'Attendee'     => $att_fields_to_include,
648
+			);
649
+			foreach ($model_and_fields_to_include as $model_name => $field_list) {
650
+				$model = EE_Registry::instance()->load_model($model_name);
651
+				foreach ($field_list as $field_name) {
652
+					$field = $model->field_settings_for($field_name);
653
+					$reg_csv_array[ $this->_get_column_name_for_field(
654
+						$field
655
+					) ] = null;// $registration->get($field->get_name());
656
+				}
657
+			}
658
+			$registrations_csv_ready_array [] = $reg_csv_array;
659
+		}
660
+		if ($event_id) {
661
+			$event_slug = EEM_Event::instance()->get_var(array(array('EVT_ID' => $event_id)), 'EVT_slug');
662
+			if (! $event_slug) {
663
+				$event_slug = esc_html__('unknown', 'event_espresso');
664
+			}
665
+		} else {
666
+			$event_slug = esc_html__('all', 'event_espresso');
667
+		}
668
+		$filename = sprintf("registrations-for-%s", $event_slug);
669
+
670
+		$handle = $this->EE_CSV->begin_sending_csv($filename);
671
+		$this->EE_CSV->write_data_array_to_csv($handle, $registrations_csv_ready_array);
672
+		$this->EE_CSV->end_sending_csv($handle);
673
+	}
674
+
675
+	/**
676
+	 * Gets the 'normal' column named for fields
677
+	 *
678
+	 * @param EE_Model_Field_Base $field
679
+	 * @return string
680
+	 * @throws EE_Error
681
+	 */
682
+	protected function _get_column_name_for_field(EE_Model_Field_Base $field)
683
+	{
684
+		return $field->get_nicename() . "[" . $field->get_name() . "]";
685
+	}
686
+
687
+
688
+	/**
689
+	 * @Export data for ALL events
690
+	 * @access public
691
+	 * @return void
692
+	 */
693
+	public function export_categories()
694
+	{
695
+		// are any Event IDs set?
696
+		$query_params = array();
697
+		if (isset($this->_req_data['EVT_CAT_ID'])) {
698
+			// do we have an array of IDs ?
699
+			if (is_array($this->_req_data['EVT_CAT_ID'])) {
700
+				// generate an "IN (CSV)" where clause
701
+				$EVT_CAT_IDs = array_map('sanitize_text_field', $this->_req_data['EVT_CAT_ID']);
702
+				$filename = 'event-categories';
703
+				$query_params[0]['term_taxonomy_id'] = array('IN', $EVT_CAT_IDs);
704
+			} else {
705
+				// generate regular where = clause
706
+				$EVT_CAT_ID = absint($this->_req_data['EVT_CAT_ID']);
707
+				$filename = 'event-category#' . $EVT_CAT_ID;
708
+				$query_params[0]['term_taxonomy_id'] = $EVT_CAT_ID;
709
+			}
710
+		} else {
711
+			// no IDs means we will d/l the entire table
712
+			$filename = 'all-categories';
713
+		}
714
+
715
+		$tables_to_export = array(
716
+			'Term_Taxonomy' => $query_params,
717
+		);
718
+
719
+		$table_data = $this->_get_export_data_for_models($tables_to_export);
720
+		$filename = $this->generate_filename($filename);
721
+
722
+		if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $table_data)) {
723
+			EE_Error::add_error(
724
+				esc_html__(
725
+					'An error occurred and the Category details could not be exported from the database.',
726
+					'event_espresso'
727
+				),
728
+				__FILE__,
729
+				__FUNCTION__,
730
+				__LINE__
731
+			);
732
+		}
733
+	}
734
+
735
+
736
+	/**
737
+	 * @process export name to create a suitable filename
738
+	 * @access  private
739
+	 * @param string - export_name
740
+	 * @return string on success, FALSE on fail
741
+	 */
742
+	private function generate_filename($export_name = '')
743
+	{
744
+		if ($export_name != '') {
745
+			$filename = get_bloginfo('name') . '-' . $export_name;
746
+			$filename = sanitize_key($filename) . '-' . $this->today;
747
+			return $filename;
748
+		} else {
749
+			EE_Error::add_error(esc_html__("No filename was provided", "event_espresso"), __FILE__, __FUNCTION__, __LINE__);
750
+		}
751
+		return false;
752
+	}
753
+
754
+
755
+	/**
756
+	 * @recursive function for exporting table data and merging the results with the next results
757
+	 * @access    private
758
+	 * @param array $models_to_export keys are model names (eg 'Event', 'Attendee', etc.) and values are arrays of
759
+	 *                                query params @return bool on success, FALSE on fail
760
+	 * @throws EE_Error
761
+	 * @throws ReflectionException
762
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
763
+	 */
764
+	private function _get_export_data_for_models($models_to_export = array())
765
+	{
766
+		$table_data = false;
767
+		if (is_array($models_to_export)) {
768
+			foreach ($models_to_export as $model_name => $query_params) {
769
+				// check for a numerically-indexed array. in that case, $model_name is the value!!
770
+				if (is_int($model_name)) {
771
+					$model_name = $query_params;
772
+					$query_params = array();
773
+				}
774
+				$model = EE_Registry::instance()->load_model($model_name);
775
+				$model_objects = $model->get_all($query_params);
776
+
777
+				$table_data[ $model_name ] = array();
778
+				foreach ($model_objects as $model_object) {
779
+					$model_data_array = array();
780
+					$fields = $model->field_settings();
781
+					foreach ($fields as $field) {
782
+						$column_name = $field->get_nicename() . "[" . $field->get_name() . "]";
783
+						if ($field instanceof EE_Datetime_Field) {
784
+							// $field->set_date_format('Y-m-d');
785
+							// $field->set_time_format('H:i:s');
786
+							$model_data_array[ $column_name ] = $model_object->get_datetime(
787
+								$field->get_name(),
788
+								'Y-m-d',
789
+								'H:i:s'
790
+							);
791
+						} else {
792
+							$model_data_array[ $column_name ] = $model_object->get($field->get_name());
793
+						}
794
+					}
795
+					$table_data[ $model_name ][] = $model_data_array;
796
+				}
797
+			}
798
+		}
799
+		return $table_data;
800
+	}
801 801
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Question.class.php 1 patch
Indentation   +667 added lines, -667 removed lines patch added patch discarded remove patch
@@ -12,671 +12,671 @@
 block discarded – undo
12 12
  */
13 13
 class EE_Question extends EE_Soft_Delete_Base_Class implements EEI_Duplicatable
14 14
 {
15
-    /**
16
-     * @param array  $props_n_values          incoming values
17
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
18
-     *                                        used.)
19
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
20
-     *                                        date_format and the second value is the time format
21
-     * @return EE_Question
22
-     */
23
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
24
-    {
25
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
26
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
27
-    }
28
-
29
-
30
-    /**
31
-     * @param array  $props_n_values  incoming values from the database
32
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
33
-     *                                the website will be used.
34
-     * @return EE_Question
35
-     */
36
-    public static function new_instance_from_db($props_n_values = [], $timezone = '')
37
-    {
38
-        return new self($props_n_values, true, $timezone);
39
-    }
40
-
41
-
42
-    /**
43
-     *        Set    Question display text
44
-     *
45
-     * @access        public
46
-     * @param string $QST_display_text
47
-     */
48
-    public function set_display_text($QST_display_text = '')
49
-    {
50
-        $this->set('QST_display_text', $QST_display_text);
51
-    }
52
-
53
-
54
-    /**
55
-     *        Set    Question admin text
56
-     *
57
-     * @access        public
58
-     * @param string $QST_admin_label
59
-     */
60
-    public function set_admin_label($QST_admin_label = '')
61
-    {
62
-        $this->set('QST_admin_label', $QST_admin_label);
63
-    }
64
-
65
-
66
-    /**
67
-     *        Set    system name
68
-     *
69
-     * @access        public
70
-     * @param mixed $QST_system
71
-     */
72
-    public function set_system_ID($QST_system = '')
73
-    {
74
-        $this->set('QST_system', $QST_system);
75
-    }
76
-
77
-
78
-    /**
79
-     *        Set    question's type
80
-     *
81
-     * @access        public
82
-     * @param string $QST_type
83
-     */
84
-    public function set_question_type($QST_type = '')
85
-    {
86
-        $this->set('QST_type', $QST_type);
87
-    }
88
-
89
-
90
-    /**
91
-     *        Sets whether this question must be answered when presented in a form
92
-     *
93
-     * @access        public
94
-     * @param bool $QST_required
95
-     */
96
-    public function set_required($QST_required = false)
97
-    {
98
-        $this->set('QST_required', $QST_required);
99
-    }
100
-
101
-
102
-    /**
103
-     *        Set    Question display text
104
-     *
105
-     * @access        public
106
-     * @param string $QST_required_text
107
-     */
108
-    public function set_required_text($QST_required_text = '')
109
-    {
110
-        $this->set('QST_required_text', $QST_required_text);
111
-    }
112
-
113
-
114
-    /**
115
-     *        Sets the order of this question when placed in a sequence of questions
116
-     *
117
-     * @access        public
118
-     * @param int $QST_order
119
-     */
120
-    public function set_order($QST_order = 0)
121
-    {
122
-        $this->set('QST_order', $QST_order);
123
-    }
124
-
125
-
126
-    /**
127
-     *        Sets whether the question is admin-only
128
-     *
129
-     * @access        public
130
-     * @param bool $QST_admin_only
131
-     */
132
-    public function set_admin_only($QST_admin_only = false)
133
-    {
134
-        $this->set('QST_admin_only', $QST_admin_only);
135
-    }
136
-
137
-
138
-    /**
139
-     *        Sets the wordpress user ID on the question
140
-     *
141
-     * @access        public
142
-     * @param int $QST_wp_user
143
-     */
144
-    public function set_wp_user($QST_wp_user = 1)
145
-    {
146
-        $this->set('QST_wp_user', $QST_wp_user);
147
-    }
148
-
149
-
150
-    /**
151
-     *        Sets whether the question has been deleted
152
-     *        (we use this boolean instead of actually
153
-     *        deleting it because when users delete this question
154
-     *        they really want to remove the question from future
155
-     *        forms, BUT keep their old answers which depend
156
-     *        on this record actually existing.
157
-     *
158
-     * @access        public
159
-     * @param bool $QST_deleted
160
-     */
161
-    public function set_deleted($QST_deleted = false)
162
-    {
163
-        $this->set('QST_deleted', $QST_deleted);
164
-    }
165
-
166
-
167
-    /**
168
-     * returns the text for displaying the question to users
169
-     *
170
-     * @access public
171
-     * @return string
172
-     */
173
-    public function display_text()
174
-    {
175
-        return $this->get('QST_display_text');
176
-    }
177
-
178
-
179
-    /**
180
-     * returns the text for the administrative label
181
-     *
182
-     * @access public
183
-     * @return string
184
-     */
185
-    public function admin_label()
186
-    {
187
-        return $this->get('QST_admin_label');
188
-    }
189
-
190
-
191
-    /**
192
-     * returns the attendee column name for this question
193
-     *
194
-     * @access public
195
-     * @return string
196
-     */
197
-    public function system_ID()
198
-    {
199
-        return $this->get('QST_system');
200
-    }
201
-
202
-
203
-    /**
204
-     * returns either a string of 'text', 'textfield', etc.
205
-     *
206
-     * @access public
207
-     * @return boolean
208
-     */
209
-    public function required()
210
-    {
211
-        return $this->get('QST_required');
212
-    }
213
-
214
-
215
-    /**
216
-     * returns the text which should be displayed when a user
217
-     * doesn't answer this question in a form
218
-     *
219
-     * @access public
220
-     * @return string
221
-     */
222
-    public function required_text()
223
-    {
224
-        return $this->get('QST_required_text');
225
-    }
226
-
227
-
228
-    /**
229
-     * returns the type of this question
230
-     *
231
-     * @access public
232
-     * @return string
233
-     */
234
-    public function type()
235
-    {
236
-        return $this->get('QST_type');
237
-    }
238
-
239
-
240
-    /**
241
-     * returns an integer showing where this question should
242
-     * be placed in a sequence of questions
243
-     *
244
-     * @access public
245
-     * @return int
246
-     */
247
-    public function order()
248
-    {
249
-        return $this->get('QST_order');
250
-    }
251
-
252
-
253
-    /**
254
-     * returns whether this question should only appears to admins,
255
-     * or to everyone
256
-     *
257
-     * @access public
258
-     * @return boolean
259
-     */
260
-    public function admin_only()
261
-    {
262
-        return $this->get('QST_admin_only');
263
-    }
264
-
265
-
266
-    /**
267
-     * returns the id the wordpress user who created this question
268
-     *
269
-     * @access public
270
-     * @return int
271
-     */
272
-    public function wp_user()
273
-    {
274
-        return $this->get('QST_wp_user');
275
-    }
276
-
277
-
278
-    /**
279
-     * returns whether this question has been marked as 'deleted'
280
-     *
281
-     * @access public
282
-     * @return boolean
283
-     */
284
-    public function deleted()
285
-    {
286
-        return $this->get('QST_deleted');
287
-    }
288
-
289
-
290
-    /**
291
-     * Gets an array of related EE_Answer  to this EE_Question
292
-     *
293
-     * @return EE_Answer[]
294
-     */
295
-    public function answers()
296
-    {
297
-        return $this->get_many_related('Answer');
298
-    }
299
-
300
-
301
-    /**
302
-     * Boolean check for if there are answers on this question in th db
303
-     *
304
-     * @return boolean true = has answers, false = no answers.
305
-     */
306
-    public function has_answers()
307
-    {
308
-        return $this->count_related('Answer') > 0 ? true : false;
309
-    }
310
-
311
-
312
-    /**
313
-     * gets an array of EE_Question_Group which relate to this question
314
-     *
315
-     * @return EE_Question_Group[]
316
-     */
317
-    public function question_groups()
318
-    {
319
-        return $this->get_many_related('Question_Group');
320
-    }
321
-
322
-
323
-    /**
324
-     * Returns all the options for this question. By default, it returns only the not-yet-deleted ones.
325
-     *
326
-     * @param boolean      $notDeletedOptionsOnly            1
327
-     *                                                       whether to return ALL options, or only the ones which have
328
-     *                                                       not yet been deleleted
329
-     * @param string|array $selected_value_to_always_include , when retrieving options to an ANSWERED question,
330
-     *                                                       we want to usually only show non-deleted options AND the
331
-     *                                                       value that was selected for the answer, whether it was
332
-     *                                                       trashed or not.
333
-     * @return EE_Question_Option[]
334
-     */
335
-    public function options($notDeletedOptionsOnly = true, $selected_value_to_always_include = null)
336
-    {
337
-        if (! $this->ID()) {
338
-            return [];
339
-        }
340
-        $query_params = [];
341
-        if ($selected_value_to_always_include) {
342
-            if (is_array($selected_value_to_always_include)) {
343
-                $query_params[0]['OR*options-query']['QSO_value'] = ['IN', $selected_value_to_always_include];
344
-            } else {
345
-                $query_params[0]['OR*options-query']['QSO_value'] = $selected_value_to_always_include;
346
-            }
347
-        }
348
-        if ($notDeletedOptionsOnly) {
349
-            $query_params[0]['OR*options-query']['QSO_deleted'] = false;
350
-        }
351
-        // order by QSO_order
352
-        $query_params['order_by'] = ['QSO_order' => 'ASC'];
353
-        return $this->get_many_related('Question_Option', $query_params);
354
-    }
355
-
356
-
357
-    /**
358
-     * returns an array of EE_Question_Options which relate to this question
359
-     *
360
-     * @return \EE_Question_Option[]
361
-     */
362
-    public function temp_options()
363
-    {
364
-        return $this->_model_relations['Question_Option'];
365
-    }
366
-
367
-
368
-    /**
369
-     * Adds an option for this question. Note: if the option were previously associated with a different
370
-     * Question, that relationship will be overwritten.
371
-     *
372
-     * @param EE_Question_Option $option
373
-     * @return boolean success
374
-     */
375
-    public function add_option(EE_Question_Option $option)
376
-    {
377
-        return $this->_add_relation_to($option, 'Question_Option');
378
-    }
379
-
380
-
381
-    /**
382
-     * Adds an option directly to this question without saving to the db
383
-     *
384
-     * @param EE_Question_Option $option
385
-     * @return boolean success
386
-     */
387
-    public function add_temp_option(EE_Question_Option $option)
388
-    {
389
-        $this->_model_relations['Question_Option'][] = $option;
390
-        return true;
391
-    }
392
-
393
-
394
-    /**
395
-     * Marks the option as deleted.
396
-     *
397
-     * @param EE_Question_Option $option
398
-     * @return boolean success
399
-     */
400
-    public function remove_option(EE_Question_Option $option)
401
-    {
402
-        return $this->_remove_relation_to($option, 'Question_Option');
403
-    }
404
-
405
-
406
-    /**
407
-     * @return bool
408
-     */
409
-    public function is_system_question()
410
-    {
411
-        $system_ID = $this->get('QST_system');
412
-        return ! empty($system_ID) ? true : false;
413
-    }
414
-
415
-
416
-    /**
417
-     * The purpose of this method is set the question order this question order to be the max out of all questions
418
-     *
419
-     * @access public
420
-     * @return void
421
-     */
422
-    public function set_order_to_latest()
423
-    {
424
-        $latest_order = $this->get_model()->get_latest_question_order();
425
-        $latest_order++;
426
-        $this->set('QST_order', $latest_order);
427
-    }
428
-
429
-
430
-    /**
431
-     * Retrieves the list of allowed question types from the model.
432
-     *
433
-     * @return string[]
434
-     */
435
-    private function _allowed_question_types()
436
-    {
437
-        $questionModel = $this->get_model();
438
-        /* @var $questionModel EEM_Question */
439
-        return $questionModel->allowed_question_types();
440
-    }
441
-
442
-
443
-    /**
444
-     * Duplicates this question and its question options
445
-     *
446
-     * @return \EE_Question
447
-     */
448
-    public function duplicate($options = [])
449
-    {
450
-        $new_question = clone $this;
451
-        $new_question->set('QST_ID', null);
452
-        $new_question->set_display_text(
453
-            sprintf(esc_html__('%s **Duplicate**', 'event_espresso'), $this->display_text())
454
-        );
455
-        $new_question->set_admin_label(sprintf(esc_html__('%s **Duplicate**', 'event_espresso'), $this->admin_label()));
456
-        $new_question->set_system_ID(null);
457
-        $new_question->set_wp_user(get_current_user_id());
458
-        // if we're duplicating a trashed question, assume we don't want the new one to be trashed
459
-        $new_question->set_deleted(false);
460
-        $success = $new_question->save();
461
-        if ($success) {
462
-            // we don't totally want to duplicate the question options, because we want them to be for the NEW question
463
-            foreach ($this->options() as $question_option) {
464
-                $question_option->duplicate(['QST_ID' => $new_question->ID()]);
465
-            }
466
-            return $new_question;
467
-        } else {
468
-            return null;
469
-        }
470
-    }
471
-
472
-
473
-    /**
474
-     * Returns the question's maximum allowed response size
475
-     *
476
-     * @return int|float
477
-     */
478
-    public function max()
479
-    {
480
-        return $this->get('QST_max');
481
-    }
482
-
483
-
484
-    /**
485
-     * Sets the question's maximum allowed response size
486
-     *
487
-     * @param int|float $new_max
488
-     * @return void
489
-     */
490
-    public function set_max($new_max)
491
-    {
492
-        $this->set('QST_max', $new_max);
493
-    }
494
-
495
-
496
-    /**
497
-     * Creates a form input from this question which can be used in HTML forms
498
-     *
499
-     * @param EE_Registration $registration
500
-     * @param EE_Answer       $answer
501
-     * @param array           $input_constructor_args
502
-     * @return EE_Form_Input_Base
503
-     */
504
-    public function generate_form_input($registration = null, $answer = null, $input_constructor_args = [])
505
-    {
506
-        $identifier = $this->is_system_question() ? $this->system_ID() : $this->ID();
507
-
508
-        $input_constructor_args = array_merge(
509
-            [
510
-                'required'                          => $this->required() ? true : false,
511
-                'html_label_text'                   => $this->display_text(),
512
-                'required_validation_error_message' => $this->required_text(),
513
-            ],
514
-            $input_constructor_args
515
-        );
516
-        if (! $answer instanceof EE_Answer && $registration instanceof EE_Registration) {
517
-            $answer = EEM_Answer::instance()->get_registration_question_answer_object($registration, $this->ID());
518
-        }
519
-        // has this question been answered ?
520
-        if (
521
-            $answer instanceof EE_Answer
522
-            && $answer->value() !== ''
523
-        ) {
524
-            // answer gets htmlspecialchars called on it, undo that please
525
-            // because the form input's display strategy may call esc_attr too
526
-            // which also does html special characters
527
-            $values_with_html_special_chars = $answer->value();
528
-            if (is_array($values_with_html_special_chars)) {
529
-                $default_value = array_map('htmlspecialchars_decode', $values_with_html_special_chars);
530
-            } else {
531
-                $default_value = htmlspecialchars_decode($values_with_html_special_chars);
532
-            }
533
-            $input_constructor_args['default'] = $default_value;
534
-        }
535
-        $max_max_for_question = EEM_Question::instance()->absolute_max_for_system_question($this->system_ID());
536
-        if (
537
-            in_array(
538
-                $this->type(),
539
-                EEM_Question::instance()->questionTypesWithMaxLength(),
540
-                true
541
-            )
542
-        ) {
543
-            $input_constructor_args['validation_strategies'][] = new EE_Max_Length_Validation_Strategy(
544
-                null,
545
-                min($max_max_for_question, $this->max())
546
-            );
547
-        }
548
-        $input_constructor_args = apply_filters(
549
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__input_constructor_args',
550
-            $input_constructor_args,
551
-            $registration,
552
-            $this,
553
-            $answer
554
-        );
555
-
556
-        $result = null;
557
-        switch ($this->type()) {
558
-            // Text
559
-            case EEM_Question::QST_type_text:
560
-                $result = new EE_Text_Input($input_constructor_args);
561
-                break;
562
-            // Textarea
563
-            case EEM_Question::QST_type_textarea:
564
-                $result = new EE_Text_Area_Input($input_constructor_args);
565
-                break;
566
-            // Radio Buttons
567
-            case EEM_Question::QST_type_radio:
568
-                $result = new EE_Radio_Button_Input($this->options(), $input_constructor_args);
569
-                break;
570
-            // Dropdown
571
-            case EEM_Question::QST_type_dropdown:
572
-                $result = new EE_Select_Input($this->options(), $input_constructor_args);
573
-                break;
574
-            // State Dropdown
575
-            case EEM_Question::QST_type_state:
576
-                $state_options = apply_filters(
577
-                    'FHEE__EE_Question__generate_form_input__state_options',
578
-                    null,
579
-                    $this,
580
-                    $registration,
581
-                    $answer
582
-                );
583
-                $result        = new EE_State_Select_Input($state_options, $input_constructor_args);
584
-                break;
585
-            // Country Dropdown
586
-            case EEM_Question::QST_type_country:
587
-                $country_options = apply_filters(
588
-                    'FHEE__EE_Question__generate_form_input__country_options',
589
-                    null,
590
-                    $this,
591
-                    $registration,
592
-                    $answer
593
-                );
594
-                $result          = new EE_Country_Select_Input($country_options, $input_constructor_args);
595
-                break;
596
-            // Checkboxes
597
-            case EEM_Question::QST_type_checkbox:
598
-                $result = new EE_Checkbox_Multi_Input($this->options(), $input_constructor_args);
599
-                break;
600
-            // Date
601
-            case EEM_Question::QST_type_date:
602
-                $result = new EE_Datepicker_Input($input_constructor_args);
603
-                break;
604
-            case EEM_Question::QST_type_html_textarea:
605
-                $input_constructor_args['validation_strategies'][] = new EE_Simple_HTML_Validation_Strategy();
606
-                $result                                            = new EE_Text_Area_Input($input_constructor_args);
607
-                $result->remove_validation_strategy('EE_Plaintext_Validation_Strategy');
608
-                break;
609
-            case EEM_Question::QST_type_email:
610
-                $result = new EE_Email_Input($input_constructor_args);
611
-                break;
612
-            // Email confirm
613
-            case EEM_Question::QST_type_email_confirm:
614
-                $result = new EE_Email_Confirm_Input($input_constructor_args);
615
-                break;
616
-            case EEM_Question::QST_type_us_phone:
617
-                $result = new EE_Phone_Input($input_constructor_args);
618
-                break;
619
-            case EEM_Question::QST_type_int:
620
-                $result = new EE_Integer_Input($input_constructor_args);
621
-                break;
622
-            case EEM_Question::QST_type_decimal:
623
-                $result = new EE_Float_Input($input_constructor_args);
624
-                break;
625
-            case EEM_Question::QST_type_url:
626
-                $result = new EE_URL_Input($input_constructor_args);
627
-                break;
628
-            case EEM_Question::QST_type_year:
629
-                $result = new EE_Year_Input(
630
-                    $input_constructor_args,
631
-                    apply_filters(
632
-                        'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__four_digit',
633
-                        true,
634
-                        $this
635
-                    ),
636
-                    apply_filters(
637
-                        'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__early_range',
638
-                        100,
639
-                        $this
640
-                    ),
641
-                    apply_filters(
642
-                        'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__end_range',
643
-                        100,
644
-                        $this
645
-                    )
646
-                );
647
-                break;
648
-            case EEM_Question::QST_type_multi_select:
649
-                $result = new EE_Select_Multiple_Input($this->options(), $input_constructor_args);
650
-                break;
651
-            // fallback
652
-            default:
653
-                $default_input = apply_filters(
654
-                    'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__default',
655
-                    null,
656
-                    $this->type(),
657
-                    $this,
658
-                    $input_constructor_args
659
-                );
660
-                if (! $default_input) {
661
-                    $default_input = new EE_Text_Input($input_constructor_args);
662
-                }
663
-                $result = $default_input;
664
-        }
665
-        return apply_filters('FHEE__EE_Question__generate_form_input__return', $result, $registration, $this, $answer);
666
-    }
667
-
668
-
669
-    /**
670
-     * Returns whether or not this question type should have question option entries
671
-     *
672
-     * @return bool
673
-     */
674
-    public function should_have_question_options()
675
-    {
676
-        return in_array(
677
-            $this->type(),
678
-            $this->_model->question_types_with_options(),
679
-            true
680
-        );
681
-    }
15
+	/**
16
+	 * @param array  $props_n_values          incoming values
17
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
18
+	 *                                        used.)
19
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
20
+	 *                                        date_format and the second value is the time format
21
+	 * @return EE_Question
22
+	 */
23
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
24
+	{
25
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
26
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
27
+	}
28
+
29
+
30
+	/**
31
+	 * @param array  $props_n_values  incoming values from the database
32
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
33
+	 *                                the website will be used.
34
+	 * @return EE_Question
35
+	 */
36
+	public static function new_instance_from_db($props_n_values = [], $timezone = '')
37
+	{
38
+		return new self($props_n_values, true, $timezone);
39
+	}
40
+
41
+
42
+	/**
43
+	 *        Set    Question display text
44
+	 *
45
+	 * @access        public
46
+	 * @param string $QST_display_text
47
+	 */
48
+	public function set_display_text($QST_display_text = '')
49
+	{
50
+		$this->set('QST_display_text', $QST_display_text);
51
+	}
52
+
53
+
54
+	/**
55
+	 *        Set    Question admin text
56
+	 *
57
+	 * @access        public
58
+	 * @param string $QST_admin_label
59
+	 */
60
+	public function set_admin_label($QST_admin_label = '')
61
+	{
62
+		$this->set('QST_admin_label', $QST_admin_label);
63
+	}
64
+
65
+
66
+	/**
67
+	 *        Set    system name
68
+	 *
69
+	 * @access        public
70
+	 * @param mixed $QST_system
71
+	 */
72
+	public function set_system_ID($QST_system = '')
73
+	{
74
+		$this->set('QST_system', $QST_system);
75
+	}
76
+
77
+
78
+	/**
79
+	 *        Set    question's type
80
+	 *
81
+	 * @access        public
82
+	 * @param string $QST_type
83
+	 */
84
+	public function set_question_type($QST_type = '')
85
+	{
86
+		$this->set('QST_type', $QST_type);
87
+	}
88
+
89
+
90
+	/**
91
+	 *        Sets whether this question must be answered when presented in a form
92
+	 *
93
+	 * @access        public
94
+	 * @param bool $QST_required
95
+	 */
96
+	public function set_required($QST_required = false)
97
+	{
98
+		$this->set('QST_required', $QST_required);
99
+	}
100
+
101
+
102
+	/**
103
+	 *        Set    Question display text
104
+	 *
105
+	 * @access        public
106
+	 * @param string $QST_required_text
107
+	 */
108
+	public function set_required_text($QST_required_text = '')
109
+	{
110
+		$this->set('QST_required_text', $QST_required_text);
111
+	}
112
+
113
+
114
+	/**
115
+	 *        Sets the order of this question when placed in a sequence of questions
116
+	 *
117
+	 * @access        public
118
+	 * @param int $QST_order
119
+	 */
120
+	public function set_order($QST_order = 0)
121
+	{
122
+		$this->set('QST_order', $QST_order);
123
+	}
124
+
125
+
126
+	/**
127
+	 *        Sets whether the question is admin-only
128
+	 *
129
+	 * @access        public
130
+	 * @param bool $QST_admin_only
131
+	 */
132
+	public function set_admin_only($QST_admin_only = false)
133
+	{
134
+		$this->set('QST_admin_only', $QST_admin_only);
135
+	}
136
+
137
+
138
+	/**
139
+	 *        Sets the wordpress user ID on the question
140
+	 *
141
+	 * @access        public
142
+	 * @param int $QST_wp_user
143
+	 */
144
+	public function set_wp_user($QST_wp_user = 1)
145
+	{
146
+		$this->set('QST_wp_user', $QST_wp_user);
147
+	}
148
+
149
+
150
+	/**
151
+	 *        Sets whether the question has been deleted
152
+	 *        (we use this boolean instead of actually
153
+	 *        deleting it because when users delete this question
154
+	 *        they really want to remove the question from future
155
+	 *        forms, BUT keep their old answers which depend
156
+	 *        on this record actually existing.
157
+	 *
158
+	 * @access        public
159
+	 * @param bool $QST_deleted
160
+	 */
161
+	public function set_deleted($QST_deleted = false)
162
+	{
163
+		$this->set('QST_deleted', $QST_deleted);
164
+	}
165
+
166
+
167
+	/**
168
+	 * returns the text for displaying the question to users
169
+	 *
170
+	 * @access public
171
+	 * @return string
172
+	 */
173
+	public function display_text()
174
+	{
175
+		return $this->get('QST_display_text');
176
+	}
177
+
178
+
179
+	/**
180
+	 * returns the text for the administrative label
181
+	 *
182
+	 * @access public
183
+	 * @return string
184
+	 */
185
+	public function admin_label()
186
+	{
187
+		return $this->get('QST_admin_label');
188
+	}
189
+
190
+
191
+	/**
192
+	 * returns the attendee column name for this question
193
+	 *
194
+	 * @access public
195
+	 * @return string
196
+	 */
197
+	public function system_ID()
198
+	{
199
+		return $this->get('QST_system');
200
+	}
201
+
202
+
203
+	/**
204
+	 * returns either a string of 'text', 'textfield', etc.
205
+	 *
206
+	 * @access public
207
+	 * @return boolean
208
+	 */
209
+	public function required()
210
+	{
211
+		return $this->get('QST_required');
212
+	}
213
+
214
+
215
+	/**
216
+	 * returns the text which should be displayed when a user
217
+	 * doesn't answer this question in a form
218
+	 *
219
+	 * @access public
220
+	 * @return string
221
+	 */
222
+	public function required_text()
223
+	{
224
+		return $this->get('QST_required_text');
225
+	}
226
+
227
+
228
+	/**
229
+	 * returns the type of this question
230
+	 *
231
+	 * @access public
232
+	 * @return string
233
+	 */
234
+	public function type()
235
+	{
236
+		return $this->get('QST_type');
237
+	}
238
+
239
+
240
+	/**
241
+	 * returns an integer showing where this question should
242
+	 * be placed in a sequence of questions
243
+	 *
244
+	 * @access public
245
+	 * @return int
246
+	 */
247
+	public function order()
248
+	{
249
+		return $this->get('QST_order');
250
+	}
251
+
252
+
253
+	/**
254
+	 * returns whether this question should only appears to admins,
255
+	 * or to everyone
256
+	 *
257
+	 * @access public
258
+	 * @return boolean
259
+	 */
260
+	public function admin_only()
261
+	{
262
+		return $this->get('QST_admin_only');
263
+	}
264
+
265
+
266
+	/**
267
+	 * returns the id the wordpress user who created this question
268
+	 *
269
+	 * @access public
270
+	 * @return int
271
+	 */
272
+	public function wp_user()
273
+	{
274
+		return $this->get('QST_wp_user');
275
+	}
276
+
277
+
278
+	/**
279
+	 * returns whether this question has been marked as 'deleted'
280
+	 *
281
+	 * @access public
282
+	 * @return boolean
283
+	 */
284
+	public function deleted()
285
+	{
286
+		return $this->get('QST_deleted');
287
+	}
288
+
289
+
290
+	/**
291
+	 * Gets an array of related EE_Answer  to this EE_Question
292
+	 *
293
+	 * @return EE_Answer[]
294
+	 */
295
+	public function answers()
296
+	{
297
+		return $this->get_many_related('Answer');
298
+	}
299
+
300
+
301
+	/**
302
+	 * Boolean check for if there are answers on this question in th db
303
+	 *
304
+	 * @return boolean true = has answers, false = no answers.
305
+	 */
306
+	public function has_answers()
307
+	{
308
+		return $this->count_related('Answer') > 0 ? true : false;
309
+	}
310
+
311
+
312
+	/**
313
+	 * gets an array of EE_Question_Group which relate to this question
314
+	 *
315
+	 * @return EE_Question_Group[]
316
+	 */
317
+	public function question_groups()
318
+	{
319
+		return $this->get_many_related('Question_Group');
320
+	}
321
+
322
+
323
+	/**
324
+	 * Returns all the options for this question. By default, it returns only the not-yet-deleted ones.
325
+	 *
326
+	 * @param boolean      $notDeletedOptionsOnly            1
327
+	 *                                                       whether to return ALL options, or only the ones which have
328
+	 *                                                       not yet been deleleted
329
+	 * @param string|array $selected_value_to_always_include , when retrieving options to an ANSWERED question,
330
+	 *                                                       we want to usually only show non-deleted options AND the
331
+	 *                                                       value that was selected for the answer, whether it was
332
+	 *                                                       trashed or not.
333
+	 * @return EE_Question_Option[]
334
+	 */
335
+	public function options($notDeletedOptionsOnly = true, $selected_value_to_always_include = null)
336
+	{
337
+		if (! $this->ID()) {
338
+			return [];
339
+		}
340
+		$query_params = [];
341
+		if ($selected_value_to_always_include) {
342
+			if (is_array($selected_value_to_always_include)) {
343
+				$query_params[0]['OR*options-query']['QSO_value'] = ['IN', $selected_value_to_always_include];
344
+			} else {
345
+				$query_params[0]['OR*options-query']['QSO_value'] = $selected_value_to_always_include;
346
+			}
347
+		}
348
+		if ($notDeletedOptionsOnly) {
349
+			$query_params[0]['OR*options-query']['QSO_deleted'] = false;
350
+		}
351
+		// order by QSO_order
352
+		$query_params['order_by'] = ['QSO_order' => 'ASC'];
353
+		return $this->get_many_related('Question_Option', $query_params);
354
+	}
355
+
356
+
357
+	/**
358
+	 * returns an array of EE_Question_Options which relate to this question
359
+	 *
360
+	 * @return \EE_Question_Option[]
361
+	 */
362
+	public function temp_options()
363
+	{
364
+		return $this->_model_relations['Question_Option'];
365
+	}
366
+
367
+
368
+	/**
369
+	 * Adds an option for this question. Note: if the option were previously associated with a different
370
+	 * Question, that relationship will be overwritten.
371
+	 *
372
+	 * @param EE_Question_Option $option
373
+	 * @return boolean success
374
+	 */
375
+	public function add_option(EE_Question_Option $option)
376
+	{
377
+		return $this->_add_relation_to($option, 'Question_Option');
378
+	}
379
+
380
+
381
+	/**
382
+	 * Adds an option directly to this question without saving to the db
383
+	 *
384
+	 * @param EE_Question_Option $option
385
+	 * @return boolean success
386
+	 */
387
+	public function add_temp_option(EE_Question_Option $option)
388
+	{
389
+		$this->_model_relations['Question_Option'][] = $option;
390
+		return true;
391
+	}
392
+
393
+
394
+	/**
395
+	 * Marks the option as deleted.
396
+	 *
397
+	 * @param EE_Question_Option $option
398
+	 * @return boolean success
399
+	 */
400
+	public function remove_option(EE_Question_Option $option)
401
+	{
402
+		return $this->_remove_relation_to($option, 'Question_Option');
403
+	}
404
+
405
+
406
+	/**
407
+	 * @return bool
408
+	 */
409
+	public function is_system_question()
410
+	{
411
+		$system_ID = $this->get('QST_system');
412
+		return ! empty($system_ID) ? true : false;
413
+	}
414
+
415
+
416
+	/**
417
+	 * The purpose of this method is set the question order this question order to be the max out of all questions
418
+	 *
419
+	 * @access public
420
+	 * @return void
421
+	 */
422
+	public function set_order_to_latest()
423
+	{
424
+		$latest_order = $this->get_model()->get_latest_question_order();
425
+		$latest_order++;
426
+		$this->set('QST_order', $latest_order);
427
+	}
428
+
429
+
430
+	/**
431
+	 * Retrieves the list of allowed question types from the model.
432
+	 *
433
+	 * @return string[]
434
+	 */
435
+	private function _allowed_question_types()
436
+	{
437
+		$questionModel = $this->get_model();
438
+		/* @var $questionModel EEM_Question */
439
+		return $questionModel->allowed_question_types();
440
+	}
441
+
442
+
443
+	/**
444
+	 * Duplicates this question and its question options
445
+	 *
446
+	 * @return \EE_Question
447
+	 */
448
+	public function duplicate($options = [])
449
+	{
450
+		$new_question = clone $this;
451
+		$new_question->set('QST_ID', null);
452
+		$new_question->set_display_text(
453
+			sprintf(esc_html__('%s **Duplicate**', 'event_espresso'), $this->display_text())
454
+		);
455
+		$new_question->set_admin_label(sprintf(esc_html__('%s **Duplicate**', 'event_espresso'), $this->admin_label()));
456
+		$new_question->set_system_ID(null);
457
+		$new_question->set_wp_user(get_current_user_id());
458
+		// if we're duplicating a trashed question, assume we don't want the new one to be trashed
459
+		$new_question->set_deleted(false);
460
+		$success = $new_question->save();
461
+		if ($success) {
462
+			// we don't totally want to duplicate the question options, because we want them to be for the NEW question
463
+			foreach ($this->options() as $question_option) {
464
+				$question_option->duplicate(['QST_ID' => $new_question->ID()]);
465
+			}
466
+			return $new_question;
467
+		} else {
468
+			return null;
469
+		}
470
+	}
471
+
472
+
473
+	/**
474
+	 * Returns the question's maximum allowed response size
475
+	 *
476
+	 * @return int|float
477
+	 */
478
+	public function max()
479
+	{
480
+		return $this->get('QST_max');
481
+	}
482
+
483
+
484
+	/**
485
+	 * Sets the question's maximum allowed response size
486
+	 *
487
+	 * @param int|float $new_max
488
+	 * @return void
489
+	 */
490
+	public function set_max($new_max)
491
+	{
492
+		$this->set('QST_max', $new_max);
493
+	}
494
+
495
+
496
+	/**
497
+	 * Creates a form input from this question which can be used in HTML forms
498
+	 *
499
+	 * @param EE_Registration $registration
500
+	 * @param EE_Answer       $answer
501
+	 * @param array           $input_constructor_args
502
+	 * @return EE_Form_Input_Base
503
+	 */
504
+	public function generate_form_input($registration = null, $answer = null, $input_constructor_args = [])
505
+	{
506
+		$identifier = $this->is_system_question() ? $this->system_ID() : $this->ID();
507
+
508
+		$input_constructor_args = array_merge(
509
+			[
510
+				'required'                          => $this->required() ? true : false,
511
+				'html_label_text'                   => $this->display_text(),
512
+				'required_validation_error_message' => $this->required_text(),
513
+			],
514
+			$input_constructor_args
515
+		);
516
+		if (! $answer instanceof EE_Answer && $registration instanceof EE_Registration) {
517
+			$answer = EEM_Answer::instance()->get_registration_question_answer_object($registration, $this->ID());
518
+		}
519
+		// has this question been answered ?
520
+		if (
521
+			$answer instanceof EE_Answer
522
+			&& $answer->value() !== ''
523
+		) {
524
+			// answer gets htmlspecialchars called on it, undo that please
525
+			// because the form input's display strategy may call esc_attr too
526
+			// which also does html special characters
527
+			$values_with_html_special_chars = $answer->value();
528
+			if (is_array($values_with_html_special_chars)) {
529
+				$default_value = array_map('htmlspecialchars_decode', $values_with_html_special_chars);
530
+			} else {
531
+				$default_value = htmlspecialchars_decode($values_with_html_special_chars);
532
+			}
533
+			$input_constructor_args['default'] = $default_value;
534
+		}
535
+		$max_max_for_question = EEM_Question::instance()->absolute_max_for_system_question($this->system_ID());
536
+		if (
537
+			in_array(
538
+				$this->type(),
539
+				EEM_Question::instance()->questionTypesWithMaxLength(),
540
+				true
541
+			)
542
+		) {
543
+			$input_constructor_args['validation_strategies'][] = new EE_Max_Length_Validation_Strategy(
544
+				null,
545
+				min($max_max_for_question, $this->max())
546
+			);
547
+		}
548
+		$input_constructor_args = apply_filters(
549
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__input_constructor_args',
550
+			$input_constructor_args,
551
+			$registration,
552
+			$this,
553
+			$answer
554
+		);
555
+
556
+		$result = null;
557
+		switch ($this->type()) {
558
+			// Text
559
+			case EEM_Question::QST_type_text:
560
+				$result = new EE_Text_Input($input_constructor_args);
561
+				break;
562
+			// Textarea
563
+			case EEM_Question::QST_type_textarea:
564
+				$result = new EE_Text_Area_Input($input_constructor_args);
565
+				break;
566
+			// Radio Buttons
567
+			case EEM_Question::QST_type_radio:
568
+				$result = new EE_Radio_Button_Input($this->options(), $input_constructor_args);
569
+				break;
570
+			// Dropdown
571
+			case EEM_Question::QST_type_dropdown:
572
+				$result = new EE_Select_Input($this->options(), $input_constructor_args);
573
+				break;
574
+			// State Dropdown
575
+			case EEM_Question::QST_type_state:
576
+				$state_options = apply_filters(
577
+					'FHEE__EE_Question__generate_form_input__state_options',
578
+					null,
579
+					$this,
580
+					$registration,
581
+					$answer
582
+				);
583
+				$result        = new EE_State_Select_Input($state_options, $input_constructor_args);
584
+				break;
585
+			// Country Dropdown
586
+			case EEM_Question::QST_type_country:
587
+				$country_options = apply_filters(
588
+					'FHEE__EE_Question__generate_form_input__country_options',
589
+					null,
590
+					$this,
591
+					$registration,
592
+					$answer
593
+				);
594
+				$result          = new EE_Country_Select_Input($country_options, $input_constructor_args);
595
+				break;
596
+			// Checkboxes
597
+			case EEM_Question::QST_type_checkbox:
598
+				$result = new EE_Checkbox_Multi_Input($this->options(), $input_constructor_args);
599
+				break;
600
+			// Date
601
+			case EEM_Question::QST_type_date:
602
+				$result = new EE_Datepicker_Input($input_constructor_args);
603
+				break;
604
+			case EEM_Question::QST_type_html_textarea:
605
+				$input_constructor_args['validation_strategies'][] = new EE_Simple_HTML_Validation_Strategy();
606
+				$result                                            = new EE_Text_Area_Input($input_constructor_args);
607
+				$result->remove_validation_strategy('EE_Plaintext_Validation_Strategy');
608
+				break;
609
+			case EEM_Question::QST_type_email:
610
+				$result = new EE_Email_Input($input_constructor_args);
611
+				break;
612
+			// Email confirm
613
+			case EEM_Question::QST_type_email_confirm:
614
+				$result = new EE_Email_Confirm_Input($input_constructor_args);
615
+				break;
616
+			case EEM_Question::QST_type_us_phone:
617
+				$result = new EE_Phone_Input($input_constructor_args);
618
+				break;
619
+			case EEM_Question::QST_type_int:
620
+				$result = new EE_Integer_Input($input_constructor_args);
621
+				break;
622
+			case EEM_Question::QST_type_decimal:
623
+				$result = new EE_Float_Input($input_constructor_args);
624
+				break;
625
+			case EEM_Question::QST_type_url:
626
+				$result = new EE_URL_Input($input_constructor_args);
627
+				break;
628
+			case EEM_Question::QST_type_year:
629
+				$result = new EE_Year_Input(
630
+					$input_constructor_args,
631
+					apply_filters(
632
+						'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__four_digit',
633
+						true,
634
+						$this
635
+					),
636
+					apply_filters(
637
+						'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__early_range',
638
+						100,
639
+						$this
640
+					),
641
+					apply_filters(
642
+						'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__end_range',
643
+						100,
644
+						$this
645
+					)
646
+				);
647
+				break;
648
+			case EEM_Question::QST_type_multi_select:
649
+				$result = new EE_Select_Multiple_Input($this->options(), $input_constructor_args);
650
+				break;
651
+			// fallback
652
+			default:
653
+				$default_input = apply_filters(
654
+					'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__default',
655
+					null,
656
+					$this->type(),
657
+					$this,
658
+					$input_constructor_args
659
+				);
660
+				if (! $default_input) {
661
+					$default_input = new EE_Text_Input($input_constructor_args);
662
+				}
663
+				$result = $default_input;
664
+		}
665
+		return apply_filters('FHEE__EE_Question__generate_form_input__return', $result, $registration, $this, $answer);
666
+	}
667
+
668
+
669
+	/**
670
+	 * Returns whether or not this question type should have question option entries
671
+	 *
672
+	 * @return bool
673
+	 */
674
+	public function should_have_question_options()
675
+	{
676
+		return in_array(
677
+			$this->type(),
678
+			$this->_model->question_types_with_options(),
679
+			true
680
+		);
681
+	}
682 682
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Datetime.class.php 2 patches
Indentation   +1568 added lines, -1568 removed lines patch added patch discarded remove patch
@@ -13,1576 +13,1576 @@
 block discarded – undo
13 13
  */
14 14
 class EE_Datetime extends EE_Soft_Delete_Base_Class
15 15
 {
16
-    /**
17
-     * constant used by get_active_status, indicates datetime has no more available spaces
18
-     */
19
-    const sold_out = 'DTS';
20
-
21
-    /**
22
-     * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
23
-     */
24
-    const active = 'DTA';
25
-
26
-    /**
27
-     * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
28
-     * expired
29
-     */
30
-    const upcoming = 'DTU';
31
-
32
-    /**
33
-     * Datetime is postponed
34
-     */
35
-    const postponed = 'DTP';
36
-
37
-    /**
38
-     * Datetime is cancelled
39
-     */
40
-    const cancelled = 'DTC';
41
-
42
-    /**
43
-     * constant used by get_active_status, indicates datetime has expired (event is over)
44
-     */
45
-    const expired = 'DTE';
46
-
47
-    /**
48
-     * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
49
-     */
50
-    const inactive = 'DTI';
51
-
52
-
53
-    /**
54
-     * @param array  $props_n_values    incoming values
55
-     * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
56
-     * @param array  $date_formats      incoming date_formats in an array where the first value is the date_format
57
-     *                                  and the second value is the time format
58
-     * @return EE_Datetime
59
-     * @throws ReflectionException
60
-     * @throws InvalidArgumentException
61
-     * @throws InvalidInterfaceException
62
-     * @throws InvalidDataTypeException
63
-     * @throws EE_Error
64
-     */
65
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
66
-    {
67
-        $has_object = parent::_check_for_object(
68
-            $props_n_values,
69
-            __CLASS__,
70
-            $timezone,
71
-            $date_formats
72
-        );
73
-        return $has_object
74
-            ? $has_object
75
-            : new self($props_n_values, false, $timezone, $date_formats);
76
-    }
77
-
78
-
79
-    /**
80
-     * @param array  $props_n_values  incoming values from the database
81
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
-     *                                the website will be used.
83
-     * @return EE_Datetime
84
-     * @throws ReflectionException
85
-     * @throws InvalidArgumentException
86
-     * @throws InvalidInterfaceException
87
-     * @throws InvalidDataTypeException
88
-     * @throws EE_Error
89
-     */
90
-    public static function new_instance_from_db($props_n_values = [], $timezone = '')
91
-    {
92
-        return new self($props_n_values, true, $timezone);
93
-    }
94
-
95
-
96
-    /**
97
-     * @param $name
98
-     * @throws ReflectionException
99
-     * @throws InvalidArgumentException
100
-     * @throws InvalidInterfaceException
101
-     * @throws InvalidDataTypeException
102
-     * @throws EE_Error
103
-     */
104
-    public function set_name($name)
105
-    {
106
-        $this->set('DTT_name', $name);
107
-    }
108
-
109
-
110
-    /**
111
-     * @param $description
112
-     * @throws ReflectionException
113
-     * @throws InvalidArgumentException
114
-     * @throws InvalidInterfaceException
115
-     * @throws InvalidDataTypeException
116
-     * @throws EE_Error
117
-     */
118
-    public function set_description($description)
119
-    {
120
-        $this->set('DTT_description', $description);
121
-    }
122
-
123
-
124
-    /**
125
-     * Set event start date
126
-     * set the start date for an event
127
-     *
128
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
129
-     * @throws ReflectionException
130
-     * @throws InvalidArgumentException
131
-     * @throws InvalidInterfaceException
132
-     * @throws InvalidDataTypeException
133
-     * @throws EE_Error
134
-     */
135
-    public function set_start_date($date)
136
-    {
137
-        $this->_set_date_for($date, 'DTT_EVT_start');
138
-    }
139
-
140
-
141
-    /**
142
-     * Set event start time
143
-     * set the start time for an event
144
-     *
145
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
146
-     * @throws ReflectionException
147
-     * @throws InvalidArgumentException
148
-     * @throws InvalidInterfaceException
149
-     * @throws InvalidDataTypeException
150
-     * @throws EE_Error
151
-     */
152
-    public function set_start_time($time)
153
-    {
154
-        $this->_set_time_for($time, 'DTT_EVT_start');
155
-    }
156
-
157
-
158
-    /**
159
-     * Set event end date
160
-     * set the end date for an event
161
-     *
162
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
163
-     * @throws ReflectionException
164
-     * @throws InvalidArgumentException
165
-     * @throws InvalidInterfaceException
166
-     * @throws InvalidDataTypeException
167
-     * @throws EE_Error
168
-     */
169
-    public function set_end_date($date)
170
-    {
171
-        $this->_set_date_for($date, 'DTT_EVT_end');
172
-    }
173
-
174
-
175
-    /**
176
-     * Set event end time
177
-     * set the end time for an event
178
-     *
179
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
180
-     * @throws ReflectionException
181
-     * @throws InvalidArgumentException
182
-     * @throws InvalidInterfaceException
183
-     * @throws InvalidDataTypeException
184
-     * @throws EE_Error
185
-     */
186
-    public function set_end_time($time)
187
-    {
188
-        $this->_set_time_for($time, 'DTT_EVT_end');
189
-    }
190
-
191
-
192
-    /**
193
-     * Set registration limit
194
-     * set the maximum number of attendees that can be registered for this datetime slot
195
-     *
196
-     * @param int|float $reg_limit
197
-     * @throws ReflectionException
198
-     * @throws InvalidArgumentException
199
-     * @throws InvalidInterfaceException
200
-     * @throws InvalidDataTypeException
201
-     * @throws EE_Error
202
-     */
203
-    public function set_reg_limit($reg_limit)
204
-    {
205
-        $this->set('DTT_reg_limit', $reg_limit);
206
-    }
207
-
208
-
209
-    /**
210
-     * get the number of tickets sold for this datetime slot
211
-     *
212
-     * @return mixed int on success, FALSE on fail
213
-     * @throws ReflectionException
214
-     * @throws InvalidArgumentException
215
-     * @throws InvalidInterfaceException
216
-     * @throws InvalidDataTypeException
217
-     * @throws EE_Error
218
-     */
219
-    public function sold()
220
-    {
221
-        return $this->get_raw('DTT_sold');
222
-    }
223
-
224
-
225
-    /**
226
-     * @param int $sold
227
-     * @throws ReflectionException
228
-     * @throws InvalidArgumentException
229
-     * @throws InvalidInterfaceException
230
-     * @throws InvalidDataTypeException
231
-     * @throws EE_Error
232
-     */
233
-    public function set_sold($sold)
234
-    {
235
-        // sold can not go below zero
236
-        $sold = max(0, $sold);
237
-        $this->set('DTT_sold', $sold);
238
-    }
239
-
240
-
241
-    /**
242
-     * Increments sold by amount passed by $qty, and persists it immediately to the database.
243
-     * Simultaneously decreases the reserved count, unless $also_decrease_reserved is false.
244
-     *
245
-     * @param int     $qty
246
-     * @param boolean $also_decrease_reserved
247
-     * @return boolean indicating success
248
-     * @throws ReflectionException
249
-     * @throws InvalidArgumentException
250
-     * @throws InvalidInterfaceException
251
-     * @throws InvalidDataTypeException
252
-     * @throws EE_Error
253
-     */
254
-    public function increaseSold(int $qty = 1, bool $also_decrease_reserved = true): bool
255
-    {
256
-        $qty = absint($qty);
257
-        if ($also_decrease_reserved) {
258
-            $success = $this->adjustNumericFieldsInDb(
259
-                [
260
-                    'DTT_reserved' => $qty * -1,
261
-                    'DTT_sold'     => $qty,
262
-                ]
263
-            );
264
-        } else {
265
-            $success = $this->adjustNumericFieldsInDb(
266
-                [
267
-                    'DTT_sold' => $qty,
268
-                ]
269
-            );
270
-        }
271
-
272
-        do_action(
273
-            'AHEE__EE_Datetime__increase_sold',
274
-            $this,
275
-            $qty,
276
-            $this->sold(),
277
-            $success
278
-        );
279
-        return $success;
280
-    }
281
-
282
-
283
-    /**
284
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
285
-     * to save afterwards.)
286
-     *
287
-     * @param int $qty
288
-     * @return boolean indicating success
289
-     * @throws ReflectionException
290
-     * @throws InvalidArgumentException
291
-     * @throws InvalidInterfaceException
292
-     * @throws InvalidDataTypeException
293
-     * @throws EE_Error
294
-     */
295
-    public function decreaseSold(int $qty = 1): bool
296
-    {
297
-        $qty     = absint($qty);
298
-        $success = $this->adjustNumericFieldsInDb(
299
-            [
300
-                'DTT_sold' => $qty * -1,
301
-            ]
302
-        );
303
-        do_action(
304
-            'AHEE__EE_Datetime__decrease_sold',
305
-            $this,
306
-            $qty,
307
-            $this->sold(),
308
-            $success
309
-        );
310
-        return $success;
311
-    }
312
-
313
-
314
-    /**
315
-     * Gets qty of reserved tickets for this datetime
316
-     *
317
-     * @return int
318
-     * @throws ReflectionException
319
-     * @throws InvalidArgumentException
320
-     * @throws InvalidInterfaceException
321
-     * @throws InvalidDataTypeException
322
-     * @throws EE_Error
323
-     */
324
-    public function reserved(): int
325
-    {
326
-        return $this->get_raw('DTT_reserved');
327
-    }
328
-
329
-
330
-    /**
331
-     * Sets qty of reserved tickets for this datetime
332
-     *
333
-     * @param int $reserved
334
-     * @throws ReflectionException
335
-     * @throws InvalidArgumentException
336
-     * @throws InvalidInterfaceException
337
-     * @throws InvalidDataTypeException
338
-     * @throws EE_Error
339
-     */
340
-    public function set_reserved(int $reserved)
341
-    {
342
-        // reserved can not go below zero
343
-        $reserved = max(0, $reserved);
344
-        $this->set('DTT_reserved', $reserved);
345
-    }
346
-
347
-
348
-    /**
349
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
350
-     *
351
-     * @param int $qty
352
-     * @return boolean indicating success
353
-     * @throws ReflectionException
354
-     * @throws InvalidArgumentException
355
-     * @throws InvalidInterfaceException
356
-     * @throws InvalidDataTypeException
357
-     * @throws EE_Error
358
-     */
359
-    public function increaseReserved(int $qty = 1): bool
360
-    {
361
-        $qty     = absint($qty);
362
-        $success = $this->incrementFieldConditionallyInDb(
363
-            'DTT_reserved',
364
-            'DTT_sold',
365
-            'DTT_reg_limit',
366
-            $qty
367
-        );
368
-        do_action(
369
-            'AHEE__EE_Datetime__increase_reserved',
370
-            $this,
371
-            $qty,
372
-            $this->reserved(),
373
-            $success
374
-        );
375
-        return $success;
376
-    }
377
-
378
-
379
-    /**
380
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
381
-     *
382
-     * @param int $qty
383
-     * @return boolean indicating success
384
-     * @throws ReflectionException
385
-     * @throws InvalidArgumentException
386
-     * @throws InvalidInterfaceException
387
-     * @throws InvalidDataTypeException
388
-     * @throws EE_Error
389
-     */
390
-    public function decreaseReserved(int $qty = 1): bool
391
-    {
392
-        $qty     = absint($qty);
393
-        $success = $this->adjustNumericFieldsInDb(
394
-            [
395
-                'DTT_reserved' => $qty * -1,
396
-            ]
397
-        );
398
-        do_action(
399
-            'AHEE__EE_Datetime__decrease_reserved',
400
-            $this,
401
-            $qty,
402
-            $this->reserved(),
403
-            $success
404
-        );
405
-        return $success;
406
-    }
407
-
408
-
409
-    /**
410
-     * total sold and reserved tickets
411
-     *
412
-     * @return int
413
-     * @throws ReflectionException
414
-     * @throws InvalidArgumentException
415
-     * @throws InvalidInterfaceException
416
-     * @throws InvalidDataTypeException
417
-     * @throws EE_Error
418
-     */
419
-    public function sold_and_reserved(): int
420
-    {
421
-        return $this->sold() + $this->reserved();
422
-    }
423
-
424
-
425
-    /**
426
-     * returns the datetime name
427
-     *
428
-     * @return string
429
-     * @throws ReflectionException
430
-     * @throws InvalidArgumentException
431
-     * @throws InvalidInterfaceException
432
-     * @throws InvalidDataTypeException
433
-     * @throws EE_Error
434
-     */
435
-    public function name(): string
436
-    {
437
-        return $this->get('DTT_name');
438
-    }
439
-
440
-
441
-    /**
442
-     * returns the datetime description
443
-     *
444
-     * @return string
445
-     * @throws ReflectionException
446
-     * @throws InvalidArgumentException
447
-     * @throws InvalidInterfaceException
448
-     * @throws InvalidDataTypeException
449
-     * @throws EE_Error
450
-     */
451
-    public function description(): string
452
-    {
453
-        return $this->get('DTT_description');
454
-    }
455
-
456
-
457
-    /**
458
-     * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
459
-     *
460
-     * @return boolean  TRUE if is primary, FALSE if not.
461
-     * @throws ReflectionException
462
-     * @throws InvalidArgumentException
463
-     * @throws InvalidInterfaceException
464
-     * @throws InvalidDataTypeException
465
-     * @throws EE_Error
466
-     */
467
-    public function is_primary(): bool
468
-    {
469
-        return $this->get('DTT_is_primary');
470
-    }
471
-
472
-
473
-    /**
474
-     * This helper simply returns the order for the datetime
475
-     *
476
-     * @return int  The order of the datetime for this event.
477
-     * @throws ReflectionException
478
-     * @throws InvalidArgumentException
479
-     * @throws InvalidInterfaceException
480
-     * @throws InvalidDataTypeException
481
-     * @throws EE_Error
482
-     */
483
-    public function order(): int
484
-    {
485
-        return $this->get('DTT_order');
486
-    }
487
-
488
-
489
-    /**
490
-     * This helper simply returns the parent id for the datetime
491
-     *
492
-     * @return int
493
-     * @throws ReflectionException
494
-     * @throws InvalidArgumentException
495
-     * @throws InvalidInterfaceException
496
-     * @throws InvalidDataTypeException
497
-     * @throws EE_Error
498
-     */
499
-    public function parent(): int
500
-    {
501
-        return $this->get('DTT_parent');
502
-    }
503
-
504
-
505
-    /**
506
-     * show date and/or time
507
-     *
508
-     * @param string $date_or_time    whether to display a date or time or both
509
-     * @param string $start_or_end    whether to display start or end datetimes
510
-     * @param string $dt_frmt
511
-     * @param string $tm_frmt
512
-     * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
513
-     *                                otherwise we use the standard formats)
514
-     * @return string|bool  string on success, FALSE on fail
515
-     * @throws ReflectionException
516
-     * @throws InvalidArgumentException
517
-     * @throws InvalidInterfaceException
518
-     * @throws InvalidDataTypeException
519
-     * @throws EE_Error
520
-     */
521
-    private function _show_datetime(
522
-        $date_or_time = null,
523
-        $start_or_end = 'start',
524
-        $dt_frmt = '',
525
-        $tm_frmt = '',
526
-        $echo = false
527
-    ) {
528
-        $field_name = "DTT_EVT_{$start_or_end}";
529
-        $dtt        = $this->_get_datetime(
530
-            $field_name,
531
-            $dt_frmt,
532
-            $tm_frmt,
533
-            $date_or_time,
534
-            $echo
535
-        );
536
-        if (! $echo) {
537
-            return $dtt;
538
-        }
539
-        return '';
540
-    }
541
-
542
-
543
-    /**
544
-     * get event start date.  Provide either the date format, or NULL to re-use the
545
-     * last-used format, or '' to use the default date format
546
-     *
547
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
548
-     * @return mixed            string on success, FALSE on fail
549
-     * @throws ReflectionException
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidInterfaceException
552
-     * @throws InvalidDataTypeException
553
-     * @throws EE_Error
554
-     */
555
-    public function start_date($dt_frmt = '')
556
-    {
557
-        return $this->_show_datetime('D', 'start', $dt_frmt);
558
-    }
559
-
560
-
561
-    /**
562
-     * Echoes start_date()
563
-     *
564
-     * @param string $dt_frmt
565
-     * @throws ReflectionException
566
-     * @throws InvalidArgumentException
567
-     * @throws InvalidInterfaceException
568
-     * @throws InvalidDataTypeException
569
-     * @throws EE_Error
570
-     */
571
-    public function e_start_date($dt_frmt = '')
572
-    {
573
-        $this->_show_datetime('D', 'start', $dt_frmt, null, true);
574
-    }
575
-
576
-
577
-    /**
578
-     * get end date. Provide either the date format, or NULL to re-use the
579
-     * last-used format, or '' to use the default date format
580
-     *
581
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
582
-     * @return mixed            string on success, FALSE on fail
583
-     * @throws ReflectionException
584
-     * @throws InvalidArgumentException
585
-     * @throws InvalidInterfaceException
586
-     * @throws InvalidDataTypeException
587
-     * @throws EE_Error
588
-     */
589
-    public function end_date($dt_frmt = '')
590
-    {
591
-        return $this->_show_datetime('D', 'end', $dt_frmt);
592
-    }
593
-
594
-
595
-    /**
596
-     * Echoes the end date. See end_date()
597
-     *
598
-     * @param string $dt_frmt
599
-     * @throws ReflectionException
600
-     * @throws InvalidArgumentException
601
-     * @throws InvalidInterfaceException
602
-     * @throws InvalidDataTypeException
603
-     * @throws EE_Error
604
-     */
605
-    public function e_end_date($dt_frmt = '')
606
-    {
607
-        $this->_show_datetime('D', 'end', $dt_frmt, null, true);
608
-    }
609
-
610
-
611
-    /**
612
-     * get date_range - meaning the start AND end date
613
-     *
614
-     * @access public
615
-     * @param string $dt_frmt     string representation of date format defaults to WP settings
616
-     * @param string $conjunction conjunction junction what's your function ?
617
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
-     * @return mixed              string on success, FALSE on fail
619
-     * @throws ReflectionException
620
-     * @throws InvalidArgumentException
621
-     * @throws InvalidInterfaceException
622
-     * @throws InvalidDataTypeException
623
-     * @throws EE_Error
624
-     */
625
-    public function date_range($dt_frmt = '', $conjunction = ' - ')
626
-    {
627
-        $dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
628
-        $start   = str_replace(
629
-            ' ',
630
-            '&nbsp;',
631
-            $this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632
-        );
633
-        $end     = str_replace(
634
-            ' ',
635
-            '&nbsp;',
636
-            $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637
-        );
638
-        return $start !== $end ? $start . $conjunction . $end : $start;
639
-    }
640
-
641
-
642
-    /**
643
-     * @param string $dt_frmt
644
-     * @param string $conjunction
645
-     * @throws ReflectionException
646
-     * @throws InvalidArgumentException
647
-     * @throws InvalidInterfaceException
648
-     * @throws InvalidDataTypeException
649
-     * @throws EE_Error
650
-     */
651
-    public function e_date_range($dt_frmt = '', $conjunction = ' - ')
652
-    {
653
-        echo esc_html($this->date_range($dt_frmt, $conjunction));
654
-    }
655
-
656
-
657
-    /**
658
-     * get start time
659
-     *
660
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
661
-     * @return mixed        string on success, FALSE on fail
662
-     * @throws ReflectionException
663
-     * @throws InvalidArgumentException
664
-     * @throws InvalidInterfaceException
665
-     * @throws InvalidDataTypeException
666
-     * @throws EE_Error
667
-     */
668
-    public function start_time($tm_format = '')
669
-    {
670
-        return $this->_show_datetime('T', 'start', null, $tm_format);
671
-    }
672
-
673
-
674
-    /**
675
-     * @param string $tm_format
676
-     * @throws ReflectionException
677
-     * @throws InvalidArgumentException
678
-     * @throws InvalidInterfaceException
679
-     * @throws InvalidDataTypeException
680
-     * @throws EE_Error
681
-     */
682
-    public function e_start_time($tm_format = '')
683
-    {
684
-        $this->_show_datetime('T', 'start', null, $tm_format, true);
685
-    }
686
-
687
-
688
-    /**
689
-     * get end time
690
-     *
691
-     * @param string $tm_format string representation of time format defaults to 'g:i a'
692
-     * @return mixed                string on success, FALSE on fail
693
-     * @throws ReflectionException
694
-     * @throws InvalidArgumentException
695
-     * @throws InvalidInterfaceException
696
-     * @throws InvalidDataTypeException
697
-     * @throws EE_Error
698
-     */
699
-    public function end_time($tm_format = '')
700
-    {
701
-        return $this->_show_datetime('T', 'end', null, $tm_format);
702
-    }
703
-
704
-
705
-    /**
706
-     * @param string $tm_format
707
-     * @throws ReflectionException
708
-     * @throws InvalidArgumentException
709
-     * @throws InvalidInterfaceException
710
-     * @throws InvalidDataTypeException
711
-     * @throws EE_Error
712
-     */
713
-    public function e_end_time($tm_format = '')
714
-    {
715
-        $this->_show_datetime('T', 'end', null, $tm_format, true);
716
-    }
717
-
718
-
719
-    /**
720
-     * get time_range
721
-     *
722
-     * @access public
723
-     * @param string $tm_format   string representation of time format defaults to 'g:i a'
724
-     * @param string $conjunction conjunction junction what's your function ?
725
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
-     * @return mixed              string on success, FALSE on fail
727
-     * @throws ReflectionException
728
-     * @throws InvalidArgumentException
729
-     * @throws InvalidInterfaceException
730
-     * @throws InvalidDataTypeException
731
-     * @throws EE_Error
732
-     */
733
-    public function time_range($tm_format = '', $conjunction = ' - ')
734
-    {
735
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
736
-        $start     = str_replace(
737
-            ' ',
738
-            '&nbsp;',
739
-            $this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740
-        );
741
-        $end       = str_replace(
742
-            ' ',
743
-            '&nbsp;',
744
-            $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745
-        );
746
-        return $start !== $end ? $start . $conjunction . $end : $start;
747
-    }
748
-
749
-
750
-    /**
751
-     * @param string $tm_format
752
-     * @param string $conjunction
753
-     * @throws ReflectionException
754
-     * @throws InvalidArgumentException
755
-     * @throws InvalidInterfaceException
756
-     * @throws InvalidDataTypeException
757
-     * @throws EE_Error
758
-     */
759
-    public function e_time_range($tm_format = '', $conjunction = ' - ')
760
-    {
761
-        echo esc_html($this->time_range($tm_format, $conjunction));
762
-    }
763
-
764
-
765
-    /**
766
-     * This returns a range representation of the date and times.
767
-     * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
768
-     * Also, the return value is localized.
769
-     *
770
-     * @param string $dt_format
771
-     * @param string $tm_format
772
-     * @param string $conjunction used between two different dates or times.
773
-     *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
774
-     * @param string $separator   used between the date and time formats.
775
-     *                            ex: Dec 1, 2016{$separator}2pm
776
-     * @return string
777
-     * @throws ReflectionException
778
-     * @throws InvalidArgumentException
779
-     * @throws InvalidInterfaceException
780
-     * @throws InvalidDataTypeException
781
-     * @throws EE_Error
782
-     */
783
-    public function date_and_time_range(
784
-        $dt_format = '',
785
-        $tm_format = '',
786
-        $conjunction = ' - ',
787
-        $separator = ' '
788
-    ) {
789
-        $dt_format   = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790
-        $tm_format   = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
-        $full_format = $dt_format . $separator . $tm_format;
792
-        // the range output depends on various conditions
793
-        switch (true) {
794
-            // start date timestamp and end date timestamp are the same.
795
-            case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
796
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
797
-                break;
798
-            // start and end date are the same but times are different
799
-            case ($this->start_date() === $this->end_date()):
800
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
801
-                          . $conjunction
802
-                          . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
803
-                break;
804
-            // all other conditions
805
-            default:
806
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
807
-                          . $conjunction
808
-                          . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
809
-                break;
810
-        }
811
-        return $output;
812
-    }
813
-
814
-
815
-    /**
816
-     * This echos the results of date and time range.
817
-     *
818
-     * @param string $dt_format
819
-     * @param string $tm_format
820
-     * @param string $conjunction
821
-     * @return void
822
-     * @throws ReflectionException
823
-     * @throws InvalidArgumentException
824
-     * @throws InvalidInterfaceException
825
-     * @throws InvalidDataTypeException
826
-     * @throws EE_Error
827
-     * @see date_and_time_range() for more details on purpose.
828
-     */
829
-    public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
830
-    {
831
-        echo esc_html($this->date_and_time_range($dt_format, $tm_format, $conjunction));
832
-    }
833
-
834
-
835
-    /**
836
-     * get start date and start time
837
-     *
838
-     * @param string $dt_format - string representation of date format defaults to 'F j, Y'
839
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
840
-     * @return    mixed    string on success, FALSE on fail
841
-     * @throws ReflectionException
842
-     * @throws InvalidArgumentException
843
-     * @throws InvalidInterfaceException
844
-     * @throws InvalidDataTypeException
845
-     * @throws EE_Error
846
-     */
847
-    public function start_date_and_time($dt_format = '', $tm_format = '')
848
-    {
849
-        return $this->_show_datetime('', 'start', $dt_format, $tm_format);
850
-    }
851
-
852
-
853
-    /**
854
-     * @param string $dt_frmt
855
-     * @param string $tm_format
856
-     * @throws ReflectionException
857
-     * @throws InvalidArgumentException
858
-     * @throws InvalidInterfaceException
859
-     * @throws InvalidDataTypeException
860
-     * @throws EE_Error
861
-     */
862
-    public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
863
-    {
864
-        $this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
865
-    }
866
-
867
-
868
-    /**
869
-     * Shows the length of the event (start to end time).
870
-     * Can be shown in 'seconds','minutes','hours', or 'days'.
871
-     * By default, rounds up. (So if you use 'days', and then event
872
-     * only occurs for 1 hour, it will return 1 day).
873
-     *
874
-     * @param string $units 'seconds','minutes','hours','days'
875
-     * @param bool   $round_up
876
-     * @return float|int|mixed
877
-     * @throws ReflectionException
878
-     * @throws InvalidArgumentException
879
-     * @throws InvalidInterfaceException
880
-     * @throws InvalidDataTypeException
881
-     * @throws EE_Error
882
-     */
883
-    public function length($units = 'seconds', $round_up = false)
884
-    {
885
-        $start           = $this->get_raw('DTT_EVT_start');
886
-        $end             = $this->get_raw('DTT_EVT_end');
887
-        $length_in_units = $end - $start;
888
-        switch ($units) {
889
-            // NOTE: We purposefully don't use "break;" in order to chain the divisions
890
-            /** @noinspection PhpMissingBreakStatementInspection */
891
-            // phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
892
-            case 'days':
893
-                $length_in_units /= 24;
894
-            /** @noinspection PhpMissingBreakStatementInspection */
895
-            case 'hours':
896
-                // fall through is intentional
897
-                $length_in_units /= 60;
898
-            /** @noinspection PhpMissingBreakStatementInspection */
899
-            case 'minutes':
900
-                // fall through is intentional
901
-                $length_in_units /= 60;
902
-            case 'seconds':
903
-            default:
904
-                $length_in_units = ceil($length_in_units);
905
-        }
906
-        // phpcs:enable
907
-        if ($round_up) {
908
-            $length_in_units = max($length_in_units, 1);
909
-        }
910
-        return $length_in_units;
911
-    }
912
-
913
-
914
-    /**
915
-     *        get end date and time
916
-     *
917
-     * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
918
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
919
-     * @return    mixed                string on success, FALSE on fail
920
-     * @throws ReflectionException
921
-     * @throws InvalidArgumentException
922
-     * @throws InvalidInterfaceException
923
-     * @throws InvalidDataTypeException
924
-     * @throws EE_Error
925
-     */
926
-    public function end_date_and_time($dt_frmt = '', $tm_format = '')
927
-    {
928
-        return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
929
-    }
930
-
931
-
932
-    /**
933
-     * @param string $dt_frmt
934
-     * @param string $tm_format
935
-     * @throws ReflectionException
936
-     * @throws InvalidArgumentException
937
-     * @throws InvalidInterfaceException
938
-     * @throws InvalidDataTypeException
939
-     * @throws EE_Error
940
-     */
941
-    public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
942
-    {
943
-        $this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
944
-    }
945
-
946
-
947
-    /**
948
-     *        get start timestamp
949
-     *
950
-     * @return        int
951
-     * @throws ReflectionException
952
-     * @throws InvalidArgumentException
953
-     * @throws InvalidInterfaceException
954
-     * @throws InvalidDataTypeException
955
-     * @throws EE_Error
956
-     */
957
-    public function start()
958
-    {
959
-        return $this->get_raw('DTT_EVT_start');
960
-    }
961
-
962
-
963
-    /**
964
-     *        get end timestamp
965
-     *
966
-     * @return        int
967
-     * @throws ReflectionException
968
-     * @throws InvalidArgumentException
969
-     * @throws InvalidInterfaceException
970
-     * @throws InvalidDataTypeException
971
-     * @throws EE_Error
972
-     */
973
-    public function end()
974
-    {
975
-        return $this->get_raw('DTT_EVT_end');
976
-    }
977
-
978
-
979
-    /**
980
-     *    get the registration limit for this datetime slot
981
-     *
982
-     * @return int|float                int = finite limit   EE_INF(float) = unlimited
983
-     * @throws ReflectionException
984
-     * @throws InvalidArgumentException
985
-     * @throws InvalidInterfaceException
986
-     * @throws InvalidDataTypeException
987
-     * @throws EE_Error
988
-     */
989
-    public function reg_limit()
990
-    {
991
-        return $this->get_raw('DTT_reg_limit');
992
-    }
993
-
994
-
995
-    /**
996
-     *    have the tickets sold for this datetime, met or exceed the registration limit ?
997
-     *
998
-     * @return boolean
999
-     * @throws ReflectionException
1000
-     * @throws InvalidArgumentException
1001
-     * @throws InvalidInterfaceException
1002
-     * @throws InvalidDataTypeException
1003
-     * @throws EE_Error
1004
-     */
1005
-    public function sold_out()
1006
-    {
1007
-        return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
1008
-    }
1009
-
1010
-
1011
-    /**
1012
-     * return the total number of spaces remaining at this venue.
1013
-     * This only takes the venue's capacity into account, NOT the tickets available for sale
1014
-     *
1015
-     * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
1016
-     *                               Because if all tickets attached to this datetime have no spaces left,
1017
-     *                               then this datetime IS effectively sold out.
1018
-     *                               However, there are cases where we just want to know the spaces
1019
-     *                               remaining for this particular datetime, hence the flag.
1020
-     * @return int|float
1021
-     * @throws ReflectionException
1022
-     * @throws InvalidArgumentException
1023
-     * @throws InvalidInterfaceException
1024
-     * @throws InvalidDataTypeException
1025
-     * @throws EE_Error
1026
-     */
1027
-    public function spaces_remaining($consider_tickets = false)
1028
-    {
1029
-        // tickets remaining available for purchase
1030
-        // no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031
-        $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
-        if (! $consider_tickets) {
1033
-            return $dtt_remaining;
1034
-        }
1035
-        $tickets_remaining = $this->tickets_remaining();
1036
-        return min($dtt_remaining, $tickets_remaining);
1037
-    }
1038
-
1039
-
1040
-    /**
1041
-     * Counts the total tickets available
1042
-     * (from all the different types of tickets which are available for this datetime).
1043
-     *
1044
-     * @param array $query_params @see
1045
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1046
-     * @return int
1047
-     * @throws ReflectionException
1048
-     * @throws InvalidArgumentException
1049
-     * @throws InvalidInterfaceException
1050
-     * @throws InvalidDataTypeException
1051
-     * @throws EE_Error
1052
-     */
1053
-    public function tickets_remaining($query_params = [])
1054
-    {
1055
-        $sum     = 0;
1056
-        $tickets = $this->tickets($query_params);
1057
-        if (! empty($tickets)) {
1058
-            foreach ($tickets as $ticket) {
1059
-                if ($ticket instanceof EE_Ticket) {
1060
-                    // get the actual amount of tickets that can be sold
1061
-                    $qty = $ticket->qty('saleable');
1062
-                    if ($qty === EE_INF) {
1063
-                        return EE_INF;
1064
-                    }
1065
-                    // no negative ticket quantities plz
1066
-                    if ($qty > 0) {
1067
-                        $sum += $qty;
1068
-                    }
1069
-                }
1070
-            }
1071
-        }
1072
-        return $sum;
1073
-    }
1074
-
1075
-
1076
-    /**
1077
-     * Gets the count of all the tickets available at this datetime (not ticket types)
1078
-     * before any were sold
1079
-     *
1080
-     * @param array $query_params @see
1081
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1082
-     * @return int
1083
-     * @throws ReflectionException
1084
-     * @throws InvalidArgumentException
1085
-     * @throws InvalidInterfaceException
1086
-     * @throws InvalidDataTypeException
1087
-     * @throws EE_Error
1088
-     */
1089
-    public function sum_tickets_initially_available($query_params = [])
1090
-    {
1091
-        return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1092
-    }
1093
-
1094
-
1095
-    /**
1096
-     * Returns the lesser-of-the two: spaces remaining at this datetime, or
1097
-     * the total tickets remaining (a sum of the tickets remaining for each ticket type
1098
-     * that is available for this datetime).
1099
-     *
1100
-     * @return int
1101
-     * @throws ReflectionException
1102
-     * @throws InvalidArgumentException
1103
-     * @throws InvalidInterfaceException
1104
-     * @throws InvalidDataTypeException
1105
-     * @throws EE_Error
1106
-     */
1107
-    public function total_tickets_available_at_this_datetime()
1108
-    {
1109
-        return $this->spaces_remaining(true);
1110
-    }
1111
-
1112
-
1113
-    /**
1114
-     * This simply compares the internal dtt for the given string with NOW
1115
-     * and determines if the date is upcoming or not.
1116
-     *
1117
-     * @access public
1118
-     * @return boolean
1119
-     * @throws ReflectionException
1120
-     * @throws InvalidArgumentException
1121
-     * @throws InvalidInterfaceException
1122
-     * @throws InvalidDataTypeException
1123
-     * @throws EE_Error
1124
-     */
1125
-    public function is_upcoming()
1126
-    {
1127
-        return ($this->get_raw('DTT_EVT_start') > time());
1128
-    }
1129
-
1130
-
1131
-    /**
1132
-     * This simply compares the internal datetime for the given string with NOW
1133
-     * and returns if the date is active (i.e. start and end time)
1134
-     *
1135
-     * @return boolean
1136
-     * @throws ReflectionException
1137
-     * @throws InvalidArgumentException
1138
-     * @throws InvalidInterfaceException
1139
-     * @throws InvalidDataTypeException
1140
-     * @throws EE_Error
1141
-     */
1142
-    public function is_active()
1143
-    {
1144
-        return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1145
-    }
1146
-
1147
-
1148
-    /**
1149
-     * This simply compares the internal dtt for the given string with NOW
1150
-     * and determines if the date is expired or not.
1151
-     *
1152
-     * @return boolean
1153
-     * @throws ReflectionException
1154
-     * @throws InvalidArgumentException
1155
-     * @throws InvalidInterfaceException
1156
-     * @throws InvalidDataTypeException
1157
-     * @throws EE_Error
1158
-     */
1159
-    public function is_expired()
1160
-    {
1161
-        return ($this->get_raw('DTT_EVT_end') < time());
1162
-    }
1163
-
1164
-
1165
-    /**
1166
-     * This returns the active status for whether an event is active, upcoming, or expired
1167
-     *
1168
-     * @return int return value will be one of the EE_Datetime status constants.
1169
-     * @throws ReflectionException
1170
-     * @throws InvalidArgumentException
1171
-     * @throws InvalidInterfaceException
1172
-     * @throws InvalidDataTypeException
1173
-     * @throws EE_Error
1174
-     */
1175
-    public function get_active_status()
1176
-    {
1177
-        $total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1178
-        if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1179
-            return EE_Datetime::sold_out;
1180
-        }
1181
-        if ($this->is_expired()) {
1182
-            return EE_Datetime::expired;
1183
-        }
1184
-        if ($this->is_upcoming()) {
1185
-            return EE_Datetime::upcoming;
1186
-        }
1187
-        if ($this->is_active()) {
1188
-            return EE_Datetime::active;
1189
-        }
1190
-        return null;
1191
-    }
1192
-
1193
-
1194
-    /**
1195
-     * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1196
-     *
1197
-     * @param boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1198
-     * @return string
1199
-     * @throws ReflectionException
1200
-     * @throws InvalidArgumentException
1201
-     * @throws InvalidInterfaceException
1202
-     * @throws InvalidDataTypeException
1203
-     * @throws EE_Error
1204
-     */
1205
-    public function get_dtt_display_name($use_dtt_name = false)
1206
-    {
1207
-        if ($use_dtt_name) {
1208
-            $dtt_name = $this->name();
1209
-            if (! empty($dtt_name)) {
1210
-                return $dtt_name;
1211
-            }
1212
-        }
1213
-        // first condition is to see if the months are different
1214
-        if (
1215
-            date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1216
-        ) {
1217
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1218
-            // next condition is if its the same month but different day
1219
-        } else {
1220
-            if (
1221
-                date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1222
-                && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1223
-            ) {
1224
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1225
-            } else {
1226
-                $display_date = $this->start_date('F j\, Y')
1227
-                                . ' @ '
1228
-                                . $this->start_date('g:i a')
1229
-                                . ' - '
1230
-                                . $this->end_date('g:i a');
1231
-            }
1232
-        }
1233
-        return $display_date;
1234
-    }
1235
-
1236
-
1237
-    /**
1238
-     * Gets all the tickets for this datetime
1239
-     *
1240
-     * @param array $query_params @see
1241
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1242
-     * @return EE_Base_Class[]|EE_Ticket[]
1243
-     * @throws ReflectionException
1244
-     * @throws InvalidArgumentException
1245
-     * @throws InvalidInterfaceException
1246
-     * @throws InvalidDataTypeException
1247
-     * @throws EE_Error
1248
-     */
1249
-    public function tickets($query_params = [])
1250
-    {
1251
-        return $this->get_many_related('Ticket', $query_params);
1252
-    }
1253
-
1254
-
1255
-    /**
1256
-     * Gets all the ticket types currently available for purchase
1257
-     *
1258
-     * @param array $query_params @see
1259
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1260
-     * @return EE_Ticket[]
1261
-     * @throws ReflectionException
1262
-     * @throws InvalidArgumentException
1263
-     * @throws InvalidInterfaceException
1264
-     * @throws InvalidDataTypeException
1265
-     * @throws EE_Error
1266
-     */
1267
-    public function ticket_types_available_for_purchase($query_params = [])
1268
-    {
1269
-        // first check if datetime is valid
1270
-        if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1271
-            return [];
1272
-        }
1273
-        if (empty($query_params)) {
1274
-            $query_params = [
1275
-                [
1276
-                    'TKT_start_date' => ['<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')],
1277
-                    'TKT_end_date'   => ['>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')],
1278
-                    'TKT_deleted'    => false,
1279
-                ],
1280
-            ];
1281
-        }
1282
-        return $this->tickets($query_params);
1283
-    }
1284
-
1285
-
1286
-    /**
1287
-     * @return EE_Base_Class|EE_Event
1288
-     * @throws ReflectionException
1289
-     * @throws InvalidArgumentException
1290
-     * @throws InvalidInterfaceException
1291
-     * @throws InvalidDataTypeException
1292
-     * @throws EE_Error
1293
-     */
1294
-    public function event()
1295
-    {
1296
-        return EEM_Event::instance()->get_one_by_ID($this->get('EVT_ID'));
1297
-    }
1298
-
1299
-
1300
-    /**
1301
-     * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1302
-     * (via the tickets).
1303
-     *
1304
-     * @return int
1305
-     * @throws ReflectionException
1306
-     * @throws InvalidArgumentException
1307
-     * @throws InvalidInterfaceException
1308
-     * @throws InvalidDataTypeException
1309
-     * @throws EE_Error
1310
-     */
1311
-    public function update_sold()
1312
-    {
1313
-        $count_regs_for_this_datetime = EEM_Registration::instance()->count(
1314
-            [
1315
-                [
1316
-                    'STS_ID'                 => RegStatus::APPROVED,
1317
-                    'REG_deleted'            => 0,
1318
-                    'Ticket.Datetime.DTT_ID' => $this->ID(),
1319
-                ],
1320
-            ]
1321
-        );
1322
-        $this->set_sold($count_regs_for_this_datetime);
1323
-        $this->save();
1324
-        return $count_regs_for_this_datetime;
1325
-    }
1326
-
1327
-
1328
-    /**
1329
-     * Adds a venue to this event
1330
-     *
1331
-     * @param int|EE_Venue /int $venue_id_or_obj
1332
-     * @return EE_Base_Class|EE_Venue
1333
-     * @throws EE_Error
1334
-     * @throws ReflectionException
1335
-     */
1336
-    public function add_venue($venue_id_or_obj): EE_Venue
1337
-    {
1338
-        return $this->_add_relation_to($venue_id_or_obj, 'Venue');
1339
-    }
1340
-
1341
-
1342
-    /**
1343
-     * Removes a venue from the event
1344
-     *
1345
-     * @param EE_Venue /int $venue_id_or_obj
1346
-     * @return EE_Base_Class|EE_Venue
1347
-     * @throws EE_Error
1348
-     * @throws ReflectionException
1349
-     */
1350
-    public function remove_venue($venue_id_or_obj): EE_Venue
1351
-    {
1352
-        $venue_id_or_obj = ! empty($venue_id_or_obj) ? $venue_id_or_obj : $this->venue();
1353
-        return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
1354
-    }
1355
-
1356
-
1357
-    /**
1358
-     * Gets the venue related to the event. May provide additional $query_params if desired
1359
-     *
1360
-     * @param array $query_params @see
1361
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1362
-     * @return int
1363
-     * @throws EE_Error
1364
-     * @throws ReflectionException
1365
-     */
1366
-    public function venue_ID(array $query_params = []): int
1367
-    {
1368
-        // If no $query_params have been passed, use the VNU_ID assigned to the Datetime itself
1369
-        if (empty($query_params)){
1370
-            return (int) $this->get('VNU_ID');
1371
-        }
1372
-        // $query_params set, pull the first related venue using those
1373
-        $venue = $this->get_first_related('Venue', $query_params);
1374
-        return $venue instanceof EE_Venue
1375
-            ? $venue->ID()
1376
-            : 0;
1377
-    }
1378
-
1379
-
1380
-    /**
1381
-     * Gets the venue related to the event. May provide additional $query_params if desired
1382
-     *
1383
-     * @param array $query_params @see
1384
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1385
-     * @return EE_Base_Class|EE_Venue|null
1386
-     * @throws EE_Error
1387
-     * @throws ReflectionException
1388
-     */
1389
-    public function venue(array $query_params = [])
1390
-    {
1391
-        // If no $query_params have been passed, use the VNU_ID assigned to the Datetime itself
1392
-        if (empty($query_params)){
1393
-            $VNU_ID = $this->venue_ID();
1394
-            return $VNU_ID ? EEM_Venue::instance()->get_one_by_ID($VNU_ID) : null;
1395
-        }
1396
-        return $this->get_first_related('Venue', $query_params);
1397
-    }
1398
-
1399
-
1400
-    /**
1401
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1402
-     * @param string                   $relationName
1403
-     * @param array                    $extra_join_model_fields_n_values
1404
-     * @param string|null              $cache_id
1405
-     * @return EE_Base_Class
1406
-     * @throws EE_Error
1407
-     * @throws ReflectionException
1408
-     * @since   5.0.0.p
1409
-     */
1410
-    public function _add_relation_to(
1411
-        $otherObjectModelObjectOrID,
1412
-        $relationName,
1413
-        $extra_join_model_fields_n_values = [],
1414
-        $cache_id = null
1415
-    ) {
1416
-        // if we're adding a new relation to a ticket
1417
-        if ($relationName === 'Ticket' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1418
-            /** @var EE_Ticket $ticket */
1419
-            $ticket = EEM_Ticket::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1420
-            $this->increaseSold($ticket->sold(), false);
1421
-            $this->increaseReserved($ticket->reserved());
1422
-            $this->save();
1423
-            $otherObjectModelObjectOrID = $ticket;
1424
-        }
1425
-        return parent::_add_relation_to(
1426
-            $otherObjectModelObjectOrID,
1427
-            $relationName,
1428
-            $extra_join_model_fields_n_values,
1429
-            $cache_id
1430
-        );
1431
-    }
1432
-
1433
-
1434
-    /**
1435
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1436
-     * @param string                   $relationName
1437
-     * @param array                    $where_query
1438
-     * @return bool|EE_Base_Class|null
1439
-     * @throws EE_Error
1440
-     * @throws ReflectionException
1441
-     * @since   5.0.0.p
1442
-     */
1443
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1444
-    {
1445
-        if ($relationName === 'Ticket' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1446
-            /** @var EE_Ticket $ticket */
1447
-            $ticket = EEM_Ticket::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1448
-            $this->decreaseSold($ticket->sold());
1449
-            $this->decreaseReserved($ticket->reserved());
1450
-            $this->save();
1451
-            $otherObjectModelObjectOrID = $ticket;
1452
-        }
1453
-        return parent::_remove_relation_to(
1454
-            $otherObjectModelObjectOrID,
1455
-            $relationName,
1456
-            $where_query
1457
-        );
1458
-    }
1459
-
1460
-
1461
-    /**
1462
-     * Removes ALL the related things for the $relationName.
1463
-     *
1464
-     * @param string $relationName
1465
-     * @param array  $where_query_params
1466
-     * @return EE_Base_Class
1467
-     * @throws ReflectionException
1468
-     * @throws InvalidArgumentException
1469
-     * @throws InvalidInterfaceException
1470
-     * @throws InvalidDataTypeException
1471
-     * @throws EE_Error
1472
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1473
-     */
1474
-    public function _remove_relations($relationName, $where_query_params = [])
1475
-    {
1476
-        if ($relationName === 'Ticket') {
1477
-            $tickets = $this->tickets();
1478
-            foreach ($tickets as $ticket) {
1479
-                $this->decreaseSold($ticket->sold());
1480
-                $this->decreaseReserved($ticket->reserved());
1481
-                $this->save();
1482
-            }
1483
-        }
1484
-        return parent::_remove_relations($relationName, $where_query_params);
1485
-    }
1486
-
1487
-
1488
-    /*******************************************************************
16
+	/**
17
+	 * constant used by get_active_status, indicates datetime has no more available spaces
18
+	 */
19
+	const sold_out = 'DTS';
20
+
21
+	/**
22
+	 * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
23
+	 */
24
+	const active = 'DTA';
25
+
26
+	/**
27
+	 * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
28
+	 * expired
29
+	 */
30
+	const upcoming = 'DTU';
31
+
32
+	/**
33
+	 * Datetime is postponed
34
+	 */
35
+	const postponed = 'DTP';
36
+
37
+	/**
38
+	 * Datetime is cancelled
39
+	 */
40
+	const cancelled = 'DTC';
41
+
42
+	/**
43
+	 * constant used by get_active_status, indicates datetime has expired (event is over)
44
+	 */
45
+	const expired = 'DTE';
46
+
47
+	/**
48
+	 * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
49
+	 */
50
+	const inactive = 'DTI';
51
+
52
+
53
+	/**
54
+	 * @param array  $props_n_values    incoming values
55
+	 * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
56
+	 * @param array  $date_formats      incoming date_formats in an array where the first value is the date_format
57
+	 *                                  and the second value is the time format
58
+	 * @return EE_Datetime
59
+	 * @throws ReflectionException
60
+	 * @throws InvalidArgumentException
61
+	 * @throws InvalidInterfaceException
62
+	 * @throws InvalidDataTypeException
63
+	 * @throws EE_Error
64
+	 */
65
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
66
+	{
67
+		$has_object = parent::_check_for_object(
68
+			$props_n_values,
69
+			__CLASS__,
70
+			$timezone,
71
+			$date_formats
72
+		);
73
+		return $has_object
74
+			? $has_object
75
+			: new self($props_n_values, false, $timezone, $date_formats);
76
+	}
77
+
78
+
79
+	/**
80
+	 * @param array  $props_n_values  incoming values from the database
81
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
+	 *                                the website will be used.
83
+	 * @return EE_Datetime
84
+	 * @throws ReflectionException
85
+	 * @throws InvalidArgumentException
86
+	 * @throws InvalidInterfaceException
87
+	 * @throws InvalidDataTypeException
88
+	 * @throws EE_Error
89
+	 */
90
+	public static function new_instance_from_db($props_n_values = [], $timezone = '')
91
+	{
92
+		return new self($props_n_values, true, $timezone);
93
+	}
94
+
95
+
96
+	/**
97
+	 * @param $name
98
+	 * @throws ReflectionException
99
+	 * @throws InvalidArgumentException
100
+	 * @throws InvalidInterfaceException
101
+	 * @throws InvalidDataTypeException
102
+	 * @throws EE_Error
103
+	 */
104
+	public function set_name($name)
105
+	{
106
+		$this->set('DTT_name', $name);
107
+	}
108
+
109
+
110
+	/**
111
+	 * @param $description
112
+	 * @throws ReflectionException
113
+	 * @throws InvalidArgumentException
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws InvalidDataTypeException
116
+	 * @throws EE_Error
117
+	 */
118
+	public function set_description($description)
119
+	{
120
+		$this->set('DTT_description', $description);
121
+	}
122
+
123
+
124
+	/**
125
+	 * Set event start date
126
+	 * set the start date for an event
127
+	 *
128
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
129
+	 * @throws ReflectionException
130
+	 * @throws InvalidArgumentException
131
+	 * @throws InvalidInterfaceException
132
+	 * @throws InvalidDataTypeException
133
+	 * @throws EE_Error
134
+	 */
135
+	public function set_start_date($date)
136
+	{
137
+		$this->_set_date_for($date, 'DTT_EVT_start');
138
+	}
139
+
140
+
141
+	/**
142
+	 * Set event start time
143
+	 * set the start time for an event
144
+	 *
145
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
146
+	 * @throws ReflectionException
147
+	 * @throws InvalidArgumentException
148
+	 * @throws InvalidInterfaceException
149
+	 * @throws InvalidDataTypeException
150
+	 * @throws EE_Error
151
+	 */
152
+	public function set_start_time($time)
153
+	{
154
+		$this->_set_time_for($time, 'DTT_EVT_start');
155
+	}
156
+
157
+
158
+	/**
159
+	 * Set event end date
160
+	 * set the end date for an event
161
+	 *
162
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
163
+	 * @throws ReflectionException
164
+	 * @throws InvalidArgumentException
165
+	 * @throws InvalidInterfaceException
166
+	 * @throws InvalidDataTypeException
167
+	 * @throws EE_Error
168
+	 */
169
+	public function set_end_date($date)
170
+	{
171
+		$this->_set_date_for($date, 'DTT_EVT_end');
172
+	}
173
+
174
+
175
+	/**
176
+	 * Set event end time
177
+	 * set the end time for an event
178
+	 *
179
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
180
+	 * @throws ReflectionException
181
+	 * @throws InvalidArgumentException
182
+	 * @throws InvalidInterfaceException
183
+	 * @throws InvalidDataTypeException
184
+	 * @throws EE_Error
185
+	 */
186
+	public function set_end_time($time)
187
+	{
188
+		$this->_set_time_for($time, 'DTT_EVT_end');
189
+	}
190
+
191
+
192
+	/**
193
+	 * Set registration limit
194
+	 * set the maximum number of attendees that can be registered for this datetime slot
195
+	 *
196
+	 * @param int|float $reg_limit
197
+	 * @throws ReflectionException
198
+	 * @throws InvalidArgumentException
199
+	 * @throws InvalidInterfaceException
200
+	 * @throws InvalidDataTypeException
201
+	 * @throws EE_Error
202
+	 */
203
+	public function set_reg_limit($reg_limit)
204
+	{
205
+		$this->set('DTT_reg_limit', $reg_limit);
206
+	}
207
+
208
+
209
+	/**
210
+	 * get the number of tickets sold for this datetime slot
211
+	 *
212
+	 * @return mixed int on success, FALSE on fail
213
+	 * @throws ReflectionException
214
+	 * @throws InvalidArgumentException
215
+	 * @throws InvalidInterfaceException
216
+	 * @throws InvalidDataTypeException
217
+	 * @throws EE_Error
218
+	 */
219
+	public function sold()
220
+	{
221
+		return $this->get_raw('DTT_sold');
222
+	}
223
+
224
+
225
+	/**
226
+	 * @param int $sold
227
+	 * @throws ReflectionException
228
+	 * @throws InvalidArgumentException
229
+	 * @throws InvalidInterfaceException
230
+	 * @throws InvalidDataTypeException
231
+	 * @throws EE_Error
232
+	 */
233
+	public function set_sold($sold)
234
+	{
235
+		// sold can not go below zero
236
+		$sold = max(0, $sold);
237
+		$this->set('DTT_sold', $sold);
238
+	}
239
+
240
+
241
+	/**
242
+	 * Increments sold by amount passed by $qty, and persists it immediately to the database.
243
+	 * Simultaneously decreases the reserved count, unless $also_decrease_reserved is false.
244
+	 *
245
+	 * @param int     $qty
246
+	 * @param boolean $also_decrease_reserved
247
+	 * @return boolean indicating success
248
+	 * @throws ReflectionException
249
+	 * @throws InvalidArgumentException
250
+	 * @throws InvalidInterfaceException
251
+	 * @throws InvalidDataTypeException
252
+	 * @throws EE_Error
253
+	 */
254
+	public function increaseSold(int $qty = 1, bool $also_decrease_reserved = true): bool
255
+	{
256
+		$qty = absint($qty);
257
+		if ($also_decrease_reserved) {
258
+			$success = $this->adjustNumericFieldsInDb(
259
+				[
260
+					'DTT_reserved' => $qty * -1,
261
+					'DTT_sold'     => $qty,
262
+				]
263
+			);
264
+		} else {
265
+			$success = $this->adjustNumericFieldsInDb(
266
+				[
267
+					'DTT_sold' => $qty,
268
+				]
269
+			);
270
+		}
271
+
272
+		do_action(
273
+			'AHEE__EE_Datetime__increase_sold',
274
+			$this,
275
+			$qty,
276
+			$this->sold(),
277
+			$success
278
+		);
279
+		return $success;
280
+	}
281
+
282
+
283
+	/**
284
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
285
+	 * to save afterwards.)
286
+	 *
287
+	 * @param int $qty
288
+	 * @return boolean indicating success
289
+	 * @throws ReflectionException
290
+	 * @throws InvalidArgumentException
291
+	 * @throws InvalidInterfaceException
292
+	 * @throws InvalidDataTypeException
293
+	 * @throws EE_Error
294
+	 */
295
+	public function decreaseSold(int $qty = 1): bool
296
+	{
297
+		$qty     = absint($qty);
298
+		$success = $this->adjustNumericFieldsInDb(
299
+			[
300
+				'DTT_sold' => $qty * -1,
301
+			]
302
+		);
303
+		do_action(
304
+			'AHEE__EE_Datetime__decrease_sold',
305
+			$this,
306
+			$qty,
307
+			$this->sold(),
308
+			$success
309
+		);
310
+		return $success;
311
+	}
312
+
313
+
314
+	/**
315
+	 * Gets qty of reserved tickets for this datetime
316
+	 *
317
+	 * @return int
318
+	 * @throws ReflectionException
319
+	 * @throws InvalidArgumentException
320
+	 * @throws InvalidInterfaceException
321
+	 * @throws InvalidDataTypeException
322
+	 * @throws EE_Error
323
+	 */
324
+	public function reserved(): int
325
+	{
326
+		return $this->get_raw('DTT_reserved');
327
+	}
328
+
329
+
330
+	/**
331
+	 * Sets qty of reserved tickets for this datetime
332
+	 *
333
+	 * @param int $reserved
334
+	 * @throws ReflectionException
335
+	 * @throws InvalidArgumentException
336
+	 * @throws InvalidInterfaceException
337
+	 * @throws InvalidDataTypeException
338
+	 * @throws EE_Error
339
+	 */
340
+	public function set_reserved(int $reserved)
341
+	{
342
+		// reserved can not go below zero
343
+		$reserved = max(0, $reserved);
344
+		$this->set('DTT_reserved', $reserved);
345
+	}
346
+
347
+
348
+	/**
349
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
350
+	 *
351
+	 * @param int $qty
352
+	 * @return boolean indicating success
353
+	 * @throws ReflectionException
354
+	 * @throws InvalidArgumentException
355
+	 * @throws InvalidInterfaceException
356
+	 * @throws InvalidDataTypeException
357
+	 * @throws EE_Error
358
+	 */
359
+	public function increaseReserved(int $qty = 1): bool
360
+	{
361
+		$qty     = absint($qty);
362
+		$success = $this->incrementFieldConditionallyInDb(
363
+			'DTT_reserved',
364
+			'DTT_sold',
365
+			'DTT_reg_limit',
366
+			$qty
367
+		);
368
+		do_action(
369
+			'AHEE__EE_Datetime__increase_reserved',
370
+			$this,
371
+			$qty,
372
+			$this->reserved(),
373
+			$success
374
+		);
375
+		return $success;
376
+	}
377
+
378
+
379
+	/**
380
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
381
+	 *
382
+	 * @param int $qty
383
+	 * @return boolean indicating success
384
+	 * @throws ReflectionException
385
+	 * @throws InvalidArgumentException
386
+	 * @throws InvalidInterfaceException
387
+	 * @throws InvalidDataTypeException
388
+	 * @throws EE_Error
389
+	 */
390
+	public function decreaseReserved(int $qty = 1): bool
391
+	{
392
+		$qty     = absint($qty);
393
+		$success = $this->adjustNumericFieldsInDb(
394
+			[
395
+				'DTT_reserved' => $qty * -1,
396
+			]
397
+		);
398
+		do_action(
399
+			'AHEE__EE_Datetime__decrease_reserved',
400
+			$this,
401
+			$qty,
402
+			$this->reserved(),
403
+			$success
404
+		);
405
+		return $success;
406
+	}
407
+
408
+
409
+	/**
410
+	 * total sold and reserved tickets
411
+	 *
412
+	 * @return int
413
+	 * @throws ReflectionException
414
+	 * @throws InvalidArgumentException
415
+	 * @throws InvalidInterfaceException
416
+	 * @throws InvalidDataTypeException
417
+	 * @throws EE_Error
418
+	 */
419
+	public function sold_and_reserved(): int
420
+	{
421
+		return $this->sold() + $this->reserved();
422
+	}
423
+
424
+
425
+	/**
426
+	 * returns the datetime name
427
+	 *
428
+	 * @return string
429
+	 * @throws ReflectionException
430
+	 * @throws InvalidArgumentException
431
+	 * @throws InvalidInterfaceException
432
+	 * @throws InvalidDataTypeException
433
+	 * @throws EE_Error
434
+	 */
435
+	public function name(): string
436
+	{
437
+		return $this->get('DTT_name');
438
+	}
439
+
440
+
441
+	/**
442
+	 * returns the datetime description
443
+	 *
444
+	 * @return string
445
+	 * @throws ReflectionException
446
+	 * @throws InvalidArgumentException
447
+	 * @throws InvalidInterfaceException
448
+	 * @throws InvalidDataTypeException
449
+	 * @throws EE_Error
450
+	 */
451
+	public function description(): string
452
+	{
453
+		return $this->get('DTT_description');
454
+	}
455
+
456
+
457
+	/**
458
+	 * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
459
+	 *
460
+	 * @return boolean  TRUE if is primary, FALSE if not.
461
+	 * @throws ReflectionException
462
+	 * @throws InvalidArgumentException
463
+	 * @throws InvalidInterfaceException
464
+	 * @throws InvalidDataTypeException
465
+	 * @throws EE_Error
466
+	 */
467
+	public function is_primary(): bool
468
+	{
469
+		return $this->get('DTT_is_primary');
470
+	}
471
+
472
+
473
+	/**
474
+	 * This helper simply returns the order for the datetime
475
+	 *
476
+	 * @return int  The order of the datetime for this event.
477
+	 * @throws ReflectionException
478
+	 * @throws InvalidArgumentException
479
+	 * @throws InvalidInterfaceException
480
+	 * @throws InvalidDataTypeException
481
+	 * @throws EE_Error
482
+	 */
483
+	public function order(): int
484
+	{
485
+		return $this->get('DTT_order');
486
+	}
487
+
488
+
489
+	/**
490
+	 * This helper simply returns the parent id for the datetime
491
+	 *
492
+	 * @return int
493
+	 * @throws ReflectionException
494
+	 * @throws InvalidArgumentException
495
+	 * @throws InvalidInterfaceException
496
+	 * @throws InvalidDataTypeException
497
+	 * @throws EE_Error
498
+	 */
499
+	public function parent(): int
500
+	{
501
+		return $this->get('DTT_parent');
502
+	}
503
+
504
+
505
+	/**
506
+	 * show date and/or time
507
+	 *
508
+	 * @param string $date_or_time    whether to display a date or time or both
509
+	 * @param string $start_or_end    whether to display start or end datetimes
510
+	 * @param string $dt_frmt
511
+	 * @param string $tm_frmt
512
+	 * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
513
+	 *                                otherwise we use the standard formats)
514
+	 * @return string|bool  string on success, FALSE on fail
515
+	 * @throws ReflectionException
516
+	 * @throws InvalidArgumentException
517
+	 * @throws InvalidInterfaceException
518
+	 * @throws InvalidDataTypeException
519
+	 * @throws EE_Error
520
+	 */
521
+	private function _show_datetime(
522
+		$date_or_time = null,
523
+		$start_or_end = 'start',
524
+		$dt_frmt = '',
525
+		$tm_frmt = '',
526
+		$echo = false
527
+	) {
528
+		$field_name = "DTT_EVT_{$start_or_end}";
529
+		$dtt        = $this->_get_datetime(
530
+			$field_name,
531
+			$dt_frmt,
532
+			$tm_frmt,
533
+			$date_or_time,
534
+			$echo
535
+		);
536
+		if (! $echo) {
537
+			return $dtt;
538
+		}
539
+		return '';
540
+	}
541
+
542
+
543
+	/**
544
+	 * get event start date.  Provide either the date format, or NULL to re-use the
545
+	 * last-used format, or '' to use the default date format
546
+	 *
547
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
548
+	 * @return mixed            string on success, FALSE on fail
549
+	 * @throws ReflectionException
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidInterfaceException
552
+	 * @throws InvalidDataTypeException
553
+	 * @throws EE_Error
554
+	 */
555
+	public function start_date($dt_frmt = '')
556
+	{
557
+		return $this->_show_datetime('D', 'start', $dt_frmt);
558
+	}
559
+
560
+
561
+	/**
562
+	 * Echoes start_date()
563
+	 *
564
+	 * @param string $dt_frmt
565
+	 * @throws ReflectionException
566
+	 * @throws InvalidArgumentException
567
+	 * @throws InvalidInterfaceException
568
+	 * @throws InvalidDataTypeException
569
+	 * @throws EE_Error
570
+	 */
571
+	public function e_start_date($dt_frmt = '')
572
+	{
573
+		$this->_show_datetime('D', 'start', $dt_frmt, null, true);
574
+	}
575
+
576
+
577
+	/**
578
+	 * get end date. Provide either the date format, or NULL to re-use the
579
+	 * last-used format, or '' to use the default date format
580
+	 *
581
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
582
+	 * @return mixed            string on success, FALSE on fail
583
+	 * @throws ReflectionException
584
+	 * @throws InvalidArgumentException
585
+	 * @throws InvalidInterfaceException
586
+	 * @throws InvalidDataTypeException
587
+	 * @throws EE_Error
588
+	 */
589
+	public function end_date($dt_frmt = '')
590
+	{
591
+		return $this->_show_datetime('D', 'end', $dt_frmt);
592
+	}
593
+
594
+
595
+	/**
596
+	 * Echoes the end date. See end_date()
597
+	 *
598
+	 * @param string $dt_frmt
599
+	 * @throws ReflectionException
600
+	 * @throws InvalidArgumentException
601
+	 * @throws InvalidInterfaceException
602
+	 * @throws InvalidDataTypeException
603
+	 * @throws EE_Error
604
+	 */
605
+	public function e_end_date($dt_frmt = '')
606
+	{
607
+		$this->_show_datetime('D', 'end', $dt_frmt, null, true);
608
+	}
609
+
610
+
611
+	/**
612
+	 * get date_range - meaning the start AND end date
613
+	 *
614
+	 * @access public
615
+	 * @param string $dt_frmt     string representation of date format defaults to WP settings
616
+	 * @param string $conjunction conjunction junction what's your function ?
617
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
+	 * @return mixed              string on success, FALSE on fail
619
+	 * @throws ReflectionException
620
+	 * @throws InvalidArgumentException
621
+	 * @throws InvalidInterfaceException
622
+	 * @throws InvalidDataTypeException
623
+	 * @throws EE_Error
624
+	 */
625
+	public function date_range($dt_frmt = '', $conjunction = ' - ')
626
+	{
627
+		$dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
628
+		$start   = str_replace(
629
+			' ',
630
+			'&nbsp;',
631
+			$this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632
+		);
633
+		$end     = str_replace(
634
+			' ',
635
+			'&nbsp;',
636
+			$this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637
+		);
638
+		return $start !== $end ? $start . $conjunction . $end : $start;
639
+	}
640
+
641
+
642
+	/**
643
+	 * @param string $dt_frmt
644
+	 * @param string $conjunction
645
+	 * @throws ReflectionException
646
+	 * @throws InvalidArgumentException
647
+	 * @throws InvalidInterfaceException
648
+	 * @throws InvalidDataTypeException
649
+	 * @throws EE_Error
650
+	 */
651
+	public function e_date_range($dt_frmt = '', $conjunction = ' - ')
652
+	{
653
+		echo esc_html($this->date_range($dt_frmt, $conjunction));
654
+	}
655
+
656
+
657
+	/**
658
+	 * get start time
659
+	 *
660
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
661
+	 * @return mixed        string on success, FALSE on fail
662
+	 * @throws ReflectionException
663
+	 * @throws InvalidArgumentException
664
+	 * @throws InvalidInterfaceException
665
+	 * @throws InvalidDataTypeException
666
+	 * @throws EE_Error
667
+	 */
668
+	public function start_time($tm_format = '')
669
+	{
670
+		return $this->_show_datetime('T', 'start', null, $tm_format);
671
+	}
672
+
673
+
674
+	/**
675
+	 * @param string $tm_format
676
+	 * @throws ReflectionException
677
+	 * @throws InvalidArgumentException
678
+	 * @throws InvalidInterfaceException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws EE_Error
681
+	 */
682
+	public function e_start_time($tm_format = '')
683
+	{
684
+		$this->_show_datetime('T', 'start', null, $tm_format, true);
685
+	}
686
+
687
+
688
+	/**
689
+	 * get end time
690
+	 *
691
+	 * @param string $tm_format string representation of time format defaults to 'g:i a'
692
+	 * @return mixed                string on success, FALSE on fail
693
+	 * @throws ReflectionException
694
+	 * @throws InvalidArgumentException
695
+	 * @throws InvalidInterfaceException
696
+	 * @throws InvalidDataTypeException
697
+	 * @throws EE_Error
698
+	 */
699
+	public function end_time($tm_format = '')
700
+	{
701
+		return $this->_show_datetime('T', 'end', null, $tm_format);
702
+	}
703
+
704
+
705
+	/**
706
+	 * @param string $tm_format
707
+	 * @throws ReflectionException
708
+	 * @throws InvalidArgumentException
709
+	 * @throws InvalidInterfaceException
710
+	 * @throws InvalidDataTypeException
711
+	 * @throws EE_Error
712
+	 */
713
+	public function e_end_time($tm_format = '')
714
+	{
715
+		$this->_show_datetime('T', 'end', null, $tm_format, true);
716
+	}
717
+
718
+
719
+	/**
720
+	 * get time_range
721
+	 *
722
+	 * @access public
723
+	 * @param string $tm_format   string representation of time format defaults to 'g:i a'
724
+	 * @param string $conjunction conjunction junction what's your function ?
725
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
+	 * @return mixed              string on success, FALSE on fail
727
+	 * @throws ReflectionException
728
+	 * @throws InvalidArgumentException
729
+	 * @throws InvalidInterfaceException
730
+	 * @throws InvalidDataTypeException
731
+	 * @throws EE_Error
732
+	 */
733
+	public function time_range($tm_format = '', $conjunction = ' - ')
734
+	{
735
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
736
+		$start     = str_replace(
737
+			' ',
738
+			'&nbsp;',
739
+			$this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740
+		);
741
+		$end       = str_replace(
742
+			' ',
743
+			'&nbsp;',
744
+			$this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745
+		);
746
+		return $start !== $end ? $start . $conjunction . $end : $start;
747
+	}
748
+
749
+
750
+	/**
751
+	 * @param string $tm_format
752
+	 * @param string $conjunction
753
+	 * @throws ReflectionException
754
+	 * @throws InvalidArgumentException
755
+	 * @throws InvalidInterfaceException
756
+	 * @throws InvalidDataTypeException
757
+	 * @throws EE_Error
758
+	 */
759
+	public function e_time_range($tm_format = '', $conjunction = ' - ')
760
+	{
761
+		echo esc_html($this->time_range($tm_format, $conjunction));
762
+	}
763
+
764
+
765
+	/**
766
+	 * This returns a range representation of the date and times.
767
+	 * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
768
+	 * Also, the return value is localized.
769
+	 *
770
+	 * @param string $dt_format
771
+	 * @param string $tm_format
772
+	 * @param string $conjunction used between two different dates or times.
773
+	 *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
774
+	 * @param string $separator   used between the date and time formats.
775
+	 *                            ex: Dec 1, 2016{$separator}2pm
776
+	 * @return string
777
+	 * @throws ReflectionException
778
+	 * @throws InvalidArgumentException
779
+	 * @throws InvalidInterfaceException
780
+	 * @throws InvalidDataTypeException
781
+	 * @throws EE_Error
782
+	 */
783
+	public function date_and_time_range(
784
+		$dt_format = '',
785
+		$tm_format = '',
786
+		$conjunction = ' - ',
787
+		$separator = ' '
788
+	) {
789
+		$dt_format   = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790
+		$tm_format   = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
+		$full_format = $dt_format . $separator . $tm_format;
792
+		// the range output depends on various conditions
793
+		switch (true) {
794
+			// start date timestamp and end date timestamp are the same.
795
+			case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
796
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
797
+				break;
798
+			// start and end date are the same but times are different
799
+			case ($this->start_date() === $this->end_date()):
800
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
801
+						  . $conjunction
802
+						  . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
803
+				break;
804
+			// all other conditions
805
+			default:
806
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
807
+						  . $conjunction
808
+						  . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
809
+				break;
810
+		}
811
+		return $output;
812
+	}
813
+
814
+
815
+	/**
816
+	 * This echos the results of date and time range.
817
+	 *
818
+	 * @param string $dt_format
819
+	 * @param string $tm_format
820
+	 * @param string $conjunction
821
+	 * @return void
822
+	 * @throws ReflectionException
823
+	 * @throws InvalidArgumentException
824
+	 * @throws InvalidInterfaceException
825
+	 * @throws InvalidDataTypeException
826
+	 * @throws EE_Error
827
+	 * @see date_and_time_range() for more details on purpose.
828
+	 */
829
+	public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
830
+	{
831
+		echo esc_html($this->date_and_time_range($dt_format, $tm_format, $conjunction));
832
+	}
833
+
834
+
835
+	/**
836
+	 * get start date and start time
837
+	 *
838
+	 * @param string $dt_format - string representation of date format defaults to 'F j, Y'
839
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
840
+	 * @return    mixed    string on success, FALSE on fail
841
+	 * @throws ReflectionException
842
+	 * @throws InvalidArgumentException
843
+	 * @throws InvalidInterfaceException
844
+	 * @throws InvalidDataTypeException
845
+	 * @throws EE_Error
846
+	 */
847
+	public function start_date_and_time($dt_format = '', $tm_format = '')
848
+	{
849
+		return $this->_show_datetime('', 'start', $dt_format, $tm_format);
850
+	}
851
+
852
+
853
+	/**
854
+	 * @param string $dt_frmt
855
+	 * @param string $tm_format
856
+	 * @throws ReflectionException
857
+	 * @throws InvalidArgumentException
858
+	 * @throws InvalidInterfaceException
859
+	 * @throws InvalidDataTypeException
860
+	 * @throws EE_Error
861
+	 */
862
+	public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
863
+	{
864
+		$this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
865
+	}
866
+
867
+
868
+	/**
869
+	 * Shows the length of the event (start to end time).
870
+	 * Can be shown in 'seconds','minutes','hours', or 'days'.
871
+	 * By default, rounds up. (So if you use 'days', and then event
872
+	 * only occurs for 1 hour, it will return 1 day).
873
+	 *
874
+	 * @param string $units 'seconds','minutes','hours','days'
875
+	 * @param bool   $round_up
876
+	 * @return float|int|mixed
877
+	 * @throws ReflectionException
878
+	 * @throws InvalidArgumentException
879
+	 * @throws InvalidInterfaceException
880
+	 * @throws InvalidDataTypeException
881
+	 * @throws EE_Error
882
+	 */
883
+	public function length($units = 'seconds', $round_up = false)
884
+	{
885
+		$start           = $this->get_raw('DTT_EVT_start');
886
+		$end             = $this->get_raw('DTT_EVT_end');
887
+		$length_in_units = $end - $start;
888
+		switch ($units) {
889
+			// NOTE: We purposefully don't use "break;" in order to chain the divisions
890
+			/** @noinspection PhpMissingBreakStatementInspection */
891
+			// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
892
+			case 'days':
893
+				$length_in_units /= 24;
894
+			/** @noinspection PhpMissingBreakStatementInspection */
895
+			case 'hours':
896
+				// fall through is intentional
897
+				$length_in_units /= 60;
898
+			/** @noinspection PhpMissingBreakStatementInspection */
899
+			case 'minutes':
900
+				// fall through is intentional
901
+				$length_in_units /= 60;
902
+			case 'seconds':
903
+			default:
904
+				$length_in_units = ceil($length_in_units);
905
+		}
906
+		// phpcs:enable
907
+		if ($round_up) {
908
+			$length_in_units = max($length_in_units, 1);
909
+		}
910
+		return $length_in_units;
911
+	}
912
+
913
+
914
+	/**
915
+	 *        get end date and time
916
+	 *
917
+	 * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
918
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
919
+	 * @return    mixed                string on success, FALSE on fail
920
+	 * @throws ReflectionException
921
+	 * @throws InvalidArgumentException
922
+	 * @throws InvalidInterfaceException
923
+	 * @throws InvalidDataTypeException
924
+	 * @throws EE_Error
925
+	 */
926
+	public function end_date_and_time($dt_frmt = '', $tm_format = '')
927
+	{
928
+		return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
929
+	}
930
+
931
+
932
+	/**
933
+	 * @param string $dt_frmt
934
+	 * @param string $tm_format
935
+	 * @throws ReflectionException
936
+	 * @throws InvalidArgumentException
937
+	 * @throws InvalidInterfaceException
938
+	 * @throws InvalidDataTypeException
939
+	 * @throws EE_Error
940
+	 */
941
+	public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
942
+	{
943
+		$this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
944
+	}
945
+
946
+
947
+	/**
948
+	 *        get start timestamp
949
+	 *
950
+	 * @return        int
951
+	 * @throws ReflectionException
952
+	 * @throws InvalidArgumentException
953
+	 * @throws InvalidInterfaceException
954
+	 * @throws InvalidDataTypeException
955
+	 * @throws EE_Error
956
+	 */
957
+	public function start()
958
+	{
959
+		return $this->get_raw('DTT_EVT_start');
960
+	}
961
+
962
+
963
+	/**
964
+	 *        get end timestamp
965
+	 *
966
+	 * @return        int
967
+	 * @throws ReflectionException
968
+	 * @throws InvalidArgumentException
969
+	 * @throws InvalidInterfaceException
970
+	 * @throws InvalidDataTypeException
971
+	 * @throws EE_Error
972
+	 */
973
+	public function end()
974
+	{
975
+		return $this->get_raw('DTT_EVT_end');
976
+	}
977
+
978
+
979
+	/**
980
+	 *    get the registration limit for this datetime slot
981
+	 *
982
+	 * @return int|float                int = finite limit   EE_INF(float) = unlimited
983
+	 * @throws ReflectionException
984
+	 * @throws InvalidArgumentException
985
+	 * @throws InvalidInterfaceException
986
+	 * @throws InvalidDataTypeException
987
+	 * @throws EE_Error
988
+	 */
989
+	public function reg_limit()
990
+	{
991
+		return $this->get_raw('DTT_reg_limit');
992
+	}
993
+
994
+
995
+	/**
996
+	 *    have the tickets sold for this datetime, met or exceed the registration limit ?
997
+	 *
998
+	 * @return boolean
999
+	 * @throws ReflectionException
1000
+	 * @throws InvalidArgumentException
1001
+	 * @throws InvalidInterfaceException
1002
+	 * @throws InvalidDataTypeException
1003
+	 * @throws EE_Error
1004
+	 */
1005
+	public function sold_out()
1006
+	{
1007
+		return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
1008
+	}
1009
+
1010
+
1011
+	/**
1012
+	 * return the total number of spaces remaining at this venue.
1013
+	 * This only takes the venue's capacity into account, NOT the tickets available for sale
1014
+	 *
1015
+	 * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
1016
+	 *                               Because if all tickets attached to this datetime have no spaces left,
1017
+	 *                               then this datetime IS effectively sold out.
1018
+	 *                               However, there are cases where we just want to know the spaces
1019
+	 *                               remaining for this particular datetime, hence the flag.
1020
+	 * @return int|float
1021
+	 * @throws ReflectionException
1022
+	 * @throws InvalidArgumentException
1023
+	 * @throws InvalidInterfaceException
1024
+	 * @throws InvalidDataTypeException
1025
+	 * @throws EE_Error
1026
+	 */
1027
+	public function spaces_remaining($consider_tickets = false)
1028
+	{
1029
+		// tickets remaining available for purchase
1030
+		// no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031
+		$dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
+		if (! $consider_tickets) {
1033
+			return $dtt_remaining;
1034
+		}
1035
+		$tickets_remaining = $this->tickets_remaining();
1036
+		return min($dtt_remaining, $tickets_remaining);
1037
+	}
1038
+
1039
+
1040
+	/**
1041
+	 * Counts the total tickets available
1042
+	 * (from all the different types of tickets which are available for this datetime).
1043
+	 *
1044
+	 * @param array $query_params @see
1045
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1046
+	 * @return int
1047
+	 * @throws ReflectionException
1048
+	 * @throws InvalidArgumentException
1049
+	 * @throws InvalidInterfaceException
1050
+	 * @throws InvalidDataTypeException
1051
+	 * @throws EE_Error
1052
+	 */
1053
+	public function tickets_remaining($query_params = [])
1054
+	{
1055
+		$sum     = 0;
1056
+		$tickets = $this->tickets($query_params);
1057
+		if (! empty($tickets)) {
1058
+			foreach ($tickets as $ticket) {
1059
+				if ($ticket instanceof EE_Ticket) {
1060
+					// get the actual amount of tickets that can be sold
1061
+					$qty = $ticket->qty('saleable');
1062
+					if ($qty === EE_INF) {
1063
+						return EE_INF;
1064
+					}
1065
+					// no negative ticket quantities plz
1066
+					if ($qty > 0) {
1067
+						$sum += $qty;
1068
+					}
1069
+				}
1070
+			}
1071
+		}
1072
+		return $sum;
1073
+	}
1074
+
1075
+
1076
+	/**
1077
+	 * Gets the count of all the tickets available at this datetime (not ticket types)
1078
+	 * before any were sold
1079
+	 *
1080
+	 * @param array $query_params @see
1081
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1082
+	 * @return int
1083
+	 * @throws ReflectionException
1084
+	 * @throws InvalidArgumentException
1085
+	 * @throws InvalidInterfaceException
1086
+	 * @throws InvalidDataTypeException
1087
+	 * @throws EE_Error
1088
+	 */
1089
+	public function sum_tickets_initially_available($query_params = [])
1090
+	{
1091
+		return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1092
+	}
1093
+
1094
+
1095
+	/**
1096
+	 * Returns the lesser-of-the two: spaces remaining at this datetime, or
1097
+	 * the total tickets remaining (a sum of the tickets remaining for each ticket type
1098
+	 * that is available for this datetime).
1099
+	 *
1100
+	 * @return int
1101
+	 * @throws ReflectionException
1102
+	 * @throws InvalidArgumentException
1103
+	 * @throws InvalidInterfaceException
1104
+	 * @throws InvalidDataTypeException
1105
+	 * @throws EE_Error
1106
+	 */
1107
+	public function total_tickets_available_at_this_datetime()
1108
+	{
1109
+		return $this->spaces_remaining(true);
1110
+	}
1111
+
1112
+
1113
+	/**
1114
+	 * This simply compares the internal dtt for the given string with NOW
1115
+	 * and determines if the date is upcoming or not.
1116
+	 *
1117
+	 * @access public
1118
+	 * @return boolean
1119
+	 * @throws ReflectionException
1120
+	 * @throws InvalidArgumentException
1121
+	 * @throws InvalidInterfaceException
1122
+	 * @throws InvalidDataTypeException
1123
+	 * @throws EE_Error
1124
+	 */
1125
+	public function is_upcoming()
1126
+	{
1127
+		return ($this->get_raw('DTT_EVT_start') > time());
1128
+	}
1129
+
1130
+
1131
+	/**
1132
+	 * This simply compares the internal datetime for the given string with NOW
1133
+	 * and returns if the date is active (i.e. start and end time)
1134
+	 *
1135
+	 * @return boolean
1136
+	 * @throws ReflectionException
1137
+	 * @throws InvalidArgumentException
1138
+	 * @throws InvalidInterfaceException
1139
+	 * @throws InvalidDataTypeException
1140
+	 * @throws EE_Error
1141
+	 */
1142
+	public function is_active()
1143
+	{
1144
+		return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1145
+	}
1146
+
1147
+
1148
+	/**
1149
+	 * This simply compares the internal dtt for the given string with NOW
1150
+	 * and determines if the date is expired or not.
1151
+	 *
1152
+	 * @return boolean
1153
+	 * @throws ReflectionException
1154
+	 * @throws InvalidArgumentException
1155
+	 * @throws InvalidInterfaceException
1156
+	 * @throws InvalidDataTypeException
1157
+	 * @throws EE_Error
1158
+	 */
1159
+	public function is_expired()
1160
+	{
1161
+		return ($this->get_raw('DTT_EVT_end') < time());
1162
+	}
1163
+
1164
+
1165
+	/**
1166
+	 * This returns the active status for whether an event is active, upcoming, or expired
1167
+	 *
1168
+	 * @return int return value will be one of the EE_Datetime status constants.
1169
+	 * @throws ReflectionException
1170
+	 * @throws InvalidArgumentException
1171
+	 * @throws InvalidInterfaceException
1172
+	 * @throws InvalidDataTypeException
1173
+	 * @throws EE_Error
1174
+	 */
1175
+	public function get_active_status()
1176
+	{
1177
+		$total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1178
+		if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1179
+			return EE_Datetime::sold_out;
1180
+		}
1181
+		if ($this->is_expired()) {
1182
+			return EE_Datetime::expired;
1183
+		}
1184
+		if ($this->is_upcoming()) {
1185
+			return EE_Datetime::upcoming;
1186
+		}
1187
+		if ($this->is_active()) {
1188
+			return EE_Datetime::active;
1189
+		}
1190
+		return null;
1191
+	}
1192
+
1193
+
1194
+	/**
1195
+	 * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1196
+	 *
1197
+	 * @param boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1198
+	 * @return string
1199
+	 * @throws ReflectionException
1200
+	 * @throws InvalidArgumentException
1201
+	 * @throws InvalidInterfaceException
1202
+	 * @throws InvalidDataTypeException
1203
+	 * @throws EE_Error
1204
+	 */
1205
+	public function get_dtt_display_name($use_dtt_name = false)
1206
+	{
1207
+		if ($use_dtt_name) {
1208
+			$dtt_name = $this->name();
1209
+			if (! empty($dtt_name)) {
1210
+				return $dtt_name;
1211
+			}
1212
+		}
1213
+		// first condition is to see if the months are different
1214
+		if (
1215
+			date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1216
+		) {
1217
+			$display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1218
+			// next condition is if its the same month but different day
1219
+		} else {
1220
+			if (
1221
+				date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1222
+				&& date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1223
+			) {
1224
+				$display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1225
+			} else {
1226
+				$display_date = $this->start_date('F j\, Y')
1227
+								. ' @ '
1228
+								. $this->start_date('g:i a')
1229
+								. ' - '
1230
+								. $this->end_date('g:i a');
1231
+			}
1232
+		}
1233
+		return $display_date;
1234
+	}
1235
+
1236
+
1237
+	/**
1238
+	 * Gets all the tickets for this datetime
1239
+	 *
1240
+	 * @param array $query_params @see
1241
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1242
+	 * @return EE_Base_Class[]|EE_Ticket[]
1243
+	 * @throws ReflectionException
1244
+	 * @throws InvalidArgumentException
1245
+	 * @throws InvalidInterfaceException
1246
+	 * @throws InvalidDataTypeException
1247
+	 * @throws EE_Error
1248
+	 */
1249
+	public function tickets($query_params = [])
1250
+	{
1251
+		return $this->get_many_related('Ticket', $query_params);
1252
+	}
1253
+
1254
+
1255
+	/**
1256
+	 * Gets all the ticket types currently available for purchase
1257
+	 *
1258
+	 * @param array $query_params @see
1259
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1260
+	 * @return EE_Ticket[]
1261
+	 * @throws ReflectionException
1262
+	 * @throws InvalidArgumentException
1263
+	 * @throws InvalidInterfaceException
1264
+	 * @throws InvalidDataTypeException
1265
+	 * @throws EE_Error
1266
+	 */
1267
+	public function ticket_types_available_for_purchase($query_params = [])
1268
+	{
1269
+		// first check if datetime is valid
1270
+		if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1271
+			return [];
1272
+		}
1273
+		if (empty($query_params)) {
1274
+			$query_params = [
1275
+				[
1276
+					'TKT_start_date' => ['<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')],
1277
+					'TKT_end_date'   => ['>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')],
1278
+					'TKT_deleted'    => false,
1279
+				],
1280
+			];
1281
+		}
1282
+		return $this->tickets($query_params);
1283
+	}
1284
+
1285
+
1286
+	/**
1287
+	 * @return EE_Base_Class|EE_Event
1288
+	 * @throws ReflectionException
1289
+	 * @throws InvalidArgumentException
1290
+	 * @throws InvalidInterfaceException
1291
+	 * @throws InvalidDataTypeException
1292
+	 * @throws EE_Error
1293
+	 */
1294
+	public function event()
1295
+	{
1296
+		return EEM_Event::instance()->get_one_by_ID($this->get('EVT_ID'));
1297
+	}
1298
+
1299
+
1300
+	/**
1301
+	 * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1302
+	 * (via the tickets).
1303
+	 *
1304
+	 * @return int
1305
+	 * @throws ReflectionException
1306
+	 * @throws InvalidArgumentException
1307
+	 * @throws InvalidInterfaceException
1308
+	 * @throws InvalidDataTypeException
1309
+	 * @throws EE_Error
1310
+	 */
1311
+	public function update_sold()
1312
+	{
1313
+		$count_regs_for_this_datetime = EEM_Registration::instance()->count(
1314
+			[
1315
+				[
1316
+					'STS_ID'                 => RegStatus::APPROVED,
1317
+					'REG_deleted'            => 0,
1318
+					'Ticket.Datetime.DTT_ID' => $this->ID(),
1319
+				],
1320
+			]
1321
+		);
1322
+		$this->set_sold($count_regs_for_this_datetime);
1323
+		$this->save();
1324
+		return $count_regs_for_this_datetime;
1325
+	}
1326
+
1327
+
1328
+	/**
1329
+	 * Adds a venue to this event
1330
+	 *
1331
+	 * @param int|EE_Venue /int $venue_id_or_obj
1332
+	 * @return EE_Base_Class|EE_Venue
1333
+	 * @throws EE_Error
1334
+	 * @throws ReflectionException
1335
+	 */
1336
+	public function add_venue($venue_id_or_obj): EE_Venue
1337
+	{
1338
+		return $this->_add_relation_to($venue_id_or_obj, 'Venue');
1339
+	}
1340
+
1341
+
1342
+	/**
1343
+	 * Removes a venue from the event
1344
+	 *
1345
+	 * @param EE_Venue /int $venue_id_or_obj
1346
+	 * @return EE_Base_Class|EE_Venue
1347
+	 * @throws EE_Error
1348
+	 * @throws ReflectionException
1349
+	 */
1350
+	public function remove_venue($venue_id_or_obj): EE_Venue
1351
+	{
1352
+		$venue_id_or_obj = ! empty($venue_id_or_obj) ? $venue_id_or_obj : $this->venue();
1353
+		return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
1354
+	}
1355
+
1356
+
1357
+	/**
1358
+	 * Gets the venue related to the event. May provide additional $query_params if desired
1359
+	 *
1360
+	 * @param array $query_params @see
1361
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1362
+	 * @return int
1363
+	 * @throws EE_Error
1364
+	 * @throws ReflectionException
1365
+	 */
1366
+	public function venue_ID(array $query_params = []): int
1367
+	{
1368
+		// If no $query_params have been passed, use the VNU_ID assigned to the Datetime itself
1369
+		if (empty($query_params)){
1370
+			return (int) $this->get('VNU_ID');
1371
+		}
1372
+		// $query_params set, pull the first related venue using those
1373
+		$venue = $this->get_first_related('Venue', $query_params);
1374
+		return $venue instanceof EE_Venue
1375
+			? $venue->ID()
1376
+			: 0;
1377
+	}
1378
+
1379
+
1380
+	/**
1381
+	 * Gets the venue related to the event. May provide additional $query_params if desired
1382
+	 *
1383
+	 * @param array $query_params @see
1384
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1385
+	 * @return EE_Base_Class|EE_Venue|null
1386
+	 * @throws EE_Error
1387
+	 * @throws ReflectionException
1388
+	 */
1389
+	public function venue(array $query_params = [])
1390
+	{
1391
+		// If no $query_params have been passed, use the VNU_ID assigned to the Datetime itself
1392
+		if (empty($query_params)){
1393
+			$VNU_ID = $this->venue_ID();
1394
+			return $VNU_ID ? EEM_Venue::instance()->get_one_by_ID($VNU_ID) : null;
1395
+		}
1396
+		return $this->get_first_related('Venue', $query_params);
1397
+	}
1398
+
1399
+
1400
+	/**
1401
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1402
+	 * @param string                   $relationName
1403
+	 * @param array                    $extra_join_model_fields_n_values
1404
+	 * @param string|null              $cache_id
1405
+	 * @return EE_Base_Class
1406
+	 * @throws EE_Error
1407
+	 * @throws ReflectionException
1408
+	 * @since   5.0.0.p
1409
+	 */
1410
+	public function _add_relation_to(
1411
+		$otherObjectModelObjectOrID,
1412
+		$relationName,
1413
+		$extra_join_model_fields_n_values = [],
1414
+		$cache_id = null
1415
+	) {
1416
+		// if we're adding a new relation to a ticket
1417
+		if ($relationName === 'Ticket' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1418
+			/** @var EE_Ticket $ticket */
1419
+			$ticket = EEM_Ticket::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1420
+			$this->increaseSold($ticket->sold(), false);
1421
+			$this->increaseReserved($ticket->reserved());
1422
+			$this->save();
1423
+			$otherObjectModelObjectOrID = $ticket;
1424
+		}
1425
+		return parent::_add_relation_to(
1426
+			$otherObjectModelObjectOrID,
1427
+			$relationName,
1428
+			$extra_join_model_fields_n_values,
1429
+			$cache_id
1430
+		);
1431
+	}
1432
+
1433
+
1434
+	/**
1435
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1436
+	 * @param string                   $relationName
1437
+	 * @param array                    $where_query
1438
+	 * @return bool|EE_Base_Class|null
1439
+	 * @throws EE_Error
1440
+	 * @throws ReflectionException
1441
+	 * @since   5.0.0.p
1442
+	 */
1443
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1444
+	{
1445
+		if ($relationName === 'Ticket' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1446
+			/** @var EE_Ticket $ticket */
1447
+			$ticket = EEM_Ticket::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1448
+			$this->decreaseSold($ticket->sold());
1449
+			$this->decreaseReserved($ticket->reserved());
1450
+			$this->save();
1451
+			$otherObjectModelObjectOrID = $ticket;
1452
+		}
1453
+		return parent::_remove_relation_to(
1454
+			$otherObjectModelObjectOrID,
1455
+			$relationName,
1456
+			$where_query
1457
+		);
1458
+	}
1459
+
1460
+
1461
+	/**
1462
+	 * Removes ALL the related things for the $relationName.
1463
+	 *
1464
+	 * @param string $relationName
1465
+	 * @param array  $where_query_params
1466
+	 * @return EE_Base_Class
1467
+	 * @throws ReflectionException
1468
+	 * @throws InvalidArgumentException
1469
+	 * @throws InvalidInterfaceException
1470
+	 * @throws InvalidDataTypeException
1471
+	 * @throws EE_Error
1472
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1473
+	 */
1474
+	public function _remove_relations($relationName, $where_query_params = [])
1475
+	{
1476
+		if ($relationName === 'Ticket') {
1477
+			$tickets = $this->tickets();
1478
+			foreach ($tickets as $ticket) {
1479
+				$this->decreaseSold($ticket->sold());
1480
+				$this->decreaseReserved($ticket->reserved());
1481
+				$this->save();
1482
+			}
1483
+		}
1484
+		return parent::_remove_relations($relationName, $where_query_params);
1485
+	}
1486
+
1487
+
1488
+	/*******************************************************************
1489 1489
      ***********************  DEPRECATED METHODS  **********************
1490 1490
      *******************************************************************/
1491 1491
 
1492 1492
 
1493
-    /**
1494
-     * Increments sold by amount passed by $qty, and persists it immediately to the database.
1495
-     *
1496
-     * @param int $qty
1497
-     * @return boolean
1498
-     * @throws ReflectionException
1499
-     * @throws InvalidArgumentException
1500
-     * @throws InvalidInterfaceException
1501
-     * @throws InvalidDataTypeException
1502
-     * @throws EE_Error
1503
-     * @deprecated 4.9.80.p
1504
-     */
1505
-    public function increase_sold($qty = 1)
1506
-    {
1507
-        EE_Error::doing_it_wrong(
1508
-            __FUNCTION__,
1509
-            esc_html__('Please use EE_Datetime::increaseSold() instead', 'event_espresso'),
1510
-            '4.9.80.p',
1511
-            '5.0.0.p'
1512
-        );
1513
-        return $this->increaseSold($qty);
1514
-    }
1515
-
1516
-
1517
-    /**
1518
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
1519
-     * to save afterwards.)
1520
-     *
1521
-     * @param int $qty
1522
-     * @return boolean
1523
-     * @throws ReflectionException
1524
-     * @throws InvalidArgumentException
1525
-     * @throws InvalidInterfaceException
1526
-     * @throws InvalidDataTypeException
1527
-     * @throws EE_Error
1528
-     * @deprecated 4.9.80.p
1529
-     */
1530
-    public function decrease_sold($qty = 1)
1531
-    {
1532
-        EE_Error::doing_it_wrong(
1533
-            __FUNCTION__,
1534
-            esc_html__('Please use EE_Datetime::decreaseSold() instead', 'event_espresso'),
1535
-            '4.9.80.p',
1536
-            '5.0.0.p'
1537
-        );
1538
-        return $this->decreaseSold($qty);
1539
-    }
1540
-
1541
-
1542
-    /**
1543
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1544
-     *
1545
-     * @param int $qty
1546
-     * @return boolean indicating success
1547
-     * @throws ReflectionException
1548
-     * @throws InvalidArgumentException
1549
-     * @throws InvalidInterfaceException
1550
-     * @throws InvalidDataTypeException
1551
-     * @throws EE_Error
1552
-     * @deprecated 4.9.80.p
1553
-     */
1554
-    public function increase_reserved($qty = 1)
1555
-    {
1556
-        EE_Error::doing_it_wrong(
1557
-            __FUNCTION__,
1558
-            esc_html__('Please use EE_Datetime::increaseReserved() instead', 'event_espresso'),
1559
-            '4.9.80.p',
1560
-            '5.0.0.p'
1561
-        );
1562
-        return $this->increaseReserved($qty);
1563
-    }
1564
-
1565
-
1566
-    /**
1567
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1568
-     *
1569
-     * @param int $qty
1570
-     * @return boolean
1571
-     * @throws ReflectionException
1572
-     * @throws InvalidArgumentException
1573
-     * @throws InvalidInterfaceException
1574
-     * @throws InvalidDataTypeException
1575
-     * @throws EE_Error
1576
-     * @deprecated 4.9.80.p
1577
-     */
1578
-    public function decrease_reserved($qty = 1)
1579
-    {
1580
-        EE_Error::doing_it_wrong(
1581
-            __FUNCTION__,
1582
-            esc_html__('Please use EE_Datetime::decreaseReserved() instead', 'event_espresso'),
1583
-            '4.9.80.p',
1584
-            '5.0.0.p'
1585
-        );
1586
-        return $this->decreaseReserved($qty);
1587
-    }
1493
+	/**
1494
+	 * Increments sold by amount passed by $qty, and persists it immediately to the database.
1495
+	 *
1496
+	 * @param int $qty
1497
+	 * @return boolean
1498
+	 * @throws ReflectionException
1499
+	 * @throws InvalidArgumentException
1500
+	 * @throws InvalidInterfaceException
1501
+	 * @throws InvalidDataTypeException
1502
+	 * @throws EE_Error
1503
+	 * @deprecated 4.9.80.p
1504
+	 */
1505
+	public function increase_sold($qty = 1)
1506
+	{
1507
+		EE_Error::doing_it_wrong(
1508
+			__FUNCTION__,
1509
+			esc_html__('Please use EE_Datetime::increaseSold() instead', 'event_espresso'),
1510
+			'4.9.80.p',
1511
+			'5.0.0.p'
1512
+		);
1513
+		return $this->increaseSold($qty);
1514
+	}
1515
+
1516
+
1517
+	/**
1518
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
1519
+	 * to save afterwards.)
1520
+	 *
1521
+	 * @param int $qty
1522
+	 * @return boolean
1523
+	 * @throws ReflectionException
1524
+	 * @throws InvalidArgumentException
1525
+	 * @throws InvalidInterfaceException
1526
+	 * @throws InvalidDataTypeException
1527
+	 * @throws EE_Error
1528
+	 * @deprecated 4.9.80.p
1529
+	 */
1530
+	public function decrease_sold($qty = 1)
1531
+	{
1532
+		EE_Error::doing_it_wrong(
1533
+			__FUNCTION__,
1534
+			esc_html__('Please use EE_Datetime::decreaseSold() instead', 'event_espresso'),
1535
+			'4.9.80.p',
1536
+			'5.0.0.p'
1537
+		);
1538
+		return $this->decreaseSold($qty);
1539
+	}
1540
+
1541
+
1542
+	/**
1543
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1544
+	 *
1545
+	 * @param int $qty
1546
+	 * @return boolean indicating success
1547
+	 * @throws ReflectionException
1548
+	 * @throws InvalidArgumentException
1549
+	 * @throws InvalidInterfaceException
1550
+	 * @throws InvalidDataTypeException
1551
+	 * @throws EE_Error
1552
+	 * @deprecated 4.9.80.p
1553
+	 */
1554
+	public function increase_reserved($qty = 1)
1555
+	{
1556
+		EE_Error::doing_it_wrong(
1557
+			__FUNCTION__,
1558
+			esc_html__('Please use EE_Datetime::increaseReserved() instead', 'event_espresso'),
1559
+			'4.9.80.p',
1560
+			'5.0.0.p'
1561
+		);
1562
+		return $this->increaseReserved($qty);
1563
+	}
1564
+
1565
+
1566
+	/**
1567
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1568
+	 *
1569
+	 * @param int $qty
1570
+	 * @return boolean
1571
+	 * @throws ReflectionException
1572
+	 * @throws InvalidArgumentException
1573
+	 * @throws InvalidInterfaceException
1574
+	 * @throws InvalidDataTypeException
1575
+	 * @throws EE_Error
1576
+	 * @deprecated 4.9.80.p
1577
+	 */
1578
+	public function decrease_reserved($qty = 1)
1579
+	{
1580
+		EE_Error::doing_it_wrong(
1581
+			__FUNCTION__,
1582
+			esc_html__('Please use EE_Datetime::decreaseReserved() instead', 'event_espresso'),
1583
+			'4.9.80.p',
1584
+			'5.0.0.p'
1585
+		);
1586
+		return $this->decreaseReserved($qty);
1587
+	}
1588 1588
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -533,7 +533,7 @@  discard block
 block discarded – undo
533 533
             $date_or_time,
534 534
             $echo
535 535
         );
536
-        if (! $echo) {
536
+        if ( ! $echo) {
537 537
             return $dtt;
538 538
         }
539 539
         return '';
@@ -630,12 +630,12 @@  discard block
 block discarded – undo
630 630
             '&nbsp;',
631 631
             $this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632 632
         );
633
-        $end     = str_replace(
633
+        $end = str_replace(
634 634
             ' ',
635 635
             '&nbsp;',
636 636
             $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637 637
         );
638
-        return $start !== $end ? $start . $conjunction . $end : $start;
638
+        return $start !== $end ? $start.$conjunction.$end : $start;
639 639
     }
640 640
 
641 641
 
@@ -738,12 +738,12 @@  discard block
 block discarded – undo
738 738
             '&nbsp;',
739 739
             $this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740 740
         );
741
-        $end       = str_replace(
741
+        $end = str_replace(
742 742
             ' ',
743 743
             '&nbsp;',
744 744
             $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745 745
         );
746
-        return $start !== $end ? $start . $conjunction . $end : $start;
746
+        return $start !== $end ? $start.$conjunction.$end : $start;
747 747
     }
748 748
 
749 749
 
@@ -788,7 +788,7 @@  discard block
 block discarded – undo
788 788
     ) {
789 789
         $dt_format   = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790 790
         $tm_format   = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
-        $full_format = $dt_format . $separator . $tm_format;
791
+        $full_format = $dt_format.$separator.$tm_format;
792 792
         // the range output depends on various conditions
793 793
         switch (true) {
794 794
             // start date timestamp and end date timestamp are the same.
@@ -1029,7 +1029,7 @@  discard block
 block discarded – undo
1029 1029
         // tickets remaining available for purchase
1030 1030
         // no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031 1031
         $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
-        if (! $consider_tickets) {
1032
+        if ( ! $consider_tickets) {
1033 1033
             return $dtt_remaining;
1034 1034
         }
1035 1035
         $tickets_remaining = $this->tickets_remaining();
@@ -1054,7 +1054,7 @@  discard block
 block discarded – undo
1054 1054
     {
1055 1055
         $sum     = 0;
1056 1056
         $tickets = $this->tickets($query_params);
1057
-        if (! empty($tickets)) {
1057
+        if ( ! empty($tickets)) {
1058 1058
             foreach ($tickets as $ticket) {
1059 1059
                 if ($ticket instanceof EE_Ticket) {
1060 1060
                     // get the actual amount of tickets that can be sold
@@ -1206,7 +1206,7 @@  discard block
 block discarded – undo
1206 1206
     {
1207 1207
         if ($use_dtt_name) {
1208 1208
             $dtt_name = $this->name();
1209
-            if (! empty($dtt_name)) {
1209
+            if ( ! empty($dtt_name)) {
1210 1210
                 return $dtt_name;
1211 1211
             }
1212 1212
         }
@@ -1214,14 +1214,14 @@  discard block
 block discarded – undo
1214 1214
         if (
1215 1215
             date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1216 1216
         ) {
1217
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1217
+            $display_date = $this->start_date('M j\, Y g:i a').' - '.$this->end_date('M j\, Y g:i a');
1218 1218
             // next condition is if its the same month but different day
1219 1219
         } else {
1220 1220
             if (
1221 1221
                 date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1222 1222
                 && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1223 1223
             ) {
1224
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1224
+                $display_date = $this->start_date('M j\, g:i a').' - '.$this->end_date('M j\, g:i a Y');
1225 1225
             } else {
1226 1226
                 $display_date = $this->start_date('F j\, Y')
1227 1227
                                 . ' @ '
@@ -1366,7 +1366,7 @@  discard block
 block discarded – undo
1366 1366
     public function venue_ID(array $query_params = []): int
1367 1367
     {
1368 1368
         // If no $query_params have been passed, use the VNU_ID assigned to the Datetime itself
1369
-        if (empty($query_params)){
1369
+        if (empty($query_params)) {
1370 1370
             return (int) $this->get('VNU_ID');
1371 1371
         }
1372 1372
         // $query_params set, pull the first related venue using those
@@ -1389,7 +1389,7 @@  discard block
 block discarded – undo
1389 1389
     public function venue(array $query_params = [])
1390 1390
     {
1391 1391
         // If no $query_params have been passed, use the VNU_ID assigned to the Datetime itself
1392
-        if (empty($query_params)){
1392
+        if (empty($query_params)) {
1393 1393
             $VNU_ID = $this->venue_ID();
1394 1394
             return $VNU_ID ? EEM_Venue::instance()->get_one_by_ID($VNU_ID) : null;
1395 1395
         }
Please login to merge, or discard this patch.