Completed
Branch FET/event-question-group-refac... (7e8a15)
by
unknown
27:06 queued 18:26
created
core/db_models/relations/EE_HABTM_Relation.php 2 patches
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -65,18 +65,18 @@  discard block
 block discarded – undo
65 65
         // remember the model relation chain to the JOIN model, because we'll
66 66
         // need it for get_join_statement()
67 67
         $this->_model_relation_chain_to_join_model = $model_relation_chain;
68
-        $this_table_pk_field                       = $this->get_this_model()->get_primary_key_field();// get_foreign_key_to($this->get_other_model()->get_this_model_name());
68
+        $this_table_pk_field                       = $this->get_this_model()->get_primary_key_field(); // get_foreign_key_to($this->get_other_model()->get_this_model_name());
69 69
         $join_table_fk_field_to_this_table         = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
70 70
         $this_table_alias                          = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
71 71
             $model_relation_chain,
72 72
             $this->get_this_model()->get_this_model_name()
73
-        ) . $this_table_pk_field->get_table_alias();
73
+        ).$this_table_pk_field->get_table_alias();
74 74
 
75 75
         $join_table_alias = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
76 76
             $model_relation_chain,
77 77
             $this->get_join_model()->get_this_model_name()
78
-        ) . $join_table_fk_field_to_this_table->get_table_alias();
79
-        $join_table       = $this->get_join_model()->get_table_for_alias($join_table_alias);
78
+        ).$join_table_fk_field_to_this_table->get_table_alias();
79
+        $join_table = $this->get_join_model()->get_table_for_alias($join_table_alias);
80 80
         // phew! ok, we have all the info we need, now we can create the SQL join string
81 81
         $SQL = $this->_left_join(
82 82
             $join_table,
@@ -84,7 +84,7 @@  discard block
 block discarded – undo
84 84
             $join_table_fk_field_to_this_table->get_table_column(),
85 85
             $this_table_alias,
86 86
             $this_table_pk_field->get_table_column()
87
-        ) . $this->get_join_model()->_construct_internal_join_to_table_with_alias($join_table_alias);
87
+        ).$this->get_join_model()->_construct_internal_join_to_table_with_alias($join_table_alias);
88 88
 
89 89
         return $SQL;
90 90
     }
@@ -113,14 +113,14 @@  discard block
 block discarded – undo
113 113
         $join_table_alias                   = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
114 114
             $this->_model_relation_chain_to_join_model,
115 115
             $this->get_join_model()->get_this_model_name()
116
-        ) . $join_table_fk_field_to_this_table->get_table_alias();
116
+        ).$join_table_fk_field_to_this_table->get_table_alias();
117 117
         $other_table_pk_field               = $this->get_other_model()->get_primary_key_field();
118 118
         $join_table_fk_field_to_other_table = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
119 119
         $other_table_alias                  = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
120 120
             $model_relation_chain,
121 121
             $this->get_other_model()->get_this_model_name()
122
-        ) . $other_table_pk_field->get_table_alias();
123
-        $other_table                        = $this->get_other_model()->get_table_for_alias($other_table_alias);
122
+        ).$other_table_pk_field->get_table_alias();
123
+        $other_table = $this->get_other_model()->get_table_for_alias($other_table_alias);
124 124
 
125 125
         $SQL = $this->_left_join(
126 126
             $other_table,
@@ -128,7 +128,7 @@  discard block
 block discarded – undo
128 128
             $other_table_pk_field->get_table_column(),
129 129
             $join_table_alias,
130 130
             $join_table_fk_field_to_other_table->get_table_column()
131
-        ) . $this->get_other_model()->_construct_internal_join_to_table_with_alias($other_table_alias);
131
+        ).$this->get_other_model()->_construct_internal_join_to_table_with_alias($other_table_alias);
132 132
         return $SQL;
133 133
     }
134 134
 
@@ -161,17 +161,17 @@  discard block
 block discarded – undo
161 161
         );
162 162
 
163 163
         // if $where_query exists lets add them to the query_params.
164
-        if (! empty($extra_join_model_fields_n_values)) {
164
+        if ( ! empty($extra_join_model_fields_n_values)) {
165 165
             // make sure we strip any of the join model names from the $where_query cause we don't need that in here (why? because client code may have used the same conditionals for get_all_related which DOES need the join model name)
166 166
             // make sure we strip THIS models name from the query param
167 167
             $parsed_query = array();
168 168
             foreach ($extra_join_model_fields_n_values as $query_param => $val) {
169
-                $query_param                = str_replace(
170
-                    $this->get_join_model()->get_this_model_name() . ".",
169
+                $query_param = str_replace(
170
+                    $this->get_join_model()->get_this_model_name().".",
171 171
                     "",
172 172
                     $query_param
173 173
                 );
174
-                $parsed_query[ $query_param ] = $val;
174
+                $parsed_query[$query_param] = $val;
175 175
             }
176 176
             $all_fields = array_merge($foreign_keys, $parsed_query);
177 177
         }
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
         // new row.
182 182
         // Again, if you want more sophisticated logic or insertions (handling more columns than just 2 foreign keys to
183 183
         // the other tables) use the joining model directly!
184
-        if (! $existing_entry_in_join_table) {
184
+        if ( ! $existing_entry_in_join_table) {
185 185
             $this->get_join_model()->insert($all_fields);
186 186
         } else {
187 187
             $this->get_join_model()->update(
@@ -217,17 +217,17 @@  discard block
 block discarded – undo
217 217
         );
218 218
 
219 219
         // if $where_query exists lets add them to the query_params.
220
-        if (! empty($where_query)) {
220
+        if ( ! empty($where_query)) {
221 221
             // make sure we strip any of the join model names from the $where_query cause we don't need that in here (why? because client code may have used the same conditionals for get_all_related which DOES need the join model name)
222 222
             // make sure we strip THIS models name from the query param
223 223
             $parsed_query = array();
224 224
             foreach ($where_query as $query_param => $val) {
225
-                $query_param                = str_replace(
226
-                    $this->get_join_model()->get_this_model_name() . ".",
225
+                $query_param = str_replace(
226
+                    $this->get_join_model()->get_this_model_name().".",
227 227
                     "",
228 228
                     $query_param
229 229
                 );
230
-                $parsed_query[ $query_param ] = $val;
230
+                $parsed_query[$query_param] = $val;
231 231
             }
232 232
             $cols_n_values = array_merge($cols_n_values, $parsed_query);
233 233
         }
Please login to merge, or discard this patch.
Indentation   +239 added lines, -239 removed lines patch added patch discarded remove patch
@@ -9,266 +9,266 @@
 block discarded – undo
9 9
  */
10 10
 class EE_HABTM_Relation extends EE_Model_Relation_Base
11 11
 {
12
-    /**
13
-     * Model which defines the relation between two other models. Eg, the EE_Event_Question_Group model,
14
-     * which joins EE_Event and EE_Question_Group
15
-     *
16
-     * @var EEM_Base
17
-     */
18
-    protected $_joining_model_name;
12
+	/**
13
+	 * Model which defines the relation between two other models. Eg, the EE_Event_Question_Group model,
14
+	 * which joins EE_Event and EE_Question_Group
15
+	 *
16
+	 * @var EEM_Base
17
+	 */
18
+	protected $_joining_model_name;
19 19
 
20
-    protected $_model_relation_chain_to_join_model;
20
+	protected $_model_relation_chain_to_join_model;
21 21
 
22 22
 
23
-    /**
24
-     * Object representing the relationship between two models. HasAndBelongsToMany relations always use a join-table
25
-     * (and an ee joining-model.) This knows how to join the models,
26
-     * get related models across the relation, and add-and-remove the relationships.
27
-     *
28
-     * @param bool    $joining_model_name
29
-     * @param boolean $block_deletes                 for this type of relation, we block by default for now. if there
30
-     *                                               are related models across this relation, block (prevent and add an
31
-     *                                               error) the deletion of this model
32
-     * @param string  $blocking_delete_error_message a customized error message on blocking deletes instead of the
33
-     *                                               default
34
-     */
35
-    public function __construct($joining_model_name, $block_deletes = true, $blocking_delete_error_message = '')
36
-    {
37
-        $this->_joining_model_name = $joining_model_name;
38
-        parent::__construct($block_deletes, $blocking_delete_error_message);
39
-    }
23
+	/**
24
+	 * Object representing the relationship between two models. HasAndBelongsToMany relations always use a join-table
25
+	 * (and an ee joining-model.) This knows how to join the models,
26
+	 * get related models across the relation, and add-and-remove the relationships.
27
+	 *
28
+	 * @param bool    $joining_model_name
29
+	 * @param boolean $block_deletes                 for this type of relation, we block by default for now. if there
30
+	 *                                               are related models across this relation, block (prevent and add an
31
+	 *                                               error) the deletion of this model
32
+	 * @param string  $blocking_delete_error_message a customized error message on blocking deletes instead of the
33
+	 *                                               default
34
+	 */
35
+	public function __construct($joining_model_name, $block_deletes = true, $blocking_delete_error_message = '')
36
+	{
37
+		$this->_joining_model_name = $joining_model_name;
38
+		parent::__construct($block_deletes, $blocking_delete_error_message);
39
+	}
40 40
 
41
-    /**
42
-     * Gets the joining model's object
43
-     *
44
-     * @return EEM_Base
45
-     */
46
-    public function get_join_model()
47
-    {
48
-        return $this->_get_model($this->_joining_model_name);
49
-    }
41
+	/**
42
+	 * Gets the joining model's object
43
+	 *
44
+	 * @return EEM_Base
45
+	 */
46
+	public function get_join_model()
47
+	{
48
+		return $this->_get_model($this->_joining_model_name);
49
+	}
50 50
 
51 51
 
52
-    /**
53
-     * Gets the SQL string for joining the main model's table containing the pk to the join table. Eg "LEFT JOIN
54
-     * real_join_table AS join_table_alias ON this_table_alias.pk = join_table_alias.fk_to_this_table"
55
-     *
56
-     * @param string $model_relation_chain like 'Event.Event_Venue.Venue'
57
-     * @return string of SQL
58
-     * @throws \EE_Error
59
-     */
60
-    public function get_join_to_intermediate_model_statement($model_relation_chain)
61
-    {
62
-        // create sql like
63
-        // LEFT JOIN join_table AS join_table_alias ON this_table_alias.this_table_pk = join_table_alias.join_table_fk_to_this
64
-        // LEFT JOIN other_table AS other_table_alias ON join_table_alias.join_table_fk_to_other = other_table_alias.other_table_pk
65
-        // remember the model relation chain to the JOIN model, because we'll
66
-        // need it for get_join_statement()
67
-        $this->_model_relation_chain_to_join_model = $model_relation_chain;
68
-        $this_table_pk_field                       = $this->get_this_model()->get_primary_key_field();// get_foreign_key_to($this->get_other_model()->get_this_model_name());
69
-        $join_table_fk_field_to_this_table         = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
70
-        $this_table_alias                          = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
71
-            $model_relation_chain,
72
-            $this->get_this_model()->get_this_model_name()
73
-        ) . $this_table_pk_field->get_table_alias();
52
+	/**
53
+	 * Gets the SQL string for joining the main model's table containing the pk to the join table. Eg "LEFT JOIN
54
+	 * real_join_table AS join_table_alias ON this_table_alias.pk = join_table_alias.fk_to_this_table"
55
+	 *
56
+	 * @param string $model_relation_chain like 'Event.Event_Venue.Venue'
57
+	 * @return string of SQL
58
+	 * @throws \EE_Error
59
+	 */
60
+	public function get_join_to_intermediate_model_statement($model_relation_chain)
61
+	{
62
+		// create sql like
63
+		// LEFT JOIN join_table AS join_table_alias ON this_table_alias.this_table_pk = join_table_alias.join_table_fk_to_this
64
+		// LEFT JOIN other_table AS other_table_alias ON join_table_alias.join_table_fk_to_other = other_table_alias.other_table_pk
65
+		// remember the model relation chain to the JOIN model, because we'll
66
+		// need it for get_join_statement()
67
+		$this->_model_relation_chain_to_join_model = $model_relation_chain;
68
+		$this_table_pk_field                       = $this->get_this_model()->get_primary_key_field();// get_foreign_key_to($this->get_other_model()->get_this_model_name());
69
+		$join_table_fk_field_to_this_table         = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
70
+		$this_table_alias                          = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
71
+			$model_relation_chain,
72
+			$this->get_this_model()->get_this_model_name()
73
+		) . $this_table_pk_field->get_table_alias();
74 74
 
75
-        $join_table_alias = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
76
-            $model_relation_chain,
77
-            $this->get_join_model()->get_this_model_name()
78
-        ) . $join_table_fk_field_to_this_table->get_table_alias();
79
-        $join_table       = $this->get_join_model()->get_table_for_alias($join_table_alias);
80
-        // phew! ok, we have all the info we need, now we can create the SQL join string
81
-        $SQL = $this->_left_join(
82
-            $join_table,
83
-            $join_table_alias,
84
-            $join_table_fk_field_to_this_table->get_table_column(),
85
-            $this_table_alias,
86
-            $this_table_pk_field->get_table_column()
87
-        ) . $this->get_join_model()->_construct_internal_join_to_table_with_alias($join_table_alias);
75
+		$join_table_alias = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
76
+			$model_relation_chain,
77
+			$this->get_join_model()->get_this_model_name()
78
+		) . $join_table_fk_field_to_this_table->get_table_alias();
79
+		$join_table       = $this->get_join_model()->get_table_for_alias($join_table_alias);
80
+		// phew! ok, we have all the info we need, now we can create the SQL join string
81
+		$SQL = $this->_left_join(
82
+			$join_table,
83
+			$join_table_alias,
84
+			$join_table_fk_field_to_this_table->get_table_column(),
85
+			$this_table_alias,
86
+			$this_table_pk_field->get_table_column()
87
+		) . $this->get_join_model()->_construct_internal_join_to_table_with_alias($join_table_alias);
88 88
 
89
-        return $SQL;
90
-    }
89
+		return $SQL;
90
+	}
91 91
 
92 92
 
93
-    /**
94
-     * Gets the SQL string for joining the join table to the other model's pk's table. Eg "LEFT JOIN real_other_table
95
-     * AS other_table_alias ON join_table_alias.fk_to_other_table = other_table_alias.pk" If you want to join between
96
-     * modelA -> joinModelAB -> modelB (eg, Event -> Event_Question_Group -> Question_Group), you should prepend the
97
-     * result of this function with results from get_join_to_intermediate_model_statement(), so that you join first to
98
-     * the intermediate join table, and then to the other model's pk's table
99
-     *
100
-     * @param string $model_relation_chain like 'Event.Event_Venue.Venue'
101
-     * @return string of SQL
102
-     * @throws \EE_Error
103
-     */
104
-    public function get_join_statement($model_relation_chain)
105
-    {
106
-        if ($this->_model_relation_chain_to_join_model === null) {
107
-            throw new EE_Error(sprintf(__(
108
-                'When using EE_HABTM_Relation to create a join, you must call get_join_to_intermediate_model_statement BEFORE get_join_statement',
109
-                'event_espresso'
110
-            )));
111
-        }
112
-        $join_table_fk_field_to_this_table  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
113
-        $join_table_alias                   = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
114
-            $this->_model_relation_chain_to_join_model,
115
-            $this->get_join_model()->get_this_model_name()
116
-        ) . $join_table_fk_field_to_this_table->get_table_alias();
117
-        $other_table_pk_field               = $this->get_other_model()->get_primary_key_field();
118
-        $join_table_fk_field_to_other_table = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
119
-        $other_table_alias                  = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
120
-            $model_relation_chain,
121
-            $this->get_other_model()->get_this_model_name()
122
-        ) . $other_table_pk_field->get_table_alias();
123
-        $other_table                        = $this->get_other_model()->get_table_for_alias($other_table_alias);
93
+	/**
94
+	 * Gets the SQL string for joining the join table to the other model's pk's table. Eg "LEFT JOIN real_other_table
95
+	 * AS other_table_alias ON join_table_alias.fk_to_other_table = other_table_alias.pk" If you want to join between
96
+	 * modelA -> joinModelAB -> modelB (eg, Event -> Event_Question_Group -> Question_Group), you should prepend the
97
+	 * result of this function with results from get_join_to_intermediate_model_statement(), so that you join first to
98
+	 * the intermediate join table, and then to the other model's pk's table
99
+	 *
100
+	 * @param string $model_relation_chain like 'Event.Event_Venue.Venue'
101
+	 * @return string of SQL
102
+	 * @throws \EE_Error
103
+	 */
104
+	public function get_join_statement($model_relation_chain)
105
+	{
106
+		if ($this->_model_relation_chain_to_join_model === null) {
107
+			throw new EE_Error(sprintf(__(
108
+				'When using EE_HABTM_Relation to create a join, you must call get_join_to_intermediate_model_statement BEFORE get_join_statement',
109
+				'event_espresso'
110
+			)));
111
+		}
112
+		$join_table_fk_field_to_this_table  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
113
+		$join_table_alias                   = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
114
+			$this->_model_relation_chain_to_join_model,
115
+			$this->get_join_model()->get_this_model_name()
116
+		) . $join_table_fk_field_to_this_table->get_table_alias();
117
+		$other_table_pk_field               = $this->get_other_model()->get_primary_key_field();
118
+		$join_table_fk_field_to_other_table = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
119
+		$other_table_alias                  = EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
120
+			$model_relation_chain,
121
+			$this->get_other_model()->get_this_model_name()
122
+		) . $other_table_pk_field->get_table_alias();
123
+		$other_table                        = $this->get_other_model()->get_table_for_alias($other_table_alias);
124 124
 
125
-        $SQL = $this->_left_join(
126
-            $other_table,
127
-            $other_table_alias,
128
-            $other_table_pk_field->get_table_column(),
129
-            $join_table_alias,
130
-            $join_table_fk_field_to_other_table->get_table_column()
131
-        ) . $this->get_other_model()->_construct_internal_join_to_table_with_alias($other_table_alias);
132
-        return $SQL;
133
-    }
125
+		$SQL = $this->_left_join(
126
+			$other_table,
127
+			$other_table_alias,
128
+			$other_table_pk_field->get_table_column(),
129
+			$join_table_alias,
130
+			$join_table_fk_field_to_other_table->get_table_column()
131
+		) . $this->get_other_model()->_construct_internal_join_to_table_with_alias($other_table_alias);
132
+		return $SQL;
133
+	}
134 134
 
135 135
 
136
-    /**
137
-     * Ensures there is an entry in the join table between these two models. Feel free to do this manually if you like.
138
-     * If the join table has additional columns (eg, the Event_Question_Group table has a is_primary column), then
139
-     * you'll want to directly use the EEM_Event_Question_Group model to add the entry to the table and set those extra
140
-     * columns' values
141
-     *
142
-     * @param EE_Base_Class|int $this_obj_or_id
143
-     * @param EE_Base_Class|int $other_obj_or_id
144
-     * @param array             $extra_join_model_fields_n_values col=>val pairs that are used as extra conditions for
145
-     *                                                            checking existing values and for setting new rows if
146
-     *                                                            no exact matches.
147
-     * @return EE_Base_Class
148
-     * @throws \EE_Error
149
-     */
150
-    public function add_relation_to($this_obj_or_id, $other_obj_or_id, $extra_join_model_fields_n_values = array())
151
-    {
152
-        $this_model_obj  = $this->get_this_model()->ensure_is_obj($this_obj_or_id, true);
153
-        $other_model_obj = $this->get_other_model()->ensure_is_obj($other_obj_or_id, true);
154
-        // check if such a relationship already exists
155
-        $join_model_fk_to_this_model  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
156
-        $join_model_fk_to_other_model = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
136
+	/**
137
+	 * Ensures there is an entry in the join table between these two models. Feel free to do this manually if you like.
138
+	 * If the join table has additional columns (eg, the Event_Question_Group table has a is_primary column), then
139
+	 * you'll want to directly use the EEM_Event_Question_Group model to add the entry to the table and set those extra
140
+	 * columns' values
141
+	 *
142
+	 * @param EE_Base_Class|int $this_obj_or_id
143
+	 * @param EE_Base_Class|int $other_obj_or_id
144
+	 * @param array             $extra_join_model_fields_n_values col=>val pairs that are used as extra conditions for
145
+	 *                                                            checking existing values and for setting new rows if
146
+	 *                                                            no exact matches.
147
+	 * @return EE_Base_Class
148
+	 * @throws \EE_Error
149
+	 */
150
+	public function add_relation_to($this_obj_or_id, $other_obj_or_id, $extra_join_model_fields_n_values = array())
151
+	{
152
+		$this_model_obj  = $this->get_this_model()->ensure_is_obj($this_obj_or_id, true);
153
+		$other_model_obj = $this->get_other_model()->ensure_is_obj($other_obj_or_id, true);
154
+		// check if such a relationship already exists
155
+		$join_model_fk_to_this_model  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
156
+		$join_model_fk_to_other_model = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
157 157
 
158
-        $foreign_keys = $all_fields = array(
159
-            $join_model_fk_to_this_model->get_name()  => $this_model_obj->ID(),
160
-            $join_model_fk_to_other_model->get_name() => $other_model_obj->ID(),
161
-        );
158
+		$foreign_keys = $all_fields = array(
159
+			$join_model_fk_to_this_model->get_name()  => $this_model_obj->ID(),
160
+			$join_model_fk_to_other_model->get_name() => $other_model_obj->ID(),
161
+		);
162 162
 
163
-        // if $where_query exists lets add them to the query_params.
164
-        if (! empty($extra_join_model_fields_n_values)) {
165
-            // make sure we strip any of the join model names from the $where_query cause we don't need that in here (why? because client code may have used the same conditionals for get_all_related which DOES need the join model name)
166
-            // make sure we strip THIS models name from the query param
167
-            $parsed_query = array();
168
-            foreach ($extra_join_model_fields_n_values as $query_param => $val) {
169
-                $query_param                = str_replace(
170
-                    $this->get_join_model()->get_this_model_name() . ".",
171
-                    "",
172
-                    $query_param
173
-                );
174
-                $parsed_query[ $query_param ] = $val;
175
-            }
176
-            $all_fields = array_merge($foreign_keys, $parsed_query);
177
-        }
163
+		// if $where_query exists lets add them to the query_params.
164
+		if (! empty($extra_join_model_fields_n_values)) {
165
+			// make sure we strip any of the join model names from the $where_query cause we don't need that in here (why? because client code may have used the same conditionals for get_all_related which DOES need the join model name)
166
+			// make sure we strip THIS models name from the query param
167
+			$parsed_query = array();
168
+			foreach ($extra_join_model_fields_n_values as $query_param => $val) {
169
+				$query_param                = str_replace(
170
+					$this->get_join_model()->get_this_model_name() . ".",
171
+					"",
172
+					$query_param
173
+				);
174
+				$parsed_query[ $query_param ] = $val;
175
+			}
176
+			$all_fields = array_merge($foreign_keys, $parsed_query);
177
+		}
178 178
 
179
-        $existing_entry_in_join_table = $this->get_join_model()->get_one(array($foreign_keys));
180
-        // If there is already an entry in the join table, indicating a relationship, update it instead of adding a
181
-        // new row.
182
-        // Again, if you want more sophisticated logic or insertions (handling more columns than just 2 foreign keys to
183
-        // the other tables) use the joining model directly!
184
-        if (! $existing_entry_in_join_table) {
185
-            $this->get_join_model()->insert($all_fields);
186
-        } else {
187
-            $this->get_join_model()->update(
188
-                $all_fields,
189
-                [$foreign_keys]
190
-            );
191
-        }
192
-        return $other_model_obj;
193
-    }
179
+		$existing_entry_in_join_table = $this->get_join_model()->get_one(array($foreign_keys));
180
+		// If there is already an entry in the join table, indicating a relationship, update it instead of adding a
181
+		// new row.
182
+		// Again, if you want more sophisticated logic or insertions (handling more columns than just 2 foreign keys to
183
+		// the other tables) use the joining model directly!
184
+		if (! $existing_entry_in_join_table) {
185
+			$this->get_join_model()->insert($all_fields);
186
+		} else {
187
+			$this->get_join_model()->update(
188
+				$all_fields,
189
+				[$foreign_keys]
190
+			);
191
+		}
192
+		return $other_model_obj;
193
+	}
194 194
 
195 195
 
196
-    /**
197
-     * Deletes any rows in the join table that have foreign keys matching the other model objects specified
198
-     *
199
-     * @param EE_Base_Class|int $this_obj_or_id
200
-     * @param EE_Base_Class|int $other_obj_or_id
201
-     * @param array             $where_query col=>val pairs that are used as extra conditions for checking existing
202
-     *                                       values and for removing existing rows if exact matches exist.
203
-     * @return EE_Base_Class
204
-     * @throws \EE_Error
205
-     */
206
-    public function remove_relation_to($this_obj_or_id, $other_obj_or_id, $where_query = array())
207
-    {
208
-        $this_model_obj  = $this->get_this_model()->ensure_is_obj($this_obj_or_id, true);
209
-        $other_model_obj = $this->get_other_model()->ensure_is_obj($other_obj_or_id, true);
210
-        // check if such a relationship already exists
211
-        $join_model_fk_to_this_model  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
212
-        $join_model_fk_to_other_model = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
196
+	/**
197
+	 * Deletes any rows in the join table that have foreign keys matching the other model objects specified
198
+	 *
199
+	 * @param EE_Base_Class|int $this_obj_or_id
200
+	 * @param EE_Base_Class|int $other_obj_or_id
201
+	 * @param array             $where_query col=>val pairs that are used as extra conditions for checking existing
202
+	 *                                       values and for removing existing rows if exact matches exist.
203
+	 * @return EE_Base_Class
204
+	 * @throws \EE_Error
205
+	 */
206
+	public function remove_relation_to($this_obj_or_id, $other_obj_or_id, $where_query = array())
207
+	{
208
+		$this_model_obj  = $this->get_this_model()->ensure_is_obj($this_obj_or_id, true);
209
+		$other_model_obj = $this->get_other_model()->ensure_is_obj($other_obj_or_id, true);
210
+		// check if such a relationship already exists
211
+		$join_model_fk_to_this_model  = $this->get_join_model()->get_foreign_key_to($this->get_this_model()->get_this_model_name());
212
+		$join_model_fk_to_other_model = $this->get_join_model()->get_foreign_key_to($this->get_other_model()->get_this_model_name());
213 213
 
214
-        $cols_n_values = array(
215
-            $join_model_fk_to_this_model->get_name()  => $this_model_obj->ID(),
216
-            $join_model_fk_to_other_model->get_name() => $other_model_obj->ID(),
217
-        );
214
+		$cols_n_values = array(
215
+			$join_model_fk_to_this_model->get_name()  => $this_model_obj->ID(),
216
+			$join_model_fk_to_other_model->get_name() => $other_model_obj->ID(),
217
+		);
218 218
 
219
-        // if $where_query exists lets add them to the query_params.
220
-        if (! empty($where_query)) {
221
-            // make sure we strip any of the join model names from the $where_query cause we don't need that in here (why? because client code may have used the same conditionals for get_all_related which DOES need the join model name)
222
-            // make sure we strip THIS models name from the query param
223
-            $parsed_query = array();
224
-            foreach ($where_query as $query_param => $val) {
225
-                $query_param                = str_replace(
226
-                    $this->get_join_model()->get_this_model_name() . ".",
227
-                    "",
228
-                    $query_param
229
-                );
230
-                $parsed_query[ $query_param ] = $val;
231
-            }
232
-            $cols_n_values = array_merge($cols_n_values, $parsed_query);
233
-        }
219
+		// if $where_query exists lets add them to the query_params.
220
+		if (! empty($where_query)) {
221
+			// make sure we strip any of the join model names from the $where_query cause we don't need that in here (why? because client code may have used the same conditionals for get_all_related which DOES need the join model name)
222
+			// make sure we strip THIS models name from the query param
223
+			$parsed_query = array();
224
+			foreach ($where_query as $query_param => $val) {
225
+				$query_param                = str_replace(
226
+					$this->get_join_model()->get_this_model_name() . ".",
227
+					"",
228
+					$query_param
229
+				);
230
+				$parsed_query[ $query_param ] = $val;
231
+			}
232
+			$cols_n_values = array_merge($cols_n_values, $parsed_query);
233
+		}
234 234
 
235
-        $this->get_join_model()->delete(array($cols_n_values));
236
-        return $other_model_obj;
237
-    }
235
+		$this->get_join_model()->delete(array($cols_n_values));
236
+		return $other_model_obj;
237
+	}
238 238
 
239
-    /**
240
-     * Gets all the non-key fields (ie, not the primary key and not foreign keys) on the join model.
241
-     * @since $VID:$
242
-     * @return EE_Model_Field_Base[]
243
-     * @throws EE_Error
244
-     */
245
-    public function getNonKeyFields()
246
-    {
247
-        // all fields besides the primary key and two foreign keys should be parameters
248
-        $join_model = $this->get_join_model();
249
-        $standard_fields = array();
250
-        if ($join_model->has_primary_key_field()) {
251
-            $standard_fields[] = $join_model->primary_key_name();
252
-        }
253
-        if ($this->get_this_model()->has_primary_key_field()) {
254
-            $standard_fields[] = $this->get_this_model()->primary_key_name();
255
-        }
256
-        if ($this->get_other_model()->has_primary_key_field()) {
257
-            $standard_fields[] = $this->get_other_model()->primary_key_name();
258
-        }
259
-        return array_diff_key(
260
-            $join_model->field_settings(),
261
-            array_flip($standard_fields)
262
-        );
263
-    }
239
+	/**
240
+	 * Gets all the non-key fields (ie, not the primary key and not foreign keys) on the join model.
241
+	 * @since $VID:$
242
+	 * @return EE_Model_Field_Base[]
243
+	 * @throws EE_Error
244
+	 */
245
+	public function getNonKeyFields()
246
+	{
247
+		// all fields besides the primary key and two foreign keys should be parameters
248
+		$join_model = $this->get_join_model();
249
+		$standard_fields = array();
250
+		if ($join_model->has_primary_key_field()) {
251
+			$standard_fields[] = $join_model->primary_key_name();
252
+		}
253
+		if ($this->get_this_model()->has_primary_key_field()) {
254
+			$standard_fields[] = $this->get_this_model()->primary_key_name();
255
+		}
256
+		if ($this->get_other_model()->has_primary_key_field()) {
257
+			$standard_fields[] = $this->get_other_model()->primary_key_name();
258
+		}
259
+		return array_diff_key(
260
+			$join_model->field_settings(),
261
+			array_flip($standard_fields)
262
+		);
263
+	}
264 264
 
265
-    /**
266
-     * Returns true if the join model has non-key fields (ie, fields that aren't the primary key or foreign keys.)
267
-     * @since $VID:$
268
-     * @return boolean
269
-     */
270
-    public function hasNonKeyFields()
271
-    {
272
-        return count($this->get_join_model()->field_settings()) > 3;
273
-    }
265
+	/**
266
+	 * Returns true if the join model has non-key fields (ie, fields that aren't the primary key or foreign keys.)
267
+	 * @since $VID:$
268
+	 * @return boolean
269
+	 */
270
+	public function hasNonKeyFields()
271
+	{
272
+		return count($this->get_join_model()->field_settings()) > 3;
273
+	}
274 274
 }
Please login to merge, or discard this patch.
core/helpers/EEH_Line_Item.helper.php 1 patch
Indentation   +1685 added lines, -1685 removed lines patch added patch discarded remove patch
@@ -20,1689 +20,1689 @@
 block discarded – undo
20 20
 class EEH_Line_Item
21 21
 {
22 22
 
23
-    // other functions: cancel ticket purchase
24
-    // delete ticket purchase
25
-    // add promotion
26
-
27
-
28
-    /**
29
-     * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
30
-     * Does NOT automatically re-calculate the line item totals or update the related transaction.
31
-     * You should call recalculate_total_including_taxes() on the grant total line item after this
32
-     * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
33
-     * to keep the registration final prices in-sync with the transaction's total.
34
-     *
35
-     * @param EE_Line_Item $parent_line_item
36
-     * @param string $name
37
-     * @param float $unit_price
38
-     * @param string $description
39
-     * @param int $quantity
40
-     * @param boolean $taxable
41
-     * @param boolean $code if set to a value, ensures there is only one line item with that code
42
-     * @return boolean success
43
-     * @throws \EE_Error
44
-     */
45
-    public static function add_unrelated_item(EE_Line_Item $parent_line_item, $name, $unit_price, $description = '', $quantity = 1, $taxable = false, $code = null)
46
-    {
47
-        $items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
48
-        $line_item = EE_Line_Item::new_instance(array(
49
-            'LIN_name' => $name,
50
-            'LIN_desc' => $description,
51
-            'LIN_unit_price' => $unit_price,
52
-            'LIN_quantity' => $quantity,
53
-            'LIN_percent' => null,
54
-            'LIN_is_taxable' => $taxable,
55
-            'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0,
56
-            'LIN_total' => (float) $unit_price * (int) $quantity,
57
-            'LIN_type' => EEM_Line_Item::type_line_item,
58
-            'LIN_code' => $code,
59
-        ));
60
-        $line_item = apply_filters(
61
-            'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
62
-            $line_item,
63
-            $parent_line_item
64
-        );
65
-        return self::add_item($parent_line_item, $line_item);
66
-    }
67
-
68
-
69
-    /**
70
-     * Adds a simple item ( unrelated to any other model object) to the total line item,
71
-     * in the correct spot in the line item tree. Automatically
72
-     * re-calculates the line item totals and updates the related transaction. But
73
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
74
-     * should probably change because of this).
75
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
76
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
77
-     *
78
-     * @param EE_Line_Item $parent_line_item
79
-     * @param string $name
80
-     * @param float $percentage_amount
81
-     * @param string $description
82
-     * @param boolean $taxable
83
-     * @return boolean success
84
-     * @throws \EE_Error
85
-     */
86
-    public static function add_percentage_based_item(EE_Line_Item $parent_line_item, $name, $percentage_amount, $description = '', $taxable = false)
87
-    {
88
-        $line_item = EE_Line_Item::new_instance(array(
89
-            'LIN_name' => $name,
90
-            'LIN_desc' => $description,
91
-            'LIN_unit_price' => 0,
92
-            'LIN_percent' => $percentage_amount,
93
-            'LIN_quantity' => 1,
94
-            'LIN_is_taxable' => $taxable,
95
-            'LIN_total' => (float) ($percentage_amount * ($parent_line_item->total() / 100)),
96
-            'LIN_type' => EEM_Line_Item::type_line_item,
97
-            'LIN_parent' => $parent_line_item->ID()
98
-        ));
99
-        $line_item = apply_filters(
100
-            'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
101
-            $line_item
102
-        );
103
-        return $parent_line_item->add_child_line_item($line_item, false);
104
-    }
105
-
106
-
107
-    /**
108
-     * Returns the new line item created by adding a purchase of the ticket
109
-     * ensures that ticket line item is saved, and that cart total has been recalculated.
110
-     * If this ticket has already been purchased, just increments its count.
111
-     * Automatically re-calculates the line item totals and updates the related transaction. But
112
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
113
-     * should probably change because of this).
114
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
115
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
116
-     *
117
-     * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
118
-     * @param EE_Ticket $ticket
119
-     * @param int $qty
120
-     * @return \EE_Line_Item
121
-     * @throws \EE_Error
122
-     */
123
-    public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
124
-    {
125
-        if (!$total_line_item instanceof EE_Line_Item || !$total_line_item->is_total()) {
126
-            throw new EE_Error(sprintf(__('A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', 'event_espresso'), $ticket->ID(), $total_line_item->ID()));
127
-        }
128
-        // either increment the qty for an existing ticket
129
-        $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
130
-        // or add a new one
131
-        if (!$line_item instanceof EE_Line_Item) {
132
-            $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
133
-        }
134
-        $total_line_item->recalculate_total_including_taxes();
135
-        return $line_item;
136
-    }
137
-
138
-
139
-    /**
140
-     * Returns the new line item created by adding a purchase of the ticket
141
-     * @param \EE_Line_Item $total_line_item
142
-     * @param EE_Ticket $ticket
143
-     * @param int $qty
144
-     * @return \EE_Line_Item
145
-     * @throws \EE_Error
146
-     */
147
-    public static function increment_ticket_qty_if_already_in_cart(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
148
-    {
149
-        $line_item = null;
150
-        if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
151
-            $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
152
-            foreach ((array) $ticket_line_items as $ticket_line_item) {
153
-                if ($ticket_line_item instanceof EE_Line_Item
154
-                    && (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID()
155
-                ) {
156
-                    $line_item = $ticket_line_item;
157
-                    break;
158
-                }
159
-            }
160
-        }
161
-        if ($line_item instanceof EE_Line_Item) {
162
-            EEH_Line_Item::increment_quantity($line_item, $qty);
163
-            return $line_item;
164
-        }
165
-        return null;
166
-    }
167
-
168
-
169
-    /**
170
-     * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
171
-     * Does NOT save or recalculate other line items totals
172
-     *
173
-     * @param EE_Line_Item $line_item
174
-     * @param int $qty
175
-     * @return void
176
-     * @throws \EE_Error
177
-     */
178
-    public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
179
-    {
180
-        if (!$line_item->is_percent()) {
181
-            $qty += $line_item->quantity();
182
-            $line_item->set_quantity($qty);
183
-            $line_item->set_total($line_item->unit_price() * $qty);
184
-            $line_item->save();
185
-        }
186
-        foreach ($line_item->children() as $child) {
187
-            if ($child->is_sub_line_item()) {
188
-                EEH_Line_Item::update_quantity($child, $qty);
189
-            }
190
-        }
191
-    }
192
-
193
-
194
-    /**
195
-     * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
196
-     * Does NOT save or recalculate other line items totals
197
-     *
198
-     * @param EE_Line_Item $line_item
199
-     * @param int $qty
200
-     * @return void
201
-     * @throws \EE_Error
202
-     */
203
-    public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
204
-    {
205
-        if (!$line_item->is_percent()) {
206
-            $qty = $line_item->quantity() - $qty;
207
-            $qty = max($qty, 0);
208
-            $line_item->set_quantity($qty);
209
-            $line_item->set_total($line_item->unit_price() * $qty);
210
-            $line_item->save();
211
-        }
212
-        foreach ($line_item->children() as $child) {
213
-            if ($child->is_sub_line_item()) {
214
-                EEH_Line_Item::update_quantity($child, $qty);
215
-            }
216
-        }
217
-    }
218
-
219
-
220
-    /**
221
-     * Updates the line item and its children's quantities to the specified number.
222
-     * Does NOT save them or recalculate totals.
223
-     *
224
-     * @param EE_Line_Item $line_item
225
-     * @param int $new_quantity
226
-     * @throws \EE_Error
227
-     */
228
-    public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
229
-    {
230
-        if (!$line_item->is_percent()) {
231
-            $line_item->set_quantity($new_quantity);
232
-            $line_item->set_total($line_item->unit_price() * $new_quantity);
233
-            $line_item->save();
234
-        }
235
-        foreach ($line_item->children() as $child) {
236
-            if ($child->is_sub_line_item()) {
237
-                EEH_Line_Item::update_quantity($child, $new_quantity);
238
-            }
239
-        }
240
-    }
241
-
242
-
243
-    /**
244
-     * Returns the new line item created by adding a purchase of the ticket
245
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
246
-     * @param EE_Ticket $ticket
247
-     * @param int $qty
248
-     * @return \EE_Line_Item
249
-     * @throws \EE_Error
250
-     */
251
-    public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
252
-    {
253
-        $datetimes = $ticket->datetimes();
254
-        $first_datetime = reset($datetimes);
255
-        if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
256
-            $first_datetime_name = $first_datetime->event()->name();
257
-        } else {
258
-            $first_datetime_name = __('Event', 'event_espresso');
259
-        }
260
-        $event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
261
-        // get event subtotal line
262
-        $events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
263
-        // add $ticket to cart
264
-        $line_item = EE_Line_Item::new_instance(array(
265
-            'LIN_name' => $ticket->name(),
266
-            'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
267
-            'LIN_unit_price' => $ticket->price(),
268
-            'LIN_quantity' => $qty,
269
-            'LIN_is_taxable' => $ticket->taxable(),
270
-            'LIN_order' => count($events_sub_total->children()),
271
-            'LIN_total' => $ticket->price() * $qty,
272
-            'LIN_type' => EEM_Line_Item::type_line_item,
273
-            'OBJ_ID' => $ticket->ID(),
274
-            'OBJ_type' => 'Ticket'
275
-        ));
276
-        $line_item = apply_filters(
277
-            'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
278
-            $line_item
279
-        );
280
-        $events_sub_total->add_child_line_item($line_item);
281
-        // now add the sub-line items
282
-        $running_total_for_ticket = 0;
283
-        foreach ($ticket->prices(array('order_by' => array('PRC_order' => 'ASC'))) as $price) {
284
-            $sign = $price->is_discount() ? -1 : 1;
285
-            $price_total = $price->is_percent()
286
-                ? $running_total_for_ticket * $price->amount() / 100
287
-                : $price->amount() * $qty;
288
-            $sub_line_item = EE_Line_Item::new_instance(array(
289
-                'LIN_name' => $price->name(),
290
-                'LIN_desc' => $price->desc(),
291
-                'LIN_quantity' => $price->is_percent() ? null : $qty,
292
-                'LIN_is_taxable' => false,
293
-                'LIN_order' => $price->order(),
294
-                'LIN_total' => $sign * $price_total,
295
-                'LIN_type' => EEM_Line_Item::type_sub_line_item,
296
-                'OBJ_ID' => $price->ID(),
297
-                'OBJ_type' => 'Price'
298
-            ));
299
-            $sub_line_item = apply_filters(
300
-                'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
301
-                $sub_line_item
302
-            );
303
-            if ($price->is_percent()) {
304
-                $sub_line_item->set_percent($sign * $price->amount());
305
-            } else {
306
-                $sub_line_item->set_unit_price($sign * $price->amount());
307
-            }
308
-            $running_total_for_ticket += $price_total;
309
-            $line_item->add_child_line_item($sub_line_item);
310
-        }
311
-        return $line_item;
312
-    }
313
-
314
-
315
-    /**
316
-     * Adds the specified item under the pre-tax-sub-total line item. Automatically
317
-     * re-calculates the line item totals and updates the related transaction. But
318
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
319
-     * should probably change because of this).
320
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
321
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
322
-     *
323
-     * @param EE_Line_Item $total_line_item
324
-     * @param EE_Line_Item $item to be added
325
-     * @return boolean
326
-     * @throws \EE_Error
327
-     */
328
-    public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item)
329
-    {
330
-        $pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
331
-        if ($pre_tax_subtotal instanceof EE_Line_Item) {
332
-            $success = $pre_tax_subtotal->add_child_line_item($item);
333
-        } else {
334
-            return false;
335
-        }
336
-        $total_line_item->recalculate_total_including_taxes();
337
-        return $success;
338
-    }
339
-
340
-
341
-    /**
342
-     * cancels an existing ticket line item,
343
-     * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
344
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
345
-     *
346
-     * @param EE_Line_Item $ticket_line_item
347
-     * @param int $qty
348
-     * @return bool success
349
-     * @throws \EE_Error
350
-     */
351
-    public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
352
-    {
353
-        // validate incoming line_item
354
-        if ($ticket_line_item->OBJ_type() !== 'Ticket') {
355
-            throw new EE_Error(
356
-                sprintf(
357
-                    __('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
358
-                    $ticket_line_item->type()
359
-                )
360
-            );
361
-        }
362
-        if ($ticket_line_item->quantity() < $qty) {
363
-            throw new EE_Error(
364
-                sprintf(
365
-                    __('Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.', 'event_espresso'),
366
-                    $qty,
367
-                    $ticket_line_item->quantity()
368
-                )
369
-            );
370
-        }
371
-        // decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
372
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
373
-        foreach ($ticket_line_item->children() as $child_line_item) {
374
-            if ($child_line_item->is_sub_line_item()
375
-                && !$child_line_item->is_percent()
376
-                && !$child_line_item->is_cancellation()
377
-            ) {
378
-                $child_line_item->set_quantity($child_line_item->quantity() - $qty);
379
-            }
380
-        }
381
-        // get cancellation sub line item
382
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
383
-            $ticket_line_item,
384
-            EEM_Line_Item::type_cancellation
385
-        );
386
-        $cancellation_line_item = reset($cancellation_line_item);
387
-        // verify that this ticket was indeed previously cancelled
388
-        if ($cancellation_line_item instanceof EE_Line_Item) {
389
-            // increment cancelled quantity
390
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
391
-        } else {
392
-            // create cancellation sub line item
393
-            $cancellation_line_item = EE_Line_Item::new_instance(array(
394
-                'LIN_name' => __('Cancellation', 'event_espresso'),
395
-                'LIN_desc' => sprintf(
396
-                    _x('Cancelled %1$s : %2$s', 'Cancelled Ticket Name : 2015-01-01 11:11', 'event_espresso'),
397
-                    $ticket_line_item->name(),
398
-                    current_time(get_option('date_format') . ' ' . get_option('time_format'))
399
-                ),
400
-                'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
401
-                'LIN_quantity' => $qty,
402
-                'LIN_is_taxable' => $ticket_line_item->is_taxable(),
403
-                'LIN_order' => count($ticket_line_item->children()),
404
-                'LIN_total' => 0, // $ticket_line_item->unit_price()
405
-                'LIN_type' => EEM_Line_Item::type_cancellation,
406
-            ));
407
-            $ticket_line_item->add_child_line_item($cancellation_line_item);
408
-        }
409
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
410
-            // decrement parent line item quantity
411
-            $event_line_item = $ticket_line_item->parent();
412
-            if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
413
-                $event_line_item->set_quantity($event_line_item->quantity() - $qty);
414
-                $event_line_item->save();
415
-            }
416
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
417
-            return true;
418
-        }
419
-        return false;
420
-    }
421
-
422
-
423
-    /**
424
-     * reinstates (un-cancels?) a previously canceled ticket line item,
425
-     * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
426
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
427
-     *
428
-     * @param EE_Line_Item $ticket_line_item
429
-     * @param int $qty
430
-     * @return bool success
431
-     * @throws \EE_Error
432
-     */
433
-    public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
434
-    {
435
-        // validate incoming line_item
436
-        if ($ticket_line_item->OBJ_type() !== 'Ticket') {
437
-            throw new EE_Error(
438
-                sprintf(
439
-                    __('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
440
-                    $ticket_line_item->type()
441
-                )
442
-            );
443
-        }
444
-        // get cancellation sub line item
445
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
446
-            $ticket_line_item,
447
-            EEM_Line_Item::type_cancellation
448
-        );
449
-        $cancellation_line_item = reset($cancellation_line_item);
450
-        // verify that this ticket was indeed previously cancelled
451
-        if (!$cancellation_line_item instanceof EE_Line_Item) {
452
-            return false;
453
-        }
454
-        if ($cancellation_line_item->quantity() > $qty) {
455
-            // decrement cancelled quantity
456
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
457
-        } elseif ($cancellation_line_item->quantity() == $qty) {
458
-            // decrement cancelled quantity in case anyone still has the object kicking around
459
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
460
-            // delete because quantity will end up as 0
461
-            $cancellation_line_item->delete();
462
-            // and attempt to destroy the object,
463
-            // even though PHP won't actually destroy it until it needs the memory
464
-            unset($cancellation_line_item);
465
-        } else {
466
-            // what ?!?! negative quantity ?!?!
467
-            throw new EE_Error(
468
-                sprintf(
469
-                    __(
470
-                        'Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
471
-                        'event_espresso'
472
-                    ),
473
-                    $qty,
474
-                    $cancellation_line_item->quantity()
475
-                )
476
-            );
477
-        }
478
-        // increment ticket quantity
479
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
480
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
481
-            // increment parent line item quantity
482
-            $event_line_item = $ticket_line_item->parent();
483
-            if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
484
-                $event_line_item->set_quantity($event_line_item->quantity() + $qty);
485
-            }
486
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
487
-            return true;
488
-        }
489
-        return false;
490
-    }
491
-
492
-
493
-    /**
494
-     * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
495
-     * then EE_Line_Item::recalculate_total_including_taxes() on the result
496
-     *
497
-     * @param EE_Line_Item $line_item
498
-     * @return \EE_Line_Item
499
-     */
500
-    public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
501
-    {
502
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
503
-        return $grand_total_line_item->recalculate_total_including_taxes();
504
-    }
505
-
506
-
507
-    /**
508
-     * Gets the line item which contains the subtotal of all the items
509
-     *
510
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
511
-     * @return \EE_Line_Item
512
-     * @throws \EE_Error
513
-     */
514
-    public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
515
-    {
516
-        $pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
517
-        return $pre_tax_subtotal instanceof EE_Line_Item
518
-            ? $pre_tax_subtotal
519
-            : self::create_pre_tax_subtotal($total_line_item);
520
-    }
521
-
522
-
523
-    /**
524
-     * Gets the line item for the taxes subtotal
525
-     *
526
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
527
-     * @return \EE_Line_Item
528
-     * @throws \EE_Error
529
-     */
530
-    public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
531
-    {
532
-        $taxes = $total_line_item->get_child_line_item('taxes');
533
-        return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
534
-    }
535
-
536
-
537
-    /**
538
-     * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
539
-     *
540
-     * @param EE_Line_Item $line_item
541
-     * @param EE_Transaction $transaction
542
-     * @return void
543
-     * @throws \EE_Error
544
-     */
545
-    public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = null)
546
-    {
547
-        if ($transaction) {
548
-            /** @type EEM_Transaction $EEM_Transaction */
549
-            $EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
550
-            $TXN_ID = $EEM_Transaction->ensure_is_ID($transaction);
551
-            $line_item->set_TXN_ID($TXN_ID);
552
-        }
553
-    }
554
-
555
-
556
-    /**
557
-     * Creates a new default total line item for the transaction,
558
-     * and its tickets subtotal and taxes subtotal line items (and adds the
559
-     * existing taxes as children of the taxes subtotal line item)
560
-     *
561
-     * @param EE_Transaction $transaction
562
-     * @return \EE_Line_Item of type total
563
-     * @throws \EE_Error
564
-     */
565
-    public static function create_total_line_item($transaction = null)
566
-    {
567
-        $total_line_item = EE_Line_Item::new_instance(array(
568
-            'LIN_code' => 'total',
569
-            'LIN_name' => __('Grand Total', 'event_espresso'),
570
-            'LIN_type' => EEM_Line_Item::type_total,
571
-            'OBJ_type' => 'Transaction'
572
-        ));
573
-        $total_line_item = apply_filters(
574
-            'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
575
-            $total_line_item
576
-        );
577
-        self::set_TXN_ID($total_line_item, $transaction);
578
-        self::create_pre_tax_subtotal($total_line_item, $transaction);
579
-        self::create_taxes_subtotal($total_line_item, $transaction);
580
-        return $total_line_item;
581
-    }
582
-
583
-
584
-    /**
585
-     * Creates a default items subtotal line item
586
-     *
587
-     * @param EE_Line_Item $total_line_item
588
-     * @param EE_Transaction $transaction
589
-     * @return EE_Line_Item
590
-     * @throws \EE_Error
591
-     */
592
-    protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = null)
593
-    {
594
-        $pre_tax_line_item = EE_Line_Item::new_instance(array(
595
-            'LIN_code' => 'pre-tax-subtotal',
596
-            'LIN_name' => __('Pre-Tax Subtotal', 'event_espresso'),
597
-            'LIN_type' => EEM_Line_Item::type_sub_total
598
-        ));
599
-        $pre_tax_line_item = apply_filters(
600
-            'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
601
-            $pre_tax_line_item
602
-        );
603
-        self::set_TXN_ID($pre_tax_line_item, $transaction);
604
-        $total_line_item->add_child_line_item($pre_tax_line_item);
605
-        self::create_event_subtotal($pre_tax_line_item, $transaction);
606
-        return $pre_tax_line_item;
607
-    }
608
-
609
-
610
-    /**
611
-     * Creates a line item for the taxes subtotal and finds all the tax prices
612
-     * and applies taxes to it
613
-     *
614
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
615
-     * @param EE_Transaction $transaction
616
-     * @return EE_Line_Item
617
-     * @throws \EE_Error
618
-     */
619
-    protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
620
-    {
621
-        $tax_line_item = EE_Line_Item::new_instance(array(
622
-            'LIN_code' => 'taxes',
623
-            'LIN_name' => __('Taxes', 'event_espresso'),
624
-            'LIN_type' => EEM_Line_Item::type_tax_sub_total,
625
-            'LIN_order' => 1000,// this should always come last
626
-        ));
627
-        $tax_line_item = apply_filters(
628
-            'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
629
-            $tax_line_item
630
-        );
631
-        self::set_TXN_ID($tax_line_item, $transaction);
632
-        $total_line_item->add_child_line_item($tax_line_item);
633
-        // and lastly, add the actual taxes
634
-        self::apply_taxes($total_line_item);
635
-        return $tax_line_item;
636
-    }
637
-
638
-
639
-    /**
640
-     * Creates a default items subtotal line item
641
-     *
642
-     * @param EE_Line_Item $pre_tax_line_item
643
-     * @param EE_Transaction $transaction
644
-     * @param EE_Event $event
645
-     * @return EE_Line_Item
646
-     * @throws \EE_Error
647
-     */
648
-    public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = null, $event = null)
649
-    {
650
-        $event_line_item = EE_Line_Item::new_instance(array(
651
-            'LIN_code' => self::get_event_code($event),
652
-            'LIN_name' => self::get_event_name($event),
653
-            'LIN_desc' => self::get_event_desc($event),
654
-            'LIN_type' => EEM_Line_Item::type_sub_total,
655
-            'OBJ_type' => 'Event',
656
-            'OBJ_ID' => $event instanceof EE_Event ? $event->ID() : 0
657
-        ));
658
-        $event_line_item = apply_filters(
659
-            'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
660
-            $event_line_item
661
-        );
662
-        self::set_TXN_ID($event_line_item, $transaction);
663
-        $pre_tax_line_item->add_child_line_item($event_line_item);
664
-        return $event_line_item;
665
-    }
666
-
667
-
668
-    /**
669
-     * Gets what the event ticket's code SHOULD be
670
-     *
671
-     * @param EE_Event $event
672
-     * @return string
673
-     * @throws \EE_Error
674
-     */
675
-    public static function get_event_code($event)
676
-    {
677
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
678
-    }
679
-
680
-    /**
681
-     * Gets the event name
682
-     * @param EE_Event $event
683
-     * @return string
684
-     */
685
-    public static function get_event_name($event)
686
-    {
687
-        return $event instanceof EE_Event ? mb_substr($event->name(), 0, 245) : __('Event', 'event_espresso');
688
-    }
689
-
690
-    /**
691
-     * Gets the event excerpt
692
-     * @param EE_Event $event
693
-     * @return string
694
-     */
695
-    public static function get_event_desc($event)
696
-    {
697
-        return $event instanceof EE_Event ? $event->short_description() : '';
698
-    }
699
-
700
-    /**
701
-     * Given the grand total line item and a ticket, finds the event sub-total
702
-     * line item the ticket's purchase should be added onto
703
-     *
704
-     * @access public
705
-     * @param EE_Line_Item $grand_total the grand total line item
706
-     * @param EE_Ticket $ticket
707
-     * @throws \EE_Error
708
-     * @return EE_Line_Item
709
-     */
710
-    public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
711
-    {
712
-        $first_datetime = $ticket->first_datetime();
713
-        if (!$first_datetime instanceof EE_Datetime) {
714
-            throw new EE_Error(
715
-                sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())
716
-            );
717
-        }
718
-        $event = $first_datetime->event();
719
-        if (!$event instanceof EE_Event) {
720
-            throw new EE_Error(
721
-                sprintf(
722
-                    __('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'),
723
-                    $ticket->ID()
724
-                )
725
-            );
726
-        }
727
-        $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
728
-        if (!$events_sub_total instanceof EE_Line_Item) {
729
-            throw new EE_Error(
730
-                sprintf(
731
-                    __('There is no events sub-total for ticket %s on total line item %d', 'event_espresso'),
732
-                    $ticket->ID(),
733
-                    $grand_total->ID()
734
-                )
735
-            );
736
-        }
737
-        return $events_sub_total;
738
-    }
739
-
740
-
741
-    /**
742
-     * Gets the event line item
743
-     *
744
-     * @param EE_Line_Item $grand_total
745
-     * @param EE_Event $event
746
-     * @return EE_Line_Item for the event subtotal which is a child of $grand_total
747
-     * @throws \EE_Error
748
-     */
749
-    public static function get_event_line_item(EE_Line_Item $grand_total, $event)
750
-    {
751
-        /** @type EE_Event $event */
752
-        $event = EEM_Event::instance()->ensure_is_obj($event, true);
753
-        $event_line_item = null;
754
-        $found = false;
755
-        foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
756
-            // default event subtotal, we should only ever find this the first time this method is called
757
-            if (!$event_line_item->OBJ_ID()) {
758
-                // let's use this! but first... set the event details
759
-                EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
760
-                $found = true;
761
-                break;
762
-            } elseif ($event_line_item->OBJ_ID() === $event->ID()) {
763
-                // found existing line item for this event in the cart, so break out of loop and use this one
764
-                $found = true;
765
-                break;
766
-            }
767
-        }
768
-        if (!$found) {
769
-            // there is no event sub-total yet, so add it
770
-            $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
771
-            // create a new "event" subtotal below that
772
-            $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
773
-            // and set the event details
774
-            EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
775
-        }
776
-        return $event_line_item;
777
-    }
778
-
779
-
780
-    /**
781
-     * Creates a default items subtotal line item
782
-     *
783
-     * @param EE_Line_Item $event_line_item
784
-     * @param EE_Event $event
785
-     * @param EE_Transaction $transaction
786
-     * @return EE_Line_Item
787
-     * @throws \EE_Error
788
-     */
789
-    public static function set_event_subtotal_details(
790
-        EE_Line_Item $event_line_item,
791
-        EE_Event $event,
792
-        $transaction = null
793
-    ) {
794
-        if ($event instanceof EE_Event) {
795
-            $event_line_item->set_code(self::get_event_code($event));
796
-            $event_line_item->set_name(self::get_event_name($event));
797
-            $event_line_item->set_desc(self::get_event_desc($event));
798
-            $event_line_item->set_OBJ_ID($event->ID());
799
-        }
800
-        self::set_TXN_ID($event_line_item, $transaction);
801
-    }
802
-
803
-
804
-    /**
805
-     * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
806
-     * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
807
-     * any old taxes are removed
808
-     *
809
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
810
-     * @throws \EE_Error
811
-     */
812
-    public static function apply_taxes(EE_Line_Item $total_line_item)
813
-    {
814
-        /** @type EEM_Price $EEM_Price */
815
-        $EEM_Price = EE_Registry::instance()->load_model('Price');
816
-        // get array of taxes via Price Model
817
-        $ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes();
818
-        ksort($ordered_taxes);
819
-        $taxes_line_item = self::get_taxes_subtotal($total_line_item);
820
-        // just to be safe, remove its old tax line items
821
-        $taxes_line_item->delete_children_line_items();
822
-        // loop thru taxes
823
-        foreach ($ordered_taxes as $order => $taxes) {
824
-            foreach ($taxes as $tax) {
825
-                if ($tax instanceof EE_Price) {
826
-                    $tax_line_item = EE_Line_Item::new_instance(
827
-                        array(
828
-                            'LIN_name' => $tax->name(),
829
-                            'LIN_desc' => $tax->desc(),
830
-                            'LIN_percent' => $tax->amount(),
831
-                            'LIN_is_taxable' => false,
832
-                            'LIN_order' => $order,
833
-                            'LIN_total' => 0,
834
-                            'LIN_type' => EEM_Line_Item::type_tax,
835
-                            'OBJ_type' => 'Price',
836
-                            'OBJ_ID' => $tax->ID()
837
-                        )
838
-                    );
839
-                    $tax_line_item = apply_filters(
840
-                        'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
841
-                        $tax_line_item
842
-                    );
843
-                    $taxes_line_item->add_child_line_item($tax_line_item);
844
-                }
845
-            }
846
-        }
847
-        $total_line_item->recalculate_total_including_taxes();
848
-    }
849
-
850
-
851
-    /**
852
-     * Ensures that taxes have been applied to the order, if not applies them.
853
-     * Returns the total amount of tax
854
-     *
855
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
856
-     * @return float
857
-     * @throws \EE_Error
858
-     */
859
-    public static function ensure_taxes_applied($total_line_item)
860
-    {
861
-        $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
862
-        if (!$taxes_subtotal->children()) {
863
-            self::apply_taxes($total_line_item);
864
-        }
865
-        return $taxes_subtotal->total();
866
-    }
867
-
868
-
869
-    /**
870
-     * Deletes ALL children of the passed line item
871
-     *
872
-     * @param EE_Line_Item $parent_line_item
873
-     * @return bool
874
-     * @throws \EE_Error
875
-     */
876
-    public static function delete_all_child_items(EE_Line_Item $parent_line_item)
877
-    {
878
-        $deleted = 0;
879
-        foreach ($parent_line_item->children() as $child_line_item) {
880
-            if ($child_line_item instanceof EE_Line_Item) {
881
-                $deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
882
-                if ($child_line_item->ID()) {
883
-                    $child_line_item->delete();
884
-                    unset($child_line_item);
885
-                } else {
886
-                    $parent_line_item->delete_child_line_item($child_line_item->code());
887
-                }
888
-                $deleted++;
889
-            }
890
-        }
891
-        return $deleted;
892
-    }
893
-
894
-
895
-    /**
896
-     * Deletes the line items as indicated by the line item code(s) provided,
897
-     * regardless of where they're found in the line item tree. Automatically
898
-     * re-calculates the line item totals and updates the related transaction. But
899
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
900
-     * should probably change because of this).
901
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
902
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
903
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
904
-     * @param array|bool|string $line_item_codes
905
-     * @return int number of items successfully removed
906
-     */
907
-    public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = false)
908
-    {
909
-
910
-        if ($total_line_item->type() !== EEM_Line_Item::type_total) {
911
-            EE_Error::doing_it_wrong(
912
-                'EEH_Line_Item::delete_items',
913
-                __(
914
-                    'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
915
-                    'event_espresso'
916
-                ),
917
-                '4.6.18'
918
-            );
919
-        }
920
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
921
-
922
-        // check if only a single line_item_id was passed
923
-        if (!empty($line_item_codes) && !is_array($line_item_codes)) {
924
-            // place single line_item_id in an array to appear as multiple line_item_ids
925
-            $line_item_codes = array($line_item_codes);
926
-        }
927
-        $removals = 0;
928
-        // cycle thru line_item_ids
929
-        foreach ($line_item_codes as $line_item_id) {
930
-            $removals += $total_line_item->delete_child_line_item($line_item_id);
931
-        }
932
-
933
-        if ($removals > 0) {
934
-            $total_line_item->recalculate_taxes_and_tax_total();
935
-            return $removals;
936
-        } else {
937
-            return false;
938
-        }
939
-    }
940
-
941
-
942
-    /**
943
-     * Overwrites the previous tax by clearing out the old taxes, and creates a new
944
-     * tax and updates the total line item accordingly
945
-     *
946
-     * @param EE_Line_Item $total_line_item
947
-     * @param float $amount
948
-     * @param string $name
949
-     * @param string $description
950
-     * @param string $code
951
-     * @param boolean $add_to_existing_line_item
952
-     *                          if true, and a duplicate line item with the same code is found,
953
-     *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
954
-     * @return EE_Line_Item the new tax line item created
955
-     * @throws \EE_Error
956
-     */
957
-    public static function set_total_tax_to(
958
-        EE_Line_Item $total_line_item,
959
-        $amount,
960
-        $name = null,
961
-        $description = null,
962
-        $code = null,
963
-        $add_to_existing_line_item = false
964
-    ) {
965
-        $tax_subtotal = self::get_taxes_subtotal($total_line_item);
966
-        $taxable_total = $total_line_item->taxable_total();
967
-
968
-        if ($add_to_existing_line_item) {
969
-            $new_tax = $tax_subtotal->get_child_line_item($code);
970
-            EEM_Line_Item::instance()->delete(
971
-                array(array('LIN_code' => array('!=', $code), 'LIN_parent' => $tax_subtotal->ID()))
972
-            );
973
-        } else {
974
-            $new_tax = null;
975
-            $tax_subtotal->delete_children_line_items();
976
-        }
977
-        if ($new_tax) {
978
-            $new_tax->set_total($new_tax->total() + $amount);
979
-            $new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
980
-        } else {
981
-            // no existing tax item. Create it
982
-            $new_tax = EE_Line_Item::new_instance(array(
983
-                'TXN_ID' => $total_line_item->TXN_ID(),
984
-                'LIN_name' => $name ? $name : __('Tax', 'event_espresso'),
985
-                'LIN_desc' => $description ? $description : '',
986
-                'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
987
-                'LIN_total' => $amount,
988
-                'LIN_parent' => $tax_subtotal->ID(),
989
-                'LIN_type' => EEM_Line_Item::type_tax,
990
-                'LIN_code' => $code
991
-            ));
992
-        }
993
-
994
-        $new_tax = apply_filters(
995
-            'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
996
-            $new_tax,
997
-            $total_line_item
998
-        );
999
-        $new_tax->save();
1000
-        $tax_subtotal->set_total($new_tax->total());
1001
-        $tax_subtotal->save();
1002
-        $total_line_item->recalculate_total_including_taxes();
1003
-        return $new_tax;
1004
-    }
1005
-
1006
-
1007
-    /**
1008
-     * Makes all the line items which are children of $line_item taxable (or not).
1009
-     * Does NOT save the line items
1010
-     * @param EE_Line_Item $line_item
1011
-     * @param string $code_substring_for_whitelist if this string is part of the line item's code
1012
-     *  it will be whitelisted (ie, except from becoming taxable)
1013
-     * @param boolean $taxable
1014
-     */
1015
-    public static function set_line_items_taxable(
1016
-        EE_Line_Item $line_item,
1017
-        $taxable = true,
1018
-        $code_substring_for_whitelist = null
1019
-    ) {
1020
-        $whitelisted = false;
1021
-        if ($code_substring_for_whitelist !== null) {
1022
-            $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false ? true : false;
1023
-        }
1024
-        if (!$whitelisted && $line_item->is_line_item()) {
1025
-            $line_item->set_is_taxable($taxable);
1026
-        }
1027
-        foreach ($line_item->children() as $child_line_item) {
1028
-            EEH_Line_Item::set_line_items_taxable($child_line_item, $taxable, $code_substring_for_whitelist);
1029
-        }
1030
-    }
1031
-
1032
-
1033
-    /**
1034
-     * Gets all descendants that are event subtotals
1035
-     *
1036
-     * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1037
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1038
-     * @return EE_Line_Item[]
1039
-     */
1040
-    public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1041
-    {
1042
-        return self::get_subtotals_of_object_type($parent_line_item, 'Event');
1043
-    }
1044
-
1045
-
1046
-    /**
1047
-     * Gets all descendants subtotals that match the supplied object type
1048
-     *
1049
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1050
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1051
-     * @param string $obj_type
1052
-     * @return EE_Line_Item[]
1053
-     */
1054
-    public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1055
-    {
1056
-        return self::_get_descendants_by_type_and_object_type(
1057
-            $parent_line_item,
1058
-            EEM_Line_Item::type_sub_total,
1059
-            $obj_type
1060
-        );
1061
-    }
1062
-
1063
-
1064
-    /**
1065
-     * Gets all descendants that are tickets
1066
-     *
1067
-     * @uses  EEH_Line_Item::get_line_items_of_object_type()
1068
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1069
-     * @return EE_Line_Item[]
1070
-     */
1071
-    public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1072
-    {
1073
-        return self::get_line_items_of_object_type($parent_line_item, 'Ticket');
1074
-    }
1075
-
1076
-
1077
-    /**
1078
-     * Gets all descendants subtotals that match the supplied object type
1079
-     *
1080
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1081
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1082
-     * @param string $obj_type
1083
-     * @return EE_Line_Item[]
1084
-     */
1085
-    public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1086
-    {
1087
-        return self::_get_descendants_by_type_and_object_type($parent_line_item, EEM_Line_Item::type_line_item, $obj_type);
1088
-    }
1089
-
1090
-
1091
-    /**
1092
-     * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1093
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1094
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1095
-     * @return EE_Line_Item[]
1096
-     */
1097
-    public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1098
-    {
1099
-        return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_tax);
1100
-    }
1101
-
1102
-
1103
-    /**
1104
-     * Gets all the real items purchased which are children of this item
1105
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1106
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1107
-     * @return EE_Line_Item[]
1108
-     */
1109
-    public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1110
-    {
1111
-        return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_line_item);
1112
-    }
1113
-
1114
-
1115
-    /**
1116
-     * Gets all descendants of supplied line item that match the supplied line item type
1117
-     *
1118
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1119
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1120
-     * @param string $line_item_type one of the EEM_Line_Item constants
1121
-     * @return EE_Line_Item[]
1122
-     */
1123
-    public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1124
-    {
1125
-        return self::_get_descendants_by_type_and_object_type($parent_line_item, $line_item_type, null);
1126
-    }
1127
-
1128
-
1129
-    /**
1130
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1131
-     *
1132
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1133
-     * @param string $line_item_type one of the EEM_Line_Item constants
1134
-     * @param string | NULL $obj_type object model class name (minus prefix) or NULL to ignore object type when searching
1135
-     * @return EE_Line_Item[]
1136
-     */
1137
-    protected static function _get_descendants_by_type_and_object_type(
1138
-        EE_Line_Item $parent_line_item,
1139
-        $line_item_type,
1140
-        $obj_type = null
1141
-    ) {
1142
-        $objects = array();
1143
-        foreach ($parent_line_item->children() as $child_line_item) {
1144
-            if ($child_line_item instanceof EE_Line_Item) {
1145
-                if ($child_line_item->type() === $line_item_type
1146
-                    && (
1147
-                        $child_line_item->OBJ_type() === $obj_type || $obj_type === null
1148
-                    )
1149
-                ) {
1150
-                    $objects[] = $child_line_item;
1151
-                } else {
1152
-                    // go-through-all-its children looking for more matches
1153
-                    $objects = array_merge(
1154
-                        $objects,
1155
-                        self::_get_descendants_by_type_and_object_type(
1156
-                            $child_line_item,
1157
-                            $line_item_type,
1158
-                            $obj_type
1159
-                        )
1160
-                    );
1161
-                }
1162
-            }
1163
-        }
1164
-        return $objects;
1165
-    }
1166
-
1167
-
1168
-    /**
1169
-     * Gets all descendants subtotals that match the supplied object type
1170
-     *
1171
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1172
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1173
-     * @param string $OBJ_type object type (like Event)
1174
-     * @param array $OBJ_IDs array of OBJ_IDs
1175
-     * @return EE_Line_Item[]
1176
-     */
1177
-    public static function get_line_items_by_object_type_and_IDs(
1178
-        EE_Line_Item $parent_line_item,
1179
-        $OBJ_type = '',
1180
-        $OBJ_IDs = array()
1181
-    ) {
1182
-        return self::_get_descendants_by_object_type_and_object_ID($parent_line_item, $OBJ_type, $OBJ_IDs);
1183
-    }
1184
-
1185
-
1186
-    /**
1187
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1188
-     *
1189
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1190
-     * @param string $OBJ_type object type (like Event)
1191
-     * @param array $OBJ_IDs array of OBJ_IDs
1192
-     * @return EE_Line_Item[]
1193
-     */
1194
-    protected static function _get_descendants_by_object_type_and_object_ID(
1195
-        EE_Line_Item $parent_line_item,
1196
-        $OBJ_type,
1197
-        $OBJ_IDs
1198
-    ) {
1199
-        $objects = array();
1200
-        foreach ($parent_line_item->children() as $child_line_item) {
1201
-            if ($child_line_item instanceof EE_Line_Item) {
1202
-                if ($child_line_item->OBJ_type() === $OBJ_type
1203
-                    && is_array($OBJ_IDs)
1204
-                    && in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1205
-                ) {
1206
-                    $objects[] = $child_line_item;
1207
-                } else {
1208
-                    // go-through-all-its children looking for more matches
1209
-                    $objects = array_merge(
1210
-                        $objects,
1211
-                        self::_get_descendants_by_object_type_and_object_ID(
1212
-                            $child_line_item,
1213
-                            $OBJ_type,
1214
-                            $OBJ_IDs
1215
-                        )
1216
-                    );
1217
-                }
1218
-            }
1219
-        }
1220
-        return $objects;
1221
-    }
1222
-
1223
-
1224
-    /**
1225
-     * Uses a breadth-first-search in order to find the nearest descendant of
1226
-     * the specified type and returns it, else NULL
1227
-     *
1228
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1229
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1230
-     * @param string $type like one of the EEM_Line_Item::type_*
1231
-     * @return EE_Line_Item
1232
-     */
1233
-    public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1234
-    {
1235
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1236
-    }
1237
-
1238
-
1239
-    /**
1240
-     * Uses a breadth-first-search in order to find the nearest descendant
1241
-     * having the specified LIN_code and returns it, else NULL
1242
-     *
1243
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1244
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1245
-     * @param string $code any value used for LIN_code
1246
-     * @return EE_Line_Item
1247
-     */
1248
-    public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1249
-    {
1250
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1251
-    }
1252
-
1253
-
1254
-    /**
1255
-     * Uses a breadth-first-search in order to find the nearest descendant
1256
-     * having the specified LIN_code and returns it, else NULL
1257
-     *
1258
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1259
-     * @param string $search_field name of EE_Line_Item property
1260
-     * @param string $value any value stored in $search_field
1261
-     * @return EE_Line_Item
1262
-     */
1263
-    protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1264
-    {
1265
-        foreach ($parent_line_item->children() as $child) {
1266
-            if ($child->get($search_field) == $value) {
1267
-                return $child;
1268
-            }
1269
-        }
1270
-        foreach ($parent_line_item->children() as $child) {
1271
-            $descendant_found = self::_get_nearest_descendant($child, $search_field, $value);
1272
-            if ($descendant_found) {
1273
-                return $descendant_found;
1274
-            }
1275
-        }
1276
-        return null;
1277
-    }
1278
-
1279
-
1280
-    /**
1281
-     * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1282
-     * else recursively walks up the line item tree until a parent of type total is found,
1283
-     *
1284
-     * @param EE_Line_Item $line_item
1285
-     * @return \EE_Line_Item
1286
-     * @throws \EE_Error
1287
-     */
1288
-    public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item)
1289
-    {
1290
-        if ($line_item->TXN_ID()) {
1291
-            $total_line_item = $line_item->transaction()->total_line_item(false);
1292
-            if ($total_line_item instanceof EE_Line_Item) {
1293
-                return $total_line_item;
1294
-            }
1295
-        } else {
1296
-            $line_item_parent = $line_item->parent();
1297
-            if ($line_item_parent instanceof EE_Line_Item) {
1298
-                if ($line_item_parent->is_total()) {
1299
-                    return $line_item_parent;
1300
-                }
1301
-                return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1302
-            }
1303
-        }
1304
-        throw new EE_Error(
1305
-            sprintf(
1306
-                __('A valid grand total for line item %1$d was not found.', 'event_espresso'),
1307
-                $line_item->ID()
1308
-            )
1309
-        );
1310
-    }
1311
-
1312
-
1313
-    /**
1314
-     * Prints out a representation of the line item tree
1315
-     *
1316
-     * @param EE_Line_Item $line_item
1317
-     * @param int $indentation
1318
-     * @return void
1319
-     * @throws \EE_Error
1320
-     */
1321
-    public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1322
-    {
1323
-        echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1324
-        if (!$indentation) {
1325
-            echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1326
-        }
1327
-        for ($i = 0; $i < $indentation; $i++) {
1328
-            echo ". ";
1329
-        }
1330
-        $breakdown = '';
1331
-        if ($line_item->is_line_item()) {
1332
-            if ($line_item->is_percent()) {
1333
-                $breakdown = "{$line_item->percent()}%";
1334
-            } else {
1335
-                $breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1336
-            }
1337
-        }
1338
-        echo $line_item->name() . " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : " . '$' . "{$line_item->total()}";
1339
-        if ($breakdown) {
1340
-            echo " ( {$breakdown} )";
1341
-        }
1342
-        if ($line_item->is_taxable()) {
1343
-            echo "  * taxable";
1344
-        }
1345
-        if ($line_item->children()) {
1346
-            foreach ($line_item->children() as $child) {
1347
-                self::visualize($child, $indentation + 1);
1348
-            }
1349
-        }
1350
-    }
1351
-
1352
-
1353
-    /**
1354
-     * Calculates the registration's final price, taking into account that they
1355
-     * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1356
-     * and receive a portion of any transaction-wide discounts.
1357
-     * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1358
-     * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1359
-     * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1360
-     * and brent's final price should be $5.50.
1361
-     *
1362
-     * In order to do this, we basically need to traverse the line item tree calculating
1363
-     * the running totals (just as if we were recalculating the total), but when we identify
1364
-     * regular line items, we need to keep track of their share of the grand total.
1365
-     * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1366
-     * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1367
-     * when there are non-taxable items; otherwise they would be the same)
1368
-     *
1369
-     * @param EE_Line_Item $line_item
1370
-     * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity that
1371
-     *                                                                            can be included in price calculations at this moment
1372
-     * @return array        keys are line items for tickets IDs and values are their share of the running total,
1373
-     *                                          plus the key 'total', and 'taxable' which also has keys of all the ticket IDs. Eg
1374
-     *                                          array(
1375
-     *                                          12 => 4.3
1376
-     *                                          23 => 8.0
1377
-     *                                          'total' => 16.6,
1378
-     *                                          'taxable' => array(
1379
-     *                                          12 => 10,
1380
-     *                                          23 => 4
1381
-     *                                          ).
1382
-     *                                          So to find which registrations have which final price, we need to find which line item
1383
-     *                                          is theirs, which can be done with
1384
-     *                                          `EEM_Line_Item::instance()->get_line_item_for_registration( $registration );`
1385
-     */
1386
-    public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array())
1387
-    {
1388
-        // init running grand total if not already
1389
-        if (!isset($running_totals['total'])) {
1390
-            $running_totals['total'] = 0;
1391
-        }
1392
-        if (!isset($running_totals['taxable'])) {
1393
-            $running_totals['taxable'] = array('total' => 0);
1394
-        }
1395
-        foreach ($line_item->children() as $child_line_item) {
1396
-            switch ($child_line_item->type()) {
1397
-                case EEM_Line_Item::type_sub_total:
1398
-                    $running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item($child_line_item, $billable_ticket_quantities);
1399
-                    // combine arrays but preserve numeric keys
1400
-                    $running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1401
-                    $running_totals['total'] += $running_totals_from_subtotal['total'];
1402
-                    $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1403
-                    break;
1404
-
1405
-                case EEM_Line_Item::type_tax_sub_total:
1406
-                    // find how much the taxes percentage is
1407
-                    if ($child_line_item->percent() !== 0) {
1408
-                        $tax_percent_decimal = $child_line_item->percent() / 100;
1409
-                    } else {
1410
-                        $tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1411
-                    }
1412
-                    // and apply to all the taxable totals, and add to the pretax totals
1413
-                    foreach ($running_totals as $line_item_id => $this_running_total) {
1414
-                        // "total" and "taxable" array key is an exception
1415
-                        if ($line_item_id === 'taxable') {
1416
-                            continue;
1417
-                        }
1418
-                        $taxable_total = $running_totals['taxable'][ $line_item_id ];
1419
-                        $running_totals[ $line_item_id ] += ($taxable_total * $tax_percent_decimal);
1420
-                    }
1421
-                    break;
1422
-
1423
-                case EEM_Line_Item::type_line_item:
1424
-                    // ticket line items or ????
1425
-                    if ($child_line_item->OBJ_type() === 'Ticket') {
1426
-                        // kk it's a ticket
1427
-                        if (isset($running_totals[ $child_line_item->ID() ])) {
1428
-                            // huh? that shouldn't happen.
1429
-                            $running_totals['total'] += $child_line_item->total();
1430
-                        } else {
1431
-                            // its not in our running totals yet. great.
1432
-                            if ($child_line_item->is_taxable()) {
1433
-                                $taxable_amount = $child_line_item->unit_price();
1434
-                            } else {
1435
-                                $taxable_amount = 0;
1436
-                            }
1437
-                            // are we only calculating totals for some tickets?
1438
-                            if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) {
1439
-                                $quantity = $billable_ticket_quantities[ $child_line_item->OBJ_ID() ];
1440
-                                $running_totals[ $child_line_item->ID() ] = $quantity
1441
-                                    ? $child_line_item->unit_price()
1442
-                                    : 0;
1443
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $quantity
1444
-                                    ? $taxable_amount
1445
-                                    : 0;
1446
-                            } else {
1447
-                                $quantity = $child_line_item->quantity();
1448
-                                $running_totals[ $child_line_item->ID() ] = $child_line_item->unit_price();
1449
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount;
1450
-                            }
1451
-                            $running_totals['taxable']['total'] += $taxable_amount * $quantity;
1452
-                            $running_totals['total'] += $child_line_item->unit_price() * $quantity;
1453
-                        }
1454
-                    } else {
1455
-                        // it's some other type of item added to the cart
1456
-                        // it should affect the running totals
1457
-                        // basically we want to convert it into a PERCENT modifier. Because
1458
-                        // more clearly affect all registration's final price equally
1459
-                        $line_items_percent_of_running_total = $running_totals['total'] > 0
1460
-                            ? ($child_line_item->total() / $running_totals['total']) + 1
1461
-                            : 1;
1462
-                        foreach ($running_totals as $line_item_id => $this_running_total) {
1463
-                            // the "taxable" array key is an exception
1464
-                            if ($line_item_id === 'taxable') {
1465
-                                continue;
1466
-                            }
1467
-                            // update the running totals
1468
-                            // yes this actually even works for the running grand total!
1469
-                            $running_totals[ $line_item_id ] =
1470
-                                $line_items_percent_of_running_total * $this_running_total;
1471
-
1472
-                            if ($child_line_item->is_taxable()) {
1473
-                                $running_totals['taxable'][ $line_item_id ] =
1474
-                                    $line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ];
1475
-                            }
1476
-                        }
1477
-                    }
1478
-                    break;
1479
-            }
1480
-        }
1481
-        return $running_totals;
1482
-    }
1483
-
1484
-
1485
-    /**
1486
-     * @param \EE_Line_Item $total_line_item
1487
-     * @param \EE_Line_Item $ticket_line_item
1488
-     * @return float | null
1489
-     * @throws \OutOfRangeException
1490
-     */
1491
-    public static function calculate_final_price_for_ticket_line_item(\EE_Line_Item $total_line_item, \EE_Line_Item $ticket_line_item)
1492
-    {
1493
-        static $final_prices_per_ticket_line_item = array();
1494
-        if (empty($final_prices_per_ticket_line_item)) {
1495
-            $final_prices_per_ticket_line_item = \EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1496
-                $total_line_item
1497
-            );
1498
-        }
1499
-        // ok now find this new registration's final price
1500
-        if (isset($final_prices_per_ticket_line_item[ $ticket_line_item->ID() ])) {
1501
-            return $final_prices_per_ticket_line_item[ $ticket_line_item->ID() ];
1502
-        }
1503
-        $message = sprintf(
1504
-            __(
1505
-                'The final price for the ticket line item (ID:%1$d) could not be calculated.',
1506
-                'event_espresso'
1507
-            ),
1508
-            $ticket_line_item->ID()
1509
-        );
1510
-        if (WP_DEBUG) {
1511
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1512
-            throw new \OutOfRangeException($message);
1513
-        } else {
1514
-            EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1515
-        }
1516
-        return null;
1517
-    }
1518
-
1519
-
1520
-    /**
1521
-     * Creates a duplicate of the line item tree, except only includes billable items
1522
-     * and the portion of line items attributed to billable things
1523
-     *
1524
-     * @param EE_Line_Item $line_item
1525
-     * @param EE_Registration[] $registrations
1526
-     * @return \EE_Line_Item
1527
-     * @throws \EE_Error
1528
-     */
1529
-    public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1530
-    {
1531
-        $copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1532
-        foreach ($line_item->children() as $child_li) {
1533
-            $copy_li->add_child_line_item(EEH_Line_Item::billable_line_item_tree($child_li, $registrations));
1534
-        }
1535
-        // if this is the grand total line item, make sure the totals all add up
1536
-        // (we could have duplicated this logic AS we copied the line items, but
1537
-        // it seems DRYer this way)
1538
-        if ($copy_li->type() === EEM_Line_Item::type_total) {
1539
-            $copy_li->recalculate_total_including_taxes();
1540
-        }
1541
-        return $copy_li;
1542
-    }
1543
-
1544
-
1545
-    /**
1546
-     * Creates a new, unsaved line item from $line_item that factors in the
1547
-     * number of billable registrations on $registrations.
1548
-     *
1549
-     * @param EE_Line_Item $line_item
1550
-     * @return EE_Line_Item
1551
-     * @throws \EE_Error
1552
-     * @param EE_Registration[] $registrations
1553
-     */
1554
-    public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1555
-    {
1556
-        $new_li_fields = $line_item->model_field_array();
1557
-        if ($line_item->type() === EEM_Line_Item::type_line_item &&
1558
-            $line_item->OBJ_type() === 'Ticket'
1559
-        ) {
1560
-            $count = 0;
1561
-            foreach ($registrations as $registration) {
1562
-                if ($line_item->OBJ_ID() === $registration->ticket_ID() &&
1563
-                    in_array($registration->status_ID(), EEM_Registration::reg_statuses_that_allow_payment())
1564
-                ) {
1565
-                    $count++;
1566
-                }
1567
-            }
1568
-            $new_li_fields['LIN_quantity'] = $count;
1569
-        }
1570
-        // don't set the total. We'll leave that up to the code that calculates it
1571
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1572
-        return EE_Line_Item::new_instance($new_li_fields);
1573
-    }
1574
-
1575
-
1576
-    /**
1577
-     * Returns a modified line item tree where all the subtotals which have a total of 0
1578
-     * are removed, and line items with a quantity of 0
1579
-     *
1580
-     * @param EE_Line_Item $line_item |null
1581
-     * @return \EE_Line_Item|null
1582
-     * @throws \EE_Error
1583
-     */
1584
-    public static function non_empty_line_items(EE_Line_Item $line_item)
1585
-    {
1586
-        $copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1587
-        if ($copied_li === null) {
1588
-            return null;
1589
-        }
1590
-        // if this is an event subtotal, we want to only include it if it
1591
-        // has a non-zero total and at least one ticket line item child
1592
-        $ticket_children = 0;
1593
-        foreach ($line_item->children() as $child_li) {
1594
-            $child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1595
-            if ($child_li_copy !== null) {
1596
-                $copied_li->add_child_line_item($child_li_copy);
1597
-                if ($child_li_copy->type() === EEM_Line_Item::type_line_item &&
1598
-                    $child_li_copy->OBJ_type() === 'Ticket'
1599
-                ) {
1600
-                    $ticket_children++;
1601
-                }
1602
-            }
1603
-        }
1604
-        // if this is an event subtotal with NO ticket children
1605
-        // we basically want to ignore it
1606
-        if ($ticket_children === 0
1607
-            && $line_item->type() === EEM_Line_Item::type_sub_total
1608
-            && $line_item->OBJ_type() === 'Event'
1609
-            && $line_item->total() === 0
1610
-        ) {
1611
-            return null;
1612
-        }
1613
-        return $copied_li;
1614
-    }
1615
-
1616
-
1617
-    /**
1618
-     * Creates a new, unsaved line item, but if it's a ticket line item
1619
-     * with a total of 0, or a subtotal of 0, returns null instead
1620
-     *
1621
-     * @param EE_Line_Item $line_item
1622
-     * @return EE_Line_Item
1623
-     * @throws \EE_Error
1624
-     */
1625
-    public static function non_empty_line_item(EE_Line_Item $line_item)
1626
-    {
1627
-        if ($line_item->type() === EEM_Line_Item::type_line_item &&
1628
-            $line_item->OBJ_type() === 'Ticket' &&
1629
-            $line_item->quantity() === 0
1630
-        ) {
1631
-            return null;
1632
-        }
1633
-        $new_li_fields = $line_item->model_field_array();
1634
-        // don't set the total. We'll leave that up to the code that calculates it
1635
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1636
-        return EE_Line_Item::new_instance($new_li_fields);
1637
-    }
1638
-
1639
-
1640
-
1641
-    /**************************************** @DEPRECATED METHODS *************************************** */
1642
-    /**
1643
-     * @deprecated
1644
-     * @param EE_Line_Item $total_line_item
1645
-     * @return \EE_Line_Item
1646
-     * @throws \EE_Error
1647
-     */
1648
-    public static function get_items_subtotal(EE_Line_Item $total_line_item)
1649
-    {
1650
-        EE_Error::doing_it_wrong('EEH_Line_Item::get_items_subtotal()', __('Method replaced with EEH_Line_Item::get_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1651
-        return self::get_pre_tax_subtotal($total_line_item);
1652
-    }
1653
-
1654
-
1655
-    /**
1656
-     * @deprecated
1657
-     * @param EE_Transaction $transaction
1658
-     * @return \EE_Line_Item
1659
-     * @throws \EE_Error
1660
-     */
1661
-    public static function create_default_total_line_item($transaction = null)
1662
-    {
1663
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_total_line_item()', __('Method replaced with EEH_Line_Item::create_total_line_item()', 'event_espresso'), '4.6.0');
1664
-        return self::create_total_line_item($transaction);
1665
-    }
1666
-
1667
-
1668
-    /**
1669
-     * @deprecated
1670
-     * @param EE_Line_Item $total_line_item
1671
-     * @param EE_Transaction $transaction
1672
-     * @return \EE_Line_Item
1673
-     * @throws \EE_Error
1674
-     */
1675
-    public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = null)
1676
-    {
1677
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_tickets_subtotal()', __('Method replaced with EEH_Line_Item::create_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1678
-        return self::create_pre_tax_subtotal($total_line_item, $transaction);
1679
-    }
1680
-
1681
-
1682
-    /**
1683
-     * @deprecated
1684
-     * @param EE_Line_Item $total_line_item
1685
-     * @param EE_Transaction $transaction
1686
-     * @return \EE_Line_Item
1687
-     * @throws \EE_Error
1688
-     */
1689
-    public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
1690
-    {
1691
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_taxes_subtotal()', __('Method replaced with EEH_Line_Item::create_taxes_subtotal()', 'event_espresso'), '4.6.0');
1692
-        return self::create_taxes_subtotal($total_line_item, $transaction);
1693
-    }
1694
-
1695
-
1696
-    /**
1697
-     * @deprecated
1698
-     * @param EE_Line_Item $total_line_item
1699
-     * @param EE_Transaction $transaction
1700
-     * @return \EE_Line_Item
1701
-     * @throws \EE_Error
1702
-     */
1703
-    public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = null)
1704
-    {
1705
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_event_subtotal()', __('Method replaced with EEH_Line_Item::create_event_subtotal()', 'event_espresso'), '4.6.0');
1706
-        return self::create_event_subtotal($total_line_item, $transaction);
1707
-    }
23
+	// other functions: cancel ticket purchase
24
+	// delete ticket purchase
25
+	// add promotion
26
+
27
+
28
+	/**
29
+	 * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
30
+	 * Does NOT automatically re-calculate the line item totals or update the related transaction.
31
+	 * You should call recalculate_total_including_taxes() on the grant total line item after this
32
+	 * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
33
+	 * to keep the registration final prices in-sync with the transaction's total.
34
+	 *
35
+	 * @param EE_Line_Item $parent_line_item
36
+	 * @param string $name
37
+	 * @param float $unit_price
38
+	 * @param string $description
39
+	 * @param int $quantity
40
+	 * @param boolean $taxable
41
+	 * @param boolean $code if set to a value, ensures there is only one line item with that code
42
+	 * @return boolean success
43
+	 * @throws \EE_Error
44
+	 */
45
+	public static function add_unrelated_item(EE_Line_Item $parent_line_item, $name, $unit_price, $description = '', $quantity = 1, $taxable = false, $code = null)
46
+	{
47
+		$items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
48
+		$line_item = EE_Line_Item::new_instance(array(
49
+			'LIN_name' => $name,
50
+			'LIN_desc' => $description,
51
+			'LIN_unit_price' => $unit_price,
52
+			'LIN_quantity' => $quantity,
53
+			'LIN_percent' => null,
54
+			'LIN_is_taxable' => $taxable,
55
+			'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0,
56
+			'LIN_total' => (float) $unit_price * (int) $quantity,
57
+			'LIN_type' => EEM_Line_Item::type_line_item,
58
+			'LIN_code' => $code,
59
+		));
60
+		$line_item = apply_filters(
61
+			'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
62
+			$line_item,
63
+			$parent_line_item
64
+		);
65
+		return self::add_item($parent_line_item, $line_item);
66
+	}
67
+
68
+
69
+	/**
70
+	 * Adds a simple item ( unrelated to any other model object) to the total line item,
71
+	 * in the correct spot in the line item tree. Automatically
72
+	 * re-calculates the line item totals and updates the related transaction. But
73
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
74
+	 * should probably change because of this).
75
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
76
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
77
+	 *
78
+	 * @param EE_Line_Item $parent_line_item
79
+	 * @param string $name
80
+	 * @param float $percentage_amount
81
+	 * @param string $description
82
+	 * @param boolean $taxable
83
+	 * @return boolean success
84
+	 * @throws \EE_Error
85
+	 */
86
+	public static function add_percentage_based_item(EE_Line_Item $parent_line_item, $name, $percentage_amount, $description = '', $taxable = false)
87
+	{
88
+		$line_item = EE_Line_Item::new_instance(array(
89
+			'LIN_name' => $name,
90
+			'LIN_desc' => $description,
91
+			'LIN_unit_price' => 0,
92
+			'LIN_percent' => $percentage_amount,
93
+			'LIN_quantity' => 1,
94
+			'LIN_is_taxable' => $taxable,
95
+			'LIN_total' => (float) ($percentage_amount * ($parent_line_item->total() / 100)),
96
+			'LIN_type' => EEM_Line_Item::type_line_item,
97
+			'LIN_parent' => $parent_line_item->ID()
98
+		));
99
+		$line_item = apply_filters(
100
+			'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
101
+			$line_item
102
+		);
103
+		return $parent_line_item->add_child_line_item($line_item, false);
104
+	}
105
+
106
+
107
+	/**
108
+	 * Returns the new line item created by adding a purchase of the ticket
109
+	 * ensures that ticket line item is saved, and that cart total has been recalculated.
110
+	 * If this ticket has already been purchased, just increments its count.
111
+	 * Automatically re-calculates the line item totals and updates the related transaction. But
112
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
113
+	 * should probably change because of this).
114
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
115
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
116
+	 *
117
+	 * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
118
+	 * @param EE_Ticket $ticket
119
+	 * @param int $qty
120
+	 * @return \EE_Line_Item
121
+	 * @throws \EE_Error
122
+	 */
123
+	public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
124
+	{
125
+		if (!$total_line_item instanceof EE_Line_Item || !$total_line_item->is_total()) {
126
+			throw new EE_Error(sprintf(__('A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', 'event_espresso'), $ticket->ID(), $total_line_item->ID()));
127
+		}
128
+		// either increment the qty for an existing ticket
129
+		$line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
130
+		// or add a new one
131
+		if (!$line_item instanceof EE_Line_Item) {
132
+			$line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
133
+		}
134
+		$total_line_item->recalculate_total_including_taxes();
135
+		return $line_item;
136
+	}
137
+
138
+
139
+	/**
140
+	 * Returns the new line item created by adding a purchase of the ticket
141
+	 * @param \EE_Line_Item $total_line_item
142
+	 * @param EE_Ticket $ticket
143
+	 * @param int $qty
144
+	 * @return \EE_Line_Item
145
+	 * @throws \EE_Error
146
+	 */
147
+	public static function increment_ticket_qty_if_already_in_cart(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
148
+	{
149
+		$line_item = null;
150
+		if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
151
+			$ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
152
+			foreach ((array) $ticket_line_items as $ticket_line_item) {
153
+				if ($ticket_line_item instanceof EE_Line_Item
154
+					&& (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID()
155
+				) {
156
+					$line_item = $ticket_line_item;
157
+					break;
158
+				}
159
+			}
160
+		}
161
+		if ($line_item instanceof EE_Line_Item) {
162
+			EEH_Line_Item::increment_quantity($line_item, $qty);
163
+			return $line_item;
164
+		}
165
+		return null;
166
+	}
167
+
168
+
169
+	/**
170
+	 * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
171
+	 * Does NOT save or recalculate other line items totals
172
+	 *
173
+	 * @param EE_Line_Item $line_item
174
+	 * @param int $qty
175
+	 * @return void
176
+	 * @throws \EE_Error
177
+	 */
178
+	public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
179
+	{
180
+		if (!$line_item->is_percent()) {
181
+			$qty += $line_item->quantity();
182
+			$line_item->set_quantity($qty);
183
+			$line_item->set_total($line_item->unit_price() * $qty);
184
+			$line_item->save();
185
+		}
186
+		foreach ($line_item->children() as $child) {
187
+			if ($child->is_sub_line_item()) {
188
+				EEH_Line_Item::update_quantity($child, $qty);
189
+			}
190
+		}
191
+	}
192
+
193
+
194
+	/**
195
+	 * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
196
+	 * Does NOT save or recalculate other line items totals
197
+	 *
198
+	 * @param EE_Line_Item $line_item
199
+	 * @param int $qty
200
+	 * @return void
201
+	 * @throws \EE_Error
202
+	 */
203
+	public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
204
+	{
205
+		if (!$line_item->is_percent()) {
206
+			$qty = $line_item->quantity() - $qty;
207
+			$qty = max($qty, 0);
208
+			$line_item->set_quantity($qty);
209
+			$line_item->set_total($line_item->unit_price() * $qty);
210
+			$line_item->save();
211
+		}
212
+		foreach ($line_item->children() as $child) {
213
+			if ($child->is_sub_line_item()) {
214
+				EEH_Line_Item::update_quantity($child, $qty);
215
+			}
216
+		}
217
+	}
218
+
219
+
220
+	/**
221
+	 * Updates the line item and its children's quantities to the specified number.
222
+	 * Does NOT save them or recalculate totals.
223
+	 *
224
+	 * @param EE_Line_Item $line_item
225
+	 * @param int $new_quantity
226
+	 * @throws \EE_Error
227
+	 */
228
+	public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
229
+	{
230
+		if (!$line_item->is_percent()) {
231
+			$line_item->set_quantity($new_quantity);
232
+			$line_item->set_total($line_item->unit_price() * $new_quantity);
233
+			$line_item->save();
234
+		}
235
+		foreach ($line_item->children() as $child) {
236
+			if ($child->is_sub_line_item()) {
237
+				EEH_Line_Item::update_quantity($child, $new_quantity);
238
+			}
239
+		}
240
+	}
241
+
242
+
243
+	/**
244
+	 * Returns the new line item created by adding a purchase of the ticket
245
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
246
+	 * @param EE_Ticket $ticket
247
+	 * @param int $qty
248
+	 * @return \EE_Line_Item
249
+	 * @throws \EE_Error
250
+	 */
251
+	public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
252
+	{
253
+		$datetimes = $ticket->datetimes();
254
+		$first_datetime = reset($datetimes);
255
+		if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
256
+			$first_datetime_name = $first_datetime->event()->name();
257
+		} else {
258
+			$first_datetime_name = __('Event', 'event_espresso');
259
+		}
260
+		$event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
261
+		// get event subtotal line
262
+		$events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
263
+		// add $ticket to cart
264
+		$line_item = EE_Line_Item::new_instance(array(
265
+			'LIN_name' => $ticket->name(),
266
+			'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
267
+			'LIN_unit_price' => $ticket->price(),
268
+			'LIN_quantity' => $qty,
269
+			'LIN_is_taxable' => $ticket->taxable(),
270
+			'LIN_order' => count($events_sub_total->children()),
271
+			'LIN_total' => $ticket->price() * $qty,
272
+			'LIN_type' => EEM_Line_Item::type_line_item,
273
+			'OBJ_ID' => $ticket->ID(),
274
+			'OBJ_type' => 'Ticket'
275
+		));
276
+		$line_item = apply_filters(
277
+			'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
278
+			$line_item
279
+		);
280
+		$events_sub_total->add_child_line_item($line_item);
281
+		// now add the sub-line items
282
+		$running_total_for_ticket = 0;
283
+		foreach ($ticket->prices(array('order_by' => array('PRC_order' => 'ASC'))) as $price) {
284
+			$sign = $price->is_discount() ? -1 : 1;
285
+			$price_total = $price->is_percent()
286
+				? $running_total_for_ticket * $price->amount() / 100
287
+				: $price->amount() * $qty;
288
+			$sub_line_item = EE_Line_Item::new_instance(array(
289
+				'LIN_name' => $price->name(),
290
+				'LIN_desc' => $price->desc(),
291
+				'LIN_quantity' => $price->is_percent() ? null : $qty,
292
+				'LIN_is_taxable' => false,
293
+				'LIN_order' => $price->order(),
294
+				'LIN_total' => $sign * $price_total,
295
+				'LIN_type' => EEM_Line_Item::type_sub_line_item,
296
+				'OBJ_ID' => $price->ID(),
297
+				'OBJ_type' => 'Price'
298
+			));
299
+			$sub_line_item = apply_filters(
300
+				'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
301
+				$sub_line_item
302
+			);
303
+			if ($price->is_percent()) {
304
+				$sub_line_item->set_percent($sign * $price->amount());
305
+			} else {
306
+				$sub_line_item->set_unit_price($sign * $price->amount());
307
+			}
308
+			$running_total_for_ticket += $price_total;
309
+			$line_item->add_child_line_item($sub_line_item);
310
+		}
311
+		return $line_item;
312
+	}
313
+
314
+
315
+	/**
316
+	 * Adds the specified item under the pre-tax-sub-total line item. Automatically
317
+	 * re-calculates the line item totals and updates the related transaction. But
318
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
319
+	 * should probably change because of this).
320
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
321
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
322
+	 *
323
+	 * @param EE_Line_Item $total_line_item
324
+	 * @param EE_Line_Item $item to be added
325
+	 * @return boolean
326
+	 * @throws \EE_Error
327
+	 */
328
+	public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item)
329
+	{
330
+		$pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
331
+		if ($pre_tax_subtotal instanceof EE_Line_Item) {
332
+			$success = $pre_tax_subtotal->add_child_line_item($item);
333
+		} else {
334
+			return false;
335
+		}
336
+		$total_line_item->recalculate_total_including_taxes();
337
+		return $success;
338
+	}
339
+
340
+
341
+	/**
342
+	 * cancels an existing ticket line item,
343
+	 * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
344
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
345
+	 *
346
+	 * @param EE_Line_Item $ticket_line_item
347
+	 * @param int $qty
348
+	 * @return bool success
349
+	 * @throws \EE_Error
350
+	 */
351
+	public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
352
+	{
353
+		// validate incoming line_item
354
+		if ($ticket_line_item->OBJ_type() !== 'Ticket') {
355
+			throw new EE_Error(
356
+				sprintf(
357
+					__('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
358
+					$ticket_line_item->type()
359
+				)
360
+			);
361
+		}
362
+		if ($ticket_line_item->quantity() < $qty) {
363
+			throw new EE_Error(
364
+				sprintf(
365
+					__('Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.', 'event_espresso'),
366
+					$qty,
367
+					$ticket_line_item->quantity()
368
+				)
369
+			);
370
+		}
371
+		// decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
372
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
373
+		foreach ($ticket_line_item->children() as $child_line_item) {
374
+			if ($child_line_item->is_sub_line_item()
375
+				&& !$child_line_item->is_percent()
376
+				&& !$child_line_item->is_cancellation()
377
+			) {
378
+				$child_line_item->set_quantity($child_line_item->quantity() - $qty);
379
+			}
380
+		}
381
+		// get cancellation sub line item
382
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
383
+			$ticket_line_item,
384
+			EEM_Line_Item::type_cancellation
385
+		);
386
+		$cancellation_line_item = reset($cancellation_line_item);
387
+		// verify that this ticket was indeed previously cancelled
388
+		if ($cancellation_line_item instanceof EE_Line_Item) {
389
+			// increment cancelled quantity
390
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
391
+		} else {
392
+			// create cancellation sub line item
393
+			$cancellation_line_item = EE_Line_Item::new_instance(array(
394
+				'LIN_name' => __('Cancellation', 'event_espresso'),
395
+				'LIN_desc' => sprintf(
396
+					_x('Cancelled %1$s : %2$s', 'Cancelled Ticket Name : 2015-01-01 11:11', 'event_espresso'),
397
+					$ticket_line_item->name(),
398
+					current_time(get_option('date_format') . ' ' . get_option('time_format'))
399
+				),
400
+				'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
401
+				'LIN_quantity' => $qty,
402
+				'LIN_is_taxable' => $ticket_line_item->is_taxable(),
403
+				'LIN_order' => count($ticket_line_item->children()),
404
+				'LIN_total' => 0, // $ticket_line_item->unit_price()
405
+				'LIN_type' => EEM_Line_Item::type_cancellation,
406
+			));
407
+			$ticket_line_item->add_child_line_item($cancellation_line_item);
408
+		}
409
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
410
+			// decrement parent line item quantity
411
+			$event_line_item = $ticket_line_item->parent();
412
+			if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
413
+				$event_line_item->set_quantity($event_line_item->quantity() - $qty);
414
+				$event_line_item->save();
415
+			}
416
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
417
+			return true;
418
+		}
419
+		return false;
420
+	}
421
+
422
+
423
+	/**
424
+	 * reinstates (un-cancels?) a previously canceled ticket line item,
425
+	 * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
426
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
427
+	 *
428
+	 * @param EE_Line_Item $ticket_line_item
429
+	 * @param int $qty
430
+	 * @return bool success
431
+	 * @throws \EE_Error
432
+	 */
433
+	public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
434
+	{
435
+		// validate incoming line_item
436
+		if ($ticket_line_item->OBJ_type() !== 'Ticket') {
437
+			throw new EE_Error(
438
+				sprintf(
439
+					__('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
440
+					$ticket_line_item->type()
441
+				)
442
+			);
443
+		}
444
+		// get cancellation sub line item
445
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
446
+			$ticket_line_item,
447
+			EEM_Line_Item::type_cancellation
448
+		);
449
+		$cancellation_line_item = reset($cancellation_line_item);
450
+		// verify that this ticket was indeed previously cancelled
451
+		if (!$cancellation_line_item instanceof EE_Line_Item) {
452
+			return false;
453
+		}
454
+		if ($cancellation_line_item->quantity() > $qty) {
455
+			// decrement cancelled quantity
456
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
457
+		} elseif ($cancellation_line_item->quantity() == $qty) {
458
+			// decrement cancelled quantity in case anyone still has the object kicking around
459
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
460
+			// delete because quantity will end up as 0
461
+			$cancellation_line_item->delete();
462
+			// and attempt to destroy the object,
463
+			// even though PHP won't actually destroy it until it needs the memory
464
+			unset($cancellation_line_item);
465
+		} else {
466
+			// what ?!?! negative quantity ?!?!
467
+			throw new EE_Error(
468
+				sprintf(
469
+					__(
470
+						'Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
471
+						'event_espresso'
472
+					),
473
+					$qty,
474
+					$cancellation_line_item->quantity()
475
+				)
476
+			);
477
+		}
478
+		// increment ticket quantity
479
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
480
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
481
+			// increment parent line item quantity
482
+			$event_line_item = $ticket_line_item->parent();
483
+			if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
484
+				$event_line_item->set_quantity($event_line_item->quantity() + $qty);
485
+			}
486
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
487
+			return true;
488
+		}
489
+		return false;
490
+	}
491
+
492
+
493
+	/**
494
+	 * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
495
+	 * then EE_Line_Item::recalculate_total_including_taxes() on the result
496
+	 *
497
+	 * @param EE_Line_Item $line_item
498
+	 * @return \EE_Line_Item
499
+	 */
500
+	public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
501
+	{
502
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
503
+		return $grand_total_line_item->recalculate_total_including_taxes();
504
+	}
505
+
506
+
507
+	/**
508
+	 * Gets the line item which contains the subtotal of all the items
509
+	 *
510
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
511
+	 * @return \EE_Line_Item
512
+	 * @throws \EE_Error
513
+	 */
514
+	public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
515
+	{
516
+		$pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
517
+		return $pre_tax_subtotal instanceof EE_Line_Item
518
+			? $pre_tax_subtotal
519
+			: self::create_pre_tax_subtotal($total_line_item);
520
+	}
521
+
522
+
523
+	/**
524
+	 * Gets the line item for the taxes subtotal
525
+	 *
526
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
527
+	 * @return \EE_Line_Item
528
+	 * @throws \EE_Error
529
+	 */
530
+	public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
531
+	{
532
+		$taxes = $total_line_item->get_child_line_item('taxes');
533
+		return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
534
+	}
535
+
536
+
537
+	/**
538
+	 * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
539
+	 *
540
+	 * @param EE_Line_Item $line_item
541
+	 * @param EE_Transaction $transaction
542
+	 * @return void
543
+	 * @throws \EE_Error
544
+	 */
545
+	public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = null)
546
+	{
547
+		if ($transaction) {
548
+			/** @type EEM_Transaction $EEM_Transaction */
549
+			$EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
550
+			$TXN_ID = $EEM_Transaction->ensure_is_ID($transaction);
551
+			$line_item->set_TXN_ID($TXN_ID);
552
+		}
553
+	}
554
+
555
+
556
+	/**
557
+	 * Creates a new default total line item for the transaction,
558
+	 * and its tickets subtotal and taxes subtotal line items (and adds the
559
+	 * existing taxes as children of the taxes subtotal line item)
560
+	 *
561
+	 * @param EE_Transaction $transaction
562
+	 * @return \EE_Line_Item of type total
563
+	 * @throws \EE_Error
564
+	 */
565
+	public static function create_total_line_item($transaction = null)
566
+	{
567
+		$total_line_item = EE_Line_Item::new_instance(array(
568
+			'LIN_code' => 'total',
569
+			'LIN_name' => __('Grand Total', 'event_espresso'),
570
+			'LIN_type' => EEM_Line_Item::type_total,
571
+			'OBJ_type' => 'Transaction'
572
+		));
573
+		$total_line_item = apply_filters(
574
+			'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
575
+			$total_line_item
576
+		);
577
+		self::set_TXN_ID($total_line_item, $transaction);
578
+		self::create_pre_tax_subtotal($total_line_item, $transaction);
579
+		self::create_taxes_subtotal($total_line_item, $transaction);
580
+		return $total_line_item;
581
+	}
582
+
583
+
584
+	/**
585
+	 * Creates a default items subtotal line item
586
+	 *
587
+	 * @param EE_Line_Item $total_line_item
588
+	 * @param EE_Transaction $transaction
589
+	 * @return EE_Line_Item
590
+	 * @throws \EE_Error
591
+	 */
592
+	protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = null)
593
+	{
594
+		$pre_tax_line_item = EE_Line_Item::new_instance(array(
595
+			'LIN_code' => 'pre-tax-subtotal',
596
+			'LIN_name' => __('Pre-Tax Subtotal', 'event_espresso'),
597
+			'LIN_type' => EEM_Line_Item::type_sub_total
598
+		));
599
+		$pre_tax_line_item = apply_filters(
600
+			'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
601
+			$pre_tax_line_item
602
+		);
603
+		self::set_TXN_ID($pre_tax_line_item, $transaction);
604
+		$total_line_item->add_child_line_item($pre_tax_line_item);
605
+		self::create_event_subtotal($pre_tax_line_item, $transaction);
606
+		return $pre_tax_line_item;
607
+	}
608
+
609
+
610
+	/**
611
+	 * Creates a line item for the taxes subtotal and finds all the tax prices
612
+	 * and applies taxes to it
613
+	 *
614
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
615
+	 * @param EE_Transaction $transaction
616
+	 * @return EE_Line_Item
617
+	 * @throws \EE_Error
618
+	 */
619
+	protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
620
+	{
621
+		$tax_line_item = EE_Line_Item::new_instance(array(
622
+			'LIN_code' => 'taxes',
623
+			'LIN_name' => __('Taxes', 'event_espresso'),
624
+			'LIN_type' => EEM_Line_Item::type_tax_sub_total,
625
+			'LIN_order' => 1000,// this should always come last
626
+		));
627
+		$tax_line_item = apply_filters(
628
+			'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
629
+			$tax_line_item
630
+		);
631
+		self::set_TXN_ID($tax_line_item, $transaction);
632
+		$total_line_item->add_child_line_item($tax_line_item);
633
+		// and lastly, add the actual taxes
634
+		self::apply_taxes($total_line_item);
635
+		return $tax_line_item;
636
+	}
637
+
638
+
639
+	/**
640
+	 * Creates a default items subtotal line item
641
+	 *
642
+	 * @param EE_Line_Item $pre_tax_line_item
643
+	 * @param EE_Transaction $transaction
644
+	 * @param EE_Event $event
645
+	 * @return EE_Line_Item
646
+	 * @throws \EE_Error
647
+	 */
648
+	public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = null, $event = null)
649
+	{
650
+		$event_line_item = EE_Line_Item::new_instance(array(
651
+			'LIN_code' => self::get_event_code($event),
652
+			'LIN_name' => self::get_event_name($event),
653
+			'LIN_desc' => self::get_event_desc($event),
654
+			'LIN_type' => EEM_Line_Item::type_sub_total,
655
+			'OBJ_type' => 'Event',
656
+			'OBJ_ID' => $event instanceof EE_Event ? $event->ID() : 0
657
+		));
658
+		$event_line_item = apply_filters(
659
+			'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
660
+			$event_line_item
661
+		);
662
+		self::set_TXN_ID($event_line_item, $transaction);
663
+		$pre_tax_line_item->add_child_line_item($event_line_item);
664
+		return $event_line_item;
665
+	}
666
+
667
+
668
+	/**
669
+	 * Gets what the event ticket's code SHOULD be
670
+	 *
671
+	 * @param EE_Event $event
672
+	 * @return string
673
+	 * @throws \EE_Error
674
+	 */
675
+	public static function get_event_code($event)
676
+	{
677
+		return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
678
+	}
679
+
680
+	/**
681
+	 * Gets the event name
682
+	 * @param EE_Event $event
683
+	 * @return string
684
+	 */
685
+	public static function get_event_name($event)
686
+	{
687
+		return $event instanceof EE_Event ? mb_substr($event->name(), 0, 245) : __('Event', 'event_espresso');
688
+	}
689
+
690
+	/**
691
+	 * Gets the event excerpt
692
+	 * @param EE_Event $event
693
+	 * @return string
694
+	 */
695
+	public static function get_event_desc($event)
696
+	{
697
+		return $event instanceof EE_Event ? $event->short_description() : '';
698
+	}
699
+
700
+	/**
701
+	 * Given the grand total line item and a ticket, finds the event sub-total
702
+	 * line item the ticket's purchase should be added onto
703
+	 *
704
+	 * @access public
705
+	 * @param EE_Line_Item $grand_total the grand total line item
706
+	 * @param EE_Ticket $ticket
707
+	 * @throws \EE_Error
708
+	 * @return EE_Line_Item
709
+	 */
710
+	public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
711
+	{
712
+		$first_datetime = $ticket->first_datetime();
713
+		if (!$first_datetime instanceof EE_Datetime) {
714
+			throw new EE_Error(
715
+				sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())
716
+			);
717
+		}
718
+		$event = $first_datetime->event();
719
+		if (!$event instanceof EE_Event) {
720
+			throw new EE_Error(
721
+				sprintf(
722
+					__('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'),
723
+					$ticket->ID()
724
+				)
725
+			);
726
+		}
727
+		$events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
728
+		if (!$events_sub_total instanceof EE_Line_Item) {
729
+			throw new EE_Error(
730
+				sprintf(
731
+					__('There is no events sub-total for ticket %s on total line item %d', 'event_espresso'),
732
+					$ticket->ID(),
733
+					$grand_total->ID()
734
+				)
735
+			);
736
+		}
737
+		return $events_sub_total;
738
+	}
739
+
740
+
741
+	/**
742
+	 * Gets the event line item
743
+	 *
744
+	 * @param EE_Line_Item $grand_total
745
+	 * @param EE_Event $event
746
+	 * @return EE_Line_Item for the event subtotal which is a child of $grand_total
747
+	 * @throws \EE_Error
748
+	 */
749
+	public static function get_event_line_item(EE_Line_Item $grand_total, $event)
750
+	{
751
+		/** @type EE_Event $event */
752
+		$event = EEM_Event::instance()->ensure_is_obj($event, true);
753
+		$event_line_item = null;
754
+		$found = false;
755
+		foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
756
+			// default event subtotal, we should only ever find this the first time this method is called
757
+			if (!$event_line_item->OBJ_ID()) {
758
+				// let's use this! but first... set the event details
759
+				EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
760
+				$found = true;
761
+				break;
762
+			} elseif ($event_line_item->OBJ_ID() === $event->ID()) {
763
+				// found existing line item for this event in the cart, so break out of loop and use this one
764
+				$found = true;
765
+				break;
766
+			}
767
+		}
768
+		if (!$found) {
769
+			// there is no event sub-total yet, so add it
770
+			$pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
771
+			// create a new "event" subtotal below that
772
+			$event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
773
+			// and set the event details
774
+			EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
775
+		}
776
+		return $event_line_item;
777
+	}
778
+
779
+
780
+	/**
781
+	 * Creates a default items subtotal line item
782
+	 *
783
+	 * @param EE_Line_Item $event_line_item
784
+	 * @param EE_Event $event
785
+	 * @param EE_Transaction $transaction
786
+	 * @return EE_Line_Item
787
+	 * @throws \EE_Error
788
+	 */
789
+	public static function set_event_subtotal_details(
790
+		EE_Line_Item $event_line_item,
791
+		EE_Event $event,
792
+		$transaction = null
793
+	) {
794
+		if ($event instanceof EE_Event) {
795
+			$event_line_item->set_code(self::get_event_code($event));
796
+			$event_line_item->set_name(self::get_event_name($event));
797
+			$event_line_item->set_desc(self::get_event_desc($event));
798
+			$event_line_item->set_OBJ_ID($event->ID());
799
+		}
800
+		self::set_TXN_ID($event_line_item, $transaction);
801
+	}
802
+
803
+
804
+	/**
805
+	 * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
806
+	 * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
807
+	 * any old taxes are removed
808
+	 *
809
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
810
+	 * @throws \EE_Error
811
+	 */
812
+	public static function apply_taxes(EE_Line_Item $total_line_item)
813
+	{
814
+		/** @type EEM_Price $EEM_Price */
815
+		$EEM_Price = EE_Registry::instance()->load_model('Price');
816
+		// get array of taxes via Price Model
817
+		$ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes();
818
+		ksort($ordered_taxes);
819
+		$taxes_line_item = self::get_taxes_subtotal($total_line_item);
820
+		// just to be safe, remove its old tax line items
821
+		$taxes_line_item->delete_children_line_items();
822
+		// loop thru taxes
823
+		foreach ($ordered_taxes as $order => $taxes) {
824
+			foreach ($taxes as $tax) {
825
+				if ($tax instanceof EE_Price) {
826
+					$tax_line_item = EE_Line_Item::new_instance(
827
+						array(
828
+							'LIN_name' => $tax->name(),
829
+							'LIN_desc' => $tax->desc(),
830
+							'LIN_percent' => $tax->amount(),
831
+							'LIN_is_taxable' => false,
832
+							'LIN_order' => $order,
833
+							'LIN_total' => 0,
834
+							'LIN_type' => EEM_Line_Item::type_tax,
835
+							'OBJ_type' => 'Price',
836
+							'OBJ_ID' => $tax->ID()
837
+						)
838
+					);
839
+					$tax_line_item = apply_filters(
840
+						'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
841
+						$tax_line_item
842
+					);
843
+					$taxes_line_item->add_child_line_item($tax_line_item);
844
+				}
845
+			}
846
+		}
847
+		$total_line_item->recalculate_total_including_taxes();
848
+	}
849
+
850
+
851
+	/**
852
+	 * Ensures that taxes have been applied to the order, if not applies them.
853
+	 * Returns the total amount of tax
854
+	 *
855
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
856
+	 * @return float
857
+	 * @throws \EE_Error
858
+	 */
859
+	public static function ensure_taxes_applied($total_line_item)
860
+	{
861
+		$taxes_subtotal = self::get_taxes_subtotal($total_line_item);
862
+		if (!$taxes_subtotal->children()) {
863
+			self::apply_taxes($total_line_item);
864
+		}
865
+		return $taxes_subtotal->total();
866
+	}
867
+
868
+
869
+	/**
870
+	 * Deletes ALL children of the passed line item
871
+	 *
872
+	 * @param EE_Line_Item $parent_line_item
873
+	 * @return bool
874
+	 * @throws \EE_Error
875
+	 */
876
+	public static function delete_all_child_items(EE_Line_Item $parent_line_item)
877
+	{
878
+		$deleted = 0;
879
+		foreach ($parent_line_item->children() as $child_line_item) {
880
+			if ($child_line_item instanceof EE_Line_Item) {
881
+				$deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
882
+				if ($child_line_item->ID()) {
883
+					$child_line_item->delete();
884
+					unset($child_line_item);
885
+				} else {
886
+					$parent_line_item->delete_child_line_item($child_line_item->code());
887
+				}
888
+				$deleted++;
889
+			}
890
+		}
891
+		return $deleted;
892
+	}
893
+
894
+
895
+	/**
896
+	 * Deletes the line items as indicated by the line item code(s) provided,
897
+	 * regardless of where they're found in the line item tree. Automatically
898
+	 * re-calculates the line item totals and updates the related transaction. But
899
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
900
+	 * should probably change because of this).
901
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
902
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
903
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
904
+	 * @param array|bool|string $line_item_codes
905
+	 * @return int number of items successfully removed
906
+	 */
907
+	public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = false)
908
+	{
909
+
910
+		if ($total_line_item->type() !== EEM_Line_Item::type_total) {
911
+			EE_Error::doing_it_wrong(
912
+				'EEH_Line_Item::delete_items',
913
+				__(
914
+					'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
915
+					'event_espresso'
916
+				),
917
+				'4.6.18'
918
+			);
919
+		}
920
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
921
+
922
+		// check if only a single line_item_id was passed
923
+		if (!empty($line_item_codes) && !is_array($line_item_codes)) {
924
+			// place single line_item_id in an array to appear as multiple line_item_ids
925
+			$line_item_codes = array($line_item_codes);
926
+		}
927
+		$removals = 0;
928
+		// cycle thru line_item_ids
929
+		foreach ($line_item_codes as $line_item_id) {
930
+			$removals += $total_line_item->delete_child_line_item($line_item_id);
931
+		}
932
+
933
+		if ($removals > 0) {
934
+			$total_line_item->recalculate_taxes_and_tax_total();
935
+			return $removals;
936
+		} else {
937
+			return false;
938
+		}
939
+	}
940
+
941
+
942
+	/**
943
+	 * Overwrites the previous tax by clearing out the old taxes, and creates a new
944
+	 * tax and updates the total line item accordingly
945
+	 *
946
+	 * @param EE_Line_Item $total_line_item
947
+	 * @param float $amount
948
+	 * @param string $name
949
+	 * @param string $description
950
+	 * @param string $code
951
+	 * @param boolean $add_to_existing_line_item
952
+	 *                          if true, and a duplicate line item with the same code is found,
953
+	 *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
954
+	 * @return EE_Line_Item the new tax line item created
955
+	 * @throws \EE_Error
956
+	 */
957
+	public static function set_total_tax_to(
958
+		EE_Line_Item $total_line_item,
959
+		$amount,
960
+		$name = null,
961
+		$description = null,
962
+		$code = null,
963
+		$add_to_existing_line_item = false
964
+	) {
965
+		$tax_subtotal = self::get_taxes_subtotal($total_line_item);
966
+		$taxable_total = $total_line_item->taxable_total();
967
+
968
+		if ($add_to_existing_line_item) {
969
+			$new_tax = $tax_subtotal->get_child_line_item($code);
970
+			EEM_Line_Item::instance()->delete(
971
+				array(array('LIN_code' => array('!=', $code), 'LIN_parent' => $tax_subtotal->ID()))
972
+			);
973
+		} else {
974
+			$new_tax = null;
975
+			$tax_subtotal->delete_children_line_items();
976
+		}
977
+		if ($new_tax) {
978
+			$new_tax->set_total($new_tax->total() + $amount);
979
+			$new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
980
+		} else {
981
+			// no existing tax item. Create it
982
+			$new_tax = EE_Line_Item::new_instance(array(
983
+				'TXN_ID' => $total_line_item->TXN_ID(),
984
+				'LIN_name' => $name ? $name : __('Tax', 'event_espresso'),
985
+				'LIN_desc' => $description ? $description : '',
986
+				'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
987
+				'LIN_total' => $amount,
988
+				'LIN_parent' => $tax_subtotal->ID(),
989
+				'LIN_type' => EEM_Line_Item::type_tax,
990
+				'LIN_code' => $code
991
+			));
992
+		}
993
+
994
+		$new_tax = apply_filters(
995
+			'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
996
+			$new_tax,
997
+			$total_line_item
998
+		);
999
+		$new_tax->save();
1000
+		$tax_subtotal->set_total($new_tax->total());
1001
+		$tax_subtotal->save();
1002
+		$total_line_item->recalculate_total_including_taxes();
1003
+		return $new_tax;
1004
+	}
1005
+
1006
+
1007
+	/**
1008
+	 * Makes all the line items which are children of $line_item taxable (or not).
1009
+	 * Does NOT save the line items
1010
+	 * @param EE_Line_Item $line_item
1011
+	 * @param string $code_substring_for_whitelist if this string is part of the line item's code
1012
+	 *  it will be whitelisted (ie, except from becoming taxable)
1013
+	 * @param boolean $taxable
1014
+	 */
1015
+	public static function set_line_items_taxable(
1016
+		EE_Line_Item $line_item,
1017
+		$taxable = true,
1018
+		$code_substring_for_whitelist = null
1019
+	) {
1020
+		$whitelisted = false;
1021
+		if ($code_substring_for_whitelist !== null) {
1022
+			$whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false ? true : false;
1023
+		}
1024
+		if (!$whitelisted && $line_item->is_line_item()) {
1025
+			$line_item->set_is_taxable($taxable);
1026
+		}
1027
+		foreach ($line_item->children() as $child_line_item) {
1028
+			EEH_Line_Item::set_line_items_taxable($child_line_item, $taxable, $code_substring_for_whitelist);
1029
+		}
1030
+	}
1031
+
1032
+
1033
+	/**
1034
+	 * Gets all descendants that are event subtotals
1035
+	 *
1036
+	 * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1037
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1038
+	 * @return EE_Line_Item[]
1039
+	 */
1040
+	public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1041
+	{
1042
+		return self::get_subtotals_of_object_type($parent_line_item, 'Event');
1043
+	}
1044
+
1045
+
1046
+	/**
1047
+	 * Gets all descendants subtotals that match the supplied object type
1048
+	 *
1049
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1050
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1051
+	 * @param string $obj_type
1052
+	 * @return EE_Line_Item[]
1053
+	 */
1054
+	public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1055
+	{
1056
+		return self::_get_descendants_by_type_and_object_type(
1057
+			$parent_line_item,
1058
+			EEM_Line_Item::type_sub_total,
1059
+			$obj_type
1060
+		);
1061
+	}
1062
+
1063
+
1064
+	/**
1065
+	 * Gets all descendants that are tickets
1066
+	 *
1067
+	 * @uses  EEH_Line_Item::get_line_items_of_object_type()
1068
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1069
+	 * @return EE_Line_Item[]
1070
+	 */
1071
+	public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1072
+	{
1073
+		return self::get_line_items_of_object_type($parent_line_item, 'Ticket');
1074
+	}
1075
+
1076
+
1077
+	/**
1078
+	 * Gets all descendants subtotals that match the supplied object type
1079
+	 *
1080
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1081
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1082
+	 * @param string $obj_type
1083
+	 * @return EE_Line_Item[]
1084
+	 */
1085
+	public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1086
+	{
1087
+		return self::_get_descendants_by_type_and_object_type($parent_line_item, EEM_Line_Item::type_line_item, $obj_type);
1088
+	}
1089
+
1090
+
1091
+	/**
1092
+	 * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1093
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1094
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1095
+	 * @return EE_Line_Item[]
1096
+	 */
1097
+	public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1098
+	{
1099
+		return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_tax);
1100
+	}
1101
+
1102
+
1103
+	/**
1104
+	 * Gets all the real items purchased which are children of this item
1105
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1106
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1107
+	 * @return EE_Line_Item[]
1108
+	 */
1109
+	public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1110
+	{
1111
+		return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_line_item);
1112
+	}
1113
+
1114
+
1115
+	/**
1116
+	 * Gets all descendants of supplied line item that match the supplied line item type
1117
+	 *
1118
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1119
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1120
+	 * @param string $line_item_type one of the EEM_Line_Item constants
1121
+	 * @return EE_Line_Item[]
1122
+	 */
1123
+	public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1124
+	{
1125
+		return self::_get_descendants_by_type_and_object_type($parent_line_item, $line_item_type, null);
1126
+	}
1127
+
1128
+
1129
+	/**
1130
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1131
+	 *
1132
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1133
+	 * @param string $line_item_type one of the EEM_Line_Item constants
1134
+	 * @param string | NULL $obj_type object model class name (minus prefix) or NULL to ignore object type when searching
1135
+	 * @return EE_Line_Item[]
1136
+	 */
1137
+	protected static function _get_descendants_by_type_and_object_type(
1138
+		EE_Line_Item $parent_line_item,
1139
+		$line_item_type,
1140
+		$obj_type = null
1141
+	) {
1142
+		$objects = array();
1143
+		foreach ($parent_line_item->children() as $child_line_item) {
1144
+			if ($child_line_item instanceof EE_Line_Item) {
1145
+				if ($child_line_item->type() === $line_item_type
1146
+					&& (
1147
+						$child_line_item->OBJ_type() === $obj_type || $obj_type === null
1148
+					)
1149
+				) {
1150
+					$objects[] = $child_line_item;
1151
+				} else {
1152
+					// go-through-all-its children looking for more matches
1153
+					$objects = array_merge(
1154
+						$objects,
1155
+						self::_get_descendants_by_type_and_object_type(
1156
+							$child_line_item,
1157
+							$line_item_type,
1158
+							$obj_type
1159
+						)
1160
+					);
1161
+				}
1162
+			}
1163
+		}
1164
+		return $objects;
1165
+	}
1166
+
1167
+
1168
+	/**
1169
+	 * Gets all descendants subtotals that match the supplied object type
1170
+	 *
1171
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1172
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1173
+	 * @param string $OBJ_type object type (like Event)
1174
+	 * @param array $OBJ_IDs array of OBJ_IDs
1175
+	 * @return EE_Line_Item[]
1176
+	 */
1177
+	public static function get_line_items_by_object_type_and_IDs(
1178
+		EE_Line_Item $parent_line_item,
1179
+		$OBJ_type = '',
1180
+		$OBJ_IDs = array()
1181
+	) {
1182
+		return self::_get_descendants_by_object_type_and_object_ID($parent_line_item, $OBJ_type, $OBJ_IDs);
1183
+	}
1184
+
1185
+
1186
+	/**
1187
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1188
+	 *
1189
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1190
+	 * @param string $OBJ_type object type (like Event)
1191
+	 * @param array $OBJ_IDs array of OBJ_IDs
1192
+	 * @return EE_Line_Item[]
1193
+	 */
1194
+	protected static function _get_descendants_by_object_type_and_object_ID(
1195
+		EE_Line_Item $parent_line_item,
1196
+		$OBJ_type,
1197
+		$OBJ_IDs
1198
+	) {
1199
+		$objects = array();
1200
+		foreach ($parent_line_item->children() as $child_line_item) {
1201
+			if ($child_line_item instanceof EE_Line_Item) {
1202
+				if ($child_line_item->OBJ_type() === $OBJ_type
1203
+					&& is_array($OBJ_IDs)
1204
+					&& in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1205
+				) {
1206
+					$objects[] = $child_line_item;
1207
+				} else {
1208
+					// go-through-all-its children looking for more matches
1209
+					$objects = array_merge(
1210
+						$objects,
1211
+						self::_get_descendants_by_object_type_and_object_ID(
1212
+							$child_line_item,
1213
+							$OBJ_type,
1214
+							$OBJ_IDs
1215
+						)
1216
+					);
1217
+				}
1218
+			}
1219
+		}
1220
+		return $objects;
1221
+	}
1222
+
1223
+
1224
+	/**
1225
+	 * Uses a breadth-first-search in order to find the nearest descendant of
1226
+	 * the specified type and returns it, else NULL
1227
+	 *
1228
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1229
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1230
+	 * @param string $type like one of the EEM_Line_Item::type_*
1231
+	 * @return EE_Line_Item
1232
+	 */
1233
+	public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1234
+	{
1235
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1236
+	}
1237
+
1238
+
1239
+	/**
1240
+	 * Uses a breadth-first-search in order to find the nearest descendant
1241
+	 * having the specified LIN_code and returns it, else NULL
1242
+	 *
1243
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1244
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1245
+	 * @param string $code any value used for LIN_code
1246
+	 * @return EE_Line_Item
1247
+	 */
1248
+	public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1249
+	{
1250
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1251
+	}
1252
+
1253
+
1254
+	/**
1255
+	 * Uses a breadth-first-search in order to find the nearest descendant
1256
+	 * having the specified LIN_code and returns it, else NULL
1257
+	 *
1258
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1259
+	 * @param string $search_field name of EE_Line_Item property
1260
+	 * @param string $value any value stored in $search_field
1261
+	 * @return EE_Line_Item
1262
+	 */
1263
+	protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1264
+	{
1265
+		foreach ($parent_line_item->children() as $child) {
1266
+			if ($child->get($search_field) == $value) {
1267
+				return $child;
1268
+			}
1269
+		}
1270
+		foreach ($parent_line_item->children() as $child) {
1271
+			$descendant_found = self::_get_nearest_descendant($child, $search_field, $value);
1272
+			if ($descendant_found) {
1273
+				return $descendant_found;
1274
+			}
1275
+		}
1276
+		return null;
1277
+	}
1278
+
1279
+
1280
+	/**
1281
+	 * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1282
+	 * else recursively walks up the line item tree until a parent of type total is found,
1283
+	 *
1284
+	 * @param EE_Line_Item $line_item
1285
+	 * @return \EE_Line_Item
1286
+	 * @throws \EE_Error
1287
+	 */
1288
+	public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item)
1289
+	{
1290
+		if ($line_item->TXN_ID()) {
1291
+			$total_line_item = $line_item->transaction()->total_line_item(false);
1292
+			if ($total_line_item instanceof EE_Line_Item) {
1293
+				return $total_line_item;
1294
+			}
1295
+		} else {
1296
+			$line_item_parent = $line_item->parent();
1297
+			if ($line_item_parent instanceof EE_Line_Item) {
1298
+				if ($line_item_parent->is_total()) {
1299
+					return $line_item_parent;
1300
+				}
1301
+				return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1302
+			}
1303
+		}
1304
+		throw new EE_Error(
1305
+			sprintf(
1306
+				__('A valid grand total for line item %1$d was not found.', 'event_espresso'),
1307
+				$line_item->ID()
1308
+			)
1309
+		);
1310
+	}
1311
+
1312
+
1313
+	/**
1314
+	 * Prints out a representation of the line item tree
1315
+	 *
1316
+	 * @param EE_Line_Item $line_item
1317
+	 * @param int $indentation
1318
+	 * @return void
1319
+	 * @throws \EE_Error
1320
+	 */
1321
+	public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1322
+	{
1323
+		echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1324
+		if (!$indentation) {
1325
+			echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1326
+		}
1327
+		for ($i = 0; $i < $indentation; $i++) {
1328
+			echo ". ";
1329
+		}
1330
+		$breakdown = '';
1331
+		if ($line_item->is_line_item()) {
1332
+			if ($line_item->is_percent()) {
1333
+				$breakdown = "{$line_item->percent()}%";
1334
+			} else {
1335
+				$breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1336
+			}
1337
+		}
1338
+		echo $line_item->name() . " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : " . '$' . "{$line_item->total()}";
1339
+		if ($breakdown) {
1340
+			echo " ( {$breakdown} )";
1341
+		}
1342
+		if ($line_item->is_taxable()) {
1343
+			echo "  * taxable";
1344
+		}
1345
+		if ($line_item->children()) {
1346
+			foreach ($line_item->children() as $child) {
1347
+				self::visualize($child, $indentation + 1);
1348
+			}
1349
+		}
1350
+	}
1351
+
1352
+
1353
+	/**
1354
+	 * Calculates the registration's final price, taking into account that they
1355
+	 * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1356
+	 * and receive a portion of any transaction-wide discounts.
1357
+	 * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1358
+	 * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1359
+	 * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1360
+	 * and brent's final price should be $5.50.
1361
+	 *
1362
+	 * In order to do this, we basically need to traverse the line item tree calculating
1363
+	 * the running totals (just as if we were recalculating the total), but when we identify
1364
+	 * regular line items, we need to keep track of their share of the grand total.
1365
+	 * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1366
+	 * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1367
+	 * when there are non-taxable items; otherwise they would be the same)
1368
+	 *
1369
+	 * @param EE_Line_Item $line_item
1370
+	 * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity that
1371
+	 *                                                                            can be included in price calculations at this moment
1372
+	 * @return array        keys are line items for tickets IDs and values are their share of the running total,
1373
+	 *                                          plus the key 'total', and 'taxable' which also has keys of all the ticket IDs. Eg
1374
+	 *                                          array(
1375
+	 *                                          12 => 4.3
1376
+	 *                                          23 => 8.0
1377
+	 *                                          'total' => 16.6,
1378
+	 *                                          'taxable' => array(
1379
+	 *                                          12 => 10,
1380
+	 *                                          23 => 4
1381
+	 *                                          ).
1382
+	 *                                          So to find which registrations have which final price, we need to find which line item
1383
+	 *                                          is theirs, which can be done with
1384
+	 *                                          `EEM_Line_Item::instance()->get_line_item_for_registration( $registration );`
1385
+	 */
1386
+	public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array())
1387
+	{
1388
+		// init running grand total if not already
1389
+		if (!isset($running_totals['total'])) {
1390
+			$running_totals['total'] = 0;
1391
+		}
1392
+		if (!isset($running_totals['taxable'])) {
1393
+			$running_totals['taxable'] = array('total' => 0);
1394
+		}
1395
+		foreach ($line_item->children() as $child_line_item) {
1396
+			switch ($child_line_item->type()) {
1397
+				case EEM_Line_Item::type_sub_total:
1398
+					$running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item($child_line_item, $billable_ticket_quantities);
1399
+					// combine arrays but preserve numeric keys
1400
+					$running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1401
+					$running_totals['total'] += $running_totals_from_subtotal['total'];
1402
+					$running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1403
+					break;
1404
+
1405
+				case EEM_Line_Item::type_tax_sub_total:
1406
+					// find how much the taxes percentage is
1407
+					if ($child_line_item->percent() !== 0) {
1408
+						$tax_percent_decimal = $child_line_item->percent() / 100;
1409
+					} else {
1410
+						$tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1411
+					}
1412
+					// and apply to all the taxable totals, and add to the pretax totals
1413
+					foreach ($running_totals as $line_item_id => $this_running_total) {
1414
+						// "total" and "taxable" array key is an exception
1415
+						if ($line_item_id === 'taxable') {
1416
+							continue;
1417
+						}
1418
+						$taxable_total = $running_totals['taxable'][ $line_item_id ];
1419
+						$running_totals[ $line_item_id ] += ($taxable_total * $tax_percent_decimal);
1420
+					}
1421
+					break;
1422
+
1423
+				case EEM_Line_Item::type_line_item:
1424
+					// ticket line items or ????
1425
+					if ($child_line_item->OBJ_type() === 'Ticket') {
1426
+						// kk it's a ticket
1427
+						if (isset($running_totals[ $child_line_item->ID() ])) {
1428
+							// huh? that shouldn't happen.
1429
+							$running_totals['total'] += $child_line_item->total();
1430
+						} else {
1431
+							// its not in our running totals yet. great.
1432
+							if ($child_line_item->is_taxable()) {
1433
+								$taxable_amount = $child_line_item->unit_price();
1434
+							} else {
1435
+								$taxable_amount = 0;
1436
+							}
1437
+							// are we only calculating totals for some tickets?
1438
+							if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) {
1439
+								$quantity = $billable_ticket_quantities[ $child_line_item->OBJ_ID() ];
1440
+								$running_totals[ $child_line_item->ID() ] = $quantity
1441
+									? $child_line_item->unit_price()
1442
+									: 0;
1443
+								$running_totals['taxable'][ $child_line_item->ID() ] = $quantity
1444
+									? $taxable_amount
1445
+									: 0;
1446
+							} else {
1447
+								$quantity = $child_line_item->quantity();
1448
+								$running_totals[ $child_line_item->ID() ] = $child_line_item->unit_price();
1449
+								$running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount;
1450
+							}
1451
+							$running_totals['taxable']['total'] += $taxable_amount * $quantity;
1452
+							$running_totals['total'] += $child_line_item->unit_price() * $quantity;
1453
+						}
1454
+					} else {
1455
+						// it's some other type of item added to the cart
1456
+						// it should affect the running totals
1457
+						// basically we want to convert it into a PERCENT modifier. Because
1458
+						// more clearly affect all registration's final price equally
1459
+						$line_items_percent_of_running_total = $running_totals['total'] > 0
1460
+							? ($child_line_item->total() / $running_totals['total']) + 1
1461
+							: 1;
1462
+						foreach ($running_totals as $line_item_id => $this_running_total) {
1463
+							// the "taxable" array key is an exception
1464
+							if ($line_item_id === 'taxable') {
1465
+								continue;
1466
+							}
1467
+							// update the running totals
1468
+							// yes this actually even works for the running grand total!
1469
+							$running_totals[ $line_item_id ] =
1470
+								$line_items_percent_of_running_total * $this_running_total;
1471
+
1472
+							if ($child_line_item->is_taxable()) {
1473
+								$running_totals['taxable'][ $line_item_id ] =
1474
+									$line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ];
1475
+							}
1476
+						}
1477
+					}
1478
+					break;
1479
+			}
1480
+		}
1481
+		return $running_totals;
1482
+	}
1483
+
1484
+
1485
+	/**
1486
+	 * @param \EE_Line_Item $total_line_item
1487
+	 * @param \EE_Line_Item $ticket_line_item
1488
+	 * @return float | null
1489
+	 * @throws \OutOfRangeException
1490
+	 */
1491
+	public static function calculate_final_price_for_ticket_line_item(\EE_Line_Item $total_line_item, \EE_Line_Item $ticket_line_item)
1492
+	{
1493
+		static $final_prices_per_ticket_line_item = array();
1494
+		if (empty($final_prices_per_ticket_line_item)) {
1495
+			$final_prices_per_ticket_line_item = \EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1496
+				$total_line_item
1497
+			);
1498
+		}
1499
+		// ok now find this new registration's final price
1500
+		if (isset($final_prices_per_ticket_line_item[ $ticket_line_item->ID() ])) {
1501
+			return $final_prices_per_ticket_line_item[ $ticket_line_item->ID() ];
1502
+		}
1503
+		$message = sprintf(
1504
+			__(
1505
+				'The final price for the ticket line item (ID:%1$d) could not be calculated.',
1506
+				'event_espresso'
1507
+			),
1508
+			$ticket_line_item->ID()
1509
+		);
1510
+		if (WP_DEBUG) {
1511
+			$message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1512
+			throw new \OutOfRangeException($message);
1513
+		} else {
1514
+			EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1515
+		}
1516
+		return null;
1517
+	}
1518
+
1519
+
1520
+	/**
1521
+	 * Creates a duplicate of the line item tree, except only includes billable items
1522
+	 * and the portion of line items attributed to billable things
1523
+	 *
1524
+	 * @param EE_Line_Item $line_item
1525
+	 * @param EE_Registration[] $registrations
1526
+	 * @return \EE_Line_Item
1527
+	 * @throws \EE_Error
1528
+	 */
1529
+	public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1530
+	{
1531
+		$copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1532
+		foreach ($line_item->children() as $child_li) {
1533
+			$copy_li->add_child_line_item(EEH_Line_Item::billable_line_item_tree($child_li, $registrations));
1534
+		}
1535
+		// if this is the grand total line item, make sure the totals all add up
1536
+		// (we could have duplicated this logic AS we copied the line items, but
1537
+		// it seems DRYer this way)
1538
+		if ($copy_li->type() === EEM_Line_Item::type_total) {
1539
+			$copy_li->recalculate_total_including_taxes();
1540
+		}
1541
+		return $copy_li;
1542
+	}
1543
+
1544
+
1545
+	/**
1546
+	 * Creates a new, unsaved line item from $line_item that factors in the
1547
+	 * number of billable registrations on $registrations.
1548
+	 *
1549
+	 * @param EE_Line_Item $line_item
1550
+	 * @return EE_Line_Item
1551
+	 * @throws \EE_Error
1552
+	 * @param EE_Registration[] $registrations
1553
+	 */
1554
+	public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1555
+	{
1556
+		$new_li_fields = $line_item->model_field_array();
1557
+		if ($line_item->type() === EEM_Line_Item::type_line_item &&
1558
+			$line_item->OBJ_type() === 'Ticket'
1559
+		) {
1560
+			$count = 0;
1561
+			foreach ($registrations as $registration) {
1562
+				if ($line_item->OBJ_ID() === $registration->ticket_ID() &&
1563
+					in_array($registration->status_ID(), EEM_Registration::reg_statuses_that_allow_payment())
1564
+				) {
1565
+					$count++;
1566
+				}
1567
+			}
1568
+			$new_li_fields['LIN_quantity'] = $count;
1569
+		}
1570
+		// don't set the total. We'll leave that up to the code that calculates it
1571
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1572
+		return EE_Line_Item::new_instance($new_li_fields);
1573
+	}
1574
+
1575
+
1576
+	/**
1577
+	 * Returns a modified line item tree where all the subtotals which have a total of 0
1578
+	 * are removed, and line items with a quantity of 0
1579
+	 *
1580
+	 * @param EE_Line_Item $line_item |null
1581
+	 * @return \EE_Line_Item|null
1582
+	 * @throws \EE_Error
1583
+	 */
1584
+	public static function non_empty_line_items(EE_Line_Item $line_item)
1585
+	{
1586
+		$copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1587
+		if ($copied_li === null) {
1588
+			return null;
1589
+		}
1590
+		// if this is an event subtotal, we want to only include it if it
1591
+		// has a non-zero total and at least one ticket line item child
1592
+		$ticket_children = 0;
1593
+		foreach ($line_item->children() as $child_li) {
1594
+			$child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1595
+			if ($child_li_copy !== null) {
1596
+				$copied_li->add_child_line_item($child_li_copy);
1597
+				if ($child_li_copy->type() === EEM_Line_Item::type_line_item &&
1598
+					$child_li_copy->OBJ_type() === 'Ticket'
1599
+				) {
1600
+					$ticket_children++;
1601
+				}
1602
+			}
1603
+		}
1604
+		// if this is an event subtotal with NO ticket children
1605
+		// we basically want to ignore it
1606
+		if ($ticket_children === 0
1607
+			&& $line_item->type() === EEM_Line_Item::type_sub_total
1608
+			&& $line_item->OBJ_type() === 'Event'
1609
+			&& $line_item->total() === 0
1610
+		) {
1611
+			return null;
1612
+		}
1613
+		return $copied_li;
1614
+	}
1615
+
1616
+
1617
+	/**
1618
+	 * Creates a new, unsaved line item, but if it's a ticket line item
1619
+	 * with a total of 0, or a subtotal of 0, returns null instead
1620
+	 *
1621
+	 * @param EE_Line_Item $line_item
1622
+	 * @return EE_Line_Item
1623
+	 * @throws \EE_Error
1624
+	 */
1625
+	public static function non_empty_line_item(EE_Line_Item $line_item)
1626
+	{
1627
+		if ($line_item->type() === EEM_Line_Item::type_line_item &&
1628
+			$line_item->OBJ_type() === 'Ticket' &&
1629
+			$line_item->quantity() === 0
1630
+		) {
1631
+			return null;
1632
+		}
1633
+		$new_li_fields = $line_item->model_field_array();
1634
+		// don't set the total. We'll leave that up to the code that calculates it
1635
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1636
+		return EE_Line_Item::new_instance($new_li_fields);
1637
+	}
1638
+
1639
+
1640
+
1641
+	/**************************************** @DEPRECATED METHODS *************************************** */
1642
+	/**
1643
+	 * @deprecated
1644
+	 * @param EE_Line_Item $total_line_item
1645
+	 * @return \EE_Line_Item
1646
+	 * @throws \EE_Error
1647
+	 */
1648
+	public static function get_items_subtotal(EE_Line_Item $total_line_item)
1649
+	{
1650
+		EE_Error::doing_it_wrong('EEH_Line_Item::get_items_subtotal()', __('Method replaced with EEH_Line_Item::get_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1651
+		return self::get_pre_tax_subtotal($total_line_item);
1652
+	}
1653
+
1654
+
1655
+	/**
1656
+	 * @deprecated
1657
+	 * @param EE_Transaction $transaction
1658
+	 * @return \EE_Line_Item
1659
+	 * @throws \EE_Error
1660
+	 */
1661
+	public static function create_default_total_line_item($transaction = null)
1662
+	{
1663
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_total_line_item()', __('Method replaced with EEH_Line_Item::create_total_line_item()', 'event_espresso'), '4.6.0');
1664
+		return self::create_total_line_item($transaction);
1665
+	}
1666
+
1667
+
1668
+	/**
1669
+	 * @deprecated
1670
+	 * @param EE_Line_Item $total_line_item
1671
+	 * @param EE_Transaction $transaction
1672
+	 * @return \EE_Line_Item
1673
+	 * @throws \EE_Error
1674
+	 */
1675
+	public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = null)
1676
+	{
1677
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_tickets_subtotal()', __('Method replaced with EEH_Line_Item::create_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1678
+		return self::create_pre_tax_subtotal($total_line_item, $transaction);
1679
+	}
1680
+
1681
+
1682
+	/**
1683
+	 * @deprecated
1684
+	 * @param EE_Line_Item $total_line_item
1685
+	 * @param EE_Transaction $transaction
1686
+	 * @return \EE_Line_Item
1687
+	 * @throws \EE_Error
1688
+	 */
1689
+	public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
1690
+	{
1691
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_taxes_subtotal()', __('Method replaced with EEH_Line_Item::create_taxes_subtotal()', 'event_espresso'), '4.6.0');
1692
+		return self::create_taxes_subtotal($total_line_item, $transaction);
1693
+	}
1694
+
1695
+
1696
+	/**
1697
+	 * @deprecated
1698
+	 * @param EE_Line_Item $total_line_item
1699
+	 * @param EE_Transaction $transaction
1700
+	 * @return \EE_Line_Item
1701
+	 * @throws \EE_Error
1702
+	 */
1703
+	public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = null)
1704
+	{
1705
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_event_subtotal()', __('Method replaced with EEH_Line_Item::create_event_subtotal()', 'event_espresso'), '4.6.0');
1706
+		return self::create_event_subtotal($total_line_item, $transaction);
1707
+	}
1708 1708
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Event.model.php 3 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -2,7 +2,6 @@
 block discarded – undo
2 2
 
3 3
 use EventEspresso\core\exceptions\InvalidDataTypeException;
4 4
 use EventEspresso\core\exceptions\InvalidInterfaceException;
5
-use EventEspresso\core\services\orm\ModelFieldFactory;
6 5
 
7 6
 /**
8 7
  * EEM_Event Model
Please login to merge, or discard this patch.
Indentation   +909 added lines, -909 removed lines patch added patch discarded remove patch
@@ -15,913 +15,913 @@
 block discarded – undo
15 15
 class EEM_Event extends EEM_CPT_Base
16 16
 {
17 17
 
18
-    /**
19
-     * constant used by status(), indicating that no more tickets can be purchased for any of the datetimes for the
20
-     * event
21
-     */
22
-    const sold_out = 'sold_out';
23
-
24
-    /**
25
-     * constant used by status(), indicating that upcoming event dates have been postponed (may be pushed to a later
26
-     * date)
27
-     */
28
-    const postponed = 'postponed';
29
-
30
-    /**
31
-     * constant used by status(), indicating that the event will no longer occur
32
-     */
33
-    const cancelled = 'cancelled';
34
-
35
-
36
-    /**
37
-     * @var string
38
-     */
39
-    protected static $_default_reg_status;
40
-
41
-
42
-    /**
43
-     * This is the default for the additional limit field.
44
-     * @var int
45
-     */
46
-    protected static $_default_additional_limit = 10;
47
-
48
-
49
-    /**
50
-     * private instance of the Event object
51
-     *
52
-     * @var EEM_Event
53
-     */
54
-    protected static $_instance;
55
-
56
-
57
-
58
-
59
-    /**
60
-     * Adds a relationship to Term_Taxonomy for each CPT_Base
61
-     *
62
-     * @param string $timezone
63
-     * @throws \EE_Error
64
-     */
65
-    protected function __construct($timezone = null)
66
-    {
67
-        EE_Registry::instance()->load_model('Registration');
68
-        $this->singular_item = esc_html__('Event', 'event_espresso');
69
-        $this->plural_item = esc_html__('Events', 'event_espresso');
70
-        // to remove Cancelled events from the frontend, copy the following filter to your functions.php file
71
-        // add_filter( 'AFEE__EEM_Event__construct___custom_stati__cancelled__Public', '__return_false' );
72
-        // to remove Postponed events from the frontend, copy the following filter to your functions.php file
73
-        // add_filter( 'AFEE__EEM_Event__construct___custom_stati__postponed__Public', '__return_false' );
74
-        // to remove Sold Out events from the frontend, copy the following filter to your functions.php file
75
-        //  add_filter( 'AFEE__EEM_Event__construct___custom_stati__sold_out__Public', '__return_false' );
76
-        $this->_custom_stati = apply_filters(
77
-            'AFEE__EEM_Event__construct___custom_stati',
78
-            array(
79
-                EEM_Event::cancelled => array(
80
-                    'label'  => esc_html__('Cancelled', 'event_espresso'),
81
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__cancelled__Public', true),
82
-                ),
83
-                EEM_Event::postponed => array(
84
-                    'label'  => esc_html__('Postponed', 'event_espresso'),
85
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__postponed__Public', true),
86
-                ),
87
-                EEM_Event::sold_out  => array(
88
-                    'label'  => esc_html__('Sold Out', 'event_espresso'),
89
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__sold_out__Public', true),
90
-                ),
91
-            )
92
-        );
93
-        self::$_default_reg_status = empty(self::$_default_reg_status) ? EEM_Registration::status_id_pending_payment
94
-            : self::$_default_reg_status;
95
-        $this->_tables = array(
96
-            'Event_CPT'  => new EE_Primary_Table('posts', 'ID'),
97
-            'Event_Meta' => new EE_Secondary_Table('esp_event_meta', 'EVTM_ID', 'EVT_ID'),
98
-        );
99
-        $this->_fields = array(
100
-            'Event_CPT'  => array(
101
-                'EVT_ID'         => new EE_Primary_Key_Int_Field(
102
-                    'ID',
103
-                    esc_html__('Post ID for Event', 'event_espresso')
104
-                ),
105
-                'EVT_name'       => new EE_Plain_Text_Field(
106
-                    'post_title',
107
-                    esc_html__('Event Name', 'event_espresso'),
108
-                    false,
109
-                    ''
110
-                ),
111
-                'EVT_desc'       => new EE_Post_Content_Field(
112
-                    'post_content',
113
-                    esc_html__('Event Description', 'event_espresso'),
114
-                    false,
115
-                    ''
116
-                ),
117
-                'EVT_slug'       => new EE_Slug_Field(
118
-                    'post_name',
119
-                    esc_html__('Event Slug', 'event_espresso'),
120
-                    false,
121
-                    ''
122
-                ),
123
-                'EVT_created'    => new EE_Datetime_Field(
124
-                    'post_date',
125
-                    esc_html__('Date/Time Event Created', 'event_espresso'),
126
-                    false,
127
-                    EE_Datetime_Field::now
128
-                ),
129
-                'EVT_short_desc' => new EE_Simple_HTML_Field(
130
-                    'post_excerpt',
131
-                    esc_html__('Event Short Description', 'event_espresso'),
132
-                    false,
133
-                    ''
134
-                ),
135
-                'EVT_modified'   => new EE_Datetime_Field(
136
-                    'post_modified',
137
-                    esc_html__('Date/Time Event Modified', 'event_espresso'),
138
-                    false,
139
-                    EE_Datetime_Field::now
140
-                ),
141
-                'EVT_wp_user'    => new EE_WP_User_Field(
142
-                    'post_author',
143
-                    esc_html__('Event Creator ID', 'event_espresso'),
144
-                    false
145
-                ),
146
-                'parent'         => new EE_Integer_Field(
147
-                    'post_parent',
148
-                    esc_html__('Event Parent ID', 'event_espresso'),
149
-                    false,
150
-                    0
151
-                ),
152
-                'EVT_order'      => new EE_Integer_Field(
153
-                    'menu_order',
154
-                    esc_html__('Event Menu Order', 'event_espresso'),
155
-                    false,
156
-                    1
157
-                ),
158
-                'post_type'      => new EE_WP_Post_Type_Field('espresso_events'),
159
-                // EE_Plain_Text_Field( 'post_type', esc_html__( 'Event Post Type', 'event_espresso' ), FALSE, 'espresso_events' ),
160
-                'status'         => new EE_WP_Post_Status_Field(
161
-                    'post_status',
162
-                    esc_html__('Event Status', 'event_espresso'),
163
-                    false,
164
-                    'draft',
165
-                    $this->_custom_stati
166
-                ),
167
-                'password' => new EE_Password_Field(
168
-                    'post_password',
169
-                    __('Password', 'event_espresso'),
170
-                    false,
171
-                    '',
172
-                    array(
173
-                        'EVT_desc',
174
-                        'EVT_short_desc',
175
-                        'EVT_display_desc',
176
-                        'EVT_display_ticket_selector',
177
-                        'EVT_visible_on',
178
-                        'EVT_additional_limit',
179
-                        'EVT_default_registration_status',
180
-                        'EVT_member_only',
181
-                        'EVT_phone',
182
-                        'EVT_allow_overflow',
183
-                        'EVT_timezone_string',
184
-                        'EVT_external_URL',
185
-                        'EVT_donations'
186
-                    )
187
-                )
188
-            ),
189
-            'Event_Meta' => array(
190
-                'EVTM_ID'                         => new EE_DB_Only_Float_Field(
191
-                    'EVTM_ID',
192
-                    esc_html__('Event Meta Row ID', 'event_espresso'),
193
-                    false
194
-                ),
195
-                'EVT_ID_fk'                       => new EE_DB_Only_Int_Field(
196
-                    'EVT_ID',
197
-                    esc_html__('Foreign key to Event ID from Event Meta table', 'event_espresso'),
198
-                    false
199
-                ),
200
-                'EVT_display_desc'                => new EE_Boolean_Field(
201
-                    'EVT_display_desc',
202
-                    esc_html__('Display Description Flag', 'event_espresso'),
203
-                    false,
204
-                    true
205
-                ),
206
-                'EVT_display_ticket_selector'     => new EE_Boolean_Field(
207
-                    'EVT_display_ticket_selector',
208
-                    esc_html__('Display Ticket Selector Flag', 'event_espresso'),
209
-                    false,
210
-                    true
211
-                ),
212
-                'EVT_visible_on'                  => new EE_Datetime_Field(
213
-                    'EVT_visible_on',
214
-                    esc_html__('Event Visible Date', 'event_espresso'),
215
-                    true,
216
-                    EE_Datetime_Field::now
217
-                ),
218
-                'EVT_additional_limit'            => new EE_Integer_Field(
219
-                    'EVT_additional_limit',
220
-                    esc_html__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
221
-                    true,
222
-                    self::$_default_additional_limit
223
-                ),
224
-                'EVT_default_registration_status' => new EE_Enum_Text_Field(
225
-                    'EVT_default_registration_status',
226
-                    esc_html__('Default Registration Status on this Event', 'event_espresso'),
227
-                    false,
228
-                    EEM_Event::$_default_reg_status,
229
-                    EEM_Registration::reg_status_array()
230
-                ),
231
-                'EVT_member_only'                 => new EE_Boolean_Field(
232
-                    'EVT_member_only',
233
-                    esc_html__('Member-Only Event Flag', 'event_espresso'),
234
-                    false,
235
-                    false
236
-                ),
237
-                'EVT_phone'                       => new EE_Plain_Text_Field(
238
-                    'EVT_phone',
239
-                    esc_html__('Event Phone Number', 'event_espresso'),
240
-                    false,
241
-                    ''
242
-                ),
243
-                'EVT_allow_overflow'              => new EE_Boolean_Field(
244
-                    'EVT_allow_overflow',
245
-                    esc_html__('Allow Overflow on Event', 'event_espresso'),
246
-                    false,
247
-                    false
248
-                ),
249
-                'EVT_timezone_string'             => new EE_Plain_Text_Field(
250
-                    'EVT_timezone_string',
251
-                    esc_html__('Timezone (name) for Event times', 'event_espresso'),
252
-                    false,
253
-                    ''
254
-                ),
255
-                'EVT_external_URL'                => new EE_Plain_Text_Field(
256
-                    'EVT_external_URL',
257
-                    esc_html__('URL of Event Page if hosted elsewhere', 'event_espresso'),
258
-                    true
259
-                ),
260
-                'EVT_donations'                   => new EE_Boolean_Field(
261
-                    'EVT_donations',
262
-                    esc_html__('Accept Donations?', 'event_espresso'),
263
-                    false,
264
-                    false
265
-                ),
266
-            ),
267
-        );
268
-        $this->_model_relations = array(
269
-            'Registration'           => new EE_Has_Many_Relation(),
270
-            'Datetime'               => new EE_Has_Many_Relation(),
271
-            'Question_Group'         => new EE_HABTM_Relation('Event_Question_Group'),
272
-            'Event_Question_Group'   => new EE_Has_Many_Relation(),
273
-            'Venue'                  => new EE_HABTM_Relation('Event_Venue'),
274
-            'Term_Relationship'      => new EE_Has_Many_Relation(),
275
-            'Term_Taxonomy'          => new EE_HABTM_Relation('Term_Relationship'),
276
-            'Message_Template_Group' => new EE_HABTM_Relation('Event_Message_Template'),
277
-            'Attendee'               => new EE_HABTM_Relation('Registration'),
278
-            'WP_User'                => new EE_Belongs_To_Relation(),
279
-        );
280
-        // this model is generally available for reading
281
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
282
-        $this->model_chain_to_password = '';
283
-        parent::__construct($timezone);
284
-    }
285
-
286
-
287
-
288
-    /**
289
-     * @param string $default_reg_status
290
-     */
291
-    public static function set_default_reg_status($default_reg_status)
292
-    {
293
-        self::$_default_reg_status = $default_reg_status;
294
-        // if EEM_Event has already been instantiated,
295
-        // then we need to reset the `EVT_default_reg_status` field to use the new default.
296
-        if (self::$_instance instanceof EEM_Event) {
297
-            $default_reg_status = new EE_Enum_Text_Field(
298
-                'EVT_default_registration_status',
299
-                esc_html__('Default Registration Status on this Event', 'event_espresso'),
300
-                false,
301
-                $default_reg_status,
302
-                EEM_Registration::reg_status_array()
303
-            );
304
-            $default_reg_status->_construct_finalize(
305
-                'Event_Meta',
306
-                'EVT_default_registration_status',
307
-                'EEM_Event'
308
-            );
309
-            self::$_instance->_fields['Event_Meta']['EVT_default_registration_status'] = $default_reg_status;
310
-        }
311
-    }
312
-
313
-
314
-    /**
315
-     * Used to override the default for the additional limit field.
316
-     * @param $additional_limit
317
-     */
318
-    public static function set_default_additional_limit($additional_limit)
319
-    {
320
-        self::$_default_additional_limit = (int) $additional_limit;
321
-        if (self::$_instance instanceof EEM_Event) {
322
-            self::$_instance->_fields['Event_Meta']['EVT_additional_limit'] = new EE_Integer_Field(
323
-                'EVT_additional_limit',
324
-                __('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
325
-                true,
326
-                self::$_default_additional_limit
327
-            );
328
-            self::$_instance->_fields['Event_Meta']['EVT_additional_limit']->_construct_finalize(
329
-                'Event_Meta',
330
-                'EVT_additional_limit',
331
-                'EEM_Event'
332
-            );
333
-        }
334
-    }
335
-
336
-
337
-    /**
338
-     * Return what is currently set as the default additional limit for the event.
339
-     * @return int
340
-     */
341
-    public static function get_default_additional_limit()
342
-    {
343
-        return apply_filters('FHEE__EEM_Event__get_default_additional_limit', self::$_default_additional_limit);
344
-    }
345
-
346
-
347
-    /**
348
-     * get_question_groups
349
-     *
350
-     * @return array
351
-     * @throws \EE_Error
352
-     */
353
-    public function get_all_question_groups()
354
-    {
355
-        return EE_Registry::instance()->load_model('Question_Group')->get_all(
356
-            array(
357
-                array('QSG_deleted' => false),
358
-                'order_by' => array('QSG_order' => 'ASC'),
359
-            )
360
-        );
361
-    }
362
-
363
-
364
-
365
-    /**
366
-     * get_question_groups
367
-     *
368
-     * @param int $EVT_ID
369
-     * @return array|bool
370
-     * @throws \EE_Error
371
-     */
372
-    public function get_all_event_question_groups($EVT_ID = 0)
373
-    {
374
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
375
-            EE_Error::add_error(
376
-                esc_html__(
377
-                    'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
378
-                    'event_espresso'
379
-                ),
380
-                __FILE__,
381
-                __FUNCTION__,
382
-                __LINE__
383
-            );
384
-            return false;
385
-        }
386
-        return EE_Registry::instance()->load_model('Event_Question_Group')->get_all(
387
-            array(
388
-                array('EVT_ID' => $EVT_ID),
389
-            )
390
-        );
391
-    }
392
-
393
-
394
-    /**
395
-     * get_question_groups
396
-     *
397
-     * @param int $EVT_ID
398
-     * @param boolean $for_primary_attendee
399
-     * @return array|bool
400
-     * @throws EE_Error
401
-     * @throws InvalidArgumentException
402
-     * @throws ReflectionException
403
-     * @throws InvalidDataTypeException
404
-     * @throws InvalidInterfaceException
405
-     */
406
-    public function get_event_question_groups($EVT_ID = 0, $for_primary_attendee = true)
407
-    {
408
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
409
-            EE_Error::add_error(
410
-                esc_html__(
411
-                    // @codingStandardsIgnoreStart
412
-                    'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
413
-                    // @codingStandardsIgnoreEnd
414
-                    'event_espresso'
415
-                ),
416
-                __FILE__,
417
-                __FUNCTION__,
418
-                __LINE__
419
-            );
420
-            return false;
421
-        }
422
-        $query_params = [
423
-            [
424
-                'EVT_ID' => $EVT_ID,
425
-                EEM_Event_Question_Group::instance()->field_name_for_category($for_primary_attendee) => true
426
-            ]
427
-        ];
428
-        if ($for_primary_attendee) {
429
-            $query_params[0][] = true;
430
-        } else {
431
-            $query_params[0]['EQG_additional'] = true;
432
-        }
433
-        return EE_Registry::instance()->load_model('Event_Question_Group')->get_all($query_params);
434
-    }
435
-
436
-
437
-
438
-    /**
439
-     * get_question_groups
440
-     *
441
-     * @param int             $EVT_ID
442
-     * @param EE_Registration $registration
443
-     * @return array|bool
444
-     * @throws \EE_Error
445
-     */
446
-    public function get_question_groups_for_event($EVT_ID = 0, EE_Registration $registration)
447
-    {
448
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
449
-            EE_Error::add_error(
450
-                esc_html__(
451
-                    'An error occurred. No Question Groups could be retrieved because an Event ID was not received.',
452
-                    'event_espresso'
453
-                ),
454
-                __FILE__,
455
-                __FUNCTION__,
456
-                __LINE__
457
-            );
458
-            return false;
459
-        }
460
-        $where_params = array(
461
-            'Event_Question_Group.EVT_ID'      => $EVT_ID,
462
-            'QSG_deleted'                      => false,
463
-        );
464
-        if( $registration->is_primary_registrant()) {
465
-            $where_params['Event_Question_Group.EQG_primary'] = true;
466
-        } else {
467
-            $where_params['Event_Question_Group.EQG_additional'] = true;
468
-        }
469
-        return EE_Registry::instance()->load_model('Question_Group')->get_all(
470
-            [
471
-                $where_params,
472
-                'order_by' => ['QSG_order' => 'ASC'],
473
-            ]
474
-        );
475
-    }
476
-
477
-
478
-
479
-    /**
480
-     * get_question_target_db_column
481
-     *
482
-     * @param string $QSG_IDs csv list of $QSG IDs
483
-     * @return array|bool
484
-     * @throws \EE_Error
485
-     */
486
-    public function get_questions_in_groups($QSG_IDs = '')
487
-    {
488
-        if (empty($QSG_IDs)) {
489
-            EE_Error::add_error(
490
-                esc_html__('An error occurred. No Question Group IDs were received.', 'event_espresso'),
491
-                __FILE__,
492
-                __FUNCTION__,
493
-                __LINE__
494
-            );
495
-            return false;
496
-        }
497
-        return EE_Registry::instance()->load_model('Question')->get_all(
498
-            array(
499
-                array(
500
-                    'Question_Group.QSG_ID' => array('IN', $QSG_IDs),
501
-                    'QST_deleted'           => false,
502
-                    'QST_admin_only'        => is_admin(),
503
-                ),
504
-                'order_by' => 'QST_order',
505
-            )
506
-        );
507
-    }
508
-
509
-
510
-
511
-    /**
512
-     * get_options_for_question
513
-     *
514
-     * @param string $QST_IDs csv list of $QST IDs
515
-     * @return array|bool
516
-     * @throws \EE_Error
517
-     */
518
-    public function get_options_for_question($QST_IDs)
519
-    {
520
-        if (empty($QST_IDs)) {
521
-            EE_Error::add_error(
522
-                esc_html__('An error occurred. No Question IDs were received.', 'event_espresso'),
523
-                __FILE__,
524
-                __FUNCTION__,
525
-                __LINE__
526
-            );
527
-            return false;
528
-        }
529
-        return EE_Registry::instance()->load_model('Question_Option')->get_all(
530
-            array(
531
-                array(
532
-                    'Question.QST_ID' => array('IN', $QST_IDs),
533
-                    'QSO_deleted'     => false,
534
-                ),
535
-                'order_by' => 'QSO_ID',
536
-            )
537
-        );
538
-    }
539
-
540
-
541
-
542
-
543
-
544
-
545
-
546
-    /**
547
-     * Gets all events that are published
548
-     * and have event start time earlier than now and an event end time later than now
549
-     *
550
-     * @param  array $query_params An array of query params to further filter on
551
-     *                             (note that status and DTT_EVT_start and DTT_EVT_end will be overridden)
552
-     * @param bool   $count        whether to return the count or not (default FALSE)
553
-     * @return EE_Event[]|int
554
-     * @throws \EE_Error
555
-     */
556
-    public function get_active_events($query_params, $count = false)
557
-    {
558
-        if (array_key_exists(0, $query_params)) {
559
-            $where_params = $query_params[0];
560
-            unset($query_params[0]);
561
-        } else {
562
-            $where_params = array();
563
-        }
564
-        // if we have count make sure we don't include group by
565
-        if ($count && isset($query_params['group_by'])) {
566
-            unset($query_params['group_by']);
567
-        }
568
-        // let's add specific query_params for active_events
569
-        // keep in mind this will override any sent status in the query AND any date queries.
570
-        $where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
571
-        // if already have where params for DTT_EVT_start or DTT_EVT_end then append these conditions
572
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
573
-            $where_params['Datetime.DTT_EVT_start******'] = array(
574
-                '<',
575
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
576
-            );
577
-        } else {
578
-            $where_params['Datetime.DTT_EVT_start'] = array(
579
-                '<',
580
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
581
-            );
582
-        }
583
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
584
-            $where_params['Datetime.DTT_EVT_end*****'] = array(
585
-                '>',
586
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
587
-            );
588
-        } else {
589
-            $where_params['Datetime.DTT_EVT_end'] = array(
590
-                '>',
591
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
592
-            );
593
-        }
594
-        $query_params[0] = $where_params;
595
-        // don't use $query_params with count()
596
-        // because we don't want to include additional query clauses like "GROUP BY"
597
-        return $count
598
-            ? $this->count(array($where_params), 'EVT_ID', true)
599
-            : $this->get_all($query_params);
600
-    }
601
-
602
-
603
-
604
-    /**
605
-     * get all events that are published and have an event start time later than now
606
-     *
607
-     * @param  array $query_params An array of query params to further filter on
608
-     *                             (Note that status and DTT_EVT_start will be overridden)
609
-     * @param bool   $count        whether to return the count or not (default FALSE)
610
-     * @return EE_Event[]|int
611
-     * @throws \EE_Error
612
-     */
613
-    public function get_upcoming_events($query_params, $count = false)
614
-    {
615
-        if (array_key_exists(0, $query_params)) {
616
-            $where_params = $query_params[0];
617
-            unset($query_params[0]);
618
-        } else {
619
-            $where_params = array();
620
-        }
621
-        // if we have count make sure we don't include group by
622
-        if ($count && isset($query_params['group_by'])) {
623
-            unset($query_params['group_by']);
624
-        }
625
-        // let's add specific query_params for active_events
626
-        // keep in mind this will override any sent status in the query AND any date queries.
627
-        $where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
628
-        // if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
629
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
630
-            $where_params['Datetime.DTT_EVT_start*****'] = array(
631
-                '>',
632
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
633
-            );
634
-        } else {
635
-            $where_params['Datetime.DTT_EVT_start'] = array(
636
-                '>',
637
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
638
-            );
639
-        }
640
-        $query_params[0] = $where_params;
641
-        // don't use $query_params with count()
642
-        // because we don't want to include additional query clauses like "GROUP BY"
643
-        return $count
644
-            ? $this->count(array($where_params), 'EVT_ID', true)
645
-            : $this->get_all($query_params);
646
-    }
647
-
648
-
649
-
650
-    /**
651
-     * Gets all events that are published
652
-     * and have an event end time later than now
653
-     *
654
-     * @param  array $query_params An array of query params to further filter on
655
-     *                             (note that status and DTT_EVT_end will be overridden)
656
-     * @param bool   $count        whether to return the count or not (default FALSE)
657
-     * @return EE_Event[]|int
658
-     * @throws \EE_Error
659
-     */
660
-    public function get_active_and_upcoming_events($query_params, $count = false)
661
-    {
662
-        if (array_key_exists(0, $query_params)) {
663
-            $where_params = $query_params[0];
664
-            unset($query_params[0]);
665
-        } else {
666
-            $where_params = array();
667
-        }
668
-        // if we have count make sure we don't include group by
669
-        if ($count && isset($query_params['group_by'])) {
670
-            unset($query_params['group_by']);
671
-        }
672
-        // let's add specific query_params for active_events
673
-        // keep in mind this will override any sent status in the query AND any date queries.
674
-        $where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
675
-        // add where params for DTT_EVT_end
676
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
677
-            $where_params['Datetime.DTT_EVT_end*****'] = array(
678
-                '>',
679
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
680
-            );
681
-        } else {
682
-            $where_params['Datetime.DTT_EVT_end'] = array(
683
-                '>',
684
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
685
-            );
686
-        }
687
-        $query_params[0] = $where_params;
688
-        // don't use $query_params with count()
689
-        // because we don't want to include additional query clauses like "GROUP BY"
690
-        return $count
691
-            ? $this->count(array($where_params), 'EVT_ID', true)
692
-            : $this->get_all($query_params);
693
-    }
694
-
695
-
696
-
697
-    /**
698
-     * This only returns events that are expired.
699
-     * They may still be published but all their datetimes have expired.
700
-     *
701
-     * @param  array $query_params An array of query params to further filter on
702
-     *                             (note that status and DTT_EVT_end will be overridden)
703
-     * @param bool   $count        whether to return the count or not (default FALSE)
704
-     * @return EE_Event[]|int
705
-     * @throws \EE_Error
706
-     */
707
-    public function get_expired_events($query_params, $count = false)
708
-    {
709
-        $where_params = isset($query_params[0]) ? $query_params[0] : array();
710
-        // if we have count make sure we don't include group by
711
-        if ($count && isset($query_params['group_by'])) {
712
-            unset($query_params['group_by']);
713
-        }
714
-        // let's add specific query_params for active_events
715
-        // keep in mind this will override any sent status in the query AND any date queries.
716
-        if (isset($where_params['status'])) {
717
-            unset($where_params['status']);
718
-        }
719
-        $exclude_query = $query_params;
720
-        if (isset($exclude_query[0])) {
721
-            unset($exclude_query[0]);
722
-        }
723
-        $exclude_query[0] = array(
724
-            'Datetime.DTT_EVT_end' => array(
725
-                '>',
726
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
727
-            ),
728
-        );
729
-        // first get all events that have datetimes where its not expired.
730
-        $event_ids = $this->_get_all_wpdb_results($exclude_query, OBJECT_K, 'Event_CPT.ID');
731
-        $event_ids = array_keys($event_ids);
732
-        // if we have any additional query_params, let's add them to the 'AND' condition
733
-        $and_condition = array(
734
-            'Datetime.DTT_EVT_end' => array('<', EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end')),
735
-            'EVT_ID'               => array('NOT IN', $event_ids),
736
-        );
737
-        if (isset($where_params['OR'])) {
738
-            $and_condition['OR'] = $where_params['OR'];
739
-            unset($where_params['OR']);
740
-        }
741
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
742
-            $and_condition['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
743
-            unset($where_params['Datetime.DTT_EVT_end']);
744
-        }
745
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
746
-            $and_condition['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
747
-            unset($where_params['Datetime.DTT_EVT_start']);
748
-        }
749
-        // merge remaining $where params with the and conditions.
750
-        $where_params['AND'] = array_merge($and_condition, $where_params);
751
-        $query_params[0] = $where_params;
752
-        // don't use $query_params with count()
753
-        // because we don't want to include additional query clauses like "GROUP BY"
754
-        return $count
755
-            ? $this->count(array($where_params), 'EVT_ID', true)
756
-            : $this->get_all($query_params);
757
-    }
758
-
759
-
760
-
761
-    /**
762
-     * This basically just returns the events that do not have the publish status.
763
-     *
764
-     * @param  array   $query_params An array of query params to further filter on
765
-     *                               (note that status will be overwritten)
766
-     * @param  boolean $count        whether to return the count or not (default FALSE)
767
-     * @return EE_Event[]|int
768
-     * @throws \EE_Error
769
-     */
770
-    public function get_inactive_events($query_params, $count = false)
771
-    {
772
-        $where_params = isset($query_params[0]) ? $query_params[0] : array();
773
-        // let's add in specific query_params for inactive events.
774
-        if (isset($where_params['status'])) {
775
-            unset($where_params['status']);
776
-        }
777
-        // if we have count make sure we don't include group by
778
-        if ($count && isset($query_params['group_by'])) {
779
-            unset($query_params['group_by']);
780
-        }
781
-        // if we have any additional query_params, let's add them to the 'AND' condition
782
-        $where_params['AND']['status'] = array('!=', 'publish');
783
-        if (isset($where_params['OR'])) {
784
-            $where_params['AND']['OR'] = $where_params['OR'];
785
-            unset($where_params['OR']);
786
-        }
787
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
788
-            $where_params['AND']['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
789
-            unset($where_params['Datetime.DTT_EVT_end']);
790
-        }
791
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
792
-            $where_params['AND']['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
793
-            unset($where_params['Datetime.DTT_EVT_start']);
794
-        }
795
-        $query_params[0] = $where_params;
796
-        // don't use $query_params with count()
797
-        // because we don't want to include additional query clauses like "GROUP BY"
798
-        return $count
799
-            ? $this->count(array($where_params), 'EVT_ID', true)
800
-            : $this->get_all($query_params);
801
-    }
802
-
803
-
804
-
805
-    /**
806
-     * This is just injecting into the parent add_relationship_to so we do special handling on price relationships
807
-     * because we don't want to override any existing global default prices but instead insert NEW prices that get
808
-     * attached to the event. See parent for param descriptions
809
-     *
810
-     * @param        $id_or_obj
811
-     * @param        $other_model_id_or_obj
812
-     * @param string $relationName
813
-     * @param array  $where_query
814
-     * @return EE_Base_Class
815
-     * @throws EE_Error
816
-     */
817
-    public function add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = array())
818
-    {
819
-        if ($relationName === 'Price') {
820
-            // let's get the PRC object for the given ID to make sure that we aren't dealing with a default
821
-            $prc_chk = $this->get_related_model_obj($relationName)->ensure_is_obj($other_model_id_or_obj);
822
-            // if EVT_ID = 0, then this is a default
823
-            if ((int) $prc_chk->get('EVT_ID') === 0) {
824
-                // let's set the prc_id as 0 so we force an insert on the add_relation_to carried out by relation
825
-                $prc_chk->set('PRC_ID', 0);
826
-            }
827
-            // run parent
828
-            return parent::add_relationship_to($id_or_obj, $prc_chk, $relationName, $where_query);
829
-        }
830
-        // otherwise carry on as normal
831
-        return parent::add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query);
832
-    }
833
-
834
-
835
-
836
-    /******************** DEPRECATED METHODS ********************/
837
-
838
-
839
-
840
-    /**
841
-     * _get_question_target_db_column
842
-     *
843
-     * @deprecated as of 4.8.32.rc.001. Instead consider using
844
-     *             EE_Registration_Custom_Questions_Form located in
845
-     *             admin_pages/registrations/form_sections/EE_Registration_Custom_Questions_Form.form.php
846
-     * @access     public
847
-     * @param    EE_Registration $registration (so existing answers for registration are included)
848
-     * @param    int             $EVT_ID       so all question groups are included for event (not just answers from
849
-     *                                         registration).
850
-     * @throws EE_Error
851
-     * @return    array
852
-     */
853
-    public function assemble_array_of_groups_questions_and_options(EE_Registration $registration, $EVT_ID = 0)
854
-    {
855
-        if (empty($EVT_ID)) {
856
-            throw new EE_Error(__(
857
-                'An error occurred. No EVT_ID is included.  Needed to know which question groups to retrieve.',
858
-                'event_espresso'
859
-            ));
860
-        }
861
-        $questions = array();
862
-        // get all question groups for event
863
-        $qgs = $this->get_question_groups_for_event($EVT_ID, $registration);
864
-        if (! empty($qgs)) {
865
-            foreach ($qgs as $qg) {
866
-                $qsts = $qg->questions();
867
-                $questions[ $qg->ID() ] = $qg->model_field_array();
868
-                $questions[ $qg->ID() ]['QSG_questions'] = array();
869
-                foreach ($qsts as $qst) {
870
-                    if ($qst->is_system_question()) {
871
-                        continue;
872
-                    }
873
-                    $answer = EEM_Answer::instance()->get_one(array(
874
-                        array(
875
-                            'QST_ID' => $qst->ID(),
876
-                            'REG_ID' => $registration->ID(),
877
-                        ),
878
-                    ));
879
-                    $answer = $answer instanceof EE_Answer ? $answer : EEM_Answer::instance()->create_default_object();
880
-                    $qst_name = $qstn_id = $qst->ID();
881
-                    $ans_id = $answer->ID();
882
-                    $qst_name = ! empty($ans_id) ? '[' . $qst_name . '][' . $ans_id . ']' : '[' . $qst_name . ']';
883
-                    $input_name = '';
884
-                    $input_id = sanitize_key($qst->display_text());
885
-                    $input_class = '';
886
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ] = $qst->model_field_array();
887
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_name'] = 'qstn'
888
-                                                                                           . $input_name
889
-                                                                                           . $qst_name;
890
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_id'] = $input_id . '-' . $qstn_id;
891
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_class'] = $input_class;
892
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'] = array();
893
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['qst_obj'] = $qst;
894
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['ans_obj'] = $answer;
895
-                    // leave responses as-is, don't convert stuff into html entities please!
896
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['htmlentities'] = false;
897
-                    if ($qst->type() == 'RADIO_BTN' || $qst->type() == 'CHECKBOX' || $qst->type() == 'DROPDOWN') {
898
-                        $QSOs = $qst->options(true, $answer->value());
899
-                        if (is_array($QSOs)) {
900
-                            foreach ($QSOs as $QSO_ID => $QSO) {
901
-                                $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'][ $QSO_ID ] = $QSO->model_field_array();
902
-                            }
903
-                        }
904
-                    }
905
-                }
906
-            }
907
-        }
908
-        return $questions;
909
-    }
910
-
911
-
912
-    /**
913
-     * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
914
-     *                             or an stdClass where each property is the name of a column,
915
-     * @return EE_Base_Class
916
-     * @throws \EE_Error
917
-     */
918
-    public function instantiate_class_from_array_or_object($cols_n_values)
919
-    {
920
-        $classInstance = parent::instantiate_class_from_array_or_object($cols_n_values);
921
-        if ($classInstance instanceof EE_Event) {
922
-            // events have their timezone defined in the DB, so use it immediately
923
-            $this->set_timezone($classInstance->get_timezone());
924
-        }
925
-        return $classInstance;
926
-    }
18
+	/**
19
+	 * constant used by status(), indicating that no more tickets can be purchased for any of the datetimes for the
20
+	 * event
21
+	 */
22
+	const sold_out = 'sold_out';
23
+
24
+	/**
25
+	 * constant used by status(), indicating that upcoming event dates have been postponed (may be pushed to a later
26
+	 * date)
27
+	 */
28
+	const postponed = 'postponed';
29
+
30
+	/**
31
+	 * constant used by status(), indicating that the event will no longer occur
32
+	 */
33
+	const cancelled = 'cancelled';
34
+
35
+
36
+	/**
37
+	 * @var string
38
+	 */
39
+	protected static $_default_reg_status;
40
+
41
+
42
+	/**
43
+	 * This is the default for the additional limit field.
44
+	 * @var int
45
+	 */
46
+	protected static $_default_additional_limit = 10;
47
+
48
+
49
+	/**
50
+	 * private instance of the Event object
51
+	 *
52
+	 * @var EEM_Event
53
+	 */
54
+	protected static $_instance;
55
+
56
+
57
+
58
+
59
+	/**
60
+	 * Adds a relationship to Term_Taxonomy for each CPT_Base
61
+	 *
62
+	 * @param string $timezone
63
+	 * @throws \EE_Error
64
+	 */
65
+	protected function __construct($timezone = null)
66
+	{
67
+		EE_Registry::instance()->load_model('Registration');
68
+		$this->singular_item = esc_html__('Event', 'event_espresso');
69
+		$this->plural_item = esc_html__('Events', 'event_espresso');
70
+		// to remove Cancelled events from the frontend, copy the following filter to your functions.php file
71
+		// add_filter( 'AFEE__EEM_Event__construct___custom_stati__cancelled__Public', '__return_false' );
72
+		// to remove Postponed events from the frontend, copy the following filter to your functions.php file
73
+		// add_filter( 'AFEE__EEM_Event__construct___custom_stati__postponed__Public', '__return_false' );
74
+		// to remove Sold Out events from the frontend, copy the following filter to your functions.php file
75
+		//  add_filter( 'AFEE__EEM_Event__construct___custom_stati__sold_out__Public', '__return_false' );
76
+		$this->_custom_stati = apply_filters(
77
+			'AFEE__EEM_Event__construct___custom_stati',
78
+			array(
79
+				EEM_Event::cancelled => array(
80
+					'label'  => esc_html__('Cancelled', 'event_espresso'),
81
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__cancelled__Public', true),
82
+				),
83
+				EEM_Event::postponed => array(
84
+					'label'  => esc_html__('Postponed', 'event_espresso'),
85
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__postponed__Public', true),
86
+				),
87
+				EEM_Event::sold_out  => array(
88
+					'label'  => esc_html__('Sold Out', 'event_espresso'),
89
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__sold_out__Public', true),
90
+				),
91
+			)
92
+		);
93
+		self::$_default_reg_status = empty(self::$_default_reg_status) ? EEM_Registration::status_id_pending_payment
94
+			: self::$_default_reg_status;
95
+		$this->_tables = array(
96
+			'Event_CPT'  => new EE_Primary_Table('posts', 'ID'),
97
+			'Event_Meta' => new EE_Secondary_Table('esp_event_meta', 'EVTM_ID', 'EVT_ID'),
98
+		);
99
+		$this->_fields = array(
100
+			'Event_CPT'  => array(
101
+				'EVT_ID'         => new EE_Primary_Key_Int_Field(
102
+					'ID',
103
+					esc_html__('Post ID for Event', 'event_espresso')
104
+				),
105
+				'EVT_name'       => new EE_Plain_Text_Field(
106
+					'post_title',
107
+					esc_html__('Event Name', 'event_espresso'),
108
+					false,
109
+					''
110
+				),
111
+				'EVT_desc'       => new EE_Post_Content_Field(
112
+					'post_content',
113
+					esc_html__('Event Description', 'event_espresso'),
114
+					false,
115
+					''
116
+				),
117
+				'EVT_slug'       => new EE_Slug_Field(
118
+					'post_name',
119
+					esc_html__('Event Slug', 'event_espresso'),
120
+					false,
121
+					''
122
+				),
123
+				'EVT_created'    => new EE_Datetime_Field(
124
+					'post_date',
125
+					esc_html__('Date/Time Event Created', 'event_espresso'),
126
+					false,
127
+					EE_Datetime_Field::now
128
+				),
129
+				'EVT_short_desc' => new EE_Simple_HTML_Field(
130
+					'post_excerpt',
131
+					esc_html__('Event Short Description', 'event_espresso'),
132
+					false,
133
+					''
134
+				),
135
+				'EVT_modified'   => new EE_Datetime_Field(
136
+					'post_modified',
137
+					esc_html__('Date/Time Event Modified', 'event_espresso'),
138
+					false,
139
+					EE_Datetime_Field::now
140
+				),
141
+				'EVT_wp_user'    => new EE_WP_User_Field(
142
+					'post_author',
143
+					esc_html__('Event Creator ID', 'event_espresso'),
144
+					false
145
+				),
146
+				'parent'         => new EE_Integer_Field(
147
+					'post_parent',
148
+					esc_html__('Event Parent ID', 'event_espresso'),
149
+					false,
150
+					0
151
+				),
152
+				'EVT_order'      => new EE_Integer_Field(
153
+					'menu_order',
154
+					esc_html__('Event Menu Order', 'event_espresso'),
155
+					false,
156
+					1
157
+				),
158
+				'post_type'      => new EE_WP_Post_Type_Field('espresso_events'),
159
+				// EE_Plain_Text_Field( 'post_type', esc_html__( 'Event Post Type', 'event_espresso' ), FALSE, 'espresso_events' ),
160
+				'status'         => new EE_WP_Post_Status_Field(
161
+					'post_status',
162
+					esc_html__('Event Status', 'event_espresso'),
163
+					false,
164
+					'draft',
165
+					$this->_custom_stati
166
+				),
167
+				'password' => new EE_Password_Field(
168
+					'post_password',
169
+					__('Password', 'event_espresso'),
170
+					false,
171
+					'',
172
+					array(
173
+						'EVT_desc',
174
+						'EVT_short_desc',
175
+						'EVT_display_desc',
176
+						'EVT_display_ticket_selector',
177
+						'EVT_visible_on',
178
+						'EVT_additional_limit',
179
+						'EVT_default_registration_status',
180
+						'EVT_member_only',
181
+						'EVT_phone',
182
+						'EVT_allow_overflow',
183
+						'EVT_timezone_string',
184
+						'EVT_external_URL',
185
+						'EVT_donations'
186
+					)
187
+				)
188
+			),
189
+			'Event_Meta' => array(
190
+				'EVTM_ID'                         => new EE_DB_Only_Float_Field(
191
+					'EVTM_ID',
192
+					esc_html__('Event Meta Row ID', 'event_espresso'),
193
+					false
194
+				),
195
+				'EVT_ID_fk'                       => new EE_DB_Only_Int_Field(
196
+					'EVT_ID',
197
+					esc_html__('Foreign key to Event ID from Event Meta table', 'event_espresso'),
198
+					false
199
+				),
200
+				'EVT_display_desc'                => new EE_Boolean_Field(
201
+					'EVT_display_desc',
202
+					esc_html__('Display Description Flag', 'event_espresso'),
203
+					false,
204
+					true
205
+				),
206
+				'EVT_display_ticket_selector'     => new EE_Boolean_Field(
207
+					'EVT_display_ticket_selector',
208
+					esc_html__('Display Ticket Selector Flag', 'event_espresso'),
209
+					false,
210
+					true
211
+				),
212
+				'EVT_visible_on'                  => new EE_Datetime_Field(
213
+					'EVT_visible_on',
214
+					esc_html__('Event Visible Date', 'event_espresso'),
215
+					true,
216
+					EE_Datetime_Field::now
217
+				),
218
+				'EVT_additional_limit'            => new EE_Integer_Field(
219
+					'EVT_additional_limit',
220
+					esc_html__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
221
+					true,
222
+					self::$_default_additional_limit
223
+				),
224
+				'EVT_default_registration_status' => new EE_Enum_Text_Field(
225
+					'EVT_default_registration_status',
226
+					esc_html__('Default Registration Status on this Event', 'event_espresso'),
227
+					false,
228
+					EEM_Event::$_default_reg_status,
229
+					EEM_Registration::reg_status_array()
230
+				),
231
+				'EVT_member_only'                 => new EE_Boolean_Field(
232
+					'EVT_member_only',
233
+					esc_html__('Member-Only Event Flag', 'event_espresso'),
234
+					false,
235
+					false
236
+				),
237
+				'EVT_phone'                       => new EE_Plain_Text_Field(
238
+					'EVT_phone',
239
+					esc_html__('Event Phone Number', 'event_espresso'),
240
+					false,
241
+					''
242
+				),
243
+				'EVT_allow_overflow'              => new EE_Boolean_Field(
244
+					'EVT_allow_overflow',
245
+					esc_html__('Allow Overflow on Event', 'event_espresso'),
246
+					false,
247
+					false
248
+				),
249
+				'EVT_timezone_string'             => new EE_Plain_Text_Field(
250
+					'EVT_timezone_string',
251
+					esc_html__('Timezone (name) for Event times', 'event_espresso'),
252
+					false,
253
+					''
254
+				),
255
+				'EVT_external_URL'                => new EE_Plain_Text_Field(
256
+					'EVT_external_URL',
257
+					esc_html__('URL of Event Page if hosted elsewhere', 'event_espresso'),
258
+					true
259
+				),
260
+				'EVT_donations'                   => new EE_Boolean_Field(
261
+					'EVT_donations',
262
+					esc_html__('Accept Donations?', 'event_espresso'),
263
+					false,
264
+					false
265
+				),
266
+			),
267
+		);
268
+		$this->_model_relations = array(
269
+			'Registration'           => new EE_Has_Many_Relation(),
270
+			'Datetime'               => new EE_Has_Many_Relation(),
271
+			'Question_Group'         => new EE_HABTM_Relation('Event_Question_Group'),
272
+			'Event_Question_Group'   => new EE_Has_Many_Relation(),
273
+			'Venue'                  => new EE_HABTM_Relation('Event_Venue'),
274
+			'Term_Relationship'      => new EE_Has_Many_Relation(),
275
+			'Term_Taxonomy'          => new EE_HABTM_Relation('Term_Relationship'),
276
+			'Message_Template_Group' => new EE_HABTM_Relation('Event_Message_Template'),
277
+			'Attendee'               => new EE_HABTM_Relation('Registration'),
278
+			'WP_User'                => new EE_Belongs_To_Relation(),
279
+		);
280
+		// this model is generally available for reading
281
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
282
+		$this->model_chain_to_password = '';
283
+		parent::__construct($timezone);
284
+	}
285
+
286
+
287
+
288
+	/**
289
+	 * @param string $default_reg_status
290
+	 */
291
+	public static function set_default_reg_status($default_reg_status)
292
+	{
293
+		self::$_default_reg_status = $default_reg_status;
294
+		// if EEM_Event has already been instantiated,
295
+		// then we need to reset the `EVT_default_reg_status` field to use the new default.
296
+		if (self::$_instance instanceof EEM_Event) {
297
+			$default_reg_status = new EE_Enum_Text_Field(
298
+				'EVT_default_registration_status',
299
+				esc_html__('Default Registration Status on this Event', 'event_espresso'),
300
+				false,
301
+				$default_reg_status,
302
+				EEM_Registration::reg_status_array()
303
+			);
304
+			$default_reg_status->_construct_finalize(
305
+				'Event_Meta',
306
+				'EVT_default_registration_status',
307
+				'EEM_Event'
308
+			);
309
+			self::$_instance->_fields['Event_Meta']['EVT_default_registration_status'] = $default_reg_status;
310
+		}
311
+	}
312
+
313
+
314
+	/**
315
+	 * Used to override the default for the additional limit field.
316
+	 * @param $additional_limit
317
+	 */
318
+	public static function set_default_additional_limit($additional_limit)
319
+	{
320
+		self::$_default_additional_limit = (int) $additional_limit;
321
+		if (self::$_instance instanceof EEM_Event) {
322
+			self::$_instance->_fields['Event_Meta']['EVT_additional_limit'] = new EE_Integer_Field(
323
+				'EVT_additional_limit',
324
+				__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
325
+				true,
326
+				self::$_default_additional_limit
327
+			);
328
+			self::$_instance->_fields['Event_Meta']['EVT_additional_limit']->_construct_finalize(
329
+				'Event_Meta',
330
+				'EVT_additional_limit',
331
+				'EEM_Event'
332
+			);
333
+		}
334
+	}
335
+
336
+
337
+	/**
338
+	 * Return what is currently set as the default additional limit for the event.
339
+	 * @return int
340
+	 */
341
+	public static function get_default_additional_limit()
342
+	{
343
+		return apply_filters('FHEE__EEM_Event__get_default_additional_limit', self::$_default_additional_limit);
344
+	}
345
+
346
+
347
+	/**
348
+	 * get_question_groups
349
+	 *
350
+	 * @return array
351
+	 * @throws \EE_Error
352
+	 */
353
+	public function get_all_question_groups()
354
+	{
355
+		return EE_Registry::instance()->load_model('Question_Group')->get_all(
356
+			array(
357
+				array('QSG_deleted' => false),
358
+				'order_by' => array('QSG_order' => 'ASC'),
359
+			)
360
+		);
361
+	}
362
+
363
+
364
+
365
+	/**
366
+	 * get_question_groups
367
+	 *
368
+	 * @param int $EVT_ID
369
+	 * @return array|bool
370
+	 * @throws \EE_Error
371
+	 */
372
+	public function get_all_event_question_groups($EVT_ID = 0)
373
+	{
374
+		if (! isset($EVT_ID) || ! absint($EVT_ID)) {
375
+			EE_Error::add_error(
376
+				esc_html__(
377
+					'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
378
+					'event_espresso'
379
+				),
380
+				__FILE__,
381
+				__FUNCTION__,
382
+				__LINE__
383
+			);
384
+			return false;
385
+		}
386
+		return EE_Registry::instance()->load_model('Event_Question_Group')->get_all(
387
+			array(
388
+				array('EVT_ID' => $EVT_ID),
389
+			)
390
+		);
391
+	}
392
+
393
+
394
+	/**
395
+	 * get_question_groups
396
+	 *
397
+	 * @param int $EVT_ID
398
+	 * @param boolean $for_primary_attendee
399
+	 * @return array|bool
400
+	 * @throws EE_Error
401
+	 * @throws InvalidArgumentException
402
+	 * @throws ReflectionException
403
+	 * @throws InvalidDataTypeException
404
+	 * @throws InvalidInterfaceException
405
+	 */
406
+	public function get_event_question_groups($EVT_ID = 0, $for_primary_attendee = true)
407
+	{
408
+		if (! isset($EVT_ID) || ! absint($EVT_ID)) {
409
+			EE_Error::add_error(
410
+				esc_html__(
411
+					// @codingStandardsIgnoreStart
412
+					'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
413
+					// @codingStandardsIgnoreEnd
414
+					'event_espresso'
415
+				),
416
+				__FILE__,
417
+				__FUNCTION__,
418
+				__LINE__
419
+			);
420
+			return false;
421
+		}
422
+		$query_params = [
423
+			[
424
+				'EVT_ID' => $EVT_ID,
425
+				EEM_Event_Question_Group::instance()->field_name_for_category($for_primary_attendee) => true
426
+			]
427
+		];
428
+		if ($for_primary_attendee) {
429
+			$query_params[0][] = true;
430
+		} else {
431
+			$query_params[0]['EQG_additional'] = true;
432
+		}
433
+		return EE_Registry::instance()->load_model('Event_Question_Group')->get_all($query_params);
434
+	}
435
+
436
+
437
+
438
+	/**
439
+	 * get_question_groups
440
+	 *
441
+	 * @param int             $EVT_ID
442
+	 * @param EE_Registration $registration
443
+	 * @return array|bool
444
+	 * @throws \EE_Error
445
+	 */
446
+	public function get_question_groups_for_event($EVT_ID = 0, EE_Registration $registration)
447
+	{
448
+		if (! isset($EVT_ID) || ! absint($EVT_ID)) {
449
+			EE_Error::add_error(
450
+				esc_html__(
451
+					'An error occurred. No Question Groups could be retrieved because an Event ID was not received.',
452
+					'event_espresso'
453
+				),
454
+				__FILE__,
455
+				__FUNCTION__,
456
+				__LINE__
457
+			);
458
+			return false;
459
+		}
460
+		$where_params = array(
461
+			'Event_Question_Group.EVT_ID'      => $EVT_ID,
462
+			'QSG_deleted'                      => false,
463
+		);
464
+		if( $registration->is_primary_registrant()) {
465
+			$where_params['Event_Question_Group.EQG_primary'] = true;
466
+		} else {
467
+			$where_params['Event_Question_Group.EQG_additional'] = true;
468
+		}
469
+		return EE_Registry::instance()->load_model('Question_Group')->get_all(
470
+			[
471
+				$where_params,
472
+				'order_by' => ['QSG_order' => 'ASC'],
473
+			]
474
+		);
475
+	}
476
+
477
+
478
+
479
+	/**
480
+	 * get_question_target_db_column
481
+	 *
482
+	 * @param string $QSG_IDs csv list of $QSG IDs
483
+	 * @return array|bool
484
+	 * @throws \EE_Error
485
+	 */
486
+	public function get_questions_in_groups($QSG_IDs = '')
487
+	{
488
+		if (empty($QSG_IDs)) {
489
+			EE_Error::add_error(
490
+				esc_html__('An error occurred. No Question Group IDs were received.', 'event_espresso'),
491
+				__FILE__,
492
+				__FUNCTION__,
493
+				__LINE__
494
+			);
495
+			return false;
496
+		}
497
+		return EE_Registry::instance()->load_model('Question')->get_all(
498
+			array(
499
+				array(
500
+					'Question_Group.QSG_ID' => array('IN', $QSG_IDs),
501
+					'QST_deleted'           => false,
502
+					'QST_admin_only'        => is_admin(),
503
+				),
504
+				'order_by' => 'QST_order',
505
+			)
506
+		);
507
+	}
508
+
509
+
510
+
511
+	/**
512
+	 * get_options_for_question
513
+	 *
514
+	 * @param string $QST_IDs csv list of $QST IDs
515
+	 * @return array|bool
516
+	 * @throws \EE_Error
517
+	 */
518
+	public function get_options_for_question($QST_IDs)
519
+	{
520
+		if (empty($QST_IDs)) {
521
+			EE_Error::add_error(
522
+				esc_html__('An error occurred. No Question IDs were received.', 'event_espresso'),
523
+				__FILE__,
524
+				__FUNCTION__,
525
+				__LINE__
526
+			);
527
+			return false;
528
+		}
529
+		return EE_Registry::instance()->load_model('Question_Option')->get_all(
530
+			array(
531
+				array(
532
+					'Question.QST_ID' => array('IN', $QST_IDs),
533
+					'QSO_deleted'     => false,
534
+				),
535
+				'order_by' => 'QSO_ID',
536
+			)
537
+		);
538
+	}
539
+
540
+
541
+
542
+
543
+
544
+
545
+
546
+	/**
547
+	 * Gets all events that are published
548
+	 * and have event start time earlier than now and an event end time later than now
549
+	 *
550
+	 * @param  array $query_params An array of query params to further filter on
551
+	 *                             (note that status and DTT_EVT_start and DTT_EVT_end will be overridden)
552
+	 * @param bool   $count        whether to return the count or not (default FALSE)
553
+	 * @return EE_Event[]|int
554
+	 * @throws \EE_Error
555
+	 */
556
+	public function get_active_events($query_params, $count = false)
557
+	{
558
+		if (array_key_exists(0, $query_params)) {
559
+			$where_params = $query_params[0];
560
+			unset($query_params[0]);
561
+		} else {
562
+			$where_params = array();
563
+		}
564
+		// if we have count make sure we don't include group by
565
+		if ($count && isset($query_params['group_by'])) {
566
+			unset($query_params['group_by']);
567
+		}
568
+		// let's add specific query_params for active_events
569
+		// keep in mind this will override any sent status in the query AND any date queries.
570
+		$where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
571
+		// if already have where params for DTT_EVT_start or DTT_EVT_end then append these conditions
572
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
573
+			$where_params['Datetime.DTT_EVT_start******'] = array(
574
+				'<',
575
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
576
+			);
577
+		} else {
578
+			$where_params['Datetime.DTT_EVT_start'] = array(
579
+				'<',
580
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
581
+			);
582
+		}
583
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
584
+			$where_params['Datetime.DTT_EVT_end*****'] = array(
585
+				'>',
586
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
587
+			);
588
+		} else {
589
+			$where_params['Datetime.DTT_EVT_end'] = array(
590
+				'>',
591
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
592
+			);
593
+		}
594
+		$query_params[0] = $where_params;
595
+		// don't use $query_params with count()
596
+		// because we don't want to include additional query clauses like "GROUP BY"
597
+		return $count
598
+			? $this->count(array($where_params), 'EVT_ID', true)
599
+			: $this->get_all($query_params);
600
+	}
601
+
602
+
603
+
604
+	/**
605
+	 * get all events that are published and have an event start time later than now
606
+	 *
607
+	 * @param  array $query_params An array of query params to further filter on
608
+	 *                             (Note that status and DTT_EVT_start will be overridden)
609
+	 * @param bool   $count        whether to return the count or not (default FALSE)
610
+	 * @return EE_Event[]|int
611
+	 * @throws \EE_Error
612
+	 */
613
+	public function get_upcoming_events($query_params, $count = false)
614
+	{
615
+		if (array_key_exists(0, $query_params)) {
616
+			$where_params = $query_params[0];
617
+			unset($query_params[0]);
618
+		} else {
619
+			$where_params = array();
620
+		}
621
+		// if we have count make sure we don't include group by
622
+		if ($count && isset($query_params['group_by'])) {
623
+			unset($query_params['group_by']);
624
+		}
625
+		// let's add specific query_params for active_events
626
+		// keep in mind this will override any sent status in the query AND any date queries.
627
+		$where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
628
+		// if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
629
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
630
+			$where_params['Datetime.DTT_EVT_start*****'] = array(
631
+				'>',
632
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
633
+			);
634
+		} else {
635
+			$where_params['Datetime.DTT_EVT_start'] = array(
636
+				'>',
637
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
638
+			);
639
+		}
640
+		$query_params[0] = $where_params;
641
+		// don't use $query_params with count()
642
+		// because we don't want to include additional query clauses like "GROUP BY"
643
+		return $count
644
+			? $this->count(array($where_params), 'EVT_ID', true)
645
+			: $this->get_all($query_params);
646
+	}
647
+
648
+
649
+
650
+	/**
651
+	 * Gets all events that are published
652
+	 * and have an event end time later than now
653
+	 *
654
+	 * @param  array $query_params An array of query params to further filter on
655
+	 *                             (note that status and DTT_EVT_end will be overridden)
656
+	 * @param bool   $count        whether to return the count or not (default FALSE)
657
+	 * @return EE_Event[]|int
658
+	 * @throws \EE_Error
659
+	 */
660
+	public function get_active_and_upcoming_events($query_params, $count = false)
661
+	{
662
+		if (array_key_exists(0, $query_params)) {
663
+			$where_params = $query_params[0];
664
+			unset($query_params[0]);
665
+		} else {
666
+			$where_params = array();
667
+		}
668
+		// if we have count make sure we don't include group by
669
+		if ($count && isset($query_params['group_by'])) {
670
+			unset($query_params['group_by']);
671
+		}
672
+		// let's add specific query_params for active_events
673
+		// keep in mind this will override any sent status in the query AND any date queries.
674
+		$where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
675
+		// add where params for DTT_EVT_end
676
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
677
+			$where_params['Datetime.DTT_EVT_end*****'] = array(
678
+				'>',
679
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
680
+			);
681
+		} else {
682
+			$where_params['Datetime.DTT_EVT_end'] = array(
683
+				'>',
684
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
685
+			);
686
+		}
687
+		$query_params[0] = $where_params;
688
+		// don't use $query_params with count()
689
+		// because we don't want to include additional query clauses like "GROUP BY"
690
+		return $count
691
+			? $this->count(array($where_params), 'EVT_ID', true)
692
+			: $this->get_all($query_params);
693
+	}
694
+
695
+
696
+
697
+	/**
698
+	 * This only returns events that are expired.
699
+	 * They may still be published but all their datetimes have expired.
700
+	 *
701
+	 * @param  array $query_params An array of query params to further filter on
702
+	 *                             (note that status and DTT_EVT_end will be overridden)
703
+	 * @param bool   $count        whether to return the count or not (default FALSE)
704
+	 * @return EE_Event[]|int
705
+	 * @throws \EE_Error
706
+	 */
707
+	public function get_expired_events($query_params, $count = false)
708
+	{
709
+		$where_params = isset($query_params[0]) ? $query_params[0] : array();
710
+		// if we have count make sure we don't include group by
711
+		if ($count && isset($query_params['group_by'])) {
712
+			unset($query_params['group_by']);
713
+		}
714
+		// let's add specific query_params for active_events
715
+		// keep in mind this will override any sent status in the query AND any date queries.
716
+		if (isset($where_params['status'])) {
717
+			unset($where_params['status']);
718
+		}
719
+		$exclude_query = $query_params;
720
+		if (isset($exclude_query[0])) {
721
+			unset($exclude_query[0]);
722
+		}
723
+		$exclude_query[0] = array(
724
+			'Datetime.DTT_EVT_end' => array(
725
+				'>',
726
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
727
+			),
728
+		);
729
+		// first get all events that have datetimes where its not expired.
730
+		$event_ids = $this->_get_all_wpdb_results($exclude_query, OBJECT_K, 'Event_CPT.ID');
731
+		$event_ids = array_keys($event_ids);
732
+		// if we have any additional query_params, let's add them to the 'AND' condition
733
+		$and_condition = array(
734
+			'Datetime.DTT_EVT_end' => array('<', EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end')),
735
+			'EVT_ID'               => array('NOT IN', $event_ids),
736
+		);
737
+		if (isset($where_params['OR'])) {
738
+			$and_condition['OR'] = $where_params['OR'];
739
+			unset($where_params['OR']);
740
+		}
741
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
742
+			$and_condition['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
743
+			unset($where_params['Datetime.DTT_EVT_end']);
744
+		}
745
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
746
+			$and_condition['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
747
+			unset($where_params['Datetime.DTT_EVT_start']);
748
+		}
749
+		// merge remaining $where params with the and conditions.
750
+		$where_params['AND'] = array_merge($and_condition, $where_params);
751
+		$query_params[0] = $where_params;
752
+		// don't use $query_params with count()
753
+		// because we don't want to include additional query clauses like "GROUP BY"
754
+		return $count
755
+			? $this->count(array($where_params), 'EVT_ID', true)
756
+			: $this->get_all($query_params);
757
+	}
758
+
759
+
760
+
761
+	/**
762
+	 * This basically just returns the events that do not have the publish status.
763
+	 *
764
+	 * @param  array   $query_params An array of query params to further filter on
765
+	 *                               (note that status will be overwritten)
766
+	 * @param  boolean $count        whether to return the count or not (default FALSE)
767
+	 * @return EE_Event[]|int
768
+	 * @throws \EE_Error
769
+	 */
770
+	public function get_inactive_events($query_params, $count = false)
771
+	{
772
+		$where_params = isset($query_params[0]) ? $query_params[0] : array();
773
+		// let's add in specific query_params for inactive events.
774
+		if (isset($where_params['status'])) {
775
+			unset($where_params['status']);
776
+		}
777
+		// if we have count make sure we don't include group by
778
+		if ($count && isset($query_params['group_by'])) {
779
+			unset($query_params['group_by']);
780
+		}
781
+		// if we have any additional query_params, let's add them to the 'AND' condition
782
+		$where_params['AND']['status'] = array('!=', 'publish');
783
+		if (isset($where_params['OR'])) {
784
+			$where_params['AND']['OR'] = $where_params['OR'];
785
+			unset($where_params['OR']);
786
+		}
787
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
788
+			$where_params['AND']['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
789
+			unset($where_params['Datetime.DTT_EVT_end']);
790
+		}
791
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
792
+			$where_params['AND']['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
793
+			unset($where_params['Datetime.DTT_EVT_start']);
794
+		}
795
+		$query_params[0] = $where_params;
796
+		// don't use $query_params with count()
797
+		// because we don't want to include additional query clauses like "GROUP BY"
798
+		return $count
799
+			? $this->count(array($where_params), 'EVT_ID', true)
800
+			: $this->get_all($query_params);
801
+	}
802
+
803
+
804
+
805
+	/**
806
+	 * This is just injecting into the parent add_relationship_to so we do special handling on price relationships
807
+	 * because we don't want to override any existing global default prices but instead insert NEW prices that get
808
+	 * attached to the event. See parent for param descriptions
809
+	 *
810
+	 * @param        $id_or_obj
811
+	 * @param        $other_model_id_or_obj
812
+	 * @param string $relationName
813
+	 * @param array  $where_query
814
+	 * @return EE_Base_Class
815
+	 * @throws EE_Error
816
+	 */
817
+	public function add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = array())
818
+	{
819
+		if ($relationName === 'Price') {
820
+			// let's get the PRC object for the given ID to make sure that we aren't dealing with a default
821
+			$prc_chk = $this->get_related_model_obj($relationName)->ensure_is_obj($other_model_id_or_obj);
822
+			// if EVT_ID = 0, then this is a default
823
+			if ((int) $prc_chk->get('EVT_ID') === 0) {
824
+				// let's set the prc_id as 0 so we force an insert on the add_relation_to carried out by relation
825
+				$prc_chk->set('PRC_ID', 0);
826
+			}
827
+			// run parent
828
+			return parent::add_relationship_to($id_or_obj, $prc_chk, $relationName, $where_query);
829
+		}
830
+		// otherwise carry on as normal
831
+		return parent::add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query);
832
+	}
833
+
834
+
835
+
836
+	/******************** DEPRECATED METHODS ********************/
837
+
838
+
839
+
840
+	/**
841
+	 * _get_question_target_db_column
842
+	 *
843
+	 * @deprecated as of 4.8.32.rc.001. Instead consider using
844
+	 *             EE_Registration_Custom_Questions_Form located in
845
+	 *             admin_pages/registrations/form_sections/EE_Registration_Custom_Questions_Form.form.php
846
+	 * @access     public
847
+	 * @param    EE_Registration $registration (so existing answers for registration are included)
848
+	 * @param    int             $EVT_ID       so all question groups are included for event (not just answers from
849
+	 *                                         registration).
850
+	 * @throws EE_Error
851
+	 * @return    array
852
+	 */
853
+	public function assemble_array_of_groups_questions_and_options(EE_Registration $registration, $EVT_ID = 0)
854
+	{
855
+		if (empty($EVT_ID)) {
856
+			throw new EE_Error(__(
857
+				'An error occurred. No EVT_ID is included.  Needed to know which question groups to retrieve.',
858
+				'event_espresso'
859
+			));
860
+		}
861
+		$questions = array();
862
+		// get all question groups for event
863
+		$qgs = $this->get_question_groups_for_event($EVT_ID, $registration);
864
+		if (! empty($qgs)) {
865
+			foreach ($qgs as $qg) {
866
+				$qsts = $qg->questions();
867
+				$questions[ $qg->ID() ] = $qg->model_field_array();
868
+				$questions[ $qg->ID() ]['QSG_questions'] = array();
869
+				foreach ($qsts as $qst) {
870
+					if ($qst->is_system_question()) {
871
+						continue;
872
+					}
873
+					$answer = EEM_Answer::instance()->get_one(array(
874
+						array(
875
+							'QST_ID' => $qst->ID(),
876
+							'REG_ID' => $registration->ID(),
877
+						),
878
+					));
879
+					$answer = $answer instanceof EE_Answer ? $answer : EEM_Answer::instance()->create_default_object();
880
+					$qst_name = $qstn_id = $qst->ID();
881
+					$ans_id = $answer->ID();
882
+					$qst_name = ! empty($ans_id) ? '[' . $qst_name . '][' . $ans_id . ']' : '[' . $qst_name . ']';
883
+					$input_name = '';
884
+					$input_id = sanitize_key($qst->display_text());
885
+					$input_class = '';
886
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ] = $qst->model_field_array();
887
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_name'] = 'qstn'
888
+																						   . $input_name
889
+																						   . $qst_name;
890
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_id'] = $input_id . '-' . $qstn_id;
891
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_class'] = $input_class;
892
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'] = array();
893
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['qst_obj'] = $qst;
894
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['ans_obj'] = $answer;
895
+					// leave responses as-is, don't convert stuff into html entities please!
896
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['htmlentities'] = false;
897
+					if ($qst->type() == 'RADIO_BTN' || $qst->type() == 'CHECKBOX' || $qst->type() == 'DROPDOWN') {
898
+						$QSOs = $qst->options(true, $answer->value());
899
+						if (is_array($QSOs)) {
900
+							foreach ($QSOs as $QSO_ID => $QSO) {
901
+								$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'][ $QSO_ID ] = $QSO->model_field_array();
902
+							}
903
+						}
904
+					}
905
+				}
906
+			}
907
+		}
908
+		return $questions;
909
+	}
910
+
911
+
912
+	/**
913
+	 * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
914
+	 *                             or an stdClass where each property is the name of a column,
915
+	 * @return EE_Base_Class
916
+	 * @throws \EE_Error
917
+	 */
918
+	public function instantiate_class_from_array_or_object($cols_n_values)
919
+	{
920
+		$classInstance = parent::instantiate_class_from_array_or_object($cols_n_values);
921
+		if ($classInstance instanceof EE_Event) {
922
+			// events have their timezone defined in the DB, so use it immediately
923
+			$this->set_timezone($classInstance->get_timezone());
924
+		}
925
+		return $classInstance;
926
+	}
927 927
 }
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -278,7 +278,7 @@  discard block
 block discarded – undo
278 278
             'WP_User'                => new EE_Belongs_To_Relation(),
279 279
         );
280 280
         // this model is generally available for reading
281
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
281
+        $this->_cap_restriction_generators[EEM_Base::caps_read] = new EE_Restriction_Generator_Public();
282 282
         $this->model_chain_to_password = '';
283 283
         parent::__construct($timezone);
284 284
     }
@@ -371,7 +371,7 @@  discard block
 block discarded – undo
371 371
      */
372 372
     public function get_all_event_question_groups($EVT_ID = 0)
373 373
     {
374
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
374
+        if ( ! isset($EVT_ID) || ! absint($EVT_ID)) {
375 375
             EE_Error::add_error(
376 376
                 esc_html__(
377 377
                     'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
@@ -405,7 +405,7 @@  discard block
 block discarded – undo
405 405
      */
406 406
     public function get_event_question_groups($EVT_ID = 0, $for_primary_attendee = true)
407 407
     {
408
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
408
+        if ( ! isset($EVT_ID) || ! absint($EVT_ID)) {
409 409
             EE_Error::add_error(
410 410
                 esc_html__(
411 411
                     // @codingStandardsIgnoreStart
@@ -445,7 +445,7 @@  discard block
 block discarded – undo
445 445
      */
446 446
     public function get_question_groups_for_event($EVT_ID = 0, EE_Registration $registration)
447 447
     {
448
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
448
+        if ( ! isset($EVT_ID) || ! absint($EVT_ID)) {
449 449
             EE_Error::add_error(
450 450
                 esc_html__(
451 451
                     'An error occurred. No Question Groups could be retrieved because an Event ID was not received.',
@@ -461,7 +461,7 @@  discard block
 block discarded – undo
461 461
             'Event_Question_Group.EVT_ID'      => $EVT_ID,
462 462
             'QSG_deleted'                      => false,
463 463
         );
464
-        if( $registration->is_primary_registrant()) {
464
+        if ($registration->is_primary_registrant()) {
465 465
             $where_params['Event_Question_Group.EQG_primary'] = true;
466 466
         } else {
467 467
             $where_params['Event_Question_Group.EQG_additional'] = true;
@@ -861,11 +861,11 @@  discard block
 block discarded – undo
861 861
         $questions = array();
862 862
         // get all question groups for event
863 863
         $qgs = $this->get_question_groups_for_event($EVT_ID, $registration);
864
-        if (! empty($qgs)) {
864
+        if ( ! empty($qgs)) {
865 865
             foreach ($qgs as $qg) {
866 866
                 $qsts = $qg->questions();
867
-                $questions[ $qg->ID() ] = $qg->model_field_array();
868
-                $questions[ $qg->ID() ]['QSG_questions'] = array();
867
+                $questions[$qg->ID()] = $qg->model_field_array();
868
+                $questions[$qg->ID()]['QSG_questions'] = array();
869 869
                 foreach ($qsts as $qst) {
870 870
                     if ($qst->is_system_question()) {
871 871
                         continue;
@@ -879,26 +879,26 @@  discard block
 block discarded – undo
879 879
                     $answer = $answer instanceof EE_Answer ? $answer : EEM_Answer::instance()->create_default_object();
880 880
                     $qst_name = $qstn_id = $qst->ID();
881 881
                     $ans_id = $answer->ID();
882
-                    $qst_name = ! empty($ans_id) ? '[' . $qst_name . '][' . $ans_id . ']' : '[' . $qst_name . ']';
882
+                    $qst_name = ! empty($ans_id) ? '['.$qst_name.']['.$ans_id.']' : '['.$qst_name.']';
883 883
                     $input_name = '';
884 884
                     $input_id = sanitize_key($qst->display_text());
885 885
                     $input_class = '';
886
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ] = $qst->model_field_array();
887
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_name'] = 'qstn'
886
+                    $questions[$qg->ID()]['QSG_questions'][$qst->ID()] = $qst->model_field_array();
887
+                    $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_input_name'] = 'qstn'
888 888
                                                                                            . $input_name
889 889
                                                                                            . $qst_name;
890
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_id'] = $input_id . '-' . $qstn_id;
891
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_class'] = $input_class;
892
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'] = array();
893
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['qst_obj'] = $qst;
894
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['ans_obj'] = $answer;
890
+                    $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_input_id'] = $input_id.'-'.$qstn_id;
891
+                    $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_input_class'] = $input_class;
892
+                    $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_options'] = array();
893
+                    $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['qst_obj'] = $qst;
894
+                    $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['ans_obj'] = $answer;
895 895
                     // leave responses as-is, don't convert stuff into html entities please!
896
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['htmlentities'] = false;
896
+                    $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['htmlentities'] = false;
897 897
                     if ($qst->type() == 'RADIO_BTN' || $qst->type() == 'CHECKBOX' || $qst->type() == 'DROPDOWN') {
898 898
                         $QSOs = $qst->options(true, $answer->value());
899 899
                         if (is_array($QSOs)) {
900 900
                             foreach ($QSOs as $QSO_ID => $QSO) {
901
-                                $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'][ $QSO_ID ] = $QSO->model_field_array();
901
+                                $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_options'][$QSO_ID] = $QSO->model_field_array();
902 902
                             }
903 903
                         }
904 904
                     }
Please login to merge, or discard this patch.
registration_form/espresso_events_Registration_Form_Hooks.class.php 1 patch
Indentation   +192 added lines, -192 removed lines patch added patch discarded remove patch
@@ -16,210 +16,210 @@
 block discarded – undo
16 16
 class espresso_events_Registration_Form_Hooks extends EE_Admin_Hooks
17 17
 {
18 18
 
19
-    /**
20
-     * @var EE_Event|null
21
-     */
22
-    protected $_event;
23
-
24
-
25
-    protected function _set_hooks_properties()
26
-    {
27
-
28
-        $this->_name = 'registration_form';
29
-        $this->_metaboxes = array(
30
-            0 => array(
31
-                'page_route' => array('edit', 'create_new'),
32
-                'func'       => 'primary_questions',
33
-                'label'      => esc_html__('Questions for Primary Registrant', 'event_espresso'),
34
-                'priority'   => 'default',
35
-                'context'    => 'side',
36
-            ),
37
-        );
38
-
39
-        // hook into the handler for saving question groups
40
-        add_filter(
41
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
42
-            array($this, 'modify_callbacks'),
43
-            10
44
-        );
45
-
46
-        // hook into revision restores (we're hooking into the global action because EE_Admin_Hooks classes are already
47
-        // restricted by page)
48
-        add_action('AHEE_EE_Admin_Page_CPT__restore_revision', array($this, 'restore_revision'), 10, 2);
49
-    }
50
-
51
-
52
-    /**
53
-     * Callback for FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks hook
54
-     *
55
-     * @param $callbacks
56
-     * @return array
57
-     */
58
-    public function modify_callbacks($callbacks)
59
-    {
60
-        // now let's add the question group callback
61
-        $callbacks[] = array($this, 'primary_question_group_update');
62
-        return $callbacks;
63
-    }
64
-
65
-
66
-    /**
67
-     * Hooked into revision restores.
68
-     *
69
-     * @param $post_id
70
-     * @param $revision_id
71
-     * @return EE_Base_Class
72
-     * @throws EE_Error
73
-     * @throws InvalidArgumentException
74
-     * @throws ReflectionException
75
-     * @throws InvalidDataTypeException
76
-     * @throws InvalidInterfaceException
77
-     */
78
-    public function restore_revision($post_id, $revision_id)
79
-    {
80
-        $EVT_MDL = EE_Registry::instance()->load_model('Event');
81
-        $post_evt = $EVT_MDL->get_one_by_ID($post_id);
82
-        // restore revision for primary questions
83
-        $post_evt->restore_revision(
84
-            $revision_id,
85
-            ['Question_Group'],
86
-            ['Question_Group' => ['Event_Question_Group.EQG_primary' => true]]
87
-        );
88
-        return $post_evt;
89
-    }
90
-
91
-
92
-    /**
93
-     * Content of metabox.
94
-     *
95
-     * @param $post_id
96
-     * @param $post
97
-     * @throws EE_Error
98
-     * @throws InvalidArgumentException
99
-     * @throws InvalidDataTypeException
100
-     * @throws InvalidInterfaceException
101
-     */
102
-    public function primary_questions($post_id, $post)
103
-    {
104
-        $this->_event = $this->_adminpage_obj->get_event_object();
105
-        $event_id = $this->_event->ID();
106
-        ?>
19
+	/**
20
+	 * @var EE_Event|null
21
+	 */
22
+	protected $_event;
23
+
24
+
25
+	protected function _set_hooks_properties()
26
+	{
27
+
28
+		$this->_name = 'registration_form';
29
+		$this->_metaboxes = array(
30
+			0 => array(
31
+				'page_route' => array('edit', 'create_new'),
32
+				'func'       => 'primary_questions',
33
+				'label'      => esc_html__('Questions for Primary Registrant', 'event_espresso'),
34
+				'priority'   => 'default',
35
+				'context'    => 'side',
36
+			),
37
+		);
38
+
39
+		// hook into the handler for saving question groups
40
+		add_filter(
41
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
42
+			array($this, 'modify_callbacks'),
43
+			10
44
+		);
45
+
46
+		// hook into revision restores (we're hooking into the global action because EE_Admin_Hooks classes are already
47
+		// restricted by page)
48
+		add_action('AHEE_EE_Admin_Page_CPT__restore_revision', array($this, 'restore_revision'), 10, 2);
49
+	}
50
+
51
+
52
+	/**
53
+	 * Callback for FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks hook
54
+	 *
55
+	 * @param $callbacks
56
+	 * @return array
57
+	 */
58
+	public function modify_callbacks($callbacks)
59
+	{
60
+		// now let's add the question group callback
61
+		$callbacks[] = array($this, 'primary_question_group_update');
62
+		return $callbacks;
63
+	}
64
+
65
+
66
+	/**
67
+	 * Hooked into revision restores.
68
+	 *
69
+	 * @param $post_id
70
+	 * @param $revision_id
71
+	 * @return EE_Base_Class
72
+	 * @throws EE_Error
73
+	 * @throws InvalidArgumentException
74
+	 * @throws ReflectionException
75
+	 * @throws InvalidDataTypeException
76
+	 * @throws InvalidInterfaceException
77
+	 */
78
+	public function restore_revision($post_id, $revision_id)
79
+	{
80
+		$EVT_MDL = EE_Registry::instance()->load_model('Event');
81
+		$post_evt = $EVT_MDL->get_one_by_ID($post_id);
82
+		// restore revision for primary questions
83
+		$post_evt->restore_revision(
84
+			$revision_id,
85
+			['Question_Group'],
86
+			['Question_Group' => ['Event_Question_Group.EQG_primary' => true]]
87
+		);
88
+		return $post_evt;
89
+	}
90
+
91
+
92
+	/**
93
+	 * Content of metabox.
94
+	 *
95
+	 * @param $post_id
96
+	 * @param $post
97
+	 * @throws EE_Error
98
+	 * @throws InvalidArgumentException
99
+	 * @throws InvalidDataTypeException
100
+	 * @throws InvalidInterfaceException
101
+	 */
102
+	public function primary_questions($post_id, $post)
103
+	{
104
+		$this->_event = $this->_adminpage_obj->get_event_object();
105
+		$event_id = $this->_event->ID();
106
+		?>
107 107
         <div class="inside">
108 108
             <p><strong>
109 109
                     <?php _e('Question Groups', 'event_espresso'); ?>
110 110
                 </strong><br/>
111 111
                 <?php
112
-                printf(
113
-                    esc_html__(
114
-                        'Add a pre-populated %1$sgroup of questions%2$s to your event. The personal information group is required for all events',
115
-                        'event_espresso'
116
-                    ),
117
-                    '<a href="admin.php?page=espresso_registration_form" target="_blank">',
118
-                    '</a>'
119
-                )
120
-                ?>
112
+				printf(
113
+					esc_html__(
114
+						'Add a pre-populated %1$sgroup of questions%2$s to your event. The personal information group is required for all events',
115
+						'event_espresso'
116
+					),
117
+					'<a href="admin.php?page=espresso_registration_form" target="_blank">',
118
+					'</a>'
119
+				)
120
+				?>
121 121
             </p>
122 122
             <?php
123 123
 
124
-            $qsg_where['QSG_deleted'] = false;
125
-            $query_params = apply_filters(
126
-                'FHEE__espresso_events_Registration_Form_Hooks__primary_questions__question_group_query_parameters',
127
-                array($qsg_where, 'order_by' => array('QSG_order' => 'ASC'))
128
-            );
129
-            $QSGs = EEM_Question_Group::instance()->get_all($query_params);
130
-            $EQGs = ! empty($event_id)
131
-                ? $this->_event->get_many_related(
132
-                    'Question_Group',
133
-                    [['Event_Question_Group.EQG_primary' => true]]
134
-                )
135
-                : array();
136
-            $EQGids = array_keys($EQGs);
137
-
138
-            if (! empty($QSGs)) {
139
-                $html = count($QSGs) > 10 ? '<div style="height:250px;overflow:auto;">' : '';
140
-                foreach ($QSGs as $QSG) {
141
-                    $checked = in_array($QSG->ID(), $EQGids, true)
142
-                               || $QSG->get('QSG_system') === 1
143
-                        ? ' checked="checked"'
144
-                        : '';
145
-                    $visibility = $QSG->get('QSG_system') === 1
146
-                        ? ' style="visibility:hidden"'
147
-                        : '';
148
-                    $edit_query_args = $this->_adminpage_obj->is_caf() ? array(
149
-                        'action' => 'edit_question_group',
150
-                        'QSG_ID' => $QSG->ID(),
151
-                    ) : array('action' => 'question_groups');
152
-                    $edit_link = EE_Admin_Page::add_query_args_and_nonce(
153
-                        $edit_query_args,
154
-                        EE_FORMS_ADMIN_URL
155
-                    );
156
-
157
-                    $html .= '
124
+			$qsg_where['QSG_deleted'] = false;
125
+			$query_params = apply_filters(
126
+				'FHEE__espresso_events_Registration_Form_Hooks__primary_questions__question_group_query_parameters',
127
+				array($qsg_where, 'order_by' => array('QSG_order' => 'ASC'))
128
+			);
129
+			$QSGs = EEM_Question_Group::instance()->get_all($query_params);
130
+			$EQGs = ! empty($event_id)
131
+				? $this->_event->get_many_related(
132
+					'Question_Group',
133
+					[['Event_Question_Group.EQG_primary' => true]]
134
+				)
135
+				: array();
136
+			$EQGids = array_keys($EQGs);
137
+
138
+			if (! empty($QSGs)) {
139
+				$html = count($QSGs) > 10 ? '<div style="height:250px;overflow:auto;">' : '';
140
+				foreach ($QSGs as $QSG) {
141
+					$checked = in_array($QSG->ID(), $EQGids, true)
142
+							   || $QSG->get('QSG_system') === 1
143
+						? ' checked="checked"'
144
+						: '';
145
+					$visibility = $QSG->get('QSG_system') === 1
146
+						? ' style="visibility:hidden"'
147
+						: '';
148
+					$edit_query_args = $this->_adminpage_obj->is_caf() ? array(
149
+						'action' => 'edit_question_group',
150
+						'QSG_ID' => $QSG->ID(),
151
+					) : array('action' => 'question_groups');
152
+					$edit_link = EE_Admin_Page::add_query_args_and_nonce(
153
+						$edit_query_args,
154
+						EE_FORMS_ADMIN_URL
155
+					);
156
+
157
+					$html .= '
158 158
 					<p id="event-question-group-' . $QSG->ID() . '">
159 159
 						<input value="' . $QSG->ID() . '" type="checkbox"'
160
-                             . $visibility
161
-                             . ' name="question_groups[' . $QSG->ID() . ']"' . $checked . ' />
160
+							 . $visibility
161
+							 . ' name="question_groups[' . $QSG->ID() . ']"' . $checked . ' />
162 162
 						<a href="' . $edit_link . '" title="'
163
-                             . sprintf(
164
-                                 esc_attr__('Edit %s Group', 'event_espresso'),
165
-                                 $QSG->get('QSG_name')
166
-                             )
167
-                             . '" target="_blank">' . $QSG->get('QSG_name') . '</a>
163
+							 . sprintf(
164
+								 esc_attr__('Edit %s Group', 'event_espresso'),
165
+								 $QSG->get('QSG_name')
166
+							 )
167
+							 . '" target="_blank">' . $QSG->get('QSG_name') . '</a>
168 168
 					</p>';
169
-                }
170
-                $html .= count($QSGs) > 10 ? '</div>' : '';
171
-
172
-                echo $html;
173
-            } else {
174
-                esc_html_e(
175
-                    'There seems to be a problem with your questions. Please contact [email protected]',
176
-                    'event_espresso'
177
-                );
178
-            }
179
-            do_action('AHEE_event_editor_questions_notice');
180
-            ?>
169
+				}
170
+				$html .= count($QSGs) > 10 ? '</div>' : '';
171
+
172
+				echo $html;
173
+			} else {
174
+				esc_html_e(
175
+					'There seems to be a problem with your questions. Please contact [email protected]',
176
+					'event_espresso'
177
+				);
178
+			}
179
+			do_action('AHEE_event_editor_questions_notice');
180
+			?>
181 181
         </div>
182 182
         <?php
183
-    }
184
-
185
-
186
-    /**
187
-     * @param EE_Event $evtobj
188
-     * @param array    $data
189
-     * @return bool
190
-     * @throws EE_Error
191
-     */
192
-    public function primary_question_group_update($evtobj, $data)
193
-    {
194
-        $question_groups = ! empty($data['question_groups']) ? (array) $data['question_groups'] : array();
195
-        $added_qgs = array_keys($question_groups);
196
-        $success = array();
197
-
198
-        // let's get all current question groups associated with this event.
199
-        $current_qgs = $evtobj->get_many_related(
200
-            'Question_Group',
201
-            [['Event_Question_Group.EQG_primary' => true]]
202
-        );
203
-        $current_qgs = array_keys($current_qgs); // we just want the ids
204
-
205
-        // now let's get the groups selected in the editor and update (IF we have data)
206
-        if (! empty($question_groups)) {
207
-            foreach ($question_groups as $id => $val) {
208
-                // add to event
209
-                if ($val) {
210
-                    $qg = $evtobj->_add_relation_to($id, 'Question_Group', ['EQG_primary' => true]);
211
-                }
212
-                $success[] = ! empty($qg) ? 1 : 0;
213
-            }
214
-        }
215
-
216
-        // wait a minute... are there question groups missing in the saved groups that ARE with the current event?
217
-        $removed_qgs = array_diff($current_qgs, $added_qgs);
218
-
219
-        foreach ($removed_qgs as $qgid) {
220
-            $qg = $evtobj->_remove_relation_to($qgid, 'Question_Group', ['EQG_primary' => true]);
221
-            $success[] = ! empty($qg) ? 1 : 0;
222
-        }
223
-        return in_array(0, $success, true) ? false : true;
224
-    }
183
+	}
184
+
185
+
186
+	/**
187
+	 * @param EE_Event $evtobj
188
+	 * @param array    $data
189
+	 * @return bool
190
+	 * @throws EE_Error
191
+	 */
192
+	public function primary_question_group_update($evtobj, $data)
193
+	{
194
+		$question_groups = ! empty($data['question_groups']) ? (array) $data['question_groups'] : array();
195
+		$added_qgs = array_keys($question_groups);
196
+		$success = array();
197
+
198
+		// let's get all current question groups associated with this event.
199
+		$current_qgs = $evtobj->get_many_related(
200
+			'Question_Group',
201
+			[['Event_Question_Group.EQG_primary' => true]]
202
+		);
203
+		$current_qgs = array_keys($current_qgs); // we just want the ids
204
+
205
+		// now let's get the groups selected in the editor and update (IF we have data)
206
+		if (! empty($question_groups)) {
207
+			foreach ($question_groups as $id => $val) {
208
+				// add to event
209
+				if ($val) {
210
+					$qg = $evtobj->_add_relation_to($id, 'Question_Group', ['EQG_primary' => true]);
211
+				}
212
+				$success[] = ! empty($qg) ? 1 : 0;
213
+			}
214
+		}
215
+
216
+		// wait a minute... are there question groups missing in the saved groups that ARE with the current event?
217
+		$removed_qgs = array_diff($current_qgs, $added_qgs);
218
+
219
+		foreach ($removed_qgs as $qgid) {
220
+			$qg = $evtobj->_remove_relation_to($qgid, 'Question_Group', ['EQG_primary' => true]);
221
+			$success[] = ! empty($qg) ? 1 : 0;
222
+		}
223
+		return in_array(0, $success, true) ? false : true;
224
+	}
225 225
 }
Please login to merge, or discard this patch.
registrations/form_sections/EE_Registration_Custom_Questions_Form.form.php 2 patches
Indentation   +161 added lines, -161 removed lines patch added patch discarded remove patch
@@ -15,179 +15,179 @@
 block discarded – undo
15 15
  */
16 16
 class EE_Registration_Custom_Questions_Form extends EE_Form_Section_Proper
17 17
 {
18
-    /**
19
-     *
20
-     * @var EE_Registration
21
-     */
22
-    protected $_registration = null;
18
+	/**
19
+	 *
20
+	 * @var EE_Registration
21
+	 */
22
+	protected $_registration = null;
23 23
 
24
-    /**
25
-     *
26
-     * @param EE_Registration $reg
27
-     * @param array $options
28
-     */
29
-    public function __construct(EE_Registration $reg, $options = array())
30
-    {
31
-        $this->_registration = $reg;
32
-        if (! isset($options['layout_strategy'])) {
33
-            $options['layout_strategy'] = new EE_Admin_Two_Column_Layout();
34
-        }
35
-        if (! isset($options['html_id'])) {
36
-            $options['html_id'] = 'reg-admin-attendee-questions-frm';
37
-        }
38
-        $this->build_form_from_registration();
39
-        parent::__construct($options);
40
-    }
24
+	/**
25
+	 *
26
+	 * @param EE_Registration $reg
27
+	 * @param array $options
28
+	 */
29
+	public function __construct(EE_Registration $reg, $options = array())
30
+	{
31
+		$this->_registration = $reg;
32
+		if (! isset($options['layout_strategy'])) {
33
+			$options['layout_strategy'] = new EE_Admin_Two_Column_Layout();
34
+		}
35
+		if (! isset($options['html_id'])) {
36
+			$options['html_id'] = 'reg-admin-attendee-questions-frm';
37
+		}
38
+		$this->build_form_from_registration();
39
+		parent::__construct($options);
40
+	}
41 41
 
42 42
 
43
-    /**
44
-     * Gets the registration object this form is about
45
-     * @return EE_Registration
46
-     */
47
-    public function get_registration()
48
-    {
49
-        return $this->_registration;
50
-    }
43
+	/**
44
+	 * Gets the registration object this form is about
45
+	 * @return EE_Registration
46
+	 */
47
+	public function get_registration()
48
+	{
49
+		return $this->_registration;
50
+	}
51 51
 
52
-    /**
53
-     * @since $VID:$
54
-     * @throws EE_Error
55
-     * @throws InvalidArgumentException
56
-     * @throws ReflectionException
57
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
58
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
59
-     */
60
-    public function build_form_from_registration()
61
-    {
62
-        $reg = $this->get_registration();
63
-        if (! $reg instanceof EE_Registration) {
64
-            throw new EE_Error(__('We cannot build the registration custom questions form because there is no registration set on it yet', 'event_espresso'));
65
-        }
66
-        // we want to get all their question groups
67
-        $question_groups = EEM_Question_Group::instance()->get_all(
68
-            [
69
-                [
70
-                    'Event_Question_Group.EVT_ID' => $reg->event_ID(),
71
-                    'OR' => [
72
-                        'Question.QST_system*blank' =>  '',
73
-                        'Question.QST_system*null' => ['IS_NULL']
74
-                    ],
75
-                    'Event_Question_Group.'
76
-                    . EEM_Event_Question_Group::instance()->field_name_for_category(
77
-                        $reg->is_primary_registrant()
78
-                    ) => true
79
-                ],
80
-                'order_by' => ['QSG_order' => 'ASC']
81
-            ]
82
-        );
83
-        // get each question groups questions
84
-        foreach ($question_groups as $question_group) {
85
-            if ($question_group instanceof EE_Question_Group) {
86
-                $this->_subsections[ $question_group->ID() ] = $this->build_subform_from_question_group(
87
-                    $question_group,
88
-                    $reg
89
-                );
90
-            }
91
-        }
92
-    }
52
+	/**
53
+	 * @since $VID:$
54
+	 * @throws EE_Error
55
+	 * @throws InvalidArgumentException
56
+	 * @throws ReflectionException
57
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
58
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
59
+	 */
60
+	public function build_form_from_registration()
61
+	{
62
+		$reg = $this->get_registration();
63
+		if (! $reg instanceof EE_Registration) {
64
+			throw new EE_Error(__('We cannot build the registration custom questions form because there is no registration set on it yet', 'event_espresso'));
65
+		}
66
+		// we want to get all their question groups
67
+		$question_groups = EEM_Question_Group::instance()->get_all(
68
+			[
69
+				[
70
+					'Event_Question_Group.EVT_ID' => $reg->event_ID(),
71
+					'OR' => [
72
+						'Question.QST_system*blank' =>  '',
73
+						'Question.QST_system*null' => ['IS_NULL']
74
+					],
75
+					'Event_Question_Group.'
76
+					. EEM_Event_Question_Group::instance()->field_name_for_category(
77
+						$reg->is_primary_registrant()
78
+					) => true
79
+				],
80
+				'order_by' => ['QSG_order' => 'ASC']
81
+			]
82
+		);
83
+		// get each question groups questions
84
+		foreach ($question_groups as $question_group) {
85
+			if ($question_group instanceof EE_Question_Group) {
86
+				$this->_subsections[ $question_group->ID() ] = $this->build_subform_from_question_group(
87
+					$question_group,
88
+					$reg
89
+				);
90
+			}
91
+		}
92
+	}
93 93
 
94 94
 
95 95
 
96
-    /**
97
-     *
98
-     * @param EE_Question_Group $question_group
99
-     * @param EE_Registration   $registration
100
-     * @return \EE_Form_Section_Proper
101
-     * @throws \EE_Error
102
-     */
103
-    public function build_subform_from_question_group($question_group, $registration)
104
-    {
105
-        if (! $question_group instanceof EE_Question_Group ||
106
-            ! $registration instanceof EE_Registration) {
107
-            throw new EE_Error(__('A valid question group and registration must be passed to EE_Registration_Custom_Question_Form', 'event_espresso'));
108
-        }
109
-        $parts_of_subsection = array(
110
-            'title' => new EE_Form_Section_HTML(
111
-                EEH_HTML::h5(
112
-                    $question_group->name(),
113
-                    $question_group->identifier(),
114
-                    'espresso-question-group-title-h5 section-title'
115
-                )
116
-            )
117
-        );
118
-        $questions = $question_group->questions(
119
-            array(
120
-                array(
121
-                    'OR' => array(
122
-                        'QST_system*blank' => '',
123
-                        'QST_system*null' => array( 'IS_NULL' )
124
-                    )
125
-                )
126
-            )
127
-        );
128
-        foreach ($questions as $question) {
129
-            $parts_of_subsection[ $question->ID() ] = $question->generate_form_input($registration);
130
-        }
131
-        if (EE_Registry::instance()->CAP->current_user_can(
132
-            'ee_edit_registration',
133
-            'edit-reg-questions-mbox',
134
-            $this->_registration->ID()
135
-        )) {
136
-            $parts_of_subsection['edit_link'] = new EE_Form_Section_HTML(
137
-                '<tr><th/><td class="reg-admin-edit-attendee-question-td"><a class="reg-admin-edit-attendee-question-lnk" href="#" title="' . esc_attr__('click to edit question', 'event_espresso') . '">
96
+	/**
97
+	 *
98
+	 * @param EE_Question_Group $question_group
99
+	 * @param EE_Registration   $registration
100
+	 * @return \EE_Form_Section_Proper
101
+	 * @throws \EE_Error
102
+	 */
103
+	public function build_subform_from_question_group($question_group, $registration)
104
+	{
105
+		if (! $question_group instanceof EE_Question_Group ||
106
+			! $registration instanceof EE_Registration) {
107
+			throw new EE_Error(__('A valid question group and registration must be passed to EE_Registration_Custom_Question_Form', 'event_espresso'));
108
+		}
109
+		$parts_of_subsection = array(
110
+			'title' => new EE_Form_Section_HTML(
111
+				EEH_HTML::h5(
112
+					$question_group->name(),
113
+					$question_group->identifier(),
114
+					'espresso-question-group-title-h5 section-title'
115
+				)
116
+			)
117
+		);
118
+		$questions = $question_group->questions(
119
+			array(
120
+				array(
121
+					'OR' => array(
122
+						'QST_system*blank' => '',
123
+						'QST_system*null' => array( 'IS_NULL' )
124
+					)
125
+				)
126
+			)
127
+		);
128
+		foreach ($questions as $question) {
129
+			$parts_of_subsection[ $question->ID() ] = $question->generate_form_input($registration);
130
+		}
131
+		if (EE_Registry::instance()->CAP->current_user_can(
132
+			'ee_edit_registration',
133
+			'edit-reg-questions-mbox',
134
+			$this->_registration->ID()
135
+		)) {
136
+			$parts_of_subsection['edit_link'] = new EE_Form_Section_HTML(
137
+				'<tr><th/><td class="reg-admin-edit-attendee-question-td"><a class="reg-admin-edit-attendee-question-lnk" href="#" title="' . esc_attr__('click to edit question', 'event_espresso') . '">
138 138
 		  			<span class="reg-admin-edit-question-group-spn lt-grey-txt">' . __('edit the above question group', 'event_espresso') . '</span>
139 139
 		  			<div class="dashicons dashicons-edit"></div>
140 140
 		  		</a></td></tr>'
141
-            );
142
-        }
143
-        return new EE_Form_Section_Proper(
144
-            array(
145
-                'subsections' => $parts_of_subsection,
146
-                'html_class' => 'question-group-questions',
147
-            )
148
-        );
149
-    }
141
+			);
142
+		}
143
+		return new EE_Form_Section_Proper(
144
+			array(
145
+				'subsections' => $parts_of_subsection,
146
+				'html_class' => 'question-group-questions',
147
+			)
148
+		);
149
+	}
150 150
 
151
-    /**
152
-     * Overrides parent so if inputs were disabled, we leave those with their defaults
153
-     * from the answers in the DB
154
-     * @param array $req_data like $_POST
155
-     * @return void
156
-     */
157
-    protected function _normalize($req_data)
158
-    {
159
-        $this->_received_submission = true;
160
-        $this->_validation_errors = array();
161
-        foreach ($this->get_validatable_subsections() as $subsection) {
162
-            if ($subsection->form_data_present_in($req_data)) {
163
-                try {
164
-                    $subsection->_normalize($req_data);
165
-                } catch (EE_Validation_Error $e) {
166
-                    $subsection->add_validation_error($e);
167
-                }
168
-            }
169
-        }
170
-    }
151
+	/**
152
+	 * Overrides parent so if inputs were disabled, we leave those with their defaults
153
+	 * from the answers in the DB
154
+	 * @param array $req_data like $_POST
155
+	 * @return void
156
+	 */
157
+	protected function _normalize($req_data)
158
+	{
159
+		$this->_received_submission = true;
160
+		$this->_validation_errors = array();
161
+		foreach ($this->get_validatable_subsections() as $subsection) {
162
+			if ($subsection->form_data_present_in($req_data)) {
163
+				try {
164
+					$subsection->_normalize($req_data);
165
+				} catch (EE_Validation_Error $e) {
166
+					$subsection->add_validation_error($e);
167
+				}
168
+			}
169
+		}
170
+	}
171 171
 
172 172
 
173 173
 
174
-    /**
175
-     * Performs validation on this form section and its subsections. For each subsection,
176
-     * calls _validate_{subsection_name} on THIS form (if the function exists) and passes it the subsection, then calls _validate on that subsection.
177
-     * If you need to perform validation on the form as a whole (considering multiple) you would be best to override this _validate method,
178
-     * calling parent::_validate() first.
179
-     */
180
-    protected function _validate()
181
-    {
182
-        foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
183
-            if ($subsection->form_data_present_in(array_merge($_GET, $_POST))) {
184
-                if (method_exists($this, '_validate_'.$subsection_name)) {
185
-                    call_user_func_array(array($this,'_validate_'.$subsection_name), array($subsection));
186
-                }
187
-                $subsection->_validate();
188
-            } elseif ($subsection instanceof EE_Form_Section_Proper) {
189
-                $subsection->_received_submission = true;
190
-            }
191
-        }
192
-    }
174
+	/**
175
+	 * Performs validation on this form section and its subsections. For each subsection,
176
+	 * calls _validate_{subsection_name} on THIS form (if the function exists) and passes it the subsection, then calls _validate on that subsection.
177
+	 * If you need to perform validation on the form as a whole (considering multiple) you would be best to override this _validate method,
178
+	 * calling parent::_validate() first.
179
+	 */
180
+	protected function _validate()
181
+	{
182
+		foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
183
+			if ($subsection->form_data_present_in(array_merge($_GET, $_POST))) {
184
+				if (method_exists($this, '_validate_'.$subsection_name)) {
185
+					call_user_func_array(array($this,'_validate_'.$subsection_name), array($subsection));
186
+				}
187
+				$subsection->_validate();
188
+			} elseif ($subsection instanceof EE_Form_Section_Proper) {
189
+				$subsection->_received_submission = true;
190
+			}
191
+		}
192
+	}
193 193
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -29,10 +29,10 @@  discard block
 block discarded – undo
29 29
     public function __construct(EE_Registration $reg, $options = array())
30 30
     {
31 31
         $this->_registration = $reg;
32
-        if (! isset($options['layout_strategy'])) {
32
+        if ( ! isset($options['layout_strategy'])) {
33 33
             $options['layout_strategy'] = new EE_Admin_Two_Column_Layout();
34 34
         }
35
-        if (! isset($options['html_id'])) {
35
+        if ( ! isset($options['html_id'])) {
36 36
             $options['html_id'] = 'reg-admin-attendee-questions-frm';
37 37
         }
38 38
         $this->build_form_from_registration();
@@ -60,7 +60,7 @@  discard block
 block discarded – undo
60 60
     public function build_form_from_registration()
61 61
     {
62 62
         $reg = $this->get_registration();
63
-        if (! $reg instanceof EE_Registration) {
63
+        if ( ! $reg instanceof EE_Registration) {
64 64
             throw new EE_Error(__('We cannot build the registration custom questions form because there is no registration set on it yet', 'event_espresso'));
65 65
         }
66 66
         // we want to get all their question groups
@@ -83,7 +83,7 @@  discard block
 block discarded – undo
83 83
         // get each question groups questions
84 84
         foreach ($question_groups as $question_group) {
85 85
             if ($question_group instanceof EE_Question_Group) {
86
-                $this->_subsections[ $question_group->ID() ] = $this->build_subform_from_question_group(
86
+                $this->_subsections[$question_group->ID()] = $this->build_subform_from_question_group(
87 87
                     $question_group,
88 88
                     $reg
89 89
                 );
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
      */
103 103
     public function build_subform_from_question_group($question_group, $registration)
104 104
     {
105
-        if (! $question_group instanceof EE_Question_Group ||
105
+        if ( ! $question_group instanceof EE_Question_Group ||
106 106
             ! $registration instanceof EE_Registration) {
107 107
             throw new EE_Error(__('A valid question group and registration must be passed to EE_Registration_Custom_Question_Form', 'event_espresso'));
108 108
         }
@@ -120,13 +120,13 @@  discard block
 block discarded – undo
120 120
                 array(
121 121
                     'OR' => array(
122 122
                         'QST_system*blank' => '',
123
-                        'QST_system*null' => array( 'IS_NULL' )
123
+                        'QST_system*null' => array('IS_NULL')
124 124
                     )
125 125
                 )
126 126
             )
127 127
         );
128 128
         foreach ($questions as $question) {
129
-            $parts_of_subsection[ $question->ID() ] = $question->generate_form_input($registration);
129
+            $parts_of_subsection[$question->ID()] = $question->generate_form_input($registration);
130 130
         }
131 131
         if (EE_Registry::instance()->CAP->current_user_can(
132 132
             'ee_edit_registration',
@@ -134,8 +134,8 @@  discard block
 block discarded – undo
134 134
             $this->_registration->ID()
135 135
         )) {
136 136
             $parts_of_subsection['edit_link'] = new EE_Form_Section_HTML(
137
-                '<tr><th/><td class="reg-admin-edit-attendee-question-td"><a class="reg-admin-edit-attendee-question-lnk" href="#" title="' . esc_attr__('click to edit question', 'event_espresso') . '">
138
-		  			<span class="reg-admin-edit-question-group-spn lt-grey-txt">' . __('edit the above question group', 'event_espresso') . '</span>
137
+                '<tr><th/><td class="reg-admin-edit-attendee-question-td"><a class="reg-admin-edit-attendee-question-lnk" href="#" title="'.esc_attr__('click to edit question', 'event_espresso').'">
138
+		  			<span class="reg-admin-edit-question-group-spn lt-grey-txt">' . __('edit the above question group', 'event_espresso').'</span>
139 139
 		  			<div class="dashicons dashicons-edit"></div>
140 140
 		  		</a></td></tr>'
141 141
             );
@@ -182,7 +182,7 @@  discard block
 block discarded – undo
182 182
         foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
183 183
             if ($subsection->form_data_present_in(array_merge($_GET, $_POST))) {
184 184
                 if (method_exists($this, '_validate_'.$subsection_name)) {
185
-                    call_user_func_array(array($this,'_validate_'.$subsection_name), array($subsection));
185
+                    call_user_func_array(array($this, '_validate_'.$subsection_name), array($subsection));
186 186
                 }
187 187
                 $subsection->_validate();
188 188
             } elseif ($subsection instanceof EE_Form_Section_Proper) {
Please login to merge, or discard this patch.
attendee_information/EE_SPCO_Reg_Step_Attendee_Information.class.php 1 patch
Indentation   +1420 added lines, -1420 removed lines patch added patch discarded remove patch
@@ -18,1427 +18,1427 @@
 block discarded – undo
18 18
 class EE_SPCO_Reg_Step_Attendee_Information extends EE_SPCO_Reg_Step
19 19
 {
20 20
 
21
-    /**
22
-     * @type bool $_print_copy_info
23
-     */
24
-    private $_print_copy_info = false;
25
-
26
-    /**
27
-     * @type array $_attendee_data
28
-     */
29
-    private $_attendee_data = array();
30
-
31
-    /**
32
-     * @type array $_required_questions
33
-     */
34
-    private $_required_questions = array();
35
-
36
-    /**
37
-     * @type array $_registration_answers
38
-     */
39
-    private $_registration_answers = array();
40
-
41
-
42
-    /**
43
-     *    class constructor
44
-     *
45
-     * @access    public
46
-     * @param    EE_Checkout $checkout
47
-     */
48
-    public function __construct(EE_Checkout $checkout)
49
-    {
50
-        $this->_slug = 'attendee_information';
51
-        $this->_name = esc_html__('Attendee Information', 'event_espresso');
52
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . DS . 'attendee_info_main.template.php';
53
-        $this->checkout = $checkout;
54
-        $this->_reset_success_message();
55
-        $this->set_instructions(
56
-            esc_html__('Please answer the following registration questions before proceeding.', 'event_espresso')
57
-        );
58
-    }
59
-
60
-
61
-    public function translate_js_strings()
62
-    {
63
-        EE_Registry::$i18n_js_strings['required_field'] = esc_html__(
64
-            ' is a required question.',
65
-            'event_espresso'
66
-        );
67
-        EE_Registry::$i18n_js_strings['required_multi_field'] = esc_html__(
68
-            ' is a required question. Please enter a value for at least one of the options.',
69
-            'event_espresso'
70
-        );
71
-        EE_Registry::$i18n_js_strings['answer_required_questions'] = esc_html__(
72
-            'Please answer all required questions correctly before proceeding.',
73
-            'event_espresso'
74
-        );
75
-        EE_Registry::$i18n_js_strings['attendee_info_copied'] = sprintf(
76
-            esc_html_x(
77
-                'The attendee information was successfully copied.%sPlease ensure the rest of the registration form is completed before proceeding.',
78
-                'The attendee information was successfully copied.(line break)Please ensure the rest of the registration form is completed before proceeding.',
79
-                'event_espresso'
80
-            ),
81
-            '<br/>'
82
-        );
83
-        EE_Registry::$i18n_js_strings['attendee_info_copy_error'] = esc_html__(
84
-            'An unknown error occurred on the server while attempting to copy the attendee information. Please refresh the page and try again.',
85
-            'event_espresso'
86
-        );
87
-        EE_Registry::$i18n_js_strings['enter_valid_email'] = esc_html__(
88
-            'You must enter a valid email address.',
89
-            'event_espresso'
90
-        );
91
-        EE_Registry::$i18n_js_strings['valid_email_and_questions'] = esc_html__(
92
-            'You must enter a valid email address and answer all other required questions before you can proceed.',
93
-            'event_espresso'
94
-        );
95
-    }
96
-
97
-
98
-    public function enqueue_styles_and_scripts()
99
-    {
100
-    }
101
-
102
-
103
-    /**
104
-     * @return boolean
105
-     */
106
-    public function initialize_reg_step()
107
-    {
108
-        return true;
109
-    }
110
-
111
-
112
-    /**
113
-     * @return EE_Form_Section_Proper
114
-     * @throws DomainException
115
-     * @throws EE_Error
116
-     * @throws InvalidArgumentException
117
-     * @throws ReflectionException
118
-     * @throws EntityNotFoundException
119
-     * @throws InvalidDataTypeException
120
-     * @throws InvalidInterfaceException
121
-     */
122
-    public function generate_reg_form()
123
-    {
124
-        $this->_print_copy_info = false;
125
-        $primary_registrant = null;
126
-        // autoload Line_Item_Display classes
127
-        EEH_Autoloader::register_line_item_display_autoloaders();
128
-        $Line_Item_Display = new EE_Line_Item_Display();
129
-        // calculate taxes
130
-        $Line_Item_Display->display_line_item(
131
-            $this->checkout->cart->get_grand_total(),
132
-            array('set_tax_rate' => true)
133
-        );
134
-        /** @var $subsections EE_Form_Section_Proper[] */
135
-        $extra_inputs_section = $this->reg_step_hidden_inputs();
136
-        $subsections = array(
137
-            'default_hidden_inputs' => $extra_inputs_section,
138
-        );
139
-
140
-        /**
141
-         * @var $reg_config EE_Registration_Config
142
-         */
143
-        $reg_config = LoaderFactory::getLoader()->getShared('EE_Registration_Config');
144
-        // if this isn't a revisit, and they have the privacy consent box enalbed, add it
145
-        if (! $this->checkout->revisit && $reg_config->isConsentCheckboxEnabled()) {
146
-            $extra_inputs_section->add_subsections(
147
-                array(
148
-                    'consent_box' => new EE_Form_Section_Proper(
149
-                        array(
150
-                            'layout_strategy' =>
151
-                                new EE_Template_Layout(
152
-                                    array(
153
-                                        'input_template_file' => SPCO_REG_STEPS_PATH . $this->_slug . DS . 'privacy_consent.template.php',
154
-                                    )
155
-                                ),
156
-                            'subsections'     => array(
157
-                                'consent' => new EE_Checkbox_Multi_Input(
158
-                                    array(
159
-                                        'consent' => $reg_config->getConsentCheckboxLabelText(),
160
-                                    ),
161
-                                    array(
162
-                                        'required'                          => true,
163
-                                        'required_validation_error_message' => esc_html__(
164
-                                            'You must consent to these terms in order to register.',
165
-                                            'event_espresso'
166
-                                        ),
167
-                                        'html_label_text'                   => '',
168
-                                    )
169
-                                ),
170
-                            ),
171
-                        )
172
-                    ),
173
-                ),
174
-                null,
175
-                false
176
-            );
177
-        }
178
-        $template_args = array(
179
-            'revisit'       => $this->checkout->revisit,
180
-            'registrations' => array(),
181
-            'ticket_count'  => array(),
182
-        );
183
-        // grab the saved registrations from the transaction
184
-        $registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
185
-        if ($registrations) {
186
-            foreach ($registrations as $registration) {
187
-                // can this registration be processed during this visit ?
188
-                if ($registration instanceof EE_Registration
189
-                    && $this->checkout->visit_allows_processing_of_this_registration($registration)
190
-                ) {
191
-                    $subsections[ $registration->reg_url_link() ] = $this->_registrations_reg_form($registration);
192
-                    if (! $this->checkout->admin_request) {
193
-                        $template_args['registrations'][ $registration->reg_url_link() ] = $registration;
194
-                        $template_args['ticket_count'][ $registration->ticket()->ID() ] = isset(
195
-                            $template_args['ticket_count'][ $registration->ticket()->ID() ]
196
-                        )
197
-                            ? $template_args['ticket_count'][ $registration->ticket()->ID() ] + 1
198
-                            : 1;
199
-                        $ticket_line_item = EEH_Line_Item::get_line_items_by_object_type_and_IDs(
200
-                            $this->checkout->cart->get_grand_total(),
201
-                            'Ticket',
202
-                            array($registration->ticket()->ID())
203
-                        );
204
-                        $ticket_line_item = is_array($ticket_line_item)
205
-                            ? reset($ticket_line_item)
206
-                            : $ticket_line_item;
207
-                        $template_args['ticket_line_item'][ $registration->ticket()->ID() ] =
208
-                            $Line_Item_Display->display_line_item($ticket_line_item);
209
-                    }
210
-                    if ($registration->is_primary_registrant()) {
211
-                        $primary_registrant = $registration->reg_url_link();
212
-                    }
213
-                }
214
-            }
215
-            // print_copy_info ?
216
-            if ($primary_registrant && ! $this->checkout->admin_request && count($registrations) > 1) {
217
-                // TODO: add admin option for toggling copy attendee info,
218
-                // then use that value to change $this->_print_copy_info
219
-                $copy_options['spco_copy_attendee_chk'] = $this->_print_copy_info
220
-                    ? $this->_copy_attendee_info_form()
221
-                    : $this->_auto_copy_attendee_info();
222
-                // generate hidden input
223
-                if (isset($subsections[ $primary_registrant ])
224
-                    && $subsections[ $primary_registrant ] instanceof EE_Form_Section_Proper
225
-                ) {
226
-                    $subsections[ $primary_registrant ]->add_subsections(
227
-                        $copy_options,
228
-                        'primary_registrant',
229
-                        false
230
-                    );
231
-                }
232
-            }
233
-        }
234
-        return new EE_Form_Section_Proper(
235
-            array(
236
-                'name'            => $this->reg_form_name(),
237
-                'html_id'         => $this->reg_form_name(),
238
-                'subsections'     => $subsections,
239
-                'layout_strategy' => $this->checkout->admin_request
240
-                    ?
241
-                    new EE_Div_Per_Section_Layout()
242
-                    :
243
-                    new EE_Template_Layout(
244
-                        array(
245
-                            'layout_template_file' => $this->_template, // layout_template
246
-                            'template_args'        => $template_args,
247
-                        )
248
-                    ),
249
-            )
250
-        );
251
-    }
252
-
253
-
254
-    /**
255
-     * @param EE_Registration $registration
256
-     * @return EE_Form_Section_Base
257
-     * @throws EE_Error
258
-     * @throws InvalidArgumentException
259
-     * @throws EntityNotFoundException
260
-     * @throws InvalidDataTypeException
261
-     * @throws InvalidInterfaceException
262
-     * @throws ReflectionException
263
-     */
264
-    private function _registrations_reg_form(EE_Registration $registration)
265
-    {
266
-        static $attendee_nmbr = 1;
267
-        $form_args = array();
268
-        // verify that registration has valid event
269
-        if ($registration->event() instanceof EE_Event) {
270
-            $field_name = 'Event_Question_Group.'
271
-                . EEM_Event_Question_Group::instance()->field_name_for_category(
272
-                    $registration->is_primary_registrant()
273
-                );
274
-            $question_groups = $registration->event()->question_groups(
275
-                apply_filters(
276
-                    'FHEE__EE_SPCO_Reg_Step_Attendee_Information___registrations_reg_form__question_groups_query_parameters',
277
-                    [
278
-                        [
279
-                            'Event.EVT_ID'                     => $registration->event()->ID(),
280
-                            $field_name => true,
281
-                        ],
282
-                        'order_by' => ['QSG_order' => 'ASC'],
283
-                    ],
284
-                    $registration,
285
-                    $this
286
-                )
287
-            );
288
-            if ($question_groups) {
289
-                // array of params to pass to parent constructor
290
-                $form_args = array(
291
-                    'html_id'         => 'ee-registration-' . $registration->reg_url_link(),
292
-                    'html_class'      => 'ee-reg-form-attendee-dv',
293
-                    'html_style'      => $this->checkout->admin_request
294
-                        ? 'padding:0em 2em 1em; margin:3em 0 0; border:1px solid #ddd;'
295
-                        : '',
296
-                    'subsections'     => array(),
297
-                    'layout_strategy' => new EE_Fieldset_Section_Layout(
298
-                        array(
299
-                            'legend_class' => 'spco-attendee-lgnd smaller-text lt-grey-text',
300
-                            'legend_text'  => sprintf(
301
-                                esc_html_x(
302
-                                    'Attendee %d',
303
-                                    'Attendee 123',
304
-                                    'event_espresso'
305
-                                ),
306
-                                $attendee_nmbr
307
-                            ),
308
-                        )
309
-                    ),
310
-                );
311
-                foreach ($question_groups as $question_group) {
312
-                    if ($question_group instanceof EE_Question_Group) {
313
-                        $form_args['subsections'][ $question_group->identifier() ] = $this->_question_group_reg_form(
314
-                            $registration,
315
-                            $question_group
316
-                        );
317
-                    }
318
-                }
319
-                // add hidden input
320
-                $form_args['subsections']['additional_attendee_reg_info'] = $this->_additional_attendee_reg_info_input(
321
-                    $registration
322
-                );
323
-                // if we have question groups for additional attendees, then display the copy options
324
-                $this->_print_copy_info = $attendee_nmbr > 1 ? true : $this->_print_copy_info;
325
-                if ($registration->is_primary_registrant()) {
326
-                    // generate hidden input
327
-                    $form_args['subsections']['primary_registrant'] = $this->_additional_primary_registrant_inputs(
328
-                        $registration
329
-                    );
330
-                }
331
-            }
332
-        }
333
-        $attendee_nmbr++;
334
-        return ! empty($form_args)
335
-            ? new EE_Form_Section_Proper($form_args)
336
-            : new EE_Form_Section_HTML();
337
-    }
338
-
339
-
340
-    /**
341
-     * @param EE_Registration $registration
342
-     * @param bool            $additional_attendee_reg_info
343
-     * @return EE_Form_Input_Base
344
-     * @throws EE_Error
345
-     */
346
-    private function _additional_attendee_reg_info_input(
347
-        EE_Registration $registration,
348
-        $additional_attendee_reg_info = true
349
-    ) {
350
-        // generate hidden input
351
-        return new EE_Hidden_Input(
352
-            array(
353
-                'html_id' => 'additional-attendee-reg-info-' . $registration->reg_url_link(),
354
-                'default' => $additional_attendee_reg_info,
355
-            )
356
-        );
357
-    }
358
-
359
-
360
-    /**
361
-     * @param EE_Registration   $registration
362
-     * @param EE_Question_Group $question_group
363
-     * @return EE_Form_Section_Proper
364
-     * @throws EE_Error
365
-     * @throws InvalidArgumentException
366
-     * @throws InvalidDataTypeException
367
-     * @throws InvalidInterfaceException
368
-     * @throws ReflectionException
369
-     */
370
-    private function _question_group_reg_form(EE_Registration $registration, EE_Question_Group $question_group)
371
-    {
372
-        // array of params to pass to parent constructor
373
-        $form_args = array(
374
-            'html_id'         => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-' . $registration->ID(),
375
-            'html_class'      => $this->checkout->admin_request
376
-                ? 'form-table ee-reg-form-qstn-grp-dv'
377
-                : 'ee-reg-form-qstn-grp-dv',
378
-            'html_label_id'   => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-'
379
-                                 . $registration->ID() . '-lbl',
380
-            'subsections'     => array(
381
-                'reg_form_qstn_grp_hdr' => $this->_question_group_header($question_group),
382
-            ),
383
-            'layout_strategy' => $this->checkout->admin_request
384
-                ? new EE_Admin_Two_Column_Layout()
385
-                : new EE_Div_Per_Section_Layout(),
386
-        );
387
-        // where params
388
-        $query_params = array('QST_deleted' => 0);
389
-        // don't load admin only questions on the frontend
390
-        if (! $this->checkout->admin_request) {
391
-            $query_params['QST_admin_only'] = array('!=', true);
392
-        }
393
-        $questions = $question_group->get_many_related(
394
-            'Question',
395
-            apply_filters(
396
-                'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__related_questions_query_params',
397
-                array(
398
-                    $query_params,
399
-                    'order_by' => array(
400
-                        'Question_Group_Question.QGQ_order' => 'ASC',
401
-                    ),
402
-                ),
403
-                $question_group,
404
-                $registration,
405
-                $this
406
-            )
407
-        );
408
-        // filter for additional content before questions
409
-        $form_args['subsections']['reg_form_questions_before'] = new EE_Form_Section_HTML(
410
-            apply_filters(
411
-                'FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions',
412
-                '',
413
-                $registration,
414
-                $question_group,
415
-                $this
416
-            )
417
-        );
418
-        // loop thru questions
419
-        foreach ($questions as $question) {
420
-            if ($question instanceof EE_Question) {
421
-                $identifier = $question->is_system_question()
422
-                    ? $question->system_ID()
423
-                    : $question->ID();
424
-                $form_args['subsections'][ $identifier ] = $this->reg_form_question($registration, $question);
425
-            }
426
-        }
427
-        $form_args['subsections'] = apply_filters(
428
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information__question_group_reg_form__subsections_array',
429
-            $form_args['subsections'],
430
-            $registration,
431
-            $question_group,
432
-            $this
433
-        );
434
-        // filter for additional content after questions
435
-        $form_args['subsections']['reg_form_questions_after'] = new EE_Form_Section_HTML(
436
-            apply_filters(
437
-                'FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions',
438
-                '',
439
-                $registration,
440
-                $question_group,
441
-                $this
442
-            )
443
-        );
444
-        // d($form_args);
445
-        $question_group_reg_form = new EE_Form_Section_Proper($form_args);
446
-        return apply_filters(
447
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__question_group_reg_form',
448
-            $question_group_reg_form,
449
-            $registration,
450
-            $question_group,
451
-            $this
452
-        );
453
-    }
454
-
455
-
456
-    /**
457
-     * @param EE_Question_Group $question_group
458
-     * @return    EE_Form_Section_HTML
459
-     */
460
-    private function _question_group_header(EE_Question_Group $question_group)
461
-    {
462
-        $html = '';
463
-        // group_name
464
-        if ($question_group->show_group_name() && $question_group->name() !== '') {
465
-            if ($this->checkout->admin_request) {
466
-                $html .= EEH_HTML::br();
467
-                $html .= EEH_HTML::h3(
468
-                    $question_group->name(),
469
-                    '',
470
-                    'ee-reg-form-qstn-grp-title title',
471
-                    'font-size: 1.3em; padding-left:0;'
472
-                );
473
-            } else {
474
-                $html .= EEH_HTML::h4(
475
-                    $question_group->name(),
476
-                    '',
477
-                    'ee-reg-form-qstn-grp-title section-title'
478
-                );
479
-            }
480
-        }
481
-        // group_desc
482
-        if ($question_group->show_group_desc() && $question_group->desc() !== '') {
483
-            $html .= EEH_HTML::p(
484
-                $question_group->desc(),
485
-                '',
486
-                $this->checkout->admin_request
487
-                    ? 'ee-reg-form-qstn-grp-desc-pg'
488
-                    : 'ee-reg-form-qstn-grp-desc-pg small-text lt-grey-text'
489
-            );
490
-        }
491
-        return new EE_Form_Section_HTML($html);
492
-    }
493
-
494
-
495
-    /**
496
-     * @return    EE_Form_Section_Proper
497
-     * @throws EE_Error
498
-     * @throws InvalidArgumentException
499
-     * @throws ReflectionException
500
-     * @throws InvalidDataTypeException
501
-     * @throws InvalidInterfaceException
502
-     */
503
-    private function _copy_attendee_info_form()
504
-    {
505
-        // array of params to pass to parent constructor
506
-        return new EE_Form_Section_Proper(
507
-            array(
508
-                'subsections'     => $this->_copy_attendee_info_inputs(),
509
-                'layout_strategy' => new EE_Template_Layout(
510
-                    array(
511
-                        'layout_template_file'     => SPCO_REG_STEPS_PATH
512
-                                                      . $this->_slug
513
-                                                      . DS
514
-                                                      . 'copy_attendee_info.template.php',
515
-                        'begin_template_file'      => null,
516
-                        'input_template_file'      => null,
517
-                        'subsection_template_file' => null,
518
-                        'end_template_file'        => null,
519
-                    )
520
-                ),
521
-            )
522
-        );
523
-    }
524
-
525
-
526
-    /**
527
-     * @return EE_Form_Section_HTML
528
-     * @throws DomainException
529
-     * @throws InvalidArgumentException
530
-     * @throws InvalidDataTypeException
531
-     * @throws InvalidInterfaceException
532
-     */
533
-    private function _auto_copy_attendee_info()
534
-    {
535
-        return new EE_Form_Section_HTML(
536
-            EEH_Template::locate_template(
537
-                SPCO_REG_STEPS_PATH . $this->_slug . DS . '_auto_copy_attendee_info.template.php',
538
-                apply_filters(
539
-                    'FHEE__EE_SPCO_Reg_Step_Attendee_Information__auto_copy_attendee_info__template_args',
540
-                    array()
541
-                ),
542
-                true,
543
-                true
544
-            )
545
-        );
546
-    }
547
-
548
-
549
-    /**
550
-     * @return array
551
-     * @throws EE_Error
552
-     * @throws InvalidArgumentException
553
-     * @throws ReflectionException
554
-     * @throws InvalidDataTypeException
555
-     * @throws InvalidInterfaceException
556
-     */
557
-    private function _copy_attendee_info_inputs()
558
-    {
559
-        $copy_attendee_info_inputs = array();
560
-        $prev_ticket = null;
561
-        // grab the saved registrations from the transaction
562
-        $registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
563
-        foreach ($registrations as $registration) {
564
-            // for all  attendees other than the primary attendee
565
-            if ($registration instanceof EE_Registration && ! $registration->is_primary_registrant()) {
566
-                // if this is a new ticket OR if this is the very first additional attendee after the primary attendee
567
-                if ($registration->ticket()->ID() !== $prev_ticket) {
568
-                    $item_name = $registration->ticket()->name();
569
-                    $item_name .= $registration->ticket()->description() !== ''
570
-                        ? ' - ' . $registration->ticket()->description()
571
-                        : '';
572
-                    $copy_attendee_info_inputs[ 'spco_copy_attendee_chk[ticket-' . $registration->ticket()->ID(
573
-                    ) . ']' ] =
574
-                        new EE_Form_Section_HTML(
575
-                            '<h6 class="spco-copy-attendee-event-hdr">' . $item_name . '</h6>'
576
-                        );
577
-                    $prev_ticket = $registration->ticket()->ID();
578
-                }
579
-
580
-                $copy_attendee_info_inputs[ 'spco_copy_attendee_chk[' . $registration->ID() . ']' ] =
581
-                    new EE_Checkbox_Multi_Input(
582
-                        array(
583
-                            $registration->ID() => sprintf(
584
-                                esc_html_x('Attendee #%s', 'Attendee #123', 'event_espresso'),
585
-                                $registration->count()
586
-                            ),
587
-                        ),
588
-                        array(
589
-                            'html_id'                 => 'spco-copy-attendee-chk-' . $registration->reg_url_link(),
590
-                            'html_class'              => 'spco-copy-attendee-chk ee-do-not-validate',
591
-                            'display_html_label_text' => false,
592
-                        )
593
-                    );
594
-            }
595
-        }
596
-        return $copy_attendee_info_inputs;
597
-    }
598
-
599
-
600
-    /**
601
-     * @param EE_Registration $registration
602
-     * @return    EE_Form_Input_Base
603
-     * @throws EE_Error
604
-     */
605
-    private function _additional_primary_registrant_inputs(EE_Registration $registration)
606
-    {
607
-        // generate hidden input
608
-        return new EE_Hidden_Input(
609
-            array(
610
-                'html_id' => 'primary_registrant',
611
-                'default' => $registration->reg_url_link(),
612
-            )
613
-        );
614
-    }
615
-
616
-
617
-    /**
618
-     * @param EE_Registration $registration
619
-     * @param EE_Question     $question
620
-     * @return EE_Form_Input_Base
621
-     * @throws EE_Error
622
-     * @throws InvalidArgumentException
623
-     * @throws InvalidDataTypeException
624
-     * @throws InvalidInterfaceException
625
-     * @throws ReflectionException
626
-     */
627
-    public function reg_form_question(EE_Registration $registration, EE_Question $question)
628
-    {
629
-
630
-        // if this question was for an attendee detail, then check for that answer
631
-        $answer_value = EEM_Answer::instance()->get_attendee_property_answer_value(
632
-            $registration,
633
-            $question->system_ID()
634
-        );
635
-        $answer = $answer_value === null
636
-            ? EEM_Answer::instance()->get_one(
637
-                array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
638
-            )
639
-            : null;
640
-        // if NOT returning to edit an existing registration
641
-        // OR if this question is for an attendee property
642
-        // OR we still don't have an EE_Answer object
643
-        if ($answer_value || ! $answer instanceof EE_Answer || ! $registration->reg_url_link()) {
644
-            // create an EE_Answer object for storing everything in
645
-            $answer = EE_Answer::new_instance(
646
-                array(
647
-                    'QST_ID' => $question->ID(),
648
-                    'REG_ID' => $registration->ID(),
649
-                )
650
-            );
651
-        }
652
-        // verify instance
653
-        if ($answer instanceof EE_Answer) {
654
-            if (! empty($answer_value)) {
655
-                $answer->set('ANS_value', $answer_value);
656
-            }
657
-            $answer->cache('Question', $question);
658
-            // remember system ID had a bug where sometimes it could be null
659
-            $answer_cache_id = $question->is_system_question()
660
-                ? $question->system_ID() . '-' . $registration->reg_url_link()
661
-                : $question->ID() . '-' . $registration->reg_url_link();
662
-            $registration->cache('Answer', $answer, $answer_cache_id);
663
-        }
664
-        return $this->_generate_question_input($registration, $question, $answer);
665
-    }
666
-
667
-
668
-    /**
669
-     * @param EE_Registration $registration
670
-     * @param EE_Question     $question
671
-     * @param                 $answer
672
-     * @return EE_Form_Input_Base
673
-     * @throws EE_Error
674
-     * @throws InvalidArgumentException
675
-     * @throws ReflectionException
676
-     * @throws InvalidDataTypeException
677
-     * @throws InvalidInterfaceException
678
-     */
679
-    private function _generate_question_input(EE_Registration $registration, EE_Question $question, $answer)
680
-    {
681
-        $identifier = $question->is_system_question()
682
-            ? $question->system_ID()
683
-            : $question->ID();
684
-        $this->_required_questions[ $identifier ] = $question->required() ? true : false;
685
-        add_filter(
686
-            'FHEE__EE_Question__generate_form_input__country_options',
687
-            array($this, 'use_cached_countries_for_form_input'),
688
-            10,
689
-            4
690
-        );
691
-        add_filter(
692
-            'FHEE__EE_Question__generate_form_input__state_options',
693
-            array($this, 'use_cached_states_for_form_input'),
694
-            10,
695
-            4
696
-        );
697
-        $input_constructor_args = array(
698
-            'html_name'        => 'ee_reg_qstn[' . $registration->ID() . '][' . $identifier . ']',
699
-            'html_id'          => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
700
-            'html_class'       => 'ee-reg-qstn ee-reg-qstn-' . $identifier,
701
-            'html_label_id'    => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
702
-            'html_label_class' => 'ee-reg-qstn',
703
-        );
704
-        $input_constructor_args['html_label_id'] .= '-lbl';
705
-        if ($answer instanceof EE_Answer && $answer->ID()) {
706
-            $input_constructor_args['html_name'] .= '[' . $answer->ID() . ']';
707
-            $input_constructor_args['html_id'] .= '-' . $answer->ID();
708
-            $input_constructor_args['html_label_id'] .= '-' . $answer->ID();
709
-        }
710
-        $form_input = $question->generate_form_input(
711
-            $registration,
712
-            $answer,
713
-            $input_constructor_args
714
-        );
715
-        remove_filter(
716
-            'FHEE__EE_Question__generate_form_input__country_options',
717
-            array($this, 'use_cached_countries_for_form_input')
718
-        );
719
-        remove_filter(
720
-            'FHEE__EE_Question__generate_form_input__state_options',
721
-            array($this, 'use_cached_states_for_form_input')
722
-        );
723
-        return $form_input;
724
-    }
725
-
726
-
727
-    /**
728
-     * Gets the list of countries for the form input
729
-     *
730
-     * @param array|null      $countries_list
731
-     * @param EE_Question     $question
732
-     * @param EE_Registration $registration
733
-     * @param EE_Answer       $answer
734
-     * @return array 2d keys are country IDs, values are their names
735
-     * @throws EE_Error
736
-     * @throws InvalidArgumentException
737
-     * @throws InvalidDataTypeException
738
-     * @throws InvalidInterfaceException
739
-     * @throws ReflectionException
740
-     */
741
-    public function use_cached_countries_for_form_input(
742
-        $countries_list,
743
-        EE_Question $question = null,
744
-        EE_Registration $registration = null,
745
-        EE_Answer $answer = null
746
-    ) {
747
-        $country_options = array('' => '');
748
-        // get possibly cached list of countries
749
-        $countries = $this->checkout->action === 'process_reg_step'
750
-            ? EEM_Country::instance()->get_all_countries()
751
-            : EEM_Country::instance()->get_all_active_countries();
752
-        if (! empty($countries)) {
753
-            foreach ($countries as $country) {
754
-                if ($country instanceof EE_Country) {
755
-                    $country_options[ $country->ID() ] = $country->name();
756
-                }
757
-            }
758
-        }
759
-        if ($question instanceof EE_Question && $registration instanceof EE_Registration) {
760
-            $answer = EEM_Answer::instance()->get_one(
761
-                array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
762
-            );
763
-        } else {
764
-            $answer = EE_Answer::new_instance();
765
-        }
766
-        $country_options = apply_filters(
767
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__country_options',
768
-            $country_options,
769
-            $this,
770
-            $registration,
771
-            $question,
772
-            $answer
773
-        );
774
-        return $country_options;
775
-    }
776
-
777
-
778
-    /**
779
-     * Gets the list of states for the form input
780
-     *
781
-     * @param array|null      $states_list
782
-     * @param EE_Question     $question
783
-     * @param EE_Registration $registration
784
-     * @param EE_Answer       $answer
785
-     * @return array 2d keys are state IDs, values are their names
786
-     * @throws EE_Error
787
-     * @throws InvalidArgumentException
788
-     * @throws InvalidDataTypeException
789
-     * @throws InvalidInterfaceException
790
-     * @throws ReflectionException
791
-     */
792
-    public function use_cached_states_for_form_input(
793
-        $states_list,
794
-        EE_Question $question = null,
795
-        EE_Registration $registration = null,
796
-        EE_Answer $answer = null
797
-    ) {
798
-        $state_options = array('' => array('' => ''));
799
-        $states = $this->checkout->action === 'process_reg_step'
800
-            ? EEM_State::instance()->get_all_states()
801
-            : EEM_State::instance()->get_all_active_states();
802
-        if (! empty($states)) {
803
-            foreach ($states as $state) {
804
-                if ($state instanceof EE_State) {
805
-                    $state_options[ $state->country()->name() ][ $state->ID() ] = $state->name();
806
-                }
807
-            }
808
-        }
809
-        $state_options = apply_filters(
810
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__state_options',
811
-            $state_options,
812
-            $this,
813
-            $registration,
814
-            $question,
815
-            $answer
816
-        );
817
-        return $state_options;
818
-    }
819
-
820
-
821
-    /********************************************************************************************************/
822
-    /****************************************  PROCESS REG STEP  ****************************************/
823
-    /********************************************************************************************************/
824
-
825
-
826
-    /**
827
-     * @return bool
828
-     * @throws EE_Error
829
-     * @throws InvalidArgumentException
830
-     * @throws ReflectionException
831
-     * @throws RuntimeException
832
-     * @throws InvalidDataTypeException
833
-     * @throws InvalidInterfaceException
834
-     */
835
-    public function process_reg_step()
836
-    {
837
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
838
-        // grab validated data from form
839
-        $valid_data = $this->checkout->current_step->valid_data();
840
-        // EEH_Debug_Tools::printr( $_REQUEST, '$_REQUEST', __FILE__, __LINE__ );
841
-        // EEH_Debug_Tools::printr( $valid_data, '$valid_data', __FILE__, __LINE__ );
842
-        // if we don't have any $valid_data then something went TERRIBLY WRONG !!!
843
-        if (empty($valid_data)) {
844
-            EE_Error::add_error(
845
-                esc_html__('No valid question responses were received.', 'event_espresso'),
846
-                __FILE__,
847
-                __FUNCTION__,
848
-                __LINE__
849
-            );
850
-            return false;
851
-        }
852
-        if (! $this->checkout->transaction instanceof EE_Transaction || ! $this->checkout->continue_reg) {
853
-            EE_Error::add_error(
854
-                esc_html__(
855
-                    'A valid transaction could not be initiated for processing your registrations.',
856
-                    'event_espresso'
857
-                ),
858
-                __FILE__,
859
-                __FUNCTION__,
860
-                __LINE__
861
-            );
862
-            return false;
863
-        }
864
-        // get cached registrations
865
-        $registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
866
-        // verify we got the goods
867
-        if (empty($registrations)) {
868
-            // combine the old translated string with a new one, in order to not break translations
869
-            $error_message = esc_html__(
870
-                'Your form data could not be applied to any valid registrations.',
871
-                'event_espresso'
872
-            )
873
-            . sprintf(
874
-                esc_html_x(
875
-                    '%3$sThis can sometimes happen if too much time has been taken to complete the registration process.%3$sPlease return to the %1$sEvent List%2$s and reselect your tickets. If the problem continues, please contact the site administrator.',
876
-                    '(line break)This can sometimes happen if too much time has been taken to complete the registration process.(line break)Please return to the (link)Event List(end link) and reselect your tickets. If the problem continues, please contact the site administrator.',
877
-                    'event_espresso'
878
-                ),
879
-                '<a href="' . get_post_type_archive_link('espresso_events') . '" >',
880
-                '</a>',
881
-                '<br />'
882
-            );
883
-            EE_Error::add_error(
884
-                $error_message,
885
-                __FILE__,
886
-                __FUNCTION__,
887
-                __LINE__
888
-            );
889
-            return false;
890
-        }
891
-        // extract attendee info from form data and save to model objects
892
-        $registrations_processed = $this->_process_registrations($registrations, $valid_data);
893
-        // if first pass thru SPCO,
894
-        // then let's check processed registrations against the total number of tickets in the cart
895
-        if ($registrations_processed === false) {
896
-            // but return immediately if the previous step exited early due to errors
897
-            return false;
898
-        }
899
-        if (! $this->checkout->revisit && $registrations_processed !== $this->checkout->total_ticket_count) {
900
-            // generate a correctly translated string for all possible singular/plural combinations
901
-            if ($this->checkout->total_ticket_count === 1 && $registrations_processed !== 1) {
902
-                $error_msg = sprintf(
903
-                    esc_html_x(
904
-                        'There was %1$d ticket in the Event Queue, but %2$ds registrations were processed',
905
-                        'There was 1 ticket in the Event Queue, but 2 registrations were processed',
906
-                        'event_espresso'
907
-                    ),
908
-                    $this->checkout->total_ticket_count,
909
-                    $registrations_processed
910
-                );
911
-            } elseif ($this->checkout->total_ticket_count !== 1 && $registrations_processed === 1) {
912
-                $error_msg = sprintf(
913
-                    esc_html_x(
914
-                        'There was a total of %1$d tickets in the Event Queue, but only %2$ds registration was processed',
915
-                        'There was a total of 2 tickets in the Event Queue, but only 1 registration was processed',
916
-                        'event_espresso'
917
-                    ),
918
-                    $this->checkout->total_ticket_count,
919
-                    $registrations_processed
920
-                );
921
-            } else {
922
-                $error_msg = sprintf(
923
-                    esc_html__(
924
-                        'There was a total of 2 tickets in the Event Queue, but 2 registrations were processed',
925
-                        'event_espresso'
926
-                    ),
927
-                    $this->checkout->total_ticket_count,
928
-                    $registrations_processed
929
-                );
930
-            }
931
-            EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
932
-            return false;
933
-        }
934
-        // mark this reg step as completed
935
-        $this->set_completed();
936
-        $this->_set_success_message(
937
-            esc_html__('The Attendee Information Step has been successfully completed.', 'event_espresso')
938
-        );
939
-        // do action in case a plugin wants to do something with the data submitted in step 1.
940
-        // passes EE_Single_Page_Checkout, and it's posted data
941
-        do_action('AHEE__EE_Single_Page_Checkout__process_attendee_information__end', $this, $valid_data);
942
-        return true;
943
-    }
944
-
945
-
946
-    /**
947
-     *    _process_registrations
948
-     *
949
-     * @param EE_Registration[] $registrations
950
-     * @param array[][]         $valid_data
951
-     * @return bool|int
952
-     * @throws EntityNotFoundException
953
-     * @throws EE_Error
954
-     * @throws InvalidArgumentException
955
-     * @throws ReflectionException
956
-     * @throws RuntimeException
957
-     * @throws InvalidDataTypeException
958
-     * @throws InvalidInterfaceException
959
-     */
960
-    private function _process_registrations($registrations = array(), $valid_data = array())
961
-    {
962
-        // load resources and set some defaults
963
-        EE_Registry::instance()->load_model('Attendee');
964
-        // holder for primary registrant attendee object
965
-        $this->checkout->primary_attendee_obj = null;
966
-        // array for tracking reg form data for the primary registrant
967
-        $primary_registrant = array(
968
-            'line_item_id' => null,
969
-        );
970
-        $copy_primary = false;
971
-        // reg form sections that do not contain inputs
972
-        $non_input_form_sections = array(
973
-            'primary_registrant',
974
-            'additional_attendee_reg_info',
975
-            'spco_copy_attendee_chk',
976
-        );
977
-        // attendee counter
978
-        $att_nmbr = 0;
979
-        // grab the saved registrations from the transaction
980
-        foreach ($registrations as $registration) {
981
-            // verify EE_Registration object
982
-            if (! $registration instanceof EE_Registration) {
983
-                EE_Error::add_error(
984
-                    esc_html__(
985
-                        'An invalid Registration object was discovered when attempting to process your registration information.',
986
-                        'event_espresso'
987
-                    ),
988
-                    __FILE__,
989
-                    __FUNCTION__,
990
-                    __LINE__
991
-                );
992
-                return false;
993
-            }
994
-            /** @var string $reg_url_link */
995
-            $reg_url_link = $registration->reg_url_link();
996
-            // reg_url_link exists ?
997
-            if (! empty($reg_url_link)) {
998
-                // should this registration be processed during this visit ?
999
-                if ($this->checkout->visit_allows_processing_of_this_registration($registration)) {
1000
-                    // if NOT revisiting, then let's save the registration now,
1001
-                    // so that we have a REG_ID to use when generating other objects
1002
-                    if (! $this->checkout->revisit) {
1003
-                        $registration->save();
1004
-                    }
1005
-                    /**
1006
-                     * This allows plugins to trigger a fail on processing of a
1007
-                     * registration for any conditions they may have for it to pass.
1008
-                     *
1009
-                     * @var bool   if true is returned by the plugin then the
1010
-                     *            registration processing is halted.
1011
-                     */
1012
-                    if (apply_filters(
1013
-                        'FHEE__EE_SPCO_Reg_Step_Attendee_Information___process_registrations__pre_registration_process',
1014
-                        false,
1015
-                        $att_nmbr,
1016
-                        $registration,
1017
-                        $registrations,
1018
-                        $valid_data,
1019
-                        $this
1020
-                    )) {
1021
-                        return false;
1022
-                    }
1023
-
1024
-                    // Houston, we have a registration!
1025
-                    $att_nmbr++;
1026
-                    $this->_attendee_data[ $reg_url_link ] = array();
1027
-                    // grab any existing related answer objects
1028
-                    $this->_registration_answers = $registration->answers();
1029
-                    // unset( $valid_data[ $reg_url_link ]['additional_attendee_reg_info'] );
1030
-                    if (isset($valid_data[ $reg_url_link ])) {
1031
-                        // do we need to copy basic info from primary attendee ?
1032
-                        $copy_primary = isset($valid_data[ $reg_url_link ]['additional_attendee_reg_info'])
1033
-                                        && absint($valid_data[ $reg_url_link ]['additional_attendee_reg_info']) === 0;
1034
-                        // filter form input data for this registration
1035
-                        $valid_data[ $reg_url_link ] = (array) apply_filters(
1036
-                            'FHEE__EE_Single_Page_Checkout__process_attendee_information__valid_data_line_item',
1037
-                            $valid_data[ $reg_url_link ]
1038
-                        );
1039
-                        if (isset($valid_data['primary_attendee'])) {
1040
-                            $primary_registrant['line_item_id'] = ! empty($valid_data['primary_attendee'])
1041
-                                ? $valid_data['primary_attendee']
1042
-                                : false;
1043
-                            unset($valid_data['primary_attendee']);
1044
-                        }
1045
-                        // now loop through our array of valid post data && process attendee reg forms
1046
-                        foreach ($valid_data[ $reg_url_link ] as $form_section => $form_inputs) {
1047
-                            if (! in_array($form_section, $non_input_form_sections, true)) {
1048
-                                foreach ($form_inputs as $form_input => $input_value) {
1049
-                                    // \EEH_Debug_Tools::printr( $input_value, $form_input, __FILE__, __LINE__ );
1050
-                                    // check for critical inputs
1051
-                                    if (! $this->_verify_critical_attendee_details_are_set_and_validate_email(
1052
-                                        $form_input,
1053
-                                        $input_value
1054
-                                    )
1055
-                                    ) {
1056
-                                        return false;
1057
-                                    }
1058
-                                    // store a bit of data about the primary attendee
1059
-                                    if ($att_nmbr === 1
1060
-                                        && ! empty($input_value)
1061
-                                        && $reg_url_link === $primary_registrant['line_item_id']
1062
-                                    ) {
1063
-                                        $primary_registrant[ $form_input ] = $input_value;
1064
-                                    } elseif ($copy_primary
1065
-                                              && $input_value === null
1066
-                                              && isset($primary_registrant[ $form_input ])
1067
-                                    ) {
1068
-                                        $input_value = $primary_registrant[ $form_input ];
1069
-                                    }
1070
-                                    // now attempt to save the input data
1071
-                                    if (! $this->_save_registration_form_input(
1072
-                                        $registration,
1073
-                                        $form_input,
1074
-                                        $input_value
1075
-                                    )
1076
-                                    ) {
1077
-                                        EE_Error::add_error(
1078
-                                            sprintf(
1079
-                                                esc_html_x(
1080
-                                                    'Unable to save registration form data for the form input: "%1$s" with the submitted value: "%2$s"',
1081
-                                                    'Unable to save registration form data for the form input: "form input name" with the submitted value: "form input value"',
1082
-                                                    'event_espresso'
1083
-                                                ),
1084
-                                                $form_input,
1085
-                                                $input_value
1086
-                                            ),
1087
-                                            __FILE__,
1088
-                                            __FUNCTION__,
1089
-                                            __LINE__
1090
-                                        );
1091
-                                        return false;
1092
-                                    }
1093
-                                }
1094
-                            }
1095
-                        }  // end of foreach ( $valid_data[ $reg_url_link ] as $form_section => $form_inputs )
1096
-                    }
1097
-                    // EEH_Debug_Tools::printr( $this->_attendee_data, '$this->_attendee_data', __FILE__, __LINE__ );
1098
-                    // this registration does not require additional attendee information ?
1099
-                    if ($copy_primary
1100
-                        && $att_nmbr > 1
1101
-                        && $this->checkout->primary_attendee_obj instanceof EE_Attendee
1102
-                    ) {
1103
-                        // just copy the primary registrant
1104
-                        $attendee = $this->checkout->primary_attendee_obj;
1105
-                    } else {
1106
-                        // ensure critical details are set for additional attendees
1107
-                        $this->_attendee_data[ $reg_url_link ] = $att_nmbr > 1
1108
-                            ? $this->_copy_critical_attendee_details_from_primary_registrant(
1109
-                                $this->_attendee_data[ $reg_url_link ]
1110
-                            )
1111
-                            : $this->_attendee_data[ $reg_url_link ];
1112
-                        // execute create attendee command (which may return an existing attendee)
1113
-                        $attendee = EE_Registry::instance()->BUS->execute(
1114
-                            new CreateAttendeeCommand(
1115
-                                $this->_attendee_data[ $reg_url_link ],
1116
-                                $registration
1117
-                            )
1118
-                        );
1119
-                        // who's #1 ?
1120
-                        if ($att_nmbr === 1) {
1121
-                            $this->checkout->primary_attendee_obj = $attendee;
1122
-                        }
1123
-                    }
1124
-                    // EEH_Debug_Tools::printr( $attendee, '$attendee', __FILE__, __LINE__ );
1125
-                    // add relation to registration, set attendee ID, and cache attendee
1126
-                    $this->_associate_attendee_with_registration($registration, $attendee);
1127
-                    // \EEH_Debug_Tools::printr( $registration, '$registration', __FILE__, __LINE__ );
1128
-                    if (! $registration->attendee() instanceof EE_Attendee) {
1129
-                        EE_Error::add_error(
1130
-                            sprintf(
1131
-                                esc_html_x(
1132
-                                    'Registration %s has an invalid or missing Attendee object.',
1133
-                                    'Registration 123-456-789 has an invalid or missing Attendee object.',
1134
-                                    'event_espresso'
1135
-                                ),
1136
-                                $reg_url_link
1137
-                            ),
1138
-                            __FILE__,
1139
-                            __FUNCTION__,
1140
-                            __LINE__
1141
-                        );
1142
-                        return false;
1143
-                    }
1144
-                    /** @type EE_Registration_Processor $registration_processor */
1145
-                    $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
1146
-                    // at this point, we should have enough details about the registrant to consider the registration
1147
-                    // NOT incomplete
1148
-                    $registration_processor->toggle_incomplete_registration_status_to_default(
1149
-                        $registration,
1150
-                        false,
1151
-                        new Context(
1152
-                            'spco_reg_step_attendee_information_process_registrations',
1153
-                            esc_html__(
1154
-                                'Finished populating registration with details from the registration form after submitting the Attendee Information Reg Step.',
1155
-                                'event_espresso'
1156
-                            )
1157
-                        )
1158
-                    );
1159
-                    // we can also consider the TXN to not have been failed, so temporarily upgrade it's status to
1160
-                    // abandoned
1161
-                    $this->checkout->transaction->toggle_failed_transaction_status();
1162
-                    // if we've gotten this far, then let's save what we have
1163
-                    $registration->save();
1164
-                    // add relation between TXN and registration
1165
-                    $this->_associate_registration_with_transaction($registration);
1166
-                }
1167
-            } else {
1168
-                EE_Error::add_error(
1169
-                    esc_html__(
1170
-                        'An invalid or missing line item ID was encountered while attempting to process the registration form.',
1171
-                        'event_espresso'
1172
-                    ),
1173
-                    __FILE__,
1174
-                    __FUNCTION__,
1175
-                    __LINE__
1176
-                );
1177
-                // remove malformed data
1178
-                unset($valid_data[ $reg_url_link ]);
1179
-                return false;
1180
-            }
1181
-        } // end of foreach ( $this->checkout->transaction->registrations()  as $registration )
1182
-        return $att_nmbr;
1183
-    }
1184
-
1185
-
1186
-    /**
1187
-     *    _save_registration_form_input
1188
-     *
1189
-     * @param EE_Registration $registration
1190
-     * @param string          $form_input
1191
-     * @param string          $input_value
1192
-     * @return bool
1193
-     * @throws EE_Error
1194
-     * @throws InvalidArgumentException
1195
-     * @throws InvalidDataTypeException
1196
-     * @throws InvalidInterfaceException
1197
-     * @throws ReflectionException
1198
-     */
1199
-    private function _save_registration_form_input(
1200
-        EE_Registration $registration,
1201
-        $form_input = '',
1202
-        $input_value = ''
1203
-    ) {
1204
-        // \EEH_Debug_Tools::printr( __FUNCTION__, __CLASS__, __FILE__, __LINE__, 2 );
1205
-        // \EEH_Debug_Tools::printr( $form_input, '$form_input', __FILE__, __LINE__ );
1206
-        // \EEH_Debug_Tools::printr( $input_value, '$input_value', __FILE__, __LINE__ );
1207
-        // allow for plugins to hook in and do their own processing of the form input.
1208
-        // For plugins to bypass normal processing here, they just need to return a boolean value.
1209
-        if (apply_filters(
1210
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___save_registration_form_input',
1211
-            false,
1212
-            $registration,
1213
-            $form_input,
1214
-            $input_value,
1215
-            $this
1216
-        )) {
1217
-            return true;
1218
-        }
1219
-        /*
21
+	/**
22
+	 * @type bool $_print_copy_info
23
+	 */
24
+	private $_print_copy_info = false;
25
+
26
+	/**
27
+	 * @type array $_attendee_data
28
+	 */
29
+	private $_attendee_data = array();
30
+
31
+	/**
32
+	 * @type array $_required_questions
33
+	 */
34
+	private $_required_questions = array();
35
+
36
+	/**
37
+	 * @type array $_registration_answers
38
+	 */
39
+	private $_registration_answers = array();
40
+
41
+
42
+	/**
43
+	 *    class constructor
44
+	 *
45
+	 * @access    public
46
+	 * @param    EE_Checkout $checkout
47
+	 */
48
+	public function __construct(EE_Checkout $checkout)
49
+	{
50
+		$this->_slug = 'attendee_information';
51
+		$this->_name = esc_html__('Attendee Information', 'event_espresso');
52
+		$this->_template = SPCO_REG_STEPS_PATH . $this->_slug . DS . 'attendee_info_main.template.php';
53
+		$this->checkout = $checkout;
54
+		$this->_reset_success_message();
55
+		$this->set_instructions(
56
+			esc_html__('Please answer the following registration questions before proceeding.', 'event_espresso')
57
+		);
58
+	}
59
+
60
+
61
+	public function translate_js_strings()
62
+	{
63
+		EE_Registry::$i18n_js_strings['required_field'] = esc_html__(
64
+			' is a required question.',
65
+			'event_espresso'
66
+		);
67
+		EE_Registry::$i18n_js_strings['required_multi_field'] = esc_html__(
68
+			' is a required question. Please enter a value for at least one of the options.',
69
+			'event_espresso'
70
+		);
71
+		EE_Registry::$i18n_js_strings['answer_required_questions'] = esc_html__(
72
+			'Please answer all required questions correctly before proceeding.',
73
+			'event_espresso'
74
+		);
75
+		EE_Registry::$i18n_js_strings['attendee_info_copied'] = sprintf(
76
+			esc_html_x(
77
+				'The attendee information was successfully copied.%sPlease ensure the rest of the registration form is completed before proceeding.',
78
+				'The attendee information was successfully copied.(line break)Please ensure the rest of the registration form is completed before proceeding.',
79
+				'event_espresso'
80
+			),
81
+			'<br/>'
82
+		);
83
+		EE_Registry::$i18n_js_strings['attendee_info_copy_error'] = esc_html__(
84
+			'An unknown error occurred on the server while attempting to copy the attendee information. Please refresh the page and try again.',
85
+			'event_espresso'
86
+		);
87
+		EE_Registry::$i18n_js_strings['enter_valid_email'] = esc_html__(
88
+			'You must enter a valid email address.',
89
+			'event_espresso'
90
+		);
91
+		EE_Registry::$i18n_js_strings['valid_email_and_questions'] = esc_html__(
92
+			'You must enter a valid email address and answer all other required questions before you can proceed.',
93
+			'event_espresso'
94
+		);
95
+	}
96
+
97
+
98
+	public function enqueue_styles_and_scripts()
99
+	{
100
+	}
101
+
102
+
103
+	/**
104
+	 * @return boolean
105
+	 */
106
+	public function initialize_reg_step()
107
+	{
108
+		return true;
109
+	}
110
+
111
+
112
+	/**
113
+	 * @return EE_Form_Section_Proper
114
+	 * @throws DomainException
115
+	 * @throws EE_Error
116
+	 * @throws InvalidArgumentException
117
+	 * @throws ReflectionException
118
+	 * @throws EntityNotFoundException
119
+	 * @throws InvalidDataTypeException
120
+	 * @throws InvalidInterfaceException
121
+	 */
122
+	public function generate_reg_form()
123
+	{
124
+		$this->_print_copy_info = false;
125
+		$primary_registrant = null;
126
+		// autoload Line_Item_Display classes
127
+		EEH_Autoloader::register_line_item_display_autoloaders();
128
+		$Line_Item_Display = new EE_Line_Item_Display();
129
+		// calculate taxes
130
+		$Line_Item_Display->display_line_item(
131
+			$this->checkout->cart->get_grand_total(),
132
+			array('set_tax_rate' => true)
133
+		);
134
+		/** @var $subsections EE_Form_Section_Proper[] */
135
+		$extra_inputs_section = $this->reg_step_hidden_inputs();
136
+		$subsections = array(
137
+			'default_hidden_inputs' => $extra_inputs_section,
138
+		);
139
+
140
+		/**
141
+		 * @var $reg_config EE_Registration_Config
142
+		 */
143
+		$reg_config = LoaderFactory::getLoader()->getShared('EE_Registration_Config');
144
+		// if this isn't a revisit, and they have the privacy consent box enalbed, add it
145
+		if (! $this->checkout->revisit && $reg_config->isConsentCheckboxEnabled()) {
146
+			$extra_inputs_section->add_subsections(
147
+				array(
148
+					'consent_box' => new EE_Form_Section_Proper(
149
+						array(
150
+							'layout_strategy' =>
151
+								new EE_Template_Layout(
152
+									array(
153
+										'input_template_file' => SPCO_REG_STEPS_PATH . $this->_slug . DS . 'privacy_consent.template.php',
154
+									)
155
+								),
156
+							'subsections'     => array(
157
+								'consent' => new EE_Checkbox_Multi_Input(
158
+									array(
159
+										'consent' => $reg_config->getConsentCheckboxLabelText(),
160
+									),
161
+									array(
162
+										'required'                          => true,
163
+										'required_validation_error_message' => esc_html__(
164
+											'You must consent to these terms in order to register.',
165
+											'event_espresso'
166
+										),
167
+										'html_label_text'                   => '',
168
+									)
169
+								),
170
+							),
171
+						)
172
+					),
173
+				),
174
+				null,
175
+				false
176
+			);
177
+		}
178
+		$template_args = array(
179
+			'revisit'       => $this->checkout->revisit,
180
+			'registrations' => array(),
181
+			'ticket_count'  => array(),
182
+		);
183
+		// grab the saved registrations from the transaction
184
+		$registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
185
+		if ($registrations) {
186
+			foreach ($registrations as $registration) {
187
+				// can this registration be processed during this visit ?
188
+				if ($registration instanceof EE_Registration
189
+					&& $this->checkout->visit_allows_processing_of_this_registration($registration)
190
+				) {
191
+					$subsections[ $registration->reg_url_link() ] = $this->_registrations_reg_form($registration);
192
+					if (! $this->checkout->admin_request) {
193
+						$template_args['registrations'][ $registration->reg_url_link() ] = $registration;
194
+						$template_args['ticket_count'][ $registration->ticket()->ID() ] = isset(
195
+							$template_args['ticket_count'][ $registration->ticket()->ID() ]
196
+						)
197
+							? $template_args['ticket_count'][ $registration->ticket()->ID() ] + 1
198
+							: 1;
199
+						$ticket_line_item = EEH_Line_Item::get_line_items_by_object_type_and_IDs(
200
+							$this->checkout->cart->get_grand_total(),
201
+							'Ticket',
202
+							array($registration->ticket()->ID())
203
+						);
204
+						$ticket_line_item = is_array($ticket_line_item)
205
+							? reset($ticket_line_item)
206
+							: $ticket_line_item;
207
+						$template_args['ticket_line_item'][ $registration->ticket()->ID() ] =
208
+							$Line_Item_Display->display_line_item($ticket_line_item);
209
+					}
210
+					if ($registration->is_primary_registrant()) {
211
+						$primary_registrant = $registration->reg_url_link();
212
+					}
213
+				}
214
+			}
215
+			// print_copy_info ?
216
+			if ($primary_registrant && ! $this->checkout->admin_request && count($registrations) > 1) {
217
+				// TODO: add admin option for toggling copy attendee info,
218
+				// then use that value to change $this->_print_copy_info
219
+				$copy_options['spco_copy_attendee_chk'] = $this->_print_copy_info
220
+					? $this->_copy_attendee_info_form()
221
+					: $this->_auto_copy_attendee_info();
222
+				// generate hidden input
223
+				if (isset($subsections[ $primary_registrant ])
224
+					&& $subsections[ $primary_registrant ] instanceof EE_Form_Section_Proper
225
+				) {
226
+					$subsections[ $primary_registrant ]->add_subsections(
227
+						$copy_options,
228
+						'primary_registrant',
229
+						false
230
+					);
231
+				}
232
+			}
233
+		}
234
+		return new EE_Form_Section_Proper(
235
+			array(
236
+				'name'            => $this->reg_form_name(),
237
+				'html_id'         => $this->reg_form_name(),
238
+				'subsections'     => $subsections,
239
+				'layout_strategy' => $this->checkout->admin_request
240
+					?
241
+					new EE_Div_Per_Section_Layout()
242
+					:
243
+					new EE_Template_Layout(
244
+						array(
245
+							'layout_template_file' => $this->_template, // layout_template
246
+							'template_args'        => $template_args,
247
+						)
248
+					),
249
+			)
250
+		);
251
+	}
252
+
253
+
254
+	/**
255
+	 * @param EE_Registration $registration
256
+	 * @return EE_Form_Section_Base
257
+	 * @throws EE_Error
258
+	 * @throws InvalidArgumentException
259
+	 * @throws EntityNotFoundException
260
+	 * @throws InvalidDataTypeException
261
+	 * @throws InvalidInterfaceException
262
+	 * @throws ReflectionException
263
+	 */
264
+	private function _registrations_reg_form(EE_Registration $registration)
265
+	{
266
+		static $attendee_nmbr = 1;
267
+		$form_args = array();
268
+		// verify that registration has valid event
269
+		if ($registration->event() instanceof EE_Event) {
270
+			$field_name = 'Event_Question_Group.'
271
+				. EEM_Event_Question_Group::instance()->field_name_for_category(
272
+					$registration->is_primary_registrant()
273
+				);
274
+			$question_groups = $registration->event()->question_groups(
275
+				apply_filters(
276
+					'FHEE__EE_SPCO_Reg_Step_Attendee_Information___registrations_reg_form__question_groups_query_parameters',
277
+					[
278
+						[
279
+							'Event.EVT_ID'                     => $registration->event()->ID(),
280
+							$field_name => true,
281
+						],
282
+						'order_by' => ['QSG_order' => 'ASC'],
283
+					],
284
+					$registration,
285
+					$this
286
+				)
287
+			);
288
+			if ($question_groups) {
289
+				// array of params to pass to parent constructor
290
+				$form_args = array(
291
+					'html_id'         => 'ee-registration-' . $registration->reg_url_link(),
292
+					'html_class'      => 'ee-reg-form-attendee-dv',
293
+					'html_style'      => $this->checkout->admin_request
294
+						? 'padding:0em 2em 1em; margin:3em 0 0; border:1px solid #ddd;'
295
+						: '',
296
+					'subsections'     => array(),
297
+					'layout_strategy' => new EE_Fieldset_Section_Layout(
298
+						array(
299
+							'legend_class' => 'spco-attendee-lgnd smaller-text lt-grey-text',
300
+							'legend_text'  => sprintf(
301
+								esc_html_x(
302
+									'Attendee %d',
303
+									'Attendee 123',
304
+									'event_espresso'
305
+								),
306
+								$attendee_nmbr
307
+							),
308
+						)
309
+					),
310
+				);
311
+				foreach ($question_groups as $question_group) {
312
+					if ($question_group instanceof EE_Question_Group) {
313
+						$form_args['subsections'][ $question_group->identifier() ] = $this->_question_group_reg_form(
314
+							$registration,
315
+							$question_group
316
+						);
317
+					}
318
+				}
319
+				// add hidden input
320
+				$form_args['subsections']['additional_attendee_reg_info'] = $this->_additional_attendee_reg_info_input(
321
+					$registration
322
+				);
323
+				// if we have question groups for additional attendees, then display the copy options
324
+				$this->_print_copy_info = $attendee_nmbr > 1 ? true : $this->_print_copy_info;
325
+				if ($registration->is_primary_registrant()) {
326
+					// generate hidden input
327
+					$form_args['subsections']['primary_registrant'] = $this->_additional_primary_registrant_inputs(
328
+						$registration
329
+					);
330
+				}
331
+			}
332
+		}
333
+		$attendee_nmbr++;
334
+		return ! empty($form_args)
335
+			? new EE_Form_Section_Proper($form_args)
336
+			: new EE_Form_Section_HTML();
337
+	}
338
+
339
+
340
+	/**
341
+	 * @param EE_Registration $registration
342
+	 * @param bool            $additional_attendee_reg_info
343
+	 * @return EE_Form_Input_Base
344
+	 * @throws EE_Error
345
+	 */
346
+	private function _additional_attendee_reg_info_input(
347
+		EE_Registration $registration,
348
+		$additional_attendee_reg_info = true
349
+	) {
350
+		// generate hidden input
351
+		return new EE_Hidden_Input(
352
+			array(
353
+				'html_id' => 'additional-attendee-reg-info-' . $registration->reg_url_link(),
354
+				'default' => $additional_attendee_reg_info,
355
+			)
356
+		);
357
+	}
358
+
359
+
360
+	/**
361
+	 * @param EE_Registration   $registration
362
+	 * @param EE_Question_Group $question_group
363
+	 * @return EE_Form_Section_Proper
364
+	 * @throws EE_Error
365
+	 * @throws InvalidArgumentException
366
+	 * @throws InvalidDataTypeException
367
+	 * @throws InvalidInterfaceException
368
+	 * @throws ReflectionException
369
+	 */
370
+	private function _question_group_reg_form(EE_Registration $registration, EE_Question_Group $question_group)
371
+	{
372
+		// array of params to pass to parent constructor
373
+		$form_args = array(
374
+			'html_id'         => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-' . $registration->ID(),
375
+			'html_class'      => $this->checkout->admin_request
376
+				? 'form-table ee-reg-form-qstn-grp-dv'
377
+				: 'ee-reg-form-qstn-grp-dv',
378
+			'html_label_id'   => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-'
379
+								 . $registration->ID() . '-lbl',
380
+			'subsections'     => array(
381
+				'reg_form_qstn_grp_hdr' => $this->_question_group_header($question_group),
382
+			),
383
+			'layout_strategy' => $this->checkout->admin_request
384
+				? new EE_Admin_Two_Column_Layout()
385
+				: new EE_Div_Per_Section_Layout(),
386
+		);
387
+		// where params
388
+		$query_params = array('QST_deleted' => 0);
389
+		// don't load admin only questions on the frontend
390
+		if (! $this->checkout->admin_request) {
391
+			$query_params['QST_admin_only'] = array('!=', true);
392
+		}
393
+		$questions = $question_group->get_many_related(
394
+			'Question',
395
+			apply_filters(
396
+				'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__related_questions_query_params',
397
+				array(
398
+					$query_params,
399
+					'order_by' => array(
400
+						'Question_Group_Question.QGQ_order' => 'ASC',
401
+					),
402
+				),
403
+				$question_group,
404
+				$registration,
405
+				$this
406
+			)
407
+		);
408
+		// filter for additional content before questions
409
+		$form_args['subsections']['reg_form_questions_before'] = new EE_Form_Section_HTML(
410
+			apply_filters(
411
+				'FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions',
412
+				'',
413
+				$registration,
414
+				$question_group,
415
+				$this
416
+			)
417
+		);
418
+		// loop thru questions
419
+		foreach ($questions as $question) {
420
+			if ($question instanceof EE_Question) {
421
+				$identifier = $question->is_system_question()
422
+					? $question->system_ID()
423
+					: $question->ID();
424
+				$form_args['subsections'][ $identifier ] = $this->reg_form_question($registration, $question);
425
+			}
426
+		}
427
+		$form_args['subsections'] = apply_filters(
428
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information__question_group_reg_form__subsections_array',
429
+			$form_args['subsections'],
430
+			$registration,
431
+			$question_group,
432
+			$this
433
+		);
434
+		// filter for additional content after questions
435
+		$form_args['subsections']['reg_form_questions_after'] = new EE_Form_Section_HTML(
436
+			apply_filters(
437
+				'FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions',
438
+				'',
439
+				$registration,
440
+				$question_group,
441
+				$this
442
+			)
443
+		);
444
+		// d($form_args);
445
+		$question_group_reg_form = new EE_Form_Section_Proper($form_args);
446
+		return apply_filters(
447
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__question_group_reg_form',
448
+			$question_group_reg_form,
449
+			$registration,
450
+			$question_group,
451
+			$this
452
+		);
453
+	}
454
+
455
+
456
+	/**
457
+	 * @param EE_Question_Group $question_group
458
+	 * @return    EE_Form_Section_HTML
459
+	 */
460
+	private function _question_group_header(EE_Question_Group $question_group)
461
+	{
462
+		$html = '';
463
+		// group_name
464
+		if ($question_group->show_group_name() && $question_group->name() !== '') {
465
+			if ($this->checkout->admin_request) {
466
+				$html .= EEH_HTML::br();
467
+				$html .= EEH_HTML::h3(
468
+					$question_group->name(),
469
+					'',
470
+					'ee-reg-form-qstn-grp-title title',
471
+					'font-size: 1.3em; padding-left:0;'
472
+				);
473
+			} else {
474
+				$html .= EEH_HTML::h4(
475
+					$question_group->name(),
476
+					'',
477
+					'ee-reg-form-qstn-grp-title section-title'
478
+				);
479
+			}
480
+		}
481
+		// group_desc
482
+		if ($question_group->show_group_desc() && $question_group->desc() !== '') {
483
+			$html .= EEH_HTML::p(
484
+				$question_group->desc(),
485
+				'',
486
+				$this->checkout->admin_request
487
+					? 'ee-reg-form-qstn-grp-desc-pg'
488
+					: 'ee-reg-form-qstn-grp-desc-pg small-text lt-grey-text'
489
+			);
490
+		}
491
+		return new EE_Form_Section_HTML($html);
492
+	}
493
+
494
+
495
+	/**
496
+	 * @return    EE_Form_Section_Proper
497
+	 * @throws EE_Error
498
+	 * @throws InvalidArgumentException
499
+	 * @throws ReflectionException
500
+	 * @throws InvalidDataTypeException
501
+	 * @throws InvalidInterfaceException
502
+	 */
503
+	private function _copy_attendee_info_form()
504
+	{
505
+		// array of params to pass to parent constructor
506
+		return new EE_Form_Section_Proper(
507
+			array(
508
+				'subsections'     => $this->_copy_attendee_info_inputs(),
509
+				'layout_strategy' => new EE_Template_Layout(
510
+					array(
511
+						'layout_template_file'     => SPCO_REG_STEPS_PATH
512
+													  . $this->_slug
513
+													  . DS
514
+													  . 'copy_attendee_info.template.php',
515
+						'begin_template_file'      => null,
516
+						'input_template_file'      => null,
517
+						'subsection_template_file' => null,
518
+						'end_template_file'        => null,
519
+					)
520
+				),
521
+			)
522
+		);
523
+	}
524
+
525
+
526
+	/**
527
+	 * @return EE_Form_Section_HTML
528
+	 * @throws DomainException
529
+	 * @throws InvalidArgumentException
530
+	 * @throws InvalidDataTypeException
531
+	 * @throws InvalidInterfaceException
532
+	 */
533
+	private function _auto_copy_attendee_info()
534
+	{
535
+		return new EE_Form_Section_HTML(
536
+			EEH_Template::locate_template(
537
+				SPCO_REG_STEPS_PATH . $this->_slug . DS . '_auto_copy_attendee_info.template.php',
538
+				apply_filters(
539
+					'FHEE__EE_SPCO_Reg_Step_Attendee_Information__auto_copy_attendee_info__template_args',
540
+					array()
541
+				),
542
+				true,
543
+				true
544
+			)
545
+		);
546
+	}
547
+
548
+
549
+	/**
550
+	 * @return array
551
+	 * @throws EE_Error
552
+	 * @throws InvalidArgumentException
553
+	 * @throws ReflectionException
554
+	 * @throws InvalidDataTypeException
555
+	 * @throws InvalidInterfaceException
556
+	 */
557
+	private function _copy_attendee_info_inputs()
558
+	{
559
+		$copy_attendee_info_inputs = array();
560
+		$prev_ticket = null;
561
+		// grab the saved registrations from the transaction
562
+		$registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
563
+		foreach ($registrations as $registration) {
564
+			// for all  attendees other than the primary attendee
565
+			if ($registration instanceof EE_Registration && ! $registration->is_primary_registrant()) {
566
+				// if this is a new ticket OR if this is the very first additional attendee after the primary attendee
567
+				if ($registration->ticket()->ID() !== $prev_ticket) {
568
+					$item_name = $registration->ticket()->name();
569
+					$item_name .= $registration->ticket()->description() !== ''
570
+						? ' - ' . $registration->ticket()->description()
571
+						: '';
572
+					$copy_attendee_info_inputs[ 'spco_copy_attendee_chk[ticket-' . $registration->ticket()->ID(
573
+					) . ']' ] =
574
+						new EE_Form_Section_HTML(
575
+							'<h6 class="spco-copy-attendee-event-hdr">' . $item_name . '</h6>'
576
+						);
577
+					$prev_ticket = $registration->ticket()->ID();
578
+				}
579
+
580
+				$copy_attendee_info_inputs[ 'spco_copy_attendee_chk[' . $registration->ID() . ']' ] =
581
+					new EE_Checkbox_Multi_Input(
582
+						array(
583
+							$registration->ID() => sprintf(
584
+								esc_html_x('Attendee #%s', 'Attendee #123', 'event_espresso'),
585
+								$registration->count()
586
+							),
587
+						),
588
+						array(
589
+							'html_id'                 => 'spco-copy-attendee-chk-' . $registration->reg_url_link(),
590
+							'html_class'              => 'spco-copy-attendee-chk ee-do-not-validate',
591
+							'display_html_label_text' => false,
592
+						)
593
+					);
594
+			}
595
+		}
596
+		return $copy_attendee_info_inputs;
597
+	}
598
+
599
+
600
+	/**
601
+	 * @param EE_Registration $registration
602
+	 * @return    EE_Form_Input_Base
603
+	 * @throws EE_Error
604
+	 */
605
+	private function _additional_primary_registrant_inputs(EE_Registration $registration)
606
+	{
607
+		// generate hidden input
608
+		return new EE_Hidden_Input(
609
+			array(
610
+				'html_id' => 'primary_registrant',
611
+				'default' => $registration->reg_url_link(),
612
+			)
613
+		);
614
+	}
615
+
616
+
617
+	/**
618
+	 * @param EE_Registration $registration
619
+	 * @param EE_Question     $question
620
+	 * @return EE_Form_Input_Base
621
+	 * @throws EE_Error
622
+	 * @throws InvalidArgumentException
623
+	 * @throws InvalidDataTypeException
624
+	 * @throws InvalidInterfaceException
625
+	 * @throws ReflectionException
626
+	 */
627
+	public function reg_form_question(EE_Registration $registration, EE_Question $question)
628
+	{
629
+
630
+		// if this question was for an attendee detail, then check for that answer
631
+		$answer_value = EEM_Answer::instance()->get_attendee_property_answer_value(
632
+			$registration,
633
+			$question->system_ID()
634
+		);
635
+		$answer = $answer_value === null
636
+			? EEM_Answer::instance()->get_one(
637
+				array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
638
+			)
639
+			: null;
640
+		// if NOT returning to edit an existing registration
641
+		// OR if this question is for an attendee property
642
+		// OR we still don't have an EE_Answer object
643
+		if ($answer_value || ! $answer instanceof EE_Answer || ! $registration->reg_url_link()) {
644
+			// create an EE_Answer object for storing everything in
645
+			$answer = EE_Answer::new_instance(
646
+				array(
647
+					'QST_ID' => $question->ID(),
648
+					'REG_ID' => $registration->ID(),
649
+				)
650
+			);
651
+		}
652
+		// verify instance
653
+		if ($answer instanceof EE_Answer) {
654
+			if (! empty($answer_value)) {
655
+				$answer->set('ANS_value', $answer_value);
656
+			}
657
+			$answer->cache('Question', $question);
658
+			// remember system ID had a bug where sometimes it could be null
659
+			$answer_cache_id = $question->is_system_question()
660
+				? $question->system_ID() . '-' . $registration->reg_url_link()
661
+				: $question->ID() . '-' . $registration->reg_url_link();
662
+			$registration->cache('Answer', $answer, $answer_cache_id);
663
+		}
664
+		return $this->_generate_question_input($registration, $question, $answer);
665
+	}
666
+
667
+
668
+	/**
669
+	 * @param EE_Registration $registration
670
+	 * @param EE_Question     $question
671
+	 * @param                 $answer
672
+	 * @return EE_Form_Input_Base
673
+	 * @throws EE_Error
674
+	 * @throws InvalidArgumentException
675
+	 * @throws ReflectionException
676
+	 * @throws InvalidDataTypeException
677
+	 * @throws InvalidInterfaceException
678
+	 */
679
+	private function _generate_question_input(EE_Registration $registration, EE_Question $question, $answer)
680
+	{
681
+		$identifier = $question->is_system_question()
682
+			? $question->system_ID()
683
+			: $question->ID();
684
+		$this->_required_questions[ $identifier ] = $question->required() ? true : false;
685
+		add_filter(
686
+			'FHEE__EE_Question__generate_form_input__country_options',
687
+			array($this, 'use_cached_countries_for_form_input'),
688
+			10,
689
+			4
690
+		);
691
+		add_filter(
692
+			'FHEE__EE_Question__generate_form_input__state_options',
693
+			array($this, 'use_cached_states_for_form_input'),
694
+			10,
695
+			4
696
+		);
697
+		$input_constructor_args = array(
698
+			'html_name'        => 'ee_reg_qstn[' . $registration->ID() . '][' . $identifier . ']',
699
+			'html_id'          => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
700
+			'html_class'       => 'ee-reg-qstn ee-reg-qstn-' . $identifier,
701
+			'html_label_id'    => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
702
+			'html_label_class' => 'ee-reg-qstn',
703
+		);
704
+		$input_constructor_args['html_label_id'] .= '-lbl';
705
+		if ($answer instanceof EE_Answer && $answer->ID()) {
706
+			$input_constructor_args['html_name'] .= '[' . $answer->ID() . ']';
707
+			$input_constructor_args['html_id'] .= '-' . $answer->ID();
708
+			$input_constructor_args['html_label_id'] .= '-' . $answer->ID();
709
+		}
710
+		$form_input = $question->generate_form_input(
711
+			$registration,
712
+			$answer,
713
+			$input_constructor_args
714
+		);
715
+		remove_filter(
716
+			'FHEE__EE_Question__generate_form_input__country_options',
717
+			array($this, 'use_cached_countries_for_form_input')
718
+		);
719
+		remove_filter(
720
+			'FHEE__EE_Question__generate_form_input__state_options',
721
+			array($this, 'use_cached_states_for_form_input')
722
+		);
723
+		return $form_input;
724
+	}
725
+
726
+
727
+	/**
728
+	 * Gets the list of countries for the form input
729
+	 *
730
+	 * @param array|null      $countries_list
731
+	 * @param EE_Question     $question
732
+	 * @param EE_Registration $registration
733
+	 * @param EE_Answer       $answer
734
+	 * @return array 2d keys are country IDs, values are their names
735
+	 * @throws EE_Error
736
+	 * @throws InvalidArgumentException
737
+	 * @throws InvalidDataTypeException
738
+	 * @throws InvalidInterfaceException
739
+	 * @throws ReflectionException
740
+	 */
741
+	public function use_cached_countries_for_form_input(
742
+		$countries_list,
743
+		EE_Question $question = null,
744
+		EE_Registration $registration = null,
745
+		EE_Answer $answer = null
746
+	) {
747
+		$country_options = array('' => '');
748
+		// get possibly cached list of countries
749
+		$countries = $this->checkout->action === 'process_reg_step'
750
+			? EEM_Country::instance()->get_all_countries()
751
+			: EEM_Country::instance()->get_all_active_countries();
752
+		if (! empty($countries)) {
753
+			foreach ($countries as $country) {
754
+				if ($country instanceof EE_Country) {
755
+					$country_options[ $country->ID() ] = $country->name();
756
+				}
757
+			}
758
+		}
759
+		if ($question instanceof EE_Question && $registration instanceof EE_Registration) {
760
+			$answer = EEM_Answer::instance()->get_one(
761
+				array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
762
+			);
763
+		} else {
764
+			$answer = EE_Answer::new_instance();
765
+		}
766
+		$country_options = apply_filters(
767
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__country_options',
768
+			$country_options,
769
+			$this,
770
+			$registration,
771
+			$question,
772
+			$answer
773
+		);
774
+		return $country_options;
775
+	}
776
+
777
+
778
+	/**
779
+	 * Gets the list of states for the form input
780
+	 *
781
+	 * @param array|null      $states_list
782
+	 * @param EE_Question     $question
783
+	 * @param EE_Registration $registration
784
+	 * @param EE_Answer       $answer
785
+	 * @return array 2d keys are state IDs, values are their names
786
+	 * @throws EE_Error
787
+	 * @throws InvalidArgumentException
788
+	 * @throws InvalidDataTypeException
789
+	 * @throws InvalidInterfaceException
790
+	 * @throws ReflectionException
791
+	 */
792
+	public function use_cached_states_for_form_input(
793
+		$states_list,
794
+		EE_Question $question = null,
795
+		EE_Registration $registration = null,
796
+		EE_Answer $answer = null
797
+	) {
798
+		$state_options = array('' => array('' => ''));
799
+		$states = $this->checkout->action === 'process_reg_step'
800
+			? EEM_State::instance()->get_all_states()
801
+			: EEM_State::instance()->get_all_active_states();
802
+		if (! empty($states)) {
803
+			foreach ($states as $state) {
804
+				if ($state instanceof EE_State) {
805
+					$state_options[ $state->country()->name() ][ $state->ID() ] = $state->name();
806
+				}
807
+			}
808
+		}
809
+		$state_options = apply_filters(
810
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__state_options',
811
+			$state_options,
812
+			$this,
813
+			$registration,
814
+			$question,
815
+			$answer
816
+		);
817
+		return $state_options;
818
+	}
819
+
820
+
821
+	/********************************************************************************************************/
822
+	/****************************************  PROCESS REG STEP  ****************************************/
823
+	/********************************************************************************************************/
824
+
825
+
826
+	/**
827
+	 * @return bool
828
+	 * @throws EE_Error
829
+	 * @throws InvalidArgumentException
830
+	 * @throws ReflectionException
831
+	 * @throws RuntimeException
832
+	 * @throws InvalidDataTypeException
833
+	 * @throws InvalidInterfaceException
834
+	 */
835
+	public function process_reg_step()
836
+	{
837
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
838
+		// grab validated data from form
839
+		$valid_data = $this->checkout->current_step->valid_data();
840
+		// EEH_Debug_Tools::printr( $_REQUEST, '$_REQUEST', __FILE__, __LINE__ );
841
+		// EEH_Debug_Tools::printr( $valid_data, '$valid_data', __FILE__, __LINE__ );
842
+		// if we don't have any $valid_data then something went TERRIBLY WRONG !!!
843
+		if (empty($valid_data)) {
844
+			EE_Error::add_error(
845
+				esc_html__('No valid question responses were received.', 'event_espresso'),
846
+				__FILE__,
847
+				__FUNCTION__,
848
+				__LINE__
849
+			);
850
+			return false;
851
+		}
852
+		if (! $this->checkout->transaction instanceof EE_Transaction || ! $this->checkout->continue_reg) {
853
+			EE_Error::add_error(
854
+				esc_html__(
855
+					'A valid transaction could not be initiated for processing your registrations.',
856
+					'event_espresso'
857
+				),
858
+				__FILE__,
859
+				__FUNCTION__,
860
+				__LINE__
861
+			);
862
+			return false;
863
+		}
864
+		// get cached registrations
865
+		$registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
866
+		// verify we got the goods
867
+		if (empty($registrations)) {
868
+			// combine the old translated string with a new one, in order to not break translations
869
+			$error_message = esc_html__(
870
+				'Your form data could not be applied to any valid registrations.',
871
+				'event_espresso'
872
+			)
873
+			. sprintf(
874
+				esc_html_x(
875
+					'%3$sThis can sometimes happen if too much time has been taken to complete the registration process.%3$sPlease return to the %1$sEvent List%2$s and reselect your tickets. If the problem continues, please contact the site administrator.',
876
+					'(line break)This can sometimes happen if too much time has been taken to complete the registration process.(line break)Please return to the (link)Event List(end link) and reselect your tickets. If the problem continues, please contact the site administrator.',
877
+					'event_espresso'
878
+				),
879
+				'<a href="' . get_post_type_archive_link('espresso_events') . '" >',
880
+				'</a>',
881
+				'<br />'
882
+			);
883
+			EE_Error::add_error(
884
+				$error_message,
885
+				__FILE__,
886
+				__FUNCTION__,
887
+				__LINE__
888
+			);
889
+			return false;
890
+		}
891
+		// extract attendee info from form data and save to model objects
892
+		$registrations_processed = $this->_process_registrations($registrations, $valid_data);
893
+		// if first pass thru SPCO,
894
+		// then let's check processed registrations against the total number of tickets in the cart
895
+		if ($registrations_processed === false) {
896
+			// but return immediately if the previous step exited early due to errors
897
+			return false;
898
+		}
899
+		if (! $this->checkout->revisit && $registrations_processed !== $this->checkout->total_ticket_count) {
900
+			// generate a correctly translated string for all possible singular/plural combinations
901
+			if ($this->checkout->total_ticket_count === 1 && $registrations_processed !== 1) {
902
+				$error_msg = sprintf(
903
+					esc_html_x(
904
+						'There was %1$d ticket in the Event Queue, but %2$ds registrations were processed',
905
+						'There was 1 ticket in the Event Queue, but 2 registrations were processed',
906
+						'event_espresso'
907
+					),
908
+					$this->checkout->total_ticket_count,
909
+					$registrations_processed
910
+				);
911
+			} elseif ($this->checkout->total_ticket_count !== 1 && $registrations_processed === 1) {
912
+				$error_msg = sprintf(
913
+					esc_html_x(
914
+						'There was a total of %1$d tickets in the Event Queue, but only %2$ds registration was processed',
915
+						'There was a total of 2 tickets in the Event Queue, but only 1 registration was processed',
916
+						'event_espresso'
917
+					),
918
+					$this->checkout->total_ticket_count,
919
+					$registrations_processed
920
+				);
921
+			} else {
922
+				$error_msg = sprintf(
923
+					esc_html__(
924
+						'There was a total of 2 tickets in the Event Queue, but 2 registrations were processed',
925
+						'event_espresso'
926
+					),
927
+					$this->checkout->total_ticket_count,
928
+					$registrations_processed
929
+				);
930
+			}
931
+			EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
932
+			return false;
933
+		}
934
+		// mark this reg step as completed
935
+		$this->set_completed();
936
+		$this->_set_success_message(
937
+			esc_html__('The Attendee Information Step has been successfully completed.', 'event_espresso')
938
+		);
939
+		// do action in case a plugin wants to do something with the data submitted in step 1.
940
+		// passes EE_Single_Page_Checkout, and it's posted data
941
+		do_action('AHEE__EE_Single_Page_Checkout__process_attendee_information__end', $this, $valid_data);
942
+		return true;
943
+	}
944
+
945
+
946
+	/**
947
+	 *    _process_registrations
948
+	 *
949
+	 * @param EE_Registration[] $registrations
950
+	 * @param array[][]         $valid_data
951
+	 * @return bool|int
952
+	 * @throws EntityNotFoundException
953
+	 * @throws EE_Error
954
+	 * @throws InvalidArgumentException
955
+	 * @throws ReflectionException
956
+	 * @throws RuntimeException
957
+	 * @throws InvalidDataTypeException
958
+	 * @throws InvalidInterfaceException
959
+	 */
960
+	private function _process_registrations($registrations = array(), $valid_data = array())
961
+	{
962
+		// load resources and set some defaults
963
+		EE_Registry::instance()->load_model('Attendee');
964
+		// holder for primary registrant attendee object
965
+		$this->checkout->primary_attendee_obj = null;
966
+		// array for tracking reg form data for the primary registrant
967
+		$primary_registrant = array(
968
+			'line_item_id' => null,
969
+		);
970
+		$copy_primary = false;
971
+		// reg form sections that do not contain inputs
972
+		$non_input_form_sections = array(
973
+			'primary_registrant',
974
+			'additional_attendee_reg_info',
975
+			'spco_copy_attendee_chk',
976
+		);
977
+		// attendee counter
978
+		$att_nmbr = 0;
979
+		// grab the saved registrations from the transaction
980
+		foreach ($registrations as $registration) {
981
+			// verify EE_Registration object
982
+			if (! $registration instanceof EE_Registration) {
983
+				EE_Error::add_error(
984
+					esc_html__(
985
+						'An invalid Registration object was discovered when attempting to process your registration information.',
986
+						'event_espresso'
987
+					),
988
+					__FILE__,
989
+					__FUNCTION__,
990
+					__LINE__
991
+				);
992
+				return false;
993
+			}
994
+			/** @var string $reg_url_link */
995
+			$reg_url_link = $registration->reg_url_link();
996
+			// reg_url_link exists ?
997
+			if (! empty($reg_url_link)) {
998
+				// should this registration be processed during this visit ?
999
+				if ($this->checkout->visit_allows_processing_of_this_registration($registration)) {
1000
+					// if NOT revisiting, then let's save the registration now,
1001
+					// so that we have a REG_ID to use when generating other objects
1002
+					if (! $this->checkout->revisit) {
1003
+						$registration->save();
1004
+					}
1005
+					/**
1006
+					 * This allows plugins to trigger a fail on processing of a
1007
+					 * registration for any conditions they may have for it to pass.
1008
+					 *
1009
+					 * @var bool   if true is returned by the plugin then the
1010
+					 *            registration processing is halted.
1011
+					 */
1012
+					if (apply_filters(
1013
+						'FHEE__EE_SPCO_Reg_Step_Attendee_Information___process_registrations__pre_registration_process',
1014
+						false,
1015
+						$att_nmbr,
1016
+						$registration,
1017
+						$registrations,
1018
+						$valid_data,
1019
+						$this
1020
+					)) {
1021
+						return false;
1022
+					}
1023
+
1024
+					// Houston, we have a registration!
1025
+					$att_nmbr++;
1026
+					$this->_attendee_data[ $reg_url_link ] = array();
1027
+					// grab any existing related answer objects
1028
+					$this->_registration_answers = $registration->answers();
1029
+					// unset( $valid_data[ $reg_url_link ]['additional_attendee_reg_info'] );
1030
+					if (isset($valid_data[ $reg_url_link ])) {
1031
+						// do we need to copy basic info from primary attendee ?
1032
+						$copy_primary = isset($valid_data[ $reg_url_link ]['additional_attendee_reg_info'])
1033
+										&& absint($valid_data[ $reg_url_link ]['additional_attendee_reg_info']) === 0;
1034
+						// filter form input data for this registration
1035
+						$valid_data[ $reg_url_link ] = (array) apply_filters(
1036
+							'FHEE__EE_Single_Page_Checkout__process_attendee_information__valid_data_line_item',
1037
+							$valid_data[ $reg_url_link ]
1038
+						);
1039
+						if (isset($valid_data['primary_attendee'])) {
1040
+							$primary_registrant['line_item_id'] = ! empty($valid_data['primary_attendee'])
1041
+								? $valid_data['primary_attendee']
1042
+								: false;
1043
+							unset($valid_data['primary_attendee']);
1044
+						}
1045
+						// now loop through our array of valid post data && process attendee reg forms
1046
+						foreach ($valid_data[ $reg_url_link ] as $form_section => $form_inputs) {
1047
+							if (! in_array($form_section, $non_input_form_sections, true)) {
1048
+								foreach ($form_inputs as $form_input => $input_value) {
1049
+									// \EEH_Debug_Tools::printr( $input_value, $form_input, __FILE__, __LINE__ );
1050
+									// check for critical inputs
1051
+									if (! $this->_verify_critical_attendee_details_are_set_and_validate_email(
1052
+										$form_input,
1053
+										$input_value
1054
+									)
1055
+									) {
1056
+										return false;
1057
+									}
1058
+									// store a bit of data about the primary attendee
1059
+									if ($att_nmbr === 1
1060
+										&& ! empty($input_value)
1061
+										&& $reg_url_link === $primary_registrant['line_item_id']
1062
+									) {
1063
+										$primary_registrant[ $form_input ] = $input_value;
1064
+									} elseif ($copy_primary
1065
+											  && $input_value === null
1066
+											  && isset($primary_registrant[ $form_input ])
1067
+									) {
1068
+										$input_value = $primary_registrant[ $form_input ];
1069
+									}
1070
+									// now attempt to save the input data
1071
+									if (! $this->_save_registration_form_input(
1072
+										$registration,
1073
+										$form_input,
1074
+										$input_value
1075
+									)
1076
+									) {
1077
+										EE_Error::add_error(
1078
+											sprintf(
1079
+												esc_html_x(
1080
+													'Unable to save registration form data for the form input: "%1$s" with the submitted value: "%2$s"',
1081
+													'Unable to save registration form data for the form input: "form input name" with the submitted value: "form input value"',
1082
+													'event_espresso'
1083
+												),
1084
+												$form_input,
1085
+												$input_value
1086
+											),
1087
+											__FILE__,
1088
+											__FUNCTION__,
1089
+											__LINE__
1090
+										);
1091
+										return false;
1092
+									}
1093
+								}
1094
+							}
1095
+						}  // end of foreach ( $valid_data[ $reg_url_link ] as $form_section => $form_inputs )
1096
+					}
1097
+					// EEH_Debug_Tools::printr( $this->_attendee_data, '$this->_attendee_data', __FILE__, __LINE__ );
1098
+					// this registration does not require additional attendee information ?
1099
+					if ($copy_primary
1100
+						&& $att_nmbr > 1
1101
+						&& $this->checkout->primary_attendee_obj instanceof EE_Attendee
1102
+					) {
1103
+						// just copy the primary registrant
1104
+						$attendee = $this->checkout->primary_attendee_obj;
1105
+					} else {
1106
+						// ensure critical details are set for additional attendees
1107
+						$this->_attendee_data[ $reg_url_link ] = $att_nmbr > 1
1108
+							? $this->_copy_critical_attendee_details_from_primary_registrant(
1109
+								$this->_attendee_data[ $reg_url_link ]
1110
+							)
1111
+							: $this->_attendee_data[ $reg_url_link ];
1112
+						// execute create attendee command (which may return an existing attendee)
1113
+						$attendee = EE_Registry::instance()->BUS->execute(
1114
+							new CreateAttendeeCommand(
1115
+								$this->_attendee_data[ $reg_url_link ],
1116
+								$registration
1117
+							)
1118
+						);
1119
+						// who's #1 ?
1120
+						if ($att_nmbr === 1) {
1121
+							$this->checkout->primary_attendee_obj = $attendee;
1122
+						}
1123
+					}
1124
+					// EEH_Debug_Tools::printr( $attendee, '$attendee', __FILE__, __LINE__ );
1125
+					// add relation to registration, set attendee ID, and cache attendee
1126
+					$this->_associate_attendee_with_registration($registration, $attendee);
1127
+					// \EEH_Debug_Tools::printr( $registration, '$registration', __FILE__, __LINE__ );
1128
+					if (! $registration->attendee() instanceof EE_Attendee) {
1129
+						EE_Error::add_error(
1130
+							sprintf(
1131
+								esc_html_x(
1132
+									'Registration %s has an invalid or missing Attendee object.',
1133
+									'Registration 123-456-789 has an invalid or missing Attendee object.',
1134
+									'event_espresso'
1135
+								),
1136
+								$reg_url_link
1137
+							),
1138
+							__FILE__,
1139
+							__FUNCTION__,
1140
+							__LINE__
1141
+						);
1142
+						return false;
1143
+					}
1144
+					/** @type EE_Registration_Processor $registration_processor */
1145
+					$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
1146
+					// at this point, we should have enough details about the registrant to consider the registration
1147
+					// NOT incomplete
1148
+					$registration_processor->toggle_incomplete_registration_status_to_default(
1149
+						$registration,
1150
+						false,
1151
+						new Context(
1152
+							'spco_reg_step_attendee_information_process_registrations',
1153
+							esc_html__(
1154
+								'Finished populating registration with details from the registration form after submitting the Attendee Information Reg Step.',
1155
+								'event_espresso'
1156
+							)
1157
+						)
1158
+					);
1159
+					// we can also consider the TXN to not have been failed, so temporarily upgrade it's status to
1160
+					// abandoned
1161
+					$this->checkout->transaction->toggle_failed_transaction_status();
1162
+					// if we've gotten this far, then let's save what we have
1163
+					$registration->save();
1164
+					// add relation between TXN and registration
1165
+					$this->_associate_registration_with_transaction($registration);
1166
+				}
1167
+			} else {
1168
+				EE_Error::add_error(
1169
+					esc_html__(
1170
+						'An invalid or missing line item ID was encountered while attempting to process the registration form.',
1171
+						'event_espresso'
1172
+					),
1173
+					__FILE__,
1174
+					__FUNCTION__,
1175
+					__LINE__
1176
+				);
1177
+				// remove malformed data
1178
+				unset($valid_data[ $reg_url_link ]);
1179
+				return false;
1180
+			}
1181
+		} // end of foreach ( $this->checkout->transaction->registrations()  as $registration )
1182
+		return $att_nmbr;
1183
+	}
1184
+
1185
+
1186
+	/**
1187
+	 *    _save_registration_form_input
1188
+	 *
1189
+	 * @param EE_Registration $registration
1190
+	 * @param string          $form_input
1191
+	 * @param string          $input_value
1192
+	 * @return bool
1193
+	 * @throws EE_Error
1194
+	 * @throws InvalidArgumentException
1195
+	 * @throws InvalidDataTypeException
1196
+	 * @throws InvalidInterfaceException
1197
+	 * @throws ReflectionException
1198
+	 */
1199
+	private function _save_registration_form_input(
1200
+		EE_Registration $registration,
1201
+		$form_input = '',
1202
+		$input_value = ''
1203
+	) {
1204
+		// \EEH_Debug_Tools::printr( __FUNCTION__, __CLASS__, __FILE__, __LINE__, 2 );
1205
+		// \EEH_Debug_Tools::printr( $form_input, '$form_input', __FILE__, __LINE__ );
1206
+		// \EEH_Debug_Tools::printr( $input_value, '$input_value', __FILE__, __LINE__ );
1207
+		// allow for plugins to hook in and do their own processing of the form input.
1208
+		// For plugins to bypass normal processing here, they just need to return a boolean value.
1209
+		if (apply_filters(
1210
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___save_registration_form_input',
1211
+			false,
1212
+			$registration,
1213
+			$form_input,
1214
+			$input_value,
1215
+			$this
1216
+		)) {
1217
+			return true;
1218
+		}
1219
+		/*
1220 1220
          * $answer_cache_id is the key used to find the EE_Answer we want
1221 1221
          * @see https://events.codebasehq.com/projects/event-espresso/tickets/10477
1222 1222
          */
1223
-        $answer_cache_id = $this->checkout->reg_url_link
1224
-            ? $form_input . '-' . $registration->reg_url_link()
1225
-            : $form_input;
1226
-        $answer_is_obj = isset($this->_registration_answers[ $answer_cache_id ])
1227
-                         && $this->_registration_answers[ $answer_cache_id ] instanceof EE_Answer;
1228
-        // rename form_inputs if they are EE_Attendee properties
1229
-        switch ((string) $form_input) {
1230
-            case 'state':
1231
-            case 'STA_ID':
1232
-                $attendee_property = true;
1233
-                $form_input = 'STA_ID';
1234
-                break;
1235
-
1236
-            case 'country':
1237
-            case 'CNT_ISO':
1238
-                $attendee_property = true;
1239
-                $form_input = 'CNT_ISO';
1240
-                break;
1241
-
1242
-            default:
1243
-                $ATT_input = 'ATT_' . $form_input;
1244
-                // EEH_Debug_Tools::printr( $ATT_input, '$ATT_input', __FILE__, __LINE__ );
1245
-                $attendee_property = EEM_Attendee::instance()->has_field($ATT_input) ? true : false;
1246
-                $form_input = $attendee_property ? 'ATT_' . $form_input : $form_input;
1247
-        }
1248
-        // EEH_Debug_Tools::printr( $answer_cache_id, '$answer_cache_id', __FILE__, __LINE__ );
1249
-        // EEH_Debug_Tools::printr( $attendee_property, '$attendee_property', __FILE__, __LINE__ );
1250
-        // EEH_Debug_Tools::printr( $answer_is_obj, '$answer_is_obj', __FILE__, __LINE__ );
1251
-        // if this form input has a corresponding attendee property
1252
-        if ($attendee_property) {
1253
-            $this->_attendee_data[ $registration->reg_url_link() ][ $form_input ] = $input_value;
1254
-            if ($answer_is_obj) {
1255
-                // and delete the corresponding answer since we won't be storing this data in that object
1256
-                $registration->_remove_relation_to($this->_registration_answers[ $answer_cache_id ], 'Answer');
1257
-                $this->_registration_answers[ $answer_cache_id ]->delete_permanently();
1258
-            }
1259
-            return true;
1260
-        }
1261
-        if ($answer_is_obj) {
1262
-            // save this data to the answer object
1263
-            $this->_registration_answers[ $answer_cache_id ]->set_value($input_value);
1264
-            $result = $this->_registration_answers[ $answer_cache_id ]->save();
1265
-            return $result !== false;
1266
-        }
1267
-        foreach ($this->_registration_answers as $answer) {
1268
-            if ($answer instanceof EE_Answer && $answer->question_ID() === $answer_cache_id) {
1269
-                $answer->set_value($input_value);
1270
-                $result = $answer->save();
1271
-                return $result !== false;
1272
-            }
1273
-        }
1274
-        return false;
1275
-    }
1276
-
1277
-
1278
-    /**
1279
-     *    _verify_critical_attendee_details_are_set
1280
-     *
1281
-     * @param string $form_input
1282
-     * @param string $input_value
1283
-     * @return boolean
1284
-     */
1285
-    private function _verify_critical_attendee_details_are_set_and_validate_email(
1286
-        $form_input = '',
1287
-        $input_value = ''
1288
-    ) {
1289
-        if (empty($input_value)) {
1290
-            // if the form input isn't marked as being required, then just return
1291
-            if (! isset($this->_required_questions[ $form_input ]) || ! $this->_required_questions[ $form_input ]) {
1292
-                return true;
1293
-            }
1294
-            switch ($form_input) {
1295
-                case 'fname':
1296
-                    EE_Error::add_error(
1297
-                        esc_html__('First Name is a required value.', 'event_espresso'),
1298
-                        __FILE__,
1299
-                        __FUNCTION__,
1300
-                        __LINE__
1301
-                    );
1302
-                    return false;
1303
-                    break;
1304
-                case 'lname':
1305
-                    EE_Error::add_error(
1306
-                        esc_html__('Last Name is a required value.', 'event_espresso'),
1307
-                        __FILE__,
1308
-                        __FUNCTION__,
1309
-                        __LINE__
1310
-                    );
1311
-                    return false;
1312
-                    break;
1313
-                case 'email':
1314
-                    EE_Error::add_error(
1315
-                        esc_html__('Please enter a valid email address.', 'event_espresso'),
1316
-                        __FILE__,
1317
-                        __FUNCTION__,
1318
-                        __LINE__
1319
-                    );
1320
-                    return false;
1321
-                    break;
1322
-            }
1323
-        }
1324
-        return true;
1325
-    }
1326
-
1327
-
1328
-    /**
1329
-     *    _associate_attendee_with_registration
1330
-     *
1331
-     * @param EE_Registration $registration
1332
-     * @param EE_Attendee     $attendee
1333
-     * @return void
1334
-     * @throws EE_Error
1335
-     * @throws InvalidArgumentException
1336
-     * @throws ReflectionException
1337
-     * @throws RuntimeException
1338
-     * @throws InvalidDataTypeException
1339
-     * @throws InvalidInterfaceException
1340
-     */
1341
-    private function _associate_attendee_with_registration(EE_Registration $registration, EE_Attendee $attendee)
1342
-    {
1343
-        // add relation to attendee
1344
-        $registration->_add_relation_to($attendee, 'Attendee');
1345
-        $registration->set_attendee_id($attendee->ID());
1346
-        $registration->update_cache_after_object_save('Attendee', $attendee);
1347
-    }
1348
-
1349
-
1350
-    /**
1351
-     *    _associate_registration_with_transaction
1352
-     *
1353
-     * @param EE_Registration $registration
1354
-     * @return void
1355
-     * @throws EE_Error
1356
-     * @throws InvalidArgumentException
1357
-     * @throws ReflectionException
1358
-     * @throws InvalidDataTypeException
1359
-     * @throws InvalidInterfaceException
1360
-     */
1361
-    private function _associate_registration_with_transaction(EE_Registration $registration)
1362
-    {
1363
-        // add relation to registration
1364
-        $this->checkout->transaction->_add_relation_to($registration, 'Registration');
1365
-        $this->checkout->transaction->update_cache_after_object_save('Registration', $registration);
1366
-    }
1367
-
1368
-
1369
-    /**
1370
-     *    _copy_critical_attendee_details_from_primary_registrant
1371
-     *    ensures that all attendees at least have data for first name, last name, and email address
1372
-     *
1373
-     * @param array $attendee_data
1374
-     * @return array
1375
-     * @throws EE_Error
1376
-     * @throws InvalidArgumentException
1377
-     * @throws ReflectionException
1378
-     * @throws InvalidDataTypeException
1379
-     * @throws InvalidInterfaceException
1380
-     */
1381
-    private function _copy_critical_attendee_details_from_primary_registrant($attendee_data = array())
1382
-    {
1383
-        // bare minimum critical details include first name, last name, email address
1384
-        $critical_attendee_details = array('ATT_fname', 'ATT_lname', 'ATT_email');
1385
-        // add address info to critical details?
1386
-        if (apply_filters(
1387
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information__merge_address_details_with_critical_attendee_details',
1388
-            false
1389
-        )) {
1390
-            $address_details = array(
1391
-                'ATT_address',
1392
-                'ATT_address2',
1393
-                'ATT_city',
1394
-                'STA_ID',
1395
-                'CNT_ISO',
1396
-                'ATT_zip',
1397
-                'ATT_phone',
1398
-            );
1399
-            $critical_attendee_details = array_merge($critical_attendee_details, $address_details);
1400
-        }
1401
-        foreach ($critical_attendee_details as $critical_attendee_detail) {
1402
-            if (! isset($attendee_data[ $critical_attendee_detail ])
1403
-                || empty($attendee_data[ $critical_attendee_detail ])
1404
-            ) {
1405
-                $attendee_data[ $critical_attendee_detail ] = $this->checkout->primary_attendee_obj->get(
1406
-                    $critical_attendee_detail
1407
-                );
1408
-            }
1409
-        }
1410
-        return $attendee_data;
1411
-    }
1412
-
1413
-
1414
-    /**
1415
-     *    update_reg_step
1416
-     *    this is the final step after a user  revisits the site to edit their attendee information
1417
-     *    this gets called AFTER the process_reg_step() method above
1418
-     *
1419
-     * @return bool
1420
-     * @throws EE_Error
1421
-     * @throws InvalidArgumentException
1422
-     * @throws ReflectionException
1423
-     * @throws RuntimeException
1424
-     * @throws InvalidDataTypeException
1425
-     * @throws InvalidInterfaceException
1426
-     */
1427
-    public function update_reg_step()
1428
-    {
1429
-        // save everything
1430
-        if ($this->process_reg_step()) {
1431
-            $this->checkout->redirect = true;
1432
-            $this->checkout->redirect_url = add_query_arg(
1433
-                array(
1434
-                    'e_reg_url_link' => $this->checkout->reg_url_link,
1435
-                    'revisit'        => true,
1436
-                ),
1437
-                $this->checkout->thank_you_page_url
1438
-            );
1439
-            $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1440
-            return true;
1441
-        }
1442
-        return false;
1443
-    }
1223
+		$answer_cache_id = $this->checkout->reg_url_link
1224
+			? $form_input . '-' . $registration->reg_url_link()
1225
+			: $form_input;
1226
+		$answer_is_obj = isset($this->_registration_answers[ $answer_cache_id ])
1227
+						 && $this->_registration_answers[ $answer_cache_id ] instanceof EE_Answer;
1228
+		// rename form_inputs if they are EE_Attendee properties
1229
+		switch ((string) $form_input) {
1230
+			case 'state':
1231
+			case 'STA_ID':
1232
+				$attendee_property = true;
1233
+				$form_input = 'STA_ID';
1234
+				break;
1235
+
1236
+			case 'country':
1237
+			case 'CNT_ISO':
1238
+				$attendee_property = true;
1239
+				$form_input = 'CNT_ISO';
1240
+				break;
1241
+
1242
+			default:
1243
+				$ATT_input = 'ATT_' . $form_input;
1244
+				// EEH_Debug_Tools::printr( $ATT_input, '$ATT_input', __FILE__, __LINE__ );
1245
+				$attendee_property = EEM_Attendee::instance()->has_field($ATT_input) ? true : false;
1246
+				$form_input = $attendee_property ? 'ATT_' . $form_input : $form_input;
1247
+		}
1248
+		// EEH_Debug_Tools::printr( $answer_cache_id, '$answer_cache_id', __FILE__, __LINE__ );
1249
+		// EEH_Debug_Tools::printr( $attendee_property, '$attendee_property', __FILE__, __LINE__ );
1250
+		// EEH_Debug_Tools::printr( $answer_is_obj, '$answer_is_obj', __FILE__, __LINE__ );
1251
+		// if this form input has a corresponding attendee property
1252
+		if ($attendee_property) {
1253
+			$this->_attendee_data[ $registration->reg_url_link() ][ $form_input ] = $input_value;
1254
+			if ($answer_is_obj) {
1255
+				// and delete the corresponding answer since we won't be storing this data in that object
1256
+				$registration->_remove_relation_to($this->_registration_answers[ $answer_cache_id ], 'Answer');
1257
+				$this->_registration_answers[ $answer_cache_id ]->delete_permanently();
1258
+			}
1259
+			return true;
1260
+		}
1261
+		if ($answer_is_obj) {
1262
+			// save this data to the answer object
1263
+			$this->_registration_answers[ $answer_cache_id ]->set_value($input_value);
1264
+			$result = $this->_registration_answers[ $answer_cache_id ]->save();
1265
+			return $result !== false;
1266
+		}
1267
+		foreach ($this->_registration_answers as $answer) {
1268
+			if ($answer instanceof EE_Answer && $answer->question_ID() === $answer_cache_id) {
1269
+				$answer->set_value($input_value);
1270
+				$result = $answer->save();
1271
+				return $result !== false;
1272
+			}
1273
+		}
1274
+		return false;
1275
+	}
1276
+
1277
+
1278
+	/**
1279
+	 *    _verify_critical_attendee_details_are_set
1280
+	 *
1281
+	 * @param string $form_input
1282
+	 * @param string $input_value
1283
+	 * @return boolean
1284
+	 */
1285
+	private function _verify_critical_attendee_details_are_set_and_validate_email(
1286
+		$form_input = '',
1287
+		$input_value = ''
1288
+	) {
1289
+		if (empty($input_value)) {
1290
+			// if the form input isn't marked as being required, then just return
1291
+			if (! isset($this->_required_questions[ $form_input ]) || ! $this->_required_questions[ $form_input ]) {
1292
+				return true;
1293
+			}
1294
+			switch ($form_input) {
1295
+				case 'fname':
1296
+					EE_Error::add_error(
1297
+						esc_html__('First Name is a required value.', 'event_espresso'),
1298
+						__FILE__,
1299
+						__FUNCTION__,
1300
+						__LINE__
1301
+					);
1302
+					return false;
1303
+					break;
1304
+				case 'lname':
1305
+					EE_Error::add_error(
1306
+						esc_html__('Last Name is a required value.', 'event_espresso'),
1307
+						__FILE__,
1308
+						__FUNCTION__,
1309
+						__LINE__
1310
+					);
1311
+					return false;
1312
+					break;
1313
+				case 'email':
1314
+					EE_Error::add_error(
1315
+						esc_html__('Please enter a valid email address.', 'event_espresso'),
1316
+						__FILE__,
1317
+						__FUNCTION__,
1318
+						__LINE__
1319
+					);
1320
+					return false;
1321
+					break;
1322
+			}
1323
+		}
1324
+		return true;
1325
+	}
1326
+
1327
+
1328
+	/**
1329
+	 *    _associate_attendee_with_registration
1330
+	 *
1331
+	 * @param EE_Registration $registration
1332
+	 * @param EE_Attendee     $attendee
1333
+	 * @return void
1334
+	 * @throws EE_Error
1335
+	 * @throws InvalidArgumentException
1336
+	 * @throws ReflectionException
1337
+	 * @throws RuntimeException
1338
+	 * @throws InvalidDataTypeException
1339
+	 * @throws InvalidInterfaceException
1340
+	 */
1341
+	private function _associate_attendee_with_registration(EE_Registration $registration, EE_Attendee $attendee)
1342
+	{
1343
+		// add relation to attendee
1344
+		$registration->_add_relation_to($attendee, 'Attendee');
1345
+		$registration->set_attendee_id($attendee->ID());
1346
+		$registration->update_cache_after_object_save('Attendee', $attendee);
1347
+	}
1348
+
1349
+
1350
+	/**
1351
+	 *    _associate_registration_with_transaction
1352
+	 *
1353
+	 * @param EE_Registration $registration
1354
+	 * @return void
1355
+	 * @throws EE_Error
1356
+	 * @throws InvalidArgumentException
1357
+	 * @throws ReflectionException
1358
+	 * @throws InvalidDataTypeException
1359
+	 * @throws InvalidInterfaceException
1360
+	 */
1361
+	private function _associate_registration_with_transaction(EE_Registration $registration)
1362
+	{
1363
+		// add relation to registration
1364
+		$this->checkout->transaction->_add_relation_to($registration, 'Registration');
1365
+		$this->checkout->transaction->update_cache_after_object_save('Registration', $registration);
1366
+	}
1367
+
1368
+
1369
+	/**
1370
+	 *    _copy_critical_attendee_details_from_primary_registrant
1371
+	 *    ensures that all attendees at least have data for first name, last name, and email address
1372
+	 *
1373
+	 * @param array $attendee_data
1374
+	 * @return array
1375
+	 * @throws EE_Error
1376
+	 * @throws InvalidArgumentException
1377
+	 * @throws ReflectionException
1378
+	 * @throws InvalidDataTypeException
1379
+	 * @throws InvalidInterfaceException
1380
+	 */
1381
+	private function _copy_critical_attendee_details_from_primary_registrant($attendee_data = array())
1382
+	{
1383
+		// bare minimum critical details include first name, last name, email address
1384
+		$critical_attendee_details = array('ATT_fname', 'ATT_lname', 'ATT_email');
1385
+		// add address info to critical details?
1386
+		if (apply_filters(
1387
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information__merge_address_details_with_critical_attendee_details',
1388
+			false
1389
+		)) {
1390
+			$address_details = array(
1391
+				'ATT_address',
1392
+				'ATT_address2',
1393
+				'ATT_city',
1394
+				'STA_ID',
1395
+				'CNT_ISO',
1396
+				'ATT_zip',
1397
+				'ATT_phone',
1398
+			);
1399
+			$critical_attendee_details = array_merge($critical_attendee_details, $address_details);
1400
+		}
1401
+		foreach ($critical_attendee_details as $critical_attendee_detail) {
1402
+			if (! isset($attendee_data[ $critical_attendee_detail ])
1403
+				|| empty($attendee_data[ $critical_attendee_detail ])
1404
+			) {
1405
+				$attendee_data[ $critical_attendee_detail ] = $this->checkout->primary_attendee_obj->get(
1406
+					$critical_attendee_detail
1407
+				);
1408
+			}
1409
+		}
1410
+		return $attendee_data;
1411
+	}
1412
+
1413
+
1414
+	/**
1415
+	 *    update_reg_step
1416
+	 *    this is the final step after a user  revisits the site to edit their attendee information
1417
+	 *    this gets called AFTER the process_reg_step() method above
1418
+	 *
1419
+	 * @return bool
1420
+	 * @throws EE_Error
1421
+	 * @throws InvalidArgumentException
1422
+	 * @throws ReflectionException
1423
+	 * @throws RuntimeException
1424
+	 * @throws InvalidDataTypeException
1425
+	 * @throws InvalidInterfaceException
1426
+	 */
1427
+	public function update_reg_step()
1428
+	{
1429
+		// save everything
1430
+		if ($this->process_reg_step()) {
1431
+			$this->checkout->redirect = true;
1432
+			$this->checkout->redirect_url = add_query_arg(
1433
+				array(
1434
+					'e_reg_url_link' => $this->checkout->reg_url_link,
1435
+					'revisit'        => true,
1436
+				),
1437
+				$this->checkout->thank_you_page_url
1438
+			);
1439
+			$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1440
+			return true;
1441
+		}
1442
+		return false;
1443
+	}
1444 1444
 }
Please login to merge, or discard this patch.
core/domain/services/registration/CopyRegistrationService.php 1 patch
Indentation   +142 added lines, -142 removed lines patch added patch discarded remove patch
@@ -27,153 +27,153 @@
 block discarded – undo
27 27
 {
28 28
 
29 29
 
30
-    /**
31
-     * @param EE_Registration $target_registration
32
-     * @param EE_Registration $registration_to_copy
33
-     * @return bool
34
-     * @throws UnexpectedEntityException
35
-     * @throws EntityNotFoundException
36
-     * @throws RuntimeException
37
-     * @throws EE_Error
38
-     */
39
-    public function copyRegistrationDetails(
40
-        EE_Registration $target_registration,
41
-        EE_Registration $registration_to_copy
42
-    ) {
43
-        // copy attendee
44
-        $target_registration->set_attendee_id($registration_to_copy->attendee_ID());
45
-        $target_registration->updateStatusBasedOnTotalPaid(false);
46
-        $target_registration->save();
47
-        // get answers to previous reg questions
48
-        $answers = $this->reindexAnswersByQuestionId($registration_to_copy->answers());
49
-        // get questions to new event reg form
50
-        $new_event = $target_registration->event();
51
-        $field_name = 'Event_Question_Group.'
52
-            . EEM_Event_Question_Group::instance()->field_name_for_category(
53
-                $registration_to_copy->is_primary_registrant()
54
-            );
55
-        $question_groups = $new_event->question_groups([
56
-                [
57
-                    'Event.EVT_ID' => $new_event->ID(),
58
-                    $field_name => true,
59
-                ],
60
-                'order_by' => ['QSG_order' => 'ASC'],
61
-            ]
30
+	/**
31
+	 * @param EE_Registration $target_registration
32
+	 * @param EE_Registration $registration_to_copy
33
+	 * @return bool
34
+	 * @throws UnexpectedEntityException
35
+	 * @throws EntityNotFoundException
36
+	 * @throws RuntimeException
37
+	 * @throws EE_Error
38
+	 */
39
+	public function copyRegistrationDetails(
40
+		EE_Registration $target_registration,
41
+		EE_Registration $registration_to_copy
42
+	) {
43
+		// copy attendee
44
+		$target_registration->set_attendee_id($registration_to_copy->attendee_ID());
45
+		$target_registration->updateStatusBasedOnTotalPaid(false);
46
+		$target_registration->save();
47
+		// get answers to previous reg questions
48
+		$answers = $this->reindexAnswersByQuestionId($registration_to_copy->answers());
49
+		// get questions to new event reg form
50
+		$new_event = $target_registration->event();
51
+		$field_name = 'Event_Question_Group.'
52
+			. EEM_Event_Question_Group::instance()->field_name_for_category(
53
+				$registration_to_copy->is_primary_registrant()
54
+			);
55
+		$question_groups = $new_event->question_groups([
56
+				[
57
+					'Event.EVT_ID' => $new_event->ID(),
58
+					$field_name => true,
59
+				],
60
+				'order_by' => ['QSG_order' => 'ASC'],
61
+			]
62 62
 
63
-        );
64
-        foreach ($question_groups as $question_group) {
65
-            if ($question_group instanceof \EE_Question_Group) {
66
-                foreach ($question_group->questions() as $question) {
67
-                    if ($question instanceof EE_Question) {
68
-                        $this->generateNewAnswer(
69
-                            $question,
70
-                            $target_registration,
71
-                            $answers
72
-                        );
73
-                    }
74
-                }
75
-            }
76
-        }
77
-        return true;
78
-    }
63
+		);
64
+		foreach ($question_groups as $question_group) {
65
+			if ($question_group instanceof \EE_Question_Group) {
66
+				foreach ($question_group->questions() as $question) {
67
+					if ($question instanceof EE_Question) {
68
+						$this->generateNewAnswer(
69
+							$question,
70
+							$target_registration,
71
+							$answers
72
+						);
73
+					}
74
+				}
75
+			}
76
+		}
77
+		return true;
78
+	}
79 79
 
80 80
 
81
-    /**
82
-     * @param EE_Answer[] $answers
83
-     * @return array
84
-     * @throws EE_Error
85
-     */
86
-    protected function reindexAnswersByQuestionId(array $answers)
87
-    {
88
-        $reindexed_answers = array();
89
-        foreach ($answers as $answer) {
90
-            if ($answer instanceof EE_Answer) {
91
-                $reindexed_answers[ $answer->question_ID() ] = $answer->value();
92
-            }
93
-        }
94
-        return $reindexed_answers;
95
-    }
81
+	/**
82
+	 * @param EE_Answer[] $answers
83
+	 * @return array
84
+	 * @throws EE_Error
85
+	 */
86
+	protected function reindexAnswersByQuestionId(array $answers)
87
+	{
88
+		$reindexed_answers = array();
89
+		foreach ($answers as $answer) {
90
+			if ($answer instanceof EE_Answer) {
91
+				$reindexed_answers[ $answer->question_ID() ] = $answer->value();
92
+			}
93
+		}
94
+		return $reindexed_answers;
95
+	}
96 96
 
97 97
 
98
-    /**
99
-     * @param EE_Question      $question
100
-     * @param EE_Registration  $registration
101
-     * @param                  $previous_answers
102
-     * @return EE_Answer
103
-     * @throws UnexpectedEntityException
104
-     * @throws EE_Error
105
-     */
106
-    protected function generateNewAnswer(
107
-        EE_Question $question,
108
-        EE_Registration $registration,
109
-        $previous_answers
110
-    ) {
111
-        $old_answer_value = isset($previous_answers[ $question->ID() ])
112
-            ? $previous_answers[ $question->ID() ]
113
-            : '';
114
-        $new_answer = EE_Answer::new_instance(
115
-            array(
116
-                'QST_ID'    => $question->ID(),
117
-                'REG_ID'    => $registration->ID(),
118
-                'ANS_value' => $old_answer_value,
119
-            )
120
-        );
121
-        if (! $new_answer instanceof EE_Answer) {
122
-            throw new UnexpectedEntityException($new_answer, 'EE_Answer');
123
-        }
124
-        $new_answer->save();
125
-        return $new_answer;
126
-    }
98
+	/**
99
+	 * @param EE_Question      $question
100
+	 * @param EE_Registration  $registration
101
+	 * @param                  $previous_answers
102
+	 * @return EE_Answer
103
+	 * @throws UnexpectedEntityException
104
+	 * @throws EE_Error
105
+	 */
106
+	protected function generateNewAnswer(
107
+		EE_Question $question,
108
+		EE_Registration $registration,
109
+		$previous_answers
110
+	) {
111
+		$old_answer_value = isset($previous_answers[ $question->ID() ])
112
+			? $previous_answers[ $question->ID() ]
113
+			: '';
114
+		$new_answer = EE_Answer::new_instance(
115
+			array(
116
+				'QST_ID'    => $question->ID(),
117
+				'REG_ID'    => $registration->ID(),
118
+				'ANS_value' => $old_answer_value,
119
+			)
120
+		);
121
+		if (! $new_answer instanceof EE_Answer) {
122
+			throw new UnexpectedEntityException($new_answer, 'EE_Answer');
123
+		}
124
+		$new_answer->save();
125
+		return $new_answer;
126
+	}
127 127
 
128 128
 
129
-    /**
130
-     * @param EE_Registration $target_registration
131
-     * @param EE_Registration $registration_to_copy
132
-     * @return bool
133
-     * @throws RuntimeException
134
-     * @throws UnexpectedEntityException
135
-     * @throws EE_Error
136
-     */
137
-    public function copyPaymentDetails(
138
-        EE_Registration $target_registration,
139
-        EE_Registration $registration_to_copy
140
-    ) {
141
-        $save = false;
142
-        $previous_registration_payments = $registration_to_copy->registration_payments();
143
-        $new_registration_payment_total = 0;
144
-        $registration_to_copy_total = $registration_to_copy->paid();
145
-        foreach ($previous_registration_payments as $previous_registration_payment) {
146
-            if ($previous_registration_payment instanceof EE_Registration_Payment
147
-                && $previous_registration_payment->payment() instanceof EE_Payment
148
-                && $previous_registration_payment->payment()->is_approved()
149
-            ) {
150
-                $payment_amount = $previous_registration_payment->amount();
151
-                $new_registration_payment = EE_Registration_Payment::new_instance(
152
-                    array(
153
-                        'REG_ID'     => $target_registration->ID(),
154
-                        'PAY_ID'     => $previous_registration_payment->payment()->ID(),
155
-                        'RPY_amount' => $payment_amount,
156
-                    )
157
-                );
158
-                if (! $new_registration_payment instanceof EE_Registration_Payment) {
159
-                    throw new UnexpectedEntityException($new_registration_payment, 'EE_Registration_Payment');
160
-                }
161
-                $new_registration_payment->save();
162
-                // if new reg payment is good, then set old reg payment amount to zero
163
-                $previous_registration_payment->set_amount(0);
164
-                $previous_registration_payment->save();
165
-                // now  increment/decrement payment amounts
166
-                $new_registration_payment_total += $payment_amount;
167
-                $registration_to_copy_total -= $payment_amount;
168
-                $save = true;
169
-            }
170
-        }
171
-        if ($save) {
172
-            $target_registration->set_paid($new_registration_payment_total);
173
-            $target_registration->save();
174
-            $registration_to_copy->set_paid($registration_to_copy_total);
175
-            $registration_to_copy->save();
176
-        }
177
-        return true;
178
-    }
129
+	/**
130
+	 * @param EE_Registration $target_registration
131
+	 * @param EE_Registration $registration_to_copy
132
+	 * @return bool
133
+	 * @throws RuntimeException
134
+	 * @throws UnexpectedEntityException
135
+	 * @throws EE_Error
136
+	 */
137
+	public function copyPaymentDetails(
138
+		EE_Registration $target_registration,
139
+		EE_Registration $registration_to_copy
140
+	) {
141
+		$save = false;
142
+		$previous_registration_payments = $registration_to_copy->registration_payments();
143
+		$new_registration_payment_total = 0;
144
+		$registration_to_copy_total = $registration_to_copy->paid();
145
+		foreach ($previous_registration_payments as $previous_registration_payment) {
146
+			if ($previous_registration_payment instanceof EE_Registration_Payment
147
+				&& $previous_registration_payment->payment() instanceof EE_Payment
148
+				&& $previous_registration_payment->payment()->is_approved()
149
+			) {
150
+				$payment_amount = $previous_registration_payment->amount();
151
+				$new_registration_payment = EE_Registration_Payment::new_instance(
152
+					array(
153
+						'REG_ID'     => $target_registration->ID(),
154
+						'PAY_ID'     => $previous_registration_payment->payment()->ID(),
155
+						'RPY_amount' => $payment_amount,
156
+					)
157
+				);
158
+				if (! $new_registration_payment instanceof EE_Registration_Payment) {
159
+					throw new UnexpectedEntityException($new_registration_payment, 'EE_Registration_Payment');
160
+				}
161
+				$new_registration_payment->save();
162
+				// if new reg payment is good, then set old reg payment amount to zero
163
+				$previous_registration_payment->set_amount(0);
164
+				$previous_registration_payment->save();
165
+				// now  increment/decrement payment amounts
166
+				$new_registration_payment_total += $payment_amount;
167
+				$registration_to_copy_total -= $payment_amount;
168
+				$save = true;
169
+			}
170
+		}
171
+		if ($save) {
172
+			$target_registration->set_paid($new_registration_payment_total);
173
+			$target_registration->save();
174
+			$registration_to_copy->set_paid($registration_to_copy_total);
175
+			$registration_to_copy->save();
176
+		}
177
+		return true;
178
+	}
179 179
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Event.class.php 2 patches
Indentation   +1382 added lines, -1382 removed lines patch added patch discarded remove patch
@@ -15,1386 +15,1386 @@
 block discarded – undo
15 15
 class EE_Event extends EE_CPT_Base implements EEI_Line_Item_Object, EEI_Admin_Links, EEI_Has_Icon, EEI_Event
16 16
 {
17 17
 
18
-    /**
19
-     * cached value for the the logical active status for the event
20
-     *
21
-     * @see get_active_status()
22
-     * @var string
23
-     */
24
-    protected $_active_status = '';
25
-
26
-    /**
27
-     * This is just used for caching the Primary Datetime for the Event on initial retrieval
28
-     *
29
-     * @var EE_Datetime
30
-     */
31
-    protected $_Primary_Datetime;
32
-
33
-    /**
34
-     * @var EventSpacesCalculator $available_spaces_calculator
35
-     */
36
-    protected $available_spaces_calculator;
37
-
38
-
39
-    /**
40
-     * @param array  $props_n_values          incoming values
41
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
42
-     *                                        used.)
43
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
44
-     *                                        date_format and the second value is the time format
45
-     * @return EE_Event
46
-     * @throws EE_Error
47
-     */
48
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
49
-    {
50
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
51
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
52
-    }
53
-
54
-
55
-    /**
56
-     * @param array  $props_n_values  incoming values from the database
57
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
58
-     *                                the website will be used.
59
-     * @return EE_Event
60
-     * @throws EE_Error
61
-     */
62
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
63
-    {
64
-        return new self($props_n_values, true, $timezone);
65
-    }
66
-
67
-
68
-    /**
69
-     * @return EventSpacesCalculator
70
-     * @throws \EE_Error
71
-     */
72
-    public function getAvailableSpacesCalculator()
73
-    {
74
-        if (! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
75
-            $this->available_spaces_calculator = new EventSpacesCalculator($this);
76
-        }
77
-        return $this->available_spaces_calculator;
78
-    }
79
-
80
-
81
-    /**
82
-     * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
83
-     *
84
-     * @param string $field_name
85
-     * @param mixed  $field_value
86
-     * @param bool   $use_default
87
-     * @throws EE_Error
88
-     */
89
-    public function set($field_name, $field_value, $use_default = false)
90
-    {
91
-        switch ($field_name) {
92
-            case 'status':
93
-                $this->set_status($field_value, $use_default);
94
-                break;
95
-            default:
96
-                parent::set($field_name, $field_value, $use_default);
97
-        }
98
-    }
99
-
100
-
101
-    /**
102
-     *    set_status
103
-     * Checks if event status is being changed to SOLD OUT
104
-     * and updates event meta data with previous event status
105
-     * so that we can revert things if/when the event is no longer sold out
106
-     *
107
-     * @access public
108
-     * @param string $new_status
109
-     * @param bool   $use_default
110
-     * @return void
111
-     * @throws EE_Error
112
-     */
113
-    public function set_status($new_status = null, $use_default = false)
114
-    {
115
-        // if nothing is set, and we aren't explicitly wanting to reset the status, then just leave
116
-        if (empty($new_status) && ! $use_default) {
117
-            return;
118
-        }
119
-        // get current Event status
120
-        $old_status = $this->status();
121
-        // if status has changed
122
-        if ($old_status !== $new_status) {
123
-            // TO sold_out
124
-            if ($new_status === EEM_Event::sold_out) {
125
-                // save the previous event status so that we can revert if the event is no longer sold out
126
-                $this->add_post_meta('_previous_event_status', $old_status);
127
-                do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $new_status);
128
-                // OR FROM  sold_out
129
-            } elseif ($old_status === EEM_Event::sold_out) {
130
-                $this->delete_post_meta('_previous_event_status');
131
-                do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $new_status);
132
-            }
133
-            // clear out the active status so that it gets reset the next time it is requested
134
-            $this->_active_status = null;
135
-            // update status
136
-            parent::set('status', $new_status, $use_default);
137
-            do_action('AHEE__EE_Event__set_status__after_update', $this);
138
-            return;
139
-        }
140
-        // even though the old value matches the new value, it's still good to
141
-        // allow the parent set method to have a say
142
-        parent::set('status', $new_status, $use_default);
143
-    }
144
-
145
-
146
-    /**
147
-     * Gets all the datetimes for this event
148
-     *
149
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
150
-     * @return EE_Base_Class[]|EE_Datetime[]
151
-     * @throws EE_Error
152
-     */
153
-    public function datetimes($query_params = array())
154
-    {
155
-        return $this->get_many_related('Datetime', $query_params);
156
-    }
157
-
158
-
159
-    /**
160
-     * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
161
-     *
162
-     * @return EE_Base_Class[]|EE_Datetime[]
163
-     * @throws EE_Error
164
-     */
165
-    public function datetimes_in_chronological_order()
166
-    {
167
-        return $this->get_many_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC')));
168
-    }
169
-
170
-
171
-    /**
172
-     * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
173
-     * @darren, we should probably UNSET timezone on the EEM_Datetime model
174
-     * after running our query, so that this timezone isn't set for EVERY query
175
-     * on EEM_Datetime for the rest of the request, no?
176
-     *
177
-     * @param boolean $show_expired whether or not to include expired events
178
-     * @param boolean $show_deleted whether or not to include deleted events
179
-     * @param null    $limit
180
-     * @return EE_Datetime[]
181
-     * @throws EE_Error
182
-     */
183
-    public function datetimes_ordered($show_expired = true, $show_deleted = false, $limit = null)
184
-    {
185
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
186
-            $this->ID(),
187
-            $show_expired,
188
-            $show_deleted,
189
-            $limit
190
-        );
191
-    }
192
-
193
-
194
-    /**
195
-     * Returns one related datetime. Mostly only used by some legacy code.
196
-     *
197
-     * @return EE_Base_Class|EE_Datetime
198
-     * @throws EE_Error
199
-     */
200
-    public function first_datetime()
201
-    {
202
-        return $this->get_first_related('Datetime');
203
-    }
204
-
205
-
206
-    /**
207
-     * Returns the 'primary' datetime for the event
208
-     *
209
-     * @param bool $try_to_exclude_expired
210
-     * @param bool $try_to_exclude_deleted
211
-     * @return EE_Datetime
212
-     * @throws EE_Error
213
-     */
214
-    public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
215
-    {
216
-        if (! empty($this->_Primary_Datetime)) {
217
-            return $this->_Primary_Datetime;
218
-        }
219
-        $this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
220
-            $this->ID(),
221
-            $try_to_exclude_expired,
222
-            $try_to_exclude_deleted
223
-        );
224
-        return $this->_Primary_Datetime;
225
-    }
226
-
227
-
228
-    /**
229
-     * Gets all the tickets available for purchase of this event
230
-     *
231
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
232
-     * @return EE_Base_Class[]|EE_Ticket[]
233
-     * @throws EE_Error
234
-     */
235
-    public function tickets($query_params = array())
236
-    {
237
-        // first get all datetimes
238
-        $datetimes = $this->datetimes_ordered();
239
-        if (! $datetimes) {
240
-            return array();
241
-        }
242
-        $datetime_ids = array();
243
-        foreach ($datetimes as $datetime) {
244
-            $datetime_ids[] = $datetime->ID();
245
-        }
246
-        $where_params = array('Datetime.DTT_ID' => array('IN', $datetime_ids));
247
-        // if incoming $query_params has where conditions let's merge but not override existing.
248
-        if (is_array($query_params) && isset($query_params[0])) {
249
-            $where_params = array_merge($query_params[0], $where_params);
250
-            unset($query_params[0]);
251
-        }
252
-        // now add $where_params to $query_params
253
-        $query_params[0] = $where_params;
254
-        return EEM_Ticket::instance()->get_all($query_params);
255
-    }
256
-
257
-
258
-    /**
259
-     * get all unexpired untrashed tickets
260
-     *
261
-     * @return EE_Ticket[]
262
-     * @throws EE_Error
263
-     */
264
-    public function active_tickets()
265
-    {
266
-        return $this->tickets(
267
-            array(
268
-                array(
269
-                    'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
270
-                    'TKT_deleted'  => false,
271
-                ),
272
-            )
273
-        );
274
-    }
275
-
276
-
277
-    /**
278
-     * @return bool
279
-     * @throws EE_Error
280
-     */
281
-    public function additional_limit()
282
-    {
283
-        return $this->get('EVT_additional_limit');
284
-    }
285
-
286
-
287
-    /**
288
-     * @return bool
289
-     * @throws EE_Error
290
-     */
291
-    public function allow_overflow()
292
-    {
293
-        return $this->get('EVT_allow_overflow');
294
-    }
295
-
296
-
297
-    /**
298
-     * @return bool
299
-     * @throws EE_Error
300
-     */
301
-    public function created()
302
-    {
303
-        return $this->get('EVT_created');
304
-    }
305
-
306
-
307
-    /**
308
-     * @return bool
309
-     * @throws EE_Error
310
-     */
311
-    public function description()
312
-    {
313
-        return $this->get('EVT_desc');
314
-    }
315
-
316
-
317
-    /**
318
-     * Runs do_shortcode and wpautop on the description
319
-     *
320
-     * @return string of html
321
-     * @throws EE_Error
322
-     */
323
-    public function description_filtered()
324
-    {
325
-        return $this->get_pretty('EVT_desc');
326
-    }
327
-
328
-
329
-    /**
330
-     * @return bool
331
-     * @throws EE_Error
332
-     */
333
-    public function display_description()
334
-    {
335
-        return $this->get('EVT_display_desc');
336
-    }
337
-
338
-
339
-    /**
340
-     * @return bool
341
-     * @throws EE_Error
342
-     */
343
-    public function display_ticket_selector()
344
-    {
345
-        return (bool) $this->get('EVT_display_ticket_selector');
346
-    }
347
-
348
-
349
-    /**
350
-     * @return bool
351
-     * @throws EE_Error
352
-     */
353
-    public function external_url()
354
-    {
355
-        return $this->get('EVT_external_URL');
356
-    }
357
-
358
-
359
-    /**
360
-     * @return bool
361
-     * @throws EE_Error
362
-     */
363
-    public function member_only()
364
-    {
365
-        return $this->get('EVT_member_only');
366
-    }
367
-
368
-
369
-    /**
370
-     * @return bool
371
-     * @throws EE_Error
372
-     */
373
-    public function phone()
374
-    {
375
-        return $this->get('EVT_phone');
376
-    }
377
-
378
-
379
-    /**
380
-     * @return bool
381
-     * @throws EE_Error
382
-     */
383
-    public function modified()
384
-    {
385
-        return $this->get('EVT_modified');
386
-    }
387
-
388
-
389
-    /**
390
-     * @return bool
391
-     * @throws EE_Error
392
-     */
393
-    public function name()
394
-    {
395
-        return $this->get('EVT_name');
396
-    }
397
-
398
-
399
-    /**
400
-     * @return bool
401
-     * @throws EE_Error
402
-     */
403
-    public function order()
404
-    {
405
-        return $this->get('EVT_order');
406
-    }
407
-
408
-
409
-    /**
410
-     * @return bool|string
411
-     * @throws EE_Error
412
-     */
413
-    public function default_registration_status()
414
-    {
415
-        $event_default_registration_status = $this->get('EVT_default_registration_status');
416
-        return ! empty($event_default_registration_status)
417
-            ? $event_default_registration_status
418
-            : EE_Registry::instance()->CFG->registration->default_STS_ID;
419
-    }
420
-
421
-
422
-    /**
423
-     * @param int  $num_words
424
-     * @param null $more
425
-     * @param bool $not_full_desc
426
-     * @return bool|string
427
-     * @throws EE_Error
428
-     */
429
-    public function short_description($num_words = 55, $more = null, $not_full_desc = false)
430
-    {
431
-        $short_desc = $this->get('EVT_short_desc');
432
-        if (! empty($short_desc) || $not_full_desc) {
433
-            return $short_desc;
434
-        }
435
-        $full_desc = $this->get('EVT_desc');
436
-        return wp_trim_words($full_desc, $num_words, $more);
437
-    }
438
-
439
-
440
-    /**
441
-     * @return bool
442
-     * @throws EE_Error
443
-     */
444
-    public function slug()
445
-    {
446
-        return $this->get('EVT_slug');
447
-    }
448
-
449
-
450
-    /**
451
-     * @return bool
452
-     * @throws EE_Error
453
-     */
454
-    public function timezone_string()
455
-    {
456
-        return $this->get('EVT_timezone_string');
457
-    }
458
-
459
-
460
-    /**
461
-     * @return bool
462
-     * @throws EE_Error
463
-     */
464
-    public function visible_on()
465
-    {
466
-        return $this->get('EVT_visible_on');
467
-    }
468
-
469
-
470
-    /**
471
-     * @return int
472
-     * @throws EE_Error
473
-     */
474
-    public function wp_user()
475
-    {
476
-        return $this->get('EVT_wp_user');
477
-    }
478
-
479
-
480
-    /**
481
-     * @return bool
482
-     * @throws EE_Error
483
-     */
484
-    public function donations()
485
-    {
486
-        return $this->get('EVT_donations');
487
-    }
488
-
489
-
490
-    /**
491
-     * @param $limit
492
-     * @throws EE_Error
493
-     */
494
-    public function set_additional_limit($limit)
495
-    {
496
-        $this->set('EVT_additional_limit', $limit);
497
-    }
498
-
499
-
500
-    /**
501
-     * @param $created
502
-     * @throws EE_Error
503
-     */
504
-    public function set_created($created)
505
-    {
506
-        $this->set('EVT_created', $created);
507
-    }
508
-
509
-
510
-    /**
511
-     * @param $desc
512
-     * @throws EE_Error
513
-     */
514
-    public function set_description($desc)
515
-    {
516
-        $this->set('EVT_desc', $desc);
517
-    }
518
-
519
-
520
-    /**
521
-     * @param $display_desc
522
-     * @throws EE_Error
523
-     */
524
-    public function set_display_description($display_desc)
525
-    {
526
-        $this->set('EVT_display_desc', $display_desc);
527
-    }
528
-
529
-
530
-    /**
531
-     * @param $display_ticket_selector
532
-     * @throws EE_Error
533
-     */
534
-    public function set_display_ticket_selector($display_ticket_selector)
535
-    {
536
-        $this->set('EVT_display_ticket_selector', $display_ticket_selector);
537
-    }
538
-
539
-
540
-    /**
541
-     * @param $external_url
542
-     * @throws EE_Error
543
-     */
544
-    public function set_external_url($external_url)
545
-    {
546
-        $this->set('EVT_external_URL', $external_url);
547
-    }
548
-
549
-
550
-    /**
551
-     * @param $member_only
552
-     * @throws EE_Error
553
-     */
554
-    public function set_member_only($member_only)
555
-    {
556
-        $this->set('EVT_member_only', $member_only);
557
-    }
558
-
559
-
560
-    /**
561
-     * @param $event_phone
562
-     * @throws EE_Error
563
-     */
564
-    public function set_event_phone($event_phone)
565
-    {
566
-        $this->set('EVT_phone', $event_phone);
567
-    }
568
-
569
-
570
-    /**
571
-     * @param $modified
572
-     * @throws EE_Error
573
-     */
574
-    public function set_modified($modified)
575
-    {
576
-        $this->set('EVT_modified', $modified);
577
-    }
578
-
579
-
580
-    /**
581
-     * @param $name
582
-     * @throws EE_Error
583
-     */
584
-    public function set_name($name)
585
-    {
586
-        $this->set('EVT_name', $name);
587
-    }
588
-
589
-
590
-    /**
591
-     * @param $order
592
-     * @throws EE_Error
593
-     */
594
-    public function set_order($order)
595
-    {
596
-        $this->set('EVT_order', $order);
597
-    }
598
-
599
-
600
-    /**
601
-     * @param $short_desc
602
-     * @throws EE_Error
603
-     */
604
-    public function set_short_description($short_desc)
605
-    {
606
-        $this->set('EVT_short_desc', $short_desc);
607
-    }
608
-
609
-
610
-    /**
611
-     * @param $slug
612
-     * @throws EE_Error
613
-     */
614
-    public function set_slug($slug)
615
-    {
616
-        $this->set('EVT_slug', $slug);
617
-    }
618
-
619
-
620
-    /**
621
-     * @param $timezone_string
622
-     * @throws EE_Error
623
-     */
624
-    public function set_timezone_string($timezone_string)
625
-    {
626
-        $this->set('EVT_timezone_string', $timezone_string);
627
-    }
628
-
629
-
630
-    /**
631
-     * @param $visible_on
632
-     * @throws EE_Error
633
-     */
634
-    public function set_visible_on($visible_on)
635
-    {
636
-        $this->set('EVT_visible_on', $visible_on);
637
-    }
638
-
639
-
640
-    /**
641
-     * @param $wp_user
642
-     * @throws EE_Error
643
-     */
644
-    public function set_wp_user($wp_user)
645
-    {
646
-        $this->set('EVT_wp_user', $wp_user);
647
-    }
648
-
649
-
650
-    /**
651
-     * @param $default_registration_status
652
-     * @throws EE_Error
653
-     */
654
-    public function set_default_registration_status($default_registration_status)
655
-    {
656
-        $this->set('EVT_default_registration_status', $default_registration_status);
657
-    }
658
-
659
-
660
-    /**
661
-     * @param $donations
662
-     * @throws EE_Error
663
-     */
664
-    public function set_donations($donations)
665
-    {
666
-        $this->set('EVT_donations', $donations);
667
-    }
668
-
669
-
670
-    /**
671
-     * Adds a venue to this event
672
-     *
673
-     * @param EE_Venue /int $venue_id_or_obj
674
-     * @return EE_Base_Class|EE_Venue
675
-     * @throws EE_Error
676
-     */
677
-    public function add_venue($venue_id_or_obj)
678
-    {
679
-        return $this->_add_relation_to($venue_id_or_obj, 'Venue');
680
-    }
681
-
682
-
683
-    /**
684
-     * Removes a venue from the event
685
-     *
686
-     * @param EE_Venue /int $venue_id_or_obj
687
-     * @return EE_Base_Class|EE_Venue
688
-     * @throws EE_Error
689
-     */
690
-    public function remove_venue($venue_id_or_obj)
691
-    {
692
-        return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
693
-    }
694
-
695
-
696
-    /**
697
-     * Gets all the venues related ot the event. May provide additional $query_params if desired
698
-     *
699
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
700
-     * @return EE_Base_Class[]|EE_Venue[]
701
-     * @throws EE_Error
702
-     */
703
-    public function venues($query_params = array())
704
-    {
705
-        return $this->get_many_related('Venue', $query_params);
706
-    }
707
-
708
-
709
-    /**
710
-     * check if event id is present and if event is published
711
-     *
712
-     * @access public
713
-     * @return boolean true yes, false no
714
-     * @throws EE_Error
715
-     */
716
-    private function _has_ID_and_is_published()
717
-    {
718
-        // first check if event id is present and not NULL,
719
-        // then check if this event is published (or any of the equivalent "published" statuses)
720
-        return
721
-            $this->ID() && $this->ID() !== null
722
-            && (
723
-                $this->status() === 'publish'
724
-                || $this->status() === EEM_Event::sold_out
725
-                || $this->status() === EEM_Event::postponed
726
-                || $this->status() === EEM_Event::cancelled
727
-            );
728
-    }
729
-
730
-
731
-    /**
732
-     * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
733
-     *
734
-     * @access public
735
-     * @return boolean true yes, false no
736
-     * @throws EE_Error
737
-     */
738
-    public function is_upcoming()
739
-    {
740
-        // check if event id is present and if this event is published
741
-        if ($this->is_inactive()) {
742
-            return false;
743
-        }
744
-        // set initial value
745
-        $upcoming = false;
746
-        // next let's get all datetimes and loop through them
747
-        $datetimes = $this->datetimes_in_chronological_order();
748
-        foreach ($datetimes as $datetime) {
749
-            if ($datetime instanceof EE_Datetime) {
750
-                // if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
751
-                if ($datetime->is_expired()) {
752
-                    continue;
753
-                }
754
-                // if this dtt is active then we return false.
755
-                if ($datetime->is_active()) {
756
-                    return false;
757
-                }
758
-                // otherwise let's check upcoming status
759
-                $upcoming = $datetime->is_upcoming();
760
-            }
761
-        }
762
-        return $upcoming;
763
-    }
764
-
765
-
766
-    /**
767
-     * @return bool
768
-     * @throws EE_Error
769
-     */
770
-    public function is_active()
771
-    {
772
-        // check if event id is present and if this event is published
773
-        if ($this->is_inactive()) {
774
-            return false;
775
-        }
776
-        // set initial value
777
-        $active = false;
778
-        // next let's get all datetimes and loop through them
779
-        $datetimes = $this->datetimes_in_chronological_order();
780
-        foreach ($datetimes as $datetime) {
781
-            if ($datetime instanceof EE_Datetime) {
782
-                // if this dtt is expired then we continue cause one of the other datetimes might be active.
783
-                if ($datetime->is_expired()) {
784
-                    continue;
785
-                }
786
-                // if this dtt is upcoming then we return false.
787
-                if ($datetime->is_upcoming()) {
788
-                    return false;
789
-                }
790
-                // otherwise let's check active status
791
-                $active = $datetime->is_active();
792
-            }
793
-        }
794
-        return $active;
795
-    }
796
-
797
-
798
-    /**
799
-     * @return bool
800
-     * @throws EE_Error
801
-     */
802
-    public function is_expired()
803
-    {
804
-        // check if event id is present and if this event is published
805
-        if ($this->is_inactive()) {
806
-            return false;
807
-        }
808
-        // set initial value
809
-        $expired = false;
810
-        // first let's get all datetimes and loop through them
811
-        $datetimes = $this->datetimes_in_chronological_order();
812
-        foreach ($datetimes as $datetime) {
813
-            if ($datetime instanceof EE_Datetime) {
814
-                // if this dtt is upcoming or active then we return false.
815
-                if ($datetime->is_upcoming() || $datetime->is_active()) {
816
-                    return false;
817
-                }
818
-                // otherwise let's check active status
819
-                $expired = $datetime->is_expired();
820
-            }
821
-        }
822
-        return $expired;
823
-    }
824
-
825
-
826
-    /**
827
-     * @return bool
828
-     * @throws EE_Error
829
-     */
830
-    public function is_inactive()
831
-    {
832
-        // check if event id is present and if this event is published
833
-        if ($this->_has_ID_and_is_published()) {
834
-            return false;
835
-        }
836
-        return true;
837
-    }
838
-
839
-
840
-    /**
841
-     * calculate spaces remaining based on "saleable" tickets
842
-     *
843
-     * @param array $tickets
844
-     * @param bool  $filtered
845
-     * @return int|float
846
-     * @throws EE_Error
847
-     * @throws DomainException
848
-     * @throws UnexpectedEntityException
849
-     */
850
-    public function spaces_remaining($tickets = array(), $filtered = true)
851
-    {
852
-        $this->getAvailableSpacesCalculator()->setActiveTickets($tickets);
853
-        $spaces_remaining = $this->getAvailableSpacesCalculator()->spacesRemaining();
854
-        return $filtered
855
-            ? apply_filters(
856
-                'FHEE_EE_Event__spaces_remaining',
857
-                $spaces_remaining,
858
-                $this,
859
-                $tickets
860
-            )
861
-            : $spaces_remaining;
862
-    }
863
-
864
-
865
-    /**
866
-     *    perform_sold_out_status_check
867
-     *    checks all of this events's datetime  reg_limit - sold values to determine if ANY datetimes have spaces
868
-     *    available... if NOT, then the event status will get toggled to 'sold_out'
869
-     *
870
-     * @return bool    return the ACTUAL sold out state.
871
-     * @throws EE_Error
872
-     * @throws DomainException
873
-     * @throws UnexpectedEntityException
874
-     */
875
-    public function perform_sold_out_status_check()
876
-    {
877
-        // get all unexpired untrashed tickets
878
-        $tickets = $this->tickets(
879
-            array(
880
-                array('TKT_deleted' => false),
881
-                'order_by' => array('TKT_qty' => 'ASC'),
882
-            )
883
-        );
884
-        $all_expired = true;
885
-        foreach ($tickets as $ticket) {
886
-            if (! $ticket->is_expired()) {
887
-                $all_expired = false;
888
-                break;
889
-            }
890
-        }
891
-        // if all the tickets are just expired, then don't update the event status to sold out
892
-        if ($all_expired) {
893
-            return true;
894
-        }
895
-        $spaces_remaining = $this->spaces_remaining($tickets);
896
-        if ($spaces_remaining < 1) {
897
-            $this->set_status(EEM_Event::sold_out);
898
-            $this->save();
899
-            $sold_out = true;
900
-        } else {
901
-            $sold_out = false;
902
-            // was event previously marked as sold out ?
903
-            if ($this->status() === EEM_Event::sold_out) {
904
-                // revert status to previous value, if it was set
905
-                $previous_event_status = $this->get_post_meta('_previous_event_status', true);
906
-                if ($previous_event_status) {
907
-                    $this->set_status($previous_event_status);
908
-                    $this->save();
909
-                }
910
-            }
911
-        }
912
-        do_action('AHEE__EE_Event__perform_sold_out_status_check__end', $this, $sold_out, $spaces_remaining, $tickets);
913
-        return $sold_out;
914
-    }
915
-
916
-
917
-    /**
918
-     * This returns the total remaining spaces for sale on this event.
919
-     *
920
-     * @uses EE_Event::total_available_spaces()
921
-     * @return float|int
922
-     * @throws EE_Error
923
-     * @throws DomainException
924
-     * @throws UnexpectedEntityException
925
-     */
926
-    public function spaces_remaining_for_sale()
927
-    {
928
-        return $this->total_available_spaces(true);
929
-    }
930
-
931
-
932
-    /**
933
-     * This returns the total spaces available for an event
934
-     * while considering all the qtys on the tickets and the reg limits
935
-     * on the datetimes attached to this event.
936
-     *
937
-     * @param   bool $consider_sold Whether to consider any tickets that have already sold in our calculation.
938
-     *                              If this is false, then we return the most tickets that could ever be sold
939
-     *                              for this event with the datetime and tickets setup on the event under optimal
940
-     *                              selling conditions.  Otherwise we return a live calculation of spaces available
941
-     *                              based on tickets sold.  Depending on setup and stage of sales, this
942
-     *                              may appear to equal remaining tickets.  However, the more tickets are
943
-     *                              sold out, the more accurate the "live" total is.
944
-     * @return float|int
945
-     * @throws EE_Error
946
-     * @throws DomainException
947
-     * @throws UnexpectedEntityException
948
-     */
949
-    public function total_available_spaces($consider_sold = false)
950
-    {
951
-        $spaces_available = $consider_sold
952
-            ? $this->getAvailableSpacesCalculator()->spacesRemaining()
953
-            : $this->getAvailableSpacesCalculator()->totalSpacesAvailable();
954
-        return apply_filters(
955
-            'FHEE_EE_Event__total_available_spaces__spaces_available',
956
-            $spaces_available,
957
-            $this,
958
-            $this->getAvailableSpacesCalculator()->getDatetimes(),
959
-            $this->getAvailableSpacesCalculator()->getActiveTickets()
960
-        );
961
-    }
962
-
963
-
964
-    /**
965
-     * Checks if the event is set to sold out
966
-     *
967
-     * @param  bool $actual whether or not to perform calculations to not only figure the
968
-     *                      actual status but also to flip the status if necessary to sold
969
-     *                      out If false, we just check the existing status of the event
970
-     * @return boolean
971
-     * @throws EE_Error
972
-     */
973
-    public function is_sold_out($actual = false)
974
-    {
975
-        if (! $actual) {
976
-            return $this->status() === EEM_Event::sold_out;
977
-        }
978
-        return $this->perform_sold_out_status_check();
979
-    }
980
-
981
-
982
-    /**
983
-     * Checks if the event is marked as postponed
984
-     *
985
-     * @return boolean
986
-     */
987
-    public function is_postponed()
988
-    {
989
-        return $this->status() === EEM_Event::postponed;
990
-    }
991
-
992
-
993
-    /**
994
-     * Checks if the event is marked as cancelled
995
-     *
996
-     * @return boolean
997
-     */
998
-    public function is_cancelled()
999
-    {
1000
-        return $this->status() === EEM_Event::cancelled;
1001
-    }
1002
-
1003
-
1004
-    /**
1005
-     * Get the logical active status in a hierarchical order for all the datetimes.  Note
1006
-     * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1007
-     * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1008
-     * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1009
-     * the event is considered expired.
1010
-     * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a
1011
-     * status set on the EVENT when it is not published and thus is done
1012
-     *
1013
-     * @param bool $reset
1014
-     * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1015
-     * @throws EE_Error
1016
-     */
1017
-    public function get_active_status($reset = false)
1018
-    {
1019
-        // if the active status has already been set, then just use that value (unless we are resetting it)
1020
-        if (! empty($this->_active_status) && ! $reset) {
1021
-            return $this->_active_status;
1022
-        }
1023
-        // first check if event id is present on this object
1024
-        if (! $this->ID()) {
1025
-            return false;
1026
-        }
1027
-        $where_params_for_event = array(array('EVT_ID' => $this->ID()));
1028
-        // if event is published:
1029
-        if ($this->status() === 'publish') {
1030
-            // active?
1031
-            if (EEM_Datetime::instance()->get_datetime_count_for_status(
1032
-                EE_Datetime::active,
1033
-                $where_params_for_event
1034
-            ) > 0) {
1035
-                $this->_active_status = EE_Datetime::active;
1036
-            } else {
1037
-                // upcoming?
1038
-                if (EEM_Datetime::instance()->get_datetime_count_for_status(
1039
-                    EE_Datetime::upcoming,
1040
-                    $where_params_for_event
1041
-                ) > 0) {
1042
-                    $this->_active_status = EE_Datetime::upcoming;
1043
-                } else {
1044
-                    // expired?
1045
-                    if (EEM_Datetime::instance()->get_datetime_count_for_status(
1046
-                        EE_Datetime::expired,
1047
-                        $where_params_for_event
1048
-                    ) > 0
1049
-                    ) {
1050
-                        $this->_active_status = EE_Datetime::expired;
1051
-                    } else {
1052
-                        // it would be odd if things make it this far because it basically means there are no datetime's
1053
-                        // attached to the event.  So in this case it will just be considered inactive.
1054
-                        $this->_active_status = EE_Datetime::inactive;
1055
-                    }
1056
-                }
1057
-            }
1058
-        } else {
1059
-            // the event is not published, so let's just set it's active status according to its' post status
1060
-            switch ($this->status()) {
1061
-                case EEM_Event::sold_out:
1062
-                    $this->_active_status = EE_Datetime::sold_out;
1063
-                    break;
1064
-                case EEM_Event::cancelled:
1065
-                    $this->_active_status = EE_Datetime::cancelled;
1066
-                    break;
1067
-                case EEM_Event::postponed:
1068
-                    $this->_active_status = EE_Datetime::postponed;
1069
-                    break;
1070
-                default:
1071
-                    $this->_active_status = EE_Datetime::inactive;
1072
-            }
1073
-        }
1074
-        return $this->_active_status;
1075
-    }
1076
-
1077
-
1078
-    /**
1079
-     *    pretty_active_status
1080
-     *
1081
-     * @access public
1082
-     * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1083
-     * @return mixed void|string
1084
-     * @throws EE_Error
1085
-     */
1086
-    public function pretty_active_status($echo = true)
1087
-    {
1088
-        $active_status = $this->get_active_status();
1089
-        $status = '<span class="ee-status event-active-status-'
1090
-                  . $active_status
1091
-                  . '">'
1092
-                  . EEH_Template::pretty_status($active_status, false, 'sentence')
1093
-                  . '</span>';
1094
-        if ($echo) {
1095
-            echo $status;
1096
-            return '';
1097
-        }
1098
-        return $status;
1099
-    }
1100
-
1101
-
1102
-    /**
1103
-     * @return bool|int
1104
-     * @throws EE_Error
1105
-     */
1106
-    public function get_number_of_tickets_sold()
1107
-    {
1108
-        $tkt_sold = 0;
1109
-        if (! $this->ID()) {
1110
-            return 0;
1111
-        }
1112
-        $datetimes = $this->datetimes();
1113
-        foreach ($datetimes as $datetime) {
1114
-            if ($datetime instanceof EE_Datetime) {
1115
-                $tkt_sold += $datetime->sold();
1116
-            }
1117
-        }
1118
-        return $tkt_sold;
1119
-    }
1120
-
1121
-
1122
-    /**
1123
-     * This just returns a count of all the registrations for this event
1124
-     *
1125
-     * @access  public
1126
-     * @return int
1127
-     * @throws EE_Error
1128
-     */
1129
-    public function get_count_of_all_registrations()
1130
-    {
1131
-        return EEM_Event::instance()->count_related($this, 'Registration');
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     * This returns the ticket with the earliest start time that is
1137
-     * available for this event (across all datetimes attached to the event)
1138
-     *
1139
-     * @return EE_Base_Class|EE_Ticket|null
1140
-     * @throws EE_Error
1141
-     */
1142
-    public function get_ticket_with_earliest_start_time()
1143
-    {
1144
-        $where['Datetime.EVT_ID'] = $this->ID();
1145
-        $query_params = array($where, 'order_by' => array('TKT_start_date' => 'ASC'));
1146
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1147
-    }
1148
-
1149
-
1150
-    /**
1151
-     * This returns the ticket with the latest end time that is available
1152
-     * for this event (across all datetimes attached to the event)
1153
-     *
1154
-     * @return EE_Base_Class|EE_Ticket|null
1155
-     * @throws EE_Error
1156
-     */
1157
-    public function get_ticket_with_latest_end_time()
1158
-    {
1159
-        $where['Datetime.EVT_ID'] = $this->ID();
1160
-        $query_params = array($where, 'order_by' => array('TKT_end_date' => 'DESC'));
1161
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1162
-    }
1163
-
1164
-
1165
-    /**
1166
-     * This returns the number of different ticket types currently on sale for this event.
1167
-     *
1168
-     * @return int
1169
-     * @throws EE_Error
1170
-     */
1171
-    public function countTicketsOnSale()
1172
-    {
1173
-        $where = array(
1174
-            'Datetime.EVT_ID' => $this->ID(),
1175
-            'TKT_start_date'  => array('<', time()),
1176
-            'TKT_end_date'    => array('>', time()),
1177
-        );
1178
-        return EEM_Ticket::instance()->count(array($where));
1179
-    }
1180
-
1181
-
1182
-    /**
1183
-     * This returns whether there are any tickets on sale for this event.
1184
-     *
1185
-     * @return bool true = YES tickets on sale.
1186
-     * @throws EE_Error
1187
-     */
1188
-    public function tickets_on_sale()
1189
-    {
1190
-        return $this->countTicketsOnSale() > 0;
1191
-    }
1192
-
1193
-
1194
-    /**
1195
-     * Gets the URL for viewing this event on the front-end. Overrides parent
1196
-     * to check for an external URL first
1197
-     *
1198
-     * @return string
1199
-     * @throws EE_Error
1200
-     */
1201
-    public function get_permalink()
1202
-    {
1203
-        if ($this->external_url()) {
1204
-            return $this->external_url();
1205
-        }
1206
-        return parent::get_permalink();
1207
-    }
1208
-
1209
-
1210
-    /**
1211
-     * Gets the first term for 'espresso_event_categories' we can find
1212
-     *
1213
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1214
-     * @return EE_Base_Class|EE_Term|null
1215
-     * @throws EE_Error
1216
-     */
1217
-    public function first_event_category($query_params = array())
1218
-    {
1219
-        $query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1220
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1221
-        return EEM_Term::instance()->get_one($query_params);
1222
-    }
1223
-
1224
-
1225
-    /**
1226
-     * Gets all terms for 'espresso_event_categories' we can find
1227
-     *
1228
-     * @param array $query_params
1229
-     * @return EE_Base_Class[]|EE_Term[]
1230
-     * @throws EE_Error
1231
-     */
1232
-    public function get_all_event_categories($query_params = array())
1233
-    {
1234
-        $query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1235
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1236
-        return EEM_Term::instance()->get_all($query_params);
1237
-    }
1238
-
1239
-
1240
-    /**
1241
-     * Adds a question group to this event
1242
-     *
1243
-     * @param EE_Question_Group|int $question_group_id_or_obj
1244
-     * @param bool $for_primary if true, the question group will be added for the primary
1245
-     *                                           registrant, if false will be added for others. default: false
1246
-     * @return EE_Base_Class|EE_Question_Group
1247
-     * @throws EE_Error
1248
-     * @throws InvalidArgumentException
1249
-     * @throws InvalidDataTypeException
1250
-     * @throws InvalidInterfaceException
1251
-     * @throws ReflectionException
1252
-     */
1253
-    public function add_question_group($question_group_id_or_obj, $for_primary = false)
1254
-    {
1255
-        // If the row already exists, it will be updated. If it doesn't, it will be inserted.
1256
-        // That's in EE_HABTM_Relation::add_relation_to().
1257
-        return $this->_add_relation_to(
1258
-            $question_group_id_or_obj,
1259
-            'Question_Group',
1260
-            [
1261
-                EEM_Event_Question_Group::instance()->field_name_for_category($for_primary) => true
1262
-            ]
1263
-        );
1264
-    }
1265
-
1266
-
1267
-    /**
1268
-     * Removes a question group from the event
1269
-     *
1270
-     * @param EE_Question_Group|int $question_group_id_or_obj
1271
-     * @param bool $for_primary if true, the question group will be removed from the primary
1272
-     *                                           registrant, if false will be removed from others. default: false
1273
-     * @return EE_Base_Class|EE_Question_Group
1274
-     * @throws EE_Error
1275
-     * @throws InvalidArgumentException
1276
-     * @throws ReflectionException
1277
-     * @throws InvalidDataTypeException
1278
-     * @throws InvalidInterfaceException
1279
-     */
1280
-    public function remove_question_group($question_group_id_or_obj, $for_primary = false)
1281
-    {
1282
-        // If the question group is used for the other type (primary or additional)
1283
-        // then just update it. If not, delete it outright.
1284
-        $existing_relation = $this->get_first_related(
1285
-            'Event_Question_Group',
1286
-            [
1287
-                [
1288
-                    'QSG_ID' => EEM_Question_Group::instance()->ensure_is_ID($question_group_id_or_obj)
1289
-                ]
1290
-            ]
1291
-        );
1292
-        $field_to_update = EEM_Event_Question_Group::instance()->field_name_for_category($for_primary);
1293
-        $other_field = EEM_Event_Question_Group::instance()->field_name_for_category(! $for_primary);
1294
-        if ($existing_relation->get($other_field) === false) {
1295
-            // Delete it. It's now no longer for primary or additional question groups.
1296
-            return $this->_remove_relation_to($question_group_id_or_obj, 'Question_Group');
1297
-        }
1298
-        // Just update it. They'll still use this question group for the other category
1299
-        $existing_relation->save(
1300
-            [
1301
-                $field_to_update => false
1302
-            ]
1303
-        );
1304
-    }
1305
-
1306
-
1307
-    /**
1308
-     * Gets all the question groups, ordering them by QSG_order ascending
1309
-     *
1310
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1311
-     * @return EE_Base_Class[]|EE_Question_Group[]
1312
-     * @throws EE_Error
1313
-     */
1314
-    public function question_groups($query_params = array())
1315
-    {
1316
-        $query_params = ! empty($query_params) ? $query_params : array('order_by' => array('QSG_order' => 'ASC'));
1317
-        return $this->get_many_related('Question_Group', $query_params);
1318
-    }
1319
-
1320
-
1321
-    /**
1322
-     * Implementation for EEI_Has_Icon interface method.
1323
-     *
1324
-     * @see EEI_Visual_Representation for comments
1325
-     * @return string
1326
-     */
1327
-    public function get_icon()
1328
-    {
1329
-        return '<span class="dashicons dashicons-flag"></span>';
1330
-    }
1331
-
1332
-
1333
-    /**
1334
-     * Implementation for EEI_Admin_Links interface method.
1335
-     *
1336
-     * @see EEI_Admin_Links for comments
1337
-     * @return string
1338
-     * @throws EE_Error
1339
-     */
1340
-    public function get_admin_details_link()
1341
-    {
1342
-        return $this->get_admin_edit_link();
1343
-    }
1344
-
1345
-
1346
-    /**
1347
-     * Implementation for EEI_Admin_Links interface method.
1348
-     *
1349
-     * @see EEI_Admin_Links for comments
1350
-     * @return string
1351
-     * @throws EE_Error
1352
-     */
1353
-    public function get_admin_edit_link()
1354
-    {
1355
-        return EEH_URL::add_query_args_and_nonce(
1356
-            array(
1357
-                'page'   => 'espresso_events',
1358
-                'action' => 'edit',
1359
-                'post'   => $this->ID(),
1360
-            ),
1361
-            admin_url('admin.php')
1362
-        );
1363
-    }
1364
-
1365
-
1366
-    /**
1367
-     * Implementation for EEI_Admin_Links interface method.
1368
-     *
1369
-     * @see EEI_Admin_Links for comments
1370
-     * @return string
1371
-     */
1372
-    public function get_admin_settings_link()
1373
-    {
1374
-        return EEH_URL::add_query_args_and_nonce(
1375
-            array(
1376
-                'page'   => 'espresso_events',
1377
-                'action' => 'default_event_settings',
1378
-            ),
1379
-            admin_url('admin.php')
1380
-        );
1381
-    }
1382
-
1383
-
1384
-    /**
1385
-     * Implementation for EEI_Admin_Links interface method.
1386
-     *
1387
-     * @see EEI_Admin_Links for comments
1388
-     * @return string
1389
-     */
1390
-    public function get_admin_overview_link()
1391
-    {
1392
-        return EEH_URL::add_query_args_and_nonce(
1393
-            array(
1394
-                'page'   => 'espresso_events',
1395
-                'action' => 'default',
1396
-            ),
1397
-            admin_url('admin.php')
1398
-        );
1399
-    }
18
+	/**
19
+	 * cached value for the the logical active status for the event
20
+	 *
21
+	 * @see get_active_status()
22
+	 * @var string
23
+	 */
24
+	protected $_active_status = '';
25
+
26
+	/**
27
+	 * This is just used for caching the Primary Datetime for the Event on initial retrieval
28
+	 *
29
+	 * @var EE_Datetime
30
+	 */
31
+	protected $_Primary_Datetime;
32
+
33
+	/**
34
+	 * @var EventSpacesCalculator $available_spaces_calculator
35
+	 */
36
+	protected $available_spaces_calculator;
37
+
38
+
39
+	/**
40
+	 * @param array  $props_n_values          incoming values
41
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
42
+	 *                                        used.)
43
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
44
+	 *                                        date_format and the second value is the time format
45
+	 * @return EE_Event
46
+	 * @throws EE_Error
47
+	 */
48
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
49
+	{
50
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
51
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
52
+	}
53
+
54
+
55
+	/**
56
+	 * @param array  $props_n_values  incoming values from the database
57
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
58
+	 *                                the website will be used.
59
+	 * @return EE_Event
60
+	 * @throws EE_Error
61
+	 */
62
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
63
+	{
64
+		return new self($props_n_values, true, $timezone);
65
+	}
66
+
67
+
68
+	/**
69
+	 * @return EventSpacesCalculator
70
+	 * @throws \EE_Error
71
+	 */
72
+	public function getAvailableSpacesCalculator()
73
+	{
74
+		if (! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
75
+			$this->available_spaces_calculator = new EventSpacesCalculator($this);
76
+		}
77
+		return $this->available_spaces_calculator;
78
+	}
79
+
80
+
81
+	/**
82
+	 * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
83
+	 *
84
+	 * @param string $field_name
85
+	 * @param mixed  $field_value
86
+	 * @param bool   $use_default
87
+	 * @throws EE_Error
88
+	 */
89
+	public function set($field_name, $field_value, $use_default = false)
90
+	{
91
+		switch ($field_name) {
92
+			case 'status':
93
+				$this->set_status($field_value, $use_default);
94
+				break;
95
+			default:
96
+				parent::set($field_name, $field_value, $use_default);
97
+		}
98
+	}
99
+
100
+
101
+	/**
102
+	 *    set_status
103
+	 * Checks if event status is being changed to SOLD OUT
104
+	 * and updates event meta data with previous event status
105
+	 * so that we can revert things if/when the event is no longer sold out
106
+	 *
107
+	 * @access public
108
+	 * @param string $new_status
109
+	 * @param bool   $use_default
110
+	 * @return void
111
+	 * @throws EE_Error
112
+	 */
113
+	public function set_status($new_status = null, $use_default = false)
114
+	{
115
+		// if nothing is set, and we aren't explicitly wanting to reset the status, then just leave
116
+		if (empty($new_status) && ! $use_default) {
117
+			return;
118
+		}
119
+		// get current Event status
120
+		$old_status = $this->status();
121
+		// if status has changed
122
+		if ($old_status !== $new_status) {
123
+			// TO sold_out
124
+			if ($new_status === EEM_Event::sold_out) {
125
+				// save the previous event status so that we can revert if the event is no longer sold out
126
+				$this->add_post_meta('_previous_event_status', $old_status);
127
+				do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $new_status);
128
+				// OR FROM  sold_out
129
+			} elseif ($old_status === EEM_Event::sold_out) {
130
+				$this->delete_post_meta('_previous_event_status');
131
+				do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $new_status);
132
+			}
133
+			// clear out the active status so that it gets reset the next time it is requested
134
+			$this->_active_status = null;
135
+			// update status
136
+			parent::set('status', $new_status, $use_default);
137
+			do_action('AHEE__EE_Event__set_status__after_update', $this);
138
+			return;
139
+		}
140
+		// even though the old value matches the new value, it's still good to
141
+		// allow the parent set method to have a say
142
+		parent::set('status', $new_status, $use_default);
143
+	}
144
+
145
+
146
+	/**
147
+	 * Gets all the datetimes for this event
148
+	 *
149
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
150
+	 * @return EE_Base_Class[]|EE_Datetime[]
151
+	 * @throws EE_Error
152
+	 */
153
+	public function datetimes($query_params = array())
154
+	{
155
+		return $this->get_many_related('Datetime', $query_params);
156
+	}
157
+
158
+
159
+	/**
160
+	 * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
161
+	 *
162
+	 * @return EE_Base_Class[]|EE_Datetime[]
163
+	 * @throws EE_Error
164
+	 */
165
+	public function datetimes_in_chronological_order()
166
+	{
167
+		return $this->get_many_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC')));
168
+	}
169
+
170
+
171
+	/**
172
+	 * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
173
+	 * @darren, we should probably UNSET timezone on the EEM_Datetime model
174
+	 * after running our query, so that this timezone isn't set for EVERY query
175
+	 * on EEM_Datetime for the rest of the request, no?
176
+	 *
177
+	 * @param boolean $show_expired whether or not to include expired events
178
+	 * @param boolean $show_deleted whether or not to include deleted events
179
+	 * @param null    $limit
180
+	 * @return EE_Datetime[]
181
+	 * @throws EE_Error
182
+	 */
183
+	public function datetimes_ordered($show_expired = true, $show_deleted = false, $limit = null)
184
+	{
185
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
186
+			$this->ID(),
187
+			$show_expired,
188
+			$show_deleted,
189
+			$limit
190
+		);
191
+	}
192
+
193
+
194
+	/**
195
+	 * Returns one related datetime. Mostly only used by some legacy code.
196
+	 *
197
+	 * @return EE_Base_Class|EE_Datetime
198
+	 * @throws EE_Error
199
+	 */
200
+	public function first_datetime()
201
+	{
202
+		return $this->get_first_related('Datetime');
203
+	}
204
+
205
+
206
+	/**
207
+	 * Returns the 'primary' datetime for the event
208
+	 *
209
+	 * @param bool $try_to_exclude_expired
210
+	 * @param bool $try_to_exclude_deleted
211
+	 * @return EE_Datetime
212
+	 * @throws EE_Error
213
+	 */
214
+	public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
215
+	{
216
+		if (! empty($this->_Primary_Datetime)) {
217
+			return $this->_Primary_Datetime;
218
+		}
219
+		$this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
220
+			$this->ID(),
221
+			$try_to_exclude_expired,
222
+			$try_to_exclude_deleted
223
+		);
224
+		return $this->_Primary_Datetime;
225
+	}
226
+
227
+
228
+	/**
229
+	 * Gets all the tickets available for purchase of this event
230
+	 *
231
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
232
+	 * @return EE_Base_Class[]|EE_Ticket[]
233
+	 * @throws EE_Error
234
+	 */
235
+	public function tickets($query_params = array())
236
+	{
237
+		// first get all datetimes
238
+		$datetimes = $this->datetimes_ordered();
239
+		if (! $datetimes) {
240
+			return array();
241
+		}
242
+		$datetime_ids = array();
243
+		foreach ($datetimes as $datetime) {
244
+			$datetime_ids[] = $datetime->ID();
245
+		}
246
+		$where_params = array('Datetime.DTT_ID' => array('IN', $datetime_ids));
247
+		// if incoming $query_params has where conditions let's merge but not override existing.
248
+		if (is_array($query_params) && isset($query_params[0])) {
249
+			$where_params = array_merge($query_params[0], $where_params);
250
+			unset($query_params[0]);
251
+		}
252
+		// now add $where_params to $query_params
253
+		$query_params[0] = $where_params;
254
+		return EEM_Ticket::instance()->get_all($query_params);
255
+	}
256
+
257
+
258
+	/**
259
+	 * get all unexpired untrashed tickets
260
+	 *
261
+	 * @return EE_Ticket[]
262
+	 * @throws EE_Error
263
+	 */
264
+	public function active_tickets()
265
+	{
266
+		return $this->tickets(
267
+			array(
268
+				array(
269
+					'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
270
+					'TKT_deleted'  => false,
271
+				),
272
+			)
273
+		);
274
+	}
275
+
276
+
277
+	/**
278
+	 * @return bool
279
+	 * @throws EE_Error
280
+	 */
281
+	public function additional_limit()
282
+	{
283
+		return $this->get('EVT_additional_limit');
284
+	}
285
+
286
+
287
+	/**
288
+	 * @return bool
289
+	 * @throws EE_Error
290
+	 */
291
+	public function allow_overflow()
292
+	{
293
+		return $this->get('EVT_allow_overflow');
294
+	}
295
+
296
+
297
+	/**
298
+	 * @return bool
299
+	 * @throws EE_Error
300
+	 */
301
+	public function created()
302
+	{
303
+		return $this->get('EVT_created');
304
+	}
305
+
306
+
307
+	/**
308
+	 * @return bool
309
+	 * @throws EE_Error
310
+	 */
311
+	public function description()
312
+	{
313
+		return $this->get('EVT_desc');
314
+	}
315
+
316
+
317
+	/**
318
+	 * Runs do_shortcode and wpautop on the description
319
+	 *
320
+	 * @return string of html
321
+	 * @throws EE_Error
322
+	 */
323
+	public function description_filtered()
324
+	{
325
+		return $this->get_pretty('EVT_desc');
326
+	}
327
+
328
+
329
+	/**
330
+	 * @return bool
331
+	 * @throws EE_Error
332
+	 */
333
+	public function display_description()
334
+	{
335
+		return $this->get('EVT_display_desc');
336
+	}
337
+
338
+
339
+	/**
340
+	 * @return bool
341
+	 * @throws EE_Error
342
+	 */
343
+	public function display_ticket_selector()
344
+	{
345
+		return (bool) $this->get('EVT_display_ticket_selector');
346
+	}
347
+
348
+
349
+	/**
350
+	 * @return bool
351
+	 * @throws EE_Error
352
+	 */
353
+	public function external_url()
354
+	{
355
+		return $this->get('EVT_external_URL');
356
+	}
357
+
358
+
359
+	/**
360
+	 * @return bool
361
+	 * @throws EE_Error
362
+	 */
363
+	public function member_only()
364
+	{
365
+		return $this->get('EVT_member_only');
366
+	}
367
+
368
+
369
+	/**
370
+	 * @return bool
371
+	 * @throws EE_Error
372
+	 */
373
+	public function phone()
374
+	{
375
+		return $this->get('EVT_phone');
376
+	}
377
+
378
+
379
+	/**
380
+	 * @return bool
381
+	 * @throws EE_Error
382
+	 */
383
+	public function modified()
384
+	{
385
+		return $this->get('EVT_modified');
386
+	}
387
+
388
+
389
+	/**
390
+	 * @return bool
391
+	 * @throws EE_Error
392
+	 */
393
+	public function name()
394
+	{
395
+		return $this->get('EVT_name');
396
+	}
397
+
398
+
399
+	/**
400
+	 * @return bool
401
+	 * @throws EE_Error
402
+	 */
403
+	public function order()
404
+	{
405
+		return $this->get('EVT_order');
406
+	}
407
+
408
+
409
+	/**
410
+	 * @return bool|string
411
+	 * @throws EE_Error
412
+	 */
413
+	public function default_registration_status()
414
+	{
415
+		$event_default_registration_status = $this->get('EVT_default_registration_status');
416
+		return ! empty($event_default_registration_status)
417
+			? $event_default_registration_status
418
+			: EE_Registry::instance()->CFG->registration->default_STS_ID;
419
+	}
420
+
421
+
422
+	/**
423
+	 * @param int  $num_words
424
+	 * @param null $more
425
+	 * @param bool $not_full_desc
426
+	 * @return bool|string
427
+	 * @throws EE_Error
428
+	 */
429
+	public function short_description($num_words = 55, $more = null, $not_full_desc = false)
430
+	{
431
+		$short_desc = $this->get('EVT_short_desc');
432
+		if (! empty($short_desc) || $not_full_desc) {
433
+			return $short_desc;
434
+		}
435
+		$full_desc = $this->get('EVT_desc');
436
+		return wp_trim_words($full_desc, $num_words, $more);
437
+	}
438
+
439
+
440
+	/**
441
+	 * @return bool
442
+	 * @throws EE_Error
443
+	 */
444
+	public function slug()
445
+	{
446
+		return $this->get('EVT_slug');
447
+	}
448
+
449
+
450
+	/**
451
+	 * @return bool
452
+	 * @throws EE_Error
453
+	 */
454
+	public function timezone_string()
455
+	{
456
+		return $this->get('EVT_timezone_string');
457
+	}
458
+
459
+
460
+	/**
461
+	 * @return bool
462
+	 * @throws EE_Error
463
+	 */
464
+	public function visible_on()
465
+	{
466
+		return $this->get('EVT_visible_on');
467
+	}
468
+
469
+
470
+	/**
471
+	 * @return int
472
+	 * @throws EE_Error
473
+	 */
474
+	public function wp_user()
475
+	{
476
+		return $this->get('EVT_wp_user');
477
+	}
478
+
479
+
480
+	/**
481
+	 * @return bool
482
+	 * @throws EE_Error
483
+	 */
484
+	public function donations()
485
+	{
486
+		return $this->get('EVT_donations');
487
+	}
488
+
489
+
490
+	/**
491
+	 * @param $limit
492
+	 * @throws EE_Error
493
+	 */
494
+	public function set_additional_limit($limit)
495
+	{
496
+		$this->set('EVT_additional_limit', $limit);
497
+	}
498
+
499
+
500
+	/**
501
+	 * @param $created
502
+	 * @throws EE_Error
503
+	 */
504
+	public function set_created($created)
505
+	{
506
+		$this->set('EVT_created', $created);
507
+	}
508
+
509
+
510
+	/**
511
+	 * @param $desc
512
+	 * @throws EE_Error
513
+	 */
514
+	public function set_description($desc)
515
+	{
516
+		$this->set('EVT_desc', $desc);
517
+	}
518
+
519
+
520
+	/**
521
+	 * @param $display_desc
522
+	 * @throws EE_Error
523
+	 */
524
+	public function set_display_description($display_desc)
525
+	{
526
+		$this->set('EVT_display_desc', $display_desc);
527
+	}
528
+
529
+
530
+	/**
531
+	 * @param $display_ticket_selector
532
+	 * @throws EE_Error
533
+	 */
534
+	public function set_display_ticket_selector($display_ticket_selector)
535
+	{
536
+		$this->set('EVT_display_ticket_selector', $display_ticket_selector);
537
+	}
538
+
539
+
540
+	/**
541
+	 * @param $external_url
542
+	 * @throws EE_Error
543
+	 */
544
+	public function set_external_url($external_url)
545
+	{
546
+		$this->set('EVT_external_URL', $external_url);
547
+	}
548
+
549
+
550
+	/**
551
+	 * @param $member_only
552
+	 * @throws EE_Error
553
+	 */
554
+	public function set_member_only($member_only)
555
+	{
556
+		$this->set('EVT_member_only', $member_only);
557
+	}
558
+
559
+
560
+	/**
561
+	 * @param $event_phone
562
+	 * @throws EE_Error
563
+	 */
564
+	public function set_event_phone($event_phone)
565
+	{
566
+		$this->set('EVT_phone', $event_phone);
567
+	}
568
+
569
+
570
+	/**
571
+	 * @param $modified
572
+	 * @throws EE_Error
573
+	 */
574
+	public function set_modified($modified)
575
+	{
576
+		$this->set('EVT_modified', $modified);
577
+	}
578
+
579
+
580
+	/**
581
+	 * @param $name
582
+	 * @throws EE_Error
583
+	 */
584
+	public function set_name($name)
585
+	{
586
+		$this->set('EVT_name', $name);
587
+	}
588
+
589
+
590
+	/**
591
+	 * @param $order
592
+	 * @throws EE_Error
593
+	 */
594
+	public function set_order($order)
595
+	{
596
+		$this->set('EVT_order', $order);
597
+	}
598
+
599
+
600
+	/**
601
+	 * @param $short_desc
602
+	 * @throws EE_Error
603
+	 */
604
+	public function set_short_description($short_desc)
605
+	{
606
+		$this->set('EVT_short_desc', $short_desc);
607
+	}
608
+
609
+
610
+	/**
611
+	 * @param $slug
612
+	 * @throws EE_Error
613
+	 */
614
+	public function set_slug($slug)
615
+	{
616
+		$this->set('EVT_slug', $slug);
617
+	}
618
+
619
+
620
+	/**
621
+	 * @param $timezone_string
622
+	 * @throws EE_Error
623
+	 */
624
+	public function set_timezone_string($timezone_string)
625
+	{
626
+		$this->set('EVT_timezone_string', $timezone_string);
627
+	}
628
+
629
+
630
+	/**
631
+	 * @param $visible_on
632
+	 * @throws EE_Error
633
+	 */
634
+	public function set_visible_on($visible_on)
635
+	{
636
+		$this->set('EVT_visible_on', $visible_on);
637
+	}
638
+
639
+
640
+	/**
641
+	 * @param $wp_user
642
+	 * @throws EE_Error
643
+	 */
644
+	public function set_wp_user($wp_user)
645
+	{
646
+		$this->set('EVT_wp_user', $wp_user);
647
+	}
648
+
649
+
650
+	/**
651
+	 * @param $default_registration_status
652
+	 * @throws EE_Error
653
+	 */
654
+	public function set_default_registration_status($default_registration_status)
655
+	{
656
+		$this->set('EVT_default_registration_status', $default_registration_status);
657
+	}
658
+
659
+
660
+	/**
661
+	 * @param $donations
662
+	 * @throws EE_Error
663
+	 */
664
+	public function set_donations($donations)
665
+	{
666
+		$this->set('EVT_donations', $donations);
667
+	}
668
+
669
+
670
+	/**
671
+	 * Adds a venue to this event
672
+	 *
673
+	 * @param EE_Venue /int $venue_id_or_obj
674
+	 * @return EE_Base_Class|EE_Venue
675
+	 * @throws EE_Error
676
+	 */
677
+	public function add_venue($venue_id_or_obj)
678
+	{
679
+		return $this->_add_relation_to($venue_id_or_obj, 'Venue');
680
+	}
681
+
682
+
683
+	/**
684
+	 * Removes a venue from the event
685
+	 *
686
+	 * @param EE_Venue /int $venue_id_or_obj
687
+	 * @return EE_Base_Class|EE_Venue
688
+	 * @throws EE_Error
689
+	 */
690
+	public function remove_venue($venue_id_or_obj)
691
+	{
692
+		return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
693
+	}
694
+
695
+
696
+	/**
697
+	 * Gets all the venues related ot the event. May provide additional $query_params if desired
698
+	 *
699
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
700
+	 * @return EE_Base_Class[]|EE_Venue[]
701
+	 * @throws EE_Error
702
+	 */
703
+	public function venues($query_params = array())
704
+	{
705
+		return $this->get_many_related('Venue', $query_params);
706
+	}
707
+
708
+
709
+	/**
710
+	 * check if event id is present and if event is published
711
+	 *
712
+	 * @access public
713
+	 * @return boolean true yes, false no
714
+	 * @throws EE_Error
715
+	 */
716
+	private function _has_ID_and_is_published()
717
+	{
718
+		// first check if event id is present and not NULL,
719
+		// then check if this event is published (or any of the equivalent "published" statuses)
720
+		return
721
+			$this->ID() && $this->ID() !== null
722
+			&& (
723
+				$this->status() === 'publish'
724
+				|| $this->status() === EEM_Event::sold_out
725
+				|| $this->status() === EEM_Event::postponed
726
+				|| $this->status() === EEM_Event::cancelled
727
+			);
728
+	}
729
+
730
+
731
+	/**
732
+	 * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
733
+	 *
734
+	 * @access public
735
+	 * @return boolean true yes, false no
736
+	 * @throws EE_Error
737
+	 */
738
+	public function is_upcoming()
739
+	{
740
+		// check if event id is present and if this event is published
741
+		if ($this->is_inactive()) {
742
+			return false;
743
+		}
744
+		// set initial value
745
+		$upcoming = false;
746
+		// next let's get all datetimes and loop through them
747
+		$datetimes = $this->datetimes_in_chronological_order();
748
+		foreach ($datetimes as $datetime) {
749
+			if ($datetime instanceof EE_Datetime) {
750
+				// if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
751
+				if ($datetime->is_expired()) {
752
+					continue;
753
+				}
754
+				// if this dtt is active then we return false.
755
+				if ($datetime->is_active()) {
756
+					return false;
757
+				}
758
+				// otherwise let's check upcoming status
759
+				$upcoming = $datetime->is_upcoming();
760
+			}
761
+		}
762
+		return $upcoming;
763
+	}
764
+
765
+
766
+	/**
767
+	 * @return bool
768
+	 * @throws EE_Error
769
+	 */
770
+	public function is_active()
771
+	{
772
+		// check if event id is present and if this event is published
773
+		if ($this->is_inactive()) {
774
+			return false;
775
+		}
776
+		// set initial value
777
+		$active = false;
778
+		// next let's get all datetimes and loop through them
779
+		$datetimes = $this->datetimes_in_chronological_order();
780
+		foreach ($datetimes as $datetime) {
781
+			if ($datetime instanceof EE_Datetime) {
782
+				// if this dtt is expired then we continue cause one of the other datetimes might be active.
783
+				if ($datetime->is_expired()) {
784
+					continue;
785
+				}
786
+				// if this dtt is upcoming then we return false.
787
+				if ($datetime->is_upcoming()) {
788
+					return false;
789
+				}
790
+				// otherwise let's check active status
791
+				$active = $datetime->is_active();
792
+			}
793
+		}
794
+		return $active;
795
+	}
796
+
797
+
798
+	/**
799
+	 * @return bool
800
+	 * @throws EE_Error
801
+	 */
802
+	public function is_expired()
803
+	{
804
+		// check if event id is present and if this event is published
805
+		if ($this->is_inactive()) {
806
+			return false;
807
+		}
808
+		// set initial value
809
+		$expired = false;
810
+		// first let's get all datetimes and loop through them
811
+		$datetimes = $this->datetimes_in_chronological_order();
812
+		foreach ($datetimes as $datetime) {
813
+			if ($datetime instanceof EE_Datetime) {
814
+				// if this dtt is upcoming or active then we return false.
815
+				if ($datetime->is_upcoming() || $datetime->is_active()) {
816
+					return false;
817
+				}
818
+				// otherwise let's check active status
819
+				$expired = $datetime->is_expired();
820
+			}
821
+		}
822
+		return $expired;
823
+	}
824
+
825
+
826
+	/**
827
+	 * @return bool
828
+	 * @throws EE_Error
829
+	 */
830
+	public function is_inactive()
831
+	{
832
+		// check if event id is present and if this event is published
833
+		if ($this->_has_ID_and_is_published()) {
834
+			return false;
835
+		}
836
+		return true;
837
+	}
838
+
839
+
840
+	/**
841
+	 * calculate spaces remaining based on "saleable" tickets
842
+	 *
843
+	 * @param array $tickets
844
+	 * @param bool  $filtered
845
+	 * @return int|float
846
+	 * @throws EE_Error
847
+	 * @throws DomainException
848
+	 * @throws UnexpectedEntityException
849
+	 */
850
+	public function spaces_remaining($tickets = array(), $filtered = true)
851
+	{
852
+		$this->getAvailableSpacesCalculator()->setActiveTickets($tickets);
853
+		$spaces_remaining = $this->getAvailableSpacesCalculator()->spacesRemaining();
854
+		return $filtered
855
+			? apply_filters(
856
+				'FHEE_EE_Event__spaces_remaining',
857
+				$spaces_remaining,
858
+				$this,
859
+				$tickets
860
+			)
861
+			: $spaces_remaining;
862
+	}
863
+
864
+
865
+	/**
866
+	 *    perform_sold_out_status_check
867
+	 *    checks all of this events's datetime  reg_limit - sold values to determine if ANY datetimes have spaces
868
+	 *    available... if NOT, then the event status will get toggled to 'sold_out'
869
+	 *
870
+	 * @return bool    return the ACTUAL sold out state.
871
+	 * @throws EE_Error
872
+	 * @throws DomainException
873
+	 * @throws UnexpectedEntityException
874
+	 */
875
+	public function perform_sold_out_status_check()
876
+	{
877
+		// get all unexpired untrashed tickets
878
+		$tickets = $this->tickets(
879
+			array(
880
+				array('TKT_deleted' => false),
881
+				'order_by' => array('TKT_qty' => 'ASC'),
882
+			)
883
+		);
884
+		$all_expired = true;
885
+		foreach ($tickets as $ticket) {
886
+			if (! $ticket->is_expired()) {
887
+				$all_expired = false;
888
+				break;
889
+			}
890
+		}
891
+		// if all the tickets are just expired, then don't update the event status to sold out
892
+		if ($all_expired) {
893
+			return true;
894
+		}
895
+		$spaces_remaining = $this->spaces_remaining($tickets);
896
+		if ($spaces_remaining < 1) {
897
+			$this->set_status(EEM_Event::sold_out);
898
+			$this->save();
899
+			$sold_out = true;
900
+		} else {
901
+			$sold_out = false;
902
+			// was event previously marked as sold out ?
903
+			if ($this->status() === EEM_Event::sold_out) {
904
+				// revert status to previous value, if it was set
905
+				$previous_event_status = $this->get_post_meta('_previous_event_status', true);
906
+				if ($previous_event_status) {
907
+					$this->set_status($previous_event_status);
908
+					$this->save();
909
+				}
910
+			}
911
+		}
912
+		do_action('AHEE__EE_Event__perform_sold_out_status_check__end', $this, $sold_out, $spaces_remaining, $tickets);
913
+		return $sold_out;
914
+	}
915
+
916
+
917
+	/**
918
+	 * This returns the total remaining spaces for sale on this event.
919
+	 *
920
+	 * @uses EE_Event::total_available_spaces()
921
+	 * @return float|int
922
+	 * @throws EE_Error
923
+	 * @throws DomainException
924
+	 * @throws UnexpectedEntityException
925
+	 */
926
+	public function spaces_remaining_for_sale()
927
+	{
928
+		return $this->total_available_spaces(true);
929
+	}
930
+
931
+
932
+	/**
933
+	 * This returns the total spaces available for an event
934
+	 * while considering all the qtys on the tickets and the reg limits
935
+	 * on the datetimes attached to this event.
936
+	 *
937
+	 * @param   bool $consider_sold Whether to consider any tickets that have already sold in our calculation.
938
+	 *                              If this is false, then we return the most tickets that could ever be sold
939
+	 *                              for this event with the datetime and tickets setup on the event under optimal
940
+	 *                              selling conditions.  Otherwise we return a live calculation of spaces available
941
+	 *                              based on tickets sold.  Depending on setup and stage of sales, this
942
+	 *                              may appear to equal remaining tickets.  However, the more tickets are
943
+	 *                              sold out, the more accurate the "live" total is.
944
+	 * @return float|int
945
+	 * @throws EE_Error
946
+	 * @throws DomainException
947
+	 * @throws UnexpectedEntityException
948
+	 */
949
+	public function total_available_spaces($consider_sold = false)
950
+	{
951
+		$spaces_available = $consider_sold
952
+			? $this->getAvailableSpacesCalculator()->spacesRemaining()
953
+			: $this->getAvailableSpacesCalculator()->totalSpacesAvailable();
954
+		return apply_filters(
955
+			'FHEE_EE_Event__total_available_spaces__spaces_available',
956
+			$spaces_available,
957
+			$this,
958
+			$this->getAvailableSpacesCalculator()->getDatetimes(),
959
+			$this->getAvailableSpacesCalculator()->getActiveTickets()
960
+		);
961
+	}
962
+
963
+
964
+	/**
965
+	 * Checks if the event is set to sold out
966
+	 *
967
+	 * @param  bool $actual whether or not to perform calculations to not only figure the
968
+	 *                      actual status but also to flip the status if necessary to sold
969
+	 *                      out If false, we just check the existing status of the event
970
+	 * @return boolean
971
+	 * @throws EE_Error
972
+	 */
973
+	public function is_sold_out($actual = false)
974
+	{
975
+		if (! $actual) {
976
+			return $this->status() === EEM_Event::sold_out;
977
+		}
978
+		return $this->perform_sold_out_status_check();
979
+	}
980
+
981
+
982
+	/**
983
+	 * Checks if the event is marked as postponed
984
+	 *
985
+	 * @return boolean
986
+	 */
987
+	public function is_postponed()
988
+	{
989
+		return $this->status() === EEM_Event::postponed;
990
+	}
991
+
992
+
993
+	/**
994
+	 * Checks if the event is marked as cancelled
995
+	 *
996
+	 * @return boolean
997
+	 */
998
+	public function is_cancelled()
999
+	{
1000
+		return $this->status() === EEM_Event::cancelled;
1001
+	}
1002
+
1003
+
1004
+	/**
1005
+	 * Get the logical active status in a hierarchical order for all the datetimes.  Note
1006
+	 * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1007
+	 * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1008
+	 * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1009
+	 * the event is considered expired.
1010
+	 * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a
1011
+	 * status set on the EVENT when it is not published and thus is done
1012
+	 *
1013
+	 * @param bool $reset
1014
+	 * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1015
+	 * @throws EE_Error
1016
+	 */
1017
+	public function get_active_status($reset = false)
1018
+	{
1019
+		// if the active status has already been set, then just use that value (unless we are resetting it)
1020
+		if (! empty($this->_active_status) && ! $reset) {
1021
+			return $this->_active_status;
1022
+		}
1023
+		// first check if event id is present on this object
1024
+		if (! $this->ID()) {
1025
+			return false;
1026
+		}
1027
+		$where_params_for_event = array(array('EVT_ID' => $this->ID()));
1028
+		// if event is published:
1029
+		if ($this->status() === 'publish') {
1030
+			// active?
1031
+			if (EEM_Datetime::instance()->get_datetime_count_for_status(
1032
+				EE_Datetime::active,
1033
+				$where_params_for_event
1034
+			) > 0) {
1035
+				$this->_active_status = EE_Datetime::active;
1036
+			} else {
1037
+				// upcoming?
1038
+				if (EEM_Datetime::instance()->get_datetime_count_for_status(
1039
+					EE_Datetime::upcoming,
1040
+					$where_params_for_event
1041
+				) > 0) {
1042
+					$this->_active_status = EE_Datetime::upcoming;
1043
+				} else {
1044
+					// expired?
1045
+					if (EEM_Datetime::instance()->get_datetime_count_for_status(
1046
+						EE_Datetime::expired,
1047
+						$where_params_for_event
1048
+					) > 0
1049
+					) {
1050
+						$this->_active_status = EE_Datetime::expired;
1051
+					} else {
1052
+						// it would be odd if things make it this far because it basically means there are no datetime's
1053
+						// attached to the event.  So in this case it will just be considered inactive.
1054
+						$this->_active_status = EE_Datetime::inactive;
1055
+					}
1056
+				}
1057
+			}
1058
+		} else {
1059
+			// the event is not published, so let's just set it's active status according to its' post status
1060
+			switch ($this->status()) {
1061
+				case EEM_Event::sold_out:
1062
+					$this->_active_status = EE_Datetime::sold_out;
1063
+					break;
1064
+				case EEM_Event::cancelled:
1065
+					$this->_active_status = EE_Datetime::cancelled;
1066
+					break;
1067
+				case EEM_Event::postponed:
1068
+					$this->_active_status = EE_Datetime::postponed;
1069
+					break;
1070
+				default:
1071
+					$this->_active_status = EE_Datetime::inactive;
1072
+			}
1073
+		}
1074
+		return $this->_active_status;
1075
+	}
1076
+
1077
+
1078
+	/**
1079
+	 *    pretty_active_status
1080
+	 *
1081
+	 * @access public
1082
+	 * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1083
+	 * @return mixed void|string
1084
+	 * @throws EE_Error
1085
+	 */
1086
+	public function pretty_active_status($echo = true)
1087
+	{
1088
+		$active_status = $this->get_active_status();
1089
+		$status = '<span class="ee-status event-active-status-'
1090
+				  . $active_status
1091
+				  . '">'
1092
+				  . EEH_Template::pretty_status($active_status, false, 'sentence')
1093
+				  . '</span>';
1094
+		if ($echo) {
1095
+			echo $status;
1096
+			return '';
1097
+		}
1098
+		return $status;
1099
+	}
1100
+
1101
+
1102
+	/**
1103
+	 * @return bool|int
1104
+	 * @throws EE_Error
1105
+	 */
1106
+	public function get_number_of_tickets_sold()
1107
+	{
1108
+		$tkt_sold = 0;
1109
+		if (! $this->ID()) {
1110
+			return 0;
1111
+		}
1112
+		$datetimes = $this->datetimes();
1113
+		foreach ($datetimes as $datetime) {
1114
+			if ($datetime instanceof EE_Datetime) {
1115
+				$tkt_sold += $datetime->sold();
1116
+			}
1117
+		}
1118
+		return $tkt_sold;
1119
+	}
1120
+
1121
+
1122
+	/**
1123
+	 * This just returns a count of all the registrations for this event
1124
+	 *
1125
+	 * @access  public
1126
+	 * @return int
1127
+	 * @throws EE_Error
1128
+	 */
1129
+	public function get_count_of_all_registrations()
1130
+	{
1131
+		return EEM_Event::instance()->count_related($this, 'Registration');
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 * This returns the ticket with the earliest start time that is
1137
+	 * available for this event (across all datetimes attached to the event)
1138
+	 *
1139
+	 * @return EE_Base_Class|EE_Ticket|null
1140
+	 * @throws EE_Error
1141
+	 */
1142
+	public function get_ticket_with_earliest_start_time()
1143
+	{
1144
+		$where['Datetime.EVT_ID'] = $this->ID();
1145
+		$query_params = array($where, 'order_by' => array('TKT_start_date' => 'ASC'));
1146
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1147
+	}
1148
+
1149
+
1150
+	/**
1151
+	 * This returns the ticket with the latest end time that is available
1152
+	 * for this event (across all datetimes attached to the event)
1153
+	 *
1154
+	 * @return EE_Base_Class|EE_Ticket|null
1155
+	 * @throws EE_Error
1156
+	 */
1157
+	public function get_ticket_with_latest_end_time()
1158
+	{
1159
+		$where['Datetime.EVT_ID'] = $this->ID();
1160
+		$query_params = array($where, 'order_by' => array('TKT_end_date' => 'DESC'));
1161
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1162
+	}
1163
+
1164
+
1165
+	/**
1166
+	 * This returns the number of different ticket types currently on sale for this event.
1167
+	 *
1168
+	 * @return int
1169
+	 * @throws EE_Error
1170
+	 */
1171
+	public function countTicketsOnSale()
1172
+	{
1173
+		$where = array(
1174
+			'Datetime.EVT_ID' => $this->ID(),
1175
+			'TKT_start_date'  => array('<', time()),
1176
+			'TKT_end_date'    => array('>', time()),
1177
+		);
1178
+		return EEM_Ticket::instance()->count(array($where));
1179
+	}
1180
+
1181
+
1182
+	/**
1183
+	 * This returns whether there are any tickets on sale for this event.
1184
+	 *
1185
+	 * @return bool true = YES tickets on sale.
1186
+	 * @throws EE_Error
1187
+	 */
1188
+	public function tickets_on_sale()
1189
+	{
1190
+		return $this->countTicketsOnSale() > 0;
1191
+	}
1192
+
1193
+
1194
+	/**
1195
+	 * Gets the URL for viewing this event on the front-end. Overrides parent
1196
+	 * to check for an external URL first
1197
+	 *
1198
+	 * @return string
1199
+	 * @throws EE_Error
1200
+	 */
1201
+	public function get_permalink()
1202
+	{
1203
+		if ($this->external_url()) {
1204
+			return $this->external_url();
1205
+		}
1206
+		return parent::get_permalink();
1207
+	}
1208
+
1209
+
1210
+	/**
1211
+	 * Gets the first term for 'espresso_event_categories' we can find
1212
+	 *
1213
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1214
+	 * @return EE_Base_Class|EE_Term|null
1215
+	 * @throws EE_Error
1216
+	 */
1217
+	public function first_event_category($query_params = array())
1218
+	{
1219
+		$query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1220
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1221
+		return EEM_Term::instance()->get_one($query_params);
1222
+	}
1223
+
1224
+
1225
+	/**
1226
+	 * Gets all terms for 'espresso_event_categories' we can find
1227
+	 *
1228
+	 * @param array $query_params
1229
+	 * @return EE_Base_Class[]|EE_Term[]
1230
+	 * @throws EE_Error
1231
+	 */
1232
+	public function get_all_event_categories($query_params = array())
1233
+	{
1234
+		$query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1235
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1236
+		return EEM_Term::instance()->get_all($query_params);
1237
+	}
1238
+
1239
+
1240
+	/**
1241
+	 * Adds a question group to this event
1242
+	 *
1243
+	 * @param EE_Question_Group|int $question_group_id_or_obj
1244
+	 * @param bool $for_primary if true, the question group will be added for the primary
1245
+	 *                                           registrant, if false will be added for others. default: false
1246
+	 * @return EE_Base_Class|EE_Question_Group
1247
+	 * @throws EE_Error
1248
+	 * @throws InvalidArgumentException
1249
+	 * @throws InvalidDataTypeException
1250
+	 * @throws InvalidInterfaceException
1251
+	 * @throws ReflectionException
1252
+	 */
1253
+	public function add_question_group($question_group_id_or_obj, $for_primary = false)
1254
+	{
1255
+		// If the row already exists, it will be updated. If it doesn't, it will be inserted.
1256
+		// That's in EE_HABTM_Relation::add_relation_to().
1257
+		return $this->_add_relation_to(
1258
+			$question_group_id_or_obj,
1259
+			'Question_Group',
1260
+			[
1261
+				EEM_Event_Question_Group::instance()->field_name_for_category($for_primary) => true
1262
+			]
1263
+		);
1264
+	}
1265
+
1266
+
1267
+	/**
1268
+	 * Removes a question group from the event
1269
+	 *
1270
+	 * @param EE_Question_Group|int $question_group_id_or_obj
1271
+	 * @param bool $for_primary if true, the question group will be removed from the primary
1272
+	 *                                           registrant, if false will be removed from others. default: false
1273
+	 * @return EE_Base_Class|EE_Question_Group
1274
+	 * @throws EE_Error
1275
+	 * @throws InvalidArgumentException
1276
+	 * @throws ReflectionException
1277
+	 * @throws InvalidDataTypeException
1278
+	 * @throws InvalidInterfaceException
1279
+	 */
1280
+	public function remove_question_group($question_group_id_or_obj, $for_primary = false)
1281
+	{
1282
+		// If the question group is used for the other type (primary or additional)
1283
+		// then just update it. If not, delete it outright.
1284
+		$existing_relation = $this->get_first_related(
1285
+			'Event_Question_Group',
1286
+			[
1287
+				[
1288
+					'QSG_ID' => EEM_Question_Group::instance()->ensure_is_ID($question_group_id_or_obj)
1289
+				]
1290
+			]
1291
+		);
1292
+		$field_to_update = EEM_Event_Question_Group::instance()->field_name_for_category($for_primary);
1293
+		$other_field = EEM_Event_Question_Group::instance()->field_name_for_category(! $for_primary);
1294
+		if ($existing_relation->get($other_field) === false) {
1295
+			// Delete it. It's now no longer for primary or additional question groups.
1296
+			return $this->_remove_relation_to($question_group_id_or_obj, 'Question_Group');
1297
+		}
1298
+		// Just update it. They'll still use this question group for the other category
1299
+		$existing_relation->save(
1300
+			[
1301
+				$field_to_update => false
1302
+			]
1303
+		);
1304
+	}
1305
+
1306
+
1307
+	/**
1308
+	 * Gets all the question groups, ordering them by QSG_order ascending
1309
+	 *
1310
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1311
+	 * @return EE_Base_Class[]|EE_Question_Group[]
1312
+	 * @throws EE_Error
1313
+	 */
1314
+	public function question_groups($query_params = array())
1315
+	{
1316
+		$query_params = ! empty($query_params) ? $query_params : array('order_by' => array('QSG_order' => 'ASC'));
1317
+		return $this->get_many_related('Question_Group', $query_params);
1318
+	}
1319
+
1320
+
1321
+	/**
1322
+	 * Implementation for EEI_Has_Icon interface method.
1323
+	 *
1324
+	 * @see EEI_Visual_Representation for comments
1325
+	 * @return string
1326
+	 */
1327
+	public function get_icon()
1328
+	{
1329
+		return '<span class="dashicons dashicons-flag"></span>';
1330
+	}
1331
+
1332
+
1333
+	/**
1334
+	 * Implementation for EEI_Admin_Links interface method.
1335
+	 *
1336
+	 * @see EEI_Admin_Links for comments
1337
+	 * @return string
1338
+	 * @throws EE_Error
1339
+	 */
1340
+	public function get_admin_details_link()
1341
+	{
1342
+		return $this->get_admin_edit_link();
1343
+	}
1344
+
1345
+
1346
+	/**
1347
+	 * Implementation for EEI_Admin_Links interface method.
1348
+	 *
1349
+	 * @see EEI_Admin_Links for comments
1350
+	 * @return string
1351
+	 * @throws EE_Error
1352
+	 */
1353
+	public function get_admin_edit_link()
1354
+	{
1355
+		return EEH_URL::add_query_args_and_nonce(
1356
+			array(
1357
+				'page'   => 'espresso_events',
1358
+				'action' => 'edit',
1359
+				'post'   => $this->ID(),
1360
+			),
1361
+			admin_url('admin.php')
1362
+		);
1363
+	}
1364
+
1365
+
1366
+	/**
1367
+	 * Implementation for EEI_Admin_Links interface method.
1368
+	 *
1369
+	 * @see EEI_Admin_Links for comments
1370
+	 * @return string
1371
+	 */
1372
+	public function get_admin_settings_link()
1373
+	{
1374
+		return EEH_URL::add_query_args_and_nonce(
1375
+			array(
1376
+				'page'   => 'espresso_events',
1377
+				'action' => 'default_event_settings',
1378
+			),
1379
+			admin_url('admin.php')
1380
+		);
1381
+	}
1382
+
1383
+
1384
+	/**
1385
+	 * Implementation for EEI_Admin_Links interface method.
1386
+	 *
1387
+	 * @see EEI_Admin_Links for comments
1388
+	 * @return string
1389
+	 */
1390
+	public function get_admin_overview_link()
1391
+	{
1392
+		return EEH_URL::add_query_args_and_nonce(
1393
+			array(
1394
+				'page'   => 'espresso_events',
1395
+				'action' => 'default',
1396
+			),
1397
+			admin_url('admin.php')
1398
+		);
1399
+	}
1400 1400
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
      */
72 72
     public function getAvailableSpacesCalculator()
73 73
     {
74
-        if (! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
74
+        if ( ! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
75 75
             $this->available_spaces_calculator = new EventSpacesCalculator($this);
76 76
         }
77 77
         return $this->available_spaces_calculator;
@@ -213,7 +213,7 @@  discard block
 block discarded – undo
213 213
      */
214 214
     public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
215 215
     {
216
-        if (! empty($this->_Primary_Datetime)) {
216
+        if ( ! empty($this->_Primary_Datetime)) {
217 217
             return $this->_Primary_Datetime;
218 218
         }
219 219
         $this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
@@ -236,7 +236,7 @@  discard block
 block discarded – undo
236 236
     {
237 237
         // first get all datetimes
238 238
         $datetimes = $this->datetimes_ordered();
239
-        if (! $datetimes) {
239
+        if ( ! $datetimes) {
240 240
             return array();
241 241
         }
242 242
         $datetime_ids = array();
@@ -429,7 +429,7 @@  discard block
 block discarded – undo
429 429
     public function short_description($num_words = 55, $more = null, $not_full_desc = false)
430 430
     {
431 431
         $short_desc = $this->get('EVT_short_desc');
432
-        if (! empty($short_desc) || $not_full_desc) {
432
+        if ( ! empty($short_desc) || $not_full_desc) {
433 433
             return $short_desc;
434 434
         }
435 435
         $full_desc = $this->get('EVT_desc');
@@ -883,7 +883,7 @@  discard block
 block discarded – undo
883 883
         );
884 884
         $all_expired = true;
885 885
         foreach ($tickets as $ticket) {
886
-            if (! $ticket->is_expired()) {
886
+            if ( ! $ticket->is_expired()) {
887 887
                 $all_expired = false;
888 888
                 break;
889 889
             }
@@ -972,7 +972,7 @@  discard block
 block discarded – undo
972 972
      */
973 973
     public function is_sold_out($actual = false)
974 974
     {
975
-        if (! $actual) {
975
+        if ( ! $actual) {
976 976
             return $this->status() === EEM_Event::sold_out;
977 977
         }
978 978
         return $this->perform_sold_out_status_check();
@@ -1017,11 +1017,11 @@  discard block
 block discarded – undo
1017 1017
     public function get_active_status($reset = false)
1018 1018
     {
1019 1019
         // if the active status has already been set, then just use that value (unless we are resetting it)
1020
-        if (! empty($this->_active_status) && ! $reset) {
1020
+        if ( ! empty($this->_active_status) && ! $reset) {
1021 1021
             return $this->_active_status;
1022 1022
         }
1023 1023
         // first check if event id is present on this object
1024
-        if (! $this->ID()) {
1024
+        if ( ! $this->ID()) {
1025 1025
             return false;
1026 1026
         }
1027 1027
         $where_params_for_event = array(array('EVT_ID' => $this->ID()));
@@ -1106,7 +1106,7 @@  discard block
 block discarded – undo
1106 1106
     public function get_number_of_tickets_sold()
1107 1107
     {
1108 1108
         $tkt_sold = 0;
1109
-        if (! $this->ID()) {
1109
+        if ( ! $this->ID()) {
1110 1110
             return 0;
1111 1111
         }
1112 1112
         $datetimes = $this->datetimes();
@@ -1290,7 +1290,7 @@  discard block
 block discarded – undo
1290 1290
             ]
1291 1291
         );
1292 1292
         $field_to_update = EEM_Event_Question_Group::instance()->field_name_for_category($for_primary);
1293
-        $other_field = EEM_Event_Question_Group::instance()->field_name_for_category(! $for_primary);
1293
+        $other_field = EEM_Event_Question_Group::instance()->field_name_for_category( ! $for_primary);
1294 1294
         if ($existing_relation->get($other_field) === false) {
1295 1295
             // Delete it. It's now no longer for primary or additional question groups.
1296 1296
             return $this->_remove_relation_to($question_group_id_or_obj, 'Question_Group');
Please login to merge, or discard this patch.
core/db_classes/EE_Registration.class.php 2 patches
Indentation   +2073 added lines, -2073 removed lines patch added patch discarded remove patch
@@ -17,2077 +17,2077 @@
 block discarded – undo
17 17
 {
18 18
 
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
-    /**
38
-     * Used to reference when a registration has been checked out.
39
-     *
40
-     * @deprecated use \EE_Checkin::status_checked_out instead
41
-     * @type int
42
-     */
43
-    const checkin_status_out = 0;
44
-
45
-
46
-    /**
47
-     * extra meta key for tracking reg status os trashed registrations
48
-     *
49
-     * @type string
50
-     */
51
-    const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
52
-
53
-
54
-    /**
55
-     * extra meta key for tracking if registration has reserved ticket
56
-     *
57
-     * @type string
58
-     */
59
-    const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
60
-
61
-
62
-    /**
63
-     * @param array  $props_n_values          incoming values
64
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
65
-     *                                        used.)
66
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
67
-     *                                        date_format and the second value is the time format
68
-     * @return EE_Registration
69
-     * @throws EE_Error
70
-     */
71
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
-    {
73
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
-    }
76
-
77
-
78
-    /**
79
-     * @param array  $props_n_values  incoming values from the database
80
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
-     *                                the website will be used.
82
-     * @return EE_Registration
83
-     */
84
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
85
-    {
86
-        return new self($props_n_values, true, $timezone);
87
-    }
88
-
89
-
90
-    /**
91
-     *        Set Event ID
92
-     *
93
-     * @param        int $EVT_ID Event ID
94
-     * @throws EE_Error
95
-     * @throws RuntimeException
96
-     */
97
-    public function set_event($EVT_ID = 0)
98
-    {
99
-        $this->set('EVT_ID', $EVT_ID);
100
-    }
101
-
102
-
103
-    /**
104
-     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
105
-     * be routed to internal methods
106
-     *
107
-     * @param string $field_name
108
-     * @param mixed  $field_value
109
-     * @param bool   $use_default
110
-     * @throws EE_Error
111
-     * @throws EntityNotFoundException
112
-     * @throws InvalidArgumentException
113
-     * @throws InvalidDataTypeException
114
-     * @throws InvalidInterfaceException
115
-     * @throws ReflectionException
116
-     * @throws RuntimeException
117
-     */
118
-    public function set($field_name, $field_value, $use_default = false)
119
-    {
120
-        switch ($field_name) {
121
-            case 'REG_code':
122
-                if (! empty($field_value) && $this->reg_code() === null) {
123
-                    $this->set_reg_code($field_value, $use_default);
124
-                }
125
-                break;
126
-            case 'STS_ID':
127
-                $this->set_status($field_value, $use_default);
128
-                break;
129
-            default:
130
-                parent::set($field_name, $field_value, $use_default);
131
-        }
132
-    }
133
-
134
-
135
-    /**
136
-     * Set Status ID
137
-     * updates the registration status and ALSO...
138
-     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
139
-     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
140
-     *
141
-     * @param string                $new_STS_ID
142
-     * @param boolean               $use_default
143
-     * @param ContextInterface|null $context
144
-     * @return bool
145
-     * @throws EE_Error
146
-     * @throws EntityNotFoundException
147
-     * @throws InvalidArgumentException
148
-     * @throws ReflectionException
149
-     * @throws RuntimeException
150
-     * @throws InvalidDataTypeException
151
-     * @throws InvalidInterfaceException
152
-     */
153
-    public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
154
-    {
155
-        // get current REG_Status
156
-        $old_STS_ID = $this->status_ID();
157
-        // if status has changed
158
-        if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
159
-            && ! empty($old_STS_ID) // and that old status is actually set
160
-            && ! empty($new_STS_ID) // as well as the new status
161
-            && $this->ID() // ensure registration is in the db
162
-        ) {
163
-            // TO approved
164
-            if ($new_STS_ID === EEM_Registration::status_id_approved) {
165
-                // reserve a space by incrementing ticket and datetime sold values
166
-                $this->_reserve_registration_space();
167
-                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
168
-                // OR FROM  approved
169
-            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
170
-                // release a space by decrementing ticket and datetime sold values
171
-                $this->_release_registration_space();
172
-                do_action(
173
-                    'AHEE__EE_Registration__set_status__from_approved',
174
-                    $this,
175
-                    $old_STS_ID,
176
-                    $new_STS_ID,
177
-                    $context
178
-                );
179
-            }
180
-            // update status
181
-            parent::set('STS_ID', $new_STS_ID, $use_default);
182
-            $this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, $context);
183
-            if ($this->statusChangeUpdatesTransaction($context)) {
184
-                $this->updateTransactionAfterStatusChange();
185
-            }
186
-            do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
187
-            return true;
188
-        }
189
-        // even though the old value matches the new value, it's still good to
190
-        // allow the parent set method to have a say
191
-        parent::set('STS_ID', $new_STS_ID, $use_default);
192
-        return true;
193
-    }
194
-
195
-
196
-    /**
197
-     * update REGs and TXN when cancelled or declined registrations involved
198
-     *
199
-     * @param string                $new_STS_ID
200
-     * @param string                $old_STS_ID
201
-     * @param ContextInterface|null $context
202
-     * @throws EE_Error
203
-     * @throws InvalidArgumentException
204
-     * @throws InvalidDataTypeException
205
-     * @throws InvalidInterfaceException
206
-     * @throws ReflectionException
207
-     */
208
-    private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
209
-    {
210
-        // these reg statuses should not be considered in any calculations involving monies owing
211
-        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
212
-        // true if registration has been cancelled or declined
213
-        $this->updateIfCanceled(
214
-            $closed_reg_statuses,
215
-            $new_STS_ID,
216
-            $old_STS_ID,
217
-            $context
218
-        );
219
-        $this->updateIfDeclined(
220
-            $closed_reg_statuses,
221
-            $new_STS_ID,
222
-            $old_STS_ID,
223
-            $context
224
-        );
225
-    }
226
-
227
-
228
-    /**
229
-     * update REGs and TXN when cancelled or declined registrations involved
230
-     *
231
-     * @param array                 $closed_reg_statuses
232
-     * @param string                $new_STS_ID
233
-     * @param string                $old_STS_ID
234
-     * @param ContextInterface|null $context
235
-     * @throws EE_Error
236
-     * @throws InvalidArgumentException
237
-     * @throws InvalidDataTypeException
238
-     * @throws InvalidInterfaceException
239
-     * @throws ReflectionException
240
-     */
241
-    private function updateIfCanceled(
242
-        array $closed_reg_statuses,
243
-        $new_STS_ID,
244
-        $old_STS_ID,
245
-        ContextInterface $context = null
246
-    ) {
247
-        // true if registration has been cancelled or declined
248
-        if (in_array($new_STS_ID, $closed_reg_statuses, true)
249
-            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
250
-        ) {
251
-            /** @type EE_Registration_Processor $registration_processor */
252
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
253
-            /** @type EE_Transaction_Processor $transaction_processor */
254
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
255
-            // cancelled or declined registration
256
-            $registration_processor->update_registration_after_being_canceled_or_declined(
257
-                $this,
258
-                $closed_reg_statuses
259
-            );
260
-            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
261
-                $this,
262
-                $closed_reg_statuses,
263
-                false
264
-            );
265
-            do_action(
266
-                'AHEE__EE_Registration__set_status__canceled_or_declined',
267
-                $this,
268
-                $old_STS_ID,
269
-                $new_STS_ID,
270
-                $context
271
-            );
272
-            return;
273
-        }
274
-    }
275
-
276
-
277
-    /**
278
-     * update REGs and TXN when cancelled or declined registrations involved
279
-     *
280
-     * @param array                 $closed_reg_statuses
281
-     * @param string                $new_STS_ID
282
-     * @param string                $old_STS_ID
283
-     * @param ContextInterface|null $context
284
-     * @throws EE_Error
285
-     * @throws InvalidArgumentException
286
-     * @throws InvalidDataTypeException
287
-     * @throws InvalidInterfaceException
288
-     * @throws ReflectionException
289
-     */
290
-    private function updateIfDeclined(
291
-        array $closed_reg_statuses,
292
-        $new_STS_ID,
293
-        $old_STS_ID,
294
-        ContextInterface $context = null
295
-    ) {
296
-        // true if reinstating cancelled or declined registration
297
-        if (in_array($old_STS_ID, $closed_reg_statuses, true)
298
-            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
299
-        ) {
300
-            /** @type EE_Registration_Processor $registration_processor */
301
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
302
-            /** @type EE_Transaction_Processor $transaction_processor */
303
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
304
-            // reinstating cancelled or declined registration
305
-            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
306
-                $this,
307
-                $closed_reg_statuses
308
-            );
309
-            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
310
-                $this,
311
-                $closed_reg_statuses,
312
-                false
313
-            );
314
-            do_action(
315
-                'AHEE__EE_Registration__set_status__after_reinstated',
316
-                $this,
317
-                $old_STS_ID,
318
-                $new_STS_ID,
319
-                $context
320
-            );
321
-        }
322
-    }
323
-
324
-
325
-    /**
326
-     * @param ContextInterface|null $context
327
-     * @return bool
328
-     */
329
-    private function statusChangeUpdatesTransaction(ContextInterface $context = null)
330
-    {
331
-        $contexts_that_do_not_update_transaction = (array) apply_filters(
332
-            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
333
-            array('spco_reg_step_attendee_information_process_registrations'),
334
-            $context,
335
-            $this
336
-        );
337
-        return ! (
338
-            $context instanceof ContextInterface
339
-            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
340
-        );
341
-    }
342
-
343
-
344
-    /**
345
-     * @throws EE_Error
346
-     * @throws EntityNotFoundException
347
-     * @throws InvalidArgumentException
348
-     * @throws InvalidDataTypeException
349
-     * @throws InvalidInterfaceException
350
-     * @throws ReflectionException
351
-     * @throws RuntimeException
352
-     */
353
-    private function updateTransactionAfterStatusChange()
354
-    {
355
-        /** @type EE_Transaction_Payments $transaction_payments */
356
-        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
357
-        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
358
-        $this->transaction()->update_status_based_on_total_paid(true);
359
-    }
360
-
361
-
362
-    /**
363
-     *        get Status ID
364
-     */
365
-    public function status_ID()
366
-    {
367
-        return $this->get('STS_ID');
368
-    }
369
-
370
-
371
-    /**
372
-     * Gets the ticket this registration is for
373
-     *
374
-     * @param boolean $include_archived whether to include archived tickets or not.
375
-     *
376
-     * @return EE_Ticket|EE_Base_Class
377
-     * @throws EE_Error
378
-     */
379
-    public function ticket($include_archived = true)
380
-    {
381
-        $query_params = array();
382
-        if ($include_archived) {
383
-            $query_params['default_where_conditions'] = 'none';
384
-        }
385
-        return $this->get_first_related('Ticket', $query_params);
386
-    }
387
-
388
-
389
-    /**
390
-     * Gets the event this registration is for
391
-     *
392
-     * @return EE_Event
393
-     * @throws EE_Error
394
-     * @throws EntityNotFoundException
395
-     */
396
-    public function event()
397
-    {
398
-        $event = $this->get_first_related('Event');
399
-        if (! $event instanceof \EE_Event) {
400
-            throw new EntityNotFoundException('Event ID', $this->event_ID());
401
-        }
402
-        return $event;
403
-    }
404
-
405
-
406
-    /**
407
-     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
408
-     * with the author of the event this registration is for.
409
-     *
410
-     * @since 4.5.0
411
-     * @return int
412
-     * @throws EE_Error
413
-     * @throws EntityNotFoundException
414
-     */
415
-    public function wp_user()
416
-    {
417
-        $event = $this->event();
418
-        if ($event instanceof EE_Event) {
419
-            return $event->wp_user();
420
-        }
421
-        return 0;
422
-    }
423
-
424
-
425
-    /**
426
-     * increments this registration's related ticket sold and corresponding datetime sold values
427
-     *
428
-     * @return void
429
-     * @throws DomainException
430
-     * @throws EE_Error
431
-     * @throws EntityNotFoundException
432
-     * @throws InvalidArgumentException
433
-     * @throws InvalidDataTypeException
434
-     * @throws InvalidInterfaceException
435
-     * @throws ReflectionException
436
-     * @throws UnexpectedEntityException
437
-     */
438
-    private function _reserve_registration_space()
439
-    {
440
-        // reserved ticket and datetime counts will be decremented as sold counts are incremented
441
-        // so stop tracking that this reg has a ticket reserved
442
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
443
-        $ticket = $this->ticket();
444
-        $ticket->increase_sold();
445
-        $ticket->save();
446
-        // possibly set event status to sold out
447
-        $this->event()->perform_sold_out_status_check();
448
-    }
449
-
450
-
451
-    /**
452
-     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
453
-     *
454
-     * @return void
455
-     * @throws DomainException
456
-     * @throws EE_Error
457
-     * @throws EntityNotFoundException
458
-     * @throws InvalidArgumentException
459
-     * @throws InvalidDataTypeException
460
-     * @throws InvalidInterfaceException
461
-     * @throws ReflectionException
462
-     * @throws UnexpectedEntityException
463
-     */
464
-    private function _release_registration_space()
465
-    {
466
-        $ticket = $this->ticket();
467
-        $ticket->decrease_sold();
468
-        $ticket->save();
469
-        // possibly change event status from sold out back to previous status
470
-        $this->event()->perform_sold_out_status_check();
471
-    }
472
-
473
-
474
-    /**
475
-     * tracks this registration's ticket reservation in extra meta
476
-     * and can increment related ticket reserved and corresponding datetime reserved values
477
-     *
478
-     * @param bool $update_ticket if true, will increment ticket and datetime reserved count
479
-     * @return void
480
-     * @throws EE_Error
481
-     * @throws InvalidArgumentException
482
-     * @throws InvalidDataTypeException
483
-     * @throws InvalidInterfaceException
484
-     * @throws ReflectionException
485
-     */
486
-    public function reserve_ticket($update_ticket = false, $source = 'unknown')
487
-    {
488
-        // only reserve ticket if space is not currently reserved
489
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
490
-            $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
491
-            // IMPORTANT !!!
492
-            // although checking $update_ticket first would be more efficient,
493
-            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
494
-            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true)
495
-                && $update_ticket
496
-            ) {
497
-                $ticket = $this->ticket();
498
-                $ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
499
-                $ticket->save();
500
-            }
501
-        }
502
-    }
503
-
504
-
505
-    /**
506
-     * stops tracking this registration's ticket reservation in extra meta
507
-     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
508
-     *
509
-     * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
510
-     * @return void
511
-     * @throws EE_Error
512
-     * @throws InvalidArgumentException
513
-     * @throws InvalidDataTypeException
514
-     * @throws InvalidInterfaceException
515
-     * @throws ReflectionException
516
-     */
517
-    public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
518
-    {
519
-        // only release ticket if space is currently reserved
520
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
521
-            $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
522
-            // IMPORTANT !!!
523
-            // although checking $update_ticket first would be more efficient,
524
-            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
525
-            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false)
526
-                && $update_ticket
527
-            ) {
528
-                $ticket = $this->ticket();
529
-                $ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
530
-                $ticket->save();
531
-            }
532
-        }
533
-    }
534
-
535
-
536
-    /**
537
-     * Set Attendee ID
538
-     *
539
-     * @param        int $ATT_ID Attendee ID
540
-     * @throws EE_Error
541
-     * @throws RuntimeException
542
-     */
543
-    public function set_attendee_id($ATT_ID = 0)
544
-    {
545
-        $this->set('ATT_ID', $ATT_ID);
546
-    }
547
-
548
-
549
-    /**
550
-     *        Set Transaction ID
551
-     *
552
-     * @param        int $TXN_ID Transaction ID
553
-     * @throws EE_Error
554
-     * @throws RuntimeException
555
-     */
556
-    public function set_transaction_id($TXN_ID = 0)
557
-    {
558
-        $this->set('TXN_ID', $TXN_ID);
559
-    }
560
-
561
-
562
-    /**
563
-     *        Set Session
564
-     *
565
-     * @param    string $REG_session PHP Session ID
566
-     * @throws EE_Error
567
-     * @throws RuntimeException
568
-     */
569
-    public function set_session($REG_session = '')
570
-    {
571
-        $this->set('REG_session', $REG_session);
572
-    }
573
-
574
-
575
-    /**
576
-     *        Set Registration URL Link
577
-     *
578
-     * @param    string $REG_url_link Registration URL Link
579
-     * @throws EE_Error
580
-     * @throws RuntimeException
581
-     */
582
-    public function set_reg_url_link($REG_url_link = '')
583
-    {
584
-        $this->set('REG_url_link', $REG_url_link);
585
-    }
586
-
587
-
588
-    /**
589
-     *        Set Attendee Counter
590
-     *
591
-     * @param        int $REG_count Primary Attendee
592
-     * @throws EE_Error
593
-     * @throws RuntimeException
594
-     */
595
-    public function set_count($REG_count = 1)
596
-    {
597
-        $this->set('REG_count', $REG_count);
598
-    }
599
-
600
-
601
-    /**
602
-     *        Set Group Size
603
-     *
604
-     * @param        boolean $REG_group_size Group Registration
605
-     * @throws EE_Error
606
-     * @throws RuntimeException
607
-     */
608
-    public function set_group_size($REG_group_size = false)
609
-    {
610
-        $this->set('REG_group_size', $REG_group_size);
611
-    }
612
-
613
-
614
-    /**
615
-     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
616
-     *    EEM_Registration::status_id_not_approved
617
-     *
618
-     * @return        boolean
619
-     */
620
-    public function is_not_approved()
621
-    {
622
-        return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
623
-    }
624
-
625
-
626
-    /**
627
-     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
628
-     *    EEM_Registration::status_id_pending_payment
629
-     *
630
-     * @return        boolean
631
-     */
632
-    public function is_pending_payment()
633
-    {
634
-        return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
635
-    }
636
-
637
-
638
-    /**
639
-     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
640
-     *
641
-     * @return        boolean
642
-     */
643
-    public function is_approved()
644
-    {
645
-        return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
646
-    }
647
-
648
-
649
-    /**
650
-     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
651
-     *
652
-     * @return        boolean
653
-     */
654
-    public function is_cancelled()
655
-    {
656
-        return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
657
-    }
658
-
659
-
660
-    /**
661
-     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
662
-     *
663
-     * @return        boolean
664
-     */
665
-    public function is_declined()
666
-    {
667
-        return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
668
-    }
669
-
670
-
671
-    /**
672
-     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
673
-     *    EEM_Registration::status_id_incomplete
674
-     *
675
-     * @return        boolean
676
-     */
677
-    public function is_incomplete()
678
-    {
679
-        return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
680
-    }
681
-
682
-
683
-    /**
684
-     *        Set Registration Date
685
-     *
686
-     * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
687
-     *                                                 Date
688
-     * @throws EE_Error
689
-     * @throws RuntimeException
690
-     */
691
-    public function set_reg_date($REG_date = false)
692
-    {
693
-        $this->set('REG_date', $REG_date);
694
-    }
695
-
696
-
697
-    /**
698
-     *    Set final price owing for this registration after all ticket/price modifications
699
-     *
700
-     * @access    public
701
-     * @param    float $REG_final_price
702
-     * @throws EE_Error
703
-     * @throws RuntimeException
704
-     */
705
-    public function set_final_price($REG_final_price = 0.00)
706
-    {
707
-        $this->set('REG_final_price', $REG_final_price);
708
-    }
709
-
710
-
711
-    /**
712
-     *    Set amount paid towards this registration's final price
713
-     *
714
-     * @access    public
715
-     * @param    float $REG_paid
716
-     * @throws EE_Error
717
-     * @throws RuntimeException
718
-     */
719
-    public function set_paid($REG_paid = 0.00)
720
-    {
721
-        $this->set('REG_paid', $REG_paid);
722
-    }
723
-
724
-
725
-    /**
726
-     *        Attendee Is Going
727
-     *
728
-     * @param        boolean $REG_att_is_going Attendee Is Going
729
-     * @throws EE_Error
730
-     * @throws RuntimeException
731
-     */
732
-    public function set_att_is_going($REG_att_is_going = false)
733
-    {
734
-        $this->set('REG_att_is_going', $REG_att_is_going);
735
-    }
736
-
737
-
738
-    /**
739
-     * Gets the related attendee
740
-     *
741
-     * @return EE_Attendee
742
-     * @throws EE_Error
743
-     */
744
-    public function attendee()
745
-    {
746
-        return $this->get_first_related('Attendee');
747
-    }
748
-
749
-
750
-    /**
751
-     *        get Event ID
752
-     */
753
-    public function event_ID()
754
-    {
755
-        return $this->get('EVT_ID');
756
-    }
757
-
758
-
759
-    /**
760
-     *        get Event ID
761
-     */
762
-    public function event_name()
763
-    {
764
-        $event = $this->event_obj();
765
-        if ($event) {
766
-            return $event->name();
767
-        } else {
768
-            return null;
769
-        }
770
-    }
771
-
772
-
773
-    /**
774
-     * Fetches the event this registration is for
775
-     *
776
-     * @return EE_Event
777
-     * @throws EE_Error
778
-     */
779
-    public function event_obj()
780
-    {
781
-        return $this->get_first_related('Event');
782
-    }
783
-
784
-
785
-    /**
786
-     *        get Attendee ID
787
-     */
788
-    public function attendee_ID()
789
-    {
790
-        return $this->get('ATT_ID');
791
-    }
792
-
793
-
794
-    /**
795
-     *        get PHP Session ID
796
-     */
797
-    public function session_ID()
798
-    {
799
-        return $this->get('REG_session');
800
-    }
801
-
802
-
803
-    /**
804
-     * Gets the string which represents the URL trigger for the receipt template in the message template system.
805
-     *
806
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
807
-     * @return string
808
-     */
809
-    public function receipt_url($messenger = 'html')
810
-    {
811
-
812
-        /**
813
-         * The below will be deprecated one version after this.  We check first if there is a custom receipt template
814
-         * already in use on old system.  If there is then we just return the standard url for it.
815
-         *
816
-         * @since 4.5.0
817
-         */
818
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
819
-        $has_custom = EEH_Template::locate_template(
820
-            $template_relative_path,
821
-            array(),
822
-            true,
823
-            true,
824
-            true
825
-        );
826
-
827
-        if ($has_custom) {
828
-            return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
829
-        }
830
-        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
831
-    }
832
-
833
-
834
-    /**
835
-     * Gets the string which represents the URL trigger for the invoice template in the message template system.
836
-     *
837
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
838
-     * @return string
839
-     * @throws EE_Error
840
-     */
841
-    public function invoice_url($messenger = 'html')
842
-    {
843
-        /**
844
-         * The below will be deprecated one version after this.  We check first if there is a custom invoice template
845
-         * already in use on old system.  If there is then we just return the standard url for it.
846
-         *
847
-         * @since 4.5.0
848
-         */
849
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
850
-        $has_custom = EEH_Template::locate_template(
851
-            $template_relative_path,
852
-            array(),
853
-            true,
854
-            true,
855
-            true
856
-        );
857
-
858
-        if ($has_custom) {
859
-            if ($messenger == 'html') {
860
-                return $this->invoice_url('launch');
861
-            }
862
-            $route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
863
-
864
-            $query_args = array('ee' => $route, 'id' => $this->reg_url_link());
865
-            if ($messenger == 'html') {
866
-                $query_args['html'] = true;
867
-            }
868
-            return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
869
-        }
870
-        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
871
-    }
872
-
873
-
874
-    /**
875
-     * get Registration URL Link
876
-     *
877
-     * @access public
878
-     * @return string
879
-     * @throws EE_Error
880
-     */
881
-    public function reg_url_link()
882
-    {
883
-        return (string) $this->get('REG_url_link');
884
-    }
885
-
886
-
887
-    /**
888
-     * Echoes out invoice_url()
889
-     *
890
-     * @param string $type 'download','launch', or 'html' (default is 'launch')
891
-     * @return void
892
-     * @throws EE_Error
893
-     */
894
-    public function e_invoice_url($type = 'launch')
895
-    {
896
-        echo $this->invoice_url($type);
897
-    }
898
-
899
-
900
-    /**
901
-     * Echoes out payment_overview_url
902
-     */
903
-    public function e_payment_overview_url()
904
-    {
905
-        echo $this->payment_overview_url();
906
-    }
907
-
908
-
909
-    /**
910
-     * Gets the URL for the checkout payment options reg step
911
-     * with this registration's REG_url_link added as a query parameter
912
-     *
913
-     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
914
-     *                            payment overview url.
915
-     * @return string
916
-     * @throws InvalidInterfaceException
917
-     * @throws InvalidDataTypeException
918
-     * @throws EE_Error
919
-     * @throws InvalidArgumentException
920
-     */
921
-    public function payment_overview_url($clear_session = false)
922
-    {
923
-        return add_query_arg(
924
-            (array) apply_filters(
925
-                'FHEE__EE_Registration__payment_overview_url__query_args',
926
-                array(
927
-                    'e_reg_url_link' => $this->reg_url_link(),
928
-                    'step'           => 'payment_options',
929
-                    'revisit'        => true,
930
-                    'clear_session'  => (bool) $clear_session,
931
-                ),
932
-                $this
933
-            ),
934
-            EE_Registry::instance()->CFG->core->reg_page_url()
935
-        );
936
-    }
937
-
938
-
939
-    /**
940
-     * Gets the URL for the checkout attendee information reg step
941
-     * with this registration's REG_url_link added as a query parameter
942
-     *
943
-     * @return string
944
-     * @throws InvalidInterfaceException
945
-     * @throws InvalidDataTypeException
946
-     * @throws EE_Error
947
-     * @throws InvalidArgumentException
948
-     */
949
-    public function edit_attendee_information_url()
950
-    {
951
-        return add_query_arg(
952
-            (array) apply_filters(
953
-                'FHEE__EE_Registration__edit_attendee_information_url__query_args',
954
-                array(
955
-                    'e_reg_url_link' => $this->reg_url_link(),
956
-                    'step'           => 'attendee_information',
957
-                    'revisit'        => true,
958
-                ),
959
-                $this
960
-            ),
961
-            EE_Registry::instance()->CFG->core->reg_page_url()
962
-        );
963
-    }
964
-
965
-
966
-    /**
967
-     * Simply generates and returns the appropriate admin_url link to edit this registration
968
-     *
969
-     * @return string
970
-     * @throws EE_Error
971
-     */
972
-    public function get_admin_edit_url()
973
-    {
974
-        return EEH_URL::add_query_args_and_nonce(
975
-            array(
976
-                'page'    => 'espresso_registrations',
977
-                'action'  => 'view_registration',
978
-                '_REG_ID' => $this->ID(),
979
-            ),
980
-            admin_url('admin.php')
981
-        );
982
-    }
983
-
984
-
985
-    /**
986
-     *    is_primary_registrant?
987
-     */
988
-    public function is_primary_registrant()
989
-    {
990
-        return $this->get('REG_count') === 1 ? true : false;
991
-    }
992
-
993
-
994
-    /**
995
-     * This returns the primary registration object for this registration group (which may be this object).
996
-     *
997
-     * @return EE_Registration
998
-     * @throws EE_Error
999
-     */
1000
-    public function get_primary_registration()
1001
-    {
1002
-        if ($this->is_primary_registrant()) {
1003
-            return $this;
1004
-        }
1005
-
1006
-        // k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1007
-        /** @var EE_Registration $primary_registrant */
1008
-        $primary_registrant = EEM_Registration::instance()->get_one(
1009
-            array(
1010
-                array(
1011
-                    'TXN_ID'    => $this->transaction_ID(),
1012
-                    'REG_count' => 1,
1013
-                ),
1014
-            )
1015
-        );
1016
-        return $primary_registrant;
1017
-    }
1018
-
1019
-
1020
-    /**
1021
-     *        get  Attendee Number
1022
-     *
1023
-     * @access        public
1024
-     */
1025
-    public function count()
1026
-    {
1027
-        return $this->get('REG_count');
1028
-    }
1029
-
1030
-
1031
-    /**
1032
-     *        get Group Size
1033
-     */
1034
-    public function group_size()
1035
-    {
1036
-        return $this->get('REG_group_size');
1037
-    }
1038
-
1039
-
1040
-    /**
1041
-     *        get Registration Date
1042
-     */
1043
-    public function date()
1044
-    {
1045
-        return $this->get('REG_date');
1046
-    }
1047
-
1048
-
1049
-    /**
1050
-     * gets a pretty date
1051
-     *
1052
-     * @param string $date_format
1053
-     * @param string $time_format
1054
-     * @return string
1055
-     * @throws EE_Error
1056
-     */
1057
-    public function pretty_date($date_format = null, $time_format = null)
1058
-    {
1059
-        return $this->get_datetime('REG_date', $date_format, $time_format);
1060
-    }
1061
-
1062
-
1063
-    /**
1064
-     * final_price
1065
-     * the registration's share of the transaction total, so that the
1066
-     * sum of all the transaction's REG_final_prices equal the transaction's total
1067
-     *
1068
-     * @return float
1069
-     * @throws EE_Error
1070
-     */
1071
-    public function final_price()
1072
-    {
1073
-        return $this->get('REG_final_price');
1074
-    }
1075
-
1076
-
1077
-    /**
1078
-     * pretty_final_price
1079
-     *  final price as formatted string, with correct decimal places and currency symbol
1080
-     *
1081
-     * @return string
1082
-     * @throws EE_Error
1083
-     */
1084
-    public function pretty_final_price()
1085
-    {
1086
-        return $this->get_pretty('REG_final_price');
1087
-    }
1088
-
1089
-
1090
-    /**
1091
-     * get paid (yeah)
1092
-     *
1093
-     * @return float
1094
-     * @throws EE_Error
1095
-     */
1096
-    public function paid()
1097
-    {
1098
-        return $this->get('REG_paid');
1099
-    }
1100
-
1101
-
1102
-    /**
1103
-     * pretty_paid
1104
-     *
1105
-     * @return float
1106
-     * @throws EE_Error
1107
-     */
1108
-    public function pretty_paid()
1109
-    {
1110
-        return $this->get_pretty('REG_paid');
1111
-    }
1112
-
1113
-
1114
-    /**
1115
-     * owes_monies_and_can_pay
1116
-     * whether or not this registration has monies owing and it's' status allows payment
1117
-     *
1118
-     * @param array $requires_payment
1119
-     * @return bool
1120
-     * @throws EE_Error
1121
-     */
1122
-    public function owes_monies_and_can_pay($requires_payment = array())
1123
-    {
1124
-        // these reg statuses require payment (if event is not free)
1125
-        $requires_payment = ! empty($requires_payment)
1126
-            ? $requires_payment
1127
-            : EEM_Registration::reg_statuses_that_allow_payment();
1128
-        if (in_array($this->status_ID(), $requires_payment) &&
1129
-            $this->final_price() != 0 &&
1130
-            $this->final_price() != $this->paid()
1131
-        ) {
1132
-            return true;
1133
-        } else {
1134
-            return false;
1135
-        }
1136
-    }
1137
-
1138
-
1139
-    /**
1140
-     * Prints out the return value of $this->pretty_status()
1141
-     *
1142
-     * @param bool $show_icons
1143
-     * @return void
1144
-     * @throws EE_Error
1145
-     */
1146
-    public function e_pretty_status($show_icons = false)
1147
-    {
1148
-        echo $this->pretty_status($show_icons);
1149
-    }
1150
-
1151
-
1152
-    /**
1153
-     * Returns a nice version of the status for displaying to customers
1154
-     *
1155
-     * @param bool $show_icons
1156
-     * @return string
1157
-     * @throws EE_Error
1158
-     */
1159
-    public function pretty_status($show_icons = false)
1160
-    {
1161
-        $status = EEM_Status::instance()->localized_status(
1162
-            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1163
-            false,
1164
-            'sentence'
1165
-        );
1166
-        $icon = '';
1167
-        switch ($this->status_ID()) {
1168
-            case EEM_Registration::status_id_approved:
1169
-                $icon = $show_icons
1170
-                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1171
-                    : '';
1172
-                break;
1173
-            case EEM_Registration::status_id_pending_payment:
1174
-                $icon = $show_icons
1175
-                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1176
-                    : '';
1177
-                break;
1178
-            case EEM_Registration::status_id_not_approved:
1179
-                $icon = $show_icons
1180
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1181
-                    : '';
1182
-                break;
1183
-            case EEM_Registration::status_id_cancelled:
1184
-                $icon = $show_icons
1185
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1186
-                    : '';
1187
-                break;
1188
-            case EEM_Registration::status_id_incomplete:
1189
-                $icon = $show_icons
1190
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1191
-                    : '';
1192
-                break;
1193
-            case EEM_Registration::status_id_declined:
1194
-                $icon = $show_icons
1195
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1196
-                    : '';
1197
-                break;
1198
-            case EEM_Registration::status_id_wait_list:
1199
-                $icon = $show_icons
1200
-                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1201
-                    : '';
1202
-                break;
1203
-        }
1204
-        return $icon . $status[ $this->status_ID() ];
1205
-    }
1206
-
1207
-
1208
-    /**
1209
-     *        get Attendee Is Going
1210
-     */
1211
-    public function att_is_going()
1212
-    {
1213
-        return $this->get('REG_att_is_going');
1214
-    }
1215
-
1216
-
1217
-    /**
1218
-     * Gets related answers
1219
-     *
1220
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1221
-     * @return EE_Answer[]
1222
-     * @throws EE_Error
1223
-     */
1224
-    public function answers($query_params = null)
1225
-    {
1226
-        return $this->get_many_related('Answer', $query_params);
1227
-    }
1228
-
1229
-
1230
-    /**
1231
-     * Gets the registration's answer value to the specified question
1232
-     * (either the question's ID or a question object)
1233
-     *
1234
-     * @param EE_Question|int $question
1235
-     * @param bool            $pretty_value
1236
-     * @return array|string if pretty_value= true, the result will always be a string
1237
-     * (because the answer might be an array of answer values, so passing pretty_value=true
1238
-     * will convert it into some kind of string)
1239
-     * @throws EE_Error
1240
-     */
1241
-    public function answer_value_to_question($question, $pretty_value = true)
1242
-    {
1243
-        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1244
-        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1245
-    }
1246
-
1247
-
1248
-    /**
1249
-     * question_groups
1250
-     * returns an array of EE_Question_Group objects for this registration
1251
-     *
1252
-     * @return EE_Question_Group[]
1253
-     * @throws EE_Error
1254
-     * @throws EntityNotFoundException
1255
-     */
1256
-    public function question_groups()
1257
-    {
1258
-        $question_groups = array();
1259
-        if ($this->event() instanceof EE_Event) {
1260
-            $query_params = [
1261
-                [
1262
-                    'Event_Question_Group.'
1263
-                    . EEM_Event_Question_Group::instance()->field_name_for_category(
1264
-                        $this->is_primary_registrant()
1265
-                    ) => true
1266
-                ],
1267
-                'order_by' => ['QSG_order' => 'ASC']];
1268
-            $question_groups = $this->event()->question_groups($query_params);
1269
-        }
1270
-        return $question_groups;
1271
-    }
1272
-
1273
-
1274
-    /**
1275
-     * count_question_groups
1276
-     * returns a count of the number of EE_Question_Group objects for this registration
1277
-     *
1278
-     * @return int
1279
-     * @throws EE_Error
1280
-     * @throws EntityNotFoundException
1281
-     * @throws InvalidArgumentException
1282
-     * @throws InvalidDataTypeException
1283
-     * @throws InvalidInterfaceException
1284
-     * @throws ReflectionException
1285
-     */
1286
-    public function count_question_groups()
1287
-    {
1288
-        $qg_count = 0;
1289
-        if ($this->event() instanceof EE_Event) {
1290
-            if ($this->is_primary_registrant()) {
1291
-                $query_params =[['Event_Question_Group.EQG_primary' => true]];
1292
-            } else {
1293
-                $query_params = [['Event_Question_Group.EQG_additional' => true]];
1294
-            }
1295
-            $qg_count = $this->event()->count_related(
1296
-                'Question_Group',
1297
-                $query_params
1298
-            );
1299
-        }
1300
-        return $qg_count;
1301
-    }
1302
-
1303
-
1304
-    /**
1305
-     * Returns the registration date in the 'standard' string format
1306
-     * (function may be improved in the future to allow for different formats and timezones)
1307
-     *
1308
-     * @return string
1309
-     * @throws EE_Error
1310
-     */
1311
-    public function reg_date()
1312
-    {
1313
-        return $this->get_datetime('REG_date');
1314
-    }
1315
-
1316
-
1317
-    /**
1318
-     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1319
-     * the ticket this registration purchased, or the datetime they have registered
1320
-     * to attend)
1321
-     *
1322
-     * @return EE_Datetime_Ticket
1323
-     * @throws EE_Error
1324
-     */
1325
-    public function datetime_ticket()
1326
-    {
1327
-        return $this->get_first_related('Datetime_Ticket');
1328
-    }
1329
-
1330
-
1331
-    /**
1332
-     * Sets the registration's datetime_ticket.
1333
-     *
1334
-     * @param EE_Datetime_Ticket $datetime_ticket
1335
-     * @return EE_Datetime_Ticket
1336
-     * @throws EE_Error
1337
-     */
1338
-    public function set_datetime_ticket($datetime_ticket)
1339
-    {
1340
-        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1341
-    }
1342
-
1343
-    /**
1344
-     * Gets deleted
1345
-     *
1346
-     * @return bool
1347
-     * @throws EE_Error
1348
-     */
1349
-    public function deleted()
1350
-    {
1351
-        return $this->get('REG_deleted');
1352
-    }
1353
-
1354
-    /**
1355
-     * Sets deleted
1356
-     *
1357
-     * @param boolean $deleted
1358
-     * @return bool
1359
-     * @throws EE_Error
1360
-     * @throws RuntimeException
1361
-     */
1362
-    public function set_deleted($deleted)
1363
-    {
1364
-        if ($deleted) {
1365
-            $this->delete();
1366
-        } else {
1367
-            $this->restore();
1368
-        }
1369
-    }
1370
-
1371
-
1372
-    /**
1373
-     * Get the status object of this object
1374
-     *
1375
-     * @return EE_Status
1376
-     * @throws EE_Error
1377
-     */
1378
-    public function status_obj()
1379
-    {
1380
-        return $this->get_first_related('Status');
1381
-    }
1382
-
1383
-
1384
-    /**
1385
-     * Returns the number of times this registration has checked into any of the datetimes
1386
-     * its available for
1387
-     *
1388
-     * @return int
1389
-     * @throws EE_Error
1390
-     */
1391
-    public function count_checkins()
1392
-    {
1393
-        return $this->get_model()->count_related($this, 'Checkin');
1394
-    }
1395
-
1396
-
1397
-    /**
1398
-     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1399
-     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1400
-     *
1401
-     * @return int
1402
-     * @throws EE_Error
1403
-     */
1404
-    public function count_checkins_not_checkedout()
1405
-    {
1406
-        return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1407
-    }
1408
-
1409
-
1410
-    /**
1411
-     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1412
-     *
1413
-     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1414
-     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1415
-     *                                          consider registration status as well as datetime access.
1416
-     * @return bool
1417
-     * @throws EE_Error
1418
-     */
1419
-    public function can_checkin($DTT_OR_ID, $check_approved = true)
1420
-    {
1421
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1422
-
1423
-        // first check registration status
1424
-        if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1425
-            return false;
1426
-        }
1427
-        // is there a datetime ticket that matches this dtt_ID?
1428
-        if (! (EEM_Datetime_Ticket::instance()->exists(
1429
-            array(
1430
-                array(
1431
-                    'TKT_ID' => $this->get('TKT_ID'),
1432
-                    'DTT_ID' => $DTT_ID,
1433
-                ),
1434
-            )
1435
-        ))
1436
-        ) {
1437
-            return false;
1438
-        }
1439
-
1440
-        // final check is against TKT_uses
1441
-        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1442
-    }
1443
-
1444
-
1445
-    /**
1446
-     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1447
-     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1448
-     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1449
-     * then return false.  Otherwise return true.
1450
-     *
1451
-     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1452
-     * @return bool true means can checkin.  false means cannot checkin.
1453
-     * @throws EE_Error
1454
-     */
1455
-    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1456
-    {
1457
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1458
-
1459
-        if (! $DTT_ID) {
1460
-            return false;
1461
-        }
1462
-
1463
-        $max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1464
-
1465
-        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1466
-        // check-in or not.
1467
-        if (! $max_uses || $max_uses === EE_INF) {
1468
-            return true;
1469
-        }
1470
-
1471
-        // does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1472
-        // go ahead and toggle.
1473
-        if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1474
-            return true;
1475
-        }
1476
-
1477
-        // made it here so the last check is whether the number of checkins per unique datetime on this registration
1478
-        // disallows further check-ins.
1479
-        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1480
-            array(
1481
-                array(
1482
-                    'REG_ID' => $this->ID(),
1483
-                    'CHK_in' => true,
1484
-                ),
1485
-            ),
1486
-            'DTT_ID',
1487
-            true
1488
-        );
1489
-        // checkins have already reached their max number of uses
1490
-        // so registrant can NOT checkin
1491
-        if ($count_unique_dtt_checkins >= $max_uses) {
1492
-            EE_Error::add_error(
1493
-                esc_html__(
1494
-                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1495
-                    'event_espresso'
1496
-                ),
1497
-                __FILE__,
1498
-                __FUNCTION__,
1499
-                __LINE__
1500
-            );
1501
-            return false;
1502
-        }
1503
-        return true;
1504
-    }
1505
-
1506
-
1507
-    /**
1508
-     * toggle Check-in status for this registration
1509
-     * Check-ins are toggled in the following order:
1510
-     * never checked in -> checked in
1511
-     * checked in -> checked out
1512
-     * checked out -> checked in
1513
-     *
1514
-     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1515
-     *                      If not included or null, then it is assumed latest datetime is being toggled.
1516
-     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1517
-     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1518
-     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1519
-     * @throws EE_Error
1520
-     */
1521
-    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1522
-    {
1523
-        if (empty($DTT_ID)) {
1524
-            $datetime = $this->get_latest_related_datetime();
1525
-            $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1526
-            // verify the registration can checkin for the given DTT_ID
1527
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1528
-            EE_Error::add_error(
1529
-                sprintf(
1530
-                    esc_html__(
1531
-                        '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',
1532
-                        'event_espresso'
1533
-                    ),
1534
-                    $this->ID(),
1535
-                    $DTT_ID
1536
-                ),
1537
-                __FILE__,
1538
-                __FUNCTION__,
1539
-                __LINE__
1540
-            );
1541
-            return false;
1542
-        }
1543
-        $status_paths = array(
1544
-            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1545
-            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1546
-            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1547
-        );
1548
-        // start by getting the current status so we know what status we'll be changing to.
1549
-        $cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1550
-        $status_to = $status_paths[ $cur_status ];
1551
-        // database only records true for checked IN or false for checked OUT
1552
-        // no record ( null ) means checked in NEVER, but we obviously don't save that
1553
-        $new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1554
-        // add relation - note Check-ins are always creating new rows
1555
-        // because we are keeping track of Check-ins over time.
1556
-        // Eventually we'll probably want to show a list table
1557
-        // for the individual Check-ins so that they can be managed.
1558
-        $checkin = EE_Checkin::new_instance(
1559
-            array(
1560
-                'REG_ID' => $this->ID(),
1561
-                'DTT_ID' => $DTT_ID,
1562
-                'CHK_in' => $new_status,
1563
-            )
1564
-        );
1565
-        // if the record could not be saved then return false
1566
-        if ($checkin->save() === 0) {
1567
-            if (WP_DEBUG) {
1568
-                global $wpdb;
1569
-                $error = sprintf(
1570
-                    esc_html__(
1571
-                        'Registration check in update failed because of the following database error: %1$s%2$s',
1572
-                        'event_espresso'
1573
-                    ),
1574
-                    '<br />',
1575
-                    $wpdb->last_error
1576
-                );
1577
-            } else {
1578
-                $error = esc_html__(
1579
-                    'Registration check in update failed because of an unknown database error',
1580
-                    'event_espresso'
1581
-                );
1582
-            }
1583
-            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1584
-            return false;
1585
-        }
1586
-        return $status_to;
1587
-    }
1588
-
1589
-
1590
-    /**
1591
-     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1592
-     * "Latest" is defined by the `DTT_EVT_start` column.
1593
-     *
1594
-     * @return EE_Datetime|null
1595
-     * @throws EE_Error
1596
-     */
1597
-    public function get_latest_related_datetime()
1598
-    {
1599
-        return EEM_Datetime::instance()->get_one(
1600
-            array(
1601
-                array(
1602
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1603
-                ),
1604
-                'order_by' => array('DTT_EVT_start' => 'DESC'),
1605
-            )
1606
-        );
1607
-    }
1608
-
1609
-
1610
-    /**
1611
-     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1612
-     * "Earliest" is defined by the `DTT_EVT_start` column.
1613
-     *
1614
-     * @throws EE_Error
1615
-     */
1616
-    public function get_earliest_related_datetime()
1617
-    {
1618
-        return EEM_Datetime::instance()->get_one(
1619
-            array(
1620
-                array(
1621
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1622
-                ),
1623
-                'order_by' => array('DTT_EVT_start' => 'ASC'),
1624
-            )
1625
-        );
1626
-    }
1627
-
1628
-
1629
-    /**
1630
-     * This method simply returns the check-in status for this registration and the given datetime.
1631
-     * If neither the datetime nor the checkin values are provided as arguments,
1632
-     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1633
-     *
1634
-     * @param  int       $DTT_ID  The ID of the datetime we're checking against
1635
-     *                            (if empty we'll get the primary datetime for
1636
-     *                            this registration (via event) and use it's ID);
1637
-     * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1638
-     *
1639
-     * @return int                Integer representing Check-in status.
1640
-     * @throws EE_Error
1641
-     */
1642
-    public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1643
-    {
1644
-        $checkin_query_params = array(
1645
-            'order_by' => array('CHK_timestamp' => 'DESC'),
1646
-        );
1647
-
1648
-        if ($DTT_ID > 0) {
1649
-            $checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1650
-        }
1651
-
1652
-        // get checkin object (if exists)
1653
-        $checkin = $checkin instanceof EE_Checkin
1654
-            ? $checkin
1655
-            : $this->get_first_related('Checkin', $checkin_query_params);
1656
-        if ($checkin instanceof EE_Checkin) {
1657
-            if ($checkin->get('CHK_in')) {
1658
-                return EE_Checkin::status_checked_in; // checked in
1659
-            }
1660
-            return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1661
-        }
1662
-        return EE_Checkin::status_checked_never; // never been checked in
1663
-    }
1664
-
1665
-
1666
-    /**
1667
-     * This method returns a localized message for the toggled Check-in message.
1668
-     *
1669
-     * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1670
-     *                     then it is assumed Check-in for primary datetime was toggled.
1671
-     * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1672
-     *                     message can be customized with the attendee name.
1673
-     * @return string internationalized message
1674
-     * @throws EE_Error
1675
-     */
1676
-    public function get_checkin_msg($DTT_ID, $error = false)
1677
-    {
1678
-        // let's get the attendee first so we can include the name of the attendee
1679
-        $attendee = $this->get_first_related('Attendee');
1680
-        if ($attendee instanceof EE_Attendee) {
1681
-            if ($error) {
1682
-                return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1683
-            }
1684
-            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1685
-            // what is the status message going to be?
1686
-            switch ($cur_status) {
1687
-                case EE_Checkin::status_checked_never:
1688
-                    return sprintf(
1689
-                        __("%s has been removed from Check-in records", "event_espresso"),
1690
-                        $attendee->full_name()
1691
-                    );
1692
-                    break;
1693
-                case EE_Checkin::status_checked_in:
1694
-                    return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1695
-                    break;
1696
-                case EE_Checkin::status_checked_out:
1697
-                    return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1698
-                    break;
1699
-            }
1700
-        }
1701
-        return esc_html__("The check-in status could not be determined.", "event_espresso");
1702
-    }
1703
-
1704
-
1705
-    /**
1706
-     * Returns the related EE_Transaction to this registration
1707
-     *
1708
-     * @return EE_Transaction
1709
-     * @throws EE_Error
1710
-     * @throws EntityNotFoundException
1711
-     */
1712
-    public function transaction()
1713
-    {
1714
-        $transaction = $this->get_first_related('Transaction');
1715
-        if (! $transaction instanceof \EE_Transaction) {
1716
-            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1717
-        }
1718
-        return $transaction;
1719
-    }
1720
-
1721
-
1722
-    /**
1723
-     *        get Registration Code
1724
-     */
1725
-    public function reg_code()
1726
-    {
1727
-        return $this->get('REG_code');
1728
-    }
1729
-
1730
-
1731
-    /**
1732
-     *        get Transaction ID
1733
-     */
1734
-    public function transaction_ID()
1735
-    {
1736
-        return $this->get('TXN_ID');
1737
-    }
1738
-
1739
-
1740
-    /**
1741
-     * @return int
1742
-     * @throws EE_Error
1743
-     */
1744
-    public function ticket_ID()
1745
-    {
1746
-        return $this->get('TKT_ID');
1747
-    }
1748
-
1749
-
1750
-    /**
1751
-     *        Set Registration Code
1752
-     *
1753
-     * @access    public
1754
-     * @param    string  $REG_code Registration Code
1755
-     * @param    boolean $use_default
1756
-     * @throws EE_Error
1757
-     */
1758
-    public function set_reg_code($REG_code, $use_default = false)
1759
-    {
1760
-        if (empty($REG_code)) {
1761
-            EE_Error::add_error(
1762
-                esc_html__('REG_code can not be empty.', 'event_espresso'),
1763
-                __FILE__,
1764
-                __FUNCTION__,
1765
-                __LINE__
1766
-            );
1767
-            return;
1768
-        }
1769
-        if (! $this->reg_code()) {
1770
-            parent::set('REG_code', $REG_code, $use_default);
1771
-        } else {
1772
-            EE_Error::doing_it_wrong(
1773
-                __CLASS__ . '::' . __FUNCTION__,
1774
-                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1775
-                '4.6.0'
1776
-            );
1777
-        }
1778
-    }
1779
-
1780
-
1781
-    /**
1782
-     * Returns all other registrations in the same group as this registrant who have the same ticket option.
1783
-     * Note, if you want to just get all registrations in the same transaction (group), use:
1784
-     *    $registration->transaction()->registrations();
1785
-     *
1786
-     * @since 4.5.0
1787
-     * @return EE_Registration[] or empty array if this isn't a group registration.
1788
-     * @throws EE_Error
1789
-     */
1790
-    public function get_all_other_registrations_in_group()
1791
-    {
1792
-        if ($this->group_size() < 2) {
1793
-            return array();
1794
-        }
1795
-
1796
-        $query[0] = array(
1797
-            'TXN_ID' => $this->transaction_ID(),
1798
-            'REG_ID' => array('!=', $this->ID()),
1799
-            'TKT_ID' => $this->ticket_ID(),
1800
-        );
1801
-        /** @var EE_Registration[] $registrations */
1802
-        $registrations = $this->get_model()->get_all($query);
1803
-        return $registrations;
1804
-    }
1805
-
1806
-    /**
1807
-     * Return the link to the admin details for the object.
1808
-     *
1809
-     * @return string
1810
-     * @throws EE_Error
1811
-     */
1812
-    public function get_admin_details_link()
1813
-    {
1814
-        EE_Registry::instance()->load_helper('URL');
1815
-        return EEH_URL::add_query_args_and_nonce(
1816
-            array(
1817
-                'page'    => 'espresso_registrations',
1818
-                'action'  => 'view_registration',
1819
-                '_REG_ID' => $this->ID(),
1820
-            ),
1821
-            admin_url('admin.php')
1822
-        );
1823
-    }
1824
-
1825
-    /**
1826
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1827
-     *
1828
-     * @return string
1829
-     * @throws EE_Error
1830
-     */
1831
-    public function get_admin_edit_link()
1832
-    {
1833
-        return $this->get_admin_details_link();
1834
-    }
1835
-
1836
-    /**
1837
-     * Returns the link to a settings page for the object.
1838
-     *
1839
-     * @return string
1840
-     * @throws EE_Error
1841
-     */
1842
-    public function get_admin_settings_link()
1843
-    {
1844
-        return $this->get_admin_details_link();
1845
-    }
1846
-
1847
-    /**
1848
-     * Returns the link to the "overview" for the object (typically the "list table" view).
1849
-     *
1850
-     * @return string
1851
-     */
1852
-    public function get_admin_overview_link()
1853
-    {
1854
-        EE_Registry::instance()->load_helper('URL');
1855
-        return EEH_URL::add_query_args_and_nonce(
1856
-            array(
1857
-                'page' => 'espresso_registrations',
1858
-            ),
1859
-            admin_url('admin.php')
1860
-        );
1861
-    }
1862
-
1863
-
1864
-    /**
1865
-     * @param array $query_params
1866
-     *
1867
-     * @return \EE_Registration[]
1868
-     * @throws EE_Error
1869
-     */
1870
-    public function payments($query_params = array())
1871
-    {
1872
-        return $this->get_many_related('Payment', $query_params);
1873
-    }
1874
-
1875
-
1876
-    /**
1877
-     * @param array $query_params
1878
-     *
1879
-     * @return \EE_Registration_Payment[]
1880
-     * @throws EE_Error
1881
-     */
1882
-    public function registration_payments($query_params = array())
1883
-    {
1884
-        return $this->get_many_related('Registration_Payment', $query_params);
1885
-    }
1886
-
1887
-
1888
-    /**
1889
-     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1890
-     * Note: if there are no payments on the registration there will be no payment method returned.
1891
-     *
1892
-     * @return EE_Payment_Method|null
1893
-     */
1894
-    public function payment_method()
1895
-    {
1896
-        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1897
-    }
1898
-
1899
-
1900
-    /**
1901
-     * @return \EE_Line_Item
1902
-     * @throws EntityNotFoundException
1903
-     * @throws EE_Error
1904
-     */
1905
-    public function ticket_line_item()
1906
-    {
1907
-        $ticket = $this->ticket();
1908
-        $transaction = $this->transaction();
1909
-        $line_item = null;
1910
-        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1911
-            $transaction->total_line_item(),
1912
-            'Ticket',
1913
-            array($ticket->ID())
1914
-        );
1915
-        foreach ($ticket_line_items as $ticket_line_item) {
1916
-            if ($ticket_line_item instanceof \EE_Line_Item
1917
-                && $ticket_line_item->OBJ_type() === 'Ticket'
1918
-                && $ticket_line_item->OBJ_ID() === $ticket->ID()
1919
-            ) {
1920
-                $line_item = $ticket_line_item;
1921
-                break;
1922
-            }
1923
-        }
1924
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1925
-            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1926
-        }
1927
-        return $line_item;
1928
-    }
1929
-
1930
-
1931
-    /**
1932
-     * Soft Deletes this model object.
1933
-     *
1934
-     * @return boolean | int
1935
-     * @throws RuntimeException
1936
-     * @throws EE_Error
1937
-     */
1938
-    public function delete()
1939
-    {
1940
-        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1941
-            $this->set_status(EEM_Registration::status_id_cancelled);
1942
-        }
1943
-        return parent::delete();
1944
-    }
1945
-
1946
-
1947
-    /**
1948
-     * Restores whatever the previous status was on a registration before it was trashed (if possible)
1949
-     *
1950
-     * @throws EE_Error
1951
-     * @throws RuntimeException
1952
-     */
1953
-    public function restore()
1954
-    {
1955
-        $previous_status = $this->get_extra_meta(
1956
-            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1957
-            true,
1958
-            EEM_Registration::status_id_cancelled
1959
-        );
1960
-        if ($previous_status) {
1961
-            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1962
-            $this->set_status($previous_status);
1963
-        }
1964
-        return parent::restore();
1965
-    }
1966
-
1967
-
1968
-    /**
1969
-     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1970
-     *
1971
-     * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1972
-     *                                           depending on whether the reg status changes to or from "Approved"
1973
-     * @return boolean whether the Registration status was updated
1974
-     * @throws EE_Error
1975
-     * @throws RuntimeException
1976
-     */
1977
-    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1978
-    {
1979
-        $paid = $this->paid();
1980
-        $price = $this->final_price();
1981
-        switch (true) {
1982
-            // overpaid or paid
1983
-            case EEH_Money::compare_floats($paid, $price, '>'):
1984
-            case EEH_Money::compare_floats($paid, $price):
1985
-                $new_status = EEM_Registration::status_id_approved;
1986
-                break;
1987
-            //  underpaid
1988
-            case EEH_Money::compare_floats($paid, $price, '<'):
1989
-                $new_status = EEM_Registration::status_id_pending_payment;
1990
-                break;
1991
-            // uhhh Houston...
1992
-            default:
1993
-                throw new RuntimeException(
1994
-                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1995
-                );
1996
-        }
1997
-        if ($new_status !== $this->status_ID()) {
1998
-            if ($trigger_set_status_logic) {
1999
-                return $this->set_status($new_status);
2000
-            }
2001
-            parent::set('STS_ID', $new_status);
2002
-            return true;
2003
-        }
2004
-        return false;
2005
-    }
2006
-
2007
-
2008
-    /*************************** DEPRECATED ***************************/
2009
-
2010
-
2011
-    /**
2012
-     * @deprecated
2013
-     * @since     4.7.0
2014
-     * @access    public
2015
-     */
2016
-    public function price_paid()
2017
-    {
2018
-        EE_Error::doing_it_wrong(
2019
-            'EE_Registration::price_paid()',
2020
-            esc_html__(
2021
-                'This method is deprecated, please use EE_Registration::final_price() instead.',
2022
-                'event_espresso'
2023
-            ),
2024
-            '4.7.0'
2025
-        );
2026
-        return $this->final_price();
2027
-    }
2028
-
2029
-
2030
-    /**
2031
-     * @deprecated
2032
-     * @since     4.7.0
2033
-     * @access    public
2034
-     * @param    float $REG_final_price
2035
-     * @throws EE_Error
2036
-     * @throws RuntimeException
2037
-     */
2038
-    public function set_price_paid($REG_final_price = 0.00)
2039
-    {
2040
-        EE_Error::doing_it_wrong(
2041
-            'EE_Registration::set_price_paid()',
2042
-            esc_html__(
2043
-                'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2044
-                'event_espresso'
2045
-            ),
2046
-            '4.7.0'
2047
-        );
2048
-        $this->set_final_price($REG_final_price);
2049
-    }
2050
-
2051
-
2052
-    /**
2053
-     * @deprecated
2054
-     * @since 4.7.0
2055
-     * @return string
2056
-     * @throws EE_Error
2057
-     */
2058
-    public function pretty_price_paid()
2059
-    {
2060
-        EE_Error::doing_it_wrong(
2061
-            'EE_Registration::pretty_price_paid()',
2062
-            esc_html__(
2063
-                'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2064
-                'event_espresso'
2065
-            ),
2066
-            '4.7.0'
2067
-        );
2068
-        return $this->pretty_final_price();
2069
-    }
2070
-
2071
-
2072
-    /**
2073
-     * Gets the primary datetime related to this registration via the related Event to this registration
2074
-     *
2075
-     * @deprecated 4.9.17
2076
-     * @return EE_Datetime
2077
-     * @throws EE_Error
2078
-     * @throws EntityNotFoundException
2079
-     */
2080
-    public function get_related_primary_datetime()
2081
-    {
2082
-        EE_Error::doing_it_wrong(
2083
-            __METHOD__,
2084
-            esc_html__(
2085
-                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2086
-                'event_espresso'
2087
-            ),
2088
-            '4.9.17',
2089
-            '5.0.0'
2090
-        );
2091
-        return $this->event()->primary_datetime();
2092
-    }
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
+	/**
38
+	 * Used to reference when a registration has been checked out.
39
+	 *
40
+	 * @deprecated use \EE_Checkin::status_checked_out instead
41
+	 * @type int
42
+	 */
43
+	const checkin_status_out = 0;
44
+
45
+
46
+	/**
47
+	 * extra meta key for tracking reg status os trashed registrations
48
+	 *
49
+	 * @type string
50
+	 */
51
+	const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
52
+
53
+
54
+	/**
55
+	 * extra meta key for tracking if registration has reserved ticket
56
+	 *
57
+	 * @type string
58
+	 */
59
+	const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
60
+
61
+
62
+	/**
63
+	 * @param array  $props_n_values          incoming values
64
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
65
+	 *                                        used.)
66
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
67
+	 *                                        date_format and the second value is the time format
68
+	 * @return EE_Registration
69
+	 * @throws EE_Error
70
+	 */
71
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
+	{
73
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
+	}
76
+
77
+
78
+	/**
79
+	 * @param array  $props_n_values  incoming values from the database
80
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
+	 *                                the website will be used.
82
+	 * @return EE_Registration
83
+	 */
84
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
85
+	{
86
+		return new self($props_n_values, true, $timezone);
87
+	}
88
+
89
+
90
+	/**
91
+	 *        Set Event ID
92
+	 *
93
+	 * @param        int $EVT_ID Event ID
94
+	 * @throws EE_Error
95
+	 * @throws RuntimeException
96
+	 */
97
+	public function set_event($EVT_ID = 0)
98
+	{
99
+		$this->set('EVT_ID', $EVT_ID);
100
+	}
101
+
102
+
103
+	/**
104
+	 * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
105
+	 * be routed to internal methods
106
+	 *
107
+	 * @param string $field_name
108
+	 * @param mixed  $field_value
109
+	 * @param bool   $use_default
110
+	 * @throws EE_Error
111
+	 * @throws EntityNotFoundException
112
+	 * @throws InvalidArgumentException
113
+	 * @throws InvalidDataTypeException
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws ReflectionException
116
+	 * @throws RuntimeException
117
+	 */
118
+	public function set($field_name, $field_value, $use_default = false)
119
+	{
120
+		switch ($field_name) {
121
+			case 'REG_code':
122
+				if (! empty($field_value) && $this->reg_code() === null) {
123
+					$this->set_reg_code($field_value, $use_default);
124
+				}
125
+				break;
126
+			case 'STS_ID':
127
+				$this->set_status($field_value, $use_default);
128
+				break;
129
+			default:
130
+				parent::set($field_name, $field_value, $use_default);
131
+		}
132
+	}
133
+
134
+
135
+	/**
136
+	 * Set Status ID
137
+	 * updates the registration status and ALSO...
138
+	 * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
139
+	 * calls release_registration_space() if the reg status changes FROM approved to any other reg status
140
+	 *
141
+	 * @param string                $new_STS_ID
142
+	 * @param boolean               $use_default
143
+	 * @param ContextInterface|null $context
144
+	 * @return bool
145
+	 * @throws EE_Error
146
+	 * @throws EntityNotFoundException
147
+	 * @throws InvalidArgumentException
148
+	 * @throws ReflectionException
149
+	 * @throws RuntimeException
150
+	 * @throws InvalidDataTypeException
151
+	 * @throws InvalidInterfaceException
152
+	 */
153
+	public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
154
+	{
155
+		// get current REG_Status
156
+		$old_STS_ID = $this->status_ID();
157
+		// if status has changed
158
+		if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
159
+			&& ! empty($old_STS_ID) // and that old status is actually set
160
+			&& ! empty($new_STS_ID) // as well as the new status
161
+			&& $this->ID() // ensure registration is in the db
162
+		) {
163
+			// TO approved
164
+			if ($new_STS_ID === EEM_Registration::status_id_approved) {
165
+				// reserve a space by incrementing ticket and datetime sold values
166
+				$this->_reserve_registration_space();
167
+				do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
168
+				// OR FROM  approved
169
+			} elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
170
+				// release a space by decrementing ticket and datetime sold values
171
+				$this->_release_registration_space();
172
+				do_action(
173
+					'AHEE__EE_Registration__set_status__from_approved',
174
+					$this,
175
+					$old_STS_ID,
176
+					$new_STS_ID,
177
+					$context
178
+				);
179
+			}
180
+			// update status
181
+			parent::set('STS_ID', $new_STS_ID, $use_default);
182
+			$this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, $context);
183
+			if ($this->statusChangeUpdatesTransaction($context)) {
184
+				$this->updateTransactionAfterStatusChange();
185
+			}
186
+			do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
187
+			return true;
188
+		}
189
+		// even though the old value matches the new value, it's still good to
190
+		// allow the parent set method to have a say
191
+		parent::set('STS_ID', $new_STS_ID, $use_default);
192
+		return true;
193
+	}
194
+
195
+
196
+	/**
197
+	 * update REGs and TXN when cancelled or declined registrations involved
198
+	 *
199
+	 * @param string                $new_STS_ID
200
+	 * @param string                $old_STS_ID
201
+	 * @param ContextInterface|null $context
202
+	 * @throws EE_Error
203
+	 * @throws InvalidArgumentException
204
+	 * @throws InvalidDataTypeException
205
+	 * @throws InvalidInterfaceException
206
+	 * @throws ReflectionException
207
+	 */
208
+	private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
209
+	{
210
+		// these reg statuses should not be considered in any calculations involving monies owing
211
+		$closed_reg_statuses = EEM_Registration::closed_reg_statuses();
212
+		// true if registration has been cancelled or declined
213
+		$this->updateIfCanceled(
214
+			$closed_reg_statuses,
215
+			$new_STS_ID,
216
+			$old_STS_ID,
217
+			$context
218
+		);
219
+		$this->updateIfDeclined(
220
+			$closed_reg_statuses,
221
+			$new_STS_ID,
222
+			$old_STS_ID,
223
+			$context
224
+		);
225
+	}
226
+
227
+
228
+	/**
229
+	 * update REGs and TXN when cancelled or declined registrations involved
230
+	 *
231
+	 * @param array                 $closed_reg_statuses
232
+	 * @param string                $new_STS_ID
233
+	 * @param string                $old_STS_ID
234
+	 * @param ContextInterface|null $context
235
+	 * @throws EE_Error
236
+	 * @throws InvalidArgumentException
237
+	 * @throws InvalidDataTypeException
238
+	 * @throws InvalidInterfaceException
239
+	 * @throws ReflectionException
240
+	 */
241
+	private function updateIfCanceled(
242
+		array $closed_reg_statuses,
243
+		$new_STS_ID,
244
+		$old_STS_ID,
245
+		ContextInterface $context = null
246
+	) {
247
+		// true if registration has been cancelled or declined
248
+		if (in_array($new_STS_ID, $closed_reg_statuses, true)
249
+			&& ! in_array($old_STS_ID, $closed_reg_statuses, true)
250
+		) {
251
+			/** @type EE_Registration_Processor $registration_processor */
252
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
253
+			/** @type EE_Transaction_Processor $transaction_processor */
254
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
255
+			// cancelled or declined registration
256
+			$registration_processor->update_registration_after_being_canceled_or_declined(
257
+				$this,
258
+				$closed_reg_statuses
259
+			);
260
+			$transaction_processor->update_transaction_after_canceled_or_declined_registration(
261
+				$this,
262
+				$closed_reg_statuses,
263
+				false
264
+			);
265
+			do_action(
266
+				'AHEE__EE_Registration__set_status__canceled_or_declined',
267
+				$this,
268
+				$old_STS_ID,
269
+				$new_STS_ID,
270
+				$context
271
+			);
272
+			return;
273
+		}
274
+	}
275
+
276
+
277
+	/**
278
+	 * update REGs and TXN when cancelled or declined registrations involved
279
+	 *
280
+	 * @param array                 $closed_reg_statuses
281
+	 * @param string                $new_STS_ID
282
+	 * @param string                $old_STS_ID
283
+	 * @param ContextInterface|null $context
284
+	 * @throws EE_Error
285
+	 * @throws InvalidArgumentException
286
+	 * @throws InvalidDataTypeException
287
+	 * @throws InvalidInterfaceException
288
+	 * @throws ReflectionException
289
+	 */
290
+	private function updateIfDeclined(
291
+		array $closed_reg_statuses,
292
+		$new_STS_ID,
293
+		$old_STS_ID,
294
+		ContextInterface $context = null
295
+	) {
296
+		// true if reinstating cancelled or declined registration
297
+		if (in_array($old_STS_ID, $closed_reg_statuses, true)
298
+			&& ! in_array($new_STS_ID, $closed_reg_statuses, true)
299
+		) {
300
+			/** @type EE_Registration_Processor $registration_processor */
301
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
302
+			/** @type EE_Transaction_Processor $transaction_processor */
303
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
304
+			// reinstating cancelled or declined registration
305
+			$registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
306
+				$this,
307
+				$closed_reg_statuses
308
+			);
309
+			$transaction_processor->update_transaction_after_reinstating_canceled_registration(
310
+				$this,
311
+				$closed_reg_statuses,
312
+				false
313
+			);
314
+			do_action(
315
+				'AHEE__EE_Registration__set_status__after_reinstated',
316
+				$this,
317
+				$old_STS_ID,
318
+				$new_STS_ID,
319
+				$context
320
+			);
321
+		}
322
+	}
323
+
324
+
325
+	/**
326
+	 * @param ContextInterface|null $context
327
+	 * @return bool
328
+	 */
329
+	private function statusChangeUpdatesTransaction(ContextInterface $context = null)
330
+	{
331
+		$contexts_that_do_not_update_transaction = (array) apply_filters(
332
+			'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
333
+			array('spco_reg_step_attendee_information_process_registrations'),
334
+			$context,
335
+			$this
336
+		);
337
+		return ! (
338
+			$context instanceof ContextInterface
339
+			&& in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
340
+		);
341
+	}
342
+
343
+
344
+	/**
345
+	 * @throws EE_Error
346
+	 * @throws EntityNotFoundException
347
+	 * @throws InvalidArgumentException
348
+	 * @throws InvalidDataTypeException
349
+	 * @throws InvalidInterfaceException
350
+	 * @throws ReflectionException
351
+	 * @throws RuntimeException
352
+	 */
353
+	private function updateTransactionAfterStatusChange()
354
+	{
355
+		/** @type EE_Transaction_Payments $transaction_payments */
356
+		$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
357
+		$transaction_payments->recalculate_transaction_total($this->transaction(), false);
358
+		$this->transaction()->update_status_based_on_total_paid(true);
359
+	}
360
+
361
+
362
+	/**
363
+	 *        get Status ID
364
+	 */
365
+	public function status_ID()
366
+	{
367
+		return $this->get('STS_ID');
368
+	}
369
+
370
+
371
+	/**
372
+	 * Gets the ticket this registration is for
373
+	 *
374
+	 * @param boolean $include_archived whether to include archived tickets or not.
375
+	 *
376
+	 * @return EE_Ticket|EE_Base_Class
377
+	 * @throws EE_Error
378
+	 */
379
+	public function ticket($include_archived = true)
380
+	{
381
+		$query_params = array();
382
+		if ($include_archived) {
383
+			$query_params['default_where_conditions'] = 'none';
384
+		}
385
+		return $this->get_first_related('Ticket', $query_params);
386
+	}
387
+
388
+
389
+	/**
390
+	 * Gets the event this registration is for
391
+	 *
392
+	 * @return EE_Event
393
+	 * @throws EE_Error
394
+	 * @throws EntityNotFoundException
395
+	 */
396
+	public function event()
397
+	{
398
+		$event = $this->get_first_related('Event');
399
+		if (! $event instanceof \EE_Event) {
400
+			throw new EntityNotFoundException('Event ID', $this->event_ID());
401
+		}
402
+		return $event;
403
+	}
404
+
405
+
406
+	/**
407
+	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
408
+	 * with the author of the event this registration is for.
409
+	 *
410
+	 * @since 4.5.0
411
+	 * @return int
412
+	 * @throws EE_Error
413
+	 * @throws EntityNotFoundException
414
+	 */
415
+	public function wp_user()
416
+	{
417
+		$event = $this->event();
418
+		if ($event instanceof EE_Event) {
419
+			return $event->wp_user();
420
+		}
421
+		return 0;
422
+	}
423
+
424
+
425
+	/**
426
+	 * increments this registration's related ticket sold and corresponding datetime sold values
427
+	 *
428
+	 * @return void
429
+	 * @throws DomainException
430
+	 * @throws EE_Error
431
+	 * @throws EntityNotFoundException
432
+	 * @throws InvalidArgumentException
433
+	 * @throws InvalidDataTypeException
434
+	 * @throws InvalidInterfaceException
435
+	 * @throws ReflectionException
436
+	 * @throws UnexpectedEntityException
437
+	 */
438
+	private function _reserve_registration_space()
439
+	{
440
+		// reserved ticket and datetime counts will be decremented as sold counts are incremented
441
+		// so stop tracking that this reg has a ticket reserved
442
+		$this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
443
+		$ticket = $this->ticket();
444
+		$ticket->increase_sold();
445
+		$ticket->save();
446
+		// possibly set event status to sold out
447
+		$this->event()->perform_sold_out_status_check();
448
+	}
449
+
450
+
451
+	/**
452
+	 * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
453
+	 *
454
+	 * @return void
455
+	 * @throws DomainException
456
+	 * @throws EE_Error
457
+	 * @throws EntityNotFoundException
458
+	 * @throws InvalidArgumentException
459
+	 * @throws InvalidDataTypeException
460
+	 * @throws InvalidInterfaceException
461
+	 * @throws ReflectionException
462
+	 * @throws UnexpectedEntityException
463
+	 */
464
+	private function _release_registration_space()
465
+	{
466
+		$ticket = $this->ticket();
467
+		$ticket->decrease_sold();
468
+		$ticket->save();
469
+		// possibly change event status from sold out back to previous status
470
+		$this->event()->perform_sold_out_status_check();
471
+	}
472
+
473
+
474
+	/**
475
+	 * tracks this registration's ticket reservation in extra meta
476
+	 * and can increment related ticket reserved and corresponding datetime reserved values
477
+	 *
478
+	 * @param bool $update_ticket if true, will increment ticket and datetime reserved count
479
+	 * @return void
480
+	 * @throws EE_Error
481
+	 * @throws InvalidArgumentException
482
+	 * @throws InvalidDataTypeException
483
+	 * @throws InvalidInterfaceException
484
+	 * @throws ReflectionException
485
+	 */
486
+	public function reserve_ticket($update_ticket = false, $source = 'unknown')
487
+	{
488
+		// only reserve ticket if space is not currently reserved
489
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
490
+			$this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
491
+			// IMPORTANT !!!
492
+			// although checking $update_ticket first would be more efficient,
493
+			// we NEED to ALWAYS call update_extra_meta(), which is why that is done first
494
+			if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true)
495
+				&& $update_ticket
496
+			) {
497
+				$ticket = $this->ticket();
498
+				$ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
499
+				$ticket->save();
500
+			}
501
+		}
502
+	}
503
+
504
+
505
+	/**
506
+	 * stops tracking this registration's ticket reservation in extra meta
507
+	 * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
508
+	 *
509
+	 * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
510
+	 * @return void
511
+	 * @throws EE_Error
512
+	 * @throws InvalidArgumentException
513
+	 * @throws InvalidDataTypeException
514
+	 * @throws InvalidInterfaceException
515
+	 * @throws ReflectionException
516
+	 */
517
+	public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
518
+	{
519
+		// only release ticket if space is currently reserved
520
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
521
+			$this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
522
+			// IMPORTANT !!!
523
+			// although checking $update_ticket first would be more efficient,
524
+			// we NEED to ALWAYS call update_extra_meta(), which is why that is done first
525
+			if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false)
526
+				&& $update_ticket
527
+			) {
528
+				$ticket = $this->ticket();
529
+				$ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
530
+				$ticket->save();
531
+			}
532
+		}
533
+	}
534
+
535
+
536
+	/**
537
+	 * Set Attendee ID
538
+	 *
539
+	 * @param        int $ATT_ID Attendee ID
540
+	 * @throws EE_Error
541
+	 * @throws RuntimeException
542
+	 */
543
+	public function set_attendee_id($ATT_ID = 0)
544
+	{
545
+		$this->set('ATT_ID', $ATT_ID);
546
+	}
547
+
548
+
549
+	/**
550
+	 *        Set Transaction ID
551
+	 *
552
+	 * @param        int $TXN_ID Transaction ID
553
+	 * @throws EE_Error
554
+	 * @throws RuntimeException
555
+	 */
556
+	public function set_transaction_id($TXN_ID = 0)
557
+	{
558
+		$this->set('TXN_ID', $TXN_ID);
559
+	}
560
+
561
+
562
+	/**
563
+	 *        Set Session
564
+	 *
565
+	 * @param    string $REG_session PHP Session ID
566
+	 * @throws EE_Error
567
+	 * @throws RuntimeException
568
+	 */
569
+	public function set_session($REG_session = '')
570
+	{
571
+		$this->set('REG_session', $REG_session);
572
+	}
573
+
574
+
575
+	/**
576
+	 *        Set Registration URL Link
577
+	 *
578
+	 * @param    string $REG_url_link Registration URL Link
579
+	 * @throws EE_Error
580
+	 * @throws RuntimeException
581
+	 */
582
+	public function set_reg_url_link($REG_url_link = '')
583
+	{
584
+		$this->set('REG_url_link', $REG_url_link);
585
+	}
586
+
587
+
588
+	/**
589
+	 *        Set Attendee Counter
590
+	 *
591
+	 * @param        int $REG_count Primary Attendee
592
+	 * @throws EE_Error
593
+	 * @throws RuntimeException
594
+	 */
595
+	public function set_count($REG_count = 1)
596
+	{
597
+		$this->set('REG_count', $REG_count);
598
+	}
599
+
600
+
601
+	/**
602
+	 *        Set Group Size
603
+	 *
604
+	 * @param        boolean $REG_group_size Group Registration
605
+	 * @throws EE_Error
606
+	 * @throws RuntimeException
607
+	 */
608
+	public function set_group_size($REG_group_size = false)
609
+	{
610
+		$this->set('REG_group_size', $REG_group_size);
611
+	}
612
+
613
+
614
+	/**
615
+	 *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
616
+	 *    EEM_Registration::status_id_not_approved
617
+	 *
618
+	 * @return        boolean
619
+	 */
620
+	public function is_not_approved()
621
+	{
622
+		return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
623
+	}
624
+
625
+
626
+	/**
627
+	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
628
+	 *    EEM_Registration::status_id_pending_payment
629
+	 *
630
+	 * @return        boolean
631
+	 */
632
+	public function is_pending_payment()
633
+	{
634
+		return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
635
+	}
636
+
637
+
638
+	/**
639
+	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
640
+	 *
641
+	 * @return        boolean
642
+	 */
643
+	public function is_approved()
644
+	{
645
+		return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
646
+	}
647
+
648
+
649
+	/**
650
+	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
651
+	 *
652
+	 * @return        boolean
653
+	 */
654
+	public function is_cancelled()
655
+	{
656
+		return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
657
+	}
658
+
659
+
660
+	/**
661
+	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
662
+	 *
663
+	 * @return        boolean
664
+	 */
665
+	public function is_declined()
666
+	{
667
+		return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
668
+	}
669
+
670
+
671
+	/**
672
+	 *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
673
+	 *    EEM_Registration::status_id_incomplete
674
+	 *
675
+	 * @return        boolean
676
+	 */
677
+	public function is_incomplete()
678
+	{
679
+		return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
680
+	}
681
+
682
+
683
+	/**
684
+	 *        Set Registration Date
685
+	 *
686
+	 * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
687
+	 *                                                 Date
688
+	 * @throws EE_Error
689
+	 * @throws RuntimeException
690
+	 */
691
+	public function set_reg_date($REG_date = false)
692
+	{
693
+		$this->set('REG_date', $REG_date);
694
+	}
695
+
696
+
697
+	/**
698
+	 *    Set final price owing for this registration after all ticket/price modifications
699
+	 *
700
+	 * @access    public
701
+	 * @param    float $REG_final_price
702
+	 * @throws EE_Error
703
+	 * @throws RuntimeException
704
+	 */
705
+	public function set_final_price($REG_final_price = 0.00)
706
+	{
707
+		$this->set('REG_final_price', $REG_final_price);
708
+	}
709
+
710
+
711
+	/**
712
+	 *    Set amount paid towards this registration's final price
713
+	 *
714
+	 * @access    public
715
+	 * @param    float $REG_paid
716
+	 * @throws EE_Error
717
+	 * @throws RuntimeException
718
+	 */
719
+	public function set_paid($REG_paid = 0.00)
720
+	{
721
+		$this->set('REG_paid', $REG_paid);
722
+	}
723
+
724
+
725
+	/**
726
+	 *        Attendee Is Going
727
+	 *
728
+	 * @param        boolean $REG_att_is_going Attendee Is Going
729
+	 * @throws EE_Error
730
+	 * @throws RuntimeException
731
+	 */
732
+	public function set_att_is_going($REG_att_is_going = false)
733
+	{
734
+		$this->set('REG_att_is_going', $REG_att_is_going);
735
+	}
736
+
737
+
738
+	/**
739
+	 * Gets the related attendee
740
+	 *
741
+	 * @return EE_Attendee
742
+	 * @throws EE_Error
743
+	 */
744
+	public function attendee()
745
+	{
746
+		return $this->get_first_related('Attendee');
747
+	}
748
+
749
+
750
+	/**
751
+	 *        get Event ID
752
+	 */
753
+	public function event_ID()
754
+	{
755
+		return $this->get('EVT_ID');
756
+	}
757
+
758
+
759
+	/**
760
+	 *        get Event ID
761
+	 */
762
+	public function event_name()
763
+	{
764
+		$event = $this->event_obj();
765
+		if ($event) {
766
+			return $event->name();
767
+		} else {
768
+			return null;
769
+		}
770
+	}
771
+
772
+
773
+	/**
774
+	 * Fetches the event this registration is for
775
+	 *
776
+	 * @return EE_Event
777
+	 * @throws EE_Error
778
+	 */
779
+	public function event_obj()
780
+	{
781
+		return $this->get_first_related('Event');
782
+	}
783
+
784
+
785
+	/**
786
+	 *        get Attendee ID
787
+	 */
788
+	public function attendee_ID()
789
+	{
790
+		return $this->get('ATT_ID');
791
+	}
792
+
793
+
794
+	/**
795
+	 *        get PHP Session ID
796
+	 */
797
+	public function session_ID()
798
+	{
799
+		return $this->get('REG_session');
800
+	}
801
+
802
+
803
+	/**
804
+	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
805
+	 *
806
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
807
+	 * @return string
808
+	 */
809
+	public function receipt_url($messenger = 'html')
810
+	{
811
+
812
+		/**
813
+		 * The below will be deprecated one version after this.  We check first if there is a custom receipt template
814
+		 * already in use on old system.  If there is then we just return the standard url for it.
815
+		 *
816
+		 * @since 4.5.0
817
+		 */
818
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
819
+		$has_custom = EEH_Template::locate_template(
820
+			$template_relative_path,
821
+			array(),
822
+			true,
823
+			true,
824
+			true
825
+		);
826
+
827
+		if ($has_custom) {
828
+			return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
829
+		}
830
+		return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
831
+	}
832
+
833
+
834
+	/**
835
+	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
836
+	 *
837
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
838
+	 * @return string
839
+	 * @throws EE_Error
840
+	 */
841
+	public function invoice_url($messenger = 'html')
842
+	{
843
+		/**
844
+		 * The below will be deprecated one version after this.  We check first if there is a custom invoice template
845
+		 * already in use on old system.  If there is then we just return the standard url for it.
846
+		 *
847
+		 * @since 4.5.0
848
+		 */
849
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
850
+		$has_custom = EEH_Template::locate_template(
851
+			$template_relative_path,
852
+			array(),
853
+			true,
854
+			true,
855
+			true
856
+		);
857
+
858
+		if ($has_custom) {
859
+			if ($messenger == 'html') {
860
+				return $this->invoice_url('launch');
861
+			}
862
+			$route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
863
+
864
+			$query_args = array('ee' => $route, 'id' => $this->reg_url_link());
865
+			if ($messenger == 'html') {
866
+				$query_args['html'] = true;
867
+			}
868
+			return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
869
+		}
870
+		return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
871
+	}
872
+
873
+
874
+	/**
875
+	 * get Registration URL Link
876
+	 *
877
+	 * @access public
878
+	 * @return string
879
+	 * @throws EE_Error
880
+	 */
881
+	public function reg_url_link()
882
+	{
883
+		return (string) $this->get('REG_url_link');
884
+	}
885
+
886
+
887
+	/**
888
+	 * Echoes out invoice_url()
889
+	 *
890
+	 * @param string $type 'download','launch', or 'html' (default is 'launch')
891
+	 * @return void
892
+	 * @throws EE_Error
893
+	 */
894
+	public function e_invoice_url($type = 'launch')
895
+	{
896
+		echo $this->invoice_url($type);
897
+	}
898
+
899
+
900
+	/**
901
+	 * Echoes out payment_overview_url
902
+	 */
903
+	public function e_payment_overview_url()
904
+	{
905
+		echo $this->payment_overview_url();
906
+	}
907
+
908
+
909
+	/**
910
+	 * Gets the URL for the checkout payment options reg step
911
+	 * with this registration's REG_url_link added as a query parameter
912
+	 *
913
+	 * @param bool $clear_session Set to true when you want to clear the session on revisiting the
914
+	 *                            payment overview url.
915
+	 * @return string
916
+	 * @throws InvalidInterfaceException
917
+	 * @throws InvalidDataTypeException
918
+	 * @throws EE_Error
919
+	 * @throws InvalidArgumentException
920
+	 */
921
+	public function payment_overview_url($clear_session = false)
922
+	{
923
+		return add_query_arg(
924
+			(array) apply_filters(
925
+				'FHEE__EE_Registration__payment_overview_url__query_args',
926
+				array(
927
+					'e_reg_url_link' => $this->reg_url_link(),
928
+					'step'           => 'payment_options',
929
+					'revisit'        => true,
930
+					'clear_session'  => (bool) $clear_session,
931
+				),
932
+				$this
933
+			),
934
+			EE_Registry::instance()->CFG->core->reg_page_url()
935
+		);
936
+	}
937
+
938
+
939
+	/**
940
+	 * Gets the URL for the checkout attendee information reg step
941
+	 * with this registration's REG_url_link added as a query parameter
942
+	 *
943
+	 * @return string
944
+	 * @throws InvalidInterfaceException
945
+	 * @throws InvalidDataTypeException
946
+	 * @throws EE_Error
947
+	 * @throws InvalidArgumentException
948
+	 */
949
+	public function edit_attendee_information_url()
950
+	{
951
+		return add_query_arg(
952
+			(array) apply_filters(
953
+				'FHEE__EE_Registration__edit_attendee_information_url__query_args',
954
+				array(
955
+					'e_reg_url_link' => $this->reg_url_link(),
956
+					'step'           => 'attendee_information',
957
+					'revisit'        => true,
958
+				),
959
+				$this
960
+			),
961
+			EE_Registry::instance()->CFG->core->reg_page_url()
962
+		);
963
+	}
964
+
965
+
966
+	/**
967
+	 * Simply generates and returns the appropriate admin_url link to edit this registration
968
+	 *
969
+	 * @return string
970
+	 * @throws EE_Error
971
+	 */
972
+	public function get_admin_edit_url()
973
+	{
974
+		return EEH_URL::add_query_args_and_nonce(
975
+			array(
976
+				'page'    => 'espresso_registrations',
977
+				'action'  => 'view_registration',
978
+				'_REG_ID' => $this->ID(),
979
+			),
980
+			admin_url('admin.php')
981
+		);
982
+	}
983
+
984
+
985
+	/**
986
+	 *    is_primary_registrant?
987
+	 */
988
+	public function is_primary_registrant()
989
+	{
990
+		return $this->get('REG_count') === 1 ? true : false;
991
+	}
992
+
993
+
994
+	/**
995
+	 * This returns the primary registration object for this registration group (which may be this object).
996
+	 *
997
+	 * @return EE_Registration
998
+	 * @throws EE_Error
999
+	 */
1000
+	public function get_primary_registration()
1001
+	{
1002
+		if ($this->is_primary_registrant()) {
1003
+			return $this;
1004
+		}
1005
+
1006
+		// k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1007
+		/** @var EE_Registration $primary_registrant */
1008
+		$primary_registrant = EEM_Registration::instance()->get_one(
1009
+			array(
1010
+				array(
1011
+					'TXN_ID'    => $this->transaction_ID(),
1012
+					'REG_count' => 1,
1013
+				),
1014
+			)
1015
+		);
1016
+		return $primary_registrant;
1017
+	}
1018
+
1019
+
1020
+	/**
1021
+	 *        get  Attendee Number
1022
+	 *
1023
+	 * @access        public
1024
+	 */
1025
+	public function count()
1026
+	{
1027
+		return $this->get('REG_count');
1028
+	}
1029
+
1030
+
1031
+	/**
1032
+	 *        get Group Size
1033
+	 */
1034
+	public function group_size()
1035
+	{
1036
+		return $this->get('REG_group_size');
1037
+	}
1038
+
1039
+
1040
+	/**
1041
+	 *        get Registration Date
1042
+	 */
1043
+	public function date()
1044
+	{
1045
+		return $this->get('REG_date');
1046
+	}
1047
+
1048
+
1049
+	/**
1050
+	 * gets a pretty date
1051
+	 *
1052
+	 * @param string $date_format
1053
+	 * @param string $time_format
1054
+	 * @return string
1055
+	 * @throws EE_Error
1056
+	 */
1057
+	public function pretty_date($date_format = null, $time_format = null)
1058
+	{
1059
+		return $this->get_datetime('REG_date', $date_format, $time_format);
1060
+	}
1061
+
1062
+
1063
+	/**
1064
+	 * final_price
1065
+	 * the registration's share of the transaction total, so that the
1066
+	 * sum of all the transaction's REG_final_prices equal the transaction's total
1067
+	 *
1068
+	 * @return float
1069
+	 * @throws EE_Error
1070
+	 */
1071
+	public function final_price()
1072
+	{
1073
+		return $this->get('REG_final_price');
1074
+	}
1075
+
1076
+
1077
+	/**
1078
+	 * pretty_final_price
1079
+	 *  final price as formatted string, with correct decimal places and currency symbol
1080
+	 *
1081
+	 * @return string
1082
+	 * @throws EE_Error
1083
+	 */
1084
+	public function pretty_final_price()
1085
+	{
1086
+		return $this->get_pretty('REG_final_price');
1087
+	}
1088
+
1089
+
1090
+	/**
1091
+	 * get paid (yeah)
1092
+	 *
1093
+	 * @return float
1094
+	 * @throws EE_Error
1095
+	 */
1096
+	public function paid()
1097
+	{
1098
+		return $this->get('REG_paid');
1099
+	}
1100
+
1101
+
1102
+	/**
1103
+	 * pretty_paid
1104
+	 *
1105
+	 * @return float
1106
+	 * @throws EE_Error
1107
+	 */
1108
+	public function pretty_paid()
1109
+	{
1110
+		return $this->get_pretty('REG_paid');
1111
+	}
1112
+
1113
+
1114
+	/**
1115
+	 * owes_monies_and_can_pay
1116
+	 * whether or not this registration has monies owing and it's' status allows payment
1117
+	 *
1118
+	 * @param array $requires_payment
1119
+	 * @return bool
1120
+	 * @throws EE_Error
1121
+	 */
1122
+	public function owes_monies_and_can_pay($requires_payment = array())
1123
+	{
1124
+		// these reg statuses require payment (if event is not free)
1125
+		$requires_payment = ! empty($requires_payment)
1126
+			? $requires_payment
1127
+			: EEM_Registration::reg_statuses_that_allow_payment();
1128
+		if (in_array($this->status_ID(), $requires_payment) &&
1129
+			$this->final_price() != 0 &&
1130
+			$this->final_price() != $this->paid()
1131
+		) {
1132
+			return true;
1133
+		} else {
1134
+			return false;
1135
+		}
1136
+	}
1137
+
1138
+
1139
+	/**
1140
+	 * Prints out the return value of $this->pretty_status()
1141
+	 *
1142
+	 * @param bool $show_icons
1143
+	 * @return void
1144
+	 * @throws EE_Error
1145
+	 */
1146
+	public function e_pretty_status($show_icons = false)
1147
+	{
1148
+		echo $this->pretty_status($show_icons);
1149
+	}
1150
+
1151
+
1152
+	/**
1153
+	 * Returns a nice version of the status for displaying to customers
1154
+	 *
1155
+	 * @param bool $show_icons
1156
+	 * @return string
1157
+	 * @throws EE_Error
1158
+	 */
1159
+	public function pretty_status($show_icons = false)
1160
+	{
1161
+		$status = EEM_Status::instance()->localized_status(
1162
+			array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1163
+			false,
1164
+			'sentence'
1165
+		);
1166
+		$icon = '';
1167
+		switch ($this->status_ID()) {
1168
+			case EEM_Registration::status_id_approved:
1169
+				$icon = $show_icons
1170
+					? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1171
+					: '';
1172
+				break;
1173
+			case EEM_Registration::status_id_pending_payment:
1174
+				$icon = $show_icons
1175
+					? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1176
+					: '';
1177
+				break;
1178
+			case EEM_Registration::status_id_not_approved:
1179
+				$icon = $show_icons
1180
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1181
+					: '';
1182
+				break;
1183
+			case EEM_Registration::status_id_cancelled:
1184
+				$icon = $show_icons
1185
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1186
+					: '';
1187
+				break;
1188
+			case EEM_Registration::status_id_incomplete:
1189
+				$icon = $show_icons
1190
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1191
+					: '';
1192
+				break;
1193
+			case EEM_Registration::status_id_declined:
1194
+				$icon = $show_icons
1195
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1196
+					: '';
1197
+				break;
1198
+			case EEM_Registration::status_id_wait_list:
1199
+				$icon = $show_icons
1200
+					? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1201
+					: '';
1202
+				break;
1203
+		}
1204
+		return $icon . $status[ $this->status_ID() ];
1205
+	}
1206
+
1207
+
1208
+	/**
1209
+	 *        get Attendee Is Going
1210
+	 */
1211
+	public function att_is_going()
1212
+	{
1213
+		return $this->get('REG_att_is_going');
1214
+	}
1215
+
1216
+
1217
+	/**
1218
+	 * Gets related answers
1219
+	 *
1220
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1221
+	 * @return EE_Answer[]
1222
+	 * @throws EE_Error
1223
+	 */
1224
+	public function answers($query_params = null)
1225
+	{
1226
+		return $this->get_many_related('Answer', $query_params);
1227
+	}
1228
+
1229
+
1230
+	/**
1231
+	 * Gets the registration's answer value to the specified question
1232
+	 * (either the question's ID or a question object)
1233
+	 *
1234
+	 * @param EE_Question|int $question
1235
+	 * @param bool            $pretty_value
1236
+	 * @return array|string if pretty_value= true, the result will always be a string
1237
+	 * (because the answer might be an array of answer values, so passing pretty_value=true
1238
+	 * will convert it into some kind of string)
1239
+	 * @throws EE_Error
1240
+	 */
1241
+	public function answer_value_to_question($question, $pretty_value = true)
1242
+	{
1243
+		$question_id = EEM_Question::instance()->ensure_is_ID($question);
1244
+		return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1245
+	}
1246
+
1247
+
1248
+	/**
1249
+	 * question_groups
1250
+	 * returns an array of EE_Question_Group objects for this registration
1251
+	 *
1252
+	 * @return EE_Question_Group[]
1253
+	 * @throws EE_Error
1254
+	 * @throws EntityNotFoundException
1255
+	 */
1256
+	public function question_groups()
1257
+	{
1258
+		$question_groups = array();
1259
+		if ($this->event() instanceof EE_Event) {
1260
+			$query_params = [
1261
+				[
1262
+					'Event_Question_Group.'
1263
+					. EEM_Event_Question_Group::instance()->field_name_for_category(
1264
+						$this->is_primary_registrant()
1265
+					) => true
1266
+				],
1267
+				'order_by' => ['QSG_order' => 'ASC']];
1268
+			$question_groups = $this->event()->question_groups($query_params);
1269
+		}
1270
+		return $question_groups;
1271
+	}
1272
+
1273
+
1274
+	/**
1275
+	 * count_question_groups
1276
+	 * returns a count of the number of EE_Question_Group objects for this registration
1277
+	 *
1278
+	 * @return int
1279
+	 * @throws EE_Error
1280
+	 * @throws EntityNotFoundException
1281
+	 * @throws InvalidArgumentException
1282
+	 * @throws InvalidDataTypeException
1283
+	 * @throws InvalidInterfaceException
1284
+	 * @throws ReflectionException
1285
+	 */
1286
+	public function count_question_groups()
1287
+	{
1288
+		$qg_count = 0;
1289
+		if ($this->event() instanceof EE_Event) {
1290
+			if ($this->is_primary_registrant()) {
1291
+				$query_params =[['Event_Question_Group.EQG_primary' => true]];
1292
+			} else {
1293
+				$query_params = [['Event_Question_Group.EQG_additional' => true]];
1294
+			}
1295
+			$qg_count = $this->event()->count_related(
1296
+				'Question_Group',
1297
+				$query_params
1298
+			);
1299
+		}
1300
+		return $qg_count;
1301
+	}
1302
+
1303
+
1304
+	/**
1305
+	 * Returns the registration date in the 'standard' string format
1306
+	 * (function may be improved in the future to allow for different formats and timezones)
1307
+	 *
1308
+	 * @return string
1309
+	 * @throws EE_Error
1310
+	 */
1311
+	public function reg_date()
1312
+	{
1313
+		return $this->get_datetime('REG_date');
1314
+	}
1315
+
1316
+
1317
+	/**
1318
+	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1319
+	 * the ticket this registration purchased, or the datetime they have registered
1320
+	 * to attend)
1321
+	 *
1322
+	 * @return EE_Datetime_Ticket
1323
+	 * @throws EE_Error
1324
+	 */
1325
+	public function datetime_ticket()
1326
+	{
1327
+		return $this->get_first_related('Datetime_Ticket');
1328
+	}
1329
+
1330
+
1331
+	/**
1332
+	 * Sets the registration's datetime_ticket.
1333
+	 *
1334
+	 * @param EE_Datetime_Ticket $datetime_ticket
1335
+	 * @return EE_Datetime_Ticket
1336
+	 * @throws EE_Error
1337
+	 */
1338
+	public function set_datetime_ticket($datetime_ticket)
1339
+	{
1340
+		return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1341
+	}
1342
+
1343
+	/**
1344
+	 * Gets deleted
1345
+	 *
1346
+	 * @return bool
1347
+	 * @throws EE_Error
1348
+	 */
1349
+	public function deleted()
1350
+	{
1351
+		return $this->get('REG_deleted');
1352
+	}
1353
+
1354
+	/**
1355
+	 * Sets deleted
1356
+	 *
1357
+	 * @param boolean $deleted
1358
+	 * @return bool
1359
+	 * @throws EE_Error
1360
+	 * @throws RuntimeException
1361
+	 */
1362
+	public function set_deleted($deleted)
1363
+	{
1364
+		if ($deleted) {
1365
+			$this->delete();
1366
+		} else {
1367
+			$this->restore();
1368
+		}
1369
+	}
1370
+
1371
+
1372
+	/**
1373
+	 * Get the status object of this object
1374
+	 *
1375
+	 * @return EE_Status
1376
+	 * @throws EE_Error
1377
+	 */
1378
+	public function status_obj()
1379
+	{
1380
+		return $this->get_first_related('Status');
1381
+	}
1382
+
1383
+
1384
+	/**
1385
+	 * Returns the number of times this registration has checked into any of the datetimes
1386
+	 * its available for
1387
+	 *
1388
+	 * @return int
1389
+	 * @throws EE_Error
1390
+	 */
1391
+	public function count_checkins()
1392
+	{
1393
+		return $this->get_model()->count_related($this, 'Checkin');
1394
+	}
1395
+
1396
+
1397
+	/**
1398
+	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1399
+	 * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1400
+	 *
1401
+	 * @return int
1402
+	 * @throws EE_Error
1403
+	 */
1404
+	public function count_checkins_not_checkedout()
1405
+	{
1406
+		return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1407
+	}
1408
+
1409
+
1410
+	/**
1411
+	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1412
+	 *
1413
+	 * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1414
+	 * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1415
+	 *                                          consider registration status as well as datetime access.
1416
+	 * @return bool
1417
+	 * @throws EE_Error
1418
+	 */
1419
+	public function can_checkin($DTT_OR_ID, $check_approved = true)
1420
+	{
1421
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1422
+
1423
+		// first check registration status
1424
+		if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1425
+			return false;
1426
+		}
1427
+		// is there a datetime ticket that matches this dtt_ID?
1428
+		if (! (EEM_Datetime_Ticket::instance()->exists(
1429
+			array(
1430
+				array(
1431
+					'TKT_ID' => $this->get('TKT_ID'),
1432
+					'DTT_ID' => $DTT_ID,
1433
+				),
1434
+			)
1435
+		))
1436
+		) {
1437
+			return false;
1438
+		}
1439
+
1440
+		// final check is against TKT_uses
1441
+		return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1442
+	}
1443
+
1444
+
1445
+	/**
1446
+	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1447
+	 * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1448
+	 * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1449
+	 * then return false.  Otherwise return true.
1450
+	 *
1451
+	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1452
+	 * @return bool true means can checkin.  false means cannot checkin.
1453
+	 * @throws EE_Error
1454
+	 */
1455
+	public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1456
+	{
1457
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1458
+
1459
+		if (! $DTT_ID) {
1460
+			return false;
1461
+		}
1462
+
1463
+		$max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1464
+
1465
+		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1466
+		// check-in or not.
1467
+		if (! $max_uses || $max_uses === EE_INF) {
1468
+			return true;
1469
+		}
1470
+
1471
+		// does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1472
+		// go ahead and toggle.
1473
+		if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1474
+			return true;
1475
+		}
1476
+
1477
+		// made it here so the last check is whether the number of checkins per unique datetime on this registration
1478
+		// disallows further check-ins.
1479
+		$count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1480
+			array(
1481
+				array(
1482
+					'REG_ID' => $this->ID(),
1483
+					'CHK_in' => true,
1484
+				),
1485
+			),
1486
+			'DTT_ID',
1487
+			true
1488
+		);
1489
+		// checkins have already reached their max number of uses
1490
+		// so registrant can NOT checkin
1491
+		if ($count_unique_dtt_checkins >= $max_uses) {
1492
+			EE_Error::add_error(
1493
+				esc_html__(
1494
+					'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1495
+					'event_espresso'
1496
+				),
1497
+				__FILE__,
1498
+				__FUNCTION__,
1499
+				__LINE__
1500
+			);
1501
+			return false;
1502
+		}
1503
+		return true;
1504
+	}
1505
+
1506
+
1507
+	/**
1508
+	 * toggle Check-in status for this registration
1509
+	 * Check-ins are toggled in the following order:
1510
+	 * never checked in -> checked in
1511
+	 * checked in -> checked out
1512
+	 * checked out -> checked in
1513
+	 *
1514
+	 * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1515
+	 *                      If not included or null, then it is assumed latest datetime is being toggled.
1516
+	 * @param bool $verify  If true then can_checkin() is used to verify whether the person
1517
+	 *                      can be checked in or not.  Otherwise this forces change in checkin status.
1518
+	 * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1519
+	 * @throws EE_Error
1520
+	 */
1521
+	public function toggle_checkin_status($DTT_ID = null, $verify = false)
1522
+	{
1523
+		if (empty($DTT_ID)) {
1524
+			$datetime = $this->get_latest_related_datetime();
1525
+			$DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1526
+			// verify the registration can checkin for the given DTT_ID
1527
+		} elseif (! $this->can_checkin($DTT_ID, $verify)) {
1528
+			EE_Error::add_error(
1529
+				sprintf(
1530
+					esc_html__(
1531
+						'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',
1532
+						'event_espresso'
1533
+					),
1534
+					$this->ID(),
1535
+					$DTT_ID
1536
+				),
1537
+				__FILE__,
1538
+				__FUNCTION__,
1539
+				__LINE__
1540
+			);
1541
+			return false;
1542
+		}
1543
+		$status_paths = array(
1544
+			EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1545
+			EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1546
+			EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1547
+		);
1548
+		// start by getting the current status so we know what status we'll be changing to.
1549
+		$cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1550
+		$status_to = $status_paths[ $cur_status ];
1551
+		// database only records true for checked IN or false for checked OUT
1552
+		// no record ( null ) means checked in NEVER, but we obviously don't save that
1553
+		$new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1554
+		// add relation - note Check-ins are always creating new rows
1555
+		// because we are keeping track of Check-ins over time.
1556
+		// Eventually we'll probably want to show a list table
1557
+		// for the individual Check-ins so that they can be managed.
1558
+		$checkin = EE_Checkin::new_instance(
1559
+			array(
1560
+				'REG_ID' => $this->ID(),
1561
+				'DTT_ID' => $DTT_ID,
1562
+				'CHK_in' => $new_status,
1563
+			)
1564
+		);
1565
+		// if the record could not be saved then return false
1566
+		if ($checkin->save() === 0) {
1567
+			if (WP_DEBUG) {
1568
+				global $wpdb;
1569
+				$error = sprintf(
1570
+					esc_html__(
1571
+						'Registration check in update failed because of the following database error: %1$s%2$s',
1572
+						'event_espresso'
1573
+					),
1574
+					'<br />',
1575
+					$wpdb->last_error
1576
+				);
1577
+			} else {
1578
+				$error = esc_html__(
1579
+					'Registration check in update failed because of an unknown database error',
1580
+					'event_espresso'
1581
+				);
1582
+			}
1583
+			EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1584
+			return false;
1585
+		}
1586
+		return $status_to;
1587
+	}
1588
+
1589
+
1590
+	/**
1591
+	 * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1592
+	 * "Latest" is defined by the `DTT_EVT_start` column.
1593
+	 *
1594
+	 * @return EE_Datetime|null
1595
+	 * @throws EE_Error
1596
+	 */
1597
+	public function get_latest_related_datetime()
1598
+	{
1599
+		return EEM_Datetime::instance()->get_one(
1600
+			array(
1601
+				array(
1602
+					'Ticket.Registration.REG_ID' => $this->ID(),
1603
+				),
1604
+				'order_by' => array('DTT_EVT_start' => 'DESC'),
1605
+			)
1606
+		);
1607
+	}
1608
+
1609
+
1610
+	/**
1611
+	 * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1612
+	 * "Earliest" is defined by the `DTT_EVT_start` column.
1613
+	 *
1614
+	 * @throws EE_Error
1615
+	 */
1616
+	public function get_earliest_related_datetime()
1617
+	{
1618
+		return EEM_Datetime::instance()->get_one(
1619
+			array(
1620
+				array(
1621
+					'Ticket.Registration.REG_ID' => $this->ID(),
1622
+				),
1623
+				'order_by' => array('DTT_EVT_start' => 'ASC'),
1624
+			)
1625
+		);
1626
+	}
1627
+
1628
+
1629
+	/**
1630
+	 * This method simply returns the check-in status for this registration and the given datetime.
1631
+	 * If neither the datetime nor the checkin values are provided as arguments,
1632
+	 * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1633
+	 *
1634
+	 * @param  int       $DTT_ID  The ID of the datetime we're checking against
1635
+	 *                            (if empty we'll get the primary datetime for
1636
+	 *                            this registration (via event) and use it's ID);
1637
+	 * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1638
+	 *
1639
+	 * @return int                Integer representing Check-in status.
1640
+	 * @throws EE_Error
1641
+	 */
1642
+	public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1643
+	{
1644
+		$checkin_query_params = array(
1645
+			'order_by' => array('CHK_timestamp' => 'DESC'),
1646
+		);
1647
+
1648
+		if ($DTT_ID > 0) {
1649
+			$checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1650
+		}
1651
+
1652
+		// get checkin object (if exists)
1653
+		$checkin = $checkin instanceof EE_Checkin
1654
+			? $checkin
1655
+			: $this->get_first_related('Checkin', $checkin_query_params);
1656
+		if ($checkin instanceof EE_Checkin) {
1657
+			if ($checkin->get('CHK_in')) {
1658
+				return EE_Checkin::status_checked_in; // checked in
1659
+			}
1660
+			return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1661
+		}
1662
+		return EE_Checkin::status_checked_never; // never been checked in
1663
+	}
1664
+
1665
+
1666
+	/**
1667
+	 * This method returns a localized message for the toggled Check-in message.
1668
+	 *
1669
+	 * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1670
+	 *                     then it is assumed Check-in for primary datetime was toggled.
1671
+	 * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1672
+	 *                     message can be customized with the attendee name.
1673
+	 * @return string internationalized message
1674
+	 * @throws EE_Error
1675
+	 */
1676
+	public function get_checkin_msg($DTT_ID, $error = false)
1677
+	{
1678
+		// let's get the attendee first so we can include the name of the attendee
1679
+		$attendee = $this->get_first_related('Attendee');
1680
+		if ($attendee instanceof EE_Attendee) {
1681
+			if ($error) {
1682
+				return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1683
+			}
1684
+			$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1685
+			// what is the status message going to be?
1686
+			switch ($cur_status) {
1687
+				case EE_Checkin::status_checked_never:
1688
+					return sprintf(
1689
+						__("%s has been removed from Check-in records", "event_espresso"),
1690
+						$attendee->full_name()
1691
+					);
1692
+					break;
1693
+				case EE_Checkin::status_checked_in:
1694
+					return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1695
+					break;
1696
+				case EE_Checkin::status_checked_out:
1697
+					return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1698
+					break;
1699
+			}
1700
+		}
1701
+		return esc_html__("The check-in status could not be determined.", "event_espresso");
1702
+	}
1703
+
1704
+
1705
+	/**
1706
+	 * Returns the related EE_Transaction to this registration
1707
+	 *
1708
+	 * @return EE_Transaction
1709
+	 * @throws EE_Error
1710
+	 * @throws EntityNotFoundException
1711
+	 */
1712
+	public function transaction()
1713
+	{
1714
+		$transaction = $this->get_first_related('Transaction');
1715
+		if (! $transaction instanceof \EE_Transaction) {
1716
+			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1717
+		}
1718
+		return $transaction;
1719
+	}
1720
+
1721
+
1722
+	/**
1723
+	 *        get Registration Code
1724
+	 */
1725
+	public function reg_code()
1726
+	{
1727
+		return $this->get('REG_code');
1728
+	}
1729
+
1730
+
1731
+	/**
1732
+	 *        get Transaction ID
1733
+	 */
1734
+	public function transaction_ID()
1735
+	{
1736
+		return $this->get('TXN_ID');
1737
+	}
1738
+
1739
+
1740
+	/**
1741
+	 * @return int
1742
+	 * @throws EE_Error
1743
+	 */
1744
+	public function ticket_ID()
1745
+	{
1746
+		return $this->get('TKT_ID');
1747
+	}
1748
+
1749
+
1750
+	/**
1751
+	 *        Set Registration Code
1752
+	 *
1753
+	 * @access    public
1754
+	 * @param    string  $REG_code Registration Code
1755
+	 * @param    boolean $use_default
1756
+	 * @throws EE_Error
1757
+	 */
1758
+	public function set_reg_code($REG_code, $use_default = false)
1759
+	{
1760
+		if (empty($REG_code)) {
1761
+			EE_Error::add_error(
1762
+				esc_html__('REG_code can not be empty.', 'event_espresso'),
1763
+				__FILE__,
1764
+				__FUNCTION__,
1765
+				__LINE__
1766
+			);
1767
+			return;
1768
+		}
1769
+		if (! $this->reg_code()) {
1770
+			parent::set('REG_code', $REG_code, $use_default);
1771
+		} else {
1772
+			EE_Error::doing_it_wrong(
1773
+				__CLASS__ . '::' . __FUNCTION__,
1774
+				esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1775
+				'4.6.0'
1776
+			);
1777
+		}
1778
+	}
1779
+
1780
+
1781
+	/**
1782
+	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
1783
+	 * Note, if you want to just get all registrations in the same transaction (group), use:
1784
+	 *    $registration->transaction()->registrations();
1785
+	 *
1786
+	 * @since 4.5.0
1787
+	 * @return EE_Registration[] or empty array if this isn't a group registration.
1788
+	 * @throws EE_Error
1789
+	 */
1790
+	public function get_all_other_registrations_in_group()
1791
+	{
1792
+		if ($this->group_size() < 2) {
1793
+			return array();
1794
+		}
1795
+
1796
+		$query[0] = array(
1797
+			'TXN_ID' => $this->transaction_ID(),
1798
+			'REG_ID' => array('!=', $this->ID()),
1799
+			'TKT_ID' => $this->ticket_ID(),
1800
+		);
1801
+		/** @var EE_Registration[] $registrations */
1802
+		$registrations = $this->get_model()->get_all($query);
1803
+		return $registrations;
1804
+	}
1805
+
1806
+	/**
1807
+	 * Return the link to the admin details for the object.
1808
+	 *
1809
+	 * @return string
1810
+	 * @throws EE_Error
1811
+	 */
1812
+	public function get_admin_details_link()
1813
+	{
1814
+		EE_Registry::instance()->load_helper('URL');
1815
+		return EEH_URL::add_query_args_and_nonce(
1816
+			array(
1817
+				'page'    => 'espresso_registrations',
1818
+				'action'  => 'view_registration',
1819
+				'_REG_ID' => $this->ID(),
1820
+			),
1821
+			admin_url('admin.php')
1822
+		);
1823
+	}
1824
+
1825
+	/**
1826
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1827
+	 *
1828
+	 * @return string
1829
+	 * @throws EE_Error
1830
+	 */
1831
+	public function get_admin_edit_link()
1832
+	{
1833
+		return $this->get_admin_details_link();
1834
+	}
1835
+
1836
+	/**
1837
+	 * Returns the link to a settings page for the object.
1838
+	 *
1839
+	 * @return string
1840
+	 * @throws EE_Error
1841
+	 */
1842
+	public function get_admin_settings_link()
1843
+	{
1844
+		return $this->get_admin_details_link();
1845
+	}
1846
+
1847
+	/**
1848
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
1849
+	 *
1850
+	 * @return string
1851
+	 */
1852
+	public function get_admin_overview_link()
1853
+	{
1854
+		EE_Registry::instance()->load_helper('URL');
1855
+		return EEH_URL::add_query_args_and_nonce(
1856
+			array(
1857
+				'page' => 'espresso_registrations',
1858
+			),
1859
+			admin_url('admin.php')
1860
+		);
1861
+	}
1862
+
1863
+
1864
+	/**
1865
+	 * @param array $query_params
1866
+	 *
1867
+	 * @return \EE_Registration[]
1868
+	 * @throws EE_Error
1869
+	 */
1870
+	public function payments($query_params = array())
1871
+	{
1872
+		return $this->get_many_related('Payment', $query_params);
1873
+	}
1874
+
1875
+
1876
+	/**
1877
+	 * @param array $query_params
1878
+	 *
1879
+	 * @return \EE_Registration_Payment[]
1880
+	 * @throws EE_Error
1881
+	 */
1882
+	public function registration_payments($query_params = array())
1883
+	{
1884
+		return $this->get_many_related('Registration_Payment', $query_params);
1885
+	}
1886
+
1887
+
1888
+	/**
1889
+	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1890
+	 * Note: if there are no payments on the registration there will be no payment method returned.
1891
+	 *
1892
+	 * @return EE_Payment_Method|null
1893
+	 */
1894
+	public function payment_method()
1895
+	{
1896
+		return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1897
+	}
1898
+
1899
+
1900
+	/**
1901
+	 * @return \EE_Line_Item
1902
+	 * @throws EntityNotFoundException
1903
+	 * @throws EE_Error
1904
+	 */
1905
+	public function ticket_line_item()
1906
+	{
1907
+		$ticket = $this->ticket();
1908
+		$transaction = $this->transaction();
1909
+		$line_item = null;
1910
+		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1911
+			$transaction->total_line_item(),
1912
+			'Ticket',
1913
+			array($ticket->ID())
1914
+		);
1915
+		foreach ($ticket_line_items as $ticket_line_item) {
1916
+			if ($ticket_line_item instanceof \EE_Line_Item
1917
+				&& $ticket_line_item->OBJ_type() === 'Ticket'
1918
+				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
1919
+			) {
1920
+				$line_item = $ticket_line_item;
1921
+				break;
1922
+			}
1923
+		}
1924
+		if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1925
+			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1926
+		}
1927
+		return $line_item;
1928
+	}
1929
+
1930
+
1931
+	/**
1932
+	 * Soft Deletes this model object.
1933
+	 *
1934
+	 * @return boolean | int
1935
+	 * @throws RuntimeException
1936
+	 * @throws EE_Error
1937
+	 */
1938
+	public function delete()
1939
+	{
1940
+		if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1941
+			$this->set_status(EEM_Registration::status_id_cancelled);
1942
+		}
1943
+		return parent::delete();
1944
+	}
1945
+
1946
+
1947
+	/**
1948
+	 * Restores whatever the previous status was on a registration before it was trashed (if possible)
1949
+	 *
1950
+	 * @throws EE_Error
1951
+	 * @throws RuntimeException
1952
+	 */
1953
+	public function restore()
1954
+	{
1955
+		$previous_status = $this->get_extra_meta(
1956
+			EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1957
+			true,
1958
+			EEM_Registration::status_id_cancelled
1959
+		);
1960
+		if ($previous_status) {
1961
+			$this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1962
+			$this->set_status($previous_status);
1963
+		}
1964
+		return parent::restore();
1965
+	}
1966
+
1967
+
1968
+	/**
1969
+	 * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1970
+	 *
1971
+	 * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1972
+	 *                                           depending on whether the reg status changes to or from "Approved"
1973
+	 * @return boolean whether the Registration status was updated
1974
+	 * @throws EE_Error
1975
+	 * @throws RuntimeException
1976
+	 */
1977
+	public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1978
+	{
1979
+		$paid = $this->paid();
1980
+		$price = $this->final_price();
1981
+		switch (true) {
1982
+			// overpaid or paid
1983
+			case EEH_Money::compare_floats($paid, $price, '>'):
1984
+			case EEH_Money::compare_floats($paid, $price):
1985
+				$new_status = EEM_Registration::status_id_approved;
1986
+				break;
1987
+			//  underpaid
1988
+			case EEH_Money::compare_floats($paid, $price, '<'):
1989
+				$new_status = EEM_Registration::status_id_pending_payment;
1990
+				break;
1991
+			// uhhh Houston...
1992
+			default:
1993
+				throw new RuntimeException(
1994
+					esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1995
+				);
1996
+		}
1997
+		if ($new_status !== $this->status_ID()) {
1998
+			if ($trigger_set_status_logic) {
1999
+				return $this->set_status($new_status);
2000
+			}
2001
+			parent::set('STS_ID', $new_status);
2002
+			return true;
2003
+		}
2004
+		return false;
2005
+	}
2006
+
2007
+
2008
+	/*************************** DEPRECATED ***************************/
2009
+
2010
+
2011
+	/**
2012
+	 * @deprecated
2013
+	 * @since     4.7.0
2014
+	 * @access    public
2015
+	 */
2016
+	public function price_paid()
2017
+	{
2018
+		EE_Error::doing_it_wrong(
2019
+			'EE_Registration::price_paid()',
2020
+			esc_html__(
2021
+				'This method is deprecated, please use EE_Registration::final_price() instead.',
2022
+				'event_espresso'
2023
+			),
2024
+			'4.7.0'
2025
+		);
2026
+		return $this->final_price();
2027
+	}
2028
+
2029
+
2030
+	/**
2031
+	 * @deprecated
2032
+	 * @since     4.7.0
2033
+	 * @access    public
2034
+	 * @param    float $REG_final_price
2035
+	 * @throws EE_Error
2036
+	 * @throws RuntimeException
2037
+	 */
2038
+	public function set_price_paid($REG_final_price = 0.00)
2039
+	{
2040
+		EE_Error::doing_it_wrong(
2041
+			'EE_Registration::set_price_paid()',
2042
+			esc_html__(
2043
+				'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2044
+				'event_espresso'
2045
+			),
2046
+			'4.7.0'
2047
+		);
2048
+		$this->set_final_price($REG_final_price);
2049
+	}
2050
+
2051
+
2052
+	/**
2053
+	 * @deprecated
2054
+	 * @since 4.7.0
2055
+	 * @return string
2056
+	 * @throws EE_Error
2057
+	 */
2058
+	public function pretty_price_paid()
2059
+	{
2060
+		EE_Error::doing_it_wrong(
2061
+			'EE_Registration::pretty_price_paid()',
2062
+			esc_html__(
2063
+				'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2064
+				'event_espresso'
2065
+			),
2066
+			'4.7.0'
2067
+		);
2068
+		return $this->pretty_final_price();
2069
+	}
2070
+
2071
+
2072
+	/**
2073
+	 * Gets the primary datetime related to this registration via the related Event to this registration
2074
+	 *
2075
+	 * @deprecated 4.9.17
2076
+	 * @return EE_Datetime
2077
+	 * @throws EE_Error
2078
+	 * @throws EntityNotFoundException
2079
+	 */
2080
+	public function get_related_primary_datetime()
2081
+	{
2082
+		EE_Error::doing_it_wrong(
2083
+			__METHOD__,
2084
+			esc_html__(
2085
+				'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2086
+				'event_espresso'
2087
+			),
2088
+			'4.9.17',
2089
+			'5.0.0'
2090
+		);
2091
+		return $this->event()->primary_datetime();
2092
+	}
2093 2093
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
     {
120 120
         switch ($field_name) {
121 121
             case 'REG_code':
122
-                if (! empty($field_value) && $this->reg_code() === null) {
122
+                if ( ! empty($field_value) && $this->reg_code() === null) {
123 123
                     $this->set_reg_code($field_value, $use_default);
124 124
                 }
125 125
                 break;
@@ -396,7 +396,7 @@  discard block
 block discarded – undo
396 396
     public function event()
397 397
     {
398 398
         $event = $this->get_first_related('Event');
399
-        if (! $event instanceof \EE_Event) {
399
+        if ( ! $event instanceof \EE_Event) {
400 400
             throw new EntityNotFoundException('Event ID', $this->event_ID());
401 401
         }
402 402
         return $event;
@@ -439,7 +439,7 @@  discard block
 block discarded – undo
439 439
     {
440 440
         // reserved ticket and datetime counts will be decremented as sold counts are incremented
441 441
         // so stop tracking that this reg has a ticket reserved
442
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
442
+        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:".__LINE__.')');
443 443
         $ticket = $this->ticket();
444 444
         $ticket->increase_sold();
445 445
         $ticket->save();
@@ -495,7 +495,7 @@  discard block
 block discarded – undo
495 495
                 && $update_ticket
496 496
             ) {
497 497
                 $ticket = $this->ticket();
498
-                $ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
498
+                $ticket->increase_reserved(1, "REG: {$this->ID()} (ln:".__LINE__.')');
499 499
                 $ticket->save();
500 500
             }
501 501
         }
@@ -526,7 +526,7 @@  discard block
 block discarded – undo
526 526
                 && $update_ticket
527 527
             ) {
528 528
                 $ticket = $this->ticket();
529
-                $ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
529
+                $ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:".__LINE__.')');
530 530
                 $ticket->save();
531 531
             }
532 532
         }
@@ -1201,7 +1201,7 @@  discard block
 block discarded – undo
1201 1201
                     : '';
1202 1202
                 break;
1203 1203
         }
1204
-        return $icon . $status[ $this->status_ID() ];
1204
+        return $icon.$status[$this->status_ID()];
1205 1205
     }
1206 1206
 
1207 1207
 
@@ -1288,7 +1288,7 @@  discard block
 block discarded – undo
1288 1288
         $qg_count = 0;
1289 1289
         if ($this->event() instanceof EE_Event) {
1290 1290
             if ($this->is_primary_registrant()) {
1291
-                $query_params =[['Event_Question_Group.EQG_primary' => true]];
1291
+                $query_params = [['Event_Question_Group.EQG_primary' => true]];
1292 1292
             } else {
1293 1293
                 $query_params = [['Event_Question_Group.EQG_additional' => true]];
1294 1294
             }
@@ -1425,7 +1425,7 @@  discard block
 block discarded – undo
1425 1425
             return false;
1426 1426
         }
1427 1427
         // is there a datetime ticket that matches this dtt_ID?
1428
-        if (! (EEM_Datetime_Ticket::instance()->exists(
1428
+        if ( ! (EEM_Datetime_Ticket::instance()->exists(
1429 1429
             array(
1430 1430
                 array(
1431 1431
                     'TKT_ID' => $this->get('TKT_ID'),
@@ -1456,7 +1456,7 @@  discard block
 block discarded – undo
1456 1456
     {
1457 1457
         $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1458 1458
 
1459
-        if (! $DTT_ID) {
1459
+        if ( ! $DTT_ID) {
1460 1460
             return false;
1461 1461
         }
1462 1462
 
@@ -1464,7 +1464,7 @@  discard block
 block discarded – undo
1464 1464
 
1465 1465
         // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1466 1466
         // check-in or not.
1467
-        if (! $max_uses || $max_uses === EE_INF) {
1467
+        if ( ! $max_uses || $max_uses === EE_INF) {
1468 1468
             return true;
1469 1469
         }
1470 1470
 
@@ -1524,7 +1524,7 @@  discard block
 block discarded – undo
1524 1524
             $datetime = $this->get_latest_related_datetime();
1525 1525
             $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1526 1526
             // verify the registration can checkin for the given DTT_ID
1527
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1527
+        } elseif ( ! $this->can_checkin($DTT_ID, $verify)) {
1528 1528
             EE_Error::add_error(
1529 1529
                 sprintf(
1530 1530
                     esc_html__(
@@ -1547,7 +1547,7 @@  discard block
 block discarded – undo
1547 1547
         );
1548 1548
         // start by getting the current status so we know what status we'll be changing to.
1549 1549
         $cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1550
-        $status_to = $status_paths[ $cur_status ];
1550
+        $status_to = $status_paths[$cur_status];
1551 1551
         // database only records true for checked IN or false for checked OUT
1552 1552
         // no record ( null ) means checked in NEVER, but we obviously don't save that
1553 1553
         $new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
@@ -1712,7 +1712,7 @@  discard block
 block discarded – undo
1712 1712
     public function transaction()
1713 1713
     {
1714 1714
         $transaction = $this->get_first_related('Transaction');
1715
-        if (! $transaction instanceof \EE_Transaction) {
1715
+        if ( ! $transaction instanceof \EE_Transaction) {
1716 1716
             throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1717 1717
         }
1718 1718
         return $transaction;
@@ -1766,11 +1766,11 @@  discard block
 block discarded – undo
1766 1766
             );
1767 1767
             return;
1768 1768
         }
1769
-        if (! $this->reg_code()) {
1769
+        if ( ! $this->reg_code()) {
1770 1770
             parent::set('REG_code', $REG_code, $use_default);
1771 1771
         } else {
1772 1772
             EE_Error::doing_it_wrong(
1773
-                __CLASS__ . '::' . __FUNCTION__,
1773
+                __CLASS__.'::'.__FUNCTION__,
1774 1774
                 esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1775 1775
                 '4.6.0'
1776 1776
             );
@@ -1921,7 +1921,7 @@  discard block
 block discarded – undo
1921 1921
                 break;
1922 1922
             }
1923 1923
         }
1924
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1924
+        if ( ! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1925 1925
             throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1926 1926
         }
1927 1927
         return $line_item;
Please login to merge, or discard this patch.