Completed
Branch master (5dc63c)
by
unknown
04:57
created
core/domain/services/admin/events/data/ConfirmDeletion.php 1 patch
Indentation   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -29,89 +29,89 @@
 block discarded – undo
29 29
  */
30 30
 class ConfirmDeletion
31 31
 {
32
-    /**
33
-     * @var NodeGroupDao
34
-     */
35
-    private $dao;
32
+	/**
33
+	 * @var NodeGroupDao
34
+	 */
35
+	private $dao;
36 36
 
37 37
 
38
-    /**
39
-     * ConfirmDeletion constructor.
40
-     *
41
-     * @param NodeGroupDao $dao
42
-     */
43
-    public function __construct(NodeGroupDao $dao)
44
-    {
45
-        $this->dao = $dao;
46
-    }
38
+	/**
39
+	 * ConfirmDeletion constructor.
40
+	 *
41
+	 * @param NodeGroupDao $dao
42
+	 */
43
+	public function __construct(NodeGroupDao $dao)
44
+	{
45
+		$this->dao = $dao;
46
+	}
47 47
 
48 48
 
49
-    /**
50
-     * Redirects to the batch job for deleting events if the form submission is valid, otherwise back to the deletion
51
-     * preview page.
52
-     *
53
-     * @param $request_data
54
-     * @param $admin_base_url
55
-     * @throws EE_Error
56
-     * @throws InvalidArgumentException
57
-     * @throws InvalidDataTypeException
58
-     * @throws InvalidInterfaceException
59
-     * @throws ReflectionException
60
-     * @throws UnexpectedEntityException
61
-     * @since 4.10.12.p
62
-     */
63
-    public function handle($request_data, $admin_base_url)
64
-    {
65
-        $deletion_job_code        =
66
-            isset($request_data['deletion_job_code']) ? sanitize_key($request_data['deletion_job_code']) : '';
67
-        $models_and_ids_to_delete = $this->dao->getModelsAndIdsFromGroup($deletion_job_code);
68
-        $form                     = new ConfirmEventDeletionForm($models_and_ids_to_delete['Event']);
69
-        // Initialize the form from the request, and check if its valid.
70
-        $form->receive_form_submission($request_data);
71
-        if ($form->is_valid()) {
72
-            // Redirect the user to the deletion batch job.
73
-            EEH_URL::safeRedirectAndExit(
74
-                EE_Admin_Page::add_query_args_and_nonce(
75
-                    [
76
-                        'page'              => EED_Batch::PAGE_SLUG,
77
-                        'batch'             => EED_Batch::batch_job,
78
-                        'deletion_job_code' => $deletion_job_code,
79
-                        'job_handler'       => urlencode('EventEspresso\core\libraries\batch\JobHandlers\ExecuteBatchDeletion'),
80
-                        'return_url'        => urlencode(
81
-                            add_query_arg(
82
-                                [
83
-                                    'status' => 'trash',
84
-                                ],
85
-                                EVENTS_ADMIN_URL
86
-                            )
87
-                        ),
88
-                    ],
89
-                    admin_url()
90
-                )
91
-            );
92
-        }
93
-        // Dont' use $form->submission_error_message() because it adds the form input's label in front
94
-        // of each validation error which ends up looking quite confusing.
95
-        $validation_errors = $form->get_validation_errors_accumulated();
96
-        foreach ($validation_errors as $validation_error) {
97
-            EE_Error::add_error(
98
-                $validation_error->getMessage(),
99
-                __FILE__,
100
-                __FUNCTION__,
101
-                __LINE__
102
-            );
103
-        }
49
+	/**
50
+	 * Redirects to the batch job for deleting events if the form submission is valid, otherwise back to the deletion
51
+	 * preview page.
52
+	 *
53
+	 * @param $request_data
54
+	 * @param $admin_base_url
55
+	 * @throws EE_Error
56
+	 * @throws InvalidArgumentException
57
+	 * @throws InvalidDataTypeException
58
+	 * @throws InvalidInterfaceException
59
+	 * @throws ReflectionException
60
+	 * @throws UnexpectedEntityException
61
+	 * @since 4.10.12.p
62
+	 */
63
+	public function handle($request_data, $admin_base_url)
64
+	{
65
+		$deletion_job_code        =
66
+			isset($request_data['deletion_job_code']) ? sanitize_key($request_data['deletion_job_code']) : '';
67
+		$models_and_ids_to_delete = $this->dao->getModelsAndIdsFromGroup($deletion_job_code);
68
+		$form                     = new ConfirmEventDeletionForm($models_and_ids_to_delete['Event']);
69
+		// Initialize the form from the request, and check if its valid.
70
+		$form->receive_form_submission($request_data);
71
+		if ($form->is_valid()) {
72
+			// Redirect the user to the deletion batch job.
73
+			EEH_URL::safeRedirectAndExit(
74
+				EE_Admin_Page::add_query_args_and_nonce(
75
+					[
76
+						'page'              => EED_Batch::PAGE_SLUG,
77
+						'batch'             => EED_Batch::batch_job,
78
+						'deletion_job_code' => $deletion_job_code,
79
+						'job_handler'       => urlencode('EventEspresso\core\libraries\batch\JobHandlers\ExecuteBatchDeletion'),
80
+						'return_url'        => urlencode(
81
+							add_query_arg(
82
+								[
83
+									'status' => 'trash',
84
+								],
85
+								EVENTS_ADMIN_URL
86
+							)
87
+						),
88
+					],
89
+					admin_url()
90
+				)
91
+			);
92
+		}
93
+		// Dont' use $form->submission_error_message() because it adds the form input's label in front
94
+		// of each validation error which ends up looking quite confusing.
95
+		$validation_errors = $form->get_validation_errors_accumulated();
96
+		foreach ($validation_errors as $validation_error) {
97
+			EE_Error::add_error(
98
+				$validation_error->getMessage(),
99
+				__FILE__,
100
+				__FUNCTION__,
101
+				__LINE__
102
+			);
103
+		}
104 104
 
105
-        EEH_URL::safeRedirectAndExit(
106
-            EE_Admin_Page::add_query_args_and_nonce(
107
-                [
108
-                    'action'            => 'preview_deletion',
109
-                    'deletion_job_code' => $deletion_job_code,
110
-                ],
111
-                $admin_base_url
112
-            )
113
-        );
114
-    }
105
+		EEH_URL::safeRedirectAndExit(
106
+			EE_Admin_Page::add_query_args_and_nonce(
107
+				[
108
+					'action'            => 'preview_deletion',
109
+					'deletion_job_code' => $deletion_job_code,
110
+				],
111
+				$admin_base_url
112
+			)
113
+		);
114
+	}
115 115
 }
116 116
 // End of file ConfirmDeletion.php
117 117
 // Location: EventEspresso\core\domain\services\admin\events\data/ConfirmDeletion.php
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/AttendeesReport.php 2 patches
Indentation   +131 added lines, -131 removed lines patch added patch discarded remove patch
@@ -27,143 +27,143 @@
 block discarded – undo
27 27
  */
28 28
 class AttendeesReport extends JobHandlerFile
29 29
 {
30
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
31
-    /**
32
-     * @param JobParameters $job_parameters
33
-     * @return JobStepResponse
34
-     * @throws BatchRequestException
35
-     * @throws EE_Error
36
-     * @throws ReflectionException
37
-     */
38
-    public function create_job(JobParameters $job_parameters): JobStepResponse
39
-    {
40
-        if (! EE_Capabilities::instance()->current_user_can('ee_read_contacts', 'generating_report')) {
41
-            throw new BatchRequestException(
42
-                esc_html__('You do not have permission to view contacts', 'event_espresso')
43
-            );
44
-        }
45
-        $filepath = $this->create_file_from_job_with_name(
46
-            $job_parameters->job_id(),
47
-            esc_html__('contact-list-report.csv', 'event_espresso')
48
-        );
49
-        $job_parameters->add_extra_data('filepath', $filepath);
50
-        $job_parameters->set_job_size($this->count_units_to_process());
51
-        // we should also set the header columns
52
-        $csv_data_for_row = $this->get_csv_data(0, 1);
53
-        EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row);
54
-        // if we actually processed a row there, record it
55
-        if ($job_parameters->job_size()) {
56
-            $job_parameters->mark_processed(1);
57
-        }
58
-        return new JobStepResponse(
59
-            $job_parameters,
60
-            esc_html__('Contacts report started successfully...', 'event_espresso')
61
-        );
62
-    }
30
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
31
+	/**
32
+	 * @param JobParameters $job_parameters
33
+	 * @return JobStepResponse
34
+	 * @throws BatchRequestException
35
+	 * @throws EE_Error
36
+	 * @throws ReflectionException
37
+	 */
38
+	public function create_job(JobParameters $job_parameters): JobStepResponse
39
+	{
40
+		if (! EE_Capabilities::instance()->current_user_can('ee_read_contacts', 'generating_report')) {
41
+			throw new BatchRequestException(
42
+				esc_html__('You do not have permission to view contacts', 'event_espresso')
43
+			);
44
+		}
45
+		$filepath = $this->create_file_from_job_with_name(
46
+			$job_parameters->job_id(),
47
+			esc_html__('contact-list-report.csv', 'event_espresso')
48
+		);
49
+		$job_parameters->add_extra_data('filepath', $filepath);
50
+		$job_parameters->set_job_size($this->count_units_to_process());
51
+		// we should also set the header columns
52
+		$csv_data_for_row = $this->get_csv_data(0, 1);
53
+		EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row);
54
+		// if we actually processed a row there, record it
55
+		if ($job_parameters->job_size()) {
56
+			$job_parameters->mark_processed(1);
57
+		}
58
+		return new JobStepResponse(
59
+			$job_parameters,
60
+			esc_html__('Contacts report started successfully...', 'event_espresso')
61
+		);
62
+	}
63 63
 
64 64
 
65
-    /**
66
-     * @param JobParameters $job_parameters
67
-     * @param int           $batch_size
68
-     * @return JobStepResponse
69
-     * @throws EE_Error
70
-     * @throws ReflectionException
71
-     */
72
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
73
-    {
74
-        $csv_data = $this->get_csv_data($job_parameters->units_processed(), $batch_size);
75
-        EEH_Export::write_data_array_to_csv(
76
-            $job_parameters->extra_datum('filepath'),
77
-            $csv_data,
78
-            false
79
-        );
80
-        $units_processed = count($csv_data);
81
-        $job_parameters->mark_processed($units_processed);
82
-        $extra_response_data = [
83
-            'file_url' => '',
84
-        ];
85
-        if ($units_processed < $batch_size) {
86
-            $job_parameters->set_status(JobParameters::status_complete);
87
-            $extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
88
-        }
89
-        return new JobStepResponse(
90
-            $job_parameters,
91
-            sprintf(
92
-                esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
93
-                count($csv_data)
94
-            ),
95
-            $extra_response_data
96
-        );
97
-    }
65
+	/**
66
+	 * @param JobParameters $job_parameters
67
+	 * @param int           $batch_size
68
+	 * @return JobStepResponse
69
+	 * @throws EE_Error
70
+	 * @throws ReflectionException
71
+	 */
72
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
73
+	{
74
+		$csv_data = $this->get_csv_data($job_parameters->units_processed(), $batch_size);
75
+		EEH_Export::write_data_array_to_csv(
76
+			$job_parameters->extra_datum('filepath'),
77
+			$csv_data,
78
+			false
79
+		);
80
+		$units_processed = count($csv_data);
81
+		$job_parameters->mark_processed($units_processed);
82
+		$extra_response_data = [
83
+			'file_url' => '',
84
+		];
85
+		if ($units_processed < $batch_size) {
86
+			$job_parameters->set_status(JobParameters::status_complete);
87
+			$extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
88
+		}
89
+		return new JobStepResponse(
90
+			$job_parameters,
91
+			sprintf(
92
+				esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
93
+				count($csv_data)
94
+			),
95
+			$extra_response_data
96
+		);
97
+	}
98 98
 
99 99
 
100
-    /**
101
-     * @param JobParameters $job_parameters
102
-     * @return JobStepResponse
103
-     */
104
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse
105
-    {
106
-        $this->_file_helper->delete(
107
-            EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
108
-            true,
109
-            'd'
110
-        );
111
-        return new JobStepResponse($job_parameters, esc_html__('Cleaned up temporary file', 'event_espresso'));
112
-    }
100
+	/**
101
+	 * @param JobParameters $job_parameters
102
+	 * @return JobStepResponse
103
+	 */
104
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse
105
+	{
106
+		$this->_file_helper->delete(
107
+			EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
108
+			true,
109
+			'd'
110
+		);
111
+		return new JobStepResponse($job_parameters, esc_html__('Cleaned up temporary file', 'event_espresso'));
112
+	}
113 113
 
114 114
 
115
-    /**
116
-     * @return int
117
-     * @throws EE_Error
118
-     * @throws ReflectionException
119
-     */
120
-    public function count_units_to_process(): int
121
-    {
122
-        return EEM_Attendee::instance()->count(['caps' => EEM_Base::caps_read_admin]);
123
-    }
115
+	/**
116
+	 * @return int
117
+	 * @throws EE_Error
118
+	 * @throws ReflectionException
119
+	 */
120
+	public function count_units_to_process(): int
121
+	{
122
+		return EEM_Attendee::instance()->count(['caps' => EEM_Base::caps_read_admin]);
123
+	}
124 124
 
125 125
 
126
-    /**
127
-     * @param $offset
128
-     * @param $limit
129
-     * @return array
130
-     * @throws EE_Error
131
-     * @throws ReflectionException
132
-     */
133
-    public function get_csv_data($offset, $limit): array
134
-    {
135
-        $attendee_rows = EEM_Attendee::instance()->get_all_wpdb_results(
136
-            [
137
-                'limit'      => [$offset, $limit],
138
-                'force_join' => ['State', 'Country'],
139
-                'caps'       => EEM_Base::caps_read_admin,
140
-            ]
141
-        );
142
-        $csv_data      = [];
143
-        foreach ($attendee_rows as $attendee_row) {
144
-            $csv_row = [];
145
-            foreach (EEM_Attendee::instance()->field_settings() as $field_name => $field_obj) {
146
-                if ($field_name == 'STA_ID') {
147
-                    $state_name_field                                 =
148
-                        EEM_State::instance()->field_settings_for('STA_name');
149
-                    $csv_row[ esc_html__('State', 'event_espresso') ] =
150
-                        $attendee_row[ $state_name_field->get_qualified_column() ];
151
-                } elseif ($field_name == 'CNT_ISO') {
152
-                    $country_name_field                                 =
153
-                        EEM_Country::instance()->field_settings_for('CNT_name');
154
-                    $csv_row[ esc_html__('Country', 'event_espresso') ] =
155
-                        $attendee_row[ $country_name_field->get_qualified_column() ];
156
-                } else {
157
-                    $csv_row[ wp_specialchars_decode($field_obj->get_nicename(), ENT_QUOTES) ] =
158
-                        $attendee_row[ $field_obj->get_qualified_column() ];
159
-                }
160
-            }
161
-            $csv_data[] = apply_filters(
162
-                'FHEE___EventEspresso_core_libraries_batch_JobHandlers_AttendeesReport__get_csv_data__row',
163
-                $csv_row,
164
-                $attendee_row
165
-            );
166
-        }
167
-        return $csv_data;
168
-    }
126
+	/**
127
+	 * @param $offset
128
+	 * @param $limit
129
+	 * @return array
130
+	 * @throws EE_Error
131
+	 * @throws ReflectionException
132
+	 */
133
+	public function get_csv_data($offset, $limit): array
134
+	{
135
+		$attendee_rows = EEM_Attendee::instance()->get_all_wpdb_results(
136
+			[
137
+				'limit'      => [$offset, $limit],
138
+				'force_join' => ['State', 'Country'],
139
+				'caps'       => EEM_Base::caps_read_admin,
140
+			]
141
+		);
142
+		$csv_data      = [];
143
+		foreach ($attendee_rows as $attendee_row) {
144
+			$csv_row = [];
145
+			foreach (EEM_Attendee::instance()->field_settings() as $field_name => $field_obj) {
146
+				if ($field_name == 'STA_ID') {
147
+					$state_name_field                                 =
148
+						EEM_State::instance()->field_settings_for('STA_name');
149
+					$csv_row[ esc_html__('State', 'event_espresso') ] =
150
+						$attendee_row[ $state_name_field->get_qualified_column() ];
151
+				} elseif ($field_name == 'CNT_ISO') {
152
+					$country_name_field                                 =
153
+						EEM_Country::instance()->field_settings_for('CNT_name');
154
+					$csv_row[ esc_html__('Country', 'event_espresso') ] =
155
+						$attendee_row[ $country_name_field->get_qualified_column() ];
156
+				} else {
157
+					$csv_row[ wp_specialchars_decode($field_obj->get_nicename(), ENT_QUOTES) ] =
158
+						$attendee_row[ $field_obj->get_qualified_column() ];
159
+				}
160
+			}
161
+			$csv_data[] = apply_filters(
162
+				'FHEE___EventEspresso_core_libraries_batch_JobHandlers_AttendeesReport__get_csv_data__row',
163
+				$csv_row,
164
+				$attendee_row
165
+			);
166
+		}
167
+		return $csv_data;
168
+	}
169 169
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -37,7 +37,7 @@  discard block
 block discarded – undo
37 37
      */
38 38
     public function create_job(JobParameters $job_parameters): JobStepResponse
39 39
     {
40
-        if (! EE_Capabilities::instance()->current_user_can('ee_read_contacts', 'generating_report')) {
40
+        if ( ! EE_Capabilities::instance()->current_user_can('ee_read_contacts', 'generating_report')) {
41 41
             throw new BatchRequestException(
42 42
                 esc_html__('You do not have permission to view contacts', 'event_espresso')
43 43
             );
@@ -139,23 +139,23 @@  discard block
 block discarded – undo
139 139
                 'caps'       => EEM_Base::caps_read_admin,
140 140
             ]
141 141
         );
142
-        $csv_data      = [];
142
+        $csv_data = [];
143 143
         foreach ($attendee_rows as $attendee_row) {
144 144
             $csv_row = [];
145 145
             foreach (EEM_Attendee::instance()->field_settings() as $field_name => $field_obj) {
146 146
                 if ($field_name == 'STA_ID') {
147 147
                     $state_name_field                                 =
148 148
                         EEM_State::instance()->field_settings_for('STA_name');
149
-                    $csv_row[ esc_html__('State', 'event_espresso') ] =
150
-                        $attendee_row[ $state_name_field->get_qualified_column() ];
149
+                    $csv_row[esc_html__('State', 'event_espresso')] =
150
+                        $attendee_row[$state_name_field->get_qualified_column()];
151 151
                 } elseif ($field_name == 'CNT_ISO') {
152 152
                     $country_name_field                                 =
153 153
                         EEM_Country::instance()->field_settings_for('CNT_name');
154
-                    $csv_row[ esc_html__('Country', 'event_espresso') ] =
155
-                        $attendee_row[ $country_name_field->get_qualified_column() ];
154
+                    $csv_row[esc_html__('Country', 'event_espresso')] =
155
+                        $attendee_row[$country_name_field->get_qualified_column()];
156 156
                 } else {
157
-                    $csv_row[ wp_specialchars_decode($field_obj->get_nicename(), ENT_QUOTES) ] =
158
-                        $attendee_row[ $field_obj->get_qualified_column() ];
157
+                    $csv_row[wp_specialchars_decode($field_obj->get_nicename(), ENT_QUOTES)] =
158
+                        $attendee_row[$field_obj->get_qualified_column()];
159 159
                 }
160 160
             }
161 161
             $csv_data[] = apply_filters(
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/PreviewEventDeletion.php 1 patch
Indentation   +212 added lines, -212 removed lines patch added patch discarded remove patch
@@ -33,141 +33,141 @@  discard block
 block discarded – undo
33 33
  */
34 34
 class PreviewEventDeletion extends JobHandler
35 35
 {
36
-    protected NodeGroupDao $model_obj_node_group_persister;
36
+	protected NodeGroupDao $model_obj_node_group_persister;
37 37
 
38
-    public function __construct(NodeGroupDao $model_obj_node_group_persister)
39
-    {
40
-        $this->model_obj_node_group_persister = $model_obj_node_group_persister;
41
-    }
38
+	public function __construct(NodeGroupDao $model_obj_node_group_persister)
39
+	{
40
+		$this->model_obj_node_group_persister = $model_obj_node_group_persister;
41
+	}
42 42
 
43
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
43
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
44 44
 
45
-    /**
46
-     *
47
-     * @param JobParameters $job_parameters
48
-     * @return JobStepResponse
49
-     * @throws EE_Error
50
-     * @throws InvalidDataTypeException
51
-     * @throws InvalidInterfaceException
52
-     * @throws InvalidArgumentException
53
-     * @throws ReflectionException
54
-     */
55
-    public function create_job(JobParameters $job_parameters): JobStepResponse
56
-    {
57
-        // Set the "root" model objects we will want to delete (record their ID and model)
58
-        $event_ids = $job_parameters->request_datum('EVT_IDs', array());
59
-        // Find all the root nodes to delete (this isn't just events, because there's other data, like related tickets,
60
-        // prices, message templates, etc, whose model definition doesn't make them dependent on events. But,
61
-        // we have no UI to access them independent of events, so they may as well get deleted too.)
62
-        $roots = [];
63
-        foreach ($event_ids as $event_id) {
64
-            $roots[] = new ModelObjNode(
65
-                $event_id,
66
-                EEM_Event::instance()
67
-            );
68
-            // Also, we want to delete their related, non-global, tickets, prices and message templates
69
-            $related_non_global_tickets = EEM_Ticket::instance()->get_all_deleted_and_undeleted(
70
-                [
71
-                    [
72
-                        'TKT_is_default' => false,
73
-                        'Datetime.EVT_ID' => $event_id
74
-                    ]
75
-                ]
76
-            );
77
-            foreach ($related_non_global_tickets as $ticket) {
78
-                $roots[] = new ModelObjNode(
79
-                    $ticket->ID(),
80
-                    $ticket->get_model(),
81
-                    ['Registration']
82
-                );
83
-            }
84
-            $related_non_global_prices = EEM_Price::instance()->get_all_deleted_and_undeleted(
85
-                [
86
-                    [
87
-                        'PRC_is_default' => false,
88
-                        'Ticket.Datetime.EVT_ID' => $event_id
89
-                    ]
90
-                ]
91
-            );
92
-            foreach ($related_non_global_prices as $price) {
93
-                $roots[] = new ModelObjNode(
94
-                    $price->ID(),
95
-                    $price->get_model()
96
-                );
97
-            }
98
-        }
99
-        $transactions_ids = $this->getTransactionsToDelete($event_ids);
100
-        foreach ($transactions_ids as $transaction_id) {
101
-            $roots[] = new ModelObjNode(
102
-                $transaction_id,
103
-                EEM_Transaction::instance(),
104
-                ['Registration']
105
-            );
106
-        }
107
-        $job_parameters->add_extra_data('roots', $roots);
108
-        // Set an estimate of how long this will take (we're discovering as we go, so it seems impossible to give
109
-        // an accurate count.)
110
-        $estimated_work_per_model_obj = 10;
111
-        $count_regs = EEM_Registration::instance()->count(
112
-            [
113
-                [
114
-                    'EVT_ID' => ['IN', $event_ids]
115
-                ]
116
-            ]
117
-        );
118
-        $job_parameters->set_job_size((count($roots) + $count_regs) * $estimated_work_per_model_obj);
119
-        $this->updateTextHeader(esc_html__('Generating preview of data to be deleted...', 'event_espresso'));
120
-        return new JobStepResponse($job_parameters, $this->feedback);
121
-    }
45
+	/**
46
+	 *
47
+	 * @param JobParameters $job_parameters
48
+	 * @return JobStepResponse
49
+	 * @throws EE_Error
50
+	 * @throws InvalidDataTypeException
51
+	 * @throws InvalidInterfaceException
52
+	 * @throws InvalidArgumentException
53
+	 * @throws ReflectionException
54
+	 */
55
+	public function create_job(JobParameters $job_parameters): JobStepResponse
56
+	{
57
+		// Set the "root" model objects we will want to delete (record their ID and model)
58
+		$event_ids = $job_parameters->request_datum('EVT_IDs', array());
59
+		// Find all the root nodes to delete (this isn't just events, because there's other data, like related tickets,
60
+		// prices, message templates, etc, whose model definition doesn't make them dependent on events. But,
61
+		// we have no UI to access them independent of events, so they may as well get deleted too.)
62
+		$roots = [];
63
+		foreach ($event_ids as $event_id) {
64
+			$roots[] = new ModelObjNode(
65
+				$event_id,
66
+				EEM_Event::instance()
67
+			);
68
+			// Also, we want to delete their related, non-global, tickets, prices and message templates
69
+			$related_non_global_tickets = EEM_Ticket::instance()->get_all_deleted_and_undeleted(
70
+				[
71
+					[
72
+						'TKT_is_default' => false,
73
+						'Datetime.EVT_ID' => $event_id
74
+					]
75
+				]
76
+			);
77
+			foreach ($related_non_global_tickets as $ticket) {
78
+				$roots[] = new ModelObjNode(
79
+					$ticket->ID(),
80
+					$ticket->get_model(),
81
+					['Registration']
82
+				);
83
+			}
84
+			$related_non_global_prices = EEM_Price::instance()->get_all_deleted_and_undeleted(
85
+				[
86
+					[
87
+						'PRC_is_default' => false,
88
+						'Ticket.Datetime.EVT_ID' => $event_id
89
+					]
90
+				]
91
+			);
92
+			foreach ($related_non_global_prices as $price) {
93
+				$roots[] = new ModelObjNode(
94
+					$price->ID(),
95
+					$price->get_model()
96
+				);
97
+			}
98
+		}
99
+		$transactions_ids = $this->getTransactionsToDelete($event_ids);
100
+		foreach ($transactions_ids as $transaction_id) {
101
+			$roots[] = new ModelObjNode(
102
+				$transaction_id,
103
+				EEM_Transaction::instance(),
104
+				['Registration']
105
+			);
106
+		}
107
+		$job_parameters->add_extra_data('roots', $roots);
108
+		// Set an estimate of how long this will take (we're discovering as we go, so it seems impossible to give
109
+		// an accurate count.)
110
+		$estimated_work_per_model_obj = 10;
111
+		$count_regs = EEM_Registration::instance()->count(
112
+			[
113
+				[
114
+					'EVT_ID' => ['IN', $event_ids]
115
+				]
116
+			]
117
+		);
118
+		$job_parameters->set_job_size((count($roots) + $count_regs) * $estimated_work_per_model_obj);
119
+		$this->updateTextHeader(esc_html__('Generating preview of data to be deleted...', 'event_espresso'));
120
+		return new JobStepResponse($job_parameters, $this->feedback);
121
+	}
122 122
 
123
-    /**
124
-     * @param EE_Base_Class[] $model_objs
125
-     * @param array           $dont_traverse_models
126
-     * @return array
127
-     * @throws EE_Error
128
-     * @throws InvalidArgumentException
129
-     * @throws InvalidDataTypeException
130
-     * @throws InvalidInterfaceException
131
-     * @throws ReflectionException
132
-     * @since 4.10.12.p
133
-     */
134
-    protected function createModelObjNodes(array $model_objs, array $dont_traverse_models = []): array
135
-    {
136
-        $nodes = [];
137
-        foreach ($model_objs as $model_obj) {
138
-            $nodes[] = new ModelObjNode(
139
-                $model_obj->ID(),
140
-                $model_obj->get_model(),
141
-                $dont_traverse_models
142
-            );
143
-        }
144
-        return $nodes;
145
-    }
123
+	/**
124
+	 * @param EE_Base_Class[] $model_objs
125
+	 * @param array           $dont_traverse_models
126
+	 * @return array
127
+	 * @throws EE_Error
128
+	 * @throws InvalidArgumentException
129
+	 * @throws InvalidDataTypeException
130
+	 * @throws InvalidInterfaceException
131
+	 * @throws ReflectionException
132
+	 * @since 4.10.12.p
133
+	 */
134
+	protected function createModelObjNodes(array $model_objs, array $dont_traverse_models = []): array
135
+	{
136
+		$nodes = [];
137
+		foreach ($model_objs as $model_obj) {
138
+			$nodes[] = new ModelObjNode(
139
+				$model_obj->ID(),
140
+				$model_obj->get_model(),
141
+				$dont_traverse_models
142
+			);
143
+		}
144
+		return $nodes;
145
+	}
146 146
 
147
-    /**
148
-     * Gets all the transactions related to these events that aren't related to other events. They'll be deleted too.
149
-     * (Ones that are related to other events can stay around until those other events are deleted too.)
150
-     * @since 4.10.12.p
151
-     * @param array $event_ids
152
-     * @return array of transaction IDs
153
-     */
154
-    protected function getTransactionsToDelete(array $event_ids): array
155
-    {
156
-        if (empty($event_ids)) {
157
-            return [];
158
-        }
159
-        global $wpdb;
160
-        $event_ids = array_map('absint', $event_ids);
161
-        $imploded_sanitized_event_ids = implode(',', $event_ids);
162
-        // Select transactions with registrations for the events $event_ids which also don't have registrations
163
-        // for any events NOT in $event_ids.
164
-        // Notice the outer query searched for transactions whose registrations ARE in $event_ids,
165
-        // whereas the inner query checks if the outer query's transaction has any registrations that are
166
-        // NOT IN $event_ids (ie, don't have registrations for events we're not just about to delete.)
167
-        return array_map(
168
-            'absint',
169
-            $wpdb->get_col(
170
-                "SELECT
147
+	/**
148
+	 * Gets all the transactions related to these events that aren't related to other events. They'll be deleted too.
149
+	 * (Ones that are related to other events can stay around until those other events are deleted too.)
150
+	 * @since 4.10.12.p
151
+	 * @param array $event_ids
152
+	 * @return array of transaction IDs
153
+	 */
154
+	protected function getTransactionsToDelete(array $event_ids): array
155
+	{
156
+		if (empty($event_ids)) {
157
+			return [];
158
+		}
159
+		global $wpdb;
160
+		$event_ids = array_map('absint', $event_ids);
161
+		$imploded_sanitized_event_ids = implode(',', $event_ids);
162
+		// Select transactions with registrations for the events $event_ids which also don't have registrations
163
+		// for any events NOT in $event_ids.
164
+		// Notice the outer query searched for transactions whose registrations ARE in $event_ids,
165
+		// whereas the inner query checks if the outer query's transaction has any registrations that are
166
+		// NOT IN $event_ids (ie, don't have registrations for events we're not just about to delete.)
167
+		return array_map(
168
+			'absint',
169
+			$wpdb->get_col(
170
+				"SELECT
171 171
                       DISTINCT t.TXN_ID
172 172
                     FROM
173 173
                       {$wpdb->prefix}esp_transaction t INNER JOIN
@@ -185,90 +185,90 @@  discard block
 block discarded – undo
185 185
                            tsub.TXN_ID=t.TXN_ID AND
186 186
                            rsub.EVT_ID NOT IN ($imploded_sanitized_event_ids)
187 187
                        )"
188
-            )
189
-        );
190
-    }
188
+			)
189
+		);
190
+	}
191 191
 
192
-    /**
193
-     * Performs another step of the job
194
-     * @param JobParameters $job_parameters
195
-     * @param int $batch_size
196
-     * @return JobStepResponse
197
-     */
198
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
199
-    {
200
-        // Serializing and unserializing is what really makes this drag on (eg on localhost, the ajax requests took
201
-        // about 4 seconds when the batch size was 250, but 3 seconds when the batch size was 50. So like
202
-        // 50% of the request is just serializing and unserializing.) So, make the batches much bigger.
203
-        $batch_size *= 3;
204
-        $units_processed = 0;
205
-        foreach ($job_parameters->extra_datum('roots', array()) as $root_node) {
206
-            if ($units_processed >= $batch_size) {
207
-                break;
208
-            }
209
-            if (!$root_node instanceof ModelObjNode) {
210
-                throw new InvalidClassException('ModelObjNode');
211
-            }
212
-            if ($root_node->isComplete()) {
213
-                continue;
214
-            }
215
-            $units_processed += $root_node->visit($batch_size - $units_processed);
216
-        }
217
-        $job_parameters->mark_processed($units_processed);
218
-        // If the most-recently processed root node is complete, we must be all done because we're doing them
219
-        // sequentially.
220
-        if (! isset($root_node) || ($root_node instanceof ModelObjNode && $root_node->isComplete())) {
221
-            $job_parameters->set_status(JobParameters::status_complete);
222
-            // Show a full progress bar.
223
-            $job_parameters->set_units_processed($job_parameters->job_size());
224
-            $deletion_job_code = $job_parameters->request_datum('deletion_job_code');
225
-            $this->model_obj_node_group_persister->persistModelObjNodesGroup(
226
-                $job_parameters->extra_datum('roots'),
227
-                $deletion_job_code
228
-            );
229
-            return new JobStepResponse($job_parameters, $this->feedback);
230
-        }
231
-        // Because the job size was a guess, it may have likely been proven wrong.
232
-        // We don't want to show more work done than we originally said there would be.
233
-        // So adjust the estimate.
234
-        if (($job_parameters->units_processed() / $job_parameters->job_size()) > .8) {
235
-            $job_parameters->set_job_size($job_parameters->job_size() * 2);
236
-        }
237
-        $this->displayJobStepResults(
238
-            $units_processed,
239
-            esc_html__('Identified up to %d potential items for deletion.', 'event_espresso')
240
-        );
241
-        return new JobStepResponse($job_parameters, $this->feedback);
242
-    }
192
+	/**
193
+	 * Performs another step of the job
194
+	 * @param JobParameters $job_parameters
195
+	 * @param int $batch_size
196
+	 * @return JobStepResponse
197
+	 */
198
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
199
+	{
200
+		// Serializing and unserializing is what really makes this drag on (eg on localhost, the ajax requests took
201
+		// about 4 seconds when the batch size was 250, but 3 seconds when the batch size was 50. So like
202
+		// 50% of the request is just serializing and unserializing.) So, make the batches much bigger.
203
+		$batch_size *= 3;
204
+		$units_processed = 0;
205
+		foreach ($job_parameters->extra_datum('roots', array()) as $root_node) {
206
+			if ($units_processed >= $batch_size) {
207
+				break;
208
+			}
209
+			if (!$root_node instanceof ModelObjNode) {
210
+				throw new InvalidClassException('ModelObjNode');
211
+			}
212
+			if ($root_node->isComplete()) {
213
+				continue;
214
+			}
215
+			$units_processed += $root_node->visit($batch_size - $units_processed);
216
+		}
217
+		$job_parameters->mark_processed($units_processed);
218
+		// If the most-recently processed root node is complete, we must be all done because we're doing them
219
+		// sequentially.
220
+		if (! isset($root_node) || ($root_node instanceof ModelObjNode && $root_node->isComplete())) {
221
+			$job_parameters->set_status(JobParameters::status_complete);
222
+			// Show a full progress bar.
223
+			$job_parameters->set_units_processed($job_parameters->job_size());
224
+			$deletion_job_code = $job_parameters->request_datum('deletion_job_code');
225
+			$this->model_obj_node_group_persister->persistModelObjNodesGroup(
226
+				$job_parameters->extra_datum('roots'),
227
+				$deletion_job_code
228
+			);
229
+			return new JobStepResponse($job_parameters, $this->feedback);
230
+		}
231
+		// Because the job size was a guess, it may have likely been proven wrong.
232
+		// We don't want to show more work done than we originally said there would be.
233
+		// So adjust the estimate.
234
+		if (($job_parameters->units_processed() / $job_parameters->job_size()) > .8) {
235
+			$job_parameters->set_job_size($job_parameters->job_size() * 2);
236
+		}
237
+		$this->displayJobStepResults(
238
+			$units_processed,
239
+			esc_html__('Identified up to %d potential items for deletion.', 'event_espresso')
240
+		);
241
+		return new JobStepResponse($job_parameters, $this->feedback);
242
+	}
243 243
 
244
-    /**
245
-     * Performs any clean-up logic when we know the job is completed
246
-     * @param JobParameters $job_parameters
247
-     * @return JobStepResponse
248
-     */
249
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse
250
-    {
251
-        $this->displayJobFinalResults(
252
-            $job_parameters,
253
-            esc_html__('found %d potential items for deletion.', 'event_espresso')
254
-        );
255
-        $this->updateText(
256
-            $this->infoWrapper(
257
-                sprintf(
258
-                    esc_html__(
259
-                        'If not automatically redirected in %1$s seconds, click here to %2$scontinue to the confirmation step%3$s',
260
-                        'event_espresso'
261
-                    ),
262
-                    '<span id="ee-redirect-timer">10</span>',
263
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
264
-                    '</a>'
265
-                )
266
-            )
267
-        );
268
-        // Nothing much to do.
269
-        // We can't delete the option with the built tree because we may need it in a moment for the deletion
270
-        return new JobStepResponse($job_parameters, $this->feedback);
271
-    }
244
+	/**
245
+	 * Performs any clean-up logic when we know the job is completed
246
+	 * @param JobParameters $job_parameters
247
+	 * @return JobStepResponse
248
+	 */
249
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse
250
+	{
251
+		$this->displayJobFinalResults(
252
+			$job_parameters,
253
+			esc_html__('found %d potential items for deletion.', 'event_espresso')
254
+		);
255
+		$this->updateText(
256
+			$this->infoWrapper(
257
+				sprintf(
258
+					esc_html__(
259
+						'If not automatically redirected in %1$s seconds, click here to %2$scontinue to the confirmation step%3$s',
260
+						'event_espresso'
261
+					),
262
+					'<span id="ee-redirect-timer">10</span>',
263
+					'<a href="' . $job_parameters->request_datum('return_url') . '">',
264
+					'</a>'
265
+				)
266
+			)
267
+		);
268
+		// Nothing much to do.
269
+		// We can't delete the option with the built tree because we may need it in a moment for the deletion
270
+		return new JobStepResponse($job_parameters, $this->feedback);
271
+	}
272 272
 }
273 273
 // End of file EventDeletion.php
274 274
 // Location: EventEspresso\core\libraries\batch\JobHandlers/EventDeletion.php
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/DatetimeOffsetFix.php 2 patches
Indentation   +485 added lines, -485 removed lines patch added patch discarded remove patch
@@ -23,489 +23,489 @@
 block discarded – undo
23 23
 
24 24
 class DatetimeOffsetFix extends JobHandler
25 25
 {
26
-    /**
27
-     * Key for the option used to track which models have been processed when doing the batches.
28
-     */
29
-    const MODELS_TO_PROCESS_OPTION_KEY = 'ee_models_processed_for_datetime_offset_fix';
30
-
31
-
32
-    const COUNT_OF_MODELS_PROCESSED = 'ee_count_of_ee_models_processed_for_datetime_offset_fixed';
33
-
34
-    /**
35
-     * Key for the option used to track what the current offset is that will be applied when this tool is executed.
36
-     */
37
-    const OFFSET_TO_APPLY_OPTION_KEY = 'ee_datetime_offset_fix_offset_to_apply';
38
-
39
-
40
-    const OPTION_KEY_OFFSET_RANGE_START_DATE = 'ee_datetime_offset_start_date_range';
41
-
42
-
43
-    const OPTION_KEY_OFFSET_RANGE_END_DATE = 'ee_datetime_offset_end_date_range';
44
-
45
-
46
-    /**
47
-     * String labelling the datetime offset fix type for change-log entries.
48
-     */
49
-    const DATETIME_OFFSET_FIX_CHANGELOG_TYPE = 'datetime_offset_fix';
50
-
51
-
52
-    /**
53
-     * String labelling a datetime offset fix error for change-log entries.
54
-     */
55
-    const DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE = 'datetime_offset_fix_error';
56
-
57
-    /**
58
-     * @var EEM_Base[]
59
-     */
60
-    protected array $models_with_datetime_fields = [];
61
-
62
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
63
-
64
-
65
-    /**
66
-     * Performs any necessary setup for starting the job. This is also a good
67
-     * place to setup the $job_arguments which will be used for subsequent HTTP requests
68
-     * when continue_job will be called
69
-     *
70
-     * @param JobParameters $job_parameters
71
-     * @return JobStepResponse
72
-     * @throws EE_Error
73
-     * @throws InvalidArgumentException
74
-     * @throws InvalidDataTypeException
75
-     * @throws InvalidInterfaceException
76
-     * @throws ReflectionException
77
-     */
78
-    public function create_job(JobParameters $job_parameters): JobStepResponse
79
-    {
80
-        $models_with_datetime_fields = $this->getModelsWithDatetimeFields();
81
-        // we'll be doing each model as a batch.
82
-        $job_parameters->set_job_size(count($models_with_datetime_fields));
83
-        return new JobStepResponse(
84
-            $job_parameters,
85
-            esc_html__('Starting Datetime Offset Fix', 'event_espresso')
86
-        );
87
-    }
88
-
89
-
90
-    /**
91
-     * Performs another step of the job
92
-     *
93
-     * @param JobParameters $job_parameters
94
-     * @param int           $batch_size
95
-     * @return JobStepResponse
96
-     * @throws EE_Error
97
-     * @throws InvalidArgumentException
98
-     * @throws InvalidDataTypeException
99
-     * @throws InvalidInterfaceException
100
-     * @throws ReflectionException
101
-     */
102
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
103
-    {
104
-        $models_to_process = $this->getModelsWithDatetimeFields();
105
-        // let's pop off the a model and do the query to apply the offset.
106
-        $model_to_process = array_pop($models_to_process);
107
-        // update our record
108
-        $this->setModelsToProcess($models_to_process);
109
-        $this->processModel($model_to_process);
110
-        $this->updateCountOfModelsProcessed();
111
-        $job_parameters->set_units_processed($this->getCountOfModelsProcessed());
112
-        if (count($models_to_process) > 0) {
113
-            $job_parameters->set_status(JobParameters::status_continue);
114
-        } else {
115
-            $job_parameters->set_status(JobParameters::status_complete);
116
-        }
117
-        return new JobStepResponse(
118
-            $job_parameters,
119
-            sprintf(
120
-                esc_html__('Updated the offset for all datetime fields on the %s model.', 'event_espresso'),
121
-                $model_to_process
122
-            )
123
-        );
124
-    }
125
-
126
-
127
-    /**
128
-     * Performs any clean-up logic when we know the job is completed
129
-     *
130
-     * @param JobParameters $job_parameters
131
-     * @return JobStepResponse
132
-     */
133
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse
134
-    {
135
-        // delete important saved options.
136
-        delete_option(self::MODELS_TO_PROCESS_OPTION_KEY);
137
-        delete_option(self::COUNT_OF_MODELS_PROCESSED);
138
-        delete_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE);
139
-        delete_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE);
140
-        return new JobStepResponse($job_parameters, esc_html__(
141
-            'Offset has been applied to all affected fields.',
142
-            'event_espresso'
143
-        ));
144
-    }
145
-
146
-
147
-    /**
148
-     * Contains the logic for processing a model and applying the datetime offset to affected fields on that model.
149
-     *
150
-     * @param string $model_class_name
151
-     * @throws EE_Error
152
-     * @throws ReflectionException
153
-     */
154
-    protected function processModel(string $model_class_name)
155
-    {
156
-        global $wpdb;
157
-        /** @var EEM_Base $model */
158
-        $model             = $model_class_name::instance();
159
-        $original_offset   = self::getOffset();
160
-        $start_date_range  = self::getStartDateRange();
161
-        $end_date_range    = self::getEndDateRange();
162
-        $sql_date_function = $original_offset > 0
163
-            ? 'DATE_ADD'
164
-            : 'DATE_SUB';
165
-        $offset            = abs($original_offset) * 60;
166
-        // since some affected models might have two tables, we have to get our tables and set up a query for each table.
167
-        foreach ($model->get_tables() as $table) {
168
-            $query           = 'UPDATE ' . $table->get_table_name();
169
-            $fields_affected = [];
170
-            $inner_query     = [];
171
-            foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
172
-                if ($model_field instanceof EE_Datetime_Field) {
173
-                    $inner_query[ $model_field->get_table_column() ] = $model_field->get_table_column() . ' = '
174
-                                                                       . $sql_date_function . '('
175
-                                                                       . $model_field->get_table_column()
176
-                                                                       . ", INTERVAL $offset MINUTE)";
177
-                    $fields_affected[]                               = $model_field;
178
-                }
179
-            }
180
-            if (! $fields_affected) {
181
-                continue;
182
-            }
183
-            // do we do one query per column/field or one query for all fields on the model? It all depends on whether
184
-            // there is a date range applied or not.
185
-            if ($start_date_range instanceof DbSafeDateTime || $end_date_range instanceof DbSafeDateTime) {
186
-                $result = $this->doQueryForEachField($query, $inner_query, $start_date_range, $end_date_range);
187
-            } else {
188
-                $result = $this->doQueryForAllFields($query, $inner_query);
189
-            }
190
-
191
-            // record appropriate logs for the query
192
-            switch (true) {
193
-                case $result === false:
194
-                    // record error.
195
-                    $error_message = $wpdb->last_error;
196
-                    // handle the edge cases where last_error might be empty.
197
-                    if (! $error_message) {
198
-                        $error_message = esc_html__('Unknown mysql error occurred.', 'event_espresso');
199
-                    }
200
-                    $this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
201
-                    break;
202
-                case is_array($result) && ! empty($result):
203
-                    foreach ($result as $field_name => $error_message) {
204
-                        $this->recordChangeLog($model, $original_offset, $table, [$field_name], $error_message);
205
-                    }
206
-                    break;
207
-                default:
208
-                    $this->recordChangeLog($model, $original_offset, $table, $fields_affected);
209
-            }
210
-        }
211
-    }
212
-
213
-
214
-    /**
215
-     * Does the query on each $inner_query individually.
216
-     *
217
-     * @param string              $query
218
-     * @param array               $inner_query
219
-     * @param DbSafeDateTime|null $start_date_range
220
-     * @param DbSafeDateTime|null $end_date_range
221
-     * @return array  An array of any errors encountered and the fields they were for.
222
-     */
223
-    private function doQueryForEachField(
224
-        string $query,
225
-        array $inner_query,
226
-        ?DbSafeDateTime $start_date_range,
227
-        ?DbSafeDateTime $end_date_range
228
-    ): array {
229
-        global $wpdb;
230
-        $errors = [];
231
-        foreach ($inner_query as $field_name => $field_query) {
232
-            $query_to_run     = $query;
233
-            $where_conditions = [];
234
-            $query_to_run     .= ' SET ' . $field_query;
235
-            if ($start_date_range instanceof DbSafeDateTime) {
236
-                $start_date         = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
237
-                $where_conditions[] = "$field_name > '$start_date'";
238
-            }
239
-            if ($end_date_range instanceof DbSafeDateTime) {
240
-                $end_date           = $end_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
241
-                $where_conditions[] = "$field_name < '$end_date'";
242
-            }
243
-            if ($where_conditions) {
244
-                $query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
245
-            }
246
-            $result = $wpdb->query($query_to_run);
247
-            if ($result === false) {
248
-                // record error.
249
-                $error_message = $wpdb->last_error;
250
-                // handle the edgecases where last_error might be empty.
251
-                if (! $error_message) {
252
-                    $error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
253
-                }
254
-                $errors[ $field_name ] = $error_message;
255
-            }
256
-        }
257
-        return $errors;
258
-    }
259
-
260
-
261
-    /**
262
-     * Performs the query for all fields within the inner_query
263
-     *
264
-     * @param string $query
265
-     * @param array  $inner_query
266
-     * @return false|int
267
-     */
268
-    private function doQueryForAllFields(string $query, array $inner_query)
269
-    {
270
-        global $wpdb;
271
-        $query .= ' SET ' . implode(',', $inner_query);
272
-        return $wpdb->query($query);
273
-    }
274
-
275
-
276
-    /**
277
-     * Records a changelog entry using the given information.
278
-     *
279
-     * @param EEM_Base              $model
280
-     * @param int|float             $offset
281
-     * @param EE_Table_Base         $table
282
-     * @param EE_Model_Field_Base[] $model_fields_affected
283
-     * @param string                $error_message If present then there was an error so let's record that instead.
284
-     * @throws EE_Error
285
-     * @throws ReflectionException
286
-     */
287
-    private function recordChangeLog(
288
-        EEM_Base $model,
289
-        $offset,
290
-        EE_Table_Base $table,
291
-        array $model_fields_affected,
292
-        string $error_message = ''
293
-    ) {
294
-        // setup $fields list.
295
-        $fields = [];
296
-        /** @var EE_Datetime_Field $model_field */
297
-        foreach ($model_fields_affected as $model_field) {
298
-            if (! $model_field instanceof EE_Datetime_Field) {
299
-                continue;
300
-            }
301
-            $fields[] = $model_field->get_name();
302
-        }
303
-        // setup the message for the changelog entry.
304
-        $message = $error_message
305
-            ? sprintf(
306
-                esc_html__(
307
-                    'The %1$s table for the %2$s model did not have the offset of %3$f applied to its fields (%4$s), because of the following error:%5$s',
308
-                    'event_espresso'
309
-                ),
310
-                $table->get_table_name(),
311
-                $model->get_this_model_name(),
312
-                $offset,
313
-                implode(',', $fields),
314
-                $error_message
315
-            )
316
-            : sprintf(
317
-                esc_html__(
318
-                    'The %1$s table for the %2$s model has had the offset of %3$f applied to its following fields: %4$s',
319
-                    'event_espresso'
320
-                ),
321
-                $table->get_table_name(),
322
-                $model->get_this_model_name(),
323
-                $offset,
324
-                implode(',', $fields)
325
-            );
326
-        // write to the log
327
-        $changelog = EE_Change_Log::new_instance([
328
-            'LOG_type'    => $error_message
329
-                ? self::DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE
330
-                : self::DATETIME_OFFSET_FIX_CHANGELOG_TYPE,
331
-            'LOG_message' => $message,
332
-        ]);
333
-        $changelog->save();
334
-    }
335
-
336
-
337
-    /**
338
-     * Returns an array of models that have datetime fields.
339
-     * This array is added to a short lived transient cache to keep having to build this list to a minimum.
340
-     *
341
-     * @return array an array of model class names.
342
-     * @throws EE_Error
343
-     * @throws InvalidDataTypeException
344
-     * @throws InvalidInterfaceException
345
-     * @throws InvalidArgumentException
346
-     * @throws ReflectionException
347
-     */
348
-    private function getModelsWithDatetimeFields(): array
349
-    {
350
-        $this->getModelsToProcess();
351
-        if (! empty($this->models_with_datetime_fields)) {
352
-            return $this->models_with_datetime_fields;
353
-        }
354
-
355
-        $all_non_abstract_models = EE_Registry::instance()->non_abstract_db_models;
356
-        foreach ($all_non_abstract_models as $non_abstract_model) {
357
-            // get model instance
358
-            /** @var EEM_Base $non_abstract_model */
359
-            $non_abstract_model = $non_abstract_model::instance();
360
-            if ($non_abstract_model->get_a_field_of_type('EE_Datetime_Field') instanceof EE_Datetime_Field) {
361
-                $this->models_with_datetime_fields[] = get_class($non_abstract_model);
362
-            }
363
-        }
364
-        $this->setModelsToProcess($this->models_with_datetime_fields);
365
-        return $this->models_with_datetime_fields;
366
-    }
367
-
368
-
369
-    /**
370
-     * This simply records the models that have been processed with our tracking option.
371
-     *
372
-     * @param array $models_to_set array of model class names.
373
-     */
374
-    private function setModelsToProcess(array $models_to_set)
375
-    {
376
-        update_option(self::MODELS_TO_PROCESS_OPTION_KEY, $models_to_set);
377
-    }
378
-
379
-
380
-    /**
381
-     * Used to keep track of how many models have been processed for the batch
382
-     *
383
-     * @param int $count
384
-     */
385
-    private function updateCountOfModelsProcessed(int $count = 1)
386
-    {
387
-        $count = $this->getCountOfModelsProcessed() + $count;
388
-        update_option(self::COUNT_OF_MODELS_PROCESSED, $count);
389
-    }
390
-
391
-
392
-    /**
393
-     * Retrieve the tracked number of models processed between requests.
394
-     *
395
-     * @return int
396
-     */
397
-    private function getCountOfModelsProcessed(): int
398
-    {
399
-        return (int) get_option(self::COUNT_OF_MODELS_PROCESSED, 0);
400
-    }
401
-
402
-
403
-    /**
404
-     * Returns the models that are left to process.
405
-     *
406
-     * @return array  an array of model class names.
407
-     */
408
-    private function getModelsToProcess(): array
409
-    {
410
-        if (empty($this->models_with_datetime_fields)) {
411
-            $this->models_with_datetime_fields = get_option(self::MODELS_TO_PROCESS_OPTION_KEY, []);
412
-        }
413
-        return $this->models_with_datetime_fields;
414
-    }
415
-
416
-
417
-    /**
418
-     * Used to record the offset that will be applied to dates and times for EE_Datetime_Field columns.
419
-     *
420
-     * @param float $offset
421
-     */
422
-    public static function updateOffset(float $offset)
423
-    {
424
-        update_option(self::OFFSET_TO_APPLY_OPTION_KEY, $offset);
425
-    }
426
-
427
-
428
-    /**
429
-     * Used to retrieve the saved offset that will be applied to dates and times for EE_Datetime_Field columns.
430
-     *
431
-     * @return float
432
-     */
433
-    public static function getOffset(): float
434
-    {
435
-        return (float) get_option(self::OFFSET_TO_APPLY_OPTION_KEY, 0);
436
-    }
437
-
438
-
439
-    /**
440
-     * Used to set the saved offset range start date.
441
-     *
442
-     * @param DbSafeDateTime|null $start_date
443
-     */
444
-    public static function updateStartDateRange(DbSafeDateTime $start_date = null)
445
-    {
446
-        $date_to_save = $start_date instanceof DbSafeDateTime
447
-            ? $start_date->format('U')
448
-            : '';
449
-        update_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, $date_to_save);
450
-    }
451
-
452
-
453
-    /**
454
-     * Used to get the saved offset range start date.
455
-     *
456
-     * @return DbSafeDateTime|null
457
-     */
458
-    public static function getStartDateRange(): ?DbSafeDateTime
459
-    {
460
-        $start_date = get_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, null);
461
-        if (! $start_date) {
462
-            return null;
463
-        }
464
-        try {
465
-            $datetime   = DateTime::createFromFormat('U', $start_date, new DateTimeZone('UTC'));
466
-            $start_date = $datetime instanceof DateTime
467
-                ? DbSafeDateTime::createFromDateTime($datetime)
468
-                : null;
469
-        } catch (Exception $e) {
470
-            $start_date = null;
471
-        }
472
-        return $start_date;
473
-    }
474
-
475
-
476
-    /**
477
-     * Used to set the saved offset range end date.
478
-     *
479
-     * @param DbSafeDateTime|null $end_date
480
-     */
481
-    public static function updateEndDateRange(DbSafeDateTime $end_date = null)
482
-    {
483
-        $date_to_save = $end_date instanceof DbSafeDateTime
484
-            ? $end_date->format('U')
485
-            : '';
486
-        update_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, $date_to_save);
487
-    }
488
-
489
-
490
-    /**
491
-     * Used to get the saved offset range end date.
492
-     *
493
-     * @return DbSafeDateTime|null
494
-     */
495
-    public static function getEndDateRange(): ?DbSafeDateTime
496
-    {
497
-        $end_date = get_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, null);
498
-        if (! $end_date) {
499
-            return null;
500
-        }
501
-        try {
502
-            $datetime = DateTime::createFromFormat('U', $end_date, new DateTimeZone('UTC'));
503
-            $end_date = $datetime instanceof Datetime
504
-                ? DbSafeDateTime::createFromDateTime($datetime)
505
-                : null;
506
-        } catch (Exception $e) {
507
-            $end_date = null;
508
-        }
509
-        return $end_date;
510
-    }
26
+	/**
27
+	 * Key for the option used to track which models have been processed when doing the batches.
28
+	 */
29
+	const MODELS_TO_PROCESS_OPTION_KEY = 'ee_models_processed_for_datetime_offset_fix';
30
+
31
+
32
+	const COUNT_OF_MODELS_PROCESSED = 'ee_count_of_ee_models_processed_for_datetime_offset_fixed';
33
+
34
+	/**
35
+	 * Key for the option used to track what the current offset is that will be applied when this tool is executed.
36
+	 */
37
+	const OFFSET_TO_APPLY_OPTION_KEY = 'ee_datetime_offset_fix_offset_to_apply';
38
+
39
+
40
+	const OPTION_KEY_OFFSET_RANGE_START_DATE = 'ee_datetime_offset_start_date_range';
41
+
42
+
43
+	const OPTION_KEY_OFFSET_RANGE_END_DATE = 'ee_datetime_offset_end_date_range';
44
+
45
+
46
+	/**
47
+	 * String labelling the datetime offset fix type for change-log entries.
48
+	 */
49
+	const DATETIME_OFFSET_FIX_CHANGELOG_TYPE = 'datetime_offset_fix';
50
+
51
+
52
+	/**
53
+	 * String labelling a datetime offset fix error for change-log entries.
54
+	 */
55
+	const DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE = 'datetime_offset_fix_error';
56
+
57
+	/**
58
+	 * @var EEM_Base[]
59
+	 */
60
+	protected array $models_with_datetime_fields = [];
61
+
62
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
63
+
64
+
65
+	/**
66
+	 * Performs any necessary setup for starting the job. This is also a good
67
+	 * place to setup the $job_arguments which will be used for subsequent HTTP requests
68
+	 * when continue_job will be called
69
+	 *
70
+	 * @param JobParameters $job_parameters
71
+	 * @return JobStepResponse
72
+	 * @throws EE_Error
73
+	 * @throws InvalidArgumentException
74
+	 * @throws InvalidDataTypeException
75
+	 * @throws InvalidInterfaceException
76
+	 * @throws ReflectionException
77
+	 */
78
+	public function create_job(JobParameters $job_parameters): JobStepResponse
79
+	{
80
+		$models_with_datetime_fields = $this->getModelsWithDatetimeFields();
81
+		// we'll be doing each model as a batch.
82
+		$job_parameters->set_job_size(count($models_with_datetime_fields));
83
+		return new JobStepResponse(
84
+			$job_parameters,
85
+			esc_html__('Starting Datetime Offset Fix', 'event_espresso')
86
+		);
87
+	}
88
+
89
+
90
+	/**
91
+	 * Performs another step of the job
92
+	 *
93
+	 * @param JobParameters $job_parameters
94
+	 * @param int           $batch_size
95
+	 * @return JobStepResponse
96
+	 * @throws EE_Error
97
+	 * @throws InvalidArgumentException
98
+	 * @throws InvalidDataTypeException
99
+	 * @throws InvalidInterfaceException
100
+	 * @throws ReflectionException
101
+	 */
102
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
103
+	{
104
+		$models_to_process = $this->getModelsWithDatetimeFields();
105
+		// let's pop off the a model and do the query to apply the offset.
106
+		$model_to_process = array_pop($models_to_process);
107
+		// update our record
108
+		$this->setModelsToProcess($models_to_process);
109
+		$this->processModel($model_to_process);
110
+		$this->updateCountOfModelsProcessed();
111
+		$job_parameters->set_units_processed($this->getCountOfModelsProcessed());
112
+		if (count($models_to_process) > 0) {
113
+			$job_parameters->set_status(JobParameters::status_continue);
114
+		} else {
115
+			$job_parameters->set_status(JobParameters::status_complete);
116
+		}
117
+		return new JobStepResponse(
118
+			$job_parameters,
119
+			sprintf(
120
+				esc_html__('Updated the offset for all datetime fields on the %s model.', 'event_espresso'),
121
+				$model_to_process
122
+			)
123
+		);
124
+	}
125
+
126
+
127
+	/**
128
+	 * Performs any clean-up logic when we know the job is completed
129
+	 *
130
+	 * @param JobParameters $job_parameters
131
+	 * @return JobStepResponse
132
+	 */
133
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse
134
+	{
135
+		// delete important saved options.
136
+		delete_option(self::MODELS_TO_PROCESS_OPTION_KEY);
137
+		delete_option(self::COUNT_OF_MODELS_PROCESSED);
138
+		delete_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE);
139
+		delete_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE);
140
+		return new JobStepResponse($job_parameters, esc_html__(
141
+			'Offset has been applied to all affected fields.',
142
+			'event_espresso'
143
+		));
144
+	}
145
+
146
+
147
+	/**
148
+	 * Contains the logic for processing a model and applying the datetime offset to affected fields on that model.
149
+	 *
150
+	 * @param string $model_class_name
151
+	 * @throws EE_Error
152
+	 * @throws ReflectionException
153
+	 */
154
+	protected function processModel(string $model_class_name)
155
+	{
156
+		global $wpdb;
157
+		/** @var EEM_Base $model */
158
+		$model             = $model_class_name::instance();
159
+		$original_offset   = self::getOffset();
160
+		$start_date_range  = self::getStartDateRange();
161
+		$end_date_range    = self::getEndDateRange();
162
+		$sql_date_function = $original_offset > 0
163
+			? 'DATE_ADD'
164
+			: 'DATE_SUB';
165
+		$offset            = abs($original_offset) * 60;
166
+		// since some affected models might have two tables, we have to get our tables and set up a query for each table.
167
+		foreach ($model->get_tables() as $table) {
168
+			$query           = 'UPDATE ' . $table->get_table_name();
169
+			$fields_affected = [];
170
+			$inner_query     = [];
171
+			foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
172
+				if ($model_field instanceof EE_Datetime_Field) {
173
+					$inner_query[ $model_field->get_table_column() ] = $model_field->get_table_column() . ' = '
174
+																	   . $sql_date_function . '('
175
+																	   . $model_field->get_table_column()
176
+																	   . ", INTERVAL $offset MINUTE)";
177
+					$fields_affected[]                               = $model_field;
178
+				}
179
+			}
180
+			if (! $fields_affected) {
181
+				continue;
182
+			}
183
+			// do we do one query per column/field or one query for all fields on the model? It all depends on whether
184
+			// there is a date range applied or not.
185
+			if ($start_date_range instanceof DbSafeDateTime || $end_date_range instanceof DbSafeDateTime) {
186
+				$result = $this->doQueryForEachField($query, $inner_query, $start_date_range, $end_date_range);
187
+			} else {
188
+				$result = $this->doQueryForAllFields($query, $inner_query);
189
+			}
190
+
191
+			// record appropriate logs for the query
192
+			switch (true) {
193
+				case $result === false:
194
+					// record error.
195
+					$error_message = $wpdb->last_error;
196
+					// handle the edge cases where last_error might be empty.
197
+					if (! $error_message) {
198
+						$error_message = esc_html__('Unknown mysql error occurred.', 'event_espresso');
199
+					}
200
+					$this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
201
+					break;
202
+				case is_array($result) && ! empty($result):
203
+					foreach ($result as $field_name => $error_message) {
204
+						$this->recordChangeLog($model, $original_offset, $table, [$field_name], $error_message);
205
+					}
206
+					break;
207
+				default:
208
+					$this->recordChangeLog($model, $original_offset, $table, $fields_affected);
209
+			}
210
+		}
211
+	}
212
+
213
+
214
+	/**
215
+	 * Does the query on each $inner_query individually.
216
+	 *
217
+	 * @param string              $query
218
+	 * @param array               $inner_query
219
+	 * @param DbSafeDateTime|null $start_date_range
220
+	 * @param DbSafeDateTime|null $end_date_range
221
+	 * @return array  An array of any errors encountered and the fields they were for.
222
+	 */
223
+	private function doQueryForEachField(
224
+		string $query,
225
+		array $inner_query,
226
+		?DbSafeDateTime $start_date_range,
227
+		?DbSafeDateTime $end_date_range
228
+	): array {
229
+		global $wpdb;
230
+		$errors = [];
231
+		foreach ($inner_query as $field_name => $field_query) {
232
+			$query_to_run     = $query;
233
+			$where_conditions = [];
234
+			$query_to_run     .= ' SET ' . $field_query;
235
+			if ($start_date_range instanceof DbSafeDateTime) {
236
+				$start_date         = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
237
+				$where_conditions[] = "$field_name > '$start_date'";
238
+			}
239
+			if ($end_date_range instanceof DbSafeDateTime) {
240
+				$end_date           = $end_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
241
+				$where_conditions[] = "$field_name < '$end_date'";
242
+			}
243
+			if ($where_conditions) {
244
+				$query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
245
+			}
246
+			$result = $wpdb->query($query_to_run);
247
+			if ($result === false) {
248
+				// record error.
249
+				$error_message = $wpdb->last_error;
250
+				// handle the edgecases where last_error might be empty.
251
+				if (! $error_message) {
252
+					$error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
253
+				}
254
+				$errors[ $field_name ] = $error_message;
255
+			}
256
+		}
257
+		return $errors;
258
+	}
259
+
260
+
261
+	/**
262
+	 * Performs the query for all fields within the inner_query
263
+	 *
264
+	 * @param string $query
265
+	 * @param array  $inner_query
266
+	 * @return false|int
267
+	 */
268
+	private function doQueryForAllFields(string $query, array $inner_query)
269
+	{
270
+		global $wpdb;
271
+		$query .= ' SET ' . implode(',', $inner_query);
272
+		return $wpdb->query($query);
273
+	}
274
+
275
+
276
+	/**
277
+	 * Records a changelog entry using the given information.
278
+	 *
279
+	 * @param EEM_Base              $model
280
+	 * @param int|float             $offset
281
+	 * @param EE_Table_Base         $table
282
+	 * @param EE_Model_Field_Base[] $model_fields_affected
283
+	 * @param string                $error_message If present then there was an error so let's record that instead.
284
+	 * @throws EE_Error
285
+	 * @throws ReflectionException
286
+	 */
287
+	private function recordChangeLog(
288
+		EEM_Base $model,
289
+		$offset,
290
+		EE_Table_Base $table,
291
+		array $model_fields_affected,
292
+		string $error_message = ''
293
+	) {
294
+		// setup $fields list.
295
+		$fields = [];
296
+		/** @var EE_Datetime_Field $model_field */
297
+		foreach ($model_fields_affected as $model_field) {
298
+			if (! $model_field instanceof EE_Datetime_Field) {
299
+				continue;
300
+			}
301
+			$fields[] = $model_field->get_name();
302
+		}
303
+		// setup the message for the changelog entry.
304
+		$message = $error_message
305
+			? sprintf(
306
+				esc_html__(
307
+					'The %1$s table for the %2$s model did not have the offset of %3$f applied to its fields (%4$s), because of the following error:%5$s',
308
+					'event_espresso'
309
+				),
310
+				$table->get_table_name(),
311
+				$model->get_this_model_name(),
312
+				$offset,
313
+				implode(',', $fields),
314
+				$error_message
315
+			)
316
+			: sprintf(
317
+				esc_html__(
318
+					'The %1$s table for the %2$s model has had the offset of %3$f applied to its following fields: %4$s',
319
+					'event_espresso'
320
+				),
321
+				$table->get_table_name(),
322
+				$model->get_this_model_name(),
323
+				$offset,
324
+				implode(',', $fields)
325
+			);
326
+		// write to the log
327
+		$changelog = EE_Change_Log::new_instance([
328
+			'LOG_type'    => $error_message
329
+				? self::DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE
330
+				: self::DATETIME_OFFSET_FIX_CHANGELOG_TYPE,
331
+			'LOG_message' => $message,
332
+		]);
333
+		$changelog->save();
334
+	}
335
+
336
+
337
+	/**
338
+	 * Returns an array of models that have datetime fields.
339
+	 * This array is added to a short lived transient cache to keep having to build this list to a minimum.
340
+	 *
341
+	 * @return array an array of model class names.
342
+	 * @throws EE_Error
343
+	 * @throws InvalidDataTypeException
344
+	 * @throws InvalidInterfaceException
345
+	 * @throws InvalidArgumentException
346
+	 * @throws ReflectionException
347
+	 */
348
+	private function getModelsWithDatetimeFields(): array
349
+	{
350
+		$this->getModelsToProcess();
351
+		if (! empty($this->models_with_datetime_fields)) {
352
+			return $this->models_with_datetime_fields;
353
+		}
354
+
355
+		$all_non_abstract_models = EE_Registry::instance()->non_abstract_db_models;
356
+		foreach ($all_non_abstract_models as $non_abstract_model) {
357
+			// get model instance
358
+			/** @var EEM_Base $non_abstract_model */
359
+			$non_abstract_model = $non_abstract_model::instance();
360
+			if ($non_abstract_model->get_a_field_of_type('EE_Datetime_Field') instanceof EE_Datetime_Field) {
361
+				$this->models_with_datetime_fields[] = get_class($non_abstract_model);
362
+			}
363
+		}
364
+		$this->setModelsToProcess($this->models_with_datetime_fields);
365
+		return $this->models_with_datetime_fields;
366
+	}
367
+
368
+
369
+	/**
370
+	 * This simply records the models that have been processed with our tracking option.
371
+	 *
372
+	 * @param array $models_to_set array of model class names.
373
+	 */
374
+	private function setModelsToProcess(array $models_to_set)
375
+	{
376
+		update_option(self::MODELS_TO_PROCESS_OPTION_KEY, $models_to_set);
377
+	}
378
+
379
+
380
+	/**
381
+	 * Used to keep track of how many models have been processed for the batch
382
+	 *
383
+	 * @param int $count
384
+	 */
385
+	private function updateCountOfModelsProcessed(int $count = 1)
386
+	{
387
+		$count = $this->getCountOfModelsProcessed() + $count;
388
+		update_option(self::COUNT_OF_MODELS_PROCESSED, $count);
389
+	}
390
+
391
+
392
+	/**
393
+	 * Retrieve the tracked number of models processed between requests.
394
+	 *
395
+	 * @return int
396
+	 */
397
+	private function getCountOfModelsProcessed(): int
398
+	{
399
+		return (int) get_option(self::COUNT_OF_MODELS_PROCESSED, 0);
400
+	}
401
+
402
+
403
+	/**
404
+	 * Returns the models that are left to process.
405
+	 *
406
+	 * @return array  an array of model class names.
407
+	 */
408
+	private function getModelsToProcess(): array
409
+	{
410
+		if (empty($this->models_with_datetime_fields)) {
411
+			$this->models_with_datetime_fields = get_option(self::MODELS_TO_PROCESS_OPTION_KEY, []);
412
+		}
413
+		return $this->models_with_datetime_fields;
414
+	}
415
+
416
+
417
+	/**
418
+	 * Used to record the offset that will be applied to dates and times for EE_Datetime_Field columns.
419
+	 *
420
+	 * @param float $offset
421
+	 */
422
+	public static function updateOffset(float $offset)
423
+	{
424
+		update_option(self::OFFSET_TO_APPLY_OPTION_KEY, $offset);
425
+	}
426
+
427
+
428
+	/**
429
+	 * Used to retrieve the saved offset that will be applied to dates and times for EE_Datetime_Field columns.
430
+	 *
431
+	 * @return float
432
+	 */
433
+	public static function getOffset(): float
434
+	{
435
+		return (float) get_option(self::OFFSET_TO_APPLY_OPTION_KEY, 0);
436
+	}
437
+
438
+
439
+	/**
440
+	 * Used to set the saved offset range start date.
441
+	 *
442
+	 * @param DbSafeDateTime|null $start_date
443
+	 */
444
+	public static function updateStartDateRange(DbSafeDateTime $start_date = null)
445
+	{
446
+		$date_to_save = $start_date instanceof DbSafeDateTime
447
+			? $start_date->format('U')
448
+			: '';
449
+		update_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, $date_to_save);
450
+	}
451
+
452
+
453
+	/**
454
+	 * Used to get the saved offset range start date.
455
+	 *
456
+	 * @return DbSafeDateTime|null
457
+	 */
458
+	public static function getStartDateRange(): ?DbSafeDateTime
459
+	{
460
+		$start_date = get_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, null);
461
+		if (! $start_date) {
462
+			return null;
463
+		}
464
+		try {
465
+			$datetime   = DateTime::createFromFormat('U', $start_date, new DateTimeZone('UTC'));
466
+			$start_date = $datetime instanceof DateTime
467
+				? DbSafeDateTime::createFromDateTime($datetime)
468
+				: null;
469
+		} catch (Exception $e) {
470
+			$start_date = null;
471
+		}
472
+		return $start_date;
473
+	}
474
+
475
+
476
+	/**
477
+	 * Used to set the saved offset range end date.
478
+	 *
479
+	 * @param DbSafeDateTime|null $end_date
480
+	 */
481
+	public static function updateEndDateRange(DbSafeDateTime $end_date = null)
482
+	{
483
+		$date_to_save = $end_date instanceof DbSafeDateTime
484
+			? $end_date->format('U')
485
+			: '';
486
+		update_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, $date_to_save);
487
+	}
488
+
489
+
490
+	/**
491
+	 * Used to get the saved offset range end date.
492
+	 *
493
+	 * @return DbSafeDateTime|null
494
+	 */
495
+	public static function getEndDateRange(): ?DbSafeDateTime
496
+	{
497
+		$end_date = get_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, null);
498
+		if (! $end_date) {
499
+			return null;
500
+		}
501
+		try {
502
+			$datetime = DateTime::createFromFormat('U', $end_date, new DateTimeZone('UTC'));
503
+			$end_date = $datetime instanceof Datetime
504
+				? DbSafeDateTime::createFromDateTime($datetime)
505
+				: null;
506
+		} catch (Exception $e) {
507
+			$end_date = null;
508
+		}
509
+		return $end_date;
510
+	}
511 511
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -165,19 +165,19 @@  discard block
 block discarded – undo
165 165
         $offset            = abs($original_offset) * 60;
166 166
         // since some affected models might have two tables, we have to get our tables and set up a query for each table.
167 167
         foreach ($model->get_tables() as $table) {
168
-            $query           = 'UPDATE ' . $table->get_table_name();
168
+            $query           = 'UPDATE '.$table->get_table_name();
169 169
             $fields_affected = [];
170 170
             $inner_query     = [];
171 171
             foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
172 172
                 if ($model_field instanceof EE_Datetime_Field) {
173
-                    $inner_query[ $model_field->get_table_column() ] = $model_field->get_table_column() . ' = '
174
-                                                                       . $sql_date_function . '('
173
+                    $inner_query[$model_field->get_table_column()] = $model_field->get_table_column().' = '
174
+                                                                       . $sql_date_function.'('
175 175
                                                                        . $model_field->get_table_column()
176 176
                                                                        . ", INTERVAL $offset MINUTE)";
177
-                    $fields_affected[]                               = $model_field;
177
+                    $fields_affected[] = $model_field;
178 178
                 }
179 179
             }
180
-            if (! $fields_affected) {
180
+            if ( ! $fields_affected) {
181 181
                 continue;
182 182
             }
183 183
             // do we do one query per column/field or one query for all fields on the model? It all depends on whether
@@ -194,7 +194,7 @@  discard block
 block discarded – undo
194 194
                     // record error.
195 195
                     $error_message = $wpdb->last_error;
196 196
                     // handle the edge cases where last_error might be empty.
197
-                    if (! $error_message) {
197
+                    if ( ! $error_message) {
198 198
                         $error_message = esc_html__('Unknown mysql error occurred.', 'event_espresso');
199 199
                     }
200 200
                     $this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
         foreach ($inner_query as $field_name => $field_query) {
232 232
             $query_to_run     = $query;
233 233
             $where_conditions = [];
234
-            $query_to_run     .= ' SET ' . $field_query;
234
+            $query_to_run .= ' SET '.$field_query;
235 235
             if ($start_date_range instanceof DbSafeDateTime) {
236 236
                 $start_date         = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
237 237
                 $where_conditions[] = "$field_name > '$start_date'";
@@ -241,17 +241,17 @@  discard block
 block discarded – undo
241 241
                 $where_conditions[] = "$field_name < '$end_date'";
242 242
             }
243 243
             if ($where_conditions) {
244
-                $query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
244
+                $query_to_run .= ' WHERE '.implode(' AND ', $where_conditions);
245 245
             }
246 246
             $result = $wpdb->query($query_to_run);
247 247
             if ($result === false) {
248 248
                 // record error.
249 249
                 $error_message = $wpdb->last_error;
250 250
                 // handle the edgecases where last_error might be empty.
251
-                if (! $error_message) {
251
+                if ( ! $error_message) {
252 252
                     $error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
253 253
                 }
254
-                $errors[ $field_name ] = $error_message;
254
+                $errors[$field_name] = $error_message;
255 255
             }
256 256
         }
257 257
         return $errors;
@@ -268,7 +268,7 @@  discard block
 block discarded – undo
268 268
     private function doQueryForAllFields(string $query, array $inner_query)
269 269
     {
270 270
         global $wpdb;
271
-        $query .= ' SET ' . implode(',', $inner_query);
271
+        $query .= ' SET '.implode(',', $inner_query);
272 272
         return $wpdb->query($query);
273 273
     }
274 274
 
@@ -295,7 +295,7 @@  discard block
 block discarded – undo
295 295
         $fields = [];
296 296
         /** @var EE_Datetime_Field $model_field */
297 297
         foreach ($model_fields_affected as $model_field) {
298
-            if (! $model_field instanceof EE_Datetime_Field) {
298
+            if ( ! $model_field instanceof EE_Datetime_Field) {
299 299
                 continue;
300 300
             }
301 301
             $fields[] = $model_field->get_name();
@@ -348,7 +348,7 @@  discard block
 block discarded – undo
348 348
     private function getModelsWithDatetimeFields(): array
349 349
     {
350 350
         $this->getModelsToProcess();
351
-        if (! empty($this->models_with_datetime_fields)) {
351
+        if ( ! empty($this->models_with_datetime_fields)) {
352 352
             return $this->models_with_datetime_fields;
353 353
         }
354 354
 
@@ -458,7 +458,7 @@  discard block
 block discarded – undo
458 458
     public static function getStartDateRange(): ?DbSafeDateTime
459 459
     {
460 460
         $start_date = get_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, null);
461
-        if (! $start_date) {
461
+        if ( ! $start_date) {
462 462
             return null;
463 463
         }
464 464
         try {
@@ -495,7 +495,7 @@  discard block
 block discarded – undo
495 495
     public static function getEndDateRange(): ?DbSafeDateTime
496 496
     {
497 497
         $end_date = get_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, null);
498
-        if (! $end_date) {
498
+        if ( ! $end_date) {
499 499
             return null;
500 500
         }
501 501
         try {
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/RegistrationsReport.php 1 patch
Indentation   +600 added lines, -600 removed lines patch added patch discarded remove patch
@@ -42,604 +42,604 @@
 block discarded – undo
42 42
  */
43 43
 class RegistrationsReport extends JobHandlerFile
44 44
 {
45
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
46
-    // phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
47
-    /**
48
-     * Performs any necessary setup for starting the job. This is also a good
49
-     * place to setup the $job_arguments which will be used for subsequent HTTP requests
50
-     * when continue_job will be called
51
-     *
52
-     * @param JobParameters $job_parameters
53
-     * @return JobStepResponse
54
-     * @throws BatchRequestException
55
-     * @throws EE_Error
56
-     * @throws ReflectionException
57
-     */
58
-    public function create_job(JobParameters $job_parameters): JobStepResponse
59
-    {
60
-        $event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
61
-        $DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
62
-        if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
63
-            throw new BatchRequestException(
64
-                esc_html__('You do not have permission to view registrations', 'event_espresso')
65
-            );
66
-        }
67
-        $filepath = $this->create_file_from_job_with_name(
68
-            $job_parameters->job_id(),
69
-            $this->get_filename()
70
-        );
71
-        $job_parameters->add_extra_data('filepath', $filepath);
72
-
73
-        if ($job_parameters->request_datum('use_filters', false)) {
74
-            $query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
75
-        } else {
76
-            $query_params = [
77
-                [
78
-                    'OR'                 => [
79
-                        // don't include registrations from failed or abandoned transactions...
80
-                        'Transaction.STS_ID' => [
81
-                            'NOT IN',
82
-                            [
83
-                                EEM_Transaction::failed_status_code,
84
-                                EEM_Transaction::abandoned_status_code,
85
-                            ],
86
-                        ],
87
-                        // unless the registration is approved,
88
-                        // in which case include it regardless of transaction status
89
-                        'STS_ID'             => EEM_Registration::status_id_approved,
90
-                    ],
91
-                    'Ticket.TKT_deleted' => ['IN', [true, false]],
92
-                ],
93
-                'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
94
-                'force_join' => ['Transaction', 'Ticket', 'Attendee'],
95
-                'caps'       => EEM_Base::caps_read_admin,
96
-            ];
97
-            if ($event_id) {
98
-                $query_params[0]['EVT_ID'] = $event_id;
99
-            } else {
100
-                $query_params['force_join'][] = 'Event';
101
-            }
102
-        }
103
-
104
-        if (! isset($query_params['force_join'])) {
105
-            $query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
106
-        }
107
-
108
-        $return_url_args = [];
109
-        parse_str(
110
-            parse_url(
111
-                $job_parameters->request_datum('return_url', ''),
112
-                PHP_URL_QUERY
113
-            ),
114
-            $return_url_args
115
-        );
116
-
117
-        if (
118
-            isset($return_url_args['orderby'], $return_url_args['order'])
119
-            && $return_url_args['orderby'] === 'ATT_lname'
120
-        ) {
121
-            $query_params['order_by'] = [
122
-                'Attendee.ATT_lname' => $return_url_args['order'],
123
-                'Attendee.ATT_fname' => $return_url_args['order'],
124
-                'REG_ID' => $return_url_args['order']
125
-            ];
126
-        }
127
-
128
-        $query_params = apply_filters(
129
-            'FHEE__EE_Export__report_registration_for_event',
130
-            $query_params,
131
-            $event_id
132
-        );
133
-
134
-        $job_parameters->add_extra_data('query_params', $query_params);
135
-        $question_labels = $this->_get_question_labels($query_params);
136
-        $job_parameters->add_extra_data('question_labels', $question_labels);
137
-        $job_parameters->set_job_size($this->count_units_to_process($query_params));
138
-        // we need to set the header columns
139
-        // but to do that we need to process one row so that we can extract ALL of the column headers
140
-        $csv_data_for_row = $this->get_csv_data_for(
141
-            $event_id,
142
-            0,
143
-            1,
144
-            $question_labels,
145
-            $query_params,
146
-            $DTT_ID
147
-        );
148
-        // but we don't want to write any actual data yet...
149
-        // so let's blank out all of the values for that first row
150
-        array_walk(
151
-            $csv_data_for_row[0],
152
-            function (&$value) {
153
-                $value = null;
154
-            }
155
-        );
156
-
157
-        EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row, true, true);
158
-        $this->updateTextHeader(
159
-            esc_html__('Registrations report started successfully...', 'event_espresso')
160
-        );
161
-        return new JobStepResponse($job_parameters, $this->feedback);
162
-    }
163
-
164
-
165
-    /**
166
-     * Gets the filename
167
-     *
168
-     * @return string
169
-     */
170
-    protected function get_filename(): string
171
-    {
172
-        return apply_filters(
173
-            'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__get_filename',
174
-            sprintf(
175
-                'event-espresso-registrations-%s.csv',
176
-                str_replace([':', ' '], '-', current_time('mysql'))
177
-            )
178
-        );
179
-    }
180
-
181
-
182
-    /**
183
-     * Gets the questions which are to be used for this report,
184
-     * so they can be remembered for later
185
-     *
186
-     * @param array $registration_query_params
187
-     * @return array question admin labels to be used for this report
188
-     * @throws EE_Error
189
-     * @throws ReflectionException
190
-     */
191
-    protected function _get_question_labels(array $registration_query_params): array
192
-    {
193
-        $where                 = $registration_query_params[0] ?? null;
194
-        $question_query_params = [];
195
-        if ($where !== null) {
196
-            $question_query_params = [
197
-                $this->_change_registration_where_params_to_question_where_params($registration_query_params[0]),
198
-            ];
199
-        }
200
-        // Make sure it's not a system question
201
-        $question_query_params[0]['OR*not-system-questions'] = [
202
-            'QST_system'      => '',
203
-            'QST_system*null' => ['IS_NULL']
204
-        ];
205
-        if (
206
-            apply_filters(
207
-                'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport___get_question_labels__only_include_answered_questions',
208
-                false,
209
-                $registration_query_params
210
-            )
211
-        ) {
212
-            $question_query_params[0]['Answer.ANS_ID'] = ['IS_NOT_NULL'];
213
-        }
214
-        $question_query_params['order_by'] = ['QST_admin_label' => 'ASC'];
215
-        $question_query_params['group_by'] = ['QST_ID'];
216
-        return array_unique(EEM_Question::instance()->get_col($question_query_params, 'QST_admin_label'));
217
-    }
218
-
219
-
220
-    /**
221
-     * Takes where params meant for registrations and changes them to work for questions
222
-     *
223
-     * @param array $reg_where_params
224
-     * @return array
225
-     * @throws EE_Error
226
-     * @throws ReflectionException
227
-     */
228
-    protected function _change_registration_where_params_to_question_where_params(array $reg_where_params): array
229
-    {
230
-        $question_where_params = [];
231
-        foreach ($reg_where_params as $key => $val) {
232
-            if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
233
-                $question_where_params[ $key ] =
234
-                    $this->_change_registration_where_params_to_question_where_params($val);
235
-            } else {
236
-                // it's a normal where condition
237
-                $question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
238
-            }
239
-        }
240
-        return $question_where_params;
241
-    }
242
-
243
-
244
-    /**
245
-     * Performs another step of the job
246
-     *
247
-     * @param JobParameters $job_parameters
248
-     * @param int           $batch_size
249
-     * @return JobStepResponse
250
-     * @throws EE_Error
251
-     * @throws ReflectionException
252
-     */
253
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
254
-    {
255
-        if ($job_parameters->units_processed() < $job_parameters->job_size()) {
256
-            $csv_data = $this->get_csv_data_for(
257
-                (int) $job_parameters->request_datum('EVT_ID', '0'),
258
-                $job_parameters->units_processed(),
259
-                $batch_size,
260
-                $job_parameters->extra_datum('question_labels'),
261
-                $job_parameters->extra_datum('query_params'),
262
-                (int) $job_parameters->request_datum('DTT_ID', '0')
263
-            );
264
-            EEH_Export::write_data_array_to_csv(
265
-                $job_parameters->extra_datum('filepath'),
266
-                $csv_data,
267
-                false
268
-            );
269
-            $units_processed = count($csv_data);
270
-            if ($units_processed) {
271
-                $job_parameters->mark_processed($units_processed);
272
-                $this->updateText(
273
-                    sprintf(
274
-                        esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
275
-                        $units_processed
276
-                    )
277
-                );
278
-            }
279
-        }
280
-        $extra_response_data = ['file_url' => ''];
281
-        if ($job_parameters->units_processed() >= $job_parameters->job_size()) {
282
-            $job_parameters->set_status(JobParameters::status_complete);
283
-            $extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
284
-            $this->displayJobFinalResults($job_parameters);
285
-        } else {
286
-            $job_parameters->set_status(JobParameters::status_continue);
287
-        }
288
-        return new JobStepResponse($job_parameters, $this->feedback, $extra_response_data);
289
-    }
290
-
291
-
292
-    /**
293
-     * Gets the csv data for a batch of registrations
294
-     *
295
-     * @param int|null $event_id
296
-     * @param int      $offset
297
-     * @param int      $limit
298
-     * @param array    $question_labels the IDs for all the questions which were answered by someone in this selection
299
-     * @param array    $query_params    for using where querying the model
300
-     * @param int      $DTT_ID
301
-     * @return array top-level keys are numeric, next-level keys are column headers
302
-     * @throws EE_Error
303
-     * @throws ReflectionException
304
-     */
305
-    public function get_csv_data_for(
306
-        ?int $event_id,
307
-        int $offset,
308
-        int $limit,
309
-        array $question_labels,
310
-        array $query_params,
311
-        int $DTT_ID = 0
312
-    ): array {
313
-        $reg_fields_to_include = [
314
-            'TXN_ID',
315
-            'ATT_ID',
316
-            'REG_ID',
317
-            'REG_date',
318
-            'REG_code',
319
-            'REG_count',
320
-            'REG_final_price',
321
-        ];
322
-        $att_fields_to_include = [
323
-            'ATT_fname',
324
-            'ATT_lname',
325
-            'ATT_email',
326
-            'ATT_address',
327
-            'ATT_address2',
328
-            'ATT_city',
329
-            'STA_ID',
330
-            'CNT_ISO',
331
-            'ATT_zip',
332
-            'ATT_phone',
333
-        ];
334
-
335
-        // get models
336
-        $event_model   = EEM_Event::instance();
337
-        $date_model    = EEM_Datetime::instance();
338
-        $ticket_model  = EEM_Ticket::instance();
339
-        $txn_model     = EEM_Transaction::instance();
340
-        $reg_model     = EEM_Registration::instance();
341
-        $pay_model     = EEM_Payment::instance();
342
-        $status_model  = EEM_Status::instance();
343
-
344
-        $registrations_csv_ready_array = [];
345
-        $query_params['limit']         = [$offset, $limit];
346
-        $registration_rows             = $reg_model->get_all_wpdb_results($query_params);
347
-
348
-        foreach ($registration_rows as $reg_row) {
349
-            if (! is_array($reg_row)) {
350
-                continue;
351
-            }
352
-            $reg_csv_array = [];
353
-            // registration ID
354
-            $reg_id_field = $reg_model->field_settings_for('REG_ID');
355
-            $reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
356
-                EEH_Export::prepare_value_from_db_for_display(
357
-                    $reg_model,
358
-                    'REG_ID',
359
-                    $reg_row[ $reg_id_field->get_qualified_column() ]
360
-                );
361
-            // ALL registrations, or is list filtered to just one?
362
-            if (! $event_id) {
363
-                // ALL registrations, so get each event's name and ID
364
-                $reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
365
-                    /* translators: 1: event name, 2: event ID */
366
-                    esc_html__('%1$s (%2$s)', 'event_espresso'),
367
-                    EEH_Export::prepare_value_from_db_for_display(
368
-                        $event_model,
369
-                        'EVT_name',
370
-                        $reg_row['Event_CPT.post_title']
371
-                    ),
372
-                    $reg_row['Event_CPT.ID']
373
-                );
374
-            }
375
-            // add attendee columns
376
-            $reg_csv_array = AttendeeCSV::addAttendeeColumns($att_fields_to_include, $reg_row, $reg_csv_array);
377
-            // add registration columns
378
-            $reg_csv_array = RegistrationCSV::addRegistrationColumns($reg_fields_to_include, $reg_row, $reg_csv_array);
379
-            // get pretty status
380
-            $stati = $status_model->localized_status(
381
-                [
382
-                    $reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
383
-                    $reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
384
-                ],
385
-                false,
386
-                'sentence'
387
-            );
388
-            $is_primary_reg = $reg_row['Registration.REG_count'] == '1';
389
-
390
-            $reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
391
-                $stati[ $reg_row['Registration.STS_ID'] ];
392
-            // get pretty transaction status
393
-            $reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
394
-                $stati[ $reg_row['TransactionTable.STS_ID'] ];
395
-            $reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
396
-                ? EEH_Export::prepare_value_from_db_for_display(
397
-                    $txn_model,
398
-                    'TXN_total',
399
-                    $reg_row['TransactionTable.TXN_total'],
400
-                    'localized_float'
401
-                )
402
-                : '0.00';
403
-
404
-            $reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
405
-                ? EEH_Export::prepare_value_from_db_for_display(
406
-                    $txn_model,
407
-                    'TXN_paid',
408
-                    $reg_row['TransactionTable.TXN_paid'],
409
-                    'localized_float'
410
-                )
411
-                : '0.00';
412
-
413
-            $payment_methods                                                                  = [];
414
-            $gateway_txn_ids_etc                                                              = [];
415
-            $payment_times                                                                    = [];
416
-            if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
417
-                $payments_info = $pay_model->get_all_wpdb_results(
418
-                    [
419
-                        [
420
-                            'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
421
-                            'STS_ID' => EEM_Payment::status_id_approved,
422
-                        ],
423
-                        'force_join' => ['Payment_Method'],
424
-                    ],
425
-                    ARRAY_A,
426
-                    'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
427
-                );
428
-                [$payment_methods, $gateway_txn_ids_etc, $payment_times] = PaymentsInfoCSV::extractPaymentInfo(
429
-                    $payments_info
430
-                );
431
-            }
432
-
433
-            $reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
434
-                ',',
435
-                $payment_times
436
-            );
437
-
438
-            $reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
439
-                ',',
440
-                $payment_methods
441
-            );
442
-
443
-            $reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
444
-                ',',
445
-                $gateway_txn_ids_etc
446
-            );
447
-
448
-            $ticket_name      = esc_html__('Unknown', 'event_espresso');
449
-            $datetime_strings = [esc_html__('Unknown', 'event_espresso')];
450
-            if ($reg_row['Ticket.TKT_ID']) {
451
-                $ticket_name       = EEH_Export::prepare_value_from_db_for_display(
452
-                    $ticket_model,
453
-                    'TKT_name',
454
-                    $reg_row['Ticket.TKT_name']
455
-                );
456
-                $datetime_strings = [];
457
-                $datetimes        = $date_model->get_all_wpdb_results(
458
-                    [
459
-                        ['Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']],
460
-                        'order_by'                 => ['DTT_EVT_start' => 'ASC'],
461
-                        'default_where_conditions' => 'none',
462
-                    ]
463
-                );
464
-                foreach ($datetimes as $datetime) {
465
-                    $datetime_strings[] = EEH_Export::prepare_value_from_db_for_display(
466
-                        $date_model,
467
-                        'DTT_EVT_start',
468
-                        $datetime['Datetime.DTT_EVT_start']
469
-                    );
470
-                }
471
-            }
472
-
473
-            $reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
474
-
475
-
476
-            $reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
477
-                ', ',
478
-                $datetime_strings
479
-            );
480
-
481
-            // add answer columns
482
-            $reg_csv_array = AnswersCSV::addAnswerColumns($reg_row, $reg_csv_array, $question_labels);
483
-            // Include check-in data
484
-            if ($event_id && $DTT_ID) {
485
-                // get whether or not the user has checked in
486
-                $reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
487
-                    $reg_model->count_related(
488
-                        $reg_row['Registration.REG_ID'],
489
-                        'Checkin',
490
-                        [
491
-                            [
492
-                                'DTT_ID' => $DTT_ID
493
-                            ]
494
-                        ]
495
-                    );
496
-                $datetime     = $date_model->get_one_by_ID($DTT_ID);
497
-                $checkin_rows = EEM_Checkin::instance()->get_all(
498
-                    [
499
-                        [
500
-                            'REG_ID' => $reg_row['Registration.REG_ID'],
501
-                            'DTT_ID' => $datetime->get('DTT_ID'),
502
-                        ],
503
-                    ]
504
-                );
505
-                $checkins     = [];
506
-                foreach ($checkin_rows as $checkin_row) {
507
-                    /** @var EE_Checkin $checkin_row */
508
-                    $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
509
-                    if ($checkin_value) {
510
-                        $checkins[] = $checkin_value;
511
-                    }
512
-                }
513
-                $datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
514
-                $reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
515
-            } elseif ($event_id) {
516
-                // get whether or not the user has checked in
517
-                $reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
518
-                    $reg_model->count_related(
519
-                        $reg_row['Registration.REG_ID'],
520
-                        'Checkin'
521
-                    );
522
-
523
-                $datetimes = $date_model->get_all(
524
-                    [
525
-                        [
526
-                            'Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID'],
527
-                        ],
528
-                        'order_by'                 => ['DTT_EVT_start' => 'ASC'],
529
-                        'default_where_conditions' => 'none',
530
-                    ]
531
-                );
532
-                foreach ($datetimes as $datetime) {
533
-                    if (! $datetime instanceof EE_Datetime) {
534
-                        continue;
535
-                    }
536
-
537
-                    /** @var EE_Checkin $checkin_row */
538
-                    $checkin_row = EEM_Checkin::instance()->get_one(
539
-                        [
540
-                            [
541
-                                'REG_ID' => $reg_row['Registration.REG_ID'],
542
-                                'DTT_ID' => $datetime->get('DTT_ID'),
543
-                            ],
544
-                            'limit'    => 1,
545
-                            'order_by' => [
546
-                                'CHK_ID' => 'DESC'
547
-                            ]
548
-                        ]
549
-                    );
550
-
551
-                    $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
552
-                    $datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
553
-
554
-                    $reg_csv_array[ $datetime_name ] = $checkin_value;
555
-                }
556
-            }
557
-            /**
558
-             * Filter to change the contents of each row of the registrations report CSV file.
559
-             * This can be used to add or remote columns from the CSV file, or change their values.
560
-             * Note when using: all rows in the CSV should have the same columns.
561
-             *
562
-             * @param array $reg_csv_array keys are the column names, values are their cell values
563
-             * @param array $reg_row       one entry from EEM_Registration::get_all_wpdb_results()
564
-             */
565
-            $registrations_csv_ready_array[] = apply_filters(
566
-                'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
567
-                $reg_csv_array,
568
-                $reg_row
569
-            );
570
-        }
571
-        // if we couldn't export anything, we want to at least show the column headers
572
-        if (empty($registrations_csv_ready_array)) {
573
-            $reg_csv_array               = [];
574
-            $model_and_fields_to_include = [
575
-                'Registration' => $reg_fields_to_include,
576
-                'Attendee'     => $att_fields_to_include,
577
-            ];
578
-            foreach ($model_and_fields_to_include as $model_name => $field_list) {
579
-                $model = EE_Registry::instance()->load_model($model_name);
580
-                foreach ($field_list as $field_name) {
581
-                    $field                                                          =
582
-                        $model->field_settings_for($field_name);
583
-                    $reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
584
-                }
585
-            }
586
-            $registrations_csv_ready_array[] = $reg_csv_array;
587
-        }
588
-        return $registrations_csv_ready_array;
589
-    }
590
-
591
-
592
-    /**
593
-     * Counts total unit to process
594
-     *
595
-     * @param array $query_params
596
-     * @return int
597
-     * @throws EE_Error
598
-     * @throws ReflectionException
599
-     */
600
-    public function count_units_to_process(array $query_params): int
601
-    {
602
-        return EEM_Registration::instance()->count(
603
-            array_diff_key(
604
-                $query_params,
605
-                array_flip(
606
-                    ['limit']
607
-                )
608
-            )
609
-        );
610
-    }
611
-
612
-
613
-    /**
614
-     * Performs any clean-up logic when we know the job is completed.
615
-     * In this case, we delete the temporary file
616
-     *
617
-     * @param JobParameters $job_parameters
618
-     * @return JobStepResponse
619
-     */
620
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse
621
-    {
622
-        $this->updateText(esc_html__('File Generation complete and downloaded', 'event_espresso'));
623
-
624
-        $this->_file_helper->delete(
625
-            EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
626
-            true,
627
-            'd'
628
-        );
629
-        $this->updateText(esc_html__('Cleaned up temporary file', 'event_espresso'));
630
-        $this->updateText(
631
-            $this->infoWrapper(
632
-                sprintf(
633
-                    esc_html__(
634
-                        'If not automatically redirected in %1$s seconds, click here to return to the %2$sRegistrations List Table%3$s',
635
-                        'event_espresso'
636
-                    ),
637
-                    '<span id="ee-redirect-timer">10</span>',
638
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
639
-                    '</a>'
640
-                )
641
-            )
642
-        );
643
-        return new JobStepResponse($job_parameters, $this->feedback);
644
-    }
45
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
46
+	// phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
47
+	/**
48
+	 * Performs any necessary setup for starting the job. This is also a good
49
+	 * place to setup the $job_arguments which will be used for subsequent HTTP requests
50
+	 * when continue_job will be called
51
+	 *
52
+	 * @param JobParameters $job_parameters
53
+	 * @return JobStepResponse
54
+	 * @throws BatchRequestException
55
+	 * @throws EE_Error
56
+	 * @throws ReflectionException
57
+	 */
58
+	public function create_job(JobParameters $job_parameters): JobStepResponse
59
+	{
60
+		$event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
61
+		$DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
62
+		if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
63
+			throw new BatchRequestException(
64
+				esc_html__('You do not have permission to view registrations', 'event_espresso')
65
+			);
66
+		}
67
+		$filepath = $this->create_file_from_job_with_name(
68
+			$job_parameters->job_id(),
69
+			$this->get_filename()
70
+		);
71
+		$job_parameters->add_extra_data('filepath', $filepath);
72
+
73
+		if ($job_parameters->request_datum('use_filters', false)) {
74
+			$query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
75
+		} else {
76
+			$query_params = [
77
+				[
78
+					'OR'                 => [
79
+						// don't include registrations from failed or abandoned transactions...
80
+						'Transaction.STS_ID' => [
81
+							'NOT IN',
82
+							[
83
+								EEM_Transaction::failed_status_code,
84
+								EEM_Transaction::abandoned_status_code,
85
+							],
86
+						],
87
+						// unless the registration is approved,
88
+						// in which case include it regardless of transaction status
89
+						'STS_ID'             => EEM_Registration::status_id_approved,
90
+					],
91
+					'Ticket.TKT_deleted' => ['IN', [true, false]],
92
+				],
93
+				'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
94
+				'force_join' => ['Transaction', 'Ticket', 'Attendee'],
95
+				'caps'       => EEM_Base::caps_read_admin,
96
+			];
97
+			if ($event_id) {
98
+				$query_params[0]['EVT_ID'] = $event_id;
99
+			} else {
100
+				$query_params['force_join'][] = 'Event';
101
+			}
102
+		}
103
+
104
+		if (! isset($query_params['force_join'])) {
105
+			$query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
106
+		}
107
+
108
+		$return_url_args = [];
109
+		parse_str(
110
+			parse_url(
111
+				$job_parameters->request_datum('return_url', ''),
112
+				PHP_URL_QUERY
113
+			),
114
+			$return_url_args
115
+		);
116
+
117
+		if (
118
+			isset($return_url_args['orderby'], $return_url_args['order'])
119
+			&& $return_url_args['orderby'] === 'ATT_lname'
120
+		) {
121
+			$query_params['order_by'] = [
122
+				'Attendee.ATT_lname' => $return_url_args['order'],
123
+				'Attendee.ATT_fname' => $return_url_args['order'],
124
+				'REG_ID' => $return_url_args['order']
125
+			];
126
+		}
127
+
128
+		$query_params = apply_filters(
129
+			'FHEE__EE_Export__report_registration_for_event',
130
+			$query_params,
131
+			$event_id
132
+		);
133
+
134
+		$job_parameters->add_extra_data('query_params', $query_params);
135
+		$question_labels = $this->_get_question_labels($query_params);
136
+		$job_parameters->add_extra_data('question_labels', $question_labels);
137
+		$job_parameters->set_job_size($this->count_units_to_process($query_params));
138
+		// we need to set the header columns
139
+		// but to do that we need to process one row so that we can extract ALL of the column headers
140
+		$csv_data_for_row = $this->get_csv_data_for(
141
+			$event_id,
142
+			0,
143
+			1,
144
+			$question_labels,
145
+			$query_params,
146
+			$DTT_ID
147
+		);
148
+		// but we don't want to write any actual data yet...
149
+		// so let's blank out all of the values for that first row
150
+		array_walk(
151
+			$csv_data_for_row[0],
152
+			function (&$value) {
153
+				$value = null;
154
+			}
155
+		);
156
+
157
+		EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row, true, true);
158
+		$this->updateTextHeader(
159
+			esc_html__('Registrations report started successfully...', 'event_espresso')
160
+		);
161
+		return new JobStepResponse($job_parameters, $this->feedback);
162
+	}
163
+
164
+
165
+	/**
166
+	 * Gets the filename
167
+	 *
168
+	 * @return string
169
+	 */
170
+	protected function get_filename(): string
171
+	{
172
+		return apply_filters(
173
+			'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__get_filename',
174
+			sprintf(
175
+				'event-espresso-registrations-%s.csv',
176
+				str_replace([':', ' '], '-', current_time('mysql'))
177
+			)
178
+		);
179
+	}
180
+
181
+
182
+	/**
183
+	 * Gets the questions which are to be used for this report,
184
+	 * so they can be remembered for later
185
+	 *
186
+	 * @param array $registration_query_params
187
+	 * @return array question admin labels to be used for this report
188
+	 * @throws EE_Error
189
+	 * @throws ReflectionException
190
+	 */
191
+	protected function _get_question_labels(array $registration_query_params): array
192
+	{
193
+		$where                 = $registration_query_params[0] ?? null;
194
+		$question_query_params = [];
195
+		if ($where !== null) {
196
+			$question_query_params = [
197
+				$this->_change_registration_where_params_to_question_where_params($registration_query_params[0]),
198
+			];
199
+		}
200
+		// Make sure it's not a system question
201
+		$question_query_params[0]['OR*not-system-questions'] = [
202
+			'QST_system'      => '',
203
+			'QST_system*null' => ['IS_NULL']
204
+		];
205
+		if (
206
+			apply_filters(
207
+				'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport___get_question_labels__only_include_answered_questions',
208
+				false,
209
+				$registration_query_params
210
+			)
211
+		) {
212
+			$question_query_params[0]['Answer.ANS_ID'] = ['IS_NOT_NULL'];
213
+		}
214
+		$question_query_params['order_by'] = ['QST_admin_label' => 'ASC'];
215
+		$question_query_params['group_by'] = ['QST_ID'];
216
+		return array_unique(EEM_Question::instance()->get_col($question_query_params, 'QST_admin_label'));
217
+	}
218
+
219
+
220
+	/**
221
+	 * Takes where params meant for registrations and changes them to work for questions
222
+	 *
223
+	 * @param array $reg_where_params
224
+	 * @return array
225
+	 * @throws EE_Error
226
+	 * @throws ReflectionException
227
+	 */
228
+	protected function _change_registration_where_params_to_question_where_params(array $reg_where_params): array
229
+	{
230
+		$question_where_params = [];
231
+		foreach ($reg_where_params as $key => $val) {
232
+			if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
233
+				$question_where_params[ $key ] =
234
+					$this->_change_registration_where_params_to_question_where_params($val);
235
+			} else {
236
+				// it's a normal where condition
237
+				$question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
238
+			}
239
+		}
240
+		return $question_where_params;
241
+	}
242
+
243
+
244
+	/**
245
+	 * Performs another step of the job
246
+	 *
247
+	 * @param JobParameters $job_parameters
248
+	 * @param int           $batch_size
249
+	 * @return JobStepResponse
250
+	 * @throws EE_Error
251
+	 * @throws ReflectionException
252
+	 */
253
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
254
+	{
255
+		if ($job_parameters->units_processed() < $job_parameters->job_size()) {
256
+			$csv_data = $this->get_csv_data_for(
257
+				(int) $job_parameters->request_datum('EVT_ID', '0'),
258
+				$job_parameters->units_processed(),
259
+				$batch_size,
260
+				$job_parameters->extra_datum('question_labels'),
261
+				$job_parameters->extra_datum('query_params'),
262
+				(int) $job_parameters->request_datum('DTT_ID', '0')
263
+			);
264
+			EEH_Export::write_data_array_to_csv(
265
+				$job_parameters->extra_datum('filepath'),
266
+				$csv_data,
267
+				false
268
+			);
269
+			$units_processed = count($csv_data);
270
+			if ($units_processed) {
271
+				$job_parameters->mark_processed($units_processed);
272
+				$this->updateText(
273
+					sprintf(
274
+						esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
275
+						$units_processed
276
+					)
277
+				);
278
+			}
279
+		}
280
+		$extra_response_data = ['file_url' => ''];
281
+		if ($job_parameters->units_processed() >= $job_parameters->job_size()) {
282
+			$job_parameters->set_status(JobParameters::status_complete);
283
+			$extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
284
+			$this->displayJobFinalResults($job_parameters);
285
+		} else {
286
+			$job_parameters->set_status(JobParameters::status_continue);
287
+		}
288
+		return new JobStepResponse($job_parameters, $this->feedback, $extra_response_data);
289
+	}
290
+
291
+
292
+	/**
293
+	 * Gets the csv data for a batch of registrations
294
+	 *
295
+	 * @param int|null $event_id
296
+	 * @param int      $offset
297
+	 * @param int      $limit
298
+	 * @param array    $question_labels the IDs for all the questions which were answered by someone in this selection
299
+	 * @param array    $query_params    for using where querying the model
300
+	 * @param int      $DTT_ID
301
+	 * @return array top-level keys are numeric, next-level keys are column headers
302
+	 * @throws EE_Error
303
+	 * @throws ReflectionException
304
+	 */
305
+	public function get_csv_data_for(
306
+		?int $event_id,
307
+		int $offset,
308
+		int $limit,
309
+		array $question_labels,
310
+		array $query_params,
311
+		int $DTT_ID = 0
312
+	): array {
313
+		$reg_fields_to_include = [
314
+			'TXN_ID',
315
+			'ATT_ID',
316
+			'REG_ID',
317
+			'REG_date',
318
+			'REG_code',
319
+			'REG_count',
320
+			'REG_final_price',
321
+		];
322
+		$att_fields_to_include = [
323
+			'ATT_fname',
324
+			'ATT_lname',
325
+			'ATT_email',
326
+			'ATT_address',
327
+			'ATT_address2',
328
+			'ATT_city',
329
+			'STA_ID',
330
+			'CNT_ISO',
331
+			'ATT_zip',
332
+			'ATT_phone',
333
+		];
334
+
335
+		// get models
336
+		$event_model   = EEM_Event::instance();
337
+		$date_model    = EEM_Datetime::instance();
338
+		$ticket_model  = EEM_Ticket::instance();
339
+		$txn_model     = EEM_Transaction::instance();
340
+		$reg_model     = EEM_Registration::instance();
341
+		$pay_model     = EEM_Payment::instance();
342
+		$status_model  = EEM_Status::instance();
343
+
344
+		$registrations_csv_ready_array = [];
345
+		$query_params['limit']         = [$offset, $limit];
346
+		$registration_rows             = $reg_model->get_all_wpdb_results($query_params);
347
+
348
+		foreach ($registration_rows as $reg_row) {
349
+			if (! is_array($reg_row)) {
350
+				continue;
351
+			}
352
+			$reg_csv_array = [];
353
+			// registration ID
354
+			$reg_id_field = $reg_model->field_settings_for('REG_ID');
355
+			$reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
356
+				EEH_Export::prepare_value_from_db_for_display(
357
+					$reg_model,
358
+					'REG_ID',
359
+					$reg_row[ $reg_id_field->get_qualified_column() ]
360
+				);
361
+			// ALL registrations, or is list filtered to just one?
362
+			if (! $event_id) {
363
+				// ALL registrations, so get each event's name and ID
364
+				$reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
365
+					/* translators: 1: event name, 2: event ID */
366
+					esc_html__('%1$s (%2$s)', 'event_espresso'),
367
+					EEH_Export::prepare_value_from_db_for_display(
368
+						$event_model,
369
+						'EVT_name',
370
+						$reg_row['Event_CPT.post_title']
371
+					),
372
+					$reg_row['Event_CPT.ID']
373
+				);
374
+			}
375
+			// add attendee columns
376
+			$reg_csv_array = AttendeeCSV::addAttendeeColumns($att_fields_to_include, $reg_row, $reg_csv_array);
377
+			// add registration columns
378
+			$reg_csv_array = RegistrationCSV::addRegistrationColumns($reg_fields_to_include, $reg_row, $reg_csv_array);
379
+			// get pretty status
380
+			$stati = $status_model->localized_status(
381
+				[
382
+					$reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
383
+					$reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
384
+				],
385
+				false,
386
+				'sentence'
387
+			);
388
+			$is_primary_reg = $reg_row['Registration.REG_count'] == '1';
389
+
390
+			$reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
391
+				$stati[ $reg_row['Registration.STS_ID'] ];
392
+			// get pretty transaction status
393
+			$reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
394
+				$stati[ $reg_row['TransactionTable.STS_ID'] ];
395
+			$reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
396
+				? EEH_Export::prepare_value_from_db_for_display(
397
+					$txn_model,
398
+					'TXN_total',
399
+					$reg_row['TransactionTable.TXN_total'],
400
+					'localized_float'
401
+				)
402
+				: '0.00';
403
+
404
+			$reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
405
+				? EEH_Export::prepare_value_from_db_for_display(
406
+					$txn_model,
407
+					'TXN_paid',
408
+					$reg_row['TransactionTable.TXN_paid'],
409
+					'localized_float'
410
+				)
411
+				: '0.00';
412
+
413
+			$payment_methods                                                                  = [];
414
+			$gateway_txn_ids_etc                                                              = [];
415
+			$payment_times                                                                    = [];
416
+			if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
417
+				$payments_info = $pay_model->get_all_wpdb_results(
418
+					[
419
+						[
420
+							'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
421
+							'STS_ID' => EEM_Payment::status_id_approved,
422
+						],
423
+						'force_join' => ['Payment_Method'],
424
+					],
425
+					ARRAY_A,
426
+					'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
427
+				);
428
+				[$payment_methods, $gateway_txn_ids_etc, $payment_times] = PaymentsInfoCSV::extractPaymentInfo(
429
+					$payments_info
430
+				);
431
+			}
432
+
433
+			$reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
434
+				',',
435
+				$payment_times
436
+			);
437
+
438
+			$reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
439
+				',',
440
+				$payment_methods
441
+			);
442
+
443
+			$reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
444
+				',',
445
+				$gateway_txn_ids_etc
446
+			);
447
+
448
+			$ticket_name      = esc_html__('Unknown', 'event_espresso');
449
+			$datetime_strings = [esc_html__('Unknown', 'event_espresso')];
450
+			if ($reg_row['Ticket.TKT_ID']) {
451
+				$ticket_name       = EEH_Export::prepare_value_from_db_for_display(
452
+					$ticket_model,
453
+					'TKT_name',
454
+					$reg_row['Ticket.TKT_name']
455
+				);
456
+				$datetime_strings = [];
457
+				$datetimes        = $date_model->get_all_wpdb_results(
458
+					[
459
+						['Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']],
460
+						'order_by'                 => ['DTT_EVT_start' => 'ASC'],
461
+						'default_where_conditions' => 'none',
462
+					]
463
+				);
464
+				foreach ($datetimes as $datetime) {
465
+					$datetime_strings[] = EEH_Export::prepare_value_from_db_for_display(
466
+						$date_model,
467
+						'DTT_EVT_start',
468
+						$datetime['Datetime.DTT_EVT_start']
469
+					);
470
+				}
471
+			}
472
+
473
+			$reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
474
+
475
+
476
+			$reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
477
+				', ',
478
+				$datetime_strings
479
+			);
480
+
481
+			// add answer columns
482
+			$reg_csv_array = AnswersCSV::addAnswerColumns($reg_row, $reg_csv_array, $question_labels);
483
+			// Include check-in data
484
+			if ($event_id && $DTT_ID) {
485
+				// get whether or not the user has checked in
486
+				$reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
487
+					$reg_model->count_related(
488
+						$reg_row['Registration.REG_ID'],
489
+						'Checkin',
490
+						[
491
+							[
492
+								'DTT_ID' => $DTT_ID
493
+							]
494
+						]
495
+					);
496
+				$datetime     = $date_model->get_one_by_ID($DTT_ID);
497
+				$checkin_rows = EEM_Checkin::instance()->get_all(
498
+					[
499
+						[
500
+							'REG_ID' => $reg_row['Registration.REG_ID'],
501
+							'DTT_ID' => $datetime->get('DTT_ID'),
502
+						],
503
+					]
504
+				);
505
+				$checkins     = [];
506
+				foreach ($checkin_rows as $checkin_row) {
507
+					/** @var EE_Checkin $checkin_row */
508
+					$checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
509
+					if ($checkin_value) {
510
+						$checkins[] = $checkin_value;
511
+					}
512
+				}
513
+				$datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
514
+				$reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
515
+			} elseif ($event_id) {
516
+				// get whether or not the user has checked in
517
+				$reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
518
+					$reg_model->count_related(
519
+						$reg_row['Registration.REG_ID'],
520
+						'Checkin'
521
+					);
522
+
523
+				$datetimes = $date_model->get_all(
524
+					[
525
+						[
526
+							'Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID'],
527
+						],
528
+						'order_by'                 => ['DTT_EVT_start' => 'ASC'],
529
+						'default_where_conditions' => 'none',
530
+					]
531
+				);
532
+				foreach ($datetimes as $datetime) {
533
+					if (! $datetime instanceof EE_Datetime) {
534
+						continue;
535
+					}
536
+
537
+					/** @var EE_Checkin $checkin_row */
538
+					$checkin_row = EEM_Checkin::instance()->get_one(
539
+						[
540
+							[
541
+								'REG_ID' => $reg_row['Registration.REG_ID'],
542
+								'DTT_ID' => $datetime->get('DTT_ID'),
543
+							],
544
+							'limit'    => 1,
545
+							'order_by' => [
546
+								'CHK_ID' => 'DESC'
547
+							]
548
+						]
549
+					);
550
+
551
+					$checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
552
+					$datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
553
+
554
+					$reg_csv_array[ $datetime_name ] = $checkin_value;
555
+				}
556
+			}
557
+			/**
558
+			 * Filter to change the contents of each row of the registrations report CSV file.
559
+			 * This can be used to add or remote columns from the CSV file, or change their values.
560
+			 * Note when using: all rows in the CSV should have the same columns.
561
+			 *
562
+			 * @param array $reg_csv_array keys are the column names, values are their cell values
563
+			 * @param array $reg_row       one entry from EEM_Registration::get_all_wpdb_results()
564
+			 */
565
+			$registrations_csv_ready_array[] = apply_filters(
566
+				'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
567
+				$reg_csv_array,
568
+				$reg_row
569
+			);
570
+		}
571
+		// if we couldn't export anything, we want to at least show the column headers
572
+		if (empty($registrations_csv_ready_array)) {
573
+			$reg_csv_array               = [];
574
+			$model_and_fields_to_include = [
575
+				'Registration' => $reg_fields_to_include,
576
+				'Attendee'     => $att_fields_to_include,
577
+			];
578
+			foreach ($model_and_fields_to_include as $model_name => $field_list) {
579
+				$model = EE_Registry::instance()->load_model($model_name);
580
+				foreach ($field_list as $field_name) {
581
+					$field                                                          =
582
+						$model->field_settings_for($field_name);
583
+					$reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
584
+				}
585
+			}
586
+			$registrations_csv_ready_array[] = $reg_csv_array;
587
+		}
588
+		return $registrations_csv_ready_array;
589
+	}
590
+
591
+
592
+	/**
593
+	 * Counts total unit to process
594
+	 *
595
+	 * @param array $query_params
596
+	 * @return int
597
+	 * @throws EE_Error
598
+	 * @throws ReflectionException
599
+	 */
600
+	public function count_units_to_process(array $query_params): int
601
+	{
602
+		return EEM_Registration::instance()->count(
603
+			array_diff_key(
604
+				$query_params,
605
+				array_flip(
606
+					['limit']
607
+				)
608
+			)
609
+		);
610
+	}
611
+
612
+
613
+	/**
614
+	 * Performs any clean-up logic when we know the job is completed.
615
+	 * In this case, we delete the temporary file
616
+	 *
617
+	 * @param JobParameters $job_parameters
618
+	 * @return JobStepResponse
619
+	 */
620
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse
621
+	{
622
+		$this->updateText(esc_html__('File Generation complete and downloaded', 'event_espresso'));
623
+
624
+		$this->_file_helper->delete(
625
+			EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
626
+			true,
627
+			'd'
628
+		);
629
+		$this->updateText(esc_html__('Cleaned up temporary file', 'event_espresso'));
630
+		$this->updateText(
631
+			$this->infoWrapper(
632
+				sprintf(
633
+					esc_html__(
634
+						'If not automatically redirected in %1$s seconds, click here to return to the %2$sRegistrations List Table%3$s',
635
+						'event_espresso'
636
+					),
637
+					'<span id="ee-redirect-timer">10</span>',
638
+					'<a href="' . $job_parameters->request_datum('return_url') . '">',
639
+					'</a>'
640
+				)
641
+			)
642
+		);
643
+		return new JobStepResponse($job_parameters, $this->feedback);
644
+	}
645 645
 }
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/ExecuteBatchDeletion.php 1 patch
Indentation   +164 added lines, -164 removed lines patch added patch discarded remove patch
@@ -26,180 +26,180 @@
 block discarded – undo
26 26
  */
27 27
 class ExecuteBatchDeletion extends JobHandler
28 28
 {
29
-    protected NodeGroupDao $model_obj_node_group_persister;
29
+	protected NodeGroupDao $model_obj_node_group_persister;
30 30
 
31 31
 
32
-    /**
33
-     * @param NodeGroupDao $model_obj_node_group_persister
34
-     */
35
-    public function __construct(NodeGroupDao $model_obj_node_group_persister)
36
-    {
37
-        $this->model_obj_node_group_persister = $model_obj_node_group_persister;
38
-    }
32
+	/**
33
+	 * @param NodeGroupDao $model_obj_node_group_persister
34
+	 */
35
+	public function __construct(NodeGroupDao $model_obj_node_group_persister)
36
+	{
37
+		$this->model_obj_node_group_persister = $model_obj_node_group_persister;
38
+	}
39 39
 
40 40
 
41
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
41
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
42 42
 
43 43
 
44
-    /**
45
-     *
46
-     * @param JobParameters $job_parameters
47
-     * @return JobStepResponse
48
-     * @throws EE_Error
49
-     * @throws ReflectionException
50
-     * @throws Exception
51
-     */
52
-    public function create_job(JobParameters $job_parameters): JobStepResponse
53
-    {
54
-        $deletion_job_code = $job_parameters->request_datum('deletion_job_code', null);
55
-        $roots             = $this->model_obj_node_group_persister->getModelObjNodesInGroup($deletion_job_code);
56
-        if ($roots === null) {
57
-            throw new UnexpectedEntityException(
58
-                $roots,
59
-                'array',
60
-                esc_html__(
61
-                    'The job seems to be stale. Please press the back button in your browser twice.',
62
-                    'event_espresso'
63
-                )
64
-            );
65
-        }
66
-        $models_and_ids_to_delete = [];
67
-        foreach ($roots as $root) {
68
-            if (! $root instanceof ModelObjNode) {
69
-                throw new UnexpectedEntityException($root, 'ModelObjNode');
70
-            }
71
-            $models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
72
-        }
73
-        $job_parameters->set_extra_data(
74
-            [
75
-                'models_and_ids_to_delete' => $models_and_ids_to_delete,
76
-            ]
77
-        );
78
-        // Find the job's actual size.
79
-        $job_size = 0;
80
-        foreach ($models_and_ids_to_delete as $ids) {
81
-            $job_size += count($ids);
82
-        }
83
-        $job_parameters->set_job_size($job_size);
84
-        $this->updateTextHeader(esc_html__('Beginning to delete items...', 'event_espresso'));
85
-        return new JobStepResponse($job_parameters, $this->feedback);
86
-    }
44
+	/**
45
+	 *
46
+	 * @param JobParameters $job_parameters
47
+	 * @return JobStepResponse
48
+	 * @throws EE_Error
49
+	 * @throws ReflectionException
50
+	 * @throws Exception
51
+	 */
52
+	public function create_job(JobParameters $job_parameters): JobStepResponse
53
+	{
54
+		$deletion_job_code = $job_parameters->request_datum('deletion_job_code', null);
55
+		$roots             = $this->model_obj_node_group_persister->getModelObjNodesInGroup($deletion_job_code);
56
+		if ($roots === null) {
57
+			throw new UnexpectedEntityException(
58
+				$roots,
59
+				'array',
60
+				esc_html__(
61
+					'The job seems to be stale. Please press the back button in your browser twice.',
62
+					'event_espresso'
63
+				)
64
+			);
65
+		}
66
+		$models_and_ids_to_delete = [];
67
+		foreach ($roots as $root) {
68
+			if (! $root instanceof ModelObjNode) {
69
+				throw new UnexpectedEntityException($root, 'ModelObjNode');
70
+			}
71
+			$models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
72
+		}
73
+		$job_parameters->set_extra_data(
74
+			[
75
+				'models_and_ids_to_delete' => $models_and_ids_to_delete,
76
+			]
77
+		);
78
+		// Find the job's actual size.
79
+		$job_size = 0;
80
+		foreach ($models_and_ids_to_delete as $ids) {
81
+			$job_size += count($ids);
82
+		}
83
+		$job_parameters->set_job_size($job_size);
84
+		$this->updateTextHeader(esc_html__('Beginning to delete items...', 'event_espresso'));
85
+		return new JobStepResponse($job_parameters, $this->feedback);
86
+	}
87 87
 
88 88
 
89
-    /**
90
-     * Performs another step of the job
91
-     *
92
-     * @param JobParameters $job_parameters
93
-     * @param int           $batch_size
94
-     * @return JobStepResponse
95
-     * @throws EE_Error
96
-     * @throws ReflectionException
97
-     */
98
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
99
-    {
100
-        // We already have the items IDs. So deleting is really fast. Let's speed it up.
101
-        $batch_size               *= 10;
102
-        $units_processed          = 0;
103
-        $models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
104
-        // Build a new list of everything leftover after this request's of deletions.
105
-        $models_and_ids_remaining = [];
106
-        foreach ($models_and_ids_to_delete as $model_name => $ids_to_delete) {
107
-            // don't delete logs
108
-            if ($model_name === 'Change_Log') {
109
-                continue;
110
-            }
111
-            if ($units_processed < $batch_size) {
112
-                /** @var EEM_Base $model */
113
-                $model                    = EE_Registry::instance()->load_model($model_name);
114
-                $ids_to_delete_this_query = array_slice($ids_to_delete, 0, $batch_size - $units_processed, true);
115
-                if ($model->has_primary_key_field()) {
116
-                    $where_conditions = [
117
-                        $model->primary_key_name() => [
118
-                            'IN',
119
-                            $ids_to_delete_this_query,
120
-                        ],
121
-                    ];
122
-                } else {
123
-                    $where_conditions = [
124
-                        'OR' => [],
125
-                    ];
126
-                    foreach ($ids_to_delete_this_query as $index_primary_key_string) {
127
-                        $keys_n_values                                                =
128
-                            $model->parse_index_primary_key_string($index_primary_key_string);
129
-                        $where_conditions['OR'][ 'AND*' . $index_primary_key_string ] = $keys_n_values;
130
-                    }
131
-                }
132
-                // Deleting time!
133
-                // The model's deletion method reports every ROW deleted, and in the case of CPT models that will be
134
-                // two rows deleted for event CPT item. So don't rely on it for the count of items deleted.
135
-                $model->delete_permanently([ $where_conditions ], false);
136
-                $units_processed += count($ids_to_delete_this_query);
137
-                $remaining_ids   = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
138
-                // If there's any more from this model, we'll do them next time.
139
-                if (count($remaining_ids) > 0) {
140
-                    $models_and_ids_remaining[ $model_name ] = $remaining_ids;
141
-                }
142
-            } else {
143
-                $models_and_ids_remaining[ $model_name ] = $ids_to_delete;
144
-            }
145
-        }
146
-        $job_parameters->mark_processed($units_processed);
147
-        // All done deleting for this request. Is there anything to do next time?
148
-        if (empty($models_and_ids_remaining)) {
149
-            $job_parameters->set_status(JobParameters::status_complete);
150
-            return new JobStepResponse($job_parameters, $this->feedback);
151
-        }
152
-        $job_parameters->add_extra_data('models_and_ids_to_delete', $models_and_ids_remaining);
153
-        $this->displayJobStepResults(
154
-            $units_processed,
155
-            esc_html__('Deleted %d items.', 'event_espresso')
156
-        );
157
-        return new JobStepResponse($job_parameters, $this->feedback);
158
-    }
89
+	/**
90
+	 * Performs another step of the job
91
+	 *
92
+	 * @param JobParameters $job_parameters
93
+	 * @param int           $batch_size
94
+	 * @return JobStepResponse
95
+	 * @throws EE_Error
96
+	 * @throws ReflectionException
97
+	 */
98
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
99
+	{
100
+		// We already have the items IDs. So deleting is really fast. Let's speed it up.
101
+		$batch_size               *= 10;
102
+		$units_processed          = 0;
103
+		$models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
104
+		// Build a new list of everything leftover after this request's of deletions.
105
+		$models_and_ids_remaining = [];
106
+		foreach ($models_and_ids_to_delete as $model_name => $ids_to_delete) {
107
+			// don't delete logs
108
+			if ($model_name === 'Change_Log') {
109
+				continue;
110
+			}
111
+			if ($units_processed < $batch_size) {
112
+				/** @var EEM_Base $model */
113
+				$model                    = EE_Registry::instance()->load_model($model_name);
114
+				$ids_to_delete_this_query = array_slice($ids_to_delete, 0, $batch_size - $units_processed, true);
115
+				if ($model->has_primary_key_field()) {
116
+					$where_conditions = [
117
+						$model->primary_key_name() => [
118
+							'IN',
119
+							$ids_to_delete_this_query,
120
+						],
121
+					];
122
+				} else {
123
+					$where_conditions = [
124
+						'OR' => [],
125
+					];
126
+					foreach ($ids_to_delete_this_query as $index_primary_key_string) {
127
+						$keys_n_values                                                =
128
+							$model->parse_index_primary_key_string($index_primary_key_string);
129
+						$where_conditions['OR'][ 'AND*' . $index_primary_key_string ] = $keys_n_values;
130
+					}
131
+				}
132
+				// Deleting time!
133
+				// The model's deletion method reports every ROW deleted, and in the case of CPT models that will be
134
+				// two rows deleted for event CPT item. So don't rely on it for the count of items deleted.
135
+				$model->delete_permanently([ $where_conditions ], false);
136
+				$units_processed += count($ids_to_delete_this_query);
137
+				$remaining_ids   = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
138
+				// If there's any more from this model, we'll do them next time.
139
+				if (count($remaining_ids) > 0) {
140
+					$models_and_ids_remaining[ $model_name ] = $remaining_ids;
141
+				}
142
+			} else {
143
+				$models_and_ids_remaining[ $model_name ] = $ids_to_delete;
144
+			}
145
+		}
146
+		$job_parameters->mark_processed($units_processed);
147
+		// All done deleting for this request. Is there anything to do next time?
148
+		if (empty($models_and_ids_remaining)) {
149
+			$job_parameters->set_status(JobParameters::status_complete);
150
+			return new JobStepResponse($job_parameters, $this->feedback);
151
+		}
152
+		$job_parameters->add_extra_data('models_and_ids_to_delete', $models_and_ids_remaining);
153
+		$this->displayJobStepResults(
154
+			$units_processed,
155
+			esc_html__('Deleted %d items.', 'event_espresso')
156
+		);
157
+		return new JobStepResponse($job_parameters, $this->feedback);
158
+	}
159 159
 
160 160
 
161
-    /**
162
-     * Performs any clean-up logic when we know the job is completed
163
-     *
164
-     * @param JobParameters $job_parameters
165
-     * @return JobStepResponse
166
-     */
167
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse
168
-    {
169
-        $this->model_obj_node_group_persister->deleteModelObjNodesInGroup(
170
-            $job_parameters->request_datum('deletion_job_code')
171
-        );
172
-        // For backwards compatibility with how we used to delete events, make sure we still trigger the old action.
173
-        $models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
174
-        foreach ($models_and_ids_to_delete['Event'] as $event_id) {
175
-            // TrashLogger hooks into the following to create a log entry
176
-            // so we know when and who permanently deleted this event.
177
-            do_action(
178
-                'AHEE__Events_Admin_Page___permanently_delete_event__after_event_deleted',
179
-                $event_id,
180
-                'Event',
181
-                $job_parameters
182
-            );
183
-        }
184
-        $this->displayJobFinalResults(
185
-            $job_parameters,
186
-            esc_html__('All Done. Deleted a total of %d items.', 'event_espresso')
187
-        );
188
-        $this->updateText(
189
-            $this->infoWrapper(
190
-                sprintf(
191
-                    esc_html__(
192
-                        'If not automatically redirected in %1$s seconds, click here to return to the %2$sprevious admin screen%3$s',
193
-                        'event_espresso'
194
-                    ),
195
-                    '<span id="ee-redirect-timer">10</span>',
196
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
197
-                    '</a>'
198
-                )
199
-            )
200
-        );
201
-        return new JobStepResponse($job_parameters, $this->feedback);
202
-    }
161
+	/**
162
+	 * Performs any clean-up logic when we know the job is completed
163
+	 *
164
+	 * @param JobParameters $job_parameters
165
+	 * @return JobStepResponse
166
+	 */
167
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse
168
+	{
169
+		$this->model_obj_node_group_persister->deleteModelObjNodesInGroup(
170
+			$job_parameters->request_datum('deletion_job_code')
171
+		);
172
+		// For backwards compatibility with how we used to delete events, make sure we still trigger the old action.
173
+		$models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
174
+		foreach ($models_and_ids_to_delete['Event'] as $event_id) {
175
+			// TrashLogger hooks into the following to create a log entry
176
+			// so we know when and who permanently deleted this event.
177
+			do_action(
178
+				'AHEE__Events_Admin_Page___permanently_delete_event__after_event_deleted',
179
+				$event_id,
180
+				'Event',
181
+				$job_parameters
182
+			);
183
+		}
184
+		$this->displayJobFinalResults(
185
+			$job_parameters,
186
+			esc_html__('All Done. Deleted a total of %d items.', 'event_espresso')
187
+		);
188
+		$this->updateText(
189
+			$this->infoWrapper(
190
+				sprintf(
191
+					esc_html__(
192
+						'If not automatically redirected in %1$s seconds, click here to return to the %2$sprevious admin screen%3$s',
193
+						'event_espresso'
194
+					),
195
+					'<span id="ee-redirect-timer">10</span>',
196
+					'<a href="' . $job_parameters->request_datum('return_url') . '">',
197
+					'</a>'
198
+				)
199
+			)
200
+		);
201
+		return new JobStepResponse($job_parameters, $this->feedback);
202
+	}
203 203
 }
204 204
 // End of file EventDeletion.php
205 205
 // Location: EventEspresso\core\libraries\batch\JobHandlers/EventDeletion.php
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlerBaseClasses/JobHandlerInterface.php 1 patch
Indentation   +88 added lines, -88 removed lines patch added patch discarded remove patch
@@ -23,92 +23,92 @@
 block discarded – undo
23 23
  */
24 24
 interface JobHandlerInterface
25 25
 {
26
-    /**
27
-     * Performs any necessary setup for starting the job. This is also a good
28
-     * place to set up the $job_arguments which will be used for subsequent HTTP requests
29
-     * when continue_job will be called
30
-     *
31
-     * @param JobParameters $job_parameters
32
-     * @return JobStepResponse
33
-     * @throws BatchRequestException
34
-     */
35
-    public function create_job(JobParameters $job_parameters): JobStepResponse;
36
-
37
-
38
-    /**
39
-     * Performs another step of the job
40
-     *
41
-     * @param JobParameters $job_parameters
42
-     * @param int           $batch_size
43
-     * @return JobStepResponse
44
-     * @throws BatchRequestException
45
-     */
46
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse;
47
-
48
-
49
-    /**
50
-     * used to advance from one batch job to another
51
-     * primarily used for executing a job assessment phase where an accurate count of items to update can be made,
52
-     * followed by the actual update job.
53
-     *
54
-     * @param JobParameters $job_parameters
55
-     * @return JobStepResponse
56
-     * @throws BatchRequestException
57
-     */
58
-    public function advance_job(JobParameters $job_parameters): JobStepResponse;
59
-
60
-
61
-    /**
62
-     * Performs any clean-up logic when we know the job is completed
63
-     *
64
-     * @param JobParameters $job_parameters
65
-     * @return JobStepResponse
66
-     * @throws BatchRequestException
67
-     */
68
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse;
69
-
70
-
71
-    /**
72
-     * Performs any necessary setup for starting the job. This is also a good
73
-     * place to set up the $job_arguments which will be used for subsequent HTTP requests
74
-     * when continue_job will be called
75
-     *
76
-     * @param JobParameters $job_parameters
77
-     * @return JobStepResponse
78
-     * @throws BatchRequestException
79
-     */
80
-    public function createJob(JobParameters $job_parameters): JobStepResponse;
81
-
82
-
83
-    /**
84
-     * Performs another step of the job
85
-     *
86
-     * @param JobParameters $job_parameters
87
-     * @param int           $batch_size
88
-     * @return JobStepResponse
89
-     * @throws BatchRequestException
90
-     */
91
-    public function continueJob(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse;
92
-
93
-
94
-    /**
95
-     * used to advance from one batch job to another
96
-     * primarily used for executing a job assessment phase where an accurate count of items to update can be made,
97
-     * followed by the actual update job.
98
-     *
99
-     * @param JobParameters $job_parameters
100
-     * @return JobStepResponse
101
-     * @throws BatchRequestException
102
-     */
103
-    public function advanceJob(JobParameters $job_parameters): JobStepResponse;
104
-
105
-
106
-    /**
107
-     * Performs any clean-up logic when we know the job is completed
108
-     *
109
-     * @param JobParameters $job_parameters
110
-     * @return JobStepResponse
111
-     * @throws BatchRequestException
112
-     */
113
-    public function cleanupJob(JobParameters $job_parameters): JobStepResponse;
26
+	/**
27
+	 * Performs any necessary setup for starting the job. This is also a good
28
+	 * place to set up the $job_arguments which will be used for subsequent HTTP requests
29
+	 * when continue_job will be called
30
+	 *
31
+	 * @param JobParameters $job_parameters
32
+	 * @return JobStepResponse
33
+	 * @throws BatchRequestException
34
+	 */
35
+	public function create_job(JobParameters $job_parameters): JobStepResponse;
36
+
37
+
38
+	/**
39
+	 * Performs another step of the job
40
+	 *
41
+	 * @param JobParameters $job_parameters
42
+	 * @param int           $batch_size
43
+	 * @return JobStepResponse
44
+	 * @throws BatchRequestException
45
+	 */
46
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse;
47
+
48
+
49
+	/**
50
+	 * used to advance from one batch job to another
51
+	 * primarily used for executing a job assessment phase where an accurate count of items to update can be made,
52
+	 * followed by the actual update job.
53
+	 *
54
+	 * @param JobParameters $job_parameters
55
+	 * @return JobStepResponse
56
+	 * @throws BatchRequestException
57
+	 */
58
+	public function advance_job(JobParameters $job_parameters): JobStepResponse;
59
+
60
+
61
+	/**
62
+	 * Performs any clean-up logic when we know the job is completed
63
+	 *
64
+	 * @param JobParameters $job_parameters
65
+	 * @return JobStepResponse
66
+	 * @throws BatchRequestException
67
+	 */
68
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse;
69
+
70
+
71
+	/**
72
+	 * Performs any necessary setup for starting the job. This is also a good
73
+	 * place to set up the $job_arguments which will be used for subsequent HTTP requests
74
+	 * when continue_job will be called
75
+	 *
76
+	 * @param JobParameters $job_parameters
77
+	 * @return JobStepResponse
78
+	 * @throws BatchRequestException
79
+	 */
80
+	public function createJob(JobParameters $job_parameters): JobStepResponse;
81
+
82
+
83
+	/**
84
+	 * Performs another step of the job
85
+	 *
86
+	 * @param JobParameters $job_parameters
87
+	 * @param int           $batch_size
88
+	 * @return JobStepResponse
89
+	 * @throws BatchRequestException
90
+	 */
91
+	public function continueJob(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse;
92
+
93
+
94
+	/**
95
+	 * used to advance from one batch job to another
96
+	 * primarily used for executing a job assessment phase where an accurate count of items to update can be made,
97
+	 * followed by the actual update job.
98
+	 *
99
+	 * @param JobParameters $job_parameters
100
+	 * @return JobStepResponse
101
+	 * @throws BatchRequestException
102
+	 */
103
+	public function advanceJob(JobParameters $job_parameters): JobStepResponse;
104
+
105
+
106
+	/**
107
+	 * Performs any clean-up logic when we know the job is completed
108
+	 *
109
+	 * @param JobParameters $job_parameters
110
+	 * @return JobStepResponse
111
+	 * @throws BatchRequestException
112
+	 */
113
+	public function cleanupJob(JobParameters $job_parameters): JobStepResponse;
114 114
 }
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlerBaseClasses/JobHandler.php 1 patch
Indentation   +231 added lines, -231 removed lines patch added patch discarded remove patch
@@ -17,264 +17,264 @@
 block discarded – undo
17 17
  */
18 18
 abstract class JobHandler implements JobHandlerInterface
19 19
 {
20
-    protected array $feedback = [];
21
-
22
-    protected array $request_data = [];
23
-
24
-    /**
25
-     * Extra data to include as part of the response, literally gets set as JobStepResponse::$_extra_data
26
-     *
27
-     * @var array
28
-     */
29
-    protected array $response_data = [];
30
-
31
-
32
-    /**
33
-     * utilized in newer batch job implementations, but forwarding to existing methods for now.
34
-     * Performs any necessary setup for starting the job. This is also a good
35
-     * place to setup the $job_arguments which will be used for subsequent HTTP requests
36
-     * when continue_job will be called
37
-     *
38
-     * @param JobParameters $job_parameters
39
-     * @return JobStepResponse
40
-     * @throws BatchRequestException
41
-     * @since 5.0.0.p
42
-     */
43
-    public function createJob(JobParameters $job_parameters): JobStepResponse
44
-    {
45
-        return $this->create_job($job_parameters);
46
-    }
47
-
48
-
49
-    /**
50
-     * utilized in newer batch job implementations, but forwarding to existing methods for now.
51
-     * Performs another step of the job
52
-     *
53
-     * @param JobParameters $job_parameters
54
-     * @param int           $batch_size
55
-     * @return JobStepResponse
56
-     * @throws BatchRequestException
57
-     * @since 5.0.0.p
58
-     */
59
-    public function continueJob(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
60
-    {
61
-        return $this->continue_job($job_parameters, $batch_size);
62
-    }
63
-
64
-
65
-    /**
66
-     * utilized in newer batch job implementations, but forwarding to existing methods for now.
67
-     * used to advance from one batch job to another
68
-     * primarily used for executing a job assessment phase where an accurate count of items to update can be made,
69
-     * followed by the actual update job.
70
-     *
71
-     * @param JobParameters $job_parameters
72
-     * @return JobStepResponse
73
-     * @since 5.0.0.p
74
-     */
75
-    public function advanceJob(JobParameters $job_parameters): JobStepResponse
76
-    {
77
-        return $this->advance_job($job_parameters);
78
-    }
79
-
80
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
81
-    /**
82
-     * used to advance from one batch job to another
83
-     * primarily used for executing a job assessment phase where an accurate count of items to update can be made,
84
-     * followed by the actual update job.
85
-     * By default, this function won't do anything until overridden in a chile class.
86
-     *
87
-     * @param JobParameters $job_parameters
88
-     * @return JobStepResponse
89
-     * @since 5.0.0.p
90
-     */
91
-    public function advance_job(JobParameters $job_parameters): JobStepResponse
92
-    {
93
-        $job_parameters->set_status(JobParameters::status_continue);
94
-        return new JobStepResponse($job_parameters, $this->feedback);
95
-    }
96
-
97
-
98
-    /**
99
-     * utilized in newer batch job implementations, but forwarding to existing methods for now.
100
-     * Performs any clean-up logic when we know the job is completed
101
-     *
102
-     * @param JobParameters $job_parameters
103
-     * @return JobStepResponse
104
-     * @throws BatchRequestException
105
-     * @since 5.0.0.p
106
-     */
107
-    public function cleanupJob(JobParameters $job_parameters): JobStepResponse
108
-    {
109
-        return $this->cleanup_job($job_parameters);
110
-    }
111
-
112
-
113
-    /**
114
-     * @return array
115
-     */
116
-    public function requestData(): array
117
-    {
118
-        return $this->request_data;
119
-    }
120
-
121
-
122
-    /**
123
-     * @param string|int $key
124
-     * @return mixed
125
-     */
126
-    public function getRequestData($key)
127
-    {
128
-        return $this->request_data[ $key ] ?? null;
129
-    }
130
-
131
-
132
-    /**
133
-     * @param array $request_data
134
-     */
135
-    public function setRequestData(array $request_data): void
136
-    {
137
-        $this->request_data = $request_data;
138
-    }
139
-
140
-
141
-    /**
142
-     * @return array
143
-     */
144
-    public function responseData(): array
145
-    {
146
-        return $this->response_data;
147
-    }
148
-
149
-
150
-    /**
151
-     * @param array $response_data
152
-     */
153
-    public function addResponseData(array $response_data): void
154
-    {
155
-        $this->response_data += $response_data;
156
-    }
157
-
158
-
159
-
160
-    /**
161
-     * @param array $response_data
162
-     */
163
-    public function setResponseData(array $response_data): void
164
-    {
165
-        $this->response_data = $response_data;
166
-    }
167
-
168
-
169
-
170
-
171
-    protected function displayJobStepResults(int $processed, string $custom_message = '')
172
-    {
173
-        $this->feedback[] = '
20
+	protected array $feedback = [];
21
+
22
+	protected array $request_data = [];
23
+
24
+	/**
25
+	 * Extra data to include as part of the response, literally gets set as JobStepResponse::$_extra_data
26
+	 *
27
+	 * @var array
28
+	 */
29
+	protected array $response_data = [];
30
+
31
+
32
+	/**
33
+	 * utilized in newer batch job implementations, but forwarding to existing methods for now.
34
+	 * Performs any necessary setup for starting the job. This is also a good
35
+	 * place to setup the $job_arguments which will be used for subsequent HTTP requests
36
+	 * when continue_job will be called
37
+	 *
38
+	 * @param JobParameters $job_parameters
39
+	 * @return JobStepResponse
40
+	 * @throws BatchRequestException
41
+	 * @since 5.0.0.p
42
+	 */
43
+	public function createJob(JobParameters $job_parameters): JobStepResponse
44
+	{
45
+		return $this->create_job($job_parameters);
46
+	}
47
+
48
+
49
+	/**
50
+	 * utilized in newer batch job implementations, but forwarding to existing methods for now.
51
+	 * Performs another step of the job
52
+	 *
53
+	 * @param JobParameters $job_parameters
54
+	 * @param int           $batch_size
55
+	 * @return JobStepResponse
56
+	 * @throws BatchRequestException
57
+	 * @since 5.0.0.p
58
+	 */
59
+	public function continueJob(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
60
+	{
61
+		return $this->continue_job($job_parameters, $batch_size);
62
+	}
63
+
64
+
65
+	/**
66
+	 * utilized in newer batch job implementations, but forwarding to existing methods for now.
67
+	 * used to advance from one batch job to another
68
+	 * primarily used for executing a job assessment phase where an accurate count of items to update can be made,
69
+	 * followed by the actual update job.
70
+	 *
71
+	 * @param JobParameters $job_parameters
72
+	 * @return JobStepResponse
73
+	 * @since 5.0.0.p
74
+	 */
75
+	public function advanceJob(JobParameters $job_parameters): JobStepResponse
76
+	{
77
+		return $this->advance_job($job_parameters);
78
+	}
79
+
80
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
81
+	/**
82
+	 * used to advance from one batch job to another
83
+	 * primarily used for executing a job assessment phase where an accurate count of items to update can be made,
84
+	 * followed by the actual update job.
85
+	 * By default, this function won't do anything until overridden in a chile class.
86
+	 *
87
+	 * @param JobParameters $job_parameters
88
+	 * @return JobStepResponse
89
+	 * @since 5.0.0.p
90
+	 */
91
+	public function advance_job(JobParameters $job_parameters): JobStepResponse
92
+	{
93
+		$job_parameters->set_status(JobParameters::status_continue);
94
+		return new JobStepResponse($job_parameters, $this->feedback);
95
+	}
96
+
97
+
98
+	/**
99
+	 * utilized in newer batch job implementations, but forwarding to existing methods for now.
100
+	 * Performs any clean-up logic when we know the job is completed
101
+	 *
102
+	 * @param JobParameters $job_parameters
103
+	 * @return JobStepResponse
104
+	 * @throws BatchRequestException
105
+	 * @since 5.0.0.p
106
+	 */
107
+	public function cleanupJob(JobParameters $job_parameters): JobStepResponse
108
+	{
109
+		return $this->cleanup_job($job_parameters);
110
+	}
111
+
112
+
113
+	/**
114
+	 * @return array
115
+	 */
116
+	public function requestData(): array
117
+	{
118
+		return $this->request_data;
119
+	}
120
+
121
+
122
+	/**
123
+	 * @param string|int $key
124
+	 * @return mixed
125
+	 */
126
+	public function getRequestData($key)
127
+	{
128
+		return $this->request_data[ $key ] ?? null;
129
+	}
130
+
131
+
132
+	/**
133
+	 * @param array $request_data
134
+	 */
135
+	public function setRequestData(array $request_data): void
136
+	{
137
+		$this->request_data = $request_data;
138
+	}
139
+
140
+
141
+	/**
142
+	 * @return array
143
+	 */
144
+	public function responseData(): array
145
+	{
146
+		return $this->response_data;
147
+	}
148
+
149
+
150
+	/**
151
+	 * @param array $response_data
152
+	 */
153
+	public function addResponseData(array $response_data): void
154
+	{
155
+		$this->response_data += $response_data;
156
+	}
157
+
158
+
159
+
160
+	/**
161
+	 * @param array $response_data
162
+	 */
163
+	public function setResponseData(array $response_data): void
164
+	{
165
+		$this->response_data = $response_data;
166
+	}
167
+
168
+
169
+
170
+
171
+	protected function displayJobStepResults(int $processed, string $custom_message = '')
172
+	{
173
+		$this->feedback[] = '
174 174
 			<div class="ee-batch-job-step-results">
175 175
 			' . $this->infoWrapper(
176
-            sprintf(
177
-                $custom_message !== ''
178
-                        ? $custom_message
179
-                        : esc_html__('processed this batch: %d', 'event_espresso'),
180
-                $processed
181
-            )
182
-        ) . '
176
+			sprintf(
177
+				$custom_message !== ''
178
+						? $custom_message
179
+						: esc_html__('processed this batch: %d', 'event_espresso'),
180
+				$processed
181
+			)
182
+		) . '
183 183
 			</div>';
184
-    }
184
+	}
185 185
 
186 186
 
187
-    protected function displayJobFinalResults(JobParameters $job_parameters, string $custom_message = '')
188
-    {
189
-        if ($job_parameters->status() === JobParameters::status_complete) {
190
-            $this->feedback[] = '
187
+	protected function displayJobFinalResults(JobParameters $job_parameters, string $custom_message = '')
188
+	{
189
+		if ($job_parameters->status() === JobParameters::status_complete) {
190
+			$this->feedback[] = '
191 191
 			<div class="ee-batch-job-final-results">
192 192
 				' . $this->okWrapper(
193
-                sprintf(
194
-                    $custom_message !== ''
195
-                            ? $custom_message
196
-                            : esc_html__('total units processed: %d', 'event_espresso'),
197
-                    $job_parameters->units_processed()
198
-                )
199
-            ) . '
193
+				sprintf(
194
+					$custom_message !== ''
195
+							? $custom_message
196
+							: esc_html__('total units processed: %d', 'event_espresso'),
197
+					$job_parameters->units_processed()
198
+				)
199
+			) . '
200 200
 				' . $this->jobStatusNotice($job_parameters) . '
201 201
 			</div>';
202
-        }
203
-    }
202
+		}
203
+	}
204 204
 
205 205
 
206
-    protected function jobStatusNotice(JobParameters $job_parameters): string
207
-    {
208
-        switch ($job_parameters->status()) {
209
-            case JobParameters::status_cleaned_up:
210
-            case JobParameters::status_continue:
211
-            case JobParameters::status_pause:
212
-                break;
213
-            case JobParameters::status_complete:
214
-                return $this->successWrapper('job status: COMPLETE');
215
-            case JobParameters::status_error:
216
-                return $this->errorWrapper('job status: ERROR');
217
-        }
218
-        return '';
219
-    }
206
+	protected function jobStatusNotice(JobParameters $job_parameters): string
207
+	{
208
+		switch ($job_parameters->status()) {
209
+			case JobParameters::status_cleaned_up:
210
+			case JobParameters::status_continue:
211
+			case JobParameters::status_pause:
212
+				break;
213
+			case JobParameters::status_complete:
214
+				return $this->successWrapper('job status: COMPLETE');
215
+			case JobParameters::status_error:
216
+				return $this->errorWrapper('job status: ERROR');
217
+		}
218
+		return '';
219
+	}
220 220
 
221 221
 
222
-    /**
223
-     * @param string $update_text
224
-     */
225
-    protected function updateText(string $update_text)
226
-    {
227
-        $this->feedback[] = "<p class='ee-batch-job-update'>$update_text</p>";
228
-    }
222
+	/**
223
+	 * @param string $update_text
224
+	 */
225
+	protected function updateText(string $update_text)
226
+	{
227
+		$this->feedback[] = "<p class='ee-batch-job-update'>$update_text</p>";
228
+	}
229 229
 
230 230
 
231
-    protected function updateTextHeader(string $update_text)
232
-    {
233
-        $this->feedback[] = "<h4 class='ee-batch-job-update-heading'>$update_text</h4>";
234
-    }
231
+	protected function updateTextHeader(string $update_text)
232
+	{
233
+		$this->feedback[] = "<h4 class='ee-batch-job-update-heading'>$update_text</h4>";
234
+	}
235 235
 
236 236
 
237
-    protected function attentionWrapper(string $update_text): string
238
-    {
239
-        return "<span class='ee-status-outline ee-status-bg--attention'>$update_text</span>";
240
-    }
237
+	protected function attentionWrapper(string $update_text): string
238
+	{
239
+		return "<span class='ee-status-outline ee-status-bg--attention'>$update_text</span>";
240
+	}
241 241
 
242 242
 
243
-    protected function errorWrapper(string $update_text): string
244
-    {
245
-        return "<span class='ee-status-outline ee-status-bg--error'>$update_text</span>";
246
-    }
243
+	protected function errorWrapper(string $update_text): string
244
+	{
245
+		return "<span class='ee-status-outline ee-status-bg--error'>$update_text</span>";
246
+	}
247 247
 
248 248
 
249
-    protected function infoWrapper(string $update_text): string
250
-    {
251
-        return "<span class='ee-status-outline ee-status-bg--info'>$update_text</span>";
252
-    }
249
+	protected function infoWrapper(string $update_text): string
250
+	{
251
+		return "<span class='ee-status-outline ee-status-bg--info'>$update_text</span>";
252
+	}
253 253
 
254 254
 
255
-    protected function okWrapper(string $update_text): string
256
-    {
257
-        return "<span class='ee-status-outline ee-status-bg--ok'>$update_text</span>";
258
-    }
255
+	protected function okWrapper(string $update_text): string
256
+	{
257
+		return "<span class='ee-status-outline ee-status-bg--ok'>$update_text</span>";
258
+	}
259 259
 
260 260
 
261
-    protected function successWrapper(string $update_text): string
262
-    {
263
-        return "<span class='ee-status-outline ee-status-bg--success'>$update_text</span>";
264
-    }
261
+	protected function successWrapper(string $update_text): string
262
+	{
263
+		return "<span class='ee-status-outline ee-status-bg--success'>$update_text</span>";
264
+	}
265 265
 
266 266
 
267
-    protected function warningWrapper(string $update_text): string
268
-    {
269
-        return "<span class='ee-status-outline ee-status-bg--warning'>$update_text</span>";
270
-    }
267
+	protected function warningWrapper(string $update_text): string
268
+	{
269
+		return "<span class='ee-status-outline ee-status-bg--warning'>$update_text</span>";
270
+	}
271 271
 
272 272
 
273
-    /**
274
-     * @return string
275
-     */
276
-    protected function spinner(): string
277
-    {
278
-        return '<span class="spinner"></span>';
279
-    }
273
+	/**
274
+	 * @return string
275
+	 */
276
+	protected function spinner(): string
277
+	{
278
+		return '<span class="spinner"></span>';
279
+	}
280 280
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Registration.class.php 1 patch
Indentation   +2558 added lines, -2558 removed lines patch added patch discarded remove patch
@@ -17,2562 +17,2562 @@
 block discarded – undo
17 17
  */
18 18
 class EE_Registration extends EE_Soft_Delete_Base_Class implements EEI_Registration, EEI_Admin_Links
19 19
 {
20
-    /**
21
-     * Used to reference when a registration has never been checked in.
22
-     *
23
-     * @deprecated use \EE_Checkin::status_checked_never instead
24
-     * @type int
25
-     */
26
-    const checkin_status_never = 2;
27
-
28
-    /**
29
-     * Used to reference when a registration has been checked in.
30
-     *
31
-     * @deprecated use \EE_Checkin::status_checked_in instead
32
-     * @type int
33
-     */
34
-    const checkin_status_in = 1;
35
-
36
-    /**
37
-     * Used to reference when a registration has been checked out.
38
-     *
39
-     * @deprecated use \EE_Checkin::status_checked_out instead
40
-     * @type int
41
-     */
42
-    const checkin_status_out = 0;
43
-
44
-    /**
45
-     * extra meta key for tracking reg status os trashed registrations
46
-     *
47
-     * @type string
48
-     */
49
-    const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
50
-
51
-    /**
52
-     * extra meta key for tracking if registration has reserved ticket
53
-     *
54
-     * @type string
55
-     */
56
-    const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
57
-
58
-
59
-    /**
60
-     * @param array  $props_n_values          incoming values
61
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
62
-     *                                        used.)
63
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
64
-     *                                        date_format and the second value is the time format
65
-     * @return EE_Registration
66
-     * @throws EE_Error
67
-     * @throws InvalidArgumentException
68
-     * @throws InvalidDataTypeException
69
-     * @throws InvalidInterfaceException
70
-     * @throws ReflectionException
71
-     */
72
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
73
-    {
74
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
75
-        return $has_object
76
-            ?: new self($props_n_values, false, $timezone, $date_formats);
77
-    }
78
-
79
-
80
-    /**
81
-     * @param array  $props_n_values  incoming values from the database
82
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
83
-     *                                the website will be used.
84
-     * @return EE_Registration
85
-     * @throws EE_Error
86
-     * @throws InvalidArgumentException
87
-     * @throws InvalidDataTypeException
88
-     * @throws InvalidInterfaceException
89
-     * @throws ReflectionException
90
-     */
91
-    public static function new_instance_from_db($props_n_values = [], $timezone = '')
92
-    {
93
-        return new self($props_n_values, true, $timezone);
94
-    }
95
-
96
-
97
-    /**
98
-     *        Set Event ID
99
-     *
100
-     * @param int $EVT_ID Event ID
101
-     * @throws DomainException
102
-     * @throws EE_Error
103
-     * @throws EntityNotFoundException
104
-     * @throws InvalidArgumentException
105
-     * @throws InvalidDataTypeException
106
-     * @throws InvalidInterfaceException
107
-     * @throws ReflectionException
108
-     * @throws RuntimeException
109
-     * @throws UnexpectedEntityException
110
-     */
111
-    public function set_event($EVT_ID = 0)
112
-    {
113
-        $this->set('EVT_ID', $EVT_ID);
114
-    }
115
-
116
-
117
-    /**
118
-     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
119
-     * be routed to internal methods
120
-     *
121
-     * @param string $field_name
122
-     * @param mixed  $field_value
123
-     * @param bool   $use_default
124
-     * @throws DomainException
125
-     * @throws EE_Error
126
-     * @throws EntityNotFoundException
127
-     * @throws InvalidArgumentException
128
-     * @throws InvalidDataTypeException
129
-     * @throws InvalidInterfaceException
130
-     * @throws ReflectionException
131
-     * @throws RuntimeException
132
-     * @throws UnexpectedEntityException
133
-     */
134
-    public function set($field_name, $field_value, $use_default = false)
135
-    {
136
-        switch ($field_name) {
137
-            case 'REG_code':
138
-                if (! empty($field_value) && ! $this->reg_code()) {
139
-                    $this->set_reg_code($field_value, $use_default);
140
-                }
141
-                break;
142
-            case 'STS_ID':
143
-                $this->set_status($field_value, $use_default);
144
-                break;
145
-            default:
146
-                parent::set($field_name, $field_value, $use_default);
147
-        }
148
-    }
149
-
150
-
151
-    /**
152
-     * Set Status ID
153
-     * updates the registration status and ALSO...
154
-     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
155
-     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
156
-     *
157
-     * @param string                $new_STS_ID
158
-     * @param boolean               $use_default
159
-     * @param ContextInterface|null $context
160
-     * @return bool
161
-     * @throws DomainException
162
-     * @throws EE_Error
163
-     * @throws EntityNotFoundException
164
-     * @throws InvalidArgumentException
165
-     * @throws InvalidDataTypeException
166
-     * @throws InvalidInterfaceException
167
-     * @throws ReflectionException
168
-     * @throws RuntimeException
169
-     * @throws UnexpectedEntityException
170
-     */
171
-    public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
172
-    {
173
-        // get current REG_Status
174
-        $old_STS_ID = $this->status_ID();
175
-        // if status has changed
176
-        if (
177
-            $old_STS_ID !== $new_STS_ID // and that status has actually changed
178
-            && ! empty($old_STS_ID) // and that old status is actually set
179
-            && ! empty($new_STS_ID) // as well as the new status
180
-            && $this->ID() // ensure registration is in the db
181
-        ) {
182
-            // update internal status first
183
-            parent::set('STS_ID', $new_STS_ID, $use_default);
184
-            // THEN handle other changes that occur when reg status changes
185
-            // TO approved
186
-            if ($new_STS_ID === EEM_Registration::status_id_approved) {
187
-                // reserve a space by incrementing ticket and datetime sold values
188
-                $this->reserveRegistrationSpace();
189
-                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
190
-                // OR FROM  approved
191
-            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
192
-                // release a space by decrementing ticket and datetime sold values
193
-                $this->releaseRegistrationSpace();
194
-                do_action(
195
-                    'AHEE__EE_Registration__set_status__from_approved',
196
-                    $this,
197
-                    $old_STS_ID,
198
-                    $new_STS_ID,
199
-                    $context
200
-                );
201
-            }
202
-            // update status
203
-            parent::set('STS_ID', $new_STS_ID, $use_default);
204
-            $this->updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, $context);
205
-            if ($this->statusChangeUpdatesTransaction($context)) {
206
-                $this->updateTransactionAfterStatusChange();
207
-            }
208
-            do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
209
-            return true;
210
-        }
211
-        // even though the old value matches the new value, it's still good to
212
-        // allow the parent set method to have a say
213
-        parent::set('STS_ID', $new_STS_ID, $use_default);
214
-        return true;
215
-    }
216
-
217
-
218
-    /**
219
-     * update REGs and TXN when cancelled or declined registrations involved
220
-     *
221
-     * @param string                $new_STS_ID
222
-     * @param string                $old_STS_ID
223
-     * @param ContextInterface|null $context
224
-     * @throws EE_Error
225
-     * @throws InvalidArgumentException
226
-     * @throws InvalidDataTypeException
227
-     * @throws InvalidInterfaceException
228
-     * @throws ReflectionException
229
-     * @throws RuntimeException
230
-     */
231
-    private function updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
232
-    {
233
-        // these reg statuses should not be considered in any calculations involving monies owing
234
-        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
235
-        // true if registration has been cancelled or declined
236
-        $this->updateIfCanceled(
237
-            $closed_reg_statuses,
238
-            $new_STS_ID,
239
-            $old_STS_ID,
240
-            $context
241
-        );
242
-        $this->updateIfReinstated(
243
-            $closed_reg_statuses,
244
-            $new_STS_ID,
245
-            $old_STS_ID,
246
-            $context
247
-        );
248
-    }
249
-
250
-
251
-    /**
252
-     * update REGs and TXN when cancelled or declined registrations involved
253
-     *
254
-     * @param array                 $closed_reg_statuses
255
-     * @param string                $new_STS_ID
256
-     * @param string                $old_STS_ID
257
-     * @param ContextInterface|null $context
258
-     * @throws EE_Error
259
-     * @throws InvalidArgumentException
260
-     * @throws InvalidDataTypeException
261
-     * @throws InvalidInterfaceException
262
-     * @throws ReflectionException
263
-     * @throws RuntimeException
264
-     */
265
-    private function updateIfCanceled(
266
-        array $closed_reg_statuses,
267
-        $new_STS_ID,
268
-        $old_STS_ID,
269
-        ContextInterface $context = null
270
-    ) {
271
-        // true if registration has been cancelled or declined
272
-        if (
273
-            in_array($new_STS_ID, $closed_reg_statuses, true)
274
-            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
275
-        ) {
276
-            /** @type EE_Registration_Processor $registration_processor */
277
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
278
-            /** @type EE_Transaction_Processor $transaction_processor */
279
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
280
-            // cancelled or declined registration
281
-            $registration_processor->update_registration_after_being_canceled_or_declined(
282
-                $this,
283
-                $closed_reg_statuses
284
-            );
285
-            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
286
-                $this,
287
-                $closed_reg_statuses,
288
-                false
289
-            );
290
-            do_action(
291
-                'AHEE__EE_Registration__set_status__canceled_or_declined',
292
-                $this,
293
-                $old_STS_ID,
294
-                $new_STS_ID,
295
-                $context
296
-            );
297
-            return;
298
-        }
299
-    }
300
-
301
-
302
-    /**
303
-     * update REGs and TXN when cancelled or declined registrations involved
304
-     *
305
-     * @param array                 $closed_reg_statuses
306
-     * @param string                $new_STS_ID
307
-     * @param string                $old_STS_ID
308
-     * @param ContextInterface|null $context
309
-     * @throws EE_Error
310
-     * @throws InvalidArgumentException
311
-     * @throws InvalidDataTypeException
312
-     * @throws InvalidInterfaceException
313
-     * @throws ReflectionException
314
-     * @throws RuntimeException
315
-     */
316
-    private function updateIfReinstated(
317
-        array $closed_reg_statuses,
318
-        $new_STS_ID,
319
-        $old_STS_ID,
320
-        ContextInterface $context = null
321
-    ) {
322
-        // true if reinstating cancelled or declined registration
323
-        if (
324
-            in_array($old_STS_ID, $closed_reg_statuses, true)
325
-            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
326
-        ) {
327
-            /** @type EE_Registration_Processor $registration_processor */
328
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
329
-            /** @type EE_Transaction_Processor $transaction_processor */
330
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
331
-            // reinstating cancelled or declined registration
332
-            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
333
-                $this,
334
-                $closed_reg_statuses
335
-            );
336
-            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
337
-                $this,
338
-                $closed_reg_statuses,
339
-                false
340
-            );
341
-            do_action(
342
-                'AHEE__EE_Registration__set_status__after_reinstated',
343
-                $this,
344
-                $old_STS_ID,
345
-                $new_STS_ID,
346
-                $context
347
-            );
348
-        }
349
-    }
350
-
351
-
352
-    /**
353
-     * @param ContextInterface|null $context
354
-     * @return bool
355
-     */
356
-    private function statusChangeUpdatesTransaction(ContextInterface $context = null)
357
-    {
358
-        $contexts_that_do_not_update_transaction = (array) apply_filters(
359
-            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
360
-            ['spco_reg_step_attendee_information_process_registrations'],
361
-            $context,
362
-            $this
363
-        );
364
-        return ! (
365
-            $context instanceof ContextInterface
366
-            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
367
-        );
368
-    }
369
-
370
-
371
-    /**
372
-     * @throws EE_Error
373
-     * @throws EntityNotFoundException
374
-     * @throws InvalidArgumentException
375
-     * @throws InvalidDataTypeException
376
-     * @throws InvalidInterfaceException
377
-     * @throws ReflectionException
378
-     * @throws RuntimeException
379
-     */
380
-    private function updateTransactionAfterStatusChange()
381
-    {
382
-        /** @type EE_Transaction_Payments $transaction_payments */
383
-        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
384
-        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
385
-        $this->transaction()->update_status_based_on_total_paid();
386
-    }
387
-
388
-
389
-    /**
390
-     * get Status ID
391
-     *
392
-     * @throws EE_Error
393
-     * @throws InvalidArgumentException
394
-     * @throws InvalidDataTypeException
395
-     * @throws InvalidInterfaceException
396
-     * @throws ReflectionException
397
-     */
398
-    public function status_ID()
399
-    {
400
-        return $this->get('STS_ID');
401
-    }
402
-
403
-
404
-    /**
405
-     * Gets the ticket this registration is for
406
-     *
407
-     * @param boolean $include_archived whether to include archived tickets or not.
408
-     * @return EE_Ticket|EE_Base_Class
409
-     * @throws EE_Error
410
-     * @throws InvalidArgumentException
411
-     * @throws InvalidDataTypeException
412
-     * @throws InvalidInterfaceException
413
-     * @throws ReflectionException
414
-     */
415
-    public function ticket($include_archived = true)
416
-    {
417
-        $query_params = [];
418
-        if ($include_archived) {
419
-            $query_params['default_where_conditions'] = 'none';
420
-        }
421
-        return $this->get_first_related('Ticket', $query_params);
422
-    }
423
-
424
-
425
-    /**
426
-     * Gets the event this registration is for
427
-     *
428
-     * @return EE_Event
429
-     * @throws EE_Error
430
-     * @throws EntityNotFoundException
431
-     * @throws InvalidArgumentException
432
-     * @throws InvalidDataTypeException
433
-     * @throws InvalidInterfaceException
434
-     * @throws ReflectionException
435
-     */
436
-    public function event(): EE_Event
437
-    {
438
-        $event = $this->get_first_related('Event');
439
-        if (! $event instanceof EE_Event) {
440
-            throw new EntityNotFoundException('Event ID', $this->event_ID());
441
-        }
442
-        return $event;
443
-    }
444
-
445
-
446
-    /**
447
-     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
448
-     * with the author of the event this registration is for.
449
-     *
450
-     * @return int
451
-     * @throws EE_Error
452
-     * @throws EntityNotFoundException
453
-     * @throws InvalidArgumentException
454
-     * @throws InvalidDataTypeException
455
-     * @throws InvalidInterfaceException
456
-     * @throws ReflectionException
457
-     * @since 4.5.0
458
-     */
459
-    public function wp_user(): int
460
-    {
461
-        return $this->event()->wp_user();
462
-    }
463
-
464
-
465
-    /**
466
-     * increments this registration's related ticket sold and corresponding datetime sold values
467
-     *
468
-     * @return void
469
-     * @throws DomainException
470
-     * @throws EE_Error
471
-     * @throws EntityNotFoundException
472
-     * @throws InvalidArgumentException
473
-     * @throws InvalidDataTypeException
474
-     * @throws InvalidInterfaceException
475
-     * @throws ReflectionException
476
-     * @throws UnexpectedEntityException
477
-     */
478
-    private function reserveRegistrationSpace()
479
-    {
480
-        // reserved ticket and datetime counts will be decremented as sold counts are incremented
481
-        // so stop tracking that this reg has a ticket reserved
482
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
483
-        $ticket = $this->ticket();
484
-        $ticket->increaseSold();
485
-        // possibly set event status to sold out
486
-        $this->event()->perform_sold_out_status_check();
487
-    }
488
-
489
-
490
-    /**
491
-     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
492
-     *
493
-     * @return void
494
-     * @throws DomainException
495
-     * @throws EE_Error
496
-     * @throws EntityNotFoundException
497
-     * @throws InvalidArgumentException
498
-     * @throws InvalidDataTypeException
499
-     * @throws InvalidInterfaceException
500
-     * @throws ReflectionException
501
-     * @throws UnexpectedEntityException
502
-     */
503
-    private function releaseRegistrationSpace()
504
-    {
505
-        $ticket = $this->ticket();
506
-        $ticket->decreaseSold();
507
-        // possibly change event status from sold out back to previous status
508
-        $this->event()->perform_sold_out_status_check();
509
-    }
510
-
511
-
512
-    /**
513
-     * tracks this registration's ticket reservation in extra meta
514
-     * and can increment related ticket reserved and corresponding datetime reserved values
515
-     *
516
-     * @param bool   $update_ticket if true, will increment ticket and datetime reserved count
517
-     * @param string $source
518
-     * @return void
519
-     * @throws EE_Error
520
-     * @throws InvalidArgumentException
521
-     * @throws InvalidDataTypeException
522
-     * @throws InvalidInterfaceException
523
-     * @throws ReflectionException
524
-     */
525
-    public function reserve_ticket($update_ticket = false, $source = 'unknown')
526
-    {
527
-        // only reserve ticket if space is not currently reserved
528
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
529
-            $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
530
-            if ($reserved && $update_ticket) {
531
-                $ticket = $this->ticket();
532
-                $ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
533
-                $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
534
-                $ticket->save();
535
-            }
536
-        }
537
-    }
538
-
539
-
540
-    /**
541
-     * stops tracking this registration's ticket reservation in extra meta
542
-     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
543
-     *
544
-     * @param bool   $update_ticket if true, will decrement ticket and datetime reserved count
545
-     * @param string $source
546
-     * @return void
547
-     * @throws EE_Error
548
-     * @throws InvalidArgumentException
549
-     * @throws InvalidDataTypeException
550
-     * @throws InvalidInterfaceException
551
-     * @throws ReflectionException
552
-     */
553
-    public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
554
-    {
555
-        // only release ticket if space is currently reserved
556
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
557
-            $reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
558
-            if ($reserved && $update_ticket) {
559
-                $ticket = $this->ticket();
560
-                $ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
561
-                $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
562
-            }
563
-        }
564
-    }
565
-
566
-
567
-    /**
568
-     * Set Attendee ID
569
-     *
570
-     * @param int $ATT_ID Attendee ID
571
-     * @throws DomainException
572
-     * @throws EE_Error
573
-     * @throws EntityNotFoundException
574
-     * @throws InvalidArgumentException
575
-     * @throws InvalidDataTypeException
576
-     * @throws InvalidInterfaceException
577
-     * @throws ReflectionException
578
-     * @throws RuntimeException
579
-     * @throws UnexpectedEntityException
580
-     */
581
-    public function set_attendee_id($ATT_ID = 0)
582
-    {
583
-        $this->set('ATT_ID', $ATT_ID);
584
-    }
585
-
586
-
587
-    /**
588
-     *        Set Transaction ID
589
-     *
590
-     * @param int $TXN_ID Transaction ID
591
-     * @throws DomainException
592
-     * @throws EE_Error
593
-     * @throws EntityNotFoundException
594
-     * @throws InvalidArgumentException
595
-     * @throws InvalidDataTypeException
596
-     * @throws InvalidInterfaceException
597
-     * @throws ReflectionException
598
-     * @throws RuntimeException
599
-     * @throws UnexpectedEntityException
600
-     */
601
-    public function set_transaction_id($TXN_ID = 0)
602
-    {
603
-        $this->set('TXN_ID', $TXN_ID);
604
-    }
605
-
606
-
607
-    /**
608
-     *        Set Session
609
-     *
610
-     * @param string $REG_session PHP Session ID
611
-     * @throws DomainException
612
-     * @throws EE_Error
613
-     * @throws EntityNotFoundException
614
-     * @throws InvalidArgumentException
615
-     * @throws InvalidDataTypeException
616
-     * @throws InvalidInterfaceException
617
-     * @throws ReflectionException
618
-     * @throws RuntimeException
619
-     * @throws UnexpectedEntityException
620
-     */
621
-    public function set_session($REG_session = '')
622
-    {
623
-        $this->set('REG_session', $REG_session);
624
-    }
625
-
626
-
627
-    /**
628
-     *        Set Registration URL Link
629
-     *
630
-     * @param string $REG_url_link Registration URL Link
631
-     * @throws DomainException
632
-     * @throws EE_Error
633
-     * @throws EntityNotFoundException
634
-     * @throws InvalidArgumentException
635
-     * @throws InvalidDataTypeException
636
-     * @throws InvalidInterfaceException
637
-     * @throws ReflectionException
638
-     * @throws RuntimeException
639
-     * @throws UnexpectedEntityException
640
-     */
641
-    public function set_reg_url_link($REG_url_link = '')
642
-    {
643
-        $this->set('REG_url_link', $REG_url_link);
644
-    }
645
-
646
-
647
-    /**
648
-     *        Set Attendee Counter
649
-     *
650
-     * @param int $REG_count Primary Attendee
651
-     * @throws DomainException
652
-     * @throws EE_Error
653
-     * @throws EntityNotFoundException
654
-     * @throws InvalidArgumentException
655
-     * @throws InvalidDataTypeException
656
-     * @throws InvalidInterfaceException
657
-     * @throws ReflectionException
658
-     * @throws RuntimeException
659
-     * @throws UnexpectedEntityException
660
-     */
661
-    public function set_count($REG_count = 1)
662
-    {
663
-        $this->set('REG_count', $REG_count);
664
-    }
665
-
666
-
667
-    /**
668
-     *        Set Group Size
669
-     *
670
-     * @param boolean $REG_group_size Group Registration
671
-     * @throws DomainException
672
-     * @throws EE_Error
673
-     * @throws EntityNotFoundException
674
-     * @throws InvalidArgumentException
675
-     * @throws InvalidDataTypeException
676
-     * @throws InvalidInterfaceException
677
-     * @throws ReflectionException
678
-     * @throws RuntimeException
679
-     * @throws UnexpectedEntityException
680
-     */
681
-    public function set_group_size($REG_group_size = false)
682
-    {
683
-        $this->set('REG_group_size', $REG_group_size);
684
-    }
685
-
686
-
687
-    /**
688
-     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
689
-     *    EEM_Registration::status_id_not_approved
690
-     *
691
-     * @return        boolean
692
-     * @throws EE_Error
693
-     * @throws InvalidArgumentException
694
-     * @throws InvalidDataTypeException
695
-     * @throws InvalidInterfaceException
696
-     * @throws ReflectionException
697
-     */
698
-    public function is_not_approved()
699
-    {
700
-        return $this->status_ID() === EEM_Registration::status_id_not_approved;
701
-    }
702
-
703
-
704
-    /**
705
-     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
706
-     *    EEM_Registration::status_id_pending_payment
707
-     *
708
-     * @return        boolean
709
-     * @throws EE_Error
710
-     * @throws InvalidArgumentException
711
-     * @throws InvalidDataTypeException
712
-     * @throws InvalidInterfaceException
713
-     * @throws ReflectionException
714
-     */
715
-    public function is_pending_payment()
716
-    {
717
-        return $this->status_ID() === EEM_Registration::status_id_pending_payment;
718
-    }
719
-
720
-
721
-    /**
722
-     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
723
-     *
724
-     * @return        boolean
725
-     * @throws EE_Error
726
-     * @throws InvalidArgumentException
727
-     * @throws InvalidDataTypeException
728
-     * @throws InvalidInterfaceException
729
-     * @throws ReflectionException
730
-     */
731
-    public function is_approved()
732
-    {
733
-        return $this->status_ID() === EEM_Registration::status_id_approved;
734
-    }
735
-
736
-
737
-    /**
738
-     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
739
-     *
740
-     * @return        boolean
741
-     * @throws EE_Error
742
-     * @throws InvalidArgumentException
743
-     * @throws InvalidDataTypeException
744
-     * @throws InvalidInterfaceException
745
-     * @throws ReflectionException
746
-     */
747
-    public function is_cancelled()
748
-    {
749
-        return $this->status_ID() === EEM_Registration::status_id_cancelled;
750
-    }
751
-
752
-
753
-    /**
754
-     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
755
-     *
756
-     * @return        boolean
757
-     * @throws EE_Error
758
-     * @throws InvalidArgumentException
759
-     * @throws InvalidDataTypeException
760
-     * @throws InvalidInterfaceException
761
-     * @throws ReflectionException
762
-     */
763
-    public function is_declined()
764
-    {
765
-        return $this->status_ID() === EEM_Registration::status_id_declined;
766
-    }
767
-
768
-
769
-    /**
770
-     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
771
-     *    EEM_Registration::status_id_incomplete
772
-     *
773
-     * @return        boolean
774
-     * @throws EE_Error
775
-     * @throws InvalidArgumentException
776
-     * @throws InvalidDataTypeException
777
-     * @throws InvalidInterfaceException
778
-     * @throws ReflectionException
779
-     */
780
-    public function is_incomplete()
781
-    {
782
-        return $this->status_ID() === EEM_Registration::status_id_incomplete;
783
-    }
784
-
785
-
786
-    /**
787
-     *        Set Registration Date
788
-     *
789
-     * @param mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
790
-     *                                                 Date
791
-     * @throws DomainException
792
-     * @throws EE_Error
793
-     * @throws EntityNotFoundException
794
-     * @throws InvalidArgumentException
795
-     * @throws InvalidDataTypeException
796
-     * @throws InvalidInterfaceException
797
-     * @throws ReflectionException
798
-     * @throws RuntimeException
799
-     * @throws UnexpectedEntityException
800
-     */
801
-    public function set_reg_date($REG_date = false)
802
-    {
803
-        $this->set('REG_date', $REG_date);
804
-    }
805
-
806
-
807
-    /**
808
-     *    Set final price owing for this registration after all ticket/price modifications
809
-     *
810
-     * @param float $REG_final_price
811
-     * @throws DomainException
812
-     * @throws EE_Error
813
-     * @throws EntityNotFoundException
814
-     * @throws InvalidArgumentException
815
-     * @throws InvalidDataTypeException
816
-     * @throws InvalidInterfaceException
817
-     * @throws ReflectionException
818
-     * @throws RuntimeException
819
-     * @throws UnexpectedEntityException
820
-     */
821
-    public function set_final_price($REG_final_price = 0.00)
822
-    {
823
-        $this->set('REG_final_price', $REG_final_price);
824
-    }
825
-
826
-
827
-    /**
828
-     *    Set amount paid towards this registration's final price
829
-     *
830
-     * @param float|int|string $REG_paid
831
-     * @throws DomainException
832
-     * @throws EE_Error
833
-     * @throws EntityNotFoundException
834
-     * @throws InvalidArgumentException
835
-     * @throws InvalidDataTypeException
836
-     * @throws InvalidInterfaceException
837
-     * @throws ReflectionException
838
-     * @throws RuntimeException
839
-     * @throws UnexpectedEntityException
840
-     */
841
-    public function set_paid($REG_paid = 0.00)
842
-    {
843
-        $this->set('REG_paid', (float) $REG_paid);
844
-    }
845
-
846
-
847
-    /**
848
-     *        Attendee Is Going
849
-     *
850
-     * @param boolean $REG_att_is_going Attendee Is Going
851
-     * @throws DomainException
852
-     * @throws EE_Error
853
-     * @throws EntityNotFoundException
854
-     * @throws InvalidArgumentException
855
-     * @throws InvalidDataTypeException
856
-     * @throws InvalidInterfaceException
857
-     * @throws ReflectionException
858
-     * @throws RuntimeException
859
-     * @throws UnexpectedEntityException
860
-     */
861
-    public function set_att_is_going($REG_att_is_going = false)
862
-    {
863
-        $this->set('REG_att_is_going', $REG_att_is_going);
864
-    }
865
-
866
-
867
-    /**
868
-     * Gets the related attendee
869
-     *
870
-     * @return EE_Attendee|EE_Base_Class
871
-     * @throws EE_Error
872
-     * @throws InvalidArgumentException
873
-     * @throws InvalidDataTypeException
874
-     * @throws InvalidInterfaceException
875
-     * @throws ReflectionException
876
-     */
877
-    public function attendee()
878
-    {
879
-        return $this->get_first_related('Attendee');
880
-    }
881
-
882
-
883
-    /**
884
-     * Gets the name of the attendee.
885
-     *
886
-     * @param bool $apply_html_entities set to true if you want to use HTML entities.
887
-     * @return string
888
-     * @throws EE_Error
889
-     * @throws InvalidArgumentException
890
-     * @throws InvalidDataTypeException
891
-     * @throws InvalidInterfaceException
892
-     * @throws ReflectionException
893
-     * @since 4.10.12.p
894
-     */
895
-    public function attendeeName($apply_html_entities = false)
896
-    {
897
-        $attendee = $this->get_first_related('Attendee');
898
-        if ($attendee instanceof EE_Attendee) {
899
-            $attendee_name = $attendee->full_name($apply_html_entities);
900
-        } else {
901
-            $attendee_name = esc_html__('Unknown', 'event_espresso');
902
-        }
903
-        return $attendee_name;
904
-    }
905
-
906
-
907
-    /**
908
-     *        get Event ID
909
-     */
910
-    public function event_ID()
911
-    {
912
-        return $this->get('EVT_ID');
913
-    }
914
-
915
-
916
-    /**
917
-     *        get Event ID
918
-     */
919
-    public function event_name()
920
-    {
921
-        $event = $this->event_obj();
922
-        if ($event) {
923
-            return $event->name();
924
-        } else {
925
-            return null;
926
-        }
927
-    }
928
-
929
-
930
-    /**
931
-     * Fetches the event this registration is for
932
-     *
933
-     * @return EE_Base_Class|EE_Event
934
-     * @throws EE_Error
935
-     * @throws InvalidArgumentException
936
-     * @throws InvalidDataTypeException
937
-     * @throws InvalidInterfaceException
938
-     * @throws ReflectionException
939
-     */
940
-    public function event_obj()
941
-    {
942
-        return $this->get_first_related('Event');
943
-    }
944
-
945
-
946
-    /**
947
-     *        get Attendee ID
948
-     */
949
-    public function attendee_ID()
950
-    {
951
-        return $this->get('ATT_ID');
952
-    }
953
-
954
-
955
-    /**
956
-     *        get PHP Session ID
957
-     */
958
-    public function session_ID()
959
-    {
960
-        return $this->get('REG_session');
961
-    }
962
-
963
-
964
-    /**
965
-     * Gets the string which represents the URL trigger for the receipt template in the message template system.
966
-     *
967
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
968
-     * @return string
969
-     * @throws DomainException
970
-     * @throws InvalidArgumentException
971
-     * @throws InvalidDataTypeException
972
-     * @throws InvalidInterfaceException
973
-     */
974
-    public function receipt_url($messenger = 'html')
975
-    {
976
-        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
977
-    }
978
-
979
-
980
-    /**
981
-     * Gets the string which represents the URL trigger for the invoice template in the message template system.
982
-     *
983
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
984
-     * @return string
985
-     * @throws DomainException
986
-     * @throws InvalidArgumentException
987
-     * @throws InvalidDataTypeException
988
-     * @throws InvalidInterfaceException
989
-     */
990
-    public function invoice_url($messenger = 'html')
991
-    {
992
-        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
993
-    }
994
-
995
-
996
-    /**
997
-     * get Registration URL Link
998
-     *
999
-     * @return string
1000
-     * @throws EE_Error
1001
-     * @throws InvalidArgumentException
1002
-     * @throws InvalidDataTypeException
1003
-     * @throws InvalidInterfaceException
1004
-     * @throws ReflectionException
1005
-     */
1006
-    public function reg_url_link()
1007
-    {
1008
-        return (string) $this->get('REG_url_link');
1009
-    }
1010
-
1011
-
1012
-    /**
1013
-     * Echoes out invoice_url()
1014
-     *
1015
-     * @param string $type 'download','launch', or 'html' (default is 'launch')
1016
-     * @return void
1017
-     * @throws DomainException
1018
-     * @throws EE_Error
1019
-     * @throws InvalidArgumentException
1020
-     * @throws InvalidDataTypeException
1021
-     * @throws InvalidInterfaceException
1022
-     * @throws ReflectionException
1023
-     */
1024
-    public function e_invoice_url($type = 'launch')
1025
-    {
1026
-        echo esc_url_raw($this->invoice_url($type));
1027
-    }
1028
-
1029
-
1030
-    /**
1031
-     * Echoes out payment_overview_url
1032
-     */
1033
-    public function e_payment_overview_url()
1034
-    {
1035
-        echo esc_url_raw($this->payment_overview_url());
1036
-    }
1037
-
1038
-
1039
-    /**
1040
-     * Gets the URL for the checkout payment options reg step
1041
-     * with this registration's REG_url_link added as a query parameter
1042
-     *
1043
-     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
1044
-     *                            payment overview url.
1045
-     * @return string
1046
-     * @throws EE_Error
1047
-     * @throws InvalidArgumentException
1048
-     * @throws InvalidDataTypeException
1049
-     * @throws InvalidInterfaceException
1050
-     * @throws ReflectionException
1051
-     */
1052
-    public function payment_overview_url($clear_session = false)
1053
-    {
1054
-        return add_query_arg(
1055
-            (array) apply_filters(
1056
-                'FHEE__EE_Registration__payment_overview_url__query_args',
1057
-                [
1058
-                    'e_reg_url_link' => $this->reg_url_link(),
1059
-                    'step'           => 'payment_options',
1060
-                    'revisit'        => true,
1061
-                    'clear_session'  => (bool) $clear_session,
1062
-                ],
1063
-                $this
1064
-            ),
1065
-            EE_Registry::instance()->CFG->core->reg_page_url()
1066
-        );
1067
-    }
1068
-
1069
-
1070
-    /**
1071
-     * Gets the URL for the checkout attendee information reg step
1072
-     * with this registration's REG_url_link added as a query parameter
1073
-     *
1074
-     * @return string
1075
-     * @throws EE_Error
1076
-     * @throws InvalidArgumentException
1077
-     * @throws InvalidDataTypeException
1078
-     * @throws InvalidInterfaceException
1079
-     * @throws ReflectionException
1080
-     */
1081
-    public function edit_attendee_information_url()
1082
-    {
1083
-        return add_query_arg(
1084
-            (array) apply_filters(
1085
-                'FHEE__EE_Registration__edit_attendee_information_url__query_args',
1086
-                [
1087
-                    'e_reg_url_link' => $this->reg_url_link(),
1088
-                    'step'           => 'attendee_information',
1089
-                    'revisit'        => true,
1090
-                ],
1091
-                $this
1092
-            ),
1093
-            EE_Registry::instance()->CFG->core->reg_page_url()
1094
-        );
1095
-    }
1096
-
1097
-
1098
-    /**
1099
-     * Simply generates and returns the appropriate admin_url link to edit this registration
1100
-     *
1101
-     * @return string
1102
-     * @throws EE_Error
1103
-     * @throws InvalidArgumentException
1104
-     * @throws InvalidDataTypeException
1105
-     * @throws InvalidInterfaceException
1106
-     * @throws ReflectionException
1107
-     */
1108
-    public function get_admin_edit_url()
1109
-    {
1110
-        return EEH_URL::add_query_args_and_nonce(
1111
-            [
1112
-                'page'    => 'espresso_registrations',
1113
-                'action'  => 'view_registration',
1114
-                '_REG_ID' => $this->ID(),
1115
-            ],
1116
-            admin_url('admin.php')
1117
-        );
1118
-    }
1119
-
1120
-
1121
-    /**
1122
-     * is_primary_registrant?
1123
-     *
1124
-     * @throws EE_Error
1125
-     * @throws InvalidArgumentException
1126
-     * @throws InvalidDataTypeException
1127
-     * @throws InvalidInterfaceException
1128
-     * @throws ReflectionException
1129
-     */
1130
-    public function is_primary_registrant()
1131
-    {
1132
-        return (int) $this->get('REG_count') === 1;
1133
-    }
1134
-
1135
-
1136
-    /**
1137
-     * This returns the primary registration object for this registration group (which may be this object).
1138
-     *
1139
-     * @return EE_Registration
1140
-     * @throws EE_Error
1141
-     * @throws InvalidArgumentException
1142
-     * @throws InvalidDataTypeException
1143
-     * @throws InvalidInterfaceException
1144
-     * @throws ReflectionException
1145
-     */
1146
-    public function get_primary_registration()
1147
-    {
1148
-        if ($this->is_primary_registrant()) {
1149
-            return $this;
1150
-        }
1151
-
1152
-        // k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1153
-        /** @var EE_Registration $primary_registrant */
1154
-        $primary_registrant = EEM_Registration::instance()->get_one(
1155
-            [
1156
-                [
1157
-                    'TXN_ID'    => $this->transaction_ID(),
1158
-                    'REG_count' => 1,
1159
-                ],
1160
-            ]
1161
-        );
1162
-        return $primary_registrant;
1163
-    }
1164
-
1165
-
1166
-    /**
1167
-     * get  Attendee Number
1168
-     *
1169
-     * @throws EE_Error
1170
-     * @throws InvalidArgumentException
1171
-     * @throws InvalidDataTypeException
1172
-     * @throws InvalidInterfaceException
1173
-     * @throws ReflectionException
1174
-     */
1175
-    public function count()
1176
-    {
1177
-        return $this->get('REG_count');
1178
-    }
1179
-
1180
-
1181
-    /**
1182
-     * get Group Size
1183
-     *
1184
-     * @throws EE_Error
1185
-     * @throws InvalidArgumentException
1186
-     * @throws InvalidDataTypeException
1187
-     * @throws InvalidInterfaceException
1188
-     * @throws ReflectionException
1189
-     */
1190
-    public function group_size()
1191
-    {
1192
-        return $this->get('REG_group_size');
1193
-    }
1194
-
1195
-
1196
-    /**
1197
-     * get Registration Date
1198
-     *
1199
-     * @throws EE_Error
1200
-     * @throws InvalidArgumentException
1201
-     * @throws InvalidDataTypeException
1202
-     * @throws InvalidInterfaceException
1203
-     * @throws ReflectionException
1204
-     */
1205
-    public function date()
1206
-    {
1207
-        return $this->get('REG_date');
1208
-    }
1209
-
1210
-
1211
-    /**
1212
-     * gets a pretty date
1213
-     *
1214
-     * @param string $date_format
1215
-     * @param string $time_format
1216
-     * @return string
1217
-     * @throws EE_Error
1218
-     * @throws InvalidArgumentException
1219
-     * @throws InvalidDataTypeException
1220
-     * @throws InvalidInterfaceException
1221
-     * @throws ReflectionException
1222
-     */
1223
-    public function pretty_date($date_format = null, $time_format = null)
1224
-    {
1225
-        return $this->get_datetime('REG_date', $date_format, $time_format);
1226
-    }
1227
-
1228
-
1229
-    /**
1230
-     * final_price
1231
-     * the registration's share of the transaction total, so that the
1232
-     * sum of all the transaction's REG_final_prices equal the transaction's total
1233
-     *
1234
-     * @return float
1235
-     * @throws EE_Error
1236
-     * @throws InvalidArgumentException
1237
-     * @throws InvalidDataTypeException
1238
-     * @throws InvalidInterfaceException
1239
-     * @throws ReflectionException
1240
-     */
1241
-    public function final_price(): float
1242
-    {
1243
-        return (float) $this->get('REG_final_price');
1244
-    }
1245
-
1246
-
1247
-    /**
1248
-     * pretty_final_price
1249
-     *  final price as formatted string, with correct decimal places and currency symbol
1250
-     *
1251
-     * @return string
1252
-     * @throws EE_Error
1253
-     * @throws InvalidArgumentException
1254
-     * @throws InvalidDataTypeException
1255
-     * @throws InvalidInterfaceException
1256
-     * @throws ReflectionException
1257
-     */
1258
-    public function pretty_final_price()
1259
-    {
1260
-        return $this->get_pretty('REG_final_price');
1261
-    }
1262
-
1263
-
1264
-    /**
1265
-     * get paid (yeah)
1266
-     *
1267
-     * @return float
1268
-     * @throws EE_Error
1269
-     * @throws InvalidArgumentException
1270
-     * @throws InvalidDataTypeException
1271
-     * @throws InvalidInterfaceException
1272
-     * @throws ReflectionException
1273
-     */
1274
-    public function paid(): float
1275
-    {
1276
-        return (float) $this->get('REG_paid');
1277
-    }
1278
-
1279
-
1280
-    /**
1281
-     * pretty_paid
1282
-     *
1283
-     * @return float
1284
-     * @throws EE_Error
1285
-     * @throws InvalidArgumentException
1286
-     * @throws InvalidDataTypeException
1287
-     * @throws InvalidInterfaceException
1288
-     * @throws ReflectionException
1289
-     */
1290
-    public function pretty_paid()
1291
-    {
1292
-        return $this->get_pretty('REG_paid');
1293
-    }
1294
-
1295
-
1296
-    /**
1297
-     * owes_monies_and_can_pay
1298
-     * whether or not this registration has monies owing and it's' status allows payment
1299
-     *
1300
-     * @param array $requires_payment list of registration statuses that allow a registrant to make a payment
1301
-     * @return bool
1302
-     * @throws EE_Error
1303
-     * @throws InvalidArgumentException
1304
-     * @throws InvalidDataTypeException
1305
-     * @throws InvalidInterfaceException
1306
-     * @throws ReflectionException
1307
-     */
1308
-    public function owes_monies_and_can_pay($requires_payment = [])
1309
-    {
1310
-        // these reg statuses require payment (if event is not free)
1311
-        $requires_payment = ! empty($requires_payment)
1312
-            ? $requires_payment
1313
-            : EEM_Registration::reg_statuses_that_allow_payment();
1314
-        if (
1315
-            $this->final_price() !== 0 &&
1316
-            $this->final_price() !== $this->paid() &&
1317
-            in_array($this->status_ID(), $requires_payment)
1318
-        ) {
1319
-            return true;
1320
-        }
1321
-        return false;
1322
-    }
1323
-
1324
-
1325
-    /**
1326
-     * Prints out the return value of $this->pretty_status()
1327
-     *
1328
-     * @param bool $show_icons
1329
-     * @return void
1330
-     * @throws EE_Error
1331
-     * @throws InvalidArgumentException
1332
-     * @throws InvalidDataTypeException
1333
-     * @throws InvalidInterfaceException
1334
-     * @throws ReflectionException
1335
-     */
1336
-    public function e_pretty_status($show_icons = false)
1337
-    {
1338
-        echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
1339
-    }
1340
-
1341
-
1342
-    /**
1343
-     * Returns a nice version of the status for displaying to customers
1344
-     *
1345
-     * @param bool $show_icons
1346
-     * @return string
1347
-     * @throws EE_Error
1348
-     * @throws InvalidArgumentException
1349
-     * @throws InvalidDataTypeException
1350
-     * @throws InvalidInterfaceException
1351
-     * @throws ReflectionException
1352
-     */
1353
-    public function pretty_status($show_icons = false)
1354
-    {
1355
-        $status = EEM_Status::instance()->localized_status(
1356
-            [$this->status_ID() => esc_html__('unknown', 'event_espresso')],
1357
-            false,
1358
-            'sentence'
1359
-        );
1360
-        $icon   = '';
1361
-        switch ($this->status_ID()) {
1362
-            case EEM_Registration::status_id_approved:
1363
-                $icon = $show_icons
1364
-                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1365
-                    : '';
1366
-                break;
1367
-            case EEM_Registration::status_id_pending_payment:
1368
-                $icon = $show_icons
1369
-                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1370
-                    : '';
1371
-                break;
1372
-            case EEM_Registration::status_id_not_approved:
1373
-                $icon = $show_icons
1374
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1375
-                    : '';
1376
-                break;
1377
-            case EEM_Registration::status_id_cancelled:
1378
-                $icon = $show_icons
1379
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1380
-                    : '';
1381
-                break;
1382
-            case EEM_Registration::status_id_incomplete:
1383
-                $icon = $show_icons
1384
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1385
-                    : '';
1386
-                break;
1387
-            case EEM_Registration::status_id_declined:
1388
-                $icon = $show_icons
1389
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1390
-                    : '';
1391
-                break;
1392
-            case EEM_Registration::status_id_wait_list:
1393
-                $icon = $show_icons
1394
-                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1395
-                    : '';
1396
-                break;
1397
-        }
1398
-        return $icon . $status[ $this->status_ID() ];
1399
-    }
1400
-
1401
-
1402
-    /**
1403
-     *        get Attendee Is Going
1404
-     */
1405
-    public function att_is_going()
1406
-    {
1407
-        return $this->get('REG_att_is_going');
1408
-    }
1409
-
1410
-
1411
-    /**
1412
-     * Gets related answers
1413
-     *
1414
-     * @param array $query_params @see
1415
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1416
-     * @return EE_Answer[]|EE_Base_Class[]
1417
-     * @throws EE_Error
1418
-     * @throws InvalidArgumentException
1419
-     * @throws InvalidDataTypeException
1420
-     * @throws InvalidInterfaceException
1421
-     * @throws ReflectionException
1422
-     */
1423
-    public function answers($query_params = [])
1424
-    {
1425
-        return $this->get_many_related('Answer', $query_params);
1426
-    }
1427
-
1428
-
1429
-    /**
1430
-     * Gets the registration's answer value to the specified question
1431
-     * (either the question's ID or a question object)
1432
-     *
1433
-     * @param EE_Question|int $question
1434
-     * @param bool            $pretty_value
1435
-     * @return array|string if pretty_value= true, the result will always be a string
1436
-     * (because the answer might be an array of answer values, so passing pretty_value=true
1437
-     * will convert it into some kind of string)
1438
-     * @throws EE_Error
1439
-     * @throws InvalidArgumentException
1440
-     * @throws InvalidDataTypeException
1441
-     * @throws InvalidInterfaceException
1442
-     */
1443
-    public function answer_value_to_question($question, $pretty_value = true)
1444
-    {
1445
-        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1446
-        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1447
-    }
1448
-
1449
-
1450
-    /**
1451
-     * question_groups
1452
-     * returns an array of EE_Question_Group objects for this registration
1453
-     *
1454
-     * @return EE_Question_Group[]
1455
-     * @throws EE_Error
1456
-     * @throws InvalidArgumentException
1457
-     * @throws InvalidDataTypeException
1458
-     * @throws InvalidInterfaceException
1459
-     * @throws ReflectionException
1460
-     */
1461
-    public function question_groups()
1462
-    {
1463
-        return EEM_Event::instance()->get_question_groups_for_event($this->event_ID(), $this);
1464
-    }
1465
-
1466
-
1467
-    /**
1468
-     * count_question_groups
1469
-     * returns a count of the number of EE_Question_Group objects for this registration
1470
-     *
1471
-     * @return int
1472
-     * @throws EE_Error
1473
-     * @throws EntityNotFoundException
1474
-     * @throws InvalidArgumentException
1475
-     * @throws InvalidDataTypeException
1476
-     * @throws InvalidInterfaceException
1477
-     * @throws ReflectionException
1478
-     */
1479
-    public function count_question_groups()
1480
-    {
1481
-        return EEM_Event::instance()->count_related(
1482
-            $this->event_ID(),
1483
-            'Question_Group',
1484
-            [
1485
-                [
1486
-                    'Event_Question_Group.'
1487
-                    . EEM_Event_Question_Group::instance()->fieldNameForContext($this->is_primary_registrant()) => true,
1488
-                ],
1489
-            ]
1490
-        );
1491
-    }
1492
-
1493
-
1494
-    /**
1495
-     * Returns the registration date in the 'standard' string format
1496
-     * (function may be improved in the future to allow for different formats and timezones)
1497
-     *
1498
-     * @return string
1499
-     * @throws EE_Error
1500
-     * @throws InvalidArgumentException
1501
-     * @throws InvalidDataTypeException
1502
-     * @throws InvalidInterfaceException
1503
-     * @throws ReflectionException
1504
-     */
1505
-    public function reg_date()
1506
-    {
1507
-        return $this->get_datetime('REG_date');
1508
-    }
1509
-
1510
-
1511
-    /**
1512
-     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1513
-     * the ticket this registration purchased, or the datetime they have registered
1514
-     * to attend)
1515
-     *
1516
-     * @return EE_Base_Class|EE_Datetime_Ticket
1517
-     * @throws EE_Error
1518
-     * @throws InvalidArgumentException
1519
-     * @throws InvalidDataTypeException
1520
-     * @throws InvalidInterfaceException
1521
-     * @throws ReflectionException
1522
-     */
1523
-    public function datetime_ticket()
1524
-    {
1525
-        return $this->get_first_related('Datetime_Ticket');
1526
-    }
1527
-
1528
-
1529
-    /**
1530
-     * Sets the registration's datetime_ticket.
1531
-     *
1532
-     * @param EE_Datetime_Ticket $datetime_ticket
1533
-     * @return EE_Base_Class|EE_Datetime_Ticket
1534
-     * @throws EE_Error
1535
-     * @throws InvalidArgumentException
1536
-     * @throws InvalidDataTypeException
1537
-     * @throws InvalidInterfaceException
1538
-     * @throws ReflectionException
1539
-     */
1540
-    public function set_datetime_ticket($datetime_ticket)
1541
-    {
1542
-        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1543
-    }
1544
-
1545
-
1546
-    /**
1547
-     * Gets deleted
1548
-     *
1549
-     * @return bool
1550
-     * @throws EE_Error
1551
-     * @throws InvalidArgumentException
1552
-     * @throws InvalidDataTypeException
1553
-     * @throws InvalidInterfaceException
1554
-     * @throws ReflectionException
1555
-     */
1556
-    public function deleted()
1557
-    {
1558
-        return $this->get('REG_deleted');
1559
-    }
1560
-
1561
-
1562
-    /**
1563
-     * Sets deleted
1564
-     *
1565
-     * @param boolean $deleted
1566
-     * @return void
1567
-     * @throws DomainException
1568
-     * @throws EE_Error
1569
-     * @throws EntityNotFoundException
1570
-     * @throws InvalidArgumentException
1571
-     * @throws InvalidDataTypeException
1572
-     * @throws InvalidInterfaceException
1573
-     * @throws ReflectionException
1574
-     * @throws RuntimeException
1575
-     * @throws UnexpectedEntityException
1576
-     */
1577
-    public function set_deleted($deleted)
1578
-    {
1579
-        if ($deleted) {
1580
-            $this->delete();
1581
-        } else {
1582
-            $this->restore();
1583
-        }
1584
-    }
1585
-
1586
-
1587
-    /**
1588
-     * Get the status object of this object
1589
-     *
1590
-     * @return EE_Base_Class|EE_Status
1591
-     * @throws EE_Error
1592
-     * @throws InvalidArgumentException
1593
-     * @throws InvalidDataTypeException
1594
-     * @throws InvalidInterfaceException
1595
-     * @throws ReflectionException
1596
-     */
1597
-    public function status_obj()
1598
-    {
1599
-        return $this->get_first_related('Status');
1600
-    }
1601
-
1602
-
1603
-    /**
1604
-     * Returns the number of times this registration has checked into any of the datetimes
1605
-     * its available for
1606
-     *
1607
-     * @return int
1608
-     * @throws EE_Error
1609
-     * @throws InvalidArgumentException
1610
-     * @throws InvalidDataTypeException
1611
-     * @throws InvalidInterfaceException
1612
-     * @throws ReflectionException
1613
-     */
1614
-    public function count_checkins()
1615
-    {
1616
-        return $this->get_model()->count_related($this, 'Checkin');
1617
-    }
1618
-
1619
-
1620
-    /**
1621
-     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1622
-     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1623
-     *
1624
-     * @return int
1625
-     * @throws EE_Error
1626
-     * @throws InvalidArgumentException
1627
-     * @throws InvalidDataTypeException
1628
-     * @throws InvalidInterfaceException
1629
-     * @throws ReflectionException
1630
-     */
1631
-    public function count_checkins_not_checkedout()
1632
-    {
1633
-        return $this->get_model()->count_related($this, 'Checkin', [['CHK_in' => 1]]);
1634
-    }
1635
-
1636
-
1637
-    /**
1638
-     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1639
-     *
1640
-     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1641
-     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1642
-     *                                          consider registration status as well as datetime access.
1643
-     * @return bool
1644
-     * @throws EE_Error
1645
-     * @throws InvalidArgumentException
1646
-     * @throws InvalidDataTypeException
1647
-     * @throws InvalidInterfaceException
1648
-     * @throws ReflectionException
1649
-     */
1650
-    public function can_checkin($DTT_OR_ID, $check_approved = true)
1651
-    {
1652
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1653
-        // first check registration status
1654
-        if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1655
-            return false;
1656
-        }
1657
-        // is there a datetime ticket that matches this dtt_ID?
1658
-        if (
1659
-            ! (EEM_Datetime_Ticket::instance()->exists(
1660
-                [
1661
-                    [
1662
-                        'TKT_ID' => $this->get('TKT_ID'),
1663
-                        'DTT_ID' => $DTT_ID,
1664
-                    ],
1665
-                ]
1666
-            ))
1667
-        ) {
1668
-            return false;
1669
-        }
1670
-
1671
-        // final check is against TKT_uses
1672
-        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1673
-    }
1674
-
1675
-
1676
-    /**
1677
-     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1678
-     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1679
-     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1680
-     * then return false.  Otherwise return true.
1681
-     *
1682
-     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1683
-     * @return bool true means can checkin.  false means cannot checkin.
1684
-     * @throws EE_Error
1685
-     * @throws InvalidArgumentException
1686
-     * @throws InvalidDataTypeException
1687
-     * @throws InvalidInterfaceException
1688
-     * @throws ReflectionException
1689
-     */
1690
-    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1691
-    {
1692
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1693
-
1694
-        if (! $DTT_ID) {
1695
-            return false;
1696
-        }
1697
-
1698
-        $max_uses = $this->ticket() instanceof EE_Ticket
1699
-            ? $this->ticket()->uses()
1700
-            : EE_INF;
1701
-
1702
-        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1703
-        // check-in or not.
1704
-        if (! $max_uses || $max_uses === EE_INF) {
1705
-            return true;
1706
-        }
1707
-
1708
-        // does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1709
-        // go ahead and toggle.
1710
-        if (EEM_Checkin::instance()->exists([['REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID]])) {
1711
-            return true;
1712
-        }
1713
-
1714
-        // made it here so the last check is whether the number of checkins per unique datetime on this registration
1715
-        // disallows further check-ins.
1716
-        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1717
-            [
1718
-                [
1719
-                    'REG_ID' => $this->ID(),
1720
-                    'CHK_in' => true,
1721
-                ],
1722
-            ],
1723
-            'DTT_ID',
1724
-            true
1725
-        );
1726
-        // checkins have already reached their max number of uses
1727
-        // so registrant can NOT checkin
1728
-        if ($count_unique_dtt_checkins >= $max_uses) {
1729
-            EE_Error::add_error(
1730
-                esc_html__(
1731
-                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1732
-                    'event_espresso'
1733
-                ),
1734
-                __FILE__,
1735
-                __FUNCTION__,
1736
-                __LINE__
1737
-            );
1738
-            return false;
1739
-        }
1740
-        return true;
1741
-    }
1742
-
1743
-
1744
-    /**
1745
-     * toggle Check-in status for this registration
1746
-     * Check-ins are toggled in the following order:
1747
-     * never checked in -> checked in
1748
-     * checked in -> checked out
1749
-     * checked out -> checked in
1750
-     *
1751
-     * @param int  $DTT_ID  include specific datetime to toggle Check-in for.
1752
-     *                      If not included or null, then it is assumed latest datetime is being toggled.
1753
-     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1754
-     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1755
-     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1756
-     * @throws EE_Error
1757
-     * @throws InvalidArgumentException
1758
-     * @throws InvalidDataTypeException
1759
-     * @throws InvalidInterfaceException
1760
-     * @throws ReflectionException
1761
-     */
1762
-    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1763
-    {
1764
-        if (empty($DTT_ID)) {
1765
-            $datetime = $this->get_latest_related_datetime();
1766
-            $DTT_ID   = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1767
-            // verify the registration can checkin for the given DTT_ID
1768
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1769
-            EE_Error::add_error(
1770
-                sprintf(
1771
-                    esc_html__(
1772
-                        'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1773
-                        'event_espresso'
1774
-                    ),
1775
-                    $this->ID(),
1776
-                    $DTT_ID
1777
-                ),
1778
-                __FILE__,
1779
-                __FUNCTION__,
1780
-                __LINE__
1781
-            );
1782
-            return false;
1783
-        }
1784
-        $status_paths = [
1785
-            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1786
-            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1787
-            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1788
-        ];
1789
-        // start by getting the current status so we know what status we'll be changing to.
1790
-        $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1791
-        $status_to  = $status_paths[ $cur_status ];
1792
-        // database only records true for checked IN or false for checked OUT
1793
-        // no record ( null ) means checked in NEVER, but we obviously don't save that
1794
-        $new_status = $status_to === EE_Checkin::status_checked_in;
1795
-        // add relation - note Check-ins are always creating new rows
1796
-        // because we are keeping track of Check-ins over time.
1797
-        // Eventually we'll probably want to show a list table
1798
-        // for the individual Check-ins so that they can be managed.
1799
-        $checkin = EE_Checkin::new_instance(
1800
-            [
1801
-                'REG_ID' => $this->ID(),
1802
-                'DTT_ID' => $DTT_ID,
1803
-                'CHK_in' => $new_status,
1804
-            ]
1805
-        );
1806
-        // if the record could not be saved then return false
1807
-        if ($checkin->save() === 0) {
1808
-            if (WP_DEBUG) {
1809
-                global $wpdb;
1810
-                $error = sprintf(
1811
-                    esc_html__(
1812
-                        'Registration check in update failed because of the following database error: %1$s%2$s',
1813
-                        'event_espresso'
1814
-                    ),
1815
-                    '<br />',
1816
-                    $wpdb->last_error
1817
-                );
1818
-            } else {
1819
-                $error = esc_html__(
1820
-                    'Registration check in update failed because of an unknown database error',
1821
-                    'event_espresso'
1822
-                );
1823
-            }
1824
-            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1825
-            return false;
1826
-        }
1827
-        // Fire a checked_in and checkout_out action.
1828
-        $checked_status = $status_to === EE_Checkin::status_checked_in
1829
-            ? 'checked_in'
1830
-            : 'checked_out';
1831
-        do_action("AHEE__EE_Registration__toggle_checkin_status__{$checked_status}", $this, $DTT_ID);
1832
-        return $status_to;
1833
-    }
1834
-
1835
-
1836
-    /**
1837
-     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1838
-     * "Latest" is defined by the `DTT_EVT_start` column.
1839
-     *
1840
-     * @return EE_Datetime|null
1841
-     * @throws EE_Error
1842
-     * @throws InvalidArgumentException
1843
-     * @throws InvalidDataTypeException
1844
-     * @throws InvalidInterfaceException
1845
-     * @throws ReflectionException
1846
-     */
1847
-    public function get_latest_related_datetime(): ?EE_Datetime
1848
-    {
1849
-        return EEM_Datetime::instance()->get_one(
1850
-            [
1851
-                [
1852
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1853
-                ],
1854
-                'order_by' => ['DTT_EVT_start' => 'DESC'],
1855
-            ]
1856
-        );
1857
-    }
1858
-
1859
-
1860
-    /**
1861
-     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1862
-     * "Earliest" is defined by the `DTT_EVT_start` column.
1863
-     *
1864
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1865
-     * @throws EE_Error
1866
-     * @throws InvalidArgumentException
1867
-     * @throws InvalidDataTypeException
1868
-     * @throws InvalidInterfaceException
1869
-     * @throws ReflectionException
1870
-     */
1871
-    public function get_earliest_related_datetime()
1872
-    {
1873
-        return EEM_Datetime::instance()->get_one(
1874
-            [
1875
-                [
1876
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1877
-                ],
1878
-                'order_by' => ['DTT_EVT_start' => 'ASC'],
1879
-            ]
1880
-        );
1881
-    }
1882
-
1883
-
1884
-    /**
1885
-     * This method simply returns the check-in status for this registration and the given datetime.
1886
-     * If neither the datetime nor the checkin values are provided as arguments,
1887
-     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1888
-     *
1889
-     * @param int|null        $DTT_ID  The ID of the datetime we're checking against
1890
-     *                                 (if empty we'll get the primary datetime for
1891
-     *                                 this registration (via event) and use it's ID);
1892
-     * @param EE_Checkin|null $checkin If present, we use the given checkin object rather than the dtt_id.
1893
-     * @return int                     Integer representing Check-in status.
1894
-     * @throws EE_Error
1895
-     * @throws ReflectionException
1896
-     */
1897
-    public function check_in_status_for_datetime(?int $DTT_ID = 0, ?EE_Checkin $checkin = null): int
1898
-    {
1899
-        if ($checkin instanceof EE_Checkin) {
1900
-            return $checkin->status();
1901
-        }
1902
-
1903
-        if (! $DTT_ID) {
1904
-            return EE_Checkin::status_invalid;
1905
-        }
1906
-
1907
-        $checkin_query_params = [
1908
-            0          => ['DTT_ID' => $DTT_ID],
1909
-            'order_by' => ['CHK_timestamp' => 'DESC'],
1910
-        ];
1911
-
1912
-        $checkin = $this->get_first_related(
1913
-            'Checkin',
1914
-            $checkin_query_params
1915
-        );
1916
-        return $checkin instanceof EE_Checkin ? $checkin->status() : EE_Checkin::status_checked_never;
1917
-    }
1918
-
1919
-
1920
-    /**
1921
-     * This method returns a localized message for the toggled Check-in message.
1922
-     *
1923
-     * @param int|null $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1924
-     *                         then it is assumed Check-in for primary datetime was toggled.
1925
-     * @param bool     $error  This just flags that you want an error message returned. This is put in so that the error
1926
-     *                         message can be customized with the attendee name.
1927
-     * @return string internationalized message
1928
-     * @throws EE_Error
1929
-     * @throws ReflectionException
1930
-     */
1931
-    public function get_checkin_msg(?int $DTT_ID, bool $error = false): string
1932
-    {
1933
-        // let's get the attendee first so we can include the name of the attendee
1934
-        $attendee = $this->get_first_related('Attendee');
1935
-        if ($attendee instanceof EE_Attendee) {
1936
-            if ($error) {
1937
-                return sprintf(
1938
-                    esc_html__("%s's check-in status was not changed.", "event_espresso"),
1939
-                    $attendee->full_name()
1940
-                );
1941
-            }
1942
-            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1943
-            // what is the status message going to be?
1944
-            switch ($cur_status) {
1945
-                case EE_Checkin::status_checked_never:
1946
-                    return sprintf(
1947
-                        esc_html__('%s has been removed from Check-in records', 'event_espresso'),
1948
-                        $attendee->full_name()
1949
-                    );
1950
-                case EE_Checkin::status_checked_in:
1951
-                    return sprintf(esc_html__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1952
-                case EE_Checkin::status_checked_out:
1953
-                    return sprintf(esc_html__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1954
-            }
1955
-        }
1956
-        return esc_html__('The check-in status could not be determined.', 'event_espresso');
1957
-    }
1958
-
1959
-
1960
-    /**
1961
-     * Returns the related EE_Transaction to this registration
1962
-     *
1963
-     * @return EE_Transaction
1964
-     * @throws EE_Error
1965
-     * @throws EntityNotFoundException
1966
-     * @throws ReflectionException
1967
-     */
1968
-    public function transaction(): EE_Transaction
1969
-    {
1970
-        $transaction = $this->get_first_related('Transaction');
1971
-        if (! $transaction instanceof \EE_Transaction) {
1972
-            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1973
-        }
1974
-        return $transaction;
1975
-    }
1976
-
1977
-
1978
-    /**
1979
-     * get Registration Code
1980
-     *
1981
-     * @return string
1982
-     * @throws EE_Error
1983
-     * @throws InvalidArgumentException
1984
-     * @throws InvalidDataTypeException
1985
-     * @throws InvalidInterfaceException
1986
-     * @throws ReflectionException
1987
-     */
1988
-    public function reg_code(): string
1989
-    {
1990
-        return $this->get('REG_code')
1991
-            ?: '';
1992
-    }
1993
-
1994
-
1995
-    /**
1996
-     * @return mixed
1997
-     * @throws EE_Error
1998
-     * @throws InvalidArgumentException
1999
-     * @throws InvalidDataTypeException
2000
-     * @throws InvalidInterfaceException
2001
-     * @throws ReflectionException
2002
-     */
2003
-    public function transaction_ID()
2004
-    {
2005
-        return $this->get('TXN_ID');
2006
-    }
2007
-
2008
-
2009
-    /**
2010
-     * @return int
2011
-     * @throws EE_Error
2012
-     * @throws InvalidArgumentException
2013
-     * @throws InvalidDataTypeException
2014
-     * @throws InvalidInterfaceException
2015
-     * @throws ReflectionException
2016
-     */
2017
-    public function ticket_ID()
2018
-    {
2019
-        return $this->get('TKT_ID');
2020
-    }
2021
-
2022
-
2023
-    /**
2024
-     * Set Registration Code
2025
-     *
2026
-     * @param RegCode|string $REG_code Registration Code
2027
-     * @param boolean        $use_default
2028
-     * @throws EE_Error
2029
-     * @throws InvalidArgumentException
2030
-     * @throws InvalidDataTypeException
2031
-     * @throws InvalidInterfaceException
2032
-     * @throws ReflectionException
2033
-     */
2034
-    public function set_reg_code($REG_code, bool $use_default = false)
2035
-    {
2036
-        if (empty($REG_code)) {
2037
-            EE_Error::add_error(
2038
-                esc_html__('REG_code can not be empty.', 'event_espresso'),
2039
-                __FILE__,
2040
-                __FUNCTION__,
2041
-                __LINE__
2042
-            );
2043
-            return;
2044
-        }
2045
-        if (! $this->reg_code()) {
2046
-            parent::set('REG_code', $REG_code, $use_default);
2047
-        } else {
2048
-            EE_Error::doing_it_wrong(
2049
-                __CLASS__ . '::' . __FUNCTION__,
2050
-                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2051
-                '4.6.0'
2052
-            );
2053
-        }
2054
-    }
2055
-
2056
-
2057
-    /**
2058
-     * Returns all other registrations in the same group as this registrant who have the same ticket option.
2059
-     * Note, if you want to just get all registrations in the same transaction (group), use:
2060
-     *    $registration->transaction()->registrations();
2061
-     *
2062
-     * @return EE_Registration[] or empty array if this isn't a group registration.
2063
-     * @throws EE_Error
2064
-     * @throws InvalidArgumentException
2065
-     * @throws InvalidDataTypeException
2066
-     * @throws InvalidInterfaceException
2067
-     * @throws ReflectionException
2068
-     * @since 4.5.0
2069
-     */
2070
-    public function get_all_other_registrations_in_group()
2071
-    {
2072
-        if ($this->group_size() < 2) {
2073
-            return [];
2074
-        }
2075
-
2076
-        $query[0] = [
2077
-            'TXN_ID' => $this->transaction_ID(),
2078
-            'REG_ID' => ['!=', $this->ID()],
2079
-            'TKT_ID' => $this->ticket_ID(),
2080
-        ];
2081
-        /** @var EE_Registration[] $registrations */
2082
-        $registrations = $this->get_model()->get_all($query);
2083
-        return $registrations;
2084
-    }
2085
-
2086
-
2087
-    /**
2088
-     * Return the link to the admin details for the object.
2089
-     *
2090
-     * @return string
2091
-     * @throws EE_Error
2092
-     * @throws InvalidArgumentException
2093
-     * @throws InvalidDataTypeException
2094
-     * @throws InvalidInterfaceException
2095
-     * @throws ReflectionException
2096
-     */
2097
-    public function get_admin_details_link()
2098
-    {
2099
-        EE_Registry::instance()->load_helper('URL');
2100
-        return EEH_URL::add_query_args_and_nonce(
2101
-            [
2102
-                'page'    => 'espresso_registrations',
2103
-                'action'  => 'view_registration',
2104
-                '_REG_ID' => $this->ID(),
2105
-            ],
2106
-            admin_url('admin.php')
2107
-        );
2108
-    }
2109
-
2110
-
2111
-    /**
2112
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
2113
-     *
2114
-     * @return string
2115
-     * @throws EE_Error
2116
-     * @throws InvalidArgumentException
2117
-     * @throws InvalidDataTypeException
2118
-     * @throws InvalidInterfaceException
2119
-     * @throws ReflectionException
2120
-     */
2121
-    public function get_admin_edit_link()
2122
-    {
2123
-        return $this->get_admin_details_link();
2124
-    }
2125
-
2126
-
2127
-    /**
2128
-     * Returns the link to a settings page for the object.
2129
-     *
2130
-     * @return string
2131
-     * @throws EE_Error
2132
-     * @throws InvalidArgumentException
2133
-     * @throws InvalidDataTypeException
2134
-     * @throws InvalidInterfaceException
2135
-     * @throws ReflectionException
2136
-     */
2137
-    public function get_admin_settings_link()
2138
-    {
2139
-        return $this->get_admin_details_link();
2140
-    }
2141
-
2142
-
2143
-    /**
2144
-     * Returns the link to the "overview" for the object (typically the "list table" view).
2145
-     *
2146
-     * @return string
2147
-     * @throws EE_Error
2148
-     * @throws InvalidArgumentException
2149
-     * @throws InvalidDataTypeException
2150
-     * @throws InvalidInterfaceException
2151
-     * @throws ReflectionException
2152
-     */
2153
-    public function get_admin_overview_link()
2154
-    {
2155
-        EE_Registry::instance()->load_helper('URL');
2156
-        return EEH_URL::add_query_args_and_nonce(
2157
-            [
2158
-                'page' => 'espresso_registrations',
2159
-            ],
2160
-            admin_url('admin.php')
2161
-        );
2162
-    }
2163
-
2164
-
2165
-    /**
2166
-     * @param array $query_params
2167
-     * @return EE_Base_Class[]|EE_Registration[]
2168
-     * @throws EE_Error
2169
-     * @throws InvalidArgumentException
2170
-     * @throws InvalidDataTypeException
2171
-     * @throws InvalidInterfaceException
2172
-     * @throws ReflectionException
2173
-     */
2174
-    public function payments($query_params = [])
2175
-    {
2176
-        return $this->get_many_related('Payment', $query_params);
2177
-    }
2178
-
2179
-
2180
-    /**
2181
-     * @param array $query_params
2182
-     * @return EE_Base_Class[]|EE_Registration_Payment[]
2183
-     * @throws EE_Error
2184
-     * @throws InvalidArgumentException
2185
-     * @throws InvalidDataTypeException
2186
-     * @throws InvalidInterfaceException
2187
-     * @throws ReflectionException
2188
-     */
2189
-    public function registration_payments($query_params = [])
2190
-    {
2191
-        return $this->get_many_related('Registration_Payment', $query_params);
2192
-    }
2193
-
2194
-
2195
-    /**
2196
-     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
2197
-     * Note: if there are no payments on the registration there will be no payment method returned.
2198
-     *
2199
-     * @return EE_Payment|EE_Payment_Method|null
2200
-     * @throws EE_Error
2201
-     * @throws InvalidArgumentException
2202
-     * @throws InvalidDataTypeException
2203
-     * @throws InvalidInterfaceException
2204
-     */
2205
-    public function payment_method()
2206
-    {
2207
-        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
2208
-    }
2209
-
2210
-
2211
-    /**
2212
-     * @return \EE_Line_Item
2213
-     * @throws EE_Error
2214
-     * @throws EntityNotFoundException
2215
-     * @throws InvalidArgumentException
2216
-     * @throws InvalidDataTypeException
2217
-     * @throws InvalidInterfaceException
2218
-     * @throws ReflectionException
2219
-     */
2220
-    public function ticket_line_item()
2221
-    {
2222
-        $ticket            = $this->ticket();
2223
-        $transaction       = $this->transaction();
2224
-        $line_item         = null;
2225
-        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
2226
-            $transaction->total_line_item(),
2227
-            'Ticket',
2228
-            [$ticket->ID()]
2229
-        );
2230
-        foreach ($ticket_line_items as $ticket_line_item) {
2231
-            if (
2232
-                $ticket_line_item instanceof \EE_Line_Item
2233
-                && $ticket_line_item->OBJ_type() === 'Ticket'
2234
-                && $ticket_line_item->OBJ_ID() === $ticket->ID()
2235
-            ) {
2236
-                $line_item = $ticket_line_item;
2237
-                break;
2238
-            }
2239
-        }
2240
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2241
-            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2242
-        }
2243
-        return $line_item;
2244
-    }
2245
-
2246
-
2247
-    /**
2248
-     * Soft Deletes this model object.
2249
-     *
2250
-     * @param string $source function name that called this method
2251
-     * @return boolean | int
2252
-     * @throws DomainException
2253
-     * @throws EE_Error
2254
-     * @throws EntityNotFoundException
2255
-     * @throws InvalidArgumentException
2256
-     * @throws InvalidDataTypeException
2257
-     * @throws InvalidInterfaceException
2258
-     * @throws ReflectionException
2259
-     * @throws RuntimeException
2260
-     * @throws UnexpectedEntityException
2261
-     */
2262
-    public function delete()
2263
-    {
2264
-        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
2265
-            $this->set_status(EEM_Registration::status_id_cancelled);
2266
-        }
2267
-        return parent::delete();
2268
-    }
2269
-
2270
-
2271
-    /**
2272
-     * Restores whatever the previous status was on a registration before it was trashed (if possible)
2273
-     *
2274
-     * @param string $source function name that called this method
2275
-     * @return bool|int
2276
-     * @throws DomainException
2277
-     * @throws EE_Error
2278
-     * @throws EntityNotFoundException
2279
-     * @throws InvalidArgumentException
2280
-     * @throws InvalidDataTypeException
2281
-     * @throws InvalidInterfaceException
2282
-     * @throws ReflectionException
2283
-     * @throws RuntimeException
2284
-     * @throws UnexpectedEntityException
2285
-     */
2286
-    public function restore()
2287
-    {
2288
-        $previous_status = $this->get_extra_meta(
2289
-            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
2290
-            true,
2291
-            EEM_Registration::status_id_cancelled
2292
-        );
2293
-        if ($previous_status) {
2294
-            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
2295
-            $this->set_status($previous_status);
2296
-        }
2297
-        return parent::restore();
2298
-    }
2299
-
2300
-
2301
-    /**
2302
-     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
2303
-     *
2304
-     * @param boolean $trigger_set_status_logic  EE_Registration::set_status() can trigger additional logic
2305
-     *                                           depending on whether the reg status changes to or from "Approved"
2306
-     * @return boolean whether the Registration status was updated
2307
-     * @throws DomainException
2308
-     * @throws EE_Error
2309
-     * @throws EntityNotFoundException
2310
-     * @throws InvalidArgumentException
2311
-     * @throws InvalidDataTypeException
2312
-     * @throws InvalidInterfaceException
2313
-     * @throws ReflectionException
2314
-     * @throws RuntimeException
2315
-     * @throws UnexpectedEntityException
2316
-     */
2317
-    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
2318
-    {
2319
-        $paid  = $this->paid();
2320
-        $price = $this->final_price();
2321
-        switch (true) {
2322
-            // overpaid or paid
2323
-            case EEH_Money::compare_floats($paid, $price, '>'):
2324
-            case EEH_Money::compare_floats($paid, $price):
2325
-                $new_status = EEM_Registration::status_id_approved;
2326
-                break;
2327
-            //  underpaid
2328
-            case EEH_Money::compare_floats($paid, $price, '<'):
2329
-                $new_status = EEM_Registration::status_id_pending_payment;
2330
-                break;
2331
-            // uhhh Houston...
2332
-            default:
2333
-                throw new RuntimeException(
2334
-                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
2335
-                );
2336
-        }
2337
-        if ($new_status !== $this->status_ID()) {
2338
-            if ($trigger_set_status_logic) {
2339
-                return $this->set_status($new_status);
2340
-            }
2341
-            parent::set('STS_ID', $new_status);
2342
-            return true;
2343
-        }
2344
-        return false;
2345
-    }
2346
-
2347
-
2348
-    /*************************** DEPRECATED ***************************/
2349
-
2350
-
2351
-    /**
2352
-     * @deprecated
2353
-     * @since     4.7.0
2354
-     */
2355
-    public function price_paid()
2356
-    {
2357
-        EE_Error::doing_it_wrong(
2358
-            'EE_Registration::price_paid()',
2359
-            esc_html__(
2360
-                'This method is deprecated, please use EE_Registration::final_price() instead.',
2361
-                'event_espresso'
2362
-            ),
2363
-            '4.7.0'
2364
-        );
2365
-        return $this->final_price();
2366
-    }
2367
-
2368
-
2369
-    /**
2370
-     * @param float $REG_final_price
2371
-     * @throws EE_Error
2372
-     * @throws EntityNotFoundException
2373
-     * @throws InvalidArgumentException
2374
-     * @throws InvalidDataTypeException
2375
-     * @throws InvalidInterfaceException
2376
-     * @throws ReflectionException
2377
-     * @throws RuntimeException
2378
-     * @throws DomainException
2379
-     * @deprecated
2380
-     * @since     4.7.0
2381
-     */
2382
-    public function set_price_paid($REG_final_price = 0.00)
2383
-    {
2384
-        EE_Error::doing_it_wrong(
2385
-            'EE_Registration::set_price_paid()',
2386
-            esc_html__(
2387
-                'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2388
-                'event_espresso'
2389
-            ),
2390
-            '4.7.0'
2391
-        );
2392
-        $this->set_final_price($REG_final_price);
2393
-    }
2394
-
2395
-
2396
-    /**
2397
-     * @return string
2398
-     * @throws EE_Error
2399
-     * @throws InvalidArgumentException
2400
-     * @throws InvalidDataTypeException
2401
-     * @throws InvalidInterfaceException
2402
-     * @throws ReflectionException
2403
-     * @deprecated
2404
-     * @since 4.7.0
2405
-     */
2406
-    public function pretty_price_paid()
2407
-    {
2408
-        EE_Error::doing_it_wrong(
2409
-            'EE_Registration::pretty_price_paid()',
2410
-            esc_html__(
2411
-                'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2412
-                'event_espresso'
2413
-            ),
2414
-            '4.7.0'
2415
-        );
2416
-        return $this->pretty_final_price();
2417
-    }
2418
-
2419
-
2420
-    /**
2421
-     * Gets the primary datetime related to this registration via the related Event to this registration
2422
-     *
2423
-     * @return EE_Datetime
2424
-     * @throws EE_Error
2425
-     * @throws EntityNotFoundException
2426
-     * @throws InvalidArgumentException
2427
-     * @throws InvalidDataTypeException
2428
-     * @throws InvalidInterfaceException
2429
-     * @throws ReflectionException
2430
-     * @deprecated 4.9.17
2431
-     */
2432
-    public function get_related_primary_datetime()
2433
-    {
2434
-        EE_Error::doing_it_wrong(
2435
-            __METHOD__,
2436
-            esc_html__(
2437
-                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2438
-                'event_espresso'
2439
-            ),
2440
-            '4.9.17',
2441
-            '5.0.0'
2442
-        );
2443
-        return $this->event()->primary_datetime();
2444
-    }
2445
-
2446
-
2447
-    /**
2448
-     * Returns the contact's name (or "Unknown" if there is no contact.)
2449
-     *
2450
-     * @return string
2451
-     * @throws EE_Error
2452
-     * @throws InvalidArgumentException
2453
-     * @throws InvalidDataTypeException
2454
-     * @throws InvalidInterfaceException
2455
-     * @throws ReflectionException
2456
-     * @since 4.10.12.p
2457
-     */
2458
-    public function name()
2459
-    {
2460
-        return $this->attendeeName();
2461
-    }
2462
-
2463
-
2464
-    /**
2465
-     * @return bool
2466
-     * @throws EE_Error
2467
-     * @throws ReflectionException
2468
-     */
2469
-    public function wasMoved(): bool
2470
-    {
2471
-        // only need to check 'registration-moved-to' because
2472
-        // the existence of a new REG ID means the registration was moved
2473
-        $reg_moved = $this->get_extra_meta('registration-moved-to', true, []);
2474
-        return isset($reg_moved['NEW_REG_ID']) && $reg_moved['NEW_REG_ID'];
2475
-    }
2476
-
2477
-
2478
-    /**
2479
-     * @param EE_Payment $payment
2480
-     * @param float|null $amount
2481
-     * @return float
2482
-     * @throws EE_Error
2483
-     * @throws ReflectionException
2484
-     * @since 5.0.8.p
2485
-     */
2486
-    public function applyPayment(EE_Payment $payment, ?float $amount = null): float
2487
-    {
2488
-        // echo "\n\n";
2489
-        // \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 3);
2490
-        // \EEH_Debug_Tools::printr($this->ID(), 'REG ID', __FILE__, __LINE__);
2491
-        $payment_amount = $amount ?? $payment->amount();
2492
-        // ensure $payment_amount is NOT negative
2493
-        $payment_amount = (float) abs($payment_amount);
2494
-        // \EEH_Debug_Tools::printr($payment_amount, 'incoming $payment_amount', __FILE__, __LINE__);
2495
-        // \EEH_Debug_Tools::printr($this->final_price(), 'reg final price', __FILE__, __LINE__);
2496
-        // \EEH_Debug_Tools::printr($this->paid(), 'reg paid to date', __FILE__, __LINE__);
2497
-        $payment_amount = $payment->is_a_refund()
2498
-            ? $this->processRefund($payment_amount)
2499
-            : $this->processPayment($payment_amount);
2500
-        // \EEH_Debug_Tools::printr($payment_amount, 'applied payment_amount', __FILE__, __LINE__);
2501
-        if ($payment_amount) {
2502
-            $reg_payment = EEM_Registration_Payment::instance()->get_one(
2503
-                [['REG_ID' => $this->ID(), 'PAY_ID' => $payment->ID()]]
2504
-            );
2505
-            // if existing registration payment exists
2506
-            if ($reg_payment instanceof EE_Registration_Payment) {
2507
-                // echo "\nUPDATE EXISTING REG PAYMENT";
2508
-                // then update that record
2509
-                $reg_payment->set_amount($payment_amount);
2510
-            } else {
2511
-                // echo "\nCREATE NEW REG PAYMENT";
2512
-                // or add new relation between registration and payment and set amount
2513
-                $reg_payment = EE_Registration_Payment::new_instance(
2514
-                    [
2515
-                        'REG_ID'     => $this->ID(),
2516
-                        'PAY_ID'     => $payment->ID(),
2517
-                        'RPY_amount' => $payment_amount,
2518
-                    ]
2519
-                );
2520
-                // $this->_add_relation_to($payment, 'Payment', ['RPY_amount' => $payment_amount]);
2521
-            }
2522
-            $reg_payment->save();
2523
-            // \EEH_Debug_Tools::printr($reg_payment->ID(), '$reg payment ID', __FILE__, __LINE__);
2524
-        }
2525
-        return $payment_amount;
2526
-    }
2527
-
2528
-
2529
-    /**
2530
-     * @throws EE_Error
2531
-     * @throws ReflectionException
2532
-     */
2533
-    private function processPayment(float $payment_amount): float
2534
-    {
2535
-        // echo "\n";
2536
-        // \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 3);
2537
-        $paid  = $this->paid();
2538
-        $owing = $this->final_price() - $paid;
2539
-        // \EEH_Debug_Tools::printr($owing, '$owing', __FILE__, __LINE__);
2540
-        if ($owing <= 0) {
2541
-            return 0.0;
2542
-        }
2543
-        // don't allow payment amount to exceed the incoming amount, OR the amount owing
2544
-        $payment_amount = min($payment_amount, $owing);
2545
-        $paid           = $paid + $payment_amount;
2546
-        // \EEH_Debug_Tools::printr($paid, 'NEW REG PAID AMOUNT', __FILE__, __LINE__);
2547
-        // calculate and set new REG_paid
2548
-        $this->set_paid($paid);
2549
-        // make it stick
2550
-        $this->save();
2551
-        return (float) $payment_amount;
2552
-    }
2553
-
2554
-
2555
-    /**
2556
-     * @throws ReflectionException
2557
-     * @throws EE_Error
2558
-     */
2559
-    private function processRefund(float $payment_amount): float
2560
-    {
2561
-        // echo "\n";
2562
-        // \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 3);
2563
-        $paid = $this->paid();
2564
-        if ($paid <= 0) {
2565
-            return 0.0;
2566
-        }
2567
-        // don't allow refund amount to exceed the incoming amount, OR the amount paid
2568
-        $payment_amount = min($payment_amount, $paid);
2569
-        // calculate and set new REG_paid
2570
-        $paid = $paid - $payment_amount;
2571
-        \EEH_Debug_Tools::printr($paid, 'NEW REG PAID AMOUNT', __FILE__, __LINE__);
2572
-        $this->set_paid($paid);
2573
-        // make it stick
2574
-        $this->save();
2575
-        // convert payment amount back to a negative value for storage in the db
2576
-        return (float) $payment_amount;
2577
-    }
20
+	/**
21
+	 * Used to reference when a registration has never been checked in.
22
+	 *
23
+	 * @deprecated use \EE_Checkin::status_checked_never instead
24
+	 * @type int
25
+	 */
26
+	const checkin_status_never = 2;
27
+
28
+	/**
29
+	 * Used to reference when a registration has been checked in.
30
+	 *
31
+	 * @deprecated use \EE_Checkin::status_checked_in instead
32
+	 * @type int
33
+	 */
34
+	const checkin_status_in = 1;
35
+
36
+	/**
37
+	 * Used to reference when a registration has been checked out.
38
+	 *
39
+	 * @deprecated use \EE_Checkin::status_checked_out instead
40
+	 * @type int
41
+	 */
42
+	const checkin_status_out = 0;
43
+
44
+	/**
45
+	 * extra meta key for tracking reg status os trashed registrations
46
+	 *
47
+	 * @type string
48
+	 */
49
+	const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
50
+
51
+	/**
52
+	 * extra meta key for tracking if registration has reserved ticket
53
+	 *
54
+	 * @type string
55
+	 */
56
+	const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
57
+
58
+
59
+	/**
60
+	 * @param array  $props_n_values          incoming values
61
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
62
+	 *                                        used.)
63
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
64
+	 *                                        date_format and the second value is the time format
65
+	 * @return EE_Registration
66
+	 * @throws EE_Error
67
+	 * @throws InvalidArgumentException
68
+	 * @throws InvalidDataTypeException
69
+	 * @throws InvalidInterfaceException
70
+	 * @throws ReflectionException
71
+	 */
72
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
73
+	{
74
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
75
+		return $has_object
76
+			?: new self($props_n_values, false, $timezone, $date_formats);
77
+	}
78
+
79
+
80
+	/**
81
+	 * @param array  $props_n_values  incoming values from the database
82
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
83
+	 *                                the website will be used.
84
+	 * @return EE_Registration
85
+	 * @throws EE_Error
86
+	 * @throws InvalidArgumentException
87
+	 * @throws InvalidDataTypeException
88
+	 * @throws InvalidInterfaceException
89
+	 * @throws ReflectionException
90
+	 */
91
+	public static function new_instance_from_db($props_n_values = [], $timezone = '')
92
+	{
93
+		return new self($props_n_values, true, $timezone);
94
+	}
95
+
96
+
97
+	/**
98
+	 *        Set Event ID
99
+	 *
100
+	 * @param int $EVT_ID Event ID
101
+	 * @throws DomainException
102
+	 * @throws EE_Error
103
+	 * @throws EntityNotFoundException
104
+	 * @throws InvalidArgumentException
105
+	 * @throws InvalidDataTypeException
106
+	 * @throws InvalidInterfaceException
107
+	 * @throws ReflectionException
108
+	 * @throws RuntimeException
109
+	 * @throws UnexpectedEntityException
110
+	 */
111
+	public function set_event($EVT_ID = 0)
112
+	{
113
+		$this->set('EVT_ID', $EVT_ID);
114
+	}
115
+
116
+
117
+	/**
118
+	 * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
119
+	 * be routed to internal methods
120
+	 *
121
+	 * @param string $field_name
122
+	 * @param mixed  $field_value
123
+	 * @param bool   $use_default
124
+	 * @throws DomainException
125
+	 * @throws EE_Error
126
+	 * @throws EntityNotFoundException
127
+	 * @throws InvalidArgumentException
128
+	 * @throws InvalidDataTypeException
129
+	 * @throws InvalidInterfaceException
130
+	 * @throws ReflectionException
131
+	 * @throws RuntimeException
132
+	 * @throws UnexpectedEntityException
133
+	 */
134
+	public function set($field_name, $field_value, $use_default = false)
135
+	{
136
+		switch ($field_name) {
137
+			case 'REG_code':
138
+				if (! empty($field_value) && ! $this->reg_code()) {
139
+					$this->set_reg_code($field_value, $use_default);
140
+				}
141
+				break;
142
+			case 'STS_ID':
143
+				$this->set_status($field_value, $use_default);
144
+				break;
145
+			default:
146
+				parent::set($field_name, $field_value, $use_default);
147
+		}
148
+	}
149
+
150
+
151
+	/**
152
+	 * Set Status ID
153
+	 * updates the registration status and ALSO...
154
+	 * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
155
+	 * calls release_registration_space() if the reg status changes FROM approved to any other reg status
156
+	 *
157
+	 * @param string                $new_STS_ID
158
+	 * @param boolean               $use_default
159
+	 * @param ContextInterface|null $context
160
+	 * @return bool
161
+	 * @throws DomainException
162
+	 * @throws EE_Error
163
+	 * @throws EntityNotFoundException
164
+	 * @throws InvalidArgumentException
165
+	 * @throws InvalidDataTypeException
166
+	 * @throws InvalidInterfaceException
167
+	 * @throws ReflectionException
168
+	 * @throws RuntimeException
169
+	 * @throws UnexpectedEntityException
170
+	 */
171
+	public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
172
+	{
173
+		// get current REG_Status
174
+		$old_STS_ID = $this->status_ID();
175
+		// if status has changed
176
+		if (
177
+			$old_STS_ID !== $new_STS_ID // and that status has actually changed
178
+			&& ! empty($old_STS_ID) // and that old status is actually set
179
+			&& ! empty($new_STS_ID) // as well as the new status
180
+			&& $this->ID() // ensure registration is in the db
181
+		) {
182
+			// update internal status first
183
+			parent::set('STS_ID', $new_STS_ID, $use_default);
184
+			// THEN handle other changes that occur when reg status changes
185
+			// TO approved
186
+			if ($new_STS_ID === EEM_Registration::status_id_approved) {
187
+				// reserve a space by incrementing ticket and datetime sold values
188
+				$this->reserveRegistrationSpace();
189
+				do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
190
+				// OR FROM  approved
191
+			} elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
192
+				// release a space by decrementing ticket and datetime sold values
193
+				$this->releaseRegistrationSpace();
194
+				do_action(
195
+					'AHEE__EE_Registration__set_status__from_approved',
196
+					$this,
197
+					$old_STS_ID,
198
+					$new_STS_ID,
199
+					$context
200
+				);
201
+			}
202
+			// update status
203
+			parent::set('STS_ID', $new_STS_ID, $use_default);
204
+			$this->updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, $context);
205
+			if ($this->statusChangeUpdatesTransaction($context)) {
206
+				$this->updateTransactionAfterStatusChange();
207
+			}
208
+			do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
209
+			return true;
210
+		}
211
+		// even though the old value matches the new value, it's still good to
212
+		// allow the parent set method to have a say
213
+		parent::set('STS_ID', $new_STS_ID, $use_default);
214
+		return true;
215
+	}
216
+
217
+
218
+	/**
219
+	 * update REGs and TXN when cancelled or declined registrations involved
220
+	 *
221
+	 * @param string                $new_STS_ID
222
+	 * @param string                $old_STS_ID
223
+	 * @param ContextInterface|null $context
224
+	 * @throws EE_Error
225
+	 * @throws InvalidArgumentException
226
+	 * @throws InvalidDataTypeException
227
+	 * @throws InvalidInterfaceException
228
+	 * @throws ReflectionException
229
+	 * @throws RuntimeException
230
+	 */
231
+	private function updateIfCanceledOrReinstated($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
232
+	{
233
+		// these reg statuses should not be considered in any calculations involving monies owing
234
+		$closed_reg_statuses = EEM_Registration::closed_reg_statuses();
235
+		// true if registration has been cancelled or declined
236
+		$this->updateIfCanceled(
237
+			$closed_reg_statuses,
238
+			$new_STS_ID,
239
+			$old_STS_ID,
240
+			$context
241
+		);
242
+		$this->updateIfReinstated(
243
+			$closed_reg_statuses,
244
+			$new_STS_ID,
245
+			$old_STS_ID,
246
+			$context
247
+		);
248
+	}
249
+
250
+
251
+	/**
252
+	 * update REGs and TXN when cancelled or declined registrations involved
253
+	 *
254
+	 * @param array                 $closed_reg_statuses
255
+	 * @param string                $new_STS_ID
256
+	 * @param string                $old_STS_ID
257
+	 * @param ContextInterface|null $context
258
+	 * @throws EE_Error
259
+	 * @throws InvalidArgumentException
260
+	 * @throws InvalidDataTypeException
261
+	 * @throws InvalidInterfaceException
262
+	 * @throws ReflectionException
263
+	 * @throws RuntimeException
264
+	 */
265
+	private function updateIfCanceled(
266
+		array $closed_reg_statuses,
267
+		$new_STS_ID,
268
+		$old_STS_ID,
269
+		ContextInterface $context = null
270
+	) {
271
+		// true if registration has been cancelled or declined
272
+		if (
273
+			in_array($new_STS_ID, $closed_reg_statuses, true)
274
+			&& ! in_array($old_STS_ID, $closed_reg_statuses, true)
275
+		) {
276
+			/** @type EE_Registration_Processor $registration_processor */
277
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
278
+			/** @type EE_Transaction_Processor $transaction_processor */
279
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
280
+			// cancelled or declined registration
281
+			$registration_processor->update_registration_after_being_canceled_or_declined(
282
+				$this,
283
+				$closed_reg_statuses
284
+			);
285
+			$transaction_processor->update_transaction_after_canceled_or_declined_registration(
286
+				$this,
287
+				$closed_reg_statuses,
288
+				false
289
+			);
290
+			do_action(
291
+				'AHEE__EE_Registration__set_status__canceled_or_declined',
292
+				$this,
293
+				$old_STS_ID,
294
+				$new_STS_ID,
295
+				$context
296
+			);
297
+			return;
298
+		}
299
+	}
300
+
301
+
302
+	/**
303
+	 * update REGs and TXN when cancelled or declined registrations involved
304
+	 *
305
+	 * @param array                 $closed_reg_statuses
306
+	 * @param string                $new_STS_ID
307
+	 * @param string                $old_STS_ID
308
+	 * @param ContextInterface|null $context
309
+	 * @throws EE_Error
310
+	 * @throws InvalidArgumentException
311
+	 * @throws InvalidDataTypeException
312
+	 * @throws InvalidInterfaceException
313
+	 * @throws ReflectionException
314
+	 * @throws RuntimeException
315
+	 */
316
+	private function updateIfReinstated(
317
+		array $closed_reg_statuses,
318
+		$new_STS_ID,
319
+		$old_STS_ID,
320
+		ContextInterface $context = null
321
+	) {
322
+		// true if reinstating cancelled or declined registration
323
+		if (
324
+			in_array($old_STS_ID, $closed_reg_statuses, true)
325
+			&& ! in_array($new_STS_ID, $closed_reg_statuses, true)
326
+		) {
327
+			/** @type EE_Registration_Processor $registration_processor */
328
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
329
+			/** @type EE_Transaction_Processor $transaction_processor */
330
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
331
+			// reinstating cancelled or declined registration
332
+			$registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
333
+				$this,
334
+				$closed_reg_statuses
335
+			);
336
+			$transaction_processor->update_transaction_after_reinstating_canceled_registration(
337
+				$this,
338
+				$closed_reg_statuses,
339
+				false
340
+			);
341
+			do_action(
342
+				'AHEE__EE_Registration__set_status__after_reinstated',
343
+				$this,
344
+				$old_STS_ID,
345
+				$new_STS_ID,
346
+				$context
347
+			);
348
+		}
349
+	}
350
+
351
+
352
+	/**
353
+	 * @param ContextInterface|null $context
354
+	 * @return bool
355
+	 */
356
+	private function statusChangeUpdatesTransaction(ContextInterface $context = null)
357
+	{
358
+		$contexts_that_do_not_update_transaction = (array) apply_filters(
359
+			'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
360
+			['spco_reg_step_attendee_information_process_registrations'],
361
+			$context,
362
+			$this
363
+		);
364
+		return ! (
365
+			$context instanceof ContextInterface
366
+			&& in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
367
+		);
368
+	}
369
+
370
+
371
+	/**
372
+	 * @throws EE_Error
373
+	 * @throws EntityNotFoundException
374
+	 * @throws InvalidArgumentException
375
+	 * @throws InvalidDataTypeException
376
+	 * @throws InvalidInterfaceException
377
+	 * @throws ReflectionException
378
+	 * @throws RuntimeException
379
+	 */
380
+	private function updateTransactionAfterStatusChange()
381
+	{
382
+		/** @type EE_Transaction_Payments $transaction_payments */
383
+		$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
384
+		$transaction_payments->recalculate_transaction_total($this->transaction(), false);
385
+		$this->transaction()->update_status_based_on_total_paid();
386
+	}
387
+
388
+
389
+	/**
390
+	 * get Status ID
391
+	 *
392
+	 * @throws EE_Error
393
+	 * @throws InvalidArgumentException
394
+	 * @throws InvalidDataTypeException
395
+	 * @throws InvalidInterfaceException
396
+	 * @throws ReflectionException
397
+	 */
398
+	public function status_ID()
399
+	{
400
+		return $this->get('STS_ID');
401
+	}
402
+
403
+
404
+	/**
405
+	 * Gets the ticket this registration is for
406
+	 *
407
+	 * @param boolean $include_archived whether to include archived tickets or not.
408
+	 * @return EE_Ticket|EE_Base_Class
409
+	 * @throws EE_Error
410
+	 * @throws InvalidArgumentException
411
+	 * @throws InvalidDataTypeException
412
+	 * @throws InvalidInterfaceException
413
+	 * @throws ReflectionException
414
+	 */
415
+	public function ticket($include_archived = true)
416
+	{
417
+		$query_params = [];
418
+		if ($include_archived) {
419
+			$query_params['default_where_conditions'] = 'none';
420
+		}
421
+		return $this->get_first_related('Ticket', $query_params);
422
+	}
423
+
424
+
425
+	/**
426
+	 * Gets the event this registration is for
427
+	 *
428
+	 * @return EE_Event
429
+	 * @throws EE_Error
430
+	 * @throws EntityNotFoundException
431
+	 * @throws InvalidArgumentException
432
+	 * @throws InvalidDataTypeException
433
+	 * @throws InvalidInterfaceException
434
+	 * @throws ReflectionException
435
+	 */
436
+	public function event(): EE_Event
437
+	{
438
+		$event = $this->get_first_related('Event');
439
+		if (! $event instanceof EE_Event) {
440
+			throw new EntityNotFoundException('Event ID', $this->event_ID());
441
+		}
442
+		return $event;
443
+	}
444
+
445
+
446
+	/**
447
+	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
448
+	 * with the author of the event this registration is for.
449
+	 *
450
+	 * @return int
451
+	 * @throws EE_Error
452
+	 * @throws EntityNotFoundException
453
+	 * @throws InvalidArgumentException
454
+	 * @throws InvalidDataTypeException
455
+	 * @throws InvalidInterfaceException
456
+	 * @throws ReflectionException
457
+	 * @since 4.5.0
458
+	 */
459
+	public function wp_user(): int
460
+	{
461
+		return $this->event()->wp_user();
462
+	}
463
+
464
+
465
+	/**
466
+	 * increments this registration's related ticket sold and corresponding datetime sold values
467
+	 *
468
+	 * @return void
469
+	 * @throws DomainException
470
+	 * @throws EE_Error
471
+	 * @throws EntityNotFoundException
472
+	 * @throws InvalidArgumentException
473
+	 * @throws InvalidDataTypeException
474
+	 * @throws InvalidInterfaceException
475
+	 * @throws ReflectionException
476
+	 * @throws UnexpectedEntityException
477
+	 */
478
+	private function reserveRegistrationSpace()
479
+	{
480
+		// reserved ticket and datetime counts will be decremented as sold counts are incremented
481
+		// so stop tracking that this reg has a ticket reserved
482
+		$this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
483
+		$ticket = $this->ticket();
484
+		$ticket->increaseSold();
485
+		// possibly set event status to sold out
486
+		$this->event()->perform_sold_out_status_check();
487
+	}
488
+
489
+
490
+	/**
491
+	 * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
492
+	 *
493
+	 * @return void
494
+	 * @throws DomainException
495
+	 * @throws EE_Error
496
+	 * @throws EntityNotFoundException
497
+	 * @throws InvalidArgumentException
498
+	 * @throws InvalidDataTypeException
499
+	 * @throws InvalidInterfaceException
500
+	 * @throws ReflectionException
501
+	 * @throws UnexpectedEntityException
502
+	 */
503
+	private function releaseRegistrationSpace()
504
+	{
505
+		$ticket = $this->ticket();
506
+		$ticket->decreaseSold();
507
+		// possibly change event status from sold out back to previous status
508
+		$this->event()->perform_sold_out_status_check();
509
+	}
510
+
511
+
512
+	/**
513
+	 * tracks this registration's ticket reservation in extra meta
514
+	 * and can increment related ticket reserved and corresponding datetime reserved values
515
+	 *
516
+	 * @param bool   $update_ticket if true, will increment ticket and datetime reserved count
517
+	 * @param string $source
518
+	 * @return void
519
+	 * @throws EE_Error
520
+	 * @throws InvalidArgumentException
521
+	 * @throws InvalidDataTypeException
522
+	 * @throws InvalidInterfaceException
523
+	 * @throws ReflectionException
524
+	 */
525
+	public function reserve_ticket($update_ticket = false, $source = 'unknown')
526
+	{
527
+		// only reserve ticket if space is not currently reserved
528
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
529
+			$reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true);
530
+			if ($reserved && $update_ticket) {
531
+				$ticket = $this->ticket();
532
+				$ticket->increaseReserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
533
+				$this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
534
+				$ticket->save();
535
+			}
536
+		}
537
+	}
538
+
539
+
540
+	/**
541
+	 * stops tracking this registration's ticket reservation in extra meta
542
+	 * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
543
+	 *
544
+	 * @param bool   $update_ticket if true, will decrement ticket and datetime reserved count
545
+	 * @param string $source
546
+	 * @return void
547
+	 * @throws EE_Error
548
+	 * @throws InvalidArgumentException
549
+	 * @throws InvalidDataTypeException
550
+	 * @throws InvalidInterfaceException
551
+	 * @throws ReflectionException
552
+	 */
553
+	public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
554
+	{
555
+		// only release ticket if space is currently reserved
556
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
557
+			$reserved = $this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false);
558
+			if ($reserved && $update_ticket) {
559
+				$ticket = $this->ticket();
560
+				$ticket->decreaseReserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
561
+				$this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
562
+			}
563
+		}
564
+	}
565
+
566
+
567
+	/**
568
+	 * Set Attendee ID
569
+	 *
570
+	 * @param int $ATT_ID Attendee ID
571
+	 * @throws DomainException
572
+	 * @throws EE_Error
573
+	 * @throws EntityNotFoundException
574
+	 * @throws InvalidArgumentException
575
+	 * @throws InvalidDataTypeException
576
+	 * @throws InvalidInterfaceException
577
+	 * @throws ReflectionException
578
+	 * @throws RuntimeException
579
+	 * @throws UnexpectedEntityException
580
+	 */
581
+	public function set_attendee_id($ATT_ID = 0)
582
+	{
583
+		$this->set('ATT_ID', $ATT_ID);
584
+	}
585
+
586
+
587
+	/**
588
+	 *        Set Transaction ID
589
+	 *
590
+	 * @param int $TXN_ID Transaction ID
591
+	 * @throws DomainException
592
+	 * @throws EE_Error
593
+	 * @throws EntityNotFoundException
594
+	 * @throws InvalidArgumentException
595
+	 * @throws InvalidDataTypeException
596
+	 * @throws InvalidInterfaceException
597
+	 * @throws ReflectionException
598
+	 * @throws RuntimeException
599
+	 * @throws UnexpectedEntityException
600
+	 */
601
+	public function set_transaction_id($TXN_ID = 0)
602
+	{
603
+		$this->set('TXN_ID', $TXN_ID);
604
+	}
605
+
606
+
607
+	/**
608
+	 *        Set Session
609
+	 *
610
+	 * @param string $REG_session PHP Session ID
611
+	 * @throws DomainException
612
+	 * @throws EE_Error
613
+	 * @throws EntityNotFoundException
614
+	 * @throws InvalidArgumentException
615
+	 * @throws InvalidDataTypeException
616
+	 * @throws InvalidInterfaceException
617
+	 * @throws ReflectionException
618
+	 * @throws RuntimeException
619
+	 * @throws UnexpectedEntityException
620
+	 */
621
+	public function set_session($REG_session = '')
622
+	{
623
+		$this->set('REG_session', $REG_session);
624
+	}
625
+
626
+
627
+	/**
628
+	 *        Set Registration URL Link
629
+	 *
630
+	 * @param string $REG_url_link Registration URL Link
631
+	 * @throws DomainException
632
+	 * @throws EE_Error
633
+	 * @throws EntityNotFoundException
634
+	 * @throws InvalidArgumentException
635
+	 * @throws InvalidDataTypeException
636
+	 * @throws InvalidInterfaceException
637
+	 * @throws ReflectionException
638
+	 * @throws RuntimeException
639
+	 * @throws UnexpectedEntityException
640
+	 */
641
+	public function set_reg_url_link($REG_url_link = '')
642
+	{
643
+		$this->set('REG_url_link', $REG_url_link);
644
+	}
645
+
646
+
647
+	/**
648
+	 *        Set Attendee Counter
649
+	 *
650
+	 * @param int $REG_count Primary Attendee
651
+	 * @throws DomainException
652
+	 * @throws EE_Error
653
+	 * @throws EntityNotFoundException
654
+	 * @throws InvalidArgumentException
655
+	 * @throws InvalidDataTypeException
656
+	 * @throws InvalidInterfaceException
657
+	 * @throws ReflectionException
658
+	 * @throws RuntimeException
659
+	 * @throws UnexpectedEntityException
660
+	 */
661
+	public function set_count($REG_count = 1)
662
+	{
663
+		$this->set('REG_count', $REG_count);
664
+	}
665
+
666
+
667
+	/**
668
+	 *        Set Group Size
669
+	 *
670
+	 * @param boolean $REG_group_size Group Registration
671
+	 * @throws DomainException
672
+	 * @throws EE_Error
673
+	 * @throws EntityNotFoundException
674
+	 * @throws InvalidArgumentException
675
+	 * @throws InvalidDataTypeException
676
+	 * @throws InvalidInterfaceException
677
+	 * @throws ReflectionException
678
+	 * @throws RuntimeException
679
+	 * @throws UnexpectedEntityException
680
+	 */
681
+	public function set_group_size($REG_group_size = false)
682
+	{
683
+		$this->set('REG_group_size', $REG_group_size);
684
+	}
685
+
686
+
687
+	/**
688
+	 *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
689
+	 *    EEM_Registration::status_id_not_approved
690
+	 *
691
+	 * @return        boolean
692
+	 * @throws EE_Error
693
+	 * @throws InvalidArgumentException
694
+	 * @throws InvalidDataTypeException
695
+	 * @throws InvalidInterfaceException
696
+	 * @throws ReflectionException
697
+	 */
698
+	public function is_not_approved()
699
+	{
700
+		return $this->status_ID() === EEM_Registration::status_id_not_approved;
701
+	}
702
+
703
+
704
+	/**
705
+	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
706
+	 *    EEM_Registration::status_id_pending_payment
707
+	 *
708
+	 * @return        boolean
709
+	 * @throws EE_Error
710
+	 * @throws InvalidArgumentException
711
+	 * @throws InvalidDataTypeException
712
+	 * @throws InvalidInterfaceException
713
+	 * @throws ReflectionException
714
+	 */
715
+	public function is_pending_payment()
716
+	{
717
+		return $this->status_ID() === EEM_Registration::status_id_pending_payment;
718
+	}
719
+
720
+
721
+	/**
722
+	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
723
+	 *
724
+	 * @return        boolean
725
+	 * @throws EE_Error
726
+	 * @throws InvalidArgumentException
727
+	 * @throws InvalidDataTypeException
728
+	 * @throws InvalidInterfaceException
729
+	 * @throws ReflectionException
730
+	 */
731
+	public function is_approved()
732
+	{
733
+		return $this->status_ID() === EEM_Registration::status_id_approved;
734
+	}
735
+
736
+
737
+	/**
738
+	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
739
+	 *
740
+	 * @return        boolean
741
+	 * @throws EE_Error
742
+	 * @throws InvalidArgumentException
743
+	 * @throws InvalidDataTypeException
744
+	 * @throws InvalidInterfaceException
745
+	 * @throws ReflectionException
746
+	 */
747
+	public function is_cancelled()
748
+	{
749
+		return $this->status_ID() === EEM_Registration::status_id_cancelled;
750
+	}
751
+
752
+
753
+	/**
754
+	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
755
+	 *
756
+	 * @return        boolean
757
+	 * @throws EE_Error
758
+	 * @throws InvalidArgumentException
759
+	 * @throws InvalidDataTypeException
760
+	 * @throws InvalidInterfaceException
761
+	 * @throws ReflectionException
762
+	 */
763
+	public function is_declined()
764
+	{
765
+		return $this->status_ID() === EEM_Registration::status_id_declined;
766
+	}
767
+
768
+
769
+	/**
770
+	 *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
771
+	 *    EEM_Registration::status_id_incomplete
772
+	 *
773
+	 * @return        boolean
774
+	 * @throws EE_Error
775
+	 * @throws InvalidArgumentException
776
+	 * @throws InvalidDataTypeException
777
+	 * @throws InvalidInterfaceException
778
+	 * @throws ReflectionException
779
+	 */
780
+	public function is_incomplete()
781
+	{
782
+		return $this->status_ID() === EEM_Registration::status_id_incomplete;
783
+	}
784
+
785
+
786
+	/**
787
+	 *        Set Registration Date
788
+	 *
789
+	 * @param mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
790
+	 *                                                 Date
791
+	 * @throws DomainException
792
+	 * @throws EE_Error
793
+	 * @throws EntityNotFoundException
794
+	 * @throws InvalidArgumentException
795
+	 * @throws InvalidDataTypeException
796
+	 * @throws InvalidInterfaceException
797
+	 * @throws ReflectionException
798
+	 * @throws RuntimeException
799
+	 * @throws UnexpectedEntityException
800
+	 */
801
+	public function set_reg_date($REG_date = false)
802
+	{
803
+		$this->set('REG_date', $REG_date);
804
+	}
805
+
806
+
807
+	/**
808
+	 *    Set final price owing for this registration after all ticket/price modifications
809
+	 *
810
+	 * @param float $REG_final_price
811
+	 * @throws DomainException
812
+	 * @throws EE_Error
813
+	 * @throws EntityNotFoundException
814
+	 * @throws InvalidArgumentException
815
+	 * @throws InvalidDataTypeException
816
+	 * @throws InvalidInterfaceException
817
+	 * @throws ReflectionException
818
+	 * @throws RuntimeException
819
+	 * @throws UnexpectedEntityException
820
+	 */
821
+	public function set_final_price($REG_final_price = 0.00)
822
+	{
823
+		$this->set('REG_final_price', $REG_final_price);
824
+	}
825
+
826
+
827
+	/**
828
+	 *    Set amount paid towards this registration's final price
829
+	 *
830
+	 * @param float|int|string $REG_paid
831
+	 * @throws DomainException
832
+	 * @throws EE_Error
833
+	 * @throws EntityNotFoundException
834
+	 * @throws InvalidArgumentException
835
+	 * @throws InvalidDataTypeException
836
+	 * @throws InvalidInterfaceException
837
+	 * @throws ReflectionException
838
+	 * @throws RuntimeException
839
+	 * @throws UnexpectedEntityException
840
+	 */
841
+	public function set_paid($REG_paid = 0.00)
842
+	{
843
+		$this->set('REG_paid', (float) $REG_paid);
844
+	}
845
+
846
+
847
+	/**
848
+	 *        Attendee Is Going
849
+	 *
850
+	 * @param boolean $REG_att_is_going Attendee Is Going
851
+	 * @throws DomainException
852
+	 * @throws EE_Error
853
+	 * @throws EntityNotFoundException
854
+	 * @throws InvalidArgumentException
855
+	 * @throws InvalidDataTypeException
856
+	 * @throws InvalidInterfaceException
857
+	 * @throws ReflectionException
858
+	 * @throws RuntimeException
859
+	 * @throws UnexpectedEntityException
860
+	 */
861
+	public function set_att_is_going($REG_att_is_going = false)
862
+	{
863
+		$this->set('REG_att_is_going', $REG_att_is_going);
864
+	}
865
+
866
+
867
+	/**
868
+	 * Gets the related attendee
869
+	 *
870
+	 * @return EE_Attendee|EE_Base_Class
871
+	 * @throws EE_Error
872
+	 * @throws InvalidArgumentException
873
+	 * @throws InvalidDataTypeException
874
+	 * @throws InvalidInterfaceException
875
+	 * @throws ReflectionException
876
+	 */
877
+	public function attendee()
878
+	{
879
+		return $this->get_first_related('Attendee');
880
+	}
881
+
882
+
883
+	/**
884
+	 * Gets the name of the attendee.
885
+	 *
886
+	 * @param bool $apply_html_entities set to true if you want to use HTML entities.
887
+	 * @return string
888
+	 * @throws EE_Error
889
+	 * @throws InvalidArgumentException
890
+	 * @throws InvalidDataTypeException
891
+	 * @throws InvalidInterfaceException
892
+	 * @throws ReflectionException
893
+	 * @since 4.10.12.p
894
+	 */
895
+	public function attendeeName($apply_html_entities = false)
896
+	{
897
+		$attendee = $this->get_first_related('Attendee');
898
+		if ($attendee instanceof EE_Attendee) {
899
+			$attendee_name = $attendee->full_name($apply_html_entities);
900
+		} else {
901
+			$attendee_name = esc_html__('Unknown', 'event_espresso');
902
+		}
903
+		return $attendee_name;
904
+	}
905
+
906
+
907
+	/**
908
+	 *        get Event ID
909
+	 */
910
+	public function event_ID()
911
+	{
912
+		return $this->get('EVT_ID');
913
+	}
914
+
915
+
916
+	/**
917
+	 *        get Event ID
918
+	 */
919
+	public function event_name()
920
+	{
921
+		$event = $this->event_obj();
922
+		if ($event) {
923
+			return $event->name();
924
+		} else {
925
+			return null;
926
+		}
927
+	}
928
+
929
+
930
+	/**
931
+	 * Fetches the event this registration is for
932
+	 *
933
+	 * @return EE_Base_Class|EE_Event
934
+	 * @throws EE_Error
935
+	 * @throws InvalidArgumentException
936
+	 * @throws InvalidDataTypeException
937
+	 * @throws InvalidInterfaceException
938
+	 * @throws ReflectionException
939
+	 */
940
+	public function event_obj()
941
+	{
942
+		return $this->get_first_related('Event');
943
+	}
944
+
945
+
946
+	/**
947
+	 *        get Attendee ID
948
+	 */
949
+	public function attendee_ID()
950
+	{
951
+		return $this->get('ATT_ID');
952
+	}
953
+
954
+
955
+	/**
956
+	 *        get PHP Session ID
957
+	 */
958
+	public function session_ID()
959
+	{
960
+		return $this->get('REG_session');
961
+	}
962
+
963
+
964
+	/**
965
+	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
966
+	 *
967
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
968
+	 * @return string
969
+	 * @throws DomainException
970
+	 * @throws InvalidArgumentException
971
+	 * @throws InvalidDataTypeException
972
+	 * @throws InvalidInterfaceException
973
+	 */
974
+	public function receipt_url($messenger = 'html')
975
+	{
976
+		return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
977
+	}
978
+
979
+
980
+	/**
981
+	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
982
+	 *
983
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
984
+	 * @return string
985
+	 * @throws DomainException
986
+	 * @throws InvalidArgumentException
987
+	 * @throws InvalidDataTypeException
988
+	 * @throws InvalidInterfaceException
989
+	 */
990
+	public function invoice_url($messenger = 'html')
991
+	{
992
+		return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
993
+	}
994
+
995
+
996
+	/**
997
+	 * get Registration URL Link
998
+	 *
999
+	 * @return string
1000
+	 * @throws EE_Error
1001
+	 * @throws InvalidArgumentException
1002
+	 * @throws InvalidDataTypeException
1003
+	 * @throws InvalidInterfaceException
1004
+	 * @throws ReflectionException
1005
+	 */
1006
+	public function reg_url_link()
1007
+	{
1008
+		return (string) $this->get('REG_url_link');
1009
+	}
1010
+
1011
+
1012
+	/**
1013
+	 * Echoes out invoice_url()
1014
+	 *
1015
+	 * @param string $type 'download','launch', or 'html' (default is 'launch')
1016
+	 * @return void
1017
+	 * @throws DomainException
1018
+	 * @throws EE_Error
1019
+	 * @throws InvalidArgumentException
1020
+	 * @throws InvalidDataTypeException
1021
+	 * @throws InvalidInterfaceException
1022
+	 * @throws ReflectionException
1023
+	 */
1024
+	public function e_invoice_url($type = 'launch')
1025
+	{
1026
+		echo esc_url_raw($this->invoice_url($type));
1027
+	}
1028
+
1029
+
1030
+	/**
1031
+	 * Echoes out payment_overview_url
1032
+	 */
1033
+	public function e_payment_overview_url()
1034
+	{
1035
+		echo esc_url_raw($this->payment_overview_url());
1036
+	}
1037
+
1038
+
1039
+	/**
1040
+	 * Gets the URL for the checkout payment options reg step
1041
+	 * with this registration's REG_url_link added as a query parameter
1042
+	 *
1043
+	 * @param bool $clear_session Set to true when you want to clear the session on revisiting the
1044
+	 *                            payment overview url.
1045
+	 * @return string
1046
+	 * @throws EE_Error
1047
+	 * @throws InvalidArgumentException
1048
+	 * @throws InvalidDataTypeException
1049
+	 * @throws InvalidInterfaceException
1050
+	 * @throws ReflectionException
1051
+	 */
1052
+	public function payment_overview_url($clear_session = false)
1053
+	{
1054
+		return add_query_arg(
1055
+			(array) apply_filters(
1056
+				'FHEE__EE_Registration__payment_overview_url__query_args',
1057
+				[
1058
+					'e_reg_url_link' => $this->reg_url_link(),
1059
+					'step'           => 'payment_options',
1060
+					'revisit'        => true,
1061
+					'clear_session'  => (bool) $clear_session,
1062
+				],
1063
+				$this
1064
+			),
1065
+			EE_Registry::instance()->CFG->core->reg_page_url()
1066
+		);
1067
+	}
1068
+
1069
+
1070
+	/**
1071
+	 * Gets the URL for the checkout attendee information reg step
1072
+	 * with this registration's REG_url_link added as a query parameter
1073
+	 *
1074
+	 * @return string
1075
+	 * @throws EE_Error
1076
+	 * @throws InvalidArgumentException
1077
+	 * @throws InvalidDataTypeException
1078
+	 * @throws InvalidInterfaceException
1079
+	 * @throws ReflectionException
1080
+	 */
1081
+	public function edit_attendee_information_url()
1082
+	{
1083
+		return add_query_arg(
1084
+			(array) apply_filters(
1085
+				'FHEE__EE_Registration__edit_attendee_information_url__query_args',
1086
+				[
1087
+					'e_reg_url_link' => $this->reg_url_link(),
1088
+					'step'           => 'attendee_information',
1089
+					'revisit'        => true,
1090
+				],
1091
+				$this
1092
+			),
1093
+			EE_Registry::instance()->CFG->core->reg_page_url()
1094
+		);
1095
+	}
1096
+
1097
+
1098
+	/**
1099
+	 * Simply generates and returns the appropriate admin_url link to edit this registration
1100
+	 *
1101
+	 * @return string
1102
+	 * @throws EE_Error
1103
+	 * @throws InvalidArgumentException
1104
+	 * @throws InvalidDataTypeException
1105
+	 * @throws InvalidInterfaceException
1106
+	 * @throws ReflectionException
1107
+	 */
1108
+	public function get_admin_edit_url()
1109
+	{
1110
+		return EEH_URL::add_query_args_and_nonce(
1111
+			[
1112
+				'page'    => 'espresso_registrations',
1113
+				'action'  => 'view_registration',
1114
+				'_REG_ID' => $this->ID(),
1115
+			],
1116
+			admin_url('admin.php')
1117
+		);
1118
+	}
1119
+
1120
+
1121
+	/**
1122
+	 * is_primary_registrant?
1123
+	 *
1124
+	 * @throws EE_Error
1125
+	 * @throws InvalidArgumentException
1126
+	 * @throws InvalidDataTypeException
1127
+	 * @throws InvalidInterfaceException
1128
+	 * @throws ReflectionException
1129
+	 */
1130
+	public function is_primary_registrant()
1131
+	{
1132
+		return (int) $this->get('REG_count') === 1;
1133
+	}
1134
+
1135
+
1136
+	/**
1137
+	 * This returns the primary registration object for this registration group (which may be this object).
1138
+	 *
1139
+	 * @return EE_Registration
1140
+	 * @throws EE_Error
1141
+	 * @throws InvalidArgumentException
1142
+	 * @throws InvalidDataTypeException
1143
+	 * @throws InvalidInterfaceException
1144
+	 * @throws ReflectionException
1145
+	 */
1146
+	public function get_primary_registration()
1147
+	{
1148
+		if ($this->is_primary_registrant()) {
1149
+			return $this;
1150
+		}
1151
+
1152
+		// k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1153
+		/** @var EE_Registration $primary_registrant */
1154
+		$primary_registrant = EEM_Registration::instance()->get_one(
1155
+			[
1156
+				[
1157
+					'TXN_ID'    => $this->transaction_ID(),
1158
+					'REG_count' => 1,
1159
+				],
1160
+			]
1161
+		);
1162
+		return $primary_registrant;
1163
+	}
1164
+
1165
+
1166
+	/**
1167
+	 * get  Attendee Number
1168
+	 *
1169
+	 * @throws EE_Error
1170
+	 * @throws InvalidArgumentException
1171
+	 * @throws InvalidDataTypeException
1172
+	 * @throws InvalidInterfaceException
1173
+	 * @throws ReflectionException
1174
+	 */
1175
+	public function count()
1176
+	{
1177
+		return $this->get('REG_count');
1178
+	}
1179
+
1180
+
1181
+	/**
1182
+	 * get Group Size
1183
+	 *
1184
+	 * @throws EE_Error
1185
+	 * @throws InvalidArgumentException
1186
+	 * @throws InvalidDataTypeException
1187
+	 * @throws InvalidInterfaceException
1188
+	 * @throws ReflectionException
1189
+	 */
1190
+	public function group_size()
1191
+	{
1192
+		return $this->get('REG_group_size');
1193
+	}
1194
+
1195
+
1196
+	/**
1197
+	 * get Registration Date
1198
+	 *
1199
+	 * @throws EE_Error
1200
+	 * @throws InvalidArgumentException
1201
+	 * @throws InvalidDataTypeException
1202
+	 * @throws InvalidInterfaceException
1203
+	 * @throws ReflectionException
1204
+	 */
1205
+	public function date()
1206
+	{
1207
+		return $this->get('REG_date');
1208
+	}
1209
+
1210
+
1211
+	/**
1212
+	 * gets a pretty date
1213
+	 *
1214
+	 * @param string $date_format
1215
+	 * @param string $time_format
1216
+	 * @return string
1217
+	 * @throws EE_Error
1218
+	 * @throws InvalidArgumentException
1219
+	 * @throws InvalidDataTypeException
1220
+	 * @throws InvalidInterfaceException
1221
+	 * @throws ReflectionException
1222
+	 */
1223
+	public function pretty_date($date_format = null, $time_format = null)
1224
+	{
1225
+		return $this->get_datetime('REG_date', $date_format, $time_format);
1226
+	}
1227
+
1228
+
1229
+	/**
1230
+	 * final_price
1231
+	 * the registration's share of the transaction total, so that the
1232
+	 * sum of all the transaction's REG_final_prices equal the transaction's total
1233
+	 *
1234
+	 * @return float
1235
+	 * @throws EE_Error
1236
+	 * @throws InvalidArgumentException
1237
+	 * @throws InvalidDataTypeException
1238
+	 * @throws InvalidInterfaceException
1239
+	 * @throws ReflectionException
1240
+	 */
1241
+	public function final_price(): float
1242
+	{
1243
+		return (float) $this->get('REG_final_price');
1244
+	}
1245
+
1246
+
1247
+	/**
1248
+	 * pretty_final_price
1249
+	 *  final price as formatted string, with correct decimal places and currency symbol
1250
+	 *
1251
+	 * @return string
1252
+	 * @throws EE_Error
1253
+	 * @throws InvalidArgumentException
1254
+	 * @throws InvalidDataTypeException
1255
+	 * @throws InvalidInterfaceException
1256
+	 * @throws ReflectionException
1257
+	 */
1258
+	public function pretty_final_price()
1259
+	{
1260
+		return $this->get_pretty('REG_final_price');
1261
+	}
1262
+
1263
+
1264
+	/**
1265
+	 * get paid (yeah)
1266
+	 *
1267
+	 * @return float
1268
+	 * @throws EE_Error
1269
+	 * @throws InvalidArgumentException
1270
+	 * @throws InvalidDataTypeException
1271
+	 * @throws InvalidInterfaceException
1272
+	 * @throws ReflectionException
1273
+	 */
1274
+	public function paid(): float
1275
+	{
1276
+		return (float) $this->get('REG_paid');
1277
+	}
1278
+
1279
+
1280
+	/**
1281
+	 * pretty_paid
1282
+	 *
1283
+	 * @return float
1284
+	 * @throws EE_Error
1285
+	 * @throws InvalidArgumentException
1286
+	 * @throws InvalidDataTypeException
1287
+	 * @throws InvalidInterfaceException
1288
+	 * @throws ReflectionException
1289
+	 */
1290
+	public function pretty_paid()
1291
+	{
1292
+		return $this->get_pretty('REG_paid');
1293
+	}
1294
+
1295
+
1296
+	/**
1297
+	 * owes_monies_and_can_pay
1298
+	 * whether or not this registration has monies owing and it's' status allows payment
1299
+	 *
1300
+	 * @param array $requires_payment list of registration statuses that allow a registrant to make a payment
1301
+	 * @return bool
1302
+	 * @throws EE_Error
1303
+	 * @throws InvalidArgumentException
1304
+	 * @throws InvalidDataTypeException
1305
+	 * @throws InvalidInterfaceException
1306
+	 * @throws ReflectionException
1307
+	 */
1308
+	public function owes_monies_and_can_pay($requires_payment = [])
1309
+	{
1310
+		// these reg statuses require payment (if event is not free)
1311
+		$requires_payment = ! empty($requires_payment)
1312
+			? $requires_payment
1313
+			: EEM_Registration::reg_statuses_that_allow_payment();
1314
+		if (
1315
+			$this->final_price() !== 0 &&
1316
+			$this->final_price() !== $this->paid() &&
1317
+			in_array($this->status_ID(), $requires_payment)
1318
+		) {
1319
+			return true;
1320
+		}
1321
+		return false;
1322
+	}
1323
+
1324
+
1325
+	/**
1326
+	 * Prints out the return value of $this->pretty_status()
1327
+	 *
1328
+	 * @param bool $show_icons
1329
+	 * @return void
1330
+	 * @throws EE_Error
1331
+	 * @throws InvalidArgumentException
1332
+	 * @throws InvalidDataTypeException
1333
+	 * @throws InvalidInterfaceException
1334
+	 * @throws ReflectionException
1335
+	 */
1336
+	public function e_pretty_status($show_icons = false)
1337
+	{
1338
+		echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
1339
+	}
1340
+
1341
+
1342
+	/**
1343
+	 * Returns a nice version of the status for displaying to customers
1344
+	 *
1345
+	 * @param bool $show_icons
1346
+	 * @return string
1347
+	 * @throws EE_Error
1348
+	 * @throws InvalidArgumentException
1349
+	 * @throws InvalidDataTypeException
1350
+	 * @throws InvalidInterfaceException
1351
+	 * @throws ReflectionException
1352
+	 */
1353
+	public function pretty_status($show_icons = false)
1354
+	{
1355
+		$status = EEM_Status::instance()->localized_status(
1356
+			[$this->status_ID() => esc_html__('unknown', 'event_espresso')],
1357
+			false,
1358
+			'sentence'
1359
+		);
1360
+		$icon   = '';
1361
+		switch ($this->status_ID()) {
1362
+			case EEM_Registration::status_id_approved:
1363
+				$icon = $show_icons
1364
+					? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1365
+					: '';
1366
+				break;
1367
+			case EEM_Registration::status_id_pending_payment:
1368
+				$icon = $show_icons
1369
+					? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1370
+					: '';
1371
+				break;
1372
+			case EEM_Registration::status_id_not_approved:
1373
+				$icon = $show_icons
1374
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1375
+					: '';
1376
+				break;
1377
+			case EEM_Registration::status_id_cancelled:
1378
+				$icon = $show_icons
1379
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1380
+					: '';
1381
+				break;
1382
+			case EEM_Registration::status_id_incomplete:
1383
+				$icon = $show_icons
1384
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1385
+					: '';
1386
+				break;
1387
+			case EEM_Registration::status_id_declined:
1388
+				$icon = $show_icons
1389
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1390
+					: '';
1391
+				break;
1392
+			case EEM_Registration::status_id_wait_list:
1393
+				$icon = $show_icons
1394
+					? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1395
+					: '';
1396
+				break;
1397
+		}
1398
+		return $icon . $status[ $this->status_ID() ];
1399
+	}
1400
+
1401
+
1402
+	/**
1403
+	 *        get Attendee Is Going
1404
+	 */
1405
+	public function att_is_going()
1406
+	{
1407
+		return $this->get('REG_att_is_going');
1408
+	}
1409
+
1410
+
1411
+	/**
1412
+	 * Gets related answers
1413
+	 *
1414
+	 * @param array $query_params @see
1415
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1416
+	 * @return EE_Answer[]|EE_Base_Class[]
1417
+	 * @throws EE_Error
1418
+	 * @throws InvalidArgumentException
1419
+	 * @throws InvalidDataTypeException
1420
+	 * @throws InvalidInterfaceException
1421
+	 * @throws ReflectionException
1422
+	 */
1423
+	public function answers($query_params = [])
1424
+	{
1425
+		return $this->get_many_related('Answer', $query_params);
1426
+	}
1427
+
1428
+
1429
+	/**
1430
+	 * Gets the registration's answer value to the specified question
1431
+	 * (either the question's ID or a question object)
1432
+	 *
1433
+	 * @param EE_Question|int $question
1434
+	 * @param bool            $pretty_value
1435
+	 * @return array|string if pretty_value= true, the result will always be a string
1436
+	 * (because the answer might be an array of answer values, so passing pretty_value=true
1437
+	 * will convert it into some kind of string)
1438
+	 * @throws EE_Error
1439
+	 * @throws InvalidArgumentException
1440
+	 * @throws InvalidDataTypeException
1441
+	 * @throws InvalidInterfaceException
1442
+	 */
1443
+	public function answer_value_to_question($question, $pretty_value = true)
1444
+	{
1445
+		$question_id = EEM_Question::instance()->ensure_is_ID($question);
1446
+		return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1447
+	}
1448
+
1449
+
1450
+	/**
1451
+	 * question_groups
1452
+	 * returns an array of EE_Question_Group objects for this registration
1453
+	 *
1454
+	 * @return EE_Question_Group[]
1455
+	 * @throws EE_Error
1456
+	 * @throws InvalidArgumentException
1457
+	 * @throws InvalidDataTypeException
1458
+	 * @throws InvalidInterfaceException
1459
+	 * @throws ReflectionException
1460
+	 */
1461
+	public function question_groups()
1462
+	{
1463
+		return EEM_Event::instance()->get_question_groups_for_event($this->event_ID(), $this);
1464
+	}
1465
+
1466
+
1467
+	/**
1468
+	 * count_question_groups
1469
+	 * returns a count of the number of EE_Question_Group objects for this registration
1470
+	 *
1471
+	 * @return int
1472
+	 * @throws EE_Error
1473
+	 * @throws EntityNotFoundException
1474
+	 * @throws InvalidArgumentException
1475
+	 * @throws InvalidDataTypeException
1476
+	 * @throws InvalidInterfaceException
1477
+	 * @throws ReflectionException
1478
+	 */
1479
+	public function count_question_groups()
1480
+	{
1481
+		return EEM_Event::instance()->count_related(
1482
+			$this->event_ID(),
1483
+			'Question_Group',
1484
+			[
1485
+				[
1486
+					'Event_Question_Group.'
1487
+					. EEM_Event_Question_Group::instance()->fieldNameForContext($this->is_primary_registrant()) => true,
1488
+				],
1489
+			]
1490
+		);
1491
+	}
1492
+
1493
+
1494
+	/**
1495
+	 * Returns the registration date in the 'standard' string format
1496
+	 * (function may be improved in the future to allow for different formats and timezones)
1497
+	 *
1498
+	 * @return string
1499
+	 * @throws EE_Error
1500
+	 * @throws InvalidArgumentException
1501
+	 * @throws InvalidDataTypeException
1502
+	 * @throws InvalidInterfaceException
1503
+	 * @throws ReflectionException
1504
+	 */
1505
+	public function reg_date()
1506
+	{
1507
+		return $this->get_datetime('REG_date');
1508
+	}
1509
+
1510
+
1511
+	/**
1512
+	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1513
+	 * the ticket this registration purchased, or the datetime they have registered
1514
+	 * to attend)
1515
+	 *
1516
+	 * @return EE_Base_Class|EE_Datetime_Ticket
1517
+	 * @throws EE_Error
1518
+	 * @throws InvalidArgumentException
1519
+	 * @throws InvalidDataTypeException
1520
+	 * @throws InvalidInterfaceException
1521
+	 * @throws ReflectionException
1522
+	 */
1523
+	public function datetime_ticket()
1524
+	{
1525
+		return $this->get_first_related('Datetime_Ticket');
1526
+	}
1527
+
1528
+
1529
+	/**
1530
+	 * Sets the registration's datetime_ticket.
1531
+	 *
1532
+	 * @param EE_Datetime_Ticket $datetime_ticket
1533
+	 * @return EE_Base_Class|EE_Datetime_Ticket
1534
+	 * @throws EE_Error
1535
+	 * @throws InvalidArgumentException
1536
+	 * @throws InvalidDataTypeException
1537
+	 * @throws InvalidInterfaceException
1538
+	 * @throws ReflectionException
1539
+	 */
1540
+	public function set_datetime_ticket($datetime_ticket)
1541
+	{
1542
+		return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1543
+	}
1544
+
1545
+
1546
+	/**
1547
+	 * Gets deleted
1548
+	 *
1549
+	 * @return bool
1550
+	 * @throws EE_Error
1551
+	 * @throws InvalidArgumentException
1552
+	 * @throws InvalidDataTypeException
1553
+	 * @throws InvalidInterfaceException
1554
+	 * @throws ReflectionException
1555
+	 */
1556
+	public function deleted()
1557
+	{
1558
+		return $this->get('REG_deleted');
1559
+	}
1560
+
1561
+
1562
+	/**
1563
+	 * Sets deleted
1564
+	 *
1565
+	 * @param boolean $deleted
1566
+	 * @return void
1567
+	 * @throws DomainException
1568
+	 * @throws EE_Error
1569
+	 * @throws EntityNotFoundException
1570
+	 * @throws InvalidArgumentException
1571
+	 * @throws InvalidDataTypeException
1572
+	 * @throws InvalidInterfaceException
1573
+	 * @throws ReflectionException
1574
+	 * @throws RuntimeException
1575
+	 * @throws UnexpectedEntityException
1576
+	 */
1577
+	public function set_deleted($deleted)
1578
+	{
1579
+		if ($deleted) {
1580
+			$this->delete();
1581
+		} else {
1582
+			$this->restore();
1583
+		}
1584
+	}
1585
+
1586
+
1587
+	/**
1588
+	 * Get the status object of this object
1589
+	 *
1590
+	 * @return EE_Base_Class|EE_Status
1591
+	 * @throws EE_Error
1592
+	 * @throws InvalidArgumentException
1593
+	 * @throws InvalidDataTypeException
1594
+	 * @throws InvalidInterfaceException
1595
+	 * @throws ReflectionException
1596
+	 */
1597
+	public function status_obj()
1598
+	{
1599
+		return $this->get_first_related('Status');
1600
+	}
1601
+
1602
+
1603
+	/**
1604
+	 * Returns the number of times this registration has checked into any of the datetimes
1605
+	 * its available for
1606
+	 *
1607
+	 * @return int
1608
+	 * @throws EE_Error
1609
+	 * @throws InvalidArgumentException
1610
+	 * @throws InvalidDataTypeException
1611
+	 * @throws InvalidInterfaceException
1612
+	 * @throws ReflectionException
1613
+	 */
1614
+	public function count_checkins()
1615
+	{
1616
+		return $this->get_model()->count_related($this, 'Checkin');
1617
+	}
1618
+
1619
+
1620
+	/**
1621
+	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1622
+	 * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1623
+	 *
1624
+	 * @return int
1625
+	 * @throws EE_Error
1626
+	 * @throws InvalidArgumentException
1627
+	 * @throws InvalidDataTypeException
1628
+	 * @throws InvalidInterfaceException
1629
+	 * @throws ReflectionException
1630
+	 */
1631
+	public function count_checkins_not_checkedout()
1632
+	{
1633
+		return $this->get_model()->count_related($this, 'Checkin', [['CHK_in' => 1]]);
1634
+	}
1635
+
1636
+
1637
+	/**
1638
+	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1639
+	 *
1640
+	 * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1641
+	 * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1642
+	 *                                          consider registration status as well as datetime access.
1643
+	 * @return bool
1644
+	 * @throws EE_Error
1645
+	 * @throws InvalidArgumentException
1646
+	 * @throws InvalidDataTypeException
1647
+	 * @throws InvalidInterfaceException
1648
+	 * @throws ReflectionException
1649
+	 */
1650
+	public function can_checkin($DTT_OR_ID, $check_approved = true)
1651
+	{
1652
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1653
+		// first check registration status
1654
+		if (! $DTT_ID || ($check_approved && ! $this->is_approved())) {
1655
+			return false;
1656
+		}
1657
+		// is there a datetime ticket that matches this dtt_ID?
1658
+		if (
1659
+			! (EEM_Datetime_Ticket::instance()->exists(
1660
+				[
1661
+					[
1662
+						'TKT_ID' => $this->get('TKT_ID'),
1663
+						'DTT_ID' => $DTT_ID,
1664
+					],
1665
+				]
1666
+			))
1667
+		) {
1668
+			return false;
1669
+		}
1670
+
1671
+		// final check is against TKT_uses
1672
+		return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1673
+	}
1674
+
1675
+
1676
+	/**
1677
+	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1678
+	 * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1679
+	 * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1680
+	 * then return false.  Otherwise return true.
1681
+	 *
1682
+	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1683
+	 * @return bool true means can checkin.  false means cannot checkin.
1684
+	 * @throws EE_Error
1685
+	 * @throws InvalidArgumentException
1686
+	 * @throws InvalidDataTypeException
1687
+	 * @throws InvalidInterfaceException
1688
+	 * @throws ReflectionException
1689
+	 */
1690
+	public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1691
+	{
1692
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1693
+
1694
+		if (! $DTT_ID) {
1695
+			return false;
1696
+		}
1697
+
1698
+		$max_uses = $this->ticket() instanceof EE_Ticket
1699
+			? $this->ticket()->uses()
1700
+			: EE_INF;
1701
+
1702
+		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1703
+		// check-in or not.
1704
+		if (! $max_uses || $max_uses === EE_INF) {
1705
+			return true;
1706
+		}
1707
+
1708
+		// does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1709
+		// go ahead and toggle.
1710
+		if (EEM_Checkin::instance()->exists([['REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID]])) {
1711
+			return true;
1712
+		}
1713
+
1714
+		// made it here so the last check is whether the number of checkins per unique datetime on this registration
1715
+		// disallows further check-ins.
1716
+		$count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1717
+			[
1718
+				[
1719
+					'REG_ID' => $this->ID(),
1720
+					'CHK_in' => true,
1721
+				],
1722
+			],
1723
+			'DTT_ID',
1724
+			true
1725
+		);
1726
+		// checkins have already reached their max number of uses
1727
+		// so registrant can NOT checkin
1728
+		if ($count_unique_dtt_checkins >= $max_uses) {
1729
+			EE_Error::add_error(
1730
+				esc_html__(
1731
+					'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1732
+					'event_espresso'
1733
+				),
1734
+				__FILE__,
1735
+				__FUNCTION__,
1736
+				__LINE__
1737
+			);
1738
+			return false;
1739
+		}
1740
+		return true;
1741
+	}
1742
+
1743
+
1744
+	/**
1745
+	 * toggle Check-in status for this registration
1746
+	 * Check-ins are toggled in the following order:
1747
+	 * never checked in -> checked in
1748
+	 * checked in -> checked out
1749
+	 * checked out -> checked in
1750
+	 *
1751
+	 * @param int  $DTT_ID  include specific datetime to toggle Check-in for.
1752
+	 *                      If not included or null, then it is assumed latest datetime is being toggled.
1753
+	 * @param bool $verify  If true then can_checkin() is used to verify whether the person
1754
+	 *                      can be checked in or not.  Otherwise this forces change in checkin status.
1755
+	 * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1756
+	 * @throws EE_Error
1757
+	 * @throws InvalidArgumentException
1758
+	 * @throws InvalidDataTypeException
1759
+	 * @throws InvalidInterfaceException
1760
+	 * @throws ReflectionException
1761
+	 */
1762
+	public function toggle_checkin_status($DTT_ID = null, $verify = false)
1763
+	{
1764
+		if (empty($DTT_ID)) {
1765
+			$datetime = $this->get_latest_related_datetime();
1766
+			$DTT_ID   = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1767
+			// verify the registration can checkin for the given DTT_ID
1768
+		} elseif (! $this->can_checkin($DTT_ID, $verify)) {
1769
+			EE_Error::add_error(
1770
+				sprintf(
1771
+					esc_html__(
1772
+						'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1773
+						'event_espresso'
1774
+					),
1775
+					$this->ID(),
1776
+					$DTT_ID
1777
+				),
1778
+				__FILE__,
1779
+				__FUNCTION__,
1780
+				__LINE__
1781
+			);
1782
+			return false;
1783
+		}
1784
+		$status_paths = [
1785
+			EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1786
+			EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1787
+			EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1788
+		];
1789
+		// start by getting the current status so we know what status we'll be changing to.
1790
+		$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1791
+		$status_to  = $status_paths[ $cur_status ];
1792
+		// database only records true for checked IN or false for checked OUT
1793
+		// no record ( null ) means checked in NEVER, but we obviously don't save that
1794
+		$new_status = $status_to === EE_Checkin::status_checked_in;
1795
+		// add relation - note Check-ins are always creating new rows
1796
+		// because we are keeping track of Check-ins over time.
1797
+		// Eventually we'll probably want to show a list table
1798
+		// for the individual Check-ins so that they can be managed.
1799
+		$checkin = EE_Checkin::new_instance(
1800
+			[
1801
+				'REG_ID' => $this->ID(),
1802
+				'DTT_ID' => $DTT_ID,
1803
+				'CHK_in' => $new_status,
1804
+			]
1805
+		);
1806
+		// if the record could not be saved then return false
1807
+		if ($checkin->save() === 0) {
1808
+			if (WP_DEBUG) {
1809
+				global $wpdb;
1810
+				$error = sprintf(
1811
+					esc_html__(
1812
+						'Registration check in update failed because of the following database error: %1$s%2$s',
1813
+						'event_espresso'
1814
+					),
1815
+					'<br />',
1816
+					$wpdb->last_error
1817
+				);
1818
+			} else {
1819
+				$error = esc_html__(
1820
+					'Registration check in update failed because of an unknown database error',
1821
+					'event_espresso'
1822
+				);
1823
+			}
1824
+			EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1825
+			return false;
1826
+		}
1827
+		// Fire a checked_in and checkout_out action.
1828
+		$checked_status = $status_to === EE_Checkin::status_checked_in
1829
+			? 'checked_in'
1830
+			: 'checked_out';
1831
+		do_action("AHEE__EE_Registration__toggle_checkin_status__{$checked_status}", $this, $DTT_ID);
1832
+		return $status_to;
1833
+	}
1834
+
1835
+
1836
+	/**
1837
+	 * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1838
+	 * "Latest" is defined by the `DTT_EVT_start` column.
1839
+	 *
1840
+	 * @return EE_Datetime|null
1841
+	 * @throws EE_Error
1842
+	 * @throws InvalidArgumentException
1843
+	 * @throws InvalidDataTypeException
1844
+	 * @throws InvalidInterfaceException
1845
+	 * @throws ReflectionException
1846
+	 */
1847
+	public function get_latest_related_datetime(): ?EE_Datetime
1848
+	{
1849
+		return EEM_Datetime::instance()->get_one(
1850
+			[
1851
+				[
1852
+					'Ticket.Registration.REG_ID' => $this->ID(),
1853
+				],
1854
+				'order_by' => ['DTT_EVT_start' => 'DESC'],
1855
+			]
1856
+		);
1857
+	}
1858
+
1859
+
1860
+	/**
1861
+	 * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1862
+	 * "Earliest" is defined by the `DTT_EVT_start` column.
1863
+	 *
1864
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1865
+	 * @throws EE_Error
1866
+	 * @throws InvalidArgumentException
1867
+	 * @throws InvalidDataTypeException
1868
+	 * @throws InvalidInterfaceException
1869
+	 * @throws ReflectionException
1870
+	 */
1871
+	public function get_earliest_related_datetime()
1872
+	{
1873
+		return EEM_Datetime::instance()->get_one(
1874
+			[
1875
+				[
1876
+					'Ticket.Registration.REG_ID' => $this->ID(),
1877
+				],
1878
+				'order_by' => ['DTT_EVT_start' => 'ASC'],
1879
+			]
1880
+		);
1881
+	}
1882
+
1883
+
1884
+	/**
1885
+	 * This method simply returns the check-in status for this registration and the given datetime.
1886
+	 * If neither the datetime nor the checkin values are provided as arguments,
1887
+	 * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1888
+	 *
1889
+	 * @param int|null        $DTT_ID  The ID of the datetime we're checking against
1890
+	 *                                 (if empty we'll get the primary datetime for
1891
+	 *                                 this registration (via event) and use it's ID);
1892
+	 * @param EE_Checkin|null $checkin If present, we use the given checkin object rather than the dtt_id.
1893
+	 * @return int                     Integer representing Check-in status.
1894
+	 * @throws EE_Error
1895
+	 * @throws ReflectionException
1896
+	 */
1897
+	public function check_in_status_for_datetime(?int $DTT_ID = 0, ?EE_Checkin $checkin = null): int
1898
+	{
1899
+		if ($checkin instanceof EE_Checkin) {
1900
+			return $checkin->status();
1901
+		}
1902
+
1903
+		if (! $DTT_ID) {
1904
+			return EE_Checkin::status_invalid;
1905
+		}
1906
+
1907
+		$checkin_query_params = [
1908
+			0          => ['DTT_ID' => $DTT_ID],
1909
+			'order_by' => ['CHK_timestamp' => 'DESC'],
1910
+		];
1911
+
1912
+		$checkin = $this->get_first_related(
1913
+			'Checkin',
1914
+			$checkin_query_params
1915
+		);
1916
+		return $checkin instanceof EE_Checkin ? $checkin->status() : EE_Checkin::status_checked_never;
1917
+	}
1918
+
1919
+
1920
+	/**
1921
+	 * This method returns a localized message for the toggled Check-in message.
1922
+	 *
1923
+	 * @param int|null $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1924
+	 *                         then it is assumed Check-in for primary datetime was toggled.
1925
+	 * @param bool     $error  This just flags that you want an error message returned. This is put in so that the error
1926
+	 *                         message can be customized with the attendee name.
1927
+	 * @return string internationalized message
1928
+	 * @throws EE_Error
1929
+	 * @throws ReflectionException
1930
+	 */
1931
+	public function get_checkin_msg(?int $DTT_ID, bool $error = false): string
1932
+	{
1933
+		// let's get the attendee first so we can include the name of the attendee
1934
+		$attendee = $this->get_first_related('Attendee');
1935
+		if ($attendee instanceof EE_Attendee) {
1936
+			if ($error) {
1937
+				return sprintf(
1938
+					esc_html__("%s's check-in status was not changed.", "event_espresso"),
1939
+					$attendee->full_name()
1940
+				);
1941
+			}
1942
+			$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1943
+			// what is the status message going to be?
1944
+			switch ($cur_status) {
1945
+				case EE_Checkin::status_checked_never:
1946
+					return sprintf(
1947
+						esc_html__('%s has been removed from Check-in records', 'event_espresso'),
1948
+						$attendee->full_name()
1949
+					);
1950
+				case EE_Checkin::status_checked_in:
1951
+					return sprintf(esc_html__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1952
+				case EE_Checkin::status_checked_out:
1953
+					return sprintf(esc_html__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1954
+			}
1955
+		}
1956
+		return esc_html__('The check-in status could not be determined.', 'event_espresso');
1957
+	}
1958
+
1959
+
1960
+	/**
1961
+	 * Returns the related EE_Transaction to this registration
1962
+	 *
1963
+	 * @return EE_Transaction
1964
+	 * @throws EE_Error
1965
+	 * @throws EntityNotFoundException
1966
+	 * @throws ReflectionException
1967
+	 */
1968
+	public function transaction(): EE_Transaction
1969
+	{
1970
+		$transaction = $this->get_first_related('Transaction');
1971
+		if (! $transaction instanceof \EE_Transaction) {
1972
+			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1973
+		}
1974
+		return $transaction;
1975
+	}
1976
+
1977
+
1978
+	/**
1979
+	 * get Registration Code
1980
+	 *
1981
+	 * @return string
1982
+	 * @throws EE_Error
1983
+	 * @throws InvalidArgumentException
1984
+	 * @throws InvalidDataTypeException
1985
+	 * @throws InvalidInterfaceException
1986
+	 * @throws ReflectionException
1987
+	 */
1988
+	public function reg_code(): string
1989
+	{
1990
+		return $this->get('REG_code')
1991
+			?: '';
1992
+	}
1993
+
1994
+
1995
+	/**
1996
+	 * @return mixed
1997
+	 * @throws EE_Error
1998
+	 * @throws InvalidArgumentException
1999
+	 * @throws InvalidDataTypeException
2000
+	 * @throws InvalidInterfaceException
2001
+	 * @throws ReflectionException
2002
+	 */
2003
+	public function transaction_ID()
2004
+	{
2005
+		return $this->get('TXN_ID');
2006
+	}
2007
+
2008
+
2009
+	/**
2010
+	 * @return int
2011
+	 * @throws EE_Error
2012
+	 * @throws InvalidArgumentException
2013
+	 * @throws InvalidDataTypeException
2014
+	 * @throws InvalidInterfaceException
2015
+	 * @throws ReflectionException
2016
+	 */
2017
+	public function ticket_ID()
2018
+	{
2019
+		return $this->get('TKT_ID');
2020
+	}
2021
+
2022
+
2023
+	/**
2024
+	 * Set Registration Code
2025
+	 *
2026
+	 * @param RegCode|string $REG_code Registration Code
2027
+	 * @param boolean        $use_default
2028
+	 * @throws EE_Error
2029
+	 * @throws InvalidArgumentException
2030
+	 * @throws InvalidDataTypeException
2031
+	 * @throws InvalidInterfaceException
2032
+	 * @throws ReflectionException
2033
+	 */
2034
+	public function set_reg_code($REG_code, bool $use_default = false)
2035
+	{
2036
+		if (empty($REG_code)) {
2037
+			EE_Error::add_error(
2038
+				esc_html__('REG_code can not be empty.', 'event_espresso'),
2039
+				__FILE__,
2040
+				__FUNCTION__,
2041
+				__LINE__
2042
+			);
2043
+			return;
2044
+		}
2045
+		if (! $this->reg_code()) {
2046
+			parent::set('REG_code', $REG_code, $use_default);
2047
+		} else {
2048
+			EE_Error::doing_it_wrong(
2049
+				__CLASS__ . '::' . __FUNCTION__,
2050
+				esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
2051
+				'4.6.0'
2052
+			);
2053
+		}
2054
+	}
2055
+
2056
+
2057
+	/**
2058
+	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
2059
+	 * Note, if you want to just get all registrations in the same transaction (group), use:
2060
+	 *    $registration->transaction()->registrations();
2061
+	 *
2062
+	 * @return EE_Registration[] or empty array if this isn't a group registration.
2063
+	 * @throws EE_Error
2064
+	 * @throws InvalidArgumentException
2065
+	 * @throws InvalidDataTypeException
2066
+	 * @throws InvalidInterfaceException
2067
+	 * @throws ReflectionException
2068
+	 * @since 4.5.0
2069
+	 */
2070
+	public function get_all_other_registrations_in_group()
2071
+	{
2072
+		if ($this->group_size() < 2) {
2073
+			return [];
2074
+		}
2075
+
2076
+		$query[0] = [
2077
+			'TXN_ID' => $this->transaction_ID(),
2078
+			'REG_ID' => ['!=', $this->ID()],
2079
+			'TKT_ID' => $this->ticket_ID(),
2080
+		];
2081
+		/** @var EE_Registration[] $registrations */
2082
+		$registrations = $this->get_model()->get_all($query);
2083
+		return $registrations;
2084
+	}
2085
+
2086
+
2087
+	/**
2088
+	 * Return the link to the admin details for the object.
2089
+	 *
2090
+	 * @return string
2091
+	 * @throws EE_Error
2092
+	 * @throws InvalidArgumentException
2093
+	 * @throws InvalidDataTypeException
2094
+	 * @throws InvalidInterfaceException
2095
+	 * @throws ReflectionException
2096
+	 */
2097
+	public function get_admin_details_link()
2098
+	{
2099
+		EE_Registry::instance()->load_helper('URL');
2100
+		return EEH_URL::add_query_args_and_nonce(
2101
+			[
2102
+				'page'    => 'espresso_registrations',
2103
+				'action'  => 'view_registration',
2104
+				'_REG_ID' => $this->ID(),
2105
+			],
2106
+			admin_url('admin.php')
2107
+		);
2108
+	}
2109
+
2110
+
2111
+	/**
2112
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
2113
+	 *
2114
+	 * @return string
2115
+	 * @throws EE_Error
2116
+	 * @throws InvalidArgumentException
2117
+	 * @throws InvalidDataTypeException
2118
+	 * @throws InvalidInterfaceException
2119
+	 * @throws ReflectionException
2120
+	 */
2121
+	public function get_admin_edit_link()
2122
+	{
2123
+		return $this->get_admin_details_link();
2124
+	}
2125
+
2126
+
2127
+	/**
2128
+	 * Returns the link to a settings page for the object.
2129
+	 *
2130
+	 * @return string
2131
+	 * @throws EE_Error
2132
+	 * @throws InvalidArgumentException
2133
+	 * @throws InvalidDataTypeException
2134
+	 * @throws InvalidInterfaceException
2135
+	 * @throws ReflectionException
2136
+	 */
2137
+	public function get_admin_settings_link()
2138
+	{
2139
+		return $this->get_admin_details_link();
2140
+	}
2141
+
2142
+
2143
+	/**
2144
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
2145
+	 *
2146
+	 * @return string
2147
+	 * @throws EE_Error
2148
+	 * @throws InvalidArgumentException
2149
+	 * @throws InvalidDataTypeException
2150
+	 * @throws InvalidInterfaceException
2151
+	 * @throws ReflectionException
2152
+	 */
2153
+	public function get_admin_overview_link()
2154
+	{
2155
+		EE_Registry::instance()->load_helper('URL');
2156
+		return EEH_URL::add_query_args_and_nonce(
2157
+			[
2158
+				'page' => 'espresso_registrations',
2159
+			],
2160
+			admin_url('admin.php')
2161
+		);
2162
+	}
2163
+
2164
+
2165
+	/**
2166
+	 * @param array $query_params
2167
+	 * @return EE_Base_Class[]|EE_Registration[]
2168
+	 * @throws EE_Error
2169
+	 * @throws InvalidArgumentException
2170
+	 * @throws InvalidDataTypeException
2171
+	 * @throws InvalidInterfaceException
2172
+	 * @throws ReflectionException
2173
+	 */
2174
+	public function payments($query_params = [])
2175
+	{
2176
+		return $this->get_many_related('Payment', $query_params);
2177
+	}
2178
+
2179
+
2180
+	/**
2181
+	 * @param array $query_params
2182
+	 * @return EE_Base_Class[]|EE_Registration_Payment[]
2183
+	 * @throws EE_Error
2184
+	 * @throws InvalidArgumentException
2185
+	 * @throws InvalidDataTypeException
2186
+	 * @throws InvalidInterfaceException
2187
+	 * @throws ReflectionException
2188
+	 */
2189
+	public function registration_payments($query_params = [])
2190
+	{
2191
+		return $this->get_many_related('Registration_Payment', $query_params);
2192
+	}
2193
+
2194
+
2195
+	/**
2196
+	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
2197
+	 * Note: if there are no payments on the registration there will be no payment method returned.
2198
+	 *
2199
+	 * @return EE_Payment|EE_Payment_Method|null
2200
+	 * @throws EE_Error
2201
+	 * @throws InvalidArgumentException
2202
+	 * @throws InvalidDataTypeException
2203
+	 * @throws InvalidInterfaceException
2204
+	 */
2205
+	public function payment_method()
2206
+	{
2207
+		return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
2208
+	}
2209
+
2210
+
2211
+	/**
2212
+	 * @return \EE_Line_Item
2213
+	 * @throws EE_Error
2214
+	 * @throws EntityNotFoundException
2215
+	 * @throws InvalidArgumentException
2216
+	 * @throws InvalidDataTypeException
2217
+	 * @throws InvalidInterfaceException
2218
+	 * @throws ReflectionException
2219
+	 */
2220
+	public function ticket_line_item()
2221
+	{
2222
+		$ticket            = $this->ticket();
2223
+		$transaction       = $this->transaction();
2224
+		$line_item         = null;
2225
+		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
2226
+			$transaction->total_line_item(),
2227
+			'Ticket',
2228
+			[$ticket->ID()]
2229
+		);
2230
+		foreach ($ticket_line_items as $ticket_line_item) {
2231
+			if (
2232
+				$ticket_line_item instanceof \EE_Line_Item
2233
+				&& $ticket_line_item->OBJ_type() === 'Ticket'
2234
+				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
2235
+			) {
2236
+				$line_item = $ticket_line_item;
2237
+				break;
2238
+			}
2239
+		}
2240
+		if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
2241
+			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
2242
+		}
2243
+		return $line_item;
2244
+	}
2245
+
2246
+
2247
+	/**
2248
+	 * Soft Deletes this model object.
2249
+	 *
2250
+	 * @param string $source function name that called this method
2251
+	 * @return boolean | int
2252
+	 * @throws DomainException
2253
+	 * @throws EE_Error
2254
+	 * @throws EntityNotFoundException
2255
+	 * @throws InvalidArgumentException
2256
+	 * @throws InvalidDataTypeException
2257
+	 * @throws InvalidInterfaceException
2258
+	 * @throws ReflectionException
2259
+	 * @throws RuntimeException
2260
+	 * @throws UnexpectedEntityException
2261
+	 */
2262
+	public function delete()
2263
+	{
2264
+		if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
2265
+			$this->set_status(EEM_Registration::status_id_cancelled);
2266
+		}
2267
+		return parent::delete();
2268
+	}
2269
+
2270
+
2271
+	/**
2272
+	 * Restores whatever the previous status was on a registration before it was trashed (if possible)
2273
+	 *
2274
+	 * @param string $source function name that called this method
2275
+	 * @return bool|int
2276
+	 * @throws DomainException
2277
+	 * @throws EE_Error
2278
+	 * @throws EntityNotFoundException
2279
+	 * @throws InvalidArgumentException
2280
+	 * @throws InvalidDataTypeException
2281
+	 * @throws InvalidInterfaceException
2282
+	 * @throws ReflectionException
2283
+	 * @throws RuntimeException
2284
+	 * @throws UnexpectedEntityException
2285
+	 */
2286
+	public function restore()
2287
+	{
2288
+		$previous_status = $this->get_extra_meta(
2289
+			EE_Registration::PRE_TRASH_REG_STATUS_KEY,
2290
+			true,
2291
+			EEM_Registration::status_id_cancelled
2292
+		);
2293
+		if ($previous_status) {
2294
+			$this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
2295
+			$this->set_status($previous_status);
2296
+		}
2297
+		return parent::restore();
2298
+	}
2299
+
2300
+
2301
+	/**
2302
+	 * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
2303
+	 *
2304
+	 * @param boolean $trigger_set_status_logic  EE_Registration::set_status() can trigger additional logic
2305
+	 *                                           depending on whether the reg status changes to or from "Approved"
2306
+	 * @return boolean whether the Registration status was updated
2307
+	 * @throws DomainException
2308
+	 * @throws EE_Error
2309
+	 * @throws EntityNotFoundException
2310
+	 * @throws InvalidArgumentException
2311
+	 * @throws InvalidDataTypeException
2312
+	 * @throws InvalidInterfaceException
2313
+	 * @throws ReflectionException
2314
+	 * @throws RuntimeException
2315
+	 * @throws UnexpectedEntityException
2316
+	 */
2317
+	public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
2318
+	{
2319
+		$paid  = $this->paid();
2320
+		$price = $this->final_price();
2321
+		switch (true) {
2322
+			// overpaid or paid
2323
+			case EEH_Money::compare_floats($paid, $price, '>'):
2324
+			case EEH_Money::compare_floats($paid, $price):
2325
+				$new_status = EEM_Registration::status_id_approved;
2326
+				break;
2327
+			//  underpaid
2328
+			case EEH_Money::compare_floats($paid, $price, '<'):
2329
+				$new_status = EEM_Registration::status_id_pending_payment;
2330
+				break;
2331
+			// uhhh Houston...
2332
+			default:
2333
+				throw new RuntimeException(
2334
+					esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
2335
+				);
2336
+		}
2337
+		if ($new_status !== $this->status_ID()) {
2338
+			if ($trigger_set_status_logic) {
2339
+				return $this->set_status($new_status);
2340
+			}
2341
+			parent::set('STS_ID', $new_status);
2342
+			return true;
2343
+		}
2344
+		return false;
2345
+	}
2346
+
2347
+
2348
+	/*************************** DEPRECATED ***************************/
2349
+
2350
+
2351
+	/**
2352
+	 * @deprecated
2353
+	 * @since     4.7.0
2354
+	 */
2355
+	public function price_paid()
2356
+	{
2357
+		EE_Error::doing_it_wrong(
2358
+			'EE_Registration::price_paid()',
2359
+			esc_html__(
2360
+				'This method is deprecated, please use EE_Registration::final_price() instead.',
2361
+				'event_espresso'
2362
+			),
2363
+			'4.7.0'
2364
+		);
2365
+		return $this->final_price();
2366
+	}
2367
+
2368
+
2369
+	/**
2370
+	 * @param float $REG_final_price
2371
+	 * @throws EE_Error
2372
+	 * @throws EntityNotFoundException
2373
+	 * @throws InvalidArgumentException
2374
+	 * @throws InvalidDataTypeException
2375
+	 * @throws InvalidInterfaceException
2376
+	 * @throws ReflectionException
2377
+	 * @throws RuntimeException
2378
+	 * @throws DomainException
2379
+	 * @deprecated
2380
+	 * @since     4.7.0
2381
+	 */
2382
+	public function set_price_paid($REG_final_price = 0.00)
2383
+	{
2384
+		EE_Error::doing_it_wrong(
2385
+			'EE_Registration::set_price_paid()',
2386
+			esc_html__(
2387
+				'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2388
+				'event_espresso'
2389
+			),
2390
+			'4.7.0'
2391
+		);
2392
+		$this->set_final_price($REG_final_price);
2393
+	}
2394
+
2395
+
2396
+	/**
2397
+	 * @return string
2398
+	 * @throws EE_Error
2399
+	 * @throws InvalidArgumentException
2400
+	 * @throws InvalidDataTypeException
2401
+	 * @throws InvalidInterfaceException
2402
+	 * @throws ReflectionException
2403
+	 * @deprecated
2404
+	 * @since 4.7.0
2405
+	 */
2406
+	public function pretty_price_paid()
2407
+	{
2408
+		EE_Error::doing_it_wrong(
2409
+			'EE_Registration::pretty_price_paid()',
2410
+			esc_html__(
2411
+				'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2412
+				'event_espresso'
2413
+			),
2414
+			'4.7.0'
2415
+		);
2416
+		return $this->pretty_final_price();
2417
+	}
2418
+
2419
+
2420
+	/**
2421
+	 * Gets the primary datetime related to this registration via the related Event to this registration
2422
+	 *
2423
+	 * @return EE_Datetime
2424
+	 * @throws EE_Error
2425
+	 * @throws EntityNotFoundException
2426
+	 * @throws InvalidArgumentException
2427
+	 * @throws InvalidDataTypeException
2428
+	 * @throws InvalidInterfaceException
2429
+	 * @throws ReflectionException
2430
+	 * @deprecated 4.9.17
2431
+	 */
2432
+	public function get_related_primary_datetime()
2433
+	{
2434
+		EE_Error::doing_it_wrong(
2435
+			__METHOD__,
2436
+			esc_html__(
2437
+				'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2438
+				'event_espresso'
2439
+			),
2440
+			'4.9.17',
2441
+			'5.0.0'
2442
+		);
2443
+		return $this->event()->primary_datetime();
2444
+	}
2445
+
2446
+
2447
+	/**
2448
+	 * Returns the contact's name (or "Unknown" if there is no contact.)
2449
+	 *
2450
+	 * @return string
2451
+	 * @throws EE_Error
2452
+	 * @throws InvalidArgumentException
2453
+	 * @throws InvalidDataTypeException
2454
+	 * @throws InvalidInterfaceException
2455
+	 * @throws ReflectionException
2456
+	 * @since 4.10.12.p
2457
+	 */
2458
+	public function name()
2459
+	{
2460
+		return $this->attendeeName();
2461
+	}
2462
+
2463
+
2464
+	/**
2465
+	 * @return bool
2466
+	 * @throws EE_Error
2467
+	 * @throws ReflectionException
2468
+	 */
2469
+	public function wasMoved(): bool
2470
+	{
2471
+		// only need to check 'registration-moved-to' because
2472
+		// the existence of a new REG ID means the registration was moved
2473
+		$reg_moved = $this->get_extra_meta('registration-moved-to', true, []);
2474
+		return isset($reg_moved['NEW_REG_ID']) && $reg_moved['NEW_REG_ID'];
2475
+	}
2476
+
2477
+
2478
+	/**
2479
+	 * @param EE_Payment $payment
2480
+	 * @param float|null $amount
2481
+	 * @return float
2482
+	 * @throws EE_Error
2483
+	 * @throws ReflectionException
2484
+	 * @since 5.0.8.p
2485
+	 */
2486
+	public function applyPayment(EE_Payment $payment, ?float $amount = null): float
2487
+	{
2488
+		// echo "\n\n";
2489
+		// \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 3);
2490
+		// \EEH_Debug_Tools::printr($this->ID(), 'REG ID', __FILE__, __LINE__);
2491
+		$payment_amount = $amount ?? $payment->amount();
2492
+		// ensure $payment_amount is NOT negative
2493
+		$payment_amount = (float) abs($payment_amount);
2494
+		// \EEH_Debug_Tools::printr($payment_amount, 'incoming $payment_amount', __FILE__, __LINE__);
2495
+		// \EEH_Debug_Tools::printr($this->final_price(), 'reg final price', __FILE__, __LINE__);
2496
+		// \EEH_Debug_Tools::printr($this->paid(), 'reg paid to date', __FILE__, __LINE__);
2497
+		$payment_amount = $payment->is_a_refund()
2498
+			? $this->processRefund($payment_amount)
2499
+			: $this->processPayment($payment_amount);
2500
+		// \EEH_Debug_Tools::printr($payment_amount, 'applied payment_amount', __FILE__, __LINE__);
2501
+		if ($payment_amount) {
2502
+			$reg_payment = EEM_Registration_Payment::instance()->get_one(
2503
+				[['REG_ID' => $this->ID(), 'PAY_ID' => $payment->ID()]]
2504
+			);
2505
+			// if existing registration payment exists
2506
+			if ($reg_payment instanceof EE_Registration_Payment) {
2507
+				// echo "\nUPDATE EXISTING REG PAYMENT";
2508
+				// then update that record
2509
+				$reg_payment->set_amount($payment_amount);
2510
+			} else {
2511
+				// echo "\nCREATE NEW REG PAYMENT";
2512
+				// or add new relation between registration and payment and set amount
2513
+				$reg_payment = EE_Registration_Payment::new_instance(
2514
+					[
2515
+						'REG_ID'     => $this->ID(),
2516
+						'PAY_ID'     => $payment->ID(),
2517
+						'RPY_amount' => $payment_amount,
2518
+					]
2519
+				);
2520
+				// $this->_add_relation_to($payment, 'Payment', ['RPY_amount' => $payment_amount]);
2521
+			}
2522
+			$reg_payment->save();
2523
+			// \EEH_Debug_Tools::printr($reg_payment->ID(), '$reg payment ID', __FILE__, __LINE__);
2524
+		}
2525
+		return $payment_amount;
2526
+	}
2527
+
2528
+
2529
+	/**
2530
+	 * @throws EE_Error
2531
+	 * @throws ReflectionException
2532
+	 */
2533
+	private function processPayment(float $payment_amount): float
2534
+	{
2535
+		// echo "\n";
2536
+		// \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 3);
2537
+		$paid  = $this->paid();
2538
+		$owing = $this->final_price() - $paid;
2539
+		// \EEH_Debug_Tools::printr($owing, '$owing', __FILE__, __LINE__);
2540
+		if ($owing <= 0) {
2541
+			return 0.0;
2542
+		}
2543
+		// don't allow payment amount to exceed the incoming amount, OR the amount owing
2544
+		$payment_amount = min($payment_amount, $owing);
2545
+		$paid           = $paid + $payment_amount;
2546
+		// \EEH_Debug_Tools::printr($paid, 'NEW REG PAID AMOUNT', __FILE__, __LINE__);
2547
+		// calculate and set new REG_paid
2548
+		$this->set_paid($paid);
2549
+		// make it stick
2550
+		$this->save();
2551
+		return (float) $payment_amount;
2552
+	}
2553
+
2554
+
2555
+	/**
2556
+	 * @throws ReflectionException
2557
+	 * @throws EE_Error
2558
+	 */
2559
+	private function processRefund(float $payment_amount): float
2560
+	{
2561
+		// echo "\n";
2562
+		// \EEH_Debug_Tools::printr(__FUNCTION__, __CLASS__, __FILE__, __LINE__, 3);
2563
+		$paid = $this->paid();
2564
+		if ($paid <= 0) {
2565
+			return 0.0;
2566
+		}
2567
+		// don't allow refund amount to exceed the incoming amount, OR the amount paid
2568
+		$payment_amount = min($payment_amount, $paid);
2569
+		// calculate and set new REG_paid
2570
+		$paid = $paid - $payment_amount;
2571
+		\EEH_Debug_Tools::printr($paid, 'NEW REG PAID AMOUNT', __FILE__, __LINE__);
2572
+		$this->set_paid($paid);
2573
+		// make it stick
2574
+		$this->save();
2575
+		// convert payment amount back to a negative value for storage in the db
2576
+		return (float) $payment_amount;
2577
+	}
2578 2578
 }
Please login to merge, or discard this patch.