Completed
Branch master (c87329)
by
unknown
49:13 queued 41:42
created
core/db_models/EEM_Line_Item.model.php 1 patch
Indentation   +595 added lines, -595 removed lines patch added patch discarded remove patch
@@ -28,602 +28,602 @@
 block discarded – undo
28 28
 class EEM_Line_Item extends EEM_Base
29 29
 {
30 30
 
31
-    /**
32
-     * Tax sub-total is just the total of all the taxes, which should be children
33
-     * of this line item. There should only ever be one tax sub-total, and it should
34
-     * be a direct child of. Its quantity and LIN_unit_price = 1.
35
-     */
36
-    const type_tax_sub_total = 'tax-sub-total';
37
-
38
-    /**
39
-     * Tax line items indicate a tax applied to all the taxable line items.
40
-     * Should not have any children line items. Its LIN_unit_price = 0. Its LIN_percent is a percent, not a decimal
41
-     * (eg 10% tax = 10, not 0.1). Its LIN_total = LIN_unit_price * pre-tax-total. Quantity = 1.
42
-     */
43
-    const type_tax = 'tax';
44
-
45
-    /**
46
-     * Indicating individual items purchased, or discounts or surcharges.
47
-     * The sum of all the regular line items  plus the tax items should equal the grand total.
48
-     * Possible children are sub-line-items and cancellations.
49
-     * For flat items, LIN_unit_price * LIN_quantity = LIN_total. Its LIN_total is the sum of all the children
50
-     * LIN_totals. Its LIN_percent = 0.
51
-     * For percent items, its LIN_unit_price = 0. Its LIN_percent is a percent, not a decimal (eg 10% = 10, not 0.1).
52
-     * Its LIN_total is LIN_percent / 100 * sum of lower-priority sibling line items. Quantity = 1.
53
-     */
54
-    const type_line_item = 'line-item';
55
-
56
-    /**
57
-     * Line item indicating all the factors that make a single line item.
58
-     * Sub-line items should have NO children line items.
59
-     * For flat sub-items, their quantity should match their parent item, their LIN_unit_price should be this sub-item's
60
-     * contribution towards the price of ONE of their parent items, and its LIN_total should be
61
-     *  = LIN_quantity * LIN_unit_price. Its LIN_percent = 0.
62
-     * For percent sub-items, the quantity should be 1, LIN_unit_price should be 0, and its LIN_total should
63
-     * = LIN_percent / 100 * sum of lower-priority sibling line items..
64
-     */
65
-    const type_sub_line_item = 'sub-item';
66
-
67
-    /**
68
-     * Line item indicating a sub-total (eg total for an event, or pre-tax subtotal).
69
-     * Direct children should be event subtotals.
70
-     * Should have quantity of 1, and a LIN_total and LIN_unit_price of the sum of all its sub-items' LIN_totals.
71
-     */
72
-    const type_sub_total = 'sub-total';
73
-
74
-    /**
75
-     * Line item for the grand total of an order.
76
-     * Its direct children should be tax subtotals and (pre-tax) subtotals,
77
-     * and possibly a regular line item indicating a transaction-wide discount/surcharge.
78
-     * Should have a quantity of 1, a LIN_total and LIN_unit_price of the entire order's amount.
79
-     */
80
-    const type_total = 'total';
81
-
82
-    /**
83
-     * When a line item is cancelled, a sub-line-item of type 'cancellation'
84
-     * should be created, indicating the quantity that were cancelled
85
-     * (because a line item could have a quantity of 1, and its cancellation item
86
-     * could be for 3, indicating that originally 4 were purchased, but 3 have been
87
-     * cancelled, and only one remains).
88
-     * When items are refunded, a cancellation line item should be made, which points
89
-     * to teh payment model object which actually refunded the payment.
90
-     * Cancellations should NOT have any children line items; the should NOT affect
91
-     * any calculations, and are only meant as a record that cancellations have occurred.
92
-     * Their LIN_percent should be 0.
93
-     */
94
-    const type_cancellation = 'cancellation';
95
-
96
-    // various line item object types
97
-    const OBJ_TYPE_EVENT = 'Event';
98
-
99
-    const OBJ_TYPE_PRICE = 'Price';
100
-
101
-    const OBJ_TYPE_PROMOTION = 'Promotion';
102
-
103
-    const OBJ_TYPE_TICKET = 'Ticket';
104
-
105
-    const OBJ_TYPE_TRANSACTION = 'Transaction';
106
-
107
-    /**
108
-     * @var EEM_Line_Item $_instance
109
-     */
110
-    protected static $_instance;
111
-
112
-
113
-    /**
114
-     * private constructor to prevent direct creation
115
-     *
116
-     * @Constructor
117
-     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings
118
-     *                         (and any incoming timezone data that gets saved).
119
-     *                         Note this just sends the timezone info to the date time model field objects.
120
-     *                         Default is NULL
121
-     *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
122
-     * @throws EE_Error
123
-     * @throws InvalidArgumentException
124
-     */
125
-    protected function __construct($timezone)
126
-    {
127
-        $this->singular_item = esc_html__('Line Item', 'event_espresso');
128
-        $this->plural_item = esc_html__('Line Items', 'event_espresso');
129
-
130
-        $this->_tables = array(
131
-            'Line_Item' => new EE_Primary_Table('esp_line_item', 'LIN_ID'),
132
-        );
133
-        $line_items_can_be_for = apply_filters(
134
-            'FHEE__EEM_Line_Item__line_items_can_be_for',
135
-            array('Ticket', 'Price', 'Event')
136
-        );
137
-        $this->_fields = array(
138
-            'Line_Item' => array(
139
-                'LIN_ID'         => new EE_Primary_Key_Int_Field(
140
-                    'LIN_ID',
141
-                    esc_html__('ID', 'event_espresso')
142
-                ),
143
-                'LIN_code'       => new EE_Slug_Field(
144
-                    'LIN_code',
145
-                    esc_html__('Code for index into Cart', 'event_espresso'),
146
-                    true
147
-                ),
148
-                'TXN_ID'         => new EE_Foreign_Key_Int_Field(
149
-                    'TXN_ID',
150
-                    esc_html__('Transaction ID', 'event_espresso'),
151
-                    true,
152
-                    null,
153
-                    'Transaction'
154
-                ),
155
-                'LIN_name'       => new EE_Full_HTML_Field(
156
-                    'LIN_name',
157
-                    esc_html__('Line Item Name', 'event_espresso'),
158
-                    false,
159
-                    ''
160
-                ),
161
-                'LIN_desc'       => new EE_Full_HTML_Field(
162
-                    'LIN_desc',
163
-                    esc_html__('Line Item Description', 'event_espresso'),
164
-                    true
165
-                ),
166
-                'LIN_unit_price' => new EE_Money_Field(
167
-                    'LIN_unit_price',
168
-                    esc_html__('Unit Price', 'event_espresso'),
169
-                    false,
170
-                    0
171
-                ),
172
-                'LIN_percent'    => new EE_Float_Field(
173
-                    'LIN_percent',
174
-                    esc_html__('Percent', 'event_espresso'),
175
-                    false,
176
-                    0
177
-                ),
178
-                'LIN_is_taxable' => new EE_Boolean_Field(
179
-                    'LIN_is_taxable',
180
-                    esc_html__('Taxable', 'event_espresso'),
181
-                    false,
182
-                    false
183
-                ),
184
-                'LIN_order'      => new EE_Integer_Field(
185
-                    'LIN_order',
186
-                    esc_html__('Order of Application towards total of parent', 'event_espresso'),
187
-                    false,
188
-                    1
189
-                ),
190
-                'LIN_total'      => new EE_Money_Field(
191
-                    'LIN_total',
192
-                    esc_html__('Total (unit price x quantity)', 'event_espresso'),
193
-                    false,
194
-                    0
195
-                ),
196
-                'LIN_quantity'   => new EE_Integer_Field(
197
-                    'LIN_quantity',
198
-                    esc_html__('Quantity', 'event_espresso'),
199
-                    true,
200
-                    1
201
-                ),
202
-                'LIN_parent'     => new EE_Integer_Field(
203
-                    'LIN_parent',
204
-                    esc_html__("Parent ID (this item goes towards that Line Item's total)", 'event_espresso'),
205
-                    true,
206
-                    null
207
-                ),
208
-                'LIN_type'       => new EE_Enum_Text_Field(
209
-                    'LIN_type',
210
-                    esc_html__('Type', 'event_espresso'),
211
-                    false,
212
-                    'line-item',
213
-                    array(
214
-                        self::type_line_item     => esc_html__('Line Item', 'event_espresso'),
215
-                        self::type_sub_line_item => esc_html__('Sub-Item', 'event_espresso'),
216
-                        self::type_sub_total     => esc_html__('Subtotal', 'event_espresso'),
217
-                        self::type_tax_sub_total => esc_html__('Tax Subtotal', 'event_espresso'),
218
-                        self::type_tax           => esc_html__('Tax', 'event_espresso'),
219
-                        self::type_total         => esc_html__('Total', 'event_espresso'),
220
-                        self::type_cancellation  => esc_html__('Cancellation', 'event_espresso'),
221
-                    )
222
-                ),
223
-                'OBJ_ID'         => new EE_Foreign_Key_Int_Field(
224
-                    'OBJ_ID',
225
-                    esc_html__('ID of Item purchased.', 'event_espresso'),
226
-                    true,
227
-                    null,
228
-                    $line_items_can_be_for
229
-                ),
230
-                'OBJ_type'       => new EE_Any_Foreign_Model_Name_Field(
231
-                    'OBJ_type',
232
-                    esc_html__('Model Name this Line Item is for', 'event_espresso'),
233
-                    true,
234
-                    null,
235
-                    $line_items_can_be_for
236
-                ),
237
-                'LIN_timestamp'  => new EE_Datetime_Field(
238
-                    'LIN_timestamp',
239
-                    esc_html__('When the line item was created', 'event_espresso'),
240
-                    false,
241
-                    EE_Datetime_Field::now,
242
-                    $timezone
243
-                ),
244
-            ),
245
-        );
246
-        $this->_model_relations = array(
247
-            'Transaction' => new EE_Belongs_To_Relation(),
248
-            'Ticket'      => new EE_Belongs_To_Any_Relation(),
249
-            'Price'       => new EE_Belongs_To_Any_Relation(),
250
-            'Event'       => new EE_Belongs_To_Any_Relation(),
251
-        );
252
-        $this->_model_chain_to_wp_user = 'Transaction.Registration.Event';
253
-        $this->_caps_slug = 'transactions';
254
-        parent::__construct($timezone);
255
-    }
256
-
257
-
258
-    /**
259
-     * Gets all the line items for this transaction of the given type
260
-     *
261
-     * @param string             $line_item_type like one of EEM_Line_Item::type_*
262
-     * @param EE_Transaction|int $transaction
263
-     * @return EE_Base_Class[]|EE_Line_Item[]
264
-     * @throws EE_Error
265
-     * @throws InvalidArgumentException
266
-     * @throws InvalidDataTypeException
267
-     * @throws InvalidInterfaceException
268
-     */
269
-    public function get_all_of_type_for_transaction($line_item_type, $transaction)
270
-    {
271
-        $transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
272
-        return $this->get_all(array(
273
-            array(
274
-                'LIN_type' => $line_item_type,
275
-                'TXN_ID'   => $transaction,
276
-            ),
277
-        ));
278
-    }
279
-
280
-
281
-    /**
282
-     * Gets all line items unrelated to tickets that are normal line items
283
-     * (eg shipping, promotions, and miscellaneous other stuff should probably fit in this category)
284
-     *
285
-     * @param EE_Transaction|int $transaction
286
-     * @return EE_Base_Class[]|EE_Line_Item[]
287
-     * @throws EE_Error
288
-     * @throws InvalidArgumentException
289
-     * @throws InvalidDataTypeException
290
-     * @throws InvalidInterfaceException
291
-     */
292
-    public function get_all_non_ticket_line_items_for_transaction($transaction)
293
-    {
294
-        $transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
295
-        return $this->get_all(array(
296
-            array(
297
-                'LIN_type' => self::type_line_item,
298
-                'TXN_ID'   => $transaction,
299
-                'OR'       => array(
300
-                    'OBJ_type*notticket' => array('!=', EEM_Line_Item::OBJ_TYPE_TICKET),
301
-                    'OBJ_type*null'      => array('IS_NULL'),
302
-                ),
303
-            ),
304
-        ));
305
-    }
306
-
307
-
308
-    /**
309
-     * Deletes line items with no transaction who have passed the transaction cutoff time.
310
-     * This needs to be very efficient
311
-     * because if there are spam bots afoot there will be LOTS of line items. Also MySQL doesn't allow a limit when
312
-     * deleting and joining tables like this.
313
-     *
314
-     * @return int count of how many deleted
315
-     * @throws EE_Error
316
-     * @throws InvalidArgumentException
317
-     * @throws InvalidDataTypeException
318
-     * @throws InvalidInterfaceException
319
-     */
320
-    public function delete_line_items_with_no_transaction()
321
-    {
322
-        /** @type WPDB $wpdb */
323
-        global $wpdb;
324
-        $time_to_leave_alone = apply_filters(
325
-            'FHEE__EEM_Line_Item__delete_line_items_with_no_transaction__time_to_leave_alone',
326
-            WEEK_IN_SECONDS
327
-        );
328
-        $query = $wpdb->prepare(
329
-            'DELETE li
31
+	/**
32
+	 * Tax sub-total is just the total of all the taxes, which should be children
33
+	 * of this line item. There should only ever be one tax sub-total, and it should
34
+	 * be a direct child of. Its quantity and LIN_unit_price = 1.
35
+	 */
36
+	const type_tax_sub_total = 'tax-sub-total';
37
+
38
+	/**
39
+	 * Tax line items indicate a tax applied to all the taxable line items.
40
+	 * Should not have any children line items. Its LIN_unit_price = 0. Its LIN_percent is a percent, not a decimal
41
+	 * (eg 10% tax = 10, not 0.1). Its LIN_total = LIN_unit_price * pre-tax-total. Quantity = 1.
42
+	 */
43
+	const type_tax = 'tax';
44
+
45
+	/**
46
+	 * Indicating individual items purchased, or discounts or surcharges.
47
+	 * The sum of all the regular line items  plus the tax items should equal the grand total.
48
+	 * Possible children are sub-line-items and cancellations.
49
+	 * For flat items, LIN_unit_price * LIN_quantity = LIN_total. Its LIN_total is the sum of all the children
50
+	 * LIN_totals. Its LIN_percent = 0.
51
+	 * For percent items, its LIN_unit_price = 0. Its LIN_percent is a percent, not a decimal (eg 10% = 10, not 0.1).
52
+	 * Its LIN_total is LIN_percent / 100 * sum of lower-priority sibling line items. Quantity = 1.
53
+	 */
54
+	const type_line_item = 'line-item';
55
+
56
+	/**
57
+	 * Line item indicating all the factors that make a single line item.
58
+	 * Sub-line items should have NO children line items.
59
+	 * For flat sub-items, their quantity should match their parent item, their LIN_unit_price should be this sub-item's
60
+	 * contribution towards the price of ONE of their parent items, and its LIN_total should be
61
+	 *  = LIN_quantity * LIN_unit_price. Its LIN_percent = 0.
62
+	 * For percent sub-items, the quantity should be 1, LIN_unit_price should be 0, and its LIN_total should
63
+	 * = LIN_percent / 100 * sum of lower-priority sibling line items..
64
+	 */
65
+	const type_sub_line_item = 'sub-item';
66
+
67
+	/**
68
+	 * Line item indicating a sub-total (eg total for an event, or pre-tax subtotal).
69
+	 * Direct children should be event subtotals.
70
+	 * Should have quantity of 1, and a LIN_total and LIN_unit_price of the sum of all its sub-items' LIN_totals.
71
+	 */
72
+	const type_sub_total = 'sub-total';
73
+
74
+	/**
75
+	 * Line item for the grand total of an order.
76
+	 * Its direct children should be tax subtotals and (pre-tax) subtotals,
77
+	 * and possibly a regular line item indicating a transaction-wide discount/surcharge.
78
+	 * Should have a quantity of 1, a LIN_total and LIN_unit_price of the entire order's amount.
79
+	 */
80
+	const type_total = 'total';
81
+
82
+	/**
83
+	 * When a line item is cancelled, a sub-line-item of type 'cancellation'
84
+	 * should be created, indicating the quantity that were cancelled
85
+	 * (because a line item could have a quantity of 1, and its cancellation item
86
+	 * could be for 3, indicating that originally 4 were purchased, but 3 have been
87
+	 * cancelled, and only one remains).
88
+	 * When items are refunded, a cancellation line item should be made, which points
89
+	 * to teh payment model object which actually refunded the payment.
90
+	 * Cancellations should NOT have any children line items; the should NOT affect
91
+	 * any calculations, and are only meant as a record that cancellations have occurred.
92
+	 * Their LIN_percent should be 0.
93
+	 */
94
+	const type_cancellation = 'cancellation';
95
+
96
+	// various line item object types
97
+	const OBJ_TYPE_EVENT = 'Event';
98
+
99
+	const OBJ_TYPE_PRICE = 'Price';
100
+
101
+	const OBJ_TYPE_PROMOTION = 'Promotion';
102
+
103
+	const OBJ_TYPE_TICKET = 'Ticket';
104
+
105
+	const OBJ_TYPE_TRANSACTION = 'Transaction';
106
+
107
+	/**
108
+	 * @var EEM_Line_Item $_instance
109
+	 */
110
+	protected static $_instance;
111
+
112
+
113
+	/**
114
+	 * private constructor to prevent direct creation
115
+	 *
116
+	 * @Constructor
117
+	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings
118
+	 *                         (and any incoming timezone data that gets saved).
119
+	 *                         Note this just sends the timezone info to the date time model field objects.
120
+	 *                         Default is NULL
121
+	 *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
122
+	 * @throws EE_Error
123
+	 * @throws InvalidArgumentException
124
+	 */
125
+	protected function __construct($timezone)
126
+	{
127
+		$this->singular_item = esc_html__('Line Item', 'event_espresso');
128
+		$this->plural_item = esc_html__('Line Items', 'event_espresso');
129
+
130
+		$this->_tables = array(
131
+			'Line_Item' => new EE_Primary_Table('esp_line_item', 'LIN_ID'),
132
+		);
133
+		$line_items_can_be_for = apply_filters(
134
+			'FHEE__EEM_Line_Item__line_items_can_be_for',
135
+			array('Ticket', 'Price', 'Event')
136
+		);
137
+		$this->_fields = array(
138
+			'Line_Item' => array(
139
+				'LIN_ID'         => new EE_Primary_Key_Int_Field(
140
+					'LIN_ID',
141
+					esc_html__('ID', 'event_espresso')
142
+				),
143
+				'LIN_code'       => new EE_Slug_Field(
144
+					'LIN_code',
145
+					esc_html__('Code for index into Cart', 'event_espresso'),
146
+					true
147
+				),
148
+				'TXN_ID'         => new EE_Foreign_Key_Int_Field(
149
+					'TXN_ID',
150
+					esc_html__('Transaction ID', 'event_espresso'),
151
+					true,
152
+					null,
153
+					'Transaction'
154
+				),
155
+				'LIN_name'       => new EE_Full_HTML_Field(
156
+					'LIN_name',
157
+					esc_html__('Line Item Name', 'event_espresso'),
158
+					false,
159
+					''
160
+				),
161
+				'LIN_desc'       => new EE_Full_HTML_Field(
162
+					'LIN_desc',
163
+					esc_html__('Line Item Description', 'event_espresso'),
164
+					true
165
+				),
166
+				'LIN_unit_price' => new EE_Money_Field(
167
+					'LIN_unit_price',
168
+					esc_html__('Unit Price', 'event_espresso'),
169
+					false,
170
+					0
171
+				),
172
+				'LIN_percent'    => new EE_Float_Field(
173
+					'LIN_percent',
174
+					esc_html__('Percent', 'event_espresso'),
175
+					false,
176
+					0
177
+				),
178
+				'LIN_is_taxable' => new EE_Boolean_Field(
179
+					'LIN_is_taxable',
180
+					esc_html__('Taxable', 'event_espresso'),
181
+					false,
182
+					false
183
+				),
184
+				'LIN_order'      => new EE_Integer_Field(
185
+					'LIN_order',
186
+					esc_html__('Order of Application towards total of parent', 'event_espresso'),
187
+					false,
188
+					1
189
+				),
190
+				'LIN_total'      => new EE_Money_Field(
191
+					'LIN_total',
192
+					esc_html__('Total (unit price x quantity)', 'event_espresso'),
193
+					false,
194
+					0
195
+				),
196
+				'LIN_quantity'   => new EE_Integer_Field(
197
+					'LIN_quantity',
198
+					esc_html__('Quantity', 'event_espresso'),
199
+					true,
200
+					1
201
+				),
202
+				'LIN_parent'     => new EE_Integer_Field(
203
+					'LIN_parent',
204
+					esc_html__("Parent ID (this item goes towards that Line Item's total)", 'event_espresso'),
205
+					true,
206
+					null
207
+				),
208
+				'LIN_type'       => new EE_Enum_Text_Field(
209
+					'LIN_type',
210
+					esc_html__('Type', 'event_espresso'),
211
+					false,
212
+					'line-item',
213
+					array(
214
+						self::type_line_item     => esc_html__('Line Item', 'event_espresso'),
215
+						self::type_sub_line_item => esc_html__('Sub-Item', 'event_espresso'),
216
+						self::type_sub_total     => esc_html__('Subtotal', 'event_espresso'),
217
+						self::type_tax_sub_total => esc_html__('Tax Subtotal', 'event_espresso'),
218
+						self::type_tax           => esc_html__('Tax', 'event_espresso'),
219
+						self::type_total         => esc_html__('Total', 'event_espresso'),
220
+						self::type_cancellation  => esc_html__('Cancellation', 'event_espresso'),
221
+					)
222
+				),
223
+				'OBJ_ID'         => new EE_Foreign_Key_Int_Field(
224
+					'OBJ_ID',
225
+					esc_html__('ID of Item purchased.', 'event_espresso'),
226
+					true,
227
+					null,
228
+					$line_items_can_be_for
229
+				),
230
+				'OBJ_type'       => new EE_Any_Foreign_Model_Name_Field(
231
+					'OBJ_type',
232
+					esc_html__('Model Name this Line Item is for', 'event_espresso'),
233
+					true,
234
+					null,
235
+					$line_items_can_be_for
236
+				),
237
+				'LIN_timestamp'  => new EE_Datetime_Field(
238
+					'LIN_timestamp',
239
+					esc_html__('When the line item was created', 'event_espresso'),
240
+					false,
241
+					EE_Datetime_Field::now,
242
+					$timezone
243
+				),
244
+			),
245
+		);
246
+		$this->_model_relations = array(
247
+			'Transaction' => new EE_Belongs_To_Relation(),
248
+			'Ticket'      => new EE_Belongs_To_Any_Relation(),
249
+			'Price'       => new EE_Belongs_To_Any_Relation(),
250
+			'Event'       => new EE_Belongs_To_Any_Relation(),
251
+		);
252
+		$this->_model_chain_to_wp_user = 'Transaction.Registration.Event';
253
+		$this->_caps_slug = 'transactions';
254
+		parent::__construct($timezone);
255
+	}
256
+
257
+
258
+	/**
259
+	 * Gets all the line items for this transaction of the given type
260
+	 *
261
+	 * @param string             $line_item_type like one of EEM_Line_Item::type_*
262
+	 * @param EE_Transaction|int $transaction
263
+	 * @return EE_Base_Class[]|EE_Line_Item[]
264
+	 * @throws EE_Error
265
+	 * @throws InvalidArgumentException
266
+	 * @throws InvalidDataTypeException
267
+	 * @throws InvalidInterfaceException
268
+	 */
269
+	public function get_all_of_type_for_transaction($line_item_type, $transaction)
270
+	{
271
+		$transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
272
+		return $this->get_all(array(
273
+			array(
274
+				'LIN_type' => $line_item_type,
275
+				'TXN_ID'   => $transaction,
276
+			),
277
+		));
278
+	}
279
+
280
+
281
+	/**
282
+	 * Gets all line items unrelated to tickets that are normal line items
283
+	 * (eg shipping, promotions, and miscellaneous other stuff should probably fit in this category)
284
+	 *
285
+	 * @param EE_Transaction|int $transaction
286
+	 * @return EE_Base_Class[]|EE_Line_Item[]
287
+	 * @throws EE_Error
288
+	 * @throws InvalidArgumentException
289
+	 * @throws InvalidDataTypeException
290
+	 * @throws InvalidInterfaceException
291
+	 */
292
+	public function get_all_non_ticket_line_items_for_transaction($transaction)
293
+	{
294
+		$transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
295
+		return $this->get_all(array(
296
+			array(
297
+				'LIN_type' => self::type_line_item,
298
+				'TXN_ID'   => $transaction,
299
+				'OR'       => array(
300
+					'OBJ_type*notticket' => array('!=', EEM_Line_Item::OBJ_TYPE_TICKET),
301
+					'OBJ_type*null'      => array('IS_NULL'),
302
+				),
303
+			),
304
+		));
305
+	}
306
+
307
+
308
+	/**
309
+	 * Deletes line items with no transaction who have passed the transaction cutoff time.
310
+	 * This needs to be very efficient
311
+	 * because if there are spam bots afoot there will be LOTS of line items. Also MySQL doesn't allow a limit when
312
+	 * deleting and joining tables like this.
313
+	 *
314
+	 * @return int count of how many deleted
315
+	 * @throws EE_Error
316
+	 * @throws InvalidArgumentException
317
+	 * @throws InvalidDataTypeException
318
+	 * @throws InvalidInterfaceException
319
+	 */
320
+	public function delete_line_items_with_no_transaction()
321
+	{
322
+		/** @type WPDB $wpdb */
323
+		global $wpdb;
324
+		$time_to_leave_alone = apply_filters(
325
+			'FHEE__EEM_Line_Item__delete_line_items_with_no_transaction__time_to_leave_alone',
326
+			WEEK_IN_SECONDS
327
+		);
328
+		$query = $wpdb->prepare(
329
+			'DELETE li
330 330
 				FROM ' . $this->table() . ' li
331 331
 				LEFT JOIN ' . EEM_Transaction::instance()->table() . ' t ON li.TXN_ID = t.TXN_ID
332 332
 				WHERE t.TXN_ID IS NULL AND li.LIN_timestamp < %s',
333
-            // use GMT time because that's what TXN_timestamps are in
334
-            date('Y-m-d H:i:s', time() - $time_to_leave_alone)
335
-        );
336
-        return $wpdb->query($query);
337
-    }
338
-
339
-
340
-    /**
341
-     * get_line_item_for_transaction_object
342
-     * Gets a transaction's line item record for a specific object such as a EE_Event or EE_Ticket
343
-     *
344
-     * @param int           $TXN_ID
345
-     * @param EE_Base_Class $object
346
-     * @return EE_Base_Class[]|EE_Line_Item[]
347
-     * @throws EE_Error
348
-     * @throws InvalidArgumentException
349
-     * @throws InvalidDataTypeException
350
-     * @throws InvalidInterfaceException
351
-     * @throws ReflectionException
352
-     */
353
-    public function get_line_item_for_transaction_object($TXN_ID, EE_Base_Class $object)
354
-    {
355
-        return $this->get_all(array(
356
-            array(
357
-                'TXN_ID'   => $TXN_ID,
358
-                'OBJ_type' => str_replace('EE_', '', get_class($object)),
359
-                'OBJ_ID'   => $object->ID(),
360
-            ),
361
-        ));
362
-    }
363
-
364
-
365
-    /**
366
-     * get_object_line_items_for_transaction
367
-     * Gets all of the the object line items for a transaction, based on an object type plus an array of object IDs
368
-     *
369
-     * @param int    $TXN_ID
370
-     * @param string $OBJ_type
371
-     * @param array  $OBJ_IDs
372
-     * @return EE_Base_Class[]|EE_Line_Item[]
373
-     * @throws EE_Error
374
-     */
375
-    public function get_object_line_items_for_transaction(
376
-        $TXN_ID,
377
-        $OBJ_type = EEM_Line_Item::OBJ_TYPE_EVENT,
378
-        $OBJ_IDs = array()
379
-    ) {
380
-        $query_params = array(
381
-            'OBJ_type' => $OBJ_type,
382
-            // if incoming $OBJ_IDs is an array, then make sure it is formatted correctly for the query
383
-            'OBJ_ID'   => is_array($OBJ_IDs) && ! isset($OBJ_IDs['IN']) ? array('IN', $OBJ_IDs) : $OBJ_IDs,
384
-        );
385
-        if ($TXN_ID) {
386
-            $query_params['TXN_ID'] = $TXN_ID;
387
-        }
388
-        return $this->get_all(array($query_params));
389
-    }
390
-
391
-
392
-    /**
393
-     * get_all_ticket_line_items_for_transaction
394
-     *
395
-     * @param EE_Transaction $transaction
396
-     * @return EE_Base_Class[]|EE_Line_Item[]
397
-     * @throws EE_Error
398
-     * @throws InvalidArgumentException
399
-     * @throws InvalidDataTypeException
400
-     * @throws InvalidInterfaceException
401
-     * @throws ReflectionException
402
-     */
403
-    public function get_all_ticket_line_items_for_transaction(EE_Transaction $transaction)
404
-    {
405
-        return $this->get_all(array(
406
-            array(
407
-                'TXN_ID'   => $transaction->ID(),
408
-                'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TICKET,
409
-            ),
410
-        ));
411
-    }
412
-
413
-
414
-    /**
415
-     * get_ticket_line_item_for_transaction
416
-     *
417
-     * @param int $TXN_ID
418
-     * @param int $TKT_ID
419
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
420
-     * @throws EE_Error
421
-     * @throws InvalidArgumentException
422
-     * @throws InvalidDataTypeException
423
-     * @throws InvalidInterfaceException
424
-     */
425
-    public function get_ticket_line_item_for_transaction($TXN_ID, $TKT_ID)
426
-    {
427
-        return $this->get_one(array(
428
-            array(
429
-                'TXN_ID'   => EEM_Transaction::instance()->ensure_is_ID($TXN_ID),
430
-                'OBJ_ID'   => $TKT_ID,
431
-                'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TICKET,
432
-            ),
433
-        ));
434
-    }
435
-
436
-
437
-    /**
438
-     * get_existing_promotion_line_item
439
-     * searches the cart for existing line items for the specified promotion
440
-     *
441
-     * @since 1.0.0
442
-     * @param EE_Line_Item $parent_line_item
443
-     * @param EE_Promotion $promotion
444
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
445
-     * @throws EE_Error
446
-     * @throws InvalidArgumentException
447
-     * @throws InvalidDataTypeException
448
-     * @throws InvalidInterfaceException
449
-     * @throws ReflectionException
450
-     */
451
-    public function get_existing_promotion_line_item(EE_Line_Item $parent_line_item, EE_Promotion $promotion)
452
-    {
453
-        return $this->get_one(array(
454
-            array(
455
-                'TXN_ID'     => $parent_line_item->TXN_ID(),
456
-                'LIN_parent' => $parent_line_item->ID(),
457
-                'OBJ_type'   => EEM_Line_Item::OBJ_TYPE_PROMOTION,
458
-                'OBJ_ID'     => $promotion->ID(),
459
-            ),
460
-        ));
461
-    }
462
-
463
-
464
-    /**
465
-     * get_all_promotion_line_items
466
-     * searches the cart for any and all existing promotion line items
467
-     *
468
-     * @since   1.0.0
469
-     * @param EE_Line_Item $parent_line_item
470
-     * @return EE_Base_Class[]|EE_Line_Item[]
471
-     * @throws EE_Error
472
-     * @throws InvalidArgumentException
473
-     * @throws InvalidDataTypeException
474
-     * @throws InvalidInterfaceException
475
-     * @throws ReflectionException
476
-     */
477
-    public function get_all_promotion_line_items(EE_Line_Item $parent_line_item)
478
-    {
479
-        return $this->get_all(array(
480
-            array(
481
-                'TXN_ID'     => $parent_line_item->TXN_ID(),
482
-                'LIN_parent' => $parent_line_item->ID(),
483
-                'OBJ_type'   => EEM_Line_Item::OBJ_TYPE_PROMOTION,
484
-            ),
485
-        ));
486
-    }
487
-
488
-
489
-    /**
490
-     * Gets the registration's corresponding line item.
491
-     * Note: basically does NOT support having multiple line items for a single ticket,
492
-     * which would happen if some of the registrations had a price modifier while others didn't.
493
-     * In order to support that, we'd probably need a LIN_ID on registrations or something.
494
-     *
495
-     * @param EE_Registration $registration
496
-     * @return EE_Base_Class|EE_Line_ITem|EE_Soft_Delete_Base_Class|NULL
497
-     * @throws EE_Error
498
-     */
499
-    public function get_line_item_for_registration(EE_Registration $registration)
500
-    {
501
-        return $this->get_one($this->line_item_for_registration_query_params($registration));
502
-    }
503
-
504
-
505
-    /**
506
-     * Gets the query params used to retrieve a specific line item for the given registration
507
-     *
508
-     * @param EE_Registration $registration
509
-     * @param array           $original_query_params any extra query params you'd like to be merged with
510
-     * @return array @see
511
-     *      https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
512
-     * @throws EE_Error
513
-     */
514
-    public function line_item_for_registration_query_params(
515
-        EE_Registration $registration,
516
-        $original_query_params = array()
517
-    ) {
518
-        return array_replace_recursive($original_query_params, array(
519
-            array(
520
-                'OBJ_ID'   => $registration->ticket_ID(),
521
-                'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TICKET,
522
-                'TXN_ID'   => $registration->transaction_ID(),
523
-            ),
524
-        ));
525
-    }
526
-
527
-
528
-    /**
529
-     * @return EE_Base_Class[]|EE_Line_Item[]
530
-     * @throws InvalidInterfaceException
531
-     * @throws InvalidDataTypeException
532
-     * @throws EE_Error
533
-     * @throws InvalidArgumentException
534
-     */
535
-    public function get_total_line_items_with_no_transaction()
536
-    {
537
-        return $this->get_total_line_items_for_carts();
538
-    }
539
-
540
-
541
-    /**
542
-     * @return EE_Base_Class[]|EE_Line_Item[]
543
-     * @throws InvalidInterfaceException
544
-     * @throws InvalidDataTypeException
545
-     * @throws EE_Error
546
-     * @throws InvalidArgumentException
547
-     */
548
-    public function get_total_line_items_for_active_carts()
549
-    {
550
-        return $this->get_total_line_items_for_carts(false);
551
-    }
552
-
553
-
554
-    /**
555
-     * @return EE_Base_Class[]|EE_Line_Item[]
556
-     * @throws InvalidInterfaceException
557
-     * @throws InvalidDataTypeException
558
-     * @throws EE_Error
559
-     * @throws InvalidArgumentException
560
-     */
561
-    public function get_total_line_items_for_expired_carts()
562
-    {
563
-        return $this->get_total_line_items_for_carts(true);
564
-    }
565
-
566
-
567
-    /**
568
-     * Returns an array of grand total line items where the TXN_ID is 0.
569
-     * If $expired is set to true, then only line items for expired sessions will be returned.
570
-     * If $expired is set to false, then only line items for active sessions will be returned.
571
-     *
572
-     * @param null $expired
573
-     * @return EE_Base_Class[]|EE_Line_Item[]
574
-     * @throws EE_Error
575
-     * @throws InvalidArgumentException
576
-     * @throws InvalidDataTypeException
577
-     * @throws InvalidInterfaceException
578
-     */
579
-    private function get_total_line_items_for_carts($expired = null)
580
-    {
581
-        $where_params = array(
582
-            'TXN_ID'   => 0,
583
-            'LIN_type' => 'total',
584
-        );
585
-        if ($expired !== null) {
586
-            /** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
587
-            $session_lifespan = LoaderFactory::getLoader()->getShared(
588
-                'EventEspresso\core\domain\values\session\SessionLifespan'
589
-            );
590
-            $where_params['LIN_timestamp'] = array(
591
-                $expired ? '<=' : '>',
592
-                $session_lifespan->expiration(),
593
-            );
594
-        }
595
-        return $this->get_all(array($where_params));
596
-    }
597
-
598
-
599
-    /**
600
-     * Returns an array of ticket total line items where the TXN_ID is 0
601
-     * AND the timestamp is older than the session lifespan.
602
-     *
603
-     * @param int $timestamp
604
-     * @return EE_Base_Class[]|EE_Line_Item[]
605
-     * @throws EE_Error
606
-     * @throws InvalidArgumentException
607
-     * @throws InvalidDataTypeException
608
-     * @throws InvalidInterfaceException
609
-     */
610
-    public function getTicketLineItemsForExpiredCarts($timestamp = 0)
611
-    {
612
-        if (! absint($timestamp)) {
613
-            /** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
614
-            $session_lifespan = LoaderFactory::getLoader()->getShared(
615
-                'EventEspresso\core\domain\values\session\SessionLifespan'
616
-            );
617
-            $timestamp = $session_lifespan->expiration();
618
-        }
619
-        return $this->get_all(
620
-            array(
621
-                array(
622
-                    'TXN_ID'        => 0,
623
-                    'OBJ_type'      => EEM_Line_Item::OBJ_TYPE_TICKET,
624
-                    'LIN_timestamp' => array('<=', $timestamp),
625
-                ),
626
-            )
627
-        );
628
-    }
333
+			// use GMT time because that's what TXN_timestamps are in
334
+			date('Y-m-d H:i:s', time() - $time_to_leave_alone)
335
+		);
336
+		return $wpdb->query($query);
337
+	}
338
+
339
+
340
+	/**
341
+	 * get_line_item_for_transaction_object
342
+	 * Gets a transaction's line item record for a specific object such as a EE_Event or EE_Ticket
343
+	 *
344
+	 * @param int           $TXN_ID
345
+	 * @param EE_Base_Class $object
346
+	 * @return EE_Base_Class[]|EE_Line_Item[]
347
+	 * @throws EE_Error
348
+	 * @throws InvalidArgumentException
349
+	 * @throws InvalidDataTypeException
350
+	 * @throws InvalidInterfaceException
351
+	 * @throws ReflectionException
352
+	 */
353
+	public function get_line_item_for_transaction_object($TXN_ID, EE_Base_Class $object)
354
+	{
355
+		return $this->get_all(array(
356
+			array(
357
+				'TXN_ID'   => $TXN_ID,
358
+				'OBJ_type' => str_replace('EE_', '', get_class($object)),
359
+				'OBJ_ID'   => $object->ID(),
360
+			),
361
+		));
362
+	}
363
+
364
+
365
+	/**
366
+	 * get_object_line_items_for_transaction
367
+	 * Gets all of the the object line items for a transaction, based on an object type plus an array of object IDs
368
+	 *
369
+	 * @param int    $TXN_ID
370
+	 * @param string $OBJ_type
371
+	 * @param array  $OBJ_IDs
372
+	 * @return EE_Base_Class[]|EE_Line_Item[]
373
+	 * @throws EE_Error
374
+	 */
375
+	public function get_object_line_items_for_transaction(
376
+		$TXN_ID,
377
+		$OBJ_type = EEM_Line_Item::OBJ_TYPE_EVENT,
378
+		$OBJ_IDs = array()
379
+	) {
380
+		$query_params = array(
381
+			'OBJ_type' => $OBJ_type,
382
+			// if incoming $OBJ_IDs is an array, then make sure it is formatted correctly for the query
383
+			'OBJ_ID'   => is_array($OBJ_IDs) && ! isset($OBJ_IDs['IN']) ? array('IN', $OBJ_IDs) : $OBJ_IDs,
384
+		);
385
+		if ($TXN_ID) {
386
+			$query_params['TXN_ID'] = $TXN_ID;
387
+		}
388
+		return $this->get_all(array($query_params));
389
+	}
390
+
391
+
392
+	/**
393
+	 * get_all_ticket_line_items_for_transaction
394
+	 *
395
+	 * @param EE_Transaction $transaction
396
+	 * @return EE_Base_Class[]|EE_Line_Item[]
397
+	 * @throws EE_Error
398
+	 * @throws InvalidArgumentException
399
+	 * @throws InvalidDataTypeException
400
+	 * @throws InvalidInterfaceException
401
+	 * @throws ReflectionException
402
+	 */
403
+	public function get_all_ticket_line_items_for_transaction(EE_Transaction $transaction)
404
+	{
405
+		return $this->get_all(array(
406
+			array(
407
+				'TXN_ID'   => $transaction->ID(),
408
+				'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TICKET,
409
+			),
410
+		));
411
+	}
412
+
413
+
414
+	/**
415
+	 * get_ticket_line_item_for_transaction
416
+	 *
417
+	 * @param int $TXN_ID
418
+	 * @param int $TKT_ID
419
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
420
+	 * @throws EE_Error
421
+	 * @throws InvalidArgumentException
422
+	 * @throws InvalidDataTypeException
423
+	 * @throws InvalidInterfaceException
424
+	 */
425
+	public function get_ticket_line_item_for_transaction($TXN_ID, $TKT_ID)
426
+	{
427
+		return $this->get_one(array(
428
+			array(
429
+				'TXN_ID'   => EEM_Transaction::instance()->ensure_is_ID($TXN_ID),
430
+				'OBJ_ID'   => $TKT_ID,
431
+				'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TICKET,
432
+			),
433
+		));
434
+	}
435
+
436
+
437
+	/**
438
+	 * get_existing_promotion_line_item
439
+	 * searches the cart for existing line items for the specified promotion
440
+	 *
441
+	 * @since 1.0.0
442
+	 * @param EE_Line_Item $parent_line_item
443
+	 * @param EE_Promotion $promotion
444
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
445
+	 * @throws EE_Error
446
+	 * @throws InvalidArgumentException
447
+	 * @throws InvalidDataTypeException
448
+	 * @throws InvalidInterfaceException
449
+	 * @throws ReflectionException
450
+	 */
451
+	public function get_existing_promotion_line_item(EE_Line_Item $parent_line_item, EE_Promotion $promotion)
452
+	{
453
+		return $this->get_one(array(
454
+			array(
455
+				'TXN_ID'     => $parent_line_item->TXN_ID(),
456
+				'LIN_parent' => $parent_line_item->ID(),
457
+				'OBJ_type'   => EEM_Line_Item::OBJ_TYPE_PROMOTION,
458
+				'OBJ_ID'     => $promotion->ID(),
459
+			),
460
+		));
461
+	}
462
+
463
+
464
+	/**
465
+	 * get_all_promotion_line_items
466
+	 * searches the cart for any and all existing promotion line items
467
+	 *
468
+	 * @since   1.0.0
469
+	 * @param EE_Line_Item $parent_line_item
470
+	 * @return EE_Base_Class[]|EE_Line_Item[]
471
+	 * @throws EE_Error
472
+	 * @throws InvalidArgumentException
473
+	 * @throws InvalidDataTypeException
474
+	 * @throws InvalidInterfaceException
475
+	 * @throws ReflectionException
476
+	 */
477
+	public function get_all_promotion_line_items(EE_Line_Item $parent_line_item)
478
+	{
479
+		return $this->get_all(array(
480
+			array(
481
+				'TXN_ID'     => $parent_line_item->TXN_ID(),
482
+				'LIN_parent' => $parent_line_item->ID(),
483
+				'OBJ_type'   => EEM_Line_Item::OBJ_TYPE_PROMOTION,
484
+			),
485
+		));
486
+	}
487
+
488
+
489
+	/**
490
+	 * Gets the registration's corresponding line item.
491
+	 * Note: basically does NOT support having multiple line items for a single ticket,
492
+	 * which would happen if some of the registrations had a price modifier while others didn't.
493
+	 * In order to support that, we'd probably need a LIN_ID on registrations or something.
494
+	 *
495
+	 * @param EE_Registration $registration
496
+	 * @return EE_Base_Class|EE_Line_ITem|EE_Soft_Delete_Base_Class|NULL
497
+	 * @throws EE_Error
498
+	 */
499
+	public function get_line_item_for_registration(EE_Registration $registration)
500
+	{
501
+		return $this->get_one($this->line_item_for_registration_query_params($registration));
502
+	}
503
+
504
+
505
+	/**
506
+	 * Gets the query params used to retrieve a specific line item for the given registration
507
+	 *
508
+	 * @param EE_Registration $registration
509
+	 * @param array           $original_query_params any extra query params you'd like to be merged with
510
+	 * @return array @see
511
+	 *      https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
512
+	 * @throws EE_Error
513
+	 */
514
+	public function line_item_for_registration_query_params(
515
+		EE_Registration $registration,
516
+		$original_query_params = array()
517
+	) {
518
+		return array_replace_recursive($original_query_params, array(
519
+			array(
520
+				'OBJ_ID'   => $registration->ticket_ID(),
521
+				'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TICKET,
522
+				'TXN_ID'   => $registration->transaction_ID(),
523
+			),
524
+		));
525
+	}
526
+
527
+
528
+	/**
529
+	 * @return EE_Base_Class[]|EE_Line_Item[]
530
+	 * @throws InvalidInterfaceException
531
+	 * @throws InvalidDataTypeException
532
+	 * @throws EE_Error
533
+	 * @throws InvalidArgumentException
534
+	 */
535
+	public function get_total_line_items_with_no_transaction()
536
+	{
537
+		return $this->get_total_line_items_for_carts();
538
+	}
539
+
540
+
541
+	/**
542
+	 * @return EE_Base_Class[]|EE_Line_Item[]
543
+	 * @throws InvalidInterfaceException
544
+	 * @throws InvalidDataTypeException
545
+	 * @throws EE_Error
546
+	 * @throws InvalidArgumentException
547
+	 */
548
+	public function get_total_line_items_for_active_carts()
549
+	{
550
+		return $this->get_total_line_items_for_carts(false);
551
+	}
552
+
553
+
554
+	/**
555
+	 * @return EE_Base_Class[]|EE_Line_Item[]
556
+	 * @throws InvalidInterfaceException
557
+	 * @throws InvalidDataTypeException
558
+	 * @throws EE_Error
559
+	 * @throws InvalidArgumentException
560
+	 */
561
+	public function get_total_line_items_for_expired_carts()
562
+	{
563
+		return $this->get_total_line_items_for_carts(true);
564
+	}
565
+
566
+
567
+	/**
568
+	 * Returns an array of grand total line items where the TXN_ID is 0.
569
+	 * If $expired is set to true, then only line items for expired sessions will be returned.
570
+	 * If $expired is set to false, then only line items for active sessions will be returned.
571
+	 *
572
+	 * @param null $expired
573
+	 * @return EE_Base_Class[]|EE_Line_Item[]
574
+	 * @throws EE_Error
575
+	 * @throws InvalidArgumentException
576
+	 * @throws InvalidDataTypeException
577
+	 * @throws InvalidInterfaceException
578
+	 */
579
+	private function get_total_line_items_for_carts($expired = null)
580
+	{
581
+		$where_params = array(
582
+			'TXN_ID'   => 0,
583
+			'LIN_type' => 'total',
584
+		);
585
+		if ($expired !== null) {
586
+			/** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
587
+			$session_lifespan = LoaderFactory::getLoader()->getShared(
588
+				'EventEspresso\core\domain\values\session\SessionLifespan'
589
+			);
590
+			$where_params['LIN_timestamp'] = array(
591
+				$expired ? '<=' : '>',
592
+				$session_lifespan->expiration(),
593
+			);
594
+		}
595
+		return $this->get_all(array($where_params));
596
+	}
597
+
598
+
599
+	/**
600
+	 * Returns an array of ticket total line items where the TXN_ID is 0
601
+	 * AND the timestamp is older than the session lifespan.
602
+	 *
603
+	 * @param int $timestamp
604
+	 * @return EE_Base_Class[]|EE_Line_Item[]
605
+	 * @throws EE_Error
606
+	 * @throws InvalidArgumentException
607
+	 * @throws InvalidDataTypeException
608
+	 * @throws InvalidInterfaceException
609
+	 */
610
+	public function getTicketLineItemsForExpiredCarts($timestamp = 0)
611
+	{
612
+		if (! absint($timestamp)) {
613
+			/** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
614
+			$session_lifespan = LoaderFactory::getLoader()->getShared(
615
+				'EventEspresso\core\domain\values\session\SessionLifespan'
616
+			);
617
+			$timestamp = $session_lifespan->expiration();
618
+		}
619
+		return $this->get_all(
620
+			array(
621
+				array(
622
+					'TXN_ID'        => 0,
623
+					'OBJ_type'      => EEM_Line_Item::OBJ_TYPE_TICKET,
624
+					'LIN_timestamp' => array('<=', $timestamp),
625
+				),
626
+			)
627
+		);
628
+	}
629 629
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Registration.model.php 1 patch
Indentation   +867 added lines, -867 removed lines patch added patch discarded remove patch
@@ -13,816 +13,816 @@  discard block
 block discarded – undo
13 13
 class EEM_Registration extends EEM_Soft_Delete_Base
14 14
 {
15 15
 
16
-    /**
17
-     * @var EEM_Registration $_instance
18
-     */
19
-    protected static $_instance;
20
-
21
-    /**
22
-     * Keys are the status IDs for registrations (eg, RAP, RCN, etc), and the values
23
-     * are status codes (eg, approved, cancelled, etc)
24
-     *
25
-     * @var array
26
-     */
27
-    private static $_reg_status;
28
-
29
-    /**
30
-     * The value of REG_count for a primary registrant
31
-     */
32
-    const PRIMARY_REGISTRANT_COUNT = 1;
33
-
34
-    /**
35
-     * Status ID (STS_ID on esp_status table) to indicate an INCOMPLETE registration.
36
-     * Initial status for registrations when they are first created
37
-     * Payments are NOT allowed.
38
-     * Automatically toggled to whatever the default Event registration status is upon completion of the attendee
39
-     * information reg step NO space reserved. Registration is NOT active
40
-     */
41
-    const status_id_incomplete = 'RIC';
42
-
43
-    /**
44
-     * Status ID (STS_ID on esp_status table) to indicate an UNAPPROVED registration.
45
-     * Payments are NOT allowed.
46
-     * Event Admin must manually toggle STS_ID for it to change
47
-     * No space reserved.
48
-     * Registration is active
49
-     */
50
-    const status_id_not_approved = 'RNA';
51
-
52
-    /**
53
-     * Status ID (STS_ID on esp_status table) to indicate registration is PENDING_PAYMENT .
54
-     * Payments are allowed.
55
-     * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
56
-     * No space reserved.
57
-     * Registration is active
58
-     */
59
-    const status_id_pending_payment = 'RPP';
60
-
61
-    /**
62
-     * Status ID (STS_ID on esp_status table) to indicate registration is on the WAIT_LIST .
63
-     * Payments are allowed.
64
-     * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
65
-     * No space reserved.
66
-     * Registration is active
67
-     */
68
-    const status_id_wait_list = 'RWL';
69
-
70
-    /**
71
-     * Status ID (STS_ID on esp_status table) to indicate an APPROVED registration.
72
-     * the TXN may or may not be completed ( paid in full )
73
-     * Payments are allowed.
74
-     * A space IS reserved.
75
-     * Registration is active
76
-     */
77
-    const status_id_approved = 'RAP';
78
-
79
-    /**
80
-     * Status ID (STS_ID on esp_status table) to indicate a registration was CANCELLED by the attendee.
81
-     * Payments are NOT allowed.
82
-     * NO space reserved.
83
-     * Registration is NOT active
84
-     */
85
-    const status_id_cancelled = 'RCN';
86
-
87
-    /**
88
-     * Status ID (STS_ID on esp_status table) to indicate a registration was DECLINED by the Event Admin
89
-     * Payments are NOT allowed.
90
-     * No space reserved.
91
-     * Registration is NOT active
92
-     */
93
-    const status_id_declined = 'RDC';
94
-
95
-    /**
96
-     * @var TableAnalysis $table_analysis
97
-     */
98
-    protected $_table_analysis;
99
-
100
-
101
-    /**
102
-     *    private constructor to prevent direct creation
103
-     *
104
-     * @Constructor
105
-     * @access protected
106
-     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any
107
-     *                         incoming timezone data that gets saved). Note this just sends the timezone info to the
108
-     *                         date time model field objects.  Default is NULL (and will be assumed using the set
109
-     *                         timezone in the 'timezone_string' wp option)
110
-     * @throws EE_Error
111
-     */
112
-    protected function __construct($timezone = null)
113
-    {
114
-        $this->_table_analysis = EE_Registry::instance()->create('TableAnalysis', array(), true);
115
-        $this->singular_item = esc_html__('Registration', 'event_espresso');
116
-        $this->plural_item = esc_html__('Registrations', 'event_espresso');
117
-        $this->_tables = array(
118
-            'Registration' => new EE_Primary_Table('esp_registration', 'REG_ID'),
119
-        );
120
-        $this->_fields = array(
121
-            'Registration' => array(
122
-                'REG_ID' => new EE_Primary_Key_Int_Field(
123
-                    'REG_ID',
124
-                    esc_html__('Registration ID', 'event_espresso')
125
-                ),
126
-                'EVT_ID' => new EE_Foreign_Key_Int_Field(
127
-                    'EVT_ID',
128
-                    esc_html__('Event ID', 'event_espresso'),
129
-                    false,
130
-                    0,
131
-                    'Event'
132
-                ),
133
-                'ATT_ID' => new EE_Foreign_Key_Int_Field(
134
-                    'ATT_ID',
135
-                    esc_html__('Attendee ID', 'event_espresso'),
136
-                    false,
137
-                    0,
138
-                    'Attendee'
139
-                ),
140
-                'TXN_ID' => new EE_Foreign_Key_Int_Field(
141
-                    'TXN_ID',
142
-                    esc_html__('Transaction ID', 'event_espresso'),
143
-                    false,
144
-                    0,
145
-                    'Transaction'
146
-                ),
147
-                'TKT_ID' => new EE_Foreign_Key_Int_Field(
148
-                    'TKT_ID',
149
-                    esc_html__('Ticket ID', 'event_espresso'),
150
-                    false,
151
-                    0,
152
-                    'Ticket'
153
-                ),
154
-                'STS_ID' => new EE_Foreign_Key_String_Field(
155
-                    'STS_ID',
156
-                    esc_html__('Status ID', 'event_espresso'),
157
-                    false,
158
-                    EEM_Registration::status_id_incomplete,
159
-                    'Status'
160
-                ),
161
-                'REG_date' => new EE_Datetime_Field(
162
-                    'REG_date',
163
-                    esc_html__('Time registration occurred', 'event_espresso'),
164
-                    false,
165
-                    EE_Datetime_Field::now,
166
-                    $timezone
167
-                ),
168
-                'REG_final_price' => new EE_Money_Field(
169
-                    'REG_final_price',
170
-                    esc_html__('Registration\'s share of the transaction total', 'event_espresso'),
171
-                    false,
172
-                    0
173
-                ),
174
-                'REG_paid' => new EE_Money_Field(
175
-                    'REG_paid',
176
-                    esc_html__('Amount paid to date towards registration', 'event_espresso'),
177
-                    false,
178
-                    0
179
-                ),
180
-                'REG_session' => new EE_Plain_Text_Field(
181
-                    'REG_session',
182
-                    esc_html__('Session ID of registration', 'event_espresso'),
183
-                    false,
184
-                    ''
185
-                ),
186
-                'REG_code' => new EE_Plain_Text_Field(
187
-                    'REG_code',
188
-                    esc_html__('Unique Code for this registration', 'event_espresso'),
189
-                    false,
190
-                    ''
191
-                ),
192
-                'REG_url_link' => new EE_Plain_Text_Field(
193
-                    'REG_url_link',
194
-                    esc_html__('String to be used in URL for identifying registration', 'event_espresso'),
195
-                    false,
196
-                    ''
197
-                ),
198
-                'REG_count' => new EE_Integer_Field(
199
-                    'REG_count',
200
-                    esc_html__('Count of this registration in the group registration ', 'event_espresso'),
201
-                    true,
202
-                    1
203
-                ),
204
-                'REG_group_size' => new EE_Integer_Field(
205
-                    'REG_group_size',
206
-                    esc_html__('Number of registrations on this group', 'event_espresso'),
207
-                    false,
208
-                    1
209
-                ),
210
-                'REG_att_is_going' => new EE_Boolean_Field(
211
-                    'REG_att_is_going',
212
-                    esc_html__('Flag indicating the registrant plans on attending', 'event_espresso'),
213
-                    false,
214
-                    false
215
-                ),
216
-                'REG_deleted' => new EE_Trashed_Flag_Field(
217
-                    'REG_deleted',
218
-                    esc_html__('Flag indicating if registration has been archived or not.', 'event_espresso'),
219
-                    false,
220
-                    false
221
-                ),
222
-            ),
223
-        );
224
-        $this->_model_relations = array(
225
-            'Event' => new EE_Belongs_To_Relation(),
226
-            'Attendee' => new EE_Belongs_To_Relation(),
227
-            'Transaction' => new EE_Belongs_To_Relation(),
228
-            'Ticket' => new EE_Belongs_To_Relation(),
229
-            'Status' => new EE_Belongs_To_Relation(),
230
-            'Answer' => new EE_Has_Many_Relation(),
231
-            'Checkin' => new EE_Has_Many_Relation(),
232
-            'Registration_Payment' => new EE_Has_Many_Relation(),
233
-            'Payment' => new EE_HABTM_Relation('Registration_Payment'),
234
-            'Message' => new EE_Has_Many_Any_Relation(false)
235
-            // allow deletes even if there are messages in the queue related
236
-        );
237
-        $this->_model_chain_to_wp_user = 'Event';
238
-        parent::__construct($timezone);
239
-    }
240
-
241
-
242
-    /**
243
-     * a list of ALL valid registration statuses currently in use within the system
244
-     * generated by combining the filterable active and inactive reg status arrays
245
-     *
246
-     * @return array
247
-     */
248
-    public static function reg_statuses()
249
-    {
250
-        return array_unique(
251
-            array_merge(
252
-                EEM_Registration::active_reg_statuses(),
253
-                EEM_Registration::inactive_reg_statuses()
254
-            )
255
-        );
256
-    }
257
-
258
-
259
-    /**
260
-     * reg_statuses_that_allow_payment
261
-     * a filterable list of registration statuses that allow a registrant to make a payment
262
-     *
263
-     * @access public
264
-     * @return array
265
-     */
266
-    public static function reg_statuses_that_allow_payment()
267
-    {
268
-        return apply_filters(
269
-            'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
270
-            array(
271
-                EEM_Registration::status_id_approved,
272
-                EEM_Registration::status_id_pending_payment,
273
-            )
274
-        );
275
-    }
276
-
277
-
278
-    /**
279
-     * active_reg_statuses
280
-     * a filterable list of registration statuses that are considered active
281
-     *
282
-     * @access public
283
-     * @return array
284
-     */
285
-    public static function active_reg_statuses()
286
-    {
287
-        return apply_filters(
288
-            'FHEE__EEM_Registration__active_reg_statuses',
289
-            array(
290
-                EEM_Registration::status_id_approved,
291
-                EEM_Registration::status_id_pending_payment,
292
-                EEM_Registration::status_id_wait_list,
293
-                EEM_Registration::status_id_not_approved,
294
-            )
295
-        );
296
-    }
297
-
298
-
299
-    /**
300
-     * inactive_reg_statuses
301
-     * a filterable list of registration statuses that are not considered active
302
-     *
303
-     * @access public
304
-     * @return array
305
-     */
306
-    public static function inactive_reg_statuses()
307
-    {
308
-        return apply_filters(
309
-            'FHEE__EEM_Registration__inactive_reg_statuses',
310
-            array(
311
-                EEM_Registration::status_id_incomplete,
312
-                EEM_Registration::status_id_cancelled,
313
-                EEM_Registration::status_id_declined,
314
-            )
315
-        );
316
-    }
317
-
318
-
319
-    /**
320
-     *    closed_reg_statuses
321
-     *    a filterable list of registration statuses that are considered "closed"
322
-     * meaning they should not be considered in any calculations involving monies owing
323
-     *
324
-     * @access public
325
-     * @return array
326
-     */
327
-    public static function closed_reg_statuses()
328
-    {
329
-        return apply_filters(
330
-            'FHEE__EEM_Registration__closed_reg_statuses',
331
-            array(
332
-                EEM_Registration::status_id_cancelled,
333
-                EEM_Registration::status_id_declined,
334
-                EEM_Registration::status_id_wait_list,
335
-            )
336
-        );
337
-    }
338
-
339
-
340
-    /**
341
-     *        get list of registration statuses
342
-     *
343
-     * @access public
344
-     * @param array $exclude The status ids to exclude from the returned results
345
-     * @param bool $translated If true will return the values as singular localized strings
346
-     * @return array
347
-     * @throws EE_Error
348
-     */
349
-    public static function reg_status_array($exclude = array(), $translated = false)
350
-    {
351
-        EEM_Registration::instance()->_get_registration_status_array($exclude);
352
-        return $translated
353
-            ? EEM_Status::instance()->localized_status(self::$_reg_status, false, 'sentence')
354
-            : self::$_reg_status;
355
-    }
356
-
357
-
358
-    /**
359
-     *    get list of registration statuses
360
-     *
361
-     * @access private
362
-     * @param array $exclude
363
-     * @return void
364
-     * @throws EE_Error
365
-     */
366
-    private function _get_registration_status_array($exclude = array())
367
-    {
368
-        // in the very rare circumstance that we are deleting a model's table's data
369
-        // and the table hasn't actually been created, this could have an error
370
-        /** @type WPDB $wpdb */
371
-        global $wpdb;
372
-        if ($this->_get_table_analysis()->tableExists($wpdb->prefix . 'esp_status')) {
373
-            $results = $wpdb->get_results(
374
-                "SELECT STS_ID, STS_code FROM {$wpdb->prefix}esp_status WHERE STS_type = 'registration'"
375
-            );
376
-            self::$_reg_status = array();
377
-            foreach ($results as $status) {
378
-                if (!in_array($status->STS_ID, $exclude, true)) {
379
-                    self::$_reg_status[ $status->STS_ID ] = $status->STS_code;
380
-                }
381
-            }
382
-        }
383
-    }
384
-
385
-
386
-    /**
387
-     * Gets the injected table analyzer, or throws an exception
388
-     *
389
-     * @return TableAnalysis
390
-     * @throws EE_Error
391
-     */
392
-    protected function _get_table_analysis()
393
-    {
394
-        if ($this->_table_analysis instanceof TableAnalysis) {
395
-            return $this->_table_analysis;
396
-        }
397
-        throw new EE_Error(
398
-            sprintf(
399
-                esc_html__('Table analysis class on class %1$s is not set properly.', 'event_espresso'),
400
-                get_class($this)
401
-            )
402
-        );
403
-    }
404
-
405
-
406
-    /**
407
-     * This returns a wpdb->results array of all registration date month and years matching the incoming query params
408
-     * and grouped by month and year.
409
-     *
410
-     * @param  array $where_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
411
-     * @return array
412
-     * @throws EE_Error
413
-     */
414
-    public function get_reg_months_and_years($where_params)
415
-    {
416
-        $query_params[0] = $where_params;
417
-        $query_params['group_by'] = array('reg_year', 'reg_month');
418
-        $query_params['order_by'] = array('REG_date' => 'DESC');
419
-        $columns_to_select = array(
420
-            'reg_year' => array('YEAR(REG_date)', '%s'),
421
-            'reg_month' => array('MONTHNAME(REG_date)', '%s'),
422
-        );
423
-        return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
424
-    }
425
-
426
-
427
-    /**
428
-     * retrieve ALL registrations for a particular Attendee from db
429
-     *
430
-     * @param int $ATT_ID
431
-     * @return EE_Base_Class[]|EE_Registration[]|null
432
-     * @throws EE_Error
433
-     */
434
-    public function get_all_registrations_for_attendee($ATT_ID = 0)
435
-    {
436
-        if (!$ATT_ID) {
437
-            return null;
438
-        }
439
-        return $this->get_all(array(array('ATT_ID' => $ATT_ID)));
440
-    }
441
-
442
-
443
-    /**
444
-     * Gets a registration given their REG_url_link. Yes, this should usually
445
-     * be passed via a GET parameter.
446
-     *
447
-     * @param string $REG_url_link
448
-     * @return EE_Base_Class|EE_Registration|null
449
-     * @throws EE_Error
450
-     */
451
-    public function get_registration_for_reg_url_link($REG_url_link)
452
-    {
453
-        if (!$REG_url_link) {
454
-            return null;
455
-        }
456
-        return $this->get_one(array(array('REG_url_link' => $REG_url_link)));
457
-    }
458
-
459
-
460
-    /**
461
-     *        retrieve registration for a specific transaction attendee from db
462
-     *
463
-     * @access        public
464
-     * @param    int $TXN_ID
465
-     * @param    int $ATT_ID
466
-     * @param    int $att_nmbr in case the ATT_ID is the same for multiple registrations (same details used) then the
467
-     *                         attendee number is required
468
-     * @return        mixed        array on success, FALSE on fail
469
-     * @throws EE_Error
470
-     */
471
-    public function get_registration_for_transaction_attendee($TXN_ID = 0, $ATT_ID = 0, $att_nmbr = 0)
472
-    {
473
-        return $this->get_one(array(
474
-            array(
475
-                'TXN_ID' => $TXN_ID,
476
-                'ATT_ID' => $ATT_ID,
477
-            ),
478
-            'limit' => array(min($att_nmbr - 1, 0), 1),
479
-        ));
480
-    }
481
-
482
-
483
-    /**
484
-     *        get the number of registrations per day  for the Registration Admin page Reports Tab.
485
-     *        (doesn't utilize models because it's a fairly specialized query)
486
-     *
487
-     * @access        public
488
-     * @param $period string which can be passed to php's strtotime function (eg "-1 month")
489
-     * @return stdClass[] with properties regDate and total
490
-     * @throws EE_Error
491
-     */
492
-    public function get_registrations_per_day_report($period = '-1 month')
493
-    {
494
-        $sql_date = $this->convert_datetime_for_query(
495
-            'REG_date',
496
-            date('Y-m-d H:i:s', strtotime($period)),
497
-            'Y-m-d H:i:s',
498
-            'UTC'
499
-        );
500
-        $where = array(
501
-            'REG_date' => array('>=', $sql_date),
502
-            'STS_ID' => array('!=', EEM_Registration::status_id_incomplete),
503
-        );
504
-        if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_day_report')) {
505
-            $where['Event.EVT_wp_user'] = get_current_user_id();
506
-        }
507
-        $query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'REG_date');
508
-        $results = $this->_get_all_wpdb_results(
509
-            array(
510
-                $where,
511
-                'group_by' => 'regDate',
512
-                'order_by' => array('REG_date' => 'ASC'),
513
-            ),
514
-            OBJECT,
515
-            array(
516
-                'regDate' => array('DATE(' . $query_interval . ')', '%s'),
517
-                'total' => array('count(REG_ID)', '%d'),
518
-            )
519
-        );
520
-        return $results;
521
-    }
522
-
523
-
524
-    /**
525
-     * Get the number of registrations per day including the count of registrations for each Registration Status.
526
-     * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
527
-     *
528
-     * @param string $period
529
-     * @return stdClass[] with properties Registration_REG_date and a column for each registration status as the STS_ID
530
-     * @throws EE_Error
531
-     *                    (i.e. RAP)
532
-     */
533
-    public function get_registrations_per_day_and_per_status_report($period = '-1 month')
534
-    {
535
-        global $wpdb;
536
-        $registration_table = $wpdb->prefix . 'esp_registration';
537
-        $event_table = $wpdb->posts;
538
-        $sql_date = date('Y-m-d H:i:s', strtotime($period));
539
-        // prepare the query interval for displaying offset
540
-        $query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'dates.REG_date');
541
-        // inner date query
542
-        $inner_date_query = "SELECT DISTINCT REG_date from {$registration_table} ";
543
-        $inner_where = ' WHERE';
544
-        // exclude events not authored by user if permissions in effect
545
-        if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
546
-            $inner_date_query .= "LEFT JOIN {$event_table} ON ID = EVT_ID";
547
-            $inner_where .= ' post_author = ' . get_current_user_id() . ' AND';
548
-        }
549
-        $inner_where .= " REG_date >= '{$sql_date}'";
550
-        $inner_date_query .= $inner_where;
551
-        // start main query
552
-        $select = "SELECT DATE({$query_interval}) as Registration_REG_date, ";
553
-        $join = '';
554
-        $join_parts = array();
555
-        $select_parts = array();
556
-        // loop through registration stati to do parts for each status.
557
-        foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
558
-            if ($STS_ID === EEM_Registration::status_id_incomplete) {
559
-                continue;
560
-            }
561
-            $select_parts[] = "COUNT({$STS_code}.REG_ID) as {$STS_ID}";
562
-            $join_parts[] = "{$registration_table} AS {$STS_code} ON {$STS_code}.REG_date = dates.REG_date AND {$STS_code}.STS_ID = '{$STS_ID}'";
563
-        }
564
-        // setup the selects
565
-        $select .= implode(', ', $select_parts);
566
-        $select .= " FROM ($inner_date_query) AS dates LEFT JOIN ";
567
-        // setup the joins
568
-        $join .= implode(' LEFT JOIN ', $join_parts);
569
-        // now let's put it all together
570
-        $query = $select . $join . ' GROUP BY Registration_REG_date';
571
-        // and execute it
572
-        return $wpdb->get_results($query, ARRAY_A);
573
-    }
574
-
575
-
576
-    /**
577
-     *        get the number of registrations per event  for the Registration Admin page Reports Tab
578
-     *
579
-     * @access        public
580
-     * @param $period string which can be passed to php's strtotime function (eg "-1 month")
581
-     * @return stdClass[] each with properties event_name, reg_limit, and total
582
-     * @throws EE_Error
583
-     */
584
-    public function get_registrations_per_event_report($period = '-1 month')
585
-    {
586
-        $date_sql = $this->convert_datetime_for_query(
587
-            'REG_date',
588
-            date('Y-m-d H:i:s', strtotime($period)),
589
-            'Y-m-d H:i:s',
590
-            'UTC'
591
-        );
592
-        $where = array(
593
-            'REG_date' => array('>=', $date_sql),
594
-            'STS_ID' => array('!=', EEM_Registration::status_id_incomplete),
595
-        );
596
-        if (!EE_Registry::instance()->CAP->current_user_can(
597
-            'ee_read_others_registrations',
598
-            'reg_per_event_report'
599
-        )
600
-        ) {
601
-            $where['Event.EVT_wp_user'] = get_current_user_id();
602
-        }
603
-        $results = $this->_get_all_wpdb_results(
604
-            array(
605
-            $where,
606
-            'group_by' => 'Event.EVT_name',
607
-            'order_by' => 'Event.EVT_name',
608
-            'limit' => array(0, 24),
609
-            ),
610
-            OBJECT,
611
-            array(
612
-                'event_name' => array('Event_CPT.post_title', '%s'),
613
-                'total' => array('COUNT(REG_ID)', '%s'),
614
-            )
615
-        );
616
-        return $results;
617
-    }
618
-
619
-
620
-    /**
621
-     * Get the number of registrations per event grouped by registration status.
622
-     * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
623
-     *
624
-     * @param string $period
625
-     * @return stdClass[] with properties `Registration_Event` and a column for each registration status as the STS_ID
626
-     * @throws EE_Error
627
-     *                    (i.e. RAP)
628
-     */
629
-    public function get_registrations_per_event_and_per_status_report($period = '-1 month')
630
-    {
631
-        global $wpdb;
632
-        $registration_table = $wpdb->prefix . 'esp_registration';
633
-        $event_table = $wpdb->posts;
634
-        $sql_date = date('Y-m-d H:i:s', strtotime($period));
635
-        // inner date query
636
-        $inner_date_query = "SELECT DISTINCT EVT_ID, REG_date from $registration_table ";
637
-        $inner_where = ' WHERE';
638
-        // exclude events not authored by user if permissions in effect
639
-        if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
640
-            $inner_date_query .= "LEFT JOIN {$event_table} ON ID = EVT_ID";
641
-            $inner_where .= ' post_author = ' . get_current_user_id() . ' AND';
642
-        }
643
-        $inner_where .= " REG_date >= '{$sql_date}'";
644
-        $inner_date_query .= $inner_where;
645
-        // build main query
646
-        $select = 'SELECT Event.post_title as Registration_Event, ';
647
-        $join = '';
648
-        $join_parts = array();
649
-        $select_parts = array();
650
-        // loop through registration stati to do parts for each status.
651
-        foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
652
-            if ($STS_ID === EEM_Registration::status_id_incomplete) {
653
-                continue;
654
-            }
655
-            $select_parts[] = "COUNT({$STS_code}.REG_ID) as {$STS_ID}";
656
-            $join_parts[] = "{$registration_table} AS {$STS_code} ON {$STS_code}.EVT_ID = dates.EVT_ID AND {$STS_code}.STS_ID = '{$STS_ID}' AND {$STS_code}.REG_date = dates.REG_date";
657
-        }
658
-        // setup the selects
659
-        $select .= implode(', ', $select_parts);
660
-        $select .= " FROM ($inner_date_query) AS dates LEFT JOIN $event_table as Event ON Event.ID = dates.EVT_ID LEFT JOIN ";
661
-        // setup remaining joins
662
-        $join .= implode(' LEFT JOIN ', $join_parts);
663
-        // now put it all together
664
-        $query = $select . $join . ' GROUP BY Registration_Event';
665
-        // and execute
666
-        return $wpdb->get_results($query, ARRAY_A);
667
-    }
668
-
669
-
670
-    /**
671
-     * Returns the EE_Registration of the primary attendee on the transaction id provided
672
-     *
673
-     * @param int $TXN_ID
674
-     * @return EE_Base_Class|EE_Registration|null
675
-     * @throws EE_Error
676
-     */
677
-    public function get_primary_registration_for_transaction_ID($TXN_ID = 0)
678
-    {
679
-        if (!$TXN_ID) {
680
-            return null;
681
-        }
682
-        return $this->get_one(array(
683
-            array(
684
-                'TXN_ID' => $TXN_ID,
685
-                'REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT,
686
-            ),
687
-        ));
688
-    }
689
-
690
-
691
-    /**
692
-     *        get_event_registration_count
693
-     *
694
-     * @access public
695
-     * @param int $EVT_ID
696
-     * @param boolean $for_incomplete_payments
697
-     * @return int
698
-     * @throws EE_Error
699
-     */
700
-    public function get_event_registration_count($EVT_ID, $for_incomplete_payments = false)
701
-    {
702
-        // we only count approved registrations towards registration limits
703
-        $query_params = array(array('EVT_ID' => $EVT_ID, 'STS_ID' => self::status_id_approved));
704
-        if ($for_incomplete_payments) {
705
-            $query_params[0]['Transaction.STS_ID'] = array('!=', EEM_Transaction::complete_status_code);
706
-        }
707
-        return $this->count($query_params);
708
-    }
709
-
710
-
711
-    /**
712
-     * Deletes all registrations with no transactions. Note that this needs to be very efficient
713
-     * and so it uses wpdb directly. Also, we can't put a limit on this because MySQL doesn't allow a limit on a delete
714
-     * when joining tables like this.
715
-     *
716
-     * @global WPDB $wpdb
717
-     * @return int number deleted
718
-     * @throws EE_Error
719
-     */
720
-    public function delete_registrations_with_no_transaction()
721
-    {
722
-        /** @type WPDB $wpdb */
723
-        global $wpdb;
724
-        return $wpdb->query(
725
-            'DELETE r FROM '
726
-            . $this->table()
727
-            . ' r LEFT JOIN '
728
-            . EEM_Transaction::instance()->table()
729
-            . ' t ON r.TXN_ID = t.TXN_ID WHERE t.TXN_ID IS NULL'
730
-        );
731
-    }
732
-
733
-
734
-    /**
735
-     *  Count registrations checked into (or out of) a datetime
736
-     *
737
-     * @param int $DTT_ID datetime ID
738
-     * @param boolean $checked_in whether to count registrations checked IN or OUT
739
-     * @return int
740
-     * @throws EE_Error
741
-     */
742
-    public function count_registrations_checked_into_datetime($DTT_ID, $checked_in = true)
743
-    {
744
-        global $wpdb;
745
-        // subquery to get latest checkin
746
-        $query = $wpdb->prepare(
747
-            'SELECT '
748
-            . 'COUNT( DISTINCT checkins.REG_ID ) '
749
-            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
750
-            . '( SELECT '
751
-            . 'max( CHK_timestamp ) AS latest_checkin, '
752
-            . 'REG_ID AS REG_ID '
753
-            . 'FROM ' . EEM_Checkin::instance()->table() . ' '
754
-            . 'WHERE DTT_ID=%d '
755
-            . 'GROUP BY REG_ID'
756
-            . ') AS most_recent_checkin_per_reg '
757
-            . 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
758
-            . 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
759
-            . 'WHERE '
760
-            . 'checkins.CHK_in=%d',
761
-            $DTT_ID,
762
-            $checked_in
763
-        );
764
-        return (int) $wpdb->get_var($query);
765
-    }
766
-
767
-
768
-    /**
769
-     *  Count registrations checked into (or out of) an event.
770
-     *
771
-     * @param int $EVT_ID event ID
772
-     * @param boolean $checked_in whether to count registrations checked IN or OUT
773
-     * @return int
774
-     * @throws EE_Error
775
-     */
776
-    public function count_registrations_checked_into_event($EVT_ID, $checked_in = true)
777
-    {
778
-        global $wpdb;
779
-        // subquery to get latest checkin
780
-        $query = $wpdb->prepare(
781
-            'SELECT '
782
-            . 'COUNT( DISTINCT checkins.REG_ID ) '
783
-            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
784
-            . '( SELECT '
785
-            . 'max( CHK_timestamp ) AS latest_checkin, '
786
-            . 'REG_ID AS REG_ID '
787
-            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS c '
788
-            . 'INNER JOIN ' . EEM_Datetime::instance()->table() . ' AS d '
789
-            . 'ON c.DTT_ID=d.DTT_ID '
790
-            . 'WHERE d.EVT_ID=%d '
791
-            . 'GROUP BY REG_ID'
792
-            . ') AS most_recent_checkin_per_reg '
793
-            . 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
794
-            . 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
795
-            . 'WHERE '
796
-            . 'checkins.CHK_in=%d',
797
-            $EVT_ID,
798
-            $checked_in
799
-        );
800
-        return (int) $wpdb->get_var($query);
801
-    }
802
-
803
-
804
-    /**
805
-     * The purpose of this method is to retrieve an array of
806
-     * EE_Registration objects that represent the latest registration
807
-     * for each ATT_ID given in the function argument.
808
-     *
809
-     * @param array $attendee_ids
810
-     * @return EE_Base_Class[]|EE_Registration[]
811
-     * @throws EE_Error
812
-     */
813
-    public function get_latest_registration_for_each_of_given_contacts($attendee_ids = array())
814
-    {
815
-        // first do a native wp_query to get the latest REG_ID's matching these attendees.
816
-        global $wpdb;
817
-        $registration_table = $wpdb->prefix . 'esp_registration';
818
-        $attendee_table = $wpdb->posts;
819
-        $attendee_ids = is_array($attendee_ids)
820
-            ? array_map('absint', $attendee_ids)
821
-            : array((int) $attendee_ids);
822
-        $ATT_IDs = implode(',', $attendee_ids);
823
-        // first we do a query to get the registration ids
824
-        // (because a group by before order by causes the order by to be ignored.)
825
-        $registration_id_query = "
16
+	/**
17
+	 * @var EEM_Registration $_instance
18
+	 */
19
+	protected static $_instance;
20
+
21
+	/**
22
+	 * Keys are the status IDs for registrations (eg, RAP, RCN, etc), and the values
23
+	 * are status codes (eg, approved, cancelled, etc)
24
+	 *
25
+	 * @var array
26
+	 */
27
+	private static $_reg_status;
28
+
29
+	/**
30
+	 * The value of REG_count for a primary registrant
31
+	 */
32
+	const PRIMARY_REGISTRANT_COUNT = 1;
33
+
34
+	/**
35
+	 * Status ID (STS_ID on esp_status table) to indicate an INCOMPLETE registration.
36
+	 * Initial status for registrations when they are first created
37
+	 * Payments are NOT allowed.
38
+	 * Automatically toggled to whatever the default Event registration status is upon completion of the attendee
39
+	 * information reg step NO space reserved. Registration is NOT active
40
+	 */
41
+	const status_id_incomplete = 'RIC';
42
+
43
+	/**
44
+	 * Status ID (STS_ID on esp_status table) to indicate an UNAPPROVED registration.
45
+	 * Payments are NOT allowed.
46
+	 * Event Admin must manually toggle STS_ID for it to change
47
+	 * No space reserved.
48
+	 * Registration is active
49
+	 */
50
+	const status_id_not_approved = 'RNA';
51
+
52
+	/**
53
+	 * Status ID (STS_ID on esp_status table) to indicate registration is PENDING_PAYMENT .
54
+	 * Payments are allowed.
55
+	 * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
56
+	 * No space reserved.
57
+	 * Registration is active
58
+	 */
59
+	const status_id_pending_payment = 'RPP';
60
+
61
+	/**
62
+	 * Status ID (STS_ID on esp_status table) to indicate registration is on the WAIT_LIST .
63
+	 * Payments are allowed.
64
+	 * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
65
+	 * No space reserved.
66
+	 * Registration is active
67
+	 */
68
+	const status_id_wait_list = 'RWL';
69
+
70
+	/**
71
+	 * Status ID (STS_ID on esp_status table) to indicate an APPROVED registration.
72
+	 * the TXN may or may not be completed ( paid in full )
73
+	 * Payments are allowed.
74
+	 * A space IS reserved.
75
+	 * Registration is active
76
+	 */
77
+	const status_id_approved = 'RAP';
78
+
79
+	/**
80
+	 * Status ID (STS_ID on esp_status table) to indicate a registration was CANCELLED by the attendee.
81
+	 * Payments are NOT allowed.
82
+	 * NO space reserved.
83
+	 * Registration is NOT active
84
+	 */
85
+	const status_id_cancelled = 'RCN';
86
+
87
+	/**
88
+	 * Status ID (STS_ID on esp_status table) to indicate a registration was DECLINED by the Event Admin
89
+	 * Payments are NOT allowed.
90
+	 * No space reserved.
91
+	 * Registration is NOT active
92
+	 */
93
+	const status_id_declined = 'RDC';
94
+
95
+	/**
96
+	 * @var TableAnalysis $table_analysis
97
+	 */
98
+	protected $_table_analysis;
99
+
100
+
101
+	/**
102
+	 *    private constructor to prevent direct creation
103
+	 *
104
+	 * @Constructor
105
+	 * @access protected
106
+	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any
107
+	 *                         incoming timezone data that gets saved). Note this just sends the timezone info to the
108
+	 *                         date time model field objects.  Default is NULL (and will be assumed using the set
109
+	 *                         timezone in the 'timezone_string' wp option)
110
+	 * @throws EE_Error
111
+	 */
112
+	protected function __construct($timezone = null)
113
+	{
114
+		$this->_table_analysis = EE_Registry::instance()->create('TableAnalysis', array(), true);
115
+		$this->singular_item = esc_html__('Registration', 'event_espresso');
116
+		$this->plural_item = esc_html__('Registrations', 'event_espresso');
117
+		$this->_tables = array(
118
+			'Registration' => new EE_Primary_Table('esp_registration', 'REG_ID'),
119
+		);
120
+		$this->_fields = array(
121
+			'Registration' => array(
122
+				'REG_ID' => new EE_Primary_Key_Int_Field(
123
+					'REG_ID',
124
+					esc_html__('Registration ID', 'event_espresso')
125
+				),
126
+				'EVT_ID' => new EE_Foreign_Key_Int_Field(
127
+					'EVT_ID',
128
+					esc_html__('Event ID', 'event_espresso'),
129
+					false,
130
+					0,
131
+					'Event'
132
+				),
133
+				'ATT_ID' => new EE_Foreign_Key_Int_Field(
134
+					'ATT_ID',
135
+					esc_html__('Attendee ID', 'event_espresso'),
136
+					false,
137
+					0,
138
+					'Attendee'
139
+				),
140
+				'TXN_ID' => new EE_Foreign_Key_Int_Field(
141
+					'TXN_ID',
142
+					esc_html__('Transaction ID', 'event_espresso'),
143
+					false,
144
+					0,
145
+					'Transaction'
146
+				),
147
+				'TKT_ID' => new EE_Foreign_Key_Int_Field(
148
+					'TKT_ID',
149
+					esc_html__('Ticket ID', 'event_espresso'),
150
+					false,
151
+					0,
152
+					'Ticket'
153
+				),
154
+				'STS_ID' => new EE_Foreign_Key_String_Field(
155
+					'STS_ID',
156
+					esc_html__('Status ID', 'event_espresso'),
157
+					false,
158
+					EEM_Registration::status_id_incomplete,
159
+					'Status'
160
+				),
161
+				'REG_date' => new EE_Datetime_Field(
162
+					'REG_date',
163
+					esc_html__('Time registration occurred', 'event_espresso'),
164
+					false,
165
+					EE_Datetime_Field::now,
166
+					$timezone
167
+				),
168
+				'REG_final_price' => new EE_Money_Field(
169
+					'REG_final_price',
170
+					esc_html__('Registration\'s share of the transaction total', 'event_espresso'),
171
+					false,
172
+					0
173
+				),
174
+				'REG_paid' => new EE_Money_Field(
175
+					'REG_paid',
176
+					esc_html__('Amount paid to date towards registration', 'event_espresso'),
177
+					false,
178
+					0
179
+				),
180
+				'REG_session' => new EE_Plain_Text_Field(
181
+					'REG_session',
182
+					esc_html__('Session ID of registration', 'event_espresso'),
183
+					false,
184
+					''
185
+				),
186
+				'REG_code' => new EE_Plain_Text_Field(
187
+					'REG_code',
188
+					esc_html__('Unique Code for this registration', 'event_espresso'),
189
+					false,
190
+					''
191
+				),
192
+				'REG_url_link' => new EE_Plain_Text_Field(
193
+					'REG_url_link',
194
+					esc_html__('String to be used in URL for identifying registration', 'event_espresso'),
195
+					false,
196
+					''
197
+				),
198
+				'REG_count' => new EE_Integer_Field(
199
+					'REG_count',
200
+					esc_html__('Count of this registration in the group registration ', 'event_espresso'),
201
+					true,
202
+					1
203
+				),
204
+				'REG_group_size' => new EE_Integer_Field(
205
+					'REG_group_size',
206
+					esc_html__('Number of registrations on this group', 'event_espresso'),
207
+					false,
208
+					1
209
+				),
210
+				'REG_att_is_going' => new EE_Boolean_Field(
211
+					'REG_att_is_going',
212
+					esc_html__('Flag indicating the registrant plans on attending', 'event_espresso'),
213
+					false,
214
+					false
215
+				),
216
+				'REG_deleted' => new EE_Trashed_Flag_Field(
217
+					'REG_deleted',
218
+					esc_html__('Flag indicating if registration has been archived or not.', 'event_espresso'),
219
+					false,
220
+					false
221
+				),
222
+			),
223
+		);
224
+		$this->_model_relations = array(
225
+			'Event' => new EE_Belongs_To_Relation(),
226
+			'Attendee' => new EE_Belongs_To_Relation(),
227
+			'Transaction' => new EE_Belongs_To_Relation(),
228
+			'Ticket' => new EE_Belongs_To_Relation(),
229
+			'Status' => new EE_Belongs_To_Relation(),
230
+			'Answer' => new EE_Has_Many_Relation(),
231
+			'Checkin' => new EE_Has_Many_Relation(),
232
+			'Registration_Payment' => new EE_Has_Many_Relation(),
233
+			'Payment' => new EE_HABTM_Relation('Registration_Payment'),
234
+			'Message' => new EE_Has_Many_Any_Relation(false)
235
+			// allow deletes even if there are messages in the queue related
236
+		);
237
+		$this->_model_chain_to_wp_user = 'Event';
238
+		parent::__construct($timezone);
239
+	}
240
+
241
+
242
+	/**
243
+	 * a list of ALL valid registration statuses currently in use within the system
244
+	 * generated by combining the filterable active and inactive reg status arrays
245
+	 *
246
+	 * @return array
247
+	 */
248
+	public static function reg_statuses()
249
+	{
250
+		return array_unique(
251
+			array_merge(
252
+				EEM_Registration::active_reg_statuses(),
253
+				EEM_Registration::inactive_reg_statuses()
254
+			)
255
+		);
256
+	}
257
+
258
+
259
+	/**
260
+	 * reg_statuses_that_allow_payment
261
+	 * a filterable list of registration statuses that allow a registrant to make a payment
262
+	 *
263
+	 * @access public
264
+	 * @return array
265
+	 */
266
+	public static function reg_statuses_that_allow_payment()
267
+	{
268
+		return apply_filters(
269
+			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
270
+			array(
271
+				EEM_Registration::status_id_approved,
272
+				EEM_Registration::status_id_pending_payment,
273
+			)
274
+		);
275
+	}
276
+
277
+
278
+	/**
279
+	 * active_reg_statuses
280
+	 * a filterable list of registration statuses that are considered active
281
+	 *
282
+	 * @access public
283
+	 * @return array
284
+	 */
285
+	public static function active_reg_statuses()
286
+	{
287
+		return apply_filters(
288
+			'FHEE__EEM_Registration__active_reg_statuses',
289
+			array(
290
+				EEM_Registration::status_id_approved,
291
+				EEM_Registration::status_id_pending_payment,
292
+				EEM_Registration::status_id_wait_list,
293
+				EEM_Registration::status_id_not_approved,
294
+			)
295
+		);
296
+	}
297
+
298
+
299
+	/**
300
+	 * inactive_reg_statuses
301
+	 * a filterable list of registration statuses that are not considered active
302
+	 *
303
+	 * @access public
304
+	 * @return array
305
+	 */
306
+	public static function inactive_reg_statuses()
307
+	{
308
+		return apply_filters(
309
+			'FHEE__EEM_Registration__inactive_reg_statuses',
310
+			array(
311
+				EEM_Registration::status_id_incomplete,
312
+				EEM_Registration::status_id_cancelled,
313
+				EEM_Registration::status_id_declined,
314
+			)
315
+		);
316
+	}
317
+
318
+
319
+	/**
320
+	 *    closed_reg_statuses
321
+	 *    a filterable list of registration statuses that are considered "closed"
322
+	 * meaning they should not be considered in any calculations involving monies owing
323
+	 *
324
+	 * @access public
325
+	 * @return array
326
+	 */
327
+	public static function closed_reg_statuses()
328
+	{
329
+		return apply_filters(
330
+			'FHEE__EEM_Registration__closed_reg_statuses',
331
+			array(
332
+				EEM_Registration::status_id_cancelled,
333
+				EEM_Registration::status_id_declined,
334
+				EEM_Registration::status_id_wait_list,
335
+			)
336
+		);
337
+	}
338
+
339
+
340
+	/**
341
+	 *        get list of registration statuses
342
+	 *
343
+	 * @access public
344
+	 * @param array $exclude The status ids to exclude from the returned results
345
+	 * @param bool $translated If true will return the values as singular localized strings
346
+	 * @return array
347
+	 * @throws EE_Error
348
+	 */
349
+	public static function reg_status_array($exclude = array(), $translated = false)
350
+	{
351
+		EEM_Registration::instance()->_get_registration_status_array($exclude);
352
+		return $translated
353
+			? EEM_Status::instance()->localized_status(self::$_reg_status, false, 'sentence')
354
+			: self::$_reg_status;
355
+	}
356
+
357
+
358
+	/**
359
+	 *    get list of registration statuses
360
+	 *
361
+	 * @access private
362
+	 * @param array $exclude
363
+	 * @return void
364
+	 * @throws EE_Error
365
+	 */
366
+	private function _get_registration_status_array($exclude = array())
367
+	{
368
+		// in the very rare circumstance that we are deleting a model's table's data
369
+		// and the table hasn't actually been created, this could have an error
370
+		/** @type WPDB $wpdb */
371
+		global $wpdb;
372
+		if ($this->_get_table_analysis()->tableExists($wpdb->prefix . 'esp_status')) {
373
+			$results = $wpdb->get_results(
374
+				"SELECT STS_ID, STS_code FROM {$wpdb->prefix}esp_status WHERE STS_type = 'registration'"
375
+			);
376
+			self::$_reg_status = array();
377
+			foreach ($results as $status) {
378
+				if (!in_array($status->STS_ID, $exclude, true)) {
379
+					self::$_reg_status[ $status->STS_ID ] = $status->STS_code;
380
+				}
381
+			}
382
+		}
383
+	}
384
+
385
+
386
+	/**
387
+	 * Gets the injected table analyzer, or throws an exception
388
+	 *
389
+	 * @return TableAnalysis
390
+	 * @throws EE_Error
391
+	 */
392
+	protected function _get_table_analysis()
393
+	{
394
+		if ($this->_table_analysis instanceof TableAnalysis) {
395
+			return $this->_table_analysis;
396
+		}
397
+		throw new EE_Error(
398
+			sprintf(
399
+				esc_html__('Table analysis class on class %1$s is not set properly.', 'event_espresso'),
400
+				get_class($this)
401
+			)
402
+		);
403
+	}
404
+
405
+
406
+	/**
407
+	 * This returns a wpdb->results array of all registration date month and years matching the incoming query params
408
+	 * and grouped by month and year.
409
+	 *
410
+	 * @param  array $where_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
411
+	 * @return array
412
+	 * @throws EE_Error
413
+	 */
414
+	public function get_reg_months_and_years($where_params)
415
+	{
416
+		$query_params[0] = $where_params;
417
+		$query_params['group_by'] = array('reg_year', 'reg_month');
418
+		$query_params['order_by'] = array('REG_date' => 'DESC');
419
+		$columns_to_select = array(
420
+			'reg_year' => array('YEAR(REG_date)', '%s'),
421
+			'reg_month' => array('MONTHNAME(REG_date)', '%s'),
422
+		);
423
+		return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
424
+	}
425
+
426
+
427
+	/**
428
+	 * retrieve ALL registrations for a particular Attendee from db
429
+	 *
430
+	 * @param int $ATT_ID
431
+	 * @return EE_Base_Class[]|EE_Registration[]|null
432
+	 * @throws EE_Error
433
+	 */
434
+	public function get_all_registrations_for_attendee($ATT_ID = 0)
435
+	{
436
+		if (!$ATT_ID) {
437
+			return null;
438
+		}
439
+		return $this->get_all(array(array('ATT_ID' => $ATT_ID)));
440
+	}
441
+
442
+
443
+	/**
444
+	 * Gets a registration given their REG_url_link. Yes, this should usually
445
+	 * be passed via a GET parameter.
446
+	 *
447
+	 * @param string $REG_url_link
448
+	 * @return EE_Base_Class|EE_Registration|null
449
+	 * @throws EE_Error
450
+	 */
451
+	public function get_registration_for_reg_url_link($REG_url_link)
452
+	{
453
+		if (!$REG_url_link) {
454
+			return null;
455
+		}
456
+		return $this->get_one(array(array('REG_url_link' => $REG_url_link)));
457
+	}
458
+
459
+
460
+	/**
461
+	 *        retrieve registration for a specific transaction attendee from db
462
+	 *
463
+	 * @access        public
464
+	 * @param    int $TXN_ID
465
+	 * @param    int $ATT_ID
466
+	 * @param    int $att_nmbr in case the ATT_ID is the same for multiple registrations (same details used) then the
467
+	 *                         attendee number is required
468
+	 * @return        mixed        array on success, FALSE on fail
469
+	 * @throws EE_Error
470
+	 */
471
+	public function get_registration_for_transaction_attendee($TXN_ID = 0, $ATT_ID = 0, $att_nmbr = 0)
472
+	{
473
+		return $this->get_one(array(
474
+			array(
475
+				'TXN_ID' => $TXN_ID,
476
+				'ATT_ID' => $ATT_ID,
477
+			),
478
+			'limit' => array(min($att_nmbr - 1, 0), 1),
479
+		));
480
+	}
481
+
482
+
483
+	/**
484
+	 *        get the number of registrations per day  for the Registration Admin page Reports Tab.
485
+	 *        (doesn't utilize models because it's a fairly specialized query)
486
+	 *
487
+	 * @access        public
488
+	 * @param $period string which can be passed to php's strtotime function (eg "-1 month")
489
+	 * @return stdClass[] with properties regDate and total
490
+	 * @throws EE_Error
491
+	 */
492
+	public function get_registrations_per_day_report($period = '-1 month')
493
+	{
494
+		$sql_date = $this->convert_datetime_for_query(
495
+			'REG_date',
496
+			date('Y-m-d H:i:s', strtotime($period)),
497
+			'Y-m-d H:i:s',
498
+			'UTC'
499
+		);
500
+		$where = array(
501
+			'REG_date' => array('>=', $sql_date),
502
+			'STS_ID' => array('!=', EEM_Registration::status_id_incomplete),
503
+		);
504
+		if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_day_report')) {
505
+			$where['Event.EVT_wp_user'] = get_current_user_id();
506
+		}
507
+		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'REG_date');
508
+		$results = $this->_get_all_wpdb_results(
509
+			array(
510
+				$where,
511
+				'group_by' => 'regDate',
512
+				'order_by' => array('REG_date' => 'ASC'),
513
+			),
514
+			OBJECT,
515
+			array(
516
+				'regDate' => array('DATE(' . $query_interval . ')', '%s'),
517
+				'total' => array('count(REG_ID)', '%d'),
518
+			)
519
+		);
520
+		return $results;
521
+	}
522
+
523
+
524
+	/**
525
+	 * Get the number of registrations per day including the count of registrations for each Registration Status.
526
+	 * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
527
+	 *
528
+	 * @param string $period
529
+	 * @return stdClass[] with properties Registration_REG_date and a column for each registration status as the STS_ID
530
+	 * @throws EE_Error
531
+	 *                    (i.e. RAP)
532
+	 */
533
+	public function get_registrations_per_day_and_per_status_report($period = '-1 month')
534
+	{
535
+		global $wpdb;
536
+		$registration_table = $wpdb->prefix . 'esp_registration';
537
+		$event_table = $wpdb->posts;
538
+		$sql_date = date('Y-m-d H:i:s', strtotime($period));
539
+		// prepare the query interval for displaying offset
540
+		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'dates.REG_date');
541
+		// inner date query
542
+		$inner_date_query = "SELECT DISTINCT REG_date from {$registration_table} ";
543
+		$inner_where = ' WHERE';
544
+		// exclude events not authored by user if permissions in effect
545
+		if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
546
+			$inner_date_query .= "LEFT JOIN {$event_table} ON ID = EVT_ID";
547
+			$inner_where .= ' post_author = ' . get_current_user_id() . ' AND';
548
+		}
549
+		$inner_where .= " REG_date >= '{$sql_date}'";
550
+		$inner_date_query .= $inner_where;
551
+		// start main query
552
+		$select = "SELECT DATE({$query_interval}) as Registration_REG_date, ";
553
+		$join = '';
554
+		$join_parts = array();
555
+		$select_parts = array();
556
+		// loop through registration stati to do parts for each status.
557
+		foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
558
+			if ($STS_ID === EEM_Registration::status_id_incomplete) {
559
+				continue;
560
+			}
561
+			$select_parts[] = "COUNT({$STS_code}.REG_ID) as {$STS_ID}";
562
+			$join_parts[] = "{$registration_table} AS {$STS_code} ON {$STS_code}.REG_date = dates.REG_date AND {$STS_code}.STS_ID = '{$STS_ID}'";
563
+		}
564
+		// setup the selects
565
+		$select .= implode(', ', $select_parts);
566
+		$select .= " FROM ($inner_date_query) AS dates LEFT JOIN ";
567
+		// setup the joins
568
+		$join .= implode(' LEFT JOIN ', $join_parts);
569
+		// now let's put it all together
570
+		$query = $select . $join . ' GROUP BY Registration_REG_date';
571
+		// and execute it
572
+		return $wpdb->get_results($query, ARRAY_A);
573
+	}
574
+
575
+
576
+	/**
577
+	 *        get the number of registrations per event  for the Registration Admin page Reports Tab
578
+	 *
579
+	 * @access        public
580
+	 * @param $period string which can be passed to php's strtotime function (eg "-1 month")
581
+	 * @return stdClass[] each with properties event_name, reg_limit, and total
582
+	 * @throws EE_Error
583
+	 */
584
+	public function get_registrations_per_event_report($period = '-1 month')
585
+	{
586
+		$date_sql = $this->convert_datetime_for_query(
587
+			'REG_date',
588
+			date('Y-m-d H:i:s', strtotime($period)),
589
+			'Y-m-d H:i:s',
590
+			'UTC'
591
+		);
592
+		$where = array(
593
+			'REG_date' => array('>=', $date_sql),
594
+			'STS_ID' => array('!=', EEM_Registration::status_id_incomplete),
595
+		);
596
+		if (!EE_Registry::instance()->CAP->current_user_can(
597
+			'ee_read_others_registrations',
598
+			'reg_per_event_report'
599
+		)
600
+		) {
601
+			$where['Event.EVT_wp_user'] = get_current_user_id();
602
+		}
603
+		$results = $this->_get_all_wpdb_results(
604
+			array(
605
+			$where,
606
+			'group_by' => 'Event.EVT_name',
607
+			'order_by' => 'Event.EVT_name',
608
+			'limit' => array(0, 24),
609
+			),
610
+			OBJECT,
611
+			array(
612
+				'event_name' => array('Event_CPT.post_title', '%s'),
613
+				'total' => array('COUNT(REG_ID)', '%s'),
614
+			)
615
+		);
616
+		return $results;
617
+	}
618
+
619
+
620
+	/**
621
+	 * Get the number of registrations per event grouped by registration status.
622
+	 * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
623
+	 *
624
+	 * @param string $period
625
+	 * @return stdClass[] with properties `Registration_Event` and a column for each registration status as the STS_ID
626
+	 * @throws EE_Error
627
+	 *                    (i.e. RAP)
628
+	 */
629
+	public function get_registrations_per_event_and_per_status_report($period = '-1 month')
630
+	{
631
+		global $wpdb;
632
+		$registration_table = $wpdb->prefix . 'esp_registration';
633
+		$event_table = $wpdb->posts;
634
+		$sql_date = date('Y-m-d H:i:s', strtotime($period));
635
+		// inner date query
636
+		$inner_date_query = "SELECT DISTINCT EVT_ID, REG_date from $registration_table ";
637
+		$inner_where = ' WHERE';
638
+		// exclude events not authored by user if permissions in effect
639
+		if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
640
+			$inner_date_query .= "LEFT JOIN {$event_table} ON ID = EVT_ID";
641
+			$inner_where .= ' post_author = ' . get_current_user_id() . ' AND';
642
+		}
643
+		$inner_where .= " REG_date >= '{$sql_date}'";
644
+		$inner_date_query .= $inner_where;
645
+		// build main query
646
+		$select = 'SELECT Event.post_title as Registration_Event, ';
647
+		$join = '';
648
+		$join_parts = array();
649
+		$select_parts = array();
650
+		// loop through registration stati to do parts for each status.
651
+		foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
652
+			if ($STS_ID === EEM_Registration::status_id_incomplete) {
653
+				continue;
654
+			}
655
+			$select_parts[] = "COUNT({$STS_code}.REG_ID) as {$STS_ID}";
656
+			$join_parts[] = "{$registration_table} AS {$STS_code} ON {$STS_code}.EVT_ID = dates.EVT_ID AND {$STS_code}.STS_ID = '{$STS_ID}' AND {$STS_code}.REG_date = dates.REG_date";
657
+		}
658
+		// setup the selects
659
+		$select .= implode(', ', $select_parts);
660
+		$select .= " FROM ($inner_date_query) AS dates LEFT JOIN $event_table as Event ON Event.ID = dates.EVT_ID LEFT JOIN ";
661
+		// setup remaining joins
662
+		$join .= implode(' LEFT JOIN ', $join_parts);
663
+		// now put it all together
664
+		$query = $select . $join . ' GROUP BY Registration_Event';
665
+		// and execute
666
+		return $wpdb->get_results($query, ARRAY_A);
667
+	}
668
+
669
+
670
+	/**
671
+	 * Returns the EE_Registration of the primary attendee on the transaction id provided
672
+	 *
673
+	 * @param int $TXN_ID
674
+	 * @return EE_Base_Class|EE_Registration|null
675
+	 * @throws EE_Error
676
+	 */
677
+	public function get_primary_registration_for_transaction_ID($TXN_ID = 0)
678
+	{
679
+		if (!$TXN_ID) {
680
+			return null;
681
+		}
682
+		return $this->get_one(array(
683
+			array(
684
+				'TXN_ID' => $TXN_ID,
685
+				'REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT,
686
+			),
687
+		));
688
+	}
689
+
690
+
691
+	/**
692
+	 *        get_event_registration_count
693
+	 *
694
+	 * @access public
695
+	 * @param int $EVT_ID
696
+	 * @param boolean $for_incomplete_payments
697
+	 * @return int
698
+	 * @throws EE_Error
699
+	 */
700
+	public function get_event_registration_count($EVT_ID, $for_incomplete_payments = false)
701
+	{
702
+		// we only count approved registrations towards registration limits
703
+		$query_params = array(array('EVT_ID' => $EVT_ID, 'STS_ID' => self::status_id_approved));
704
+		if ($for_incomplete_payments) {
705
+			$query_params[0]['Transaction.STS_ID'] = array('!=', EEM_Transaction::complete_status_code);
706
+		}
707
+		return $this->count($query_params);
708
+	}
709
+
710
+
711
+	/**
712
+	 * Deletes all registrations with no transactions. Note that this needs to be very efficient
713
+	 * and so it uses wpdb directly. Also, we can't put a limit on this because MySQL doesn't allow a limit on a delete
714
+	 * when joining tables like this.
715
+	 *
716
+	 * @global WPDB $wpdb
717
+	 * @return int number deleted
718
+	 * @throws EE_Error
719
+	 */
720
+	public function delete_registrations_with_no_transaction()
721
+	{
722
+		/** @type WPDB $wpdb */
723
+		global $wpdb;
724
+		return $wpdb->query(
725
+			'DELETE r FROM '
726
+			. $this->table()
727
+			. ' r LEFT JOIN '
728
+			. EEM_Transaction::instance()->table()
729
+			. ' t ON r.TXN_ID = t.TXN_ID WHERE t.TXN_ID IS NULL'
730
+		);
731
+	}
732
+
733
+
734
+	/**
735
+	 *  Count registrations checked into (or out of) a datetime
736
+	 *
737
+	 * @param int $DTT_ID datetime ID
738
+	 * @param boolean $checked_in whether to count registrations checked IN or OUT
739
+	 * @return int
740
+	 * @throws EE_Error
741
+	 */
742
+	public function count_registrations_checked_into_datetime($DTT_ID, $checked_in = true)
743
+	{
744
+		global $wpdb;
745
+		// subquery to get latest checkin
746
+		$query = $wpdb->prepare(
747
+			'SELECT '
748
+			. 'COUNT( DISTINCT checkins.REG_ID ) '
749
+			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
750
+			. '( SELECT '
751
+			. 'max( CHK_timestamp ) AS latest_checkin, '
752
+			. 'REG_ID AS REG_ID '
753
+			. 'FROM ' . EEM_Checkin::instance()->table() . ' '
754
+			. 'WHERE DTT_ID=%d '
755
+			. 'GROUP BY REG_ID'
756
+			. ') AS most_recent_checkin_per_reg '
757
+			. 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
758
+			. 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
759
+			. 'WHERE '
760
+			. 'checkins.CHK_in=%d',
761
+			$DTT_ID,
762
+			$checked_in
763
+		);
764
+		return (int) $wpdb->get_var($query);
765
+	}
766
+
767
+
768
+	/**
769
+	 *  Count registrations checked into (or out of) an event.
770
+	 *
771
+	 * @param int $EVT_ID event ID
772
+	 * @param boolean $checked_in whether to count registrations checked IN or OUT
773
+	 * @return int
774
+	 * @throws EE_Error
775
+	 */
776
+	public function count_registrations_checked_into_event($EVT_ID, $checked_in = true)
777
+	{
778
+		global $wpdb;
779
+		// subquery to get latest checkin
780
+		$query = $wpdb->prepare(
781
+			'SELECT '
782
+			. 'COUNT( DISTINCT checkins.REG_ID ) '
783
+			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
784
+			. '( SELECT '
785
+			. 'max( CHK_timestamp ) AS latest_checkin, '
786
+			. 'REG_ID AS REG_ID '
787
+			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS c '
788
+			. 'INNER JOIN ' . EEM_Datetime::instance()->table() . ' AS d '
789
+			. 'ON c.DTT_ID=d.DTT_ID '
790
+			. 'WHERE d.EVT_ID=%d '
791
+			. 'GROUP BY REG_ID'
792
+			. ') AS most_recent_checkin_per_reg '
793
+			. 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
794
+			. 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
795
+			. 'WHERE '
796
+			. 'checkins.CHK_in=%d',
797
+			$EVT_ID,
798
+			$checked_in
799
+		);
800
+		return (int) $wpdb->get_var($query);
801
+	}
802
+
803
+
804
+	/**
805
+	 * The purpose of this method is to retrieve an array of
806
+	 * EE_Registration objects that represent the latest registration
807
+	 * for each ATT_ID given in the function argument.
808
+	 *
809
+	 * @param array $attendee_ids
810
+	 * @return EE_Base_Class[]|EE_Registration[]
811
+	 * @throws EE_Error
812
+	 */
813
+	public function get_latest_registration_for_each_of_given_contacts($attendee_ids = array())
814
+	{
815
+		// first do a native wp_query to get the latest REG_ID's matching these attendees.
816
+		global $wpdb;
817
+		$registration_table = $wpdb->prefix . 'esp_registration';
818
+		$attendee_table = $wpdb->posts;
819
+		$attendee_ids = is_array($attendee_ids)
820
+			? array_map('absint', $attendee_ids)
821
+			: array((int) $attendee_ids);
822
+		$ATT_IDs = implode(',', $attendee_ids);
823
+		// first we do a query to get the registration ids
824
+		// (because a group by before order by causes the order by to be ignored.)
825
+		$registration_id_query = "
826 826
 			SELECT registrations.registration_ids as registration_id
827 827
 			FROM (
828 828
 				SELECT
@@ -836,61 +836,61 @@  discard block
 block discarded – undo
836 836
 			  ) AS registrations
837 837
 			  GROUP BY registrations.attendee_ids
838 838
 		";
839
-        $registration_ids = $wpdb->get_results($registration_id_query, ARRAY_A);
840
-        if (empty($registration_ids)) {
841
-            return array();
842
-        }
843
-        $ids_for_model_query = array();
844
-        // let's flatten the ids so they can be used in the model query.
845
-        foreach ($registration_ids as $registration_id) {
846
-            if (isset($registration_id['registration_id'])) {
847
-                $ids_for_model_query[] = $registration_id['registration_id'];
848
-            }
849
-        }
850
-        // construct query
851
-        $_where = array(
852
-            'REG_ID' => array('IN', $ids_for_model_query),
853
-        );
854
-        return $this->get_all(array($_where));
855
-    }
856
-
857
-
858
-
859
-    /**
860
-     * returns a count of registrations for the supplied event having the status as specified
861
-     *
862
-     * @param int $EVT_ID
863
-     * @param array $statuses
864
-     * @return int
865
-     * @throws InvalidArgumentException
866
-     * @throws InvalidStatusException
867
-     * @throws EE_Error
868
-     */
869
-    public function event_reg_count_for_statuses($EVT_ID, $statuses = array())
870
-    {
871
-        $EVT_ID = absint($EVT_ID);
872
-        if (! $EVT_ID) {
873
-            throw new InvalidArgumentException(
874
-                esc_html__('An invalid Event ID was supplied.', 'event_espresso')
875
-            );
876
-        }
877
-        $statuses = is_array($statuses) ? $statuses : array($statuses);
878
-        $statuses = ! empty($statuses) ? $statuses : array(EEM_Registration::status_id_approved);
879
-        $valid_reg_statuses = EEM_Registration::reg_statuses();
880
-        foreach ($statuses as $status) {
881
-            if (! in_array($status, $valid_reg_statuses, true)) {
882
-                throw new InvalidStatusException($status, esc_html__('Registration', 'event_espresso'));
883
-            }
884
-        }
885
-        return $this->count(
886
-            array(
887
-                array(
888
-                    'EVT_ID' => $EVT_ID,
889
-                    'STS_ID' => array('IN', $statuses),
890
-                ),
891
-            ),
892
-            'REG_ID',
893
-            true
894
-        );
895
-    }
839
+		$registration_ids = $wpdb->get_results($registration_id_query, ARRAY_A);
840
+		if (empty($registration_ids)) {
841
+			return array();
842
+		}
843
+		$ids_for_model_query = array();
844
+		// let's flatten the ids so they can be used in the model query.
845
+		foreach ($registration_ids as $registration_id) {
846
+			if (isset($registration_id['registration_id'])) {
847
+				$ids_for_model_query[] = $registration_id['registration_id'];
848
+			}
849
+		}
850
+		// construct query
851
+		$_where = array(
852
+			'REG_ID' => array('IN', $ids_for_model_query),
853
+		);
854
+		return $this->get_all(array($_where));
855
+	}
856
+
857
+
858
+
859
+	/**
860
+	 * returns a count of registrations for the supplied event having the status as specified
861
+	 *
862
+	 * @param int $EVT_ID
863
+	 * @param array $statuses
864
+	 * @return int
865
+	 * @throws InvalidArgumentException
866
+	 * @throws InvalidStatusException
867
+	 * @throws EE_Error
868
+	 */
869
+	public function event_reg_count_for_statuses($EVT_ID, $statuses = array())
870
+	{
871
+		$EVT_ID = absint($EVT_ID);
872
+		if (! $EVT_ID) {
873
+			throw new InvalidArgumentException(
874
+				esc_html__('An invalid Event ID was supplied.', 'event_espresso')
875
+			);
876
+		}
877
+		$statuses = is_array($statuses) ? $statuses : array($statuses);
878
+		$statuses = ! empty($statuses) ? $statuses : array(EEM_Registration::status_id_approved);
879
+		$valid_reg_statuses = EEM_Registration::reg_statuses();
880
+		foreach ($statuses as $status) {
881
+			if (! in_array($status, $valid_reg_statuses, true)) {
882
+				throw new InvalidStatusException($status, esc_html__('Registration', 'event_espresso'));
883
+			}
884
+		}
885
+		return $this->count(
886
+			array(
887
+				array(
888
+					'EVT_ID' => $EVT_ID,
889
+					'STS_ID' => array('IN', $statuses),
890
+				),
891
+			),
892
+			'REG_ID',
893
+			true
894
+		);
895
+	}
896 896
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Message.model.php 1 patch
Indentation   +617 added lines, -617 removed lines patch added patch discarded remove patch
@@ -9,628 +9,628 @@
 block discarded – undo
9 9
 class EEM_Message extends EEM_Base implements EEI_Query_Filter
10 10
 {
11 11
 
12
-    // private instance of the Message object
13
-    protected static $_instance = null;
12
+	// private instance of the Message object
13
+	protected static $_instance = null;
14 14
 
15 15
 
16
-    /**
17
-     * This priority indicates a message should be generated and sent ASAP
18
-     *
19
-     * @type int
20
-     */
21
-    const priority_high = 10;
22
-
23
-
24
-    /**
25
-     * This priority indicates a message should be generated ASAP and queued for sending.
26
-     *
27
-     * @type
28
-     */
29
-    const priority_medium = 20;
30
-
31
-
32
-    /**
33
-     * This priority indicates a message should be queued for generating.
34
-     *
35
-     * @type int
36
-     */
37
-    const priority_low = 30;
38
-
39
-
40
-    /**
41
-     * indicates this message was sent at the time modified
42
-     */
43
-    const status_sent = 'MSN';
44
-
45
-
46
-    /**
47
-     * indicates this message is waiting to be sent
48
-     */
49
-    const status_idle = 'MID';
50
-
51
-
52
-    /**
53
-     * indicates an attempt was a made to send this message
54
-     * at the scheduled time, but it failed at the time modified.  This differs from MDO status in that it will ALWAYS
55
-     * appear to the end user.
56
-     */
57
-    const status_failed = 'MFL';
58
-
59
-
60
-    /**
61
-     * indicates the message has been flagged for resending (at the time modified).
62
-     */
63
-    const status_resend = 'MRS';
64
-
65
-
66
-    /**
67
-     * indicates the message has been flagged for generation but has not been generated yet.  Messages always start as
68
-     * this status when added to the queue.
69
-     */
70
-    const status_incomplete = 'MIC';
71
-
72
-
73
-    /**
74
-     * Indicates everything was generated fine for the message, however, the messenger was unable to send.
75
-     * This status means that its possible to retry sending the message.
76
-     */
77
-    const status_retry = 'MRT';
78
-
79
-
80
-    /**
81
-     * This is used for more informational messages that may not indicate anything is broken but still cannot be
82
-     * generated or sent correctly. An example of a message that would get flagged this way would be when a not
83
-     * approved message was queued for generation, but at time of generation, the attached registration(s) are
84
-     * approved. So the message queued for generation is no longer valid.  Messages for this status will only persist
85
-     * in the db and be viewable in the message activity list table when the messages system is in debug mode.
86
-     *
87
-     * @see EEM_Message::debug()
88
-     */
89
-    const status_debug_only = 'MDO';
90
-
91
-
92
-    /**
93
-     * This status is given to messages it is processed by the messenger send method.
94
-     * Messages with this status should rarely be seen in the Message List table, but if they are, that's usually
95
-     * indicative of a PHP timeout or memory limit issue.
96
-     */
97
-    const status_messenger_executing = 'MEX';
98
-
99
-
100
-    /**
101
-     *    Private constructor to prevent direct creation.
102
-     *
103
-     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and
104
-     *                         any incoming timezone data that gets saved).  Note this just sends the timezone info to
105
-     *                         the date time model field objects.  Default is null (and will be assumed using the set
106
-     *                         timezone in the 'timezone_string' wp option)
107
-     * @return EEM_Message
108
-     */
109
-    protected function __construct($timezone = null)
110
-    {
111
-        $this->singular_item = __('Message', 'event_espresso');
112
-        $this->plural_item   = __('Messages', 'event_espresso');
113
-
114
-        // used for token generator
115
-        EE_Registry::instance()->load_helper('URL');
116
-
117
-        $this->_tables = array(
118
-            'Message' => new EE_Primary_Table('esp_message', 'MSG_ID'),
119
-        );
120
-
121
-        $allowed_priority = array(
122
-            self::priority_high   => __('high', 'event_espresso'),
123
-            self::priority_medium => __('medium', 'event_espresso'),
124
-            self::priority_low    => __('low', 'event_espresso'),
125
-        );
126
-
127
-        $this->_fields          = array(
128
-            'Message' => array(
129
-                'MSG_ID'             => new EE_Primary_Key_Int_Field('MSG_ID', __('Message ID', 'event_espresso')),
130
-                'MSG_token'          => new EE_Plain_Text_Field(
131
-                    'MSG_token',
132
-                    __(
133
-                        'Unique Token used to represent this row in publicly viewable contexts (eg. a url).',
134
-                        'event_espresso'
135
-                    ),
136
-                    false,
137
-                    EEH_URL::generate_unique_token()
138
-                ),
139
-                'GRP_ID'             => new EE_Foreign_Key_Int_Field(
140
-                    'GRP_ID',
141
-                    __('Foreign key to the EEM_Message_Template_Group table.', 'event_espresso'),
142
-                    true,
143
-                    0,
144
-                    'Message_Template_Group'
145
-                ),
146
-                'TXN_ID'             => new EE_Foreign_Key_Int_Field(
147
-                    'TXN_ID',
148
-                    __(
149
-                        'Foreign key to the related EE_Transaction.  This is required to give context for regenerating the specific message',
150
-                        'event_espresso'
151
-                    ),
152
-                    true,
153
-                    0,
154
-                    'Transaction'
155
-                ),
156
-                'MSG_messenger'      => new EE_Plain_Text_Field(
157
-                    'MSG_messenger',
158
-                    __(
159
-                        'Corresponds to the EE_messenger::name used to send this message. This will also be used to attempt any resending of the message.',
160
-                        'event_espresso'
161
-                    ),
162
-                    false,
163
-                    'email'
164
-                ),
165
-                'MSG_message_type'   => new EE_Plain_Text_Field(
166
-                    'MSG_message_type',
167
-                    __('Corresponds to the EE_message_type::name used to generate this message.', 'event_espresso'),
168
-                    false,
169
-                    'receipt'
170
-                ),
171
-                'MSG_context'        => new EE_Plain_Text_Field('MSG_context', __('Context', 'event_espresso'), false),
172
-                'MSG_recipient_ID'   => new EE_Foreign_Key_Int_Field(
173
-                    'MSG_recipient_ID',
174
-                    __('Recipient ID', 'event_espresso'),
175
-                    true,
176
-                    null,
177
-                    array('Registration', 'Attendee', 'WP_User')
178
-                ),
179
-                'MSG_recipient_type' => new EE_Any_Foreign_Model_Name_Field(
180
-                    'MSG_recipient_type',
181
-                    __('Recipient Type', 'event_espresso'),
182
-                    true,
183
-                    null,
184
-                    array('Registration', 'Attendee', 'WP_User')
185
-                ),
186
-                'MSG_content'        => new EE_Maybe_Serialized_Text_Field(
187
-                    'MSG_content',
188
-                    __('Content', 'event_espresso'),
189
-                    true,
190
-                    ''
191
-                ),
192
-                'MSG_to'             => new EE_Maybe_Serialized_Text_Field(
193
-                    'MSG_to',
194
-                    __('Address To', 'event_espresso'),
195
-                    true
196
-                ),
197
-                'MSG_from'           => new EE_Maybe_Serialized_Text_Field(
198
-                    'MSG_from',
199
-                    __('Address From', 'event_espresso'),
200
-                    true
201
-                ),
202
-                'MSG_subject'        => new EE_Maybe_Serialized_Text_Field(
203
-                    'MSG_subject',
204
-                    __('Subject', 'event_espresso'),
205
-                    true,
206
-                    ''
207
-                ),
208
-                'MSG_priority'       => new EE_Enum_Integer_Field(
209
-                    'MSG_priority',
210
-                    __('Priority', 'event_espresso'),
211
-                    false,
212
-                    self::priority_low,
213
-                    $allowed_priority
214
-                ),
215
-                'STS_ID'             => new EE_Foreign_Key_String_Field(
216
-                    'STS_ID',
217
-                    __('Status', 'event_espresso'),
218
-                    false,
219
-                    self::status_incomplete,
220
-                    'Status'
221
-                ),
222
-                'MSG_created'        => new EE_Datetime_Field(
223
-                    'MSG_created',
224
-                    __('Created', 'event_espresso'),
225
-                    false,
226
-                    EE_Datetime_Field::now
227
-                ),
228
-                'MSG_modified'       => new EE_Datetime_Field(
229
-                    'MSG_modified',
230
-                    __('Modified', 'event_espresso'),
231
-                    true,
232
-                    EE_Datetime_Field::now
233
-                ),
234
-            ),
235
-        );
236
-        $this->_model_relations = array(
237
-            'Attendee'               => new EE_Belongs_To_Any_Relation(),
238
-            'Registration'           => new EE_Belongs_To_Any_Relation(),
239
-            'WP_User'                => new EE_Belongs_To_Any_Relation(),
240
-            'Message_Template_Group' => new EE_Belongs_To_Relation(),
241
-            'Transaction'            => new EE_Belongs_To_Relation(),
242
-        );
243
-        parent::__construct($timezone);
244
-    }
245
-
246
-
247
-    /**
248
-     * @return \EE_Message
249
-     */
250
-    public function create_default_object()
251
-    {
252
-        /** @type EE_Message $message */
253
-        $message = parent::create_default_object();
254
-        if ($message instanceof EE_Message) {
255
-            return EE_Message_Factory::set_messenger_and_message_type($message);
256
-        }
257
-        return null;
258
-    }
259
-
260
-
261
-    /**
262
-     * @param mixed $cols_n_values
263
-     * @return \EE_Message
264
-     */
265
-    public function instantiate_class_from_array_or_object($cols_n_values)
266
-    {
267
-        /** @type EE_Message $message */
268
-        $message = parent::instantiate_class_from_array_or_object($cols_n_values);
269
-        if ($message instanceof EE_Message) {
270
-            return EE_Message_Factory::set_messenger_and_message_type($message);
271
-        }
272
-        return null;
273
-    }
274
-
275
-
276
-    /**
277
-     * Returns whether or not a message of that type was sent for a given attendee.
278
-     *
279
-     * @param EE_Attendee|int $attendee
280
-     * @param string          $message_type the message type slug
281
-     * @return boolean
282
-     */
283
-    public function message_sent_for_attendee($attendee, $message_type)
284
-    {
285
-        $attendee_ID = EEM_Attendee::instance()->ensure_is_ID($attendee);
286
-        return $this->exists(array(
287
-            array(
288
-                'Attendee.ATT_ID'  => $attendee_ID,
289
-                'MSG_message_type' => $message_type,
290
-                'STS_ID'           => array('IN', $this->stati_indicating_sent()),
291
-            ),
292
-        ));
293
-    }
294
-
295
-
296
-    /**
297
-     * Returns whether or not a message of that type was sent for a given registration
298
-     *
299
-     * @param EE_Registration|int $registration
300
-     * @param string              $message_type the message type slug
301
-     * @return boolean
302
-     */
303
-    public function message_sent_for_registration($registration, $message_type)
304
-    {
305
-        $registrationID = EEM_Registration::instance()->ensure_is_ID($registration);
306
-        return $this->exists(array(
307
-            array(
308
-                'Registration.REG_ID' => $registrationID,
309
-                'MSG_message_type'    => $message_type,
310
-                'STS_ID'              => array('IN', $this->stati_indicating_sent()),
311
-            ),
312
-        ));
313
-    }
314
-
315
-
316
-    /**
317
-     * This retrieves an EE_Message object from the db matching the given token string.
318
-     *
319
-     * @param string $token
320
-     * @return EE_Message
321
-     */
322
-    public function get_one_by_token($token)
323
-    {
324
-        return $this->get_one(array(
325
-            array(
326
-                'MSG_token' => $token,
327
-            ),
328
-        ));
329
-    }
330
-
331
-
332
-    /**
333
-     * Returns stati that indicate the message HAS been sent
334
-     *
335
-     * @return array of strings for possible stati
336
-     */
337
-    public function stati_indicating_sent()
338
-    {
339
-        return apply_filters('FHEE__EEM_Message__stati_indicating_sent', array(self::status_sent));
340
-    }
341
-
342
-
343
-    /**
344
-     * Returns stati that indicate the message is waiting to be sent.
345
-     *
346
-     * @return array of strings for possible stati.
347
-     */
348
-    public function stati_indicating_to_send()
349
-    {
350
-        return apply_filters(
351
-            'FHEE__EEM_Message__stati_indicating_to_send',
352
-            array(self::status_idle, self::status_resend)
353
-        );
354
-    }
355
-
356
-
357
-    /**
358
-     * Returns stati that indicate the message has failed sending
359
-     *
360
-     * @return array  array of strings for possible stati.
361
-     */
362
-    public function stati_indicating_failed_sending()
363
-    {
364
-        $failed_stati = array(
365
-            self::status_failed,
366
-            self::status_retry,
367
-            self::status_messenger_executing,
368
-        );
369
-        // if WP_DEBUG is set, then let's include debug_only fails
370
-        if (WP_DEBUG) {
371
-            $failed_stati[] = self::status_debug_only;
372
-        }
373
-        return apply_filters('FHEE__EEM_Message__stati_indicating_failed_sending', $failed_stati);
374
-    }
375
-
376
-
377
-    /**
378
-     * Returns filterable array of all EEM_Message statuses.
379
-     *
380
-     * @return array
381
-     */
382
-    public function all_statuses()
383
-    {
384
-        return apply_filters(
385
-            'FHEE__EEM_Message__all_statuses',
386
-            array(
387
-                EEM_Message::status_sent,
388
-                EEM_Message::status_incomplete,
389
-                EEM_Message::status_idle,
390
-                EEM_Message::status_resend,
391
-                EEM_Message::status_retry,
392
-                EEM_Message::status_failed,
393
-                EEM_Message::status_messenger_executing,
394
-                EEM_Message::status_debug_only,
395
-            )
396
-        );
397
-    }
398
-
399
-    /**
400
-     * Detects any specific query variables in the request and uses those to setup appropriate
401
-     * filter for any queries.
402
-     *
403
-     * @return array
404
-     */
405
-    public function filter_by_query_params()
406
-    {
407
-        // expected possible query_vars, the key in this array matches an expected key in the request,
408
-        // the value, matches the corresponding EEM_Base child reference.
409
-        $expected_vars   = $this->_expected_vars_for_query_inject();
410
-        $query_params[0] = array();
411
-        foreach ($expected_vars as $request_key => $model_name) {
412
-            $request_value = EE_Registry::instance()->REQ->get($request_key);
413
-            if ($request_value) {
414
-                // special case
415
-                switch ($request_key) {
416
-                    case '_REG_ID':
417
-                        $query_params[0]['AND**filter_by']['OR**filter_by_REG_ID'] = array(
418
-                            'Transaction.Registration.REG_ID' => $request_value,
419
-                        );
420
-                        break;
421
-                    case 'EVT_ID':
422
-                        $query_params[0]['AND**filter_by']['OR**filter_by_EVT_ID'] = array(
423
-                            'Transaction.Registration.EVT_ID' => $request_value,
424
-                        );
425
-                        break;
426
-                    default:
427
-                        $query_params[0]['AND**filter_by'][ 'OR**filter_by_' . $request_key ][ $model_name . '.' . $request_key ] = $request_value;
428
-                        break;
429
-                }
430
-            }
431
-        }
432
-        return $query_params;
433
-    }
434
-
435
-
436
-    /**
437
-     * @return string
438
-     */
439
-    public function get_pretty_label_for_results()
440
-    {
441
-        $expected_vars = $this->_expected_vars_for_query_inject();
442
-        $pretty_label  = '';
443
-        $label_parts   = array();
444
-        foreach ($expected_vars as $request_key => $model_name) {
445
-            $model = EE_Registry::instance()->load_model($model_name);
446
-            if ($model_field_value = EE_Registry::instance()->REQ->get($request_key)) {
447
-                switch ($request_key) {
448
-                    case '_REG_ID':
449
-                        $label_parts[] = sprintf(
450
-                            esc_html__('Registration with the ID: %s', 'event_espresso'),
451
-                            $model_field_value
452
-                        );
453
-                        break;
454
-                    case 'ATT_ID':
455
-                        /** @var EE_Attendee $attendee */
456
-                        $attendee      = $model->get_one_by_ID($model_field_value);
457
-                        $label_parts[] = $attendee instanceof EE_Attendee
458
-                            ? sprintf(esc_html__('Attendee %s', 'event_espresso'), $attendee->full_name())
459
-                            : sprintf(esc_html__('Attendee ID: %s', 'event_espresso'), $model_field_value);
460
-                        break;
461
-                    case 'ID':
462
-                        /** @var EE_WP_User $wpUser */
463
-                        $wpUser        = $model->get_one_by_ID($model_field_value);
464
-                        $label_parts[] = $wpUser instanceof EE_WP_User
465
-                            ? sprintf(esc_html__('WP User: %s', 'event_espresso'), $wpUser->name())
466
-                            : sprintf(esc_html__('WP User ID: %s', 'event_espresso'), $model_field_value);
467
-                        break;
468
-                    case 'TXN_ID':
469
-                        $label_parts[] = sprintf(
470
-                            esc_html__('Transaction with the ID: %s', 'event_espresso'),
471
-                            $model_field_value
472
-                        );
473
-                        break;
474
-                    case 'EVT_ID':
475
-                        /** @var EE_Event $Event */
476
-                        $Event         = $model->get_one_by_ID($model_field_value);
477
-                        $label_parts[] = $Event instanceof EE_Event
478
-                            ? sprintf(esc_html__('for the Event: %s', 'event_espresso'), $Event->name())
479
-                            : sprintf(esc_html__('for the Event with ID: %s', 'event_espresso'), $model_field_value);
480
-                        break;
481
-                }
482
-            }
483
-        }
484
-
485
-        if ($label_parts) {
486
-            // prepend to the last element of $label_parts an "and".
487
-            if (count($label_parts) > 1) {
488
-                $label_parts_index_to_prepend               = count($label_parts) - 1;
489
-                $label_parts[ $label_parts_index_to_prepend ] = 'and' . $label_parts[ $label_parts_index_to_prepend ];
490
-            }
491
-
492
-            $pretty_label .= sprintf(
493
-                esc_html_x(
494
-                    'Showing messages for %s',
495
-                    'A label for the messages returned in a query that are filtered by items in the query. This could be Transaction, Event, Attendee, Registration, or WP_User.',
496
-                    'event_espresso'
497
-                ),
498
-                implode(', ', $label_parts)
499
-            );
500
-        }
501
-        return $pretty_label;
502
-    }
503
-
504
-
505
-    /**
506
-     * This returns the array of expected variables for the EEI_Query_Filter methods being implemented
507
-     * The array is in the format:
508
-     * array(
509
-     *  {$field_name} => {$model_name}
510
-     * );
511
-     *
512
-     * @since 4.9.0
513
-     * @return array
514
-     */
515
-    protected function _expected_vars_for_query_inject()
516
-    {
517
-        return array(
518
-            '_REG_ID' => 'Registration',
519
-            'ATT_ID'  => 'Attendee',
520
-            'ID'      => 'WP_User',
521
-            'TXN_ID'  => 'Transaction',
522
-            'EVT_ID'  => 'Event',
523
-        );
524
-    }
525
-
526
-
527
-    /**
528
-     * This returns whether EEM_Message is in debug mode or not.
529
-     * Currently "debug mode" is used to control the handling of the EEM_Message::debug_only status when
530
-     * generating/sending messages. Debug mode can be set by either:
531
-     * 1. Sending in a value for the $set_debug argument
532
-     * 2. Defining `EE_DEBUG_MESSAGES` constant in wp-config.php
533
-     * 3. Overriding the above via the provided filter.
534
-     *
535
-     * @param bool|null $set_debug      If provided, then the debug mode will be set internally until reset via the
536
-     *                                  provided boolean. When no argument is provided (default null) then the debug
537
-     *                                  mode will be returned.
538
-     * @return bool         true means Messages is in debug mode.  false means messages system is not in debug mode.
539
-     */
540
-    public static function debug($set_debug = null)
541
-    {
542
-        static $is_debugging = null;
543
-
544
-        // initialize (use constant if set).
545
-        if (is_null($set_debug) && is_null($is_debugging)) {
546
-            $is_debugging = defined('EE_DEBUG_MESSAGES') && EE_DEBUG_MESSAGES;
547
-        }
548
-
549
-        if (! is_null($set_debug)) {
550
-            $is_debugging = filter_var($set_debug, FILTER_VALIDATE_BOOLEAN);
551
-        }
552
-
553
-        // return filtered value
554
-        return apply_filters('FHEE__EEM_Message__debug', $is_debugging);
555
-    }
556
-
557
-
558
-    /**
559
-     * Deletes old messages meeting certain criteria for removal from the database.
560
-     * By default, this will delete messages that:
561
-     * - are older than the value of the delete_threshold in months.
562
-     * - have a STS_ID other than EEM_Message::status_idle
563
-     *
564
-     * @param int $delete_threshold  This integer will be used to set the boundary for what messages are deleted in months.
565
-     * @return bool|false|int Either the number of records affected or false if there was an error (you can call
566
-     *                         $wpdb->last_error to find out what the error was.
567
-     */
568
-    public function delete_old_messages($delete_threshold = 6)
569
-    {
570
-        $number_deleted = 0;
571
-        /**
572
-         * Allows code to change the boundary for what messages are kept.
573
-         * Uses the value of the `delete_threshold` variable by default.
574
-         *
575
-         * @param int $seconds seconds that will be subtracted from the timestamp for now.
576
-         * @return int
577
-         */
578
-        $time_to_leave_alone = absint(
579
-            apply_filters(
580
-                'FHEE__EEM_Message__delete_old_messages__time_to_leave_alone',
581
-                ((int) $delete_threshold) * MONTH_IN_SECONDS
582
-            )
583
-        );
584
-
585
-
586
-        /**
587
-         * Allows code to change what message stati are ignored when deleting.
588
-         * Defaults to only ignore EEM_Message::status_idle messages.
589
-         *
590
-         * @param string $message_stati_to_keep  An array of message statuses that will be ignored when deleting.
591
-         */
592
-        $message_stati_to_keep = (array) apply_filters(
593
-            'FHEE__EEM_Message__delete_old_messages__message_stati_to_keep',
594
-            array(
595
-                EEM_Message::status_idle
596
-            )
597
-        );
598
-
599
-        // first get all the ids of messages being deleted
600
-        $message_ids_to_delete = EEM_Message::instance()->get_col(
601
-            array(
602
-                0 => array(
603
-                    'STS_ID' => array('NOT_IN', $message_stati_to_keep),
604
-                    'MSG_modified' => array('<', time() - $time_to_leave_alone)
605
-                ),
606
-                'limit' => apply_filters(
607
-                    'EEM_Message__delete_old_messages__limit',
608
-                    2000,
609
-                    $delete_threshold
610
-                )
611
-            )
612
-        );
613
-
614
-        if (! empty($message_ids_to_delete) && is_array($message_ids_to_delete)) {
615
-            global $wpdb;
616
-            $number_deleted = $wpdb->query('
16
+	/**
17
+	 * This priority indicates a message should be generated and sent ASAP
18
+	 *
19
+	 * @type int
20
+	 */
21
+	const priority_high = 10;
22
+
23
+
24
+	/**
25
+	 * This priority indicates a message should be generated ASAP and queued for sending.
26
+	 *
27
+	 * @type
28
+	 */
29
+	const priority_medium = 20;
30
+
31
+
32
+	/**
33
+	 * This priority indicates a message should be queued for generating.
34
+	 *
35
+	 * @type int
36
+	 */
37
+	const priority_low = 30;
38
+
39
+
40
+	/**
41
+	 * indicates this message was sent at the time modified
42
+	 */
43
+	const status_sent = 'MSN';
44
+
45
+
46
+	/**
47
+	 * indicates this message is waiting to be sent
48
+	 */
49
+	const status_idle = 'MID';
50
+
51
+
52
+	/**
53
+	 * indicates an attempt was a made to send this message
54
+	 * at the scheduled time, but it failed at the time modified.  This differs from MDO status in that it will ALWAYS
55
+	 * appear to the end user.
56
+	 */
57
+	const status_failed = 'MFL';
58
+
59
+
60
+	/**
61
+	 * indicates the message has been flagged for resending (at the time modified).
62
+	 */
63
+	const status_resend = 'MRS';
64
+
65
+
66
+	/**
67
+	 * indicates the message has been flagged for generation but has not been generated yet.  Messages always start as
68
+	 * this status when added to the queue.
69
+	 */
70
+	const status_incomplete = 'MIC';
71
+
72
+
73
+	/**
74
+	 * Indicates everything was generated fine for the message, however, the messenger was unable to send.
75
+	 * This status means that its possible to retry sending the message.
76
+	 */
77
+	const status_retry = 'MRT';
78
+
79
+
80
+	/**
81
+	 * This is used for more informational messages that may not indicate anything is broken but still cannot be
82
+	 * generated or sent correctly. An example of a message that would get flagged this way would be when a not
83
+	 * approved message was queued for generation, but at time of generation, the attached registration(s) are
84
+	 * approved. So the message queued for generation is no longer valid.  Messages for this status will only persist
85
+	 * in the db and be viewable in the message activity list table when the messages system is in debug mode.
86
+	 *
87
+	 * @see EEM_Message::debug()
88
+	 */
89
+	const status_debug_only = 'MDO';
90
+
91
+
92
+	/**
93
+	 * This status is given to messages it is processed by the messenger send method.
94
+	 * Messages with this status should rarely be seen in the Message List table, but if they are, that's usually
95
+	 * indicative of a PHP timeout or memory limit issue.
96
+	 */
97
+	const status_messenger_executing = 'MEX';
98
+
99
+
100
+	/**
101
+	 *    Private constructor to prevent direct creation.
102
+	 *
103
+	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and
104
+	 *                         any incoming timezone data that gets saved).  Note this just sends the timezone info to
105
+	 *                         the date time model field objects.  Default is null (and will be assumed using the set
106
+	 *                         timezone in the 'timezone_string' wp option)
107
+	 * @return EEM_Message
108
+	 */
109
+	protected function __construct($timezone = null)
110
+	{
111
+		$this->singular_item = __('Message', 'event_espresso');
112
+		$this->plural_item   = __('Messages', 'event_espresso');
113
+
114
+		// used for token generator
115
+		EE_Registry::instance()->load_helper('URL');
116
+
117
+		$this->_tables = array(
118
+			'Message' => new EE_Primary_Table('esp_message', 'MSG_ID'),
119
+		);
120
+
121
+		$allowed_priority = array(
122
+			self::priority_high   => __('high', 'event_espresso'),
123
+			self::priority_medium => __('medium', 'event_espresso'),
124
+			self::priority_low    => __('low', 'event_espresso'),
125
+		);
126
+
127
+		$this->_fields          = array(
128
+			'Message' => array(
129
+				'MSG_ID'             => new EE_Primary_Key_Int_Field('MSG_ID', __('Message ID', 'event_espresso')),
130
+				'MSG_token'          => new EE_Plain_Text_Field(
131
+					'MSG_token',
132
+					__(
133
+						'Unique Token used to represent this row in publicly viewable contexts (eg. a url).',
134
+						'event_espresso'
135
+					),
136
+					false,
137
+					EEH_URL::generate_unique_token()
138
+				),
139
+				'GRP_ID'             => new EE_Foreign_Key_Int_Field(
140
+					'GRP_ID',
141
+					__('Foreign key to the EEM_Message_Template_Group table.', 'event_espresso'),
142
+					true,
143
+					0,
144
+					'Message_Template_Group'
145
+				),
146
+				'TXN_ID'             => new EE_Foreign_Key_Int_Field(
147
+					'TXN_ID',
148
+					__(
149
+						'Foreign key to the related EE_Transaction.  This is required to give context for regenerating the specific message',
150
+						'event_espresso'
151
+					),
152
+					true,
153
+					0,
154
+					'Transaction'
155
+				),
156
+				'MSG_messenger'      => new EE_Plain_Text_Field(
157
+					'MSG_messenger',
158
+					__(
159
+						'Corresponds to the EE_messenger::name used to send this message. This will also be used to attempt any resending of the message.',
160
+						'event_espresso'
161
+					),
162
+					false,
163
+					'email'
164
+				),
165
+				'MSG_message_type'   => new EE_Plain_Text_Field(
166
+					'MSG_message_type',
167
+					__('Corresponds to the EE_message_type::name used to generate this message.', 'event_espresso'),
168
+					false,
169
+					'receipt'
170
+				),
171
+				'MSG_context'        => new EE_Plain_Text_Field('MSG_context', __('Context', 'event_espresso'), false),
172
+				'MSG_recipient_ID'   => new EE_Foreign_Key_Int_Field(
173
+					'MSG_recipient_ID',
174
+					__('Recipient ID', 'event_espresso'),
175
+					true,
176
+					null,
177
+					array('Registration', 'Attendee', 'WP_User')
178
+				),
179
+				'MSG_recipient_type' => new EE_Any_Foreign_Model_Name_Field(
180
+					'MSG_recipient_type',
181
+					__('Recipient Type', 'event_espresso'),
182
+					true,
183
+					null,
184
+					array('Registration', 'Attendee', 'WP_User')
185
+				),
186
+				'MSG_content'        => new EE_Maybe_Serialized_Text_Field(
187
+					'MSG_content',
188
+					__('Content', 'event_espresso'),
189
+					true,
190
+					''
191
+				),
192
+				'MSG_to'             => new EE_Maybe_Serialized_Text_Field(
193
+					'MSG_to',
194
+					__('Address To', 'event_espresso'),
195
+					true
196
+				),
197
+				'MSG_from'           => new EE_Maybe_Serialized_Text_Field(
198
+					'MSG_from',
199
+					__('Address From', 'event_espresso'),
200
+					true
201
+				),
202
+				'MSG_subject'        => new EE_Maybe_Serialized_Text_Field(
203
+					'MSG_subject',
204
+					__('Subject', 'event_espresso'),
205
+					true,
206
+					''
207
+				),
208
+				'MSG_priority'       => new EE_Enum_Integer_Field(
209
+					'MSG_priority',
210
+					__('Priority', 'event_espresso'),
211
+					false,
212
+					self::priority_low,
213
+					$allowed_priority
214
+				),
215
+				'STS_ID'             => new EE_Foreign_Key_String_Field(
216
+					'STS_ID',
217
+					__('Status', 'event_espresso'),
218
+					false,
219
+					self::status_incomplete,
220
+					'Status'
221
+				),
222
+				'MSG_created'        => new EE_Datetime_Field(
223
+					'MSG_created',
224
+					__('Created', 'event_espresso'),
225
+					false,
226
+					EE_Datetime_Field::now
227
+				),
228
+				'MSG_modified'       => new EE_Datetime_Field(
229
+					'MSG_modified',
230
+					__('Modified', 'event_espresso'),
231
+					true,
232
+					EE_Datetime_Field::now
233
+				),
234
+			),
235
+		);
236
+		$this->_model_relations = array(
237
+			'Attendee'               => new EE_Belongs_To_Any_Relation(),
238
+			'Registration'           => new EE_Belongs_To_Any_Relation(),
239
+			'WP_User'                => new EE_Belongs_To_Any_Relation(),
240
+			'Message_Template_Group' => new EE_Belongs_To_Relation(),
241
+			'Transaction'            => new EE_Belongs_To_Relation(),
242
+		);
243
+		parent::__construct($timezone);
244
+	}
245
+
246
+
247
+	/**
248
+	 * @return \EE_Message
249
+	 */
250
+	public function create_default_object()
251
+	{
252
+		/** @type EE_Message $message */
253
+		$message = parent::create_default_object();
254
+		if ($message instanceof EE_Message) {
255
+			return EE_Message_Factory::set_messenger_and_message_type($message);
256
+		}
257
+		return null;
258
+	}
259
+
260
+
261
+	/**
262
+	 * @param mixed $cols_n_values
263
+	 * @return \EE_Message
264
+	 */
265
+	public function instantiate_class_from_array_or_object($cols_n_values)
266
+	{
267
+		/** @type EE_Message $message */
268
+		$message = parent::instantiate_class_from_array_or_object($cols_n_values);
269
+		if ($message instanceof EE_Message) {
270
+			return EE_Message_Factory::set_messenger_and_message_type($message);
271
+		}
272
+		return null;
273
+	}
274
+
275
+
276
+	/**
277
+	 * Returns whether or not a message of that type was sent for a given attendee.
278
+	 *
279
+	 * @param EE_Attendee|int $attendee
280
+	 * @param string          $message_type the message type slug
281
+	 * @return boolean
282
+	 */
283
+	public function message_sent_for_attendee($attendee, $message_type)
284
+	{
285
+		$attendee_ID = EEM_Attendee::instance()->ensure_is_ID($attendee);
286
+		return $this->exists(array(
287
+			array(
288
+				'Attendee.ATT_ID'  => $attendee_ID,
289
+				'MSG_message_type' => $message_type,
290
+				'STS_ID'           => array('IN', $this->stati_indicating_sent()),
291
+			),
292
+		));
293
+	}
294
+
295
+
296
+	/**
297
+	 * Returns whether or not a message of that type was sent for a given registration
298
+	 *
299
+	 * @param EE_Registration|int $registration
300
+	 * @param string              $message_type the message type slug
301
+	 * @return boolean
302
+	 */
303
+	public function message_sent_for_registration($registration, $message_type)
304
+	{
305
+		$registrationID = EEM_Registration::instance()->ensure_is_ID($registration);
306
+		return $this->exists(array(
307
+			array(
308
+				'Registration.REG_ID' => $registrationID,
309
+				'MSG_message_type'    => $message_type,
310
+				'STS_ID'              => array('IN', $this->stati_indicating_sent()),
311
+			),
312
+		));
313
+	}
314
+
315
+
316
+	/**
317
+	 * This retrieves an EE_Message object from the db matching the given token string.
318
+	 *
319
+	 * @param string $token
320
+	 * @return EE_Message
321
+	 */
322
+	public function get_one_by_token($token)
323
+	{
324
+		return $this->get_one(array(
325
+			array(
326
+				'MSG_token' => $token,
327
+			),
328
+		));
329
+	}
330
+
331
+
332
+	/**
333
+	 * Returns stati that indicate the message HAS been sent
334
+	 *
335
+	 * @return array of strings for possible stati
336
+	 */
337
+	public function stati_indicating_sent()
338
+	{
339
+		return apply_filters('FHEE__EEM_Message__stati_indicating_sent', array(self::status_sent));
340
+	}
341
+
342
+
343
+	/**
344
+	 * Returns stati that indicate the message is waiting to be sent.
345
+	 *
346
+	 * @return array of strings for possible stati.
347
+	 */
348
+	public function stati_indicating_to_send()
349
+	{
350
+		return apply_filters(
351
+			'FHEE__EEM_Message__stati_indicating_to_send',
352
+			array(self::status_idle, self::status_resend)
353
+		);
354
+	}
355
+
356
+
357
+	/**
358
+	 * Returns stati that indicate the message has failed sending
359
+	 *
360
+	 * @return array  array of strings for possible stati.
361
+	 */
362
+	public function stati_indicating_failed_sending()
363
+	{
364
+		$failed_stati = array(
365
+			self::status_failed,
366
+			self::status_retry,
367
+			self::status_messenger_executing,
368
+		);
369
+		// if WP_DEBUG is set, then let's include debug_only fails
370
+		if (WP_DEBUG) {
371
+			$failed_stati[] = self::status_debug_only;
372
+		}
373
+		return apply_filters('FHEE__EEM_Message__stati_indicating_failed_sending', $failed_stati);
374
+	}
375
+
376
+
377
+	/**
378
+	 * Returns filterable array of all EEM_Message statuses.
379
+	 *
380
+	 * @return array
381
+	 */
382
+	public function all_statuses()
383
+	{
384
+		return apply_filters(
385
+			'FHEE__EEM_Message__all_statuses',
386
+			array(
387
+				EEM_Message::status_sent,
388
+				EEM_Message::status_incomplete,
389
+				EEM_Message::status_idle,
390
+				EEM_Message::status_resend,
391
+				EEM_Message::status_retry,
392
+				EEM_Message::status_failed,
393
+				EEM_Message::status_messenger_executing,
394
+				EEM_Message::status_debug_only,
395
+			)
396
+		);
397
+	}
398
+
399
+	/**
400
+	 * Detects any specific query variables in the request and uses those to setup appropriate
401
+	 * filter for any queries.
402
+	 *
403
+	 * @return array
404
+	 */
405
+	public function filter_by_query_params()
406
+	{
407
+		// expected possible query_vars, the key in this array matches an expected key in the request,
408
+		// the value, matches the corresponding EEM_Base child reference.
409
+		$expected_vars   = $this->_expected_vars_for_query_inject();
410
+		$query_params[0] = array();
411
+		foreach ($expected_vars as $request_key => $model_name) {
412
+			$request_value = EE_Registry::instance()->REQ->get($request_key);
413
+			if ($request_value) {
414
+				// special case
415
+				switch ($request_key) {
416
+					case '_REG_ID':
417
+						$query_params[0]['AND**filter_by']['OR**filter_by_REG_ID'] = array(
418
+							'Transaction.Registration.REG_ID' => $request_value,
419
+						);
420
+						break;
421
+					case 'EVT_ID':
422
+						$query_params[0]['AND**filter_by']['OR**filter_by_EVT_ID'] = array(
423
+							'Transaction.Registration.EVT_ID' => $request_value,
424
+						);
425
+						break;
426
+					default:
427
+						$query_params[0]['AND**filter_by'][ 'OR**filter_by_' . $request_key ][ $model_name . '.' . $request_key ] = $request_value;
428
+						break;
429
+				}
430
+			}
431
+		}
432
+		return $query_params;
433
+	}
434
+
435
+
436
+	/**
437
+	 * @return string
438
+	 */
439
+	public function get_pretty_label_for_results()
440
+	{
441
+		$expected_vars = $this->_expected_vars_for_query_inject();
442
+		$pretty_label  = '';
443
+		$label_parts   = array();
444
+		foreach ($expected_vars as $request_key => $model_name) {
445
+			$model = EE_Registry::instance()->load_model($model_name);
446
+			if ($model_field_value = EE_Registry::instance()->REQ->get($request_key)) {
447
+				switch ($request_key) {
448
+					case '_REG_ID':
449
+						$label_parts[] = sprintf(
450
+							esc_html__('Registration with the ID: %s', 'event_espresso'),
451
+							$model_field_value
452
+						);
453
+						break;
454
+					case 'ATT_ID':
455
+						/** @var EE_Attendee $attendee */
456
+						$attendee      = $model->get_one_by_ID($model_field_value);
457
+						$label_parts[] = $attendee instanceof EE_Attendee
458
+							? sprintf(esc_html__('Attendee %s', 'event_espresso'), $attendee->full_name())
459
+							: sprintf(esc_html__('Attendee ID: %s', 'event_espresso'), $model_field_value);
460
+						break;
461
+					case 'ID':
462
+						/** @var EE_WP_User $wpUser */
463
+						$wpUser        = $model->get_one_by_ID($model_field_value);
464
+						$label_parts[] = $wpUser instanceof EE_WP_User
465
+							? sprintf(esc_html__('WP User: %s', 'event_espresso'), $wpUser->name())
466
+							: sprintf(esc_html__('WP User ID: %s', 'event_espresso'), $model_field_value);
467
+						break;
468
+					case 'TXN_ID':
469
+						$label_parts[] = sprintf(
470
+							esc_html__('Transaction with the ID: %s', 'event_espresso'),
471
+							$model_field_value
472
+						);
473
+						break;
474
+					case 'EVT_ID':
475
+						/** @var EE_Event $Event */
476
+						$Event         = $model->get_one_by_ID($model_field_value);
477
+						$label_parts[] = $Event instanceof EE_Event
478
+							? sprintf(esc_html__('for the Event: %s', 'event_espresso'), $Event->name())
479
+							: sprintf(esc_html__('for the Event with ID: %s', 'event_espresso'), $model_field_value);
480
+						break;
481
+				}
482
+			}
483
+		}
484
+
485
+		if ($label_parts) {
486
+			// prepend to the last element of $label_parts an "and".
487
+			if (count($label_parts) > 1) {
488
+				$label_parts_index_to_prepend               = count($label_parts) - 1;
489
+				$label_parts[ $label_parts_index_to_prepend ] = 'and' . $label_parts[ $label_parts_index_to_prepend ];
490
+			}
491
+
492
+			$pretty_label .= sprintf(
493
+				esc_html_x(
494
+					'Showing messages for %s',
495
+					'A label for the messages returned in a query that are filtered by items in the query. This could be Transaction, Event, Attendee, Registration, or WP_User.',
496
+					'event_espresso'
497
+				),
498
+				implode(', ', $label_parts)
499
+			);
500
+		}
501
+		return $pretty_label;
502
+	}
503
+
504
+
505
+	/**
506
+	 * This returns the array of expected variables for the EEI_Query_Filter methods being implemented
507
+	 * The array is in the format:
508
+	 * array(
509
+	 *  {$field_name} => {$model_name}
510
+	 * );
511
+	 *
512
+	 * @since 4.9.0
513
+	 * @return array
514
+	 */
515
+	protected function _expected_vars_for_query_inject()
516
+	{
517
+		return array(
518
+			'_REG_ID' => 'Registration',
519
+			'ATT_ID'  => 'Attendee',
520
+			'ID'      => 'WP_User',
521
+			'TXN_ID'  => 'Transaction',
522
+			'EVT_ID'  => 'Event',
523
+		);
524
+	}
525
+
526
+
527
+	/**
528
+	 * This returns whether EEM_Message is in debug mode or not.
529
+	 * Currently "debug mode" is used to control the handling of the EEM_Message::debug_only status when
530
+	 * generating/sending messages. Debug mode can be set by either:
531
+	 * 1. Sending in a value for the $set_debug argument
532
+	 * 2. Defining `EE_DEBUG_MESSAGES` constant in wp-config.php
533
+	 * 3. Overriding the above via the provided filter.
534
+	 *
535
+	 * @param bool|null $set_debug      If provided, then the debug mode will be set internally until reset via the
536
+	 *                                  provided boolean. When no argument is provided (default null) then the debug
537
+	 *                                  mode will be returned.
538
+	 * @return bool         true means Messages is in debug mode.  false means messages system is not in debug mode.
539
+	 */
540
+	public static function debug($set_debug = null)
541
+	{
542
+		static $is_debugging = null;
543
+
544
+		// initialize (use constant if set).
545
+		if (is_null($set_debug) && is_null($is_debugging)) {
546
+			$is_debugging = defined('EE_DEBUG_MESSAGES') && EE_DEBUG_MESSAGES;
547
+		}
548
+
549
+		if (! is_null($set_debug)) {
550
+			$is_debugging = filter_var($set_debug, FILTER_VALIDATE_BOOLEAN);
551
+		}
552
+
553
+		// return filtered value
554
+		return apply_filters('FHEE__EEM_Message__debug', $is_debugging);
555
+	}
556
+
557
+
558
+	/**
559
+	 * Deletes old messages meeting certain criteria for removal from the database.
560
+	 * By default, this will delete messages that:
561
+	 * - are older than the value of the delete_threshold in months.
562
+	 * - have a STS_ID other than EEM_Message::status_idle
563
+	 *
564
+	 * @param int $delete_threshold  This integer will be used to set the boundary for what messages are deleted in months.
565
+	 * @return bool|false|int Either the number of records affected or false if there was an error (you can call
566
+	 *                         $wpdb->last_error to find out what the error was.
567
+	 */
568
+	public function delete_old_messages($delete_threshold = 6)
569
+	{
570
+		$number_deleted = 0;
571
+		/**
572
+		 * Allows code to change the boundary for what messages are kept.
573
+		 * Uses the value of the `delete_threshold` variable by default.
574
+		 *
575
+		 * @param int $seconds seconds that will be subtracted from the timestamp for now.
576
+		 * @return int
577
+		 */
578
+		$time_to_leave_alone = absint(
579
+			apply_filters(
580
+				'FHEE__EEM_Message__delete_old_messages__time_to_leave_alone',
581
+				((int) $delete_threshold) * MONTH_IN_SECONDS
582
+			)
583
+		);
584
+
585
+
586
+		/**
587
+		 * Allows code to change what message stati are ignored when deleting.
588
+		 * Defaults to only ignore EEM_Message::status_idle messages.
589
+		 *
590
+		 * @param string $message_stati_to_keep  An array of message statuses that will be ignored when deleting.
591
+		 */
592
+		$message_stati_to_keep = (array) apply_filters(
593
+			'FHEE__EEM_Message__delete_old_messages__message_stati_to_keep',
594
+			array(
595
+				EEM_Message::status_idle
596
+			)
597
+		);
598
+
599
+		// first get all the ids of messages being deleted
600
+		$message_ids_to_delete = EEM_Message::instance()->get_col(
601
+			array(
602
+				0 => array(
603
+					'STS_ID' => array('NOT_IN', $message_stati_to_keep),
604
+					'MSG_modified' => array('<', time() - $time_to_leave_alone)
605
+				),
606
+				'limit' => apply_filters(
607
+					'EEM_Message__delete_old_messages__limit',
608
+					2000,
609
+					$delete_threshold
610
+				)
611
+			)
612
+		);
613
+
614
+		if (! empty($message_ids_to_delete) && is_array($message_ids_to_delete)) {
615
+			global $wpdb;
616
+			$number_deleted = $wpdb->query('
617 617
                 DELETE
618 618
                 FROM ' . $this->table() . '
619 619
                 WHERE
620 620
                     MSG_ID IN (' . implode(",", $message_ids_to_delete) . ')
621 621
             ');
622
-        }
623
-
624
-        /**
625
-         * This will get called if the number of records deleted 0 or greater.  So a successful deletion is one where
626
-         * there were no errors.  An unsuccessful deletion is where there were errors.  Keep that in mind for the actions
627
-         * below.
628
-         */
629
-        if ($number_deleted !== false) {
630
-            do_action('AHEE__EEM_Message__delete_old_messages__after_successful_deletion', $message_ids_to_delete, $number_deleted);
631
-        } else {
632
-            do_action('AHEE__EEM_Message__delete_old_messages__after_deletion_fail', $message_ids_to_delete, $number_deleted);
633
-        }
634
-        return $number_deleted;
635
-    }
622
+		}
623
+
624
+		/**
625
+		 * This will get called if the number of records deleted 0 or greater.  So a successful deletion is one where
626
+		 * there were no errors.  An unsuccessful deletion is where there were errors.  Keep that in mind for the actions
627
+		 * below.
628
+		 */
629
+		if ($number_deleted !== false) {
630
+			do_action('AHEE__EEM_Message__delete_old_messages__after_successful_deletion', $message_ids_to_delete, $number_deleted);
631
+		} else {
632
+			do_action('AHEE__EEM_Message__delete_old_messages__after_deletion_fail', $message_ids_to_delete, $number_deleted);
633
+		}
634
+		return $number_deleted;
635
+	}
636 636
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Transaction.model.php 2 patches
Indentation   +452 added lines, -452 removed lines patch added patch discarded remove patch
@@ -16,230 +16,230 @@  discard block
 block discarded – undo
16 16
 class EEM_Transaction extends EEM_Base
17 17
 {
18 18
 
19
-    // private instance of the Transaction object
20
-    protected static $_instance;
21
-
22
-    /**
23
-     * Status ID(STS_ID on esp_status table) to indicate the transaction is complete,
24
-     * but payment is pending. This is the state for transactions where payment is promised
25
-     * from an offline gateway.
26
-     */
27
-    //  const open_status_code = 'TPN';
28
-
29
-    /**
30
-     * Status ID(STS_ID on esp_status table) to indicate the transaction failed,
31
-     * either due to a technical reason (server or computer crash during registration),
32
-     *  or some other reason that prevent the collection of any useful contact information from any of the registrants
33
-     */
34
-    const failed_status_code = 'TFL';
35
-
36
-    /**
37
-     * Status ID(STS_ID on esp_status table) to indicate the transaction was abandoned,
38
-     * either due to a technical reason (server or computer crash during registration),
39
-     * or due to an abandoned cart after registrant chose not to complete the registration process
40
-     * HOWEVER...
41
-     * an abandoned TXN differs from a failed TXN in that it was able to capture contact information for at least one
42
-     * registrant
43
-     */
44
-    const abandoned_status_code = 'TAB';
45
-
46
-    /**
47
-     * Status ID(STS_ID on esp_status table) to indicate an incomplete transaction,
48
-     * meaning that monies are still owing: TXN_paid < TXN_total
49
-     */
50
-    const incomplete_status_code = 'TIN';
51
-
52
-    /**
53
-     * Status ID (STS_ID on esp_status table) to indicate a complete transaction.
54
-     * meaning that NO monies are owing: TXN_paid == TXN_total
55
-     */
56
-    const complete_status_code = 'TCM';
57
-
58
-    /**
59
-     *  Status ID(STS_ID on esp_status table) to indicate the transaction is overpaid.
60
-     *  This is the same as complete, but site admins actually owe clients the moneys!  TXN_paid > TXN_total
61
-     */
62
-    const overpaid_status_code = 'TOP';
63
-
64
-
65
-    /**
66
-     *    private constructor to prevent direct creation
67
-     *
68
-     * @Constructor
69
-     * @access protected
70
-     *
71
-     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any
72
-     *                         incoming timezone data that gets saved). Note this just sends the timezone info to the
73
-     *                         date time model field objects.  Default is NULL (and will be assumed using the set
74
-     *                         timezone in the 'timezone_string' wp option)
75
-     *
76
-     * @return EEM_Transaction
77
-     * @throws \EE_Error
78
-     */
79
-    protected function __construct($timezone)
80
-    {
81
-        $this->singular_item = __('Transaction', 'event_espresso');
82
-        $this->plural_item   = __('Transactions', 'event_espresso');
83
-
84
-        $this->_tables                 = array(
85
-            'TransactionTable' => new EE_Primary_Table('esp_transaction', 'TXN_ID')
86
-        );
87
-        $this->_fields                 = array(
88
-            'TransactionTable' => array(
89
-                'TXN_ID'           => new EE_Primary_Key_Int_Field('TXN_ID', __('Transaction ID', 'event_espresso')),
90
-                'TXN_timestamp'    => new EE_Datetime_Field(
91
-                    'TXN_timestamp',
92
-                    __('date when transaction was created', 'event_espresso'),
93
-                    false,
94
-                    EE_Datetime_Field::now,
95
-                    $timezone
96
-                ),
97
-                'TXN_total'        => new EE_Money_Field(
98
-                    'TXN_total',
99
-                    __('Total value of Transaction', 'event_espresso'),
100
-                    false,
101
-                    0
102
-                ),
103
-                'TXN_paid'         => new EE_Money_Field(
104
-                    'TXN_paid',
105
-                    __('Amount paid towards transaction to date', 'event_espresso'),
106
-                    false,
107
-                    0
108
-                ),
109
-                'STS_ID'           => new EE_Foreign_Key_String_Field(
110
-                    'STS_ID',
111
-                    __('Status ID', 'event_espresso'),
112
-                    false,
113
-                    EEM_Transaction::failed_status_code,
114
-                    'Status'
115
-                ),
116
-                'TXN_session_data' => new EE_Serialized_Text_Field(
117
-                    'TXN_session_data',
118
-                    __('Serialized session data', 'event_espresso'),
119
-                    true,
120
-                    ''
121
-                ),
122
-                'TXN_hash_salt'    => new EE_Plain_Text_Field(
123
-                    'TXN_hash_salt',
124
-                    __('Transaction Hash Salt', 'event_espresso'),
125
-                    true,
126
-                    ''
127
-                ),
128
-                'PMD_ID'           => new EE_Foreign_Key_Int_Field(
129
-                    'PMD_ID',
130
-                    __("Last Used Payment Method", 'event_espresso'),
131
-                    true,
132
-                    null,
133
-                    'Payment_Method'
134
-                ),
135
-                'TXN_reg_steps'    => new EE_Serialized_Text_Field(
136
-                    'TXN_reg_steps',
137
-                    __('Registration Steps', 'event_espresso'),
138
-                    false,
139
-                    array()
140
-                ),
141
-            )
142
-        );
143
-        $this->_model_relations        = array(
144
-            'Registration'   => new EE_Has_Many_Relation(),
145
-            'Payment'        => new EE_Has_Many_Relation(),
146
-            'Status'         => new EE_Belongs_To_Relation(),
147
-            'Line_Item'      => new EE_Has_Many_Relation(false),
148
-            // you can delete a transaction without needing to delete its line items
149
-            'Payment_Method' => new EE_Belongs_To_Relation(),
150
-            'Message'        => new EE_Has_Many_Relation()
151
-        );
152
-        $this->_model_chain_to_wp_user = 'Registration.Event';
153
-        parent::__construct($timezone);
154
-    }
155
-
156
-
157
-    /**
158
-     *    txn_status_array
159
-     * get list of transaction statuses
160
-     *
161
-     * @access public
162
-     * @return array
163
-     */
164
-    public static function txn_status_array()
165
-    {
166
-        return apply_filters(
167
-            'FHEE__EEM_Transaction__txn_status_array',
168
-            array(
169
-                EEM_Transaction::overpaid_status_code,
170
-                EEM_Transaction::complete_status_code,
171
-                EEM_Transaction::incomplete_status_code,
172
-                EEM_Transaction::abandoned_status_code,
173
-                EEM_Transaction::failed_status_code,
174
-            )
175
-        );
176
-    }
177
-
178
-    /**
179
-     *        get the revenue per day  for the Transaction Admin page Reports Tab
180
-     *
181
-     * @access        public
182
-     *
183
-     * @param string $period
184
-     *
185
-     * @return \stdClass[]
186
-     */
187
-    public function get_revenue_per_day_report($period = '-1 month')
188
-    {
189
-        $sql_date = $this->convert_datetime_for_query(
190
-            'TXN_timestamp',
191
-            date('Y-m-d H:i:s', strtotime($period)),
192
-            'Y-m-d H:i:s',
193
-            'UTC'
194
-        );
195
-
196
-        $query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'TXN_timestamp');
197
-
198
-        return $this->_get_all_wpdb_results(
199
-            array(
200
-                array(
201
-                    'TXN_timestamp' => array('>=', $sql_date)
202
-                ),
203
-                'group_by' => 'txnDate',
204
-                'order_by' => array('TXN_timestamp' => 'ASC')
205
-            ),
206
-            OBJECT,
207
-            array(
208
-                'txnDate' => array('DATE(' . $query_interval . ')', '%s'),
209
-                'revenue' => array('SUM(TransactionTable.TXN_paid)', '%d')
210
-            )
211
-        );
212
-    }
213
-
214
-
215
-    /**
216
-     *        get the revenue per event  for the Transaction Admin page Reports Tab
217
-     *
218
-     * @access        public
219
-     *
220
-     * @param string $period
221
-     *
222
-     * @throws \EE_Error
223
-     * @return mixed
224
-     */
225
-    public function get_revenue_per_event_report($period = '-1 month')
226
-    {
227
-        global $wpdb;
228
-        $transaction_table          = $wpdb->prefix . 'esp_transaction';
229
-        $registration_table         = $wpdb->prefix . 'esp_registration';
230
-        $registration_payment_table = $wpdb->prefix . 'esp_registration_payment';
231
-        $event_table                = $wpdb->posts;
232
-        $payment_table              = $wpdb->prefix . 'esp_payment';
233
-        $sql_date                   = date('Y-m-d H:i:s', strtotime($period));
234
-        $approved_payment_status    = EEM_Payment::status_id_approved;
235
-        $extra_event_on_join        = '';
236
-        // exclude events not authored by user if permissions in effect
237
-        if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
238
-            $extra_event_on_join = ' AND Event.post_author = ' . get_current_user_id();
239
-        }
240
-
241
-        return $wpdb->get_results(
242
-            "SELECT
19
+	// private instance of the Transaction object
20
+	protected static $_instance;
21
+
22
+	/**
23
+	 * Status ID(STS_ID on esp_status table) to indicate the transaction is complete,
24
+	 * but payment is pending. This is the state for transactions where payment is promised
25
+	 * from an offline gateway.
26
+	 */
27
+	//  const open_status_code = 'TPN';
28
+
29
+	/**
30
+	 * Status ID(STS_ID on esp_status table) to indicate the transaction failed,
31
+	 * either due to a technical reason (server or computer crash during registration),
32
+	 *  or some other reason that prevent the collection of any useful contact information from any of the registrants
33
+	 */
34
+	const failed_status_code = 'TFL';
35
+
36
+	/**
37
+	 * Status ID(STS_ID on esp_status table) to indicate the transaction was abandoned,
38
+	 * either due to a technical reason (server or computer crash during registration),
39
+	 * or due to an abandoned cart after registrant chose not to complete the registration process
40
+	 * HOWEVER...
41
+	 * an abandoned TXN differs from a failed TXN in that it was able to capture contact information for at least one
42
+	 * registrant
43
+	 */
44
+	const abandoned_status_code = 'TAB';
45
+
46
+	/**
47
+	 * Status ID(STS_ID on esp_status table) to indicate an incomplete transaction,
48
+	 * meaning that monies are still owing: TXN_paid < TXN_total
49
+	 */
50
+	const incomplete_status_code = 'TIN';
51
+
52
+	/**
53
+	 * Status ID (STS_ID on esp_status table) to indicate a complete transaction.
54
+	 * meaning that NO monies are owing: TXN_paid == TXN_total
55
+	 */
56
+	const complete_status_code = 'TCM';
57
+
58
+	/**
59
+	 *  Status ID(STS_ID on esp_status table) to indicate the transaction is overpaid.
60
+	 *  This is the same as complete, but site admins actually owe clients the moneys!  TXN_paid > TXN_total
61
+	 */
62
+	const overpaid_status_code = 'TOP';
63
+
64
+
65
+	/**
66
+	 *    private constructor to prevent direct creation
67
+	 *
68
+	 * @Constructor
69
+	 * @access protected
70
+	 *
71
+	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any
72
+	 *                         incoming timezone data that gets saved). Note this just sends the timezone info to the
73
+	 *                         date time model field objects.  Default is NULL (and will be assumed using the set
74
+	 *                         timezone in the 'timezone_string' wp option)
75
+	 *
76
+	 * @return EEM_Transaction
77
+	 * @throws \EE_Error
78
+	 */
79
+	protected function __construct($timezone)
80
+	{
81
+		$this->singular_item = __('Transaction', 'event_espresso');
82
+		$this->plural_item   = __('Transactions', 'event_espresso');
83
+
84
+		$this->_tables                 = array(
85
+			'TransactionTable' => new EE_Primary_Table('esp_transaction', 'TXN_ID')
86
+		);
87
+		$this->_fields                 = array(
88
+			'TransactionTable' => array(
89
+				'TXN_ID'           => new EE_Primary_Key_Int_Field('TXN_ID', __('Transaction ID', 'event_espresso')),
90
+				'TXN_timestamp'    => new EE_Datetime_Field(
91
+					'TXN_timestamp',
92
+					__('date when transaction was created', 'event_espresso'),
93
+					false,
94
+					EE_Datetime_Field::now,
95
+					$timezone
96
+				),
97
+				'TXN_total'        => new EE_Money_Field(
98
+					'TXN_total',
99
+					__('Total value of Transaction', 'event_espresso'),
100
+					false,
101
+					0
102
+				),
103
+				'TXN_paid'         => new EE_Money_Field(
104
+					'TXN_paid',
105
+					__('Amount paid towards transaction to date', 'event_espresso'),
106
+					false,
107
+					0
108
+				),
109
+				'STS_ID'           => new EE_Foreign_Key_String_Field(
110
+					'STS_ID',
111
+					__('Status ID', 'event_espresso'),
112
+					false,
113
+					EEM_Transaction::failed_status_code,
114
+					'Status'
115
+				),
116
+				'TXN_session_data' => new EE_Serialized_Text_Field(
117
+					'TXN_session_data',
118
+					__('Serialized session data', 'event_espresso'),
119
+					true,
120
+					''
121
+				),
122
+				'TXN_hash_salt'    => new EE_Plain_Text_Field(
123
+					'TXN_hash_salt',
124
+					__('Transaction Hash Salt', 'event_espresso'),
125
+					true,
126
+					''
127
+				),
128
+				'PMD_ID'           => new EE_Foreign_Key_Int_Field(
129
+					'PMD_ID',
130
+					__("Last Used Payment Method", 'event_espresso'),
131
+					true,
132
+					null,
133
+					'Payment_Method'
134
+				),
135
+				'TXN_reg_steps'    => new EE_Serialized_Text_Field(
136
+					'TXN_reg_steps',
137
+					__('Registration Steps', 'event_espresso'),
138
+					false,
139
+					array()
140
+				),
141
+			)
142
+		);
143
+		$this->_model_relations        = array(
144
+			'Registration'   => new EE_Has_Many_Relation(),
145
+			'Payment'        => new EE_Has_Many_Relation(),
146
+			'Status'         => new EE_Belongs_To_Relation(),
147
+			'Line_Item'      => new EE_Has_Many_Relation(false),
148
+			// you can delete a transaction without needing to delete its line items
149
+			'Payment_Method' => new EE_Belongs_To_Relation(),
150
+			'Message'        => new EE_Has_Many_Relation()
151
+		);
152
+		$this->_model_chain_to_wp_user = 'Registration.Event';
153
+		parent::__construct($timezone);
154
+	}
155
+
156
+
157
+	/**
158
+	 *    txn_status_array
159
+	 * get list of transaction statuses
160
+	 *
161
+	 * @access public
162
+	 * @return array
163
+	 */
164
+	public static function txn_status_array()
165
+	{
166
+		return apply_filters(
167
+			'FHEE__EEM_Transaction__txn_status_array',
168
+			array(
169
+				EEM_Transaction::overpaid_status_code,
170
+				EEM_Transaction::complete_status_code,
171
+				EEM_Transaction::incomplete_status_code,
172
+				EEM_Transaction::abandoned_status_code,
173
+				EEM_Transaction::failed_status_code,
174
+			)
175
+		);
176
+	}
177
+
178
+	/**
179
+	 *        get the revenue per day  for the Transaction Admin page Reports Tab
180
+	 *
181
+	 * @access        public
182
+	 *
183
+	 * @param string $period
184
+	 *
185
+	 * @return \stdClass[]
186
+	 */
187
+	public function get_revenue_per_day_report($period = '-1 month')
188
+	{
189
+		$sql_date = $this->convert_datetime_for_query(
190
+			'TXN_timestamp',
191
+			date('Y-m-d H:i:s', strtotime($period)),
192
+			'Y-m-d H:i:s',
193
+			'UTC'
194
+		);
195
+
196
+		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'TXN_timestamp');
197
+
198
+		return $this->_get_all_wpdb_results(
199
+			array(
200
+				array(
201
+					'TXN_timestamp' => array('>=', $sql_date)
202
+				),
203
+				'group_by' => 'txnDate',
204
+				'order_by' => array('TXN_timestamp' => 'ASC')
205
+			),
206
+			OBJECT,
207
+			array(
208
+				'txnDate' => array('DATE(' . $query_interval . ')', '%s'),
209
+				'revenue' => array('SUM(TransactionTable.TXN_paid)', '%d')
210
+			)
211
+		);
212
+	}
213
+
214
+
215
+	/**
216
+	 *        get the revenue per event  for the Transaction Admin page Reports Tab
217
+	 *
218
+	 * @access        public
219
+	 *
220
+	 * @param string $period
221
+	 *
222
+	 * @throws \EE_Error
223
+	 * @return mixed
224
+	 */
225
+	public function get_revenue_per_event_report($period = '-1 month')
226
+	{
227
+		global $wpdb;
228
+		$transaction_table          = $wpdb->prefix . 'esp_transaction';
229
+		$registration_table         = $wpdb->prefix . 'esp_registration';
230
+		$registration_payment_table = $wpdb->prefix . 'esp_registration_payment';
231
+		$event_table                = $wpdb->posts;
232
+		$payment_table              = $wpdb->prefix . 'esp_payment';
233
+		$sql_date                   = date('Y-m-d H:i:s', strtotime($period));
234
+		$approved_payment_status    = EEM_Payment::status_id_approved;
235
+		$extra_event_on_join        = '';
236
+		// exclude events not authored by user if permissions in effect
237
+		if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
238
+			$extra_event_on_join = ' AND Event.post_author = ' . get_current_user_id();
239
+		}
240
+
241
+		return $wpdb->get_results(
242
+			"SELECT
243 243
 			Transaction_Event.event_name AS event_name,
244 244
 			SUM(Transaction_Event.paid) AS revenue
245 245
 			FROM
@@ -267,233 +267,233 @@  discard block
 block discarded – undo
267 267
 					$extra_event_on_join
268 268
 				) AS Transaction_Event
269 269
 			GROUP BY event_name",
270
-            OBJECT
271
-        );
272
-    }
273
-
274
-
275
-    /**
276
-     * Gets the current transaction given the reg_url_link, or assumes the reg_url_link is in the
277
-     * $_REQUEST global variable. Either way, tries to find the current transaction (through
278
-     * the registration pointed to by reg_url_link), if not returns null
279
-     *
280
-     * @param string $reg_url_link
281
-     *
282
-     * @return EE_Transaction
283
-     */
284
-    public function get_transaction_from_reg_url_link($reg_url_link = '')
285
-    {
286
-        return $this->get_one(array(
287
-            array(
288
-                'Registration.REG_url_link' => ! empty($reg_url_link) ? $reg_url_link : EE_Registry::instance()->REQ->get(
289
-                    'e_reg_url_link',
290
-                    ''
291
-                )
292
-            )
293
-        ));
294
-    }
295
-
296
-
297
-    /**
298
-     * Updates the provided EE_Transaction with all the applicable payments
299
-     * (or fetch the EE_Transaction from its ID)
300
-     *
301
-     * @deprecated
302
-     *
303
-     * @param EE_Transaction|int $transaction_obj_or_id
304
-     * @param boolean            $save_txn whether or not to save the transaction during this function call
305
-     *
306
-     * @return boolean
307
-     * @throws \EE_Error
308
-     */
309
-    public function update_based_on_payments($transaction_obj_or_id, $save_txn = true)
310
-    {
311
-        EE_Error::doing_it_wrong(
312
-            __CLASS__ . '::' . __FUNCTION__,
313
-            sprintf(
314
-                __('This method is deprecated. Please use "%s" instead', 'event_espresso'),
315
-                'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
316
-            ),
317
-            '4.6.0'
318
-        );
319
-        /** @type EE_Transaction_Processor $transaction_processor */
320
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
321
-
322
-        return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
323
-            $this->ensure_is_obj($transaction_obj_or_id)
324
-        );
325
-    }
326
-
327
-    /**
328
-     * Deletes "junk" transactions that were probably added by bots. There might be TONS
329
-     * of these, so we are very careful to NOT select (which the models do even when deleting),
330
-     * and so we only use wpdb directly and only do minimal joins.
331
-     * Transactions are considered "junk" if they're failed for longer than a week.
332
-     * Also, there is an extra check for payments related to the transaction, because if a transaction has a payment on
333
-     * it, it's probably not junk (regardless of what status it has).
334
-     * The downside to this approach is that is addons are listening for object deletions
335
-     * on EEM_Base::delete() they won't be notified of this.  However, there is an action that plugins can hook into
336
-     * to catch these types of deletions.
337
-     *
338
-     * @global WPDB $wpdb
339
-     * @return mixed
340
-     */
341
-    public function delete_junk_transactions()
342
-    {
343
-        /** @type WPDB $wpdb */
344
-        global $wpdb;
345
-        $deleted             = false;
346
-        $time_to_leave_alone = apply_filters(
347
-            'FHEE__EEM_Transaction__delete_junk_transactions__time_to_leave_alone',
348
-            WEEK_IN_SECONDS
349
-        );
350
-
351
-
352
-        /**
353
-         * This allows code to filter the query arguments used for retrieving the transaction IDs to delete.
354
-         * Useful for plugins that want to exclude transactions matching certain query parameters.
355
-         * The query parameters should be in the format accepted by the EEM_Base model queries.
356
-         */
357
-        $ids_query = apply_filters(
358
-            'FHEE__EEM_Transaction__delete_junk_transactions__initial_query_args',
359
-            array(
360
-                0 => array(
361
-                    'STS_ID'        => EEM_Transaction::failed_status_code,
362
-                    'Payment.PAY_ID' => array( 'IS NULL' ),
363
-                    'TXN_timestamp' => array('<', time() - $time_to_leave_alone)
364
-                ),
365
-                'order_by' => ['TXN_timestamp' => 'ASC'],
366
-                'limit' => 1000
367
-            ),
368
-            $time_to_leave_alone
369
-        );
370
-
371
-
372
-        /**
373
-         * This filter is for when code needs to filter the list of transaction ids that represent transactions
374
-         * about to be deleted based on some other criteria that isn't easily done via the query args filter.
375
-         */
376
-        $txn_ids = apply_filters(
377
-            'FHEE__EEM_Transaction__delete_junk_transactions__transaction_ids_to_delete',
378
-            EEM_Transaction::instance()->get_col($ids_query, 'TXN_ID'),
379
-            $time_to_leave_alone
380
-        );
381
-        // now that we have the ids to delete
382
-        if (! empty($txn_ids) && is_array($txn_ids)) {
383
-            // first, make sure these TXN's are removed the "ee_locked_transactions" array
384
-            EEM_Transaction::unset_locked_transactions($txn_ids);
385
-
386
-            // Create IDs placeholder.
387
-            $placeholders = array_fill(0, count($txn_ids), '%d');
270
+			OBJECT
271
+		);
272
+	}
273
+
274
+
275
+	/**
276
+	 * Gets the current transaction given the reg_url_link, or assumes the reg_url_link is in the
277
+	 * $_REQUEST global variable. Either way, tries to find the current transaction (through
278
+	 * the registration pointed to by reg_url_link), if not returns null
279
+	 *
280
+	 * @param string $reg_url_link
281
+	 *
282
+	 * @return EE_Transaction
283
+	 */
284
+	public function get_transaction_from_reg_url_link($reg_url_link = '')
285
+	{
286
+		return $this->get_one(array(
287
+			array(
288
+				'Registration.REG_url_link' => ! empty($reg_url_link) ? $reg_url_link : EE_Registry::instance()->REQ->get(
289
+					'e_reg_url_link',
290
+					''
291
+				)
292
+			)
293
+		));
294
+	}
295
+
296
+
297
+	/**
298
+	 * Updates the provided EE_Transaction with all the applicable payments
299
+	 * (or fetch the EE_Transaction from its ID)
300
+	 *
301
+	 * @deprecated
302
+	 *
303
+	 * @param EE_Transaction|int $transaction_obj_or_id
304
+	 * @param boolean            $save_txn whether or not to save the transaction during this function call
305
+	 *
306
+	 * @return boolean
307
+	 * @throws \EE_Error
308
+	 */
309
+	public function update_based_on_payments($transaction_obj_or_id, $save_txn = true)
310
+	{
311
+		EE_Error::doing_it_wrong(
312
+			__CLASS__ . '::' . __FUNCTION__,
313
+			sprintf(
314
+				__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
315
+				'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
316
+			),
317
+			'4.6.0'
318
+		);
319
+		/** @type EE_Transaction_Processor $transaction_processor */
320
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
321
+
322
+		return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
323
+			$this->ensure_is_obj($transaction_obj_or_id)
324
+		);
325
+	}
326
+
327
+	/**
328
+	 * Deletes "junk" transactions that were probably added by bots. There might be TONS
329
+	 * of these, so we are very careful to NOT select (which the models do even when deleting),
330
+	 * and so we only use wpdb directly and only do minimal joins.
331
+	 * Transactions are considered "junk" if they're failed for longer than a week.
332
+	 * Also, there is an extra check for payments related to the transaction, because if a transaction has a payment on
333
+	 * it, it's probably not junk (regardless of what status it has).
334
+	 * The downside to this approach is that is addons are listening for object deletions
335
+	 * on EEM_Base::delete() they won't be notified of this.  However, there is an action that plugins can hook into
336
+	 * to catch these types of deletions.
337
+	 *
338
+	 * @global WPDB $wpdb
339
+	 * @return mixed
340
+	 */
341
+	public function delete_junk_transactions()
342
+	{
343
+		/** @type WPDB $wpdb */
344
+		global $wpdb;
345
+		$deleted             = false;
346
+		$time_to_leave_alone = apply_filters(
347
+			'FHEE__EEM_Transaction__delete_junk_transactions__time_to_leave_alone',
348
+			WEEK_IN_SECONDS
349
+		);
350
+
351
+
352
+		/**
353
+		 * This allows code to filter the query arguments used for retrieving the transaction IDs to delete.
354
+		 * Useful for plugins that want to exclude transactions matching certain query parameters.
355
+		 * The query parameters should be in the format accepted by the EEM_Base model queries.
356
+		 */
357
+		$ids_query = apply_filters(
358
+			'FHEE__EEM_Transaction__delete_junk_transactions__initial_query_args',
359
+			array(
360
+				0 => array(
361
+					'STS_ID'        => EEM_Transaction::failed_status_code,
362
+					'Payment.PAY_ID' => array( 'IS NULL' ),
363
+					'TXN_timestamp' => array('<', time() - $time_to_leave_alone)
364
+				),
365
+				'order_by' => ['TXN_timestamp' => 'ASC'],
366
+				'limit' => 1000
367
+			),
368
+			$time_to_leave_alone
369
+		);
370
+
371
+
372
+		/**
373
+		 * This filter is for when code needs to filter the list of transaction ids that represent transactions
374
+		 * about to be deleted based on some other criteria that isn't easily done via the query args filter.
375
+		 */
376
+		$txn_ids = apply_filters(
377
+			'FHEE__EEM_Transaction__delete_junk_transactions__transaction_ids_to_delete',
378
+			EEM_Transaction::instance()->get_col($ids_query, 'TXN_ID'),
379
+			$time_to_leave_alone
380
+		);
381
+		// now that we have the ids to delete
382
+		if (! empty($txn_ids) && is_array($txn_ids)) {
383
+			// first, make sure these TXN's are removed the "ee_locked_transactions" array
384
+			EEM_Transaction::unset_locked_transactions($txn_ids);
385
+
386
+			// Create IDs placeholder.
387
+			$placeholders = array_fill(0, count($txn_ids), '%d');
388 388
             
389
-            // Glue it together to use inside $wpdb->prepare.
390
-            $format = implode(', ', $placeholders);
391
-
392
-            // let's get deletin'...
393
-            // We got the ids from the original query to get them FROM
394
-            // the db (which is sanitized) so no need to prepare them again.
395
-            $query   = $wpdb->prepare("DELETE FROM " . $this->table() . " WHERE TXN_ID IN ( $format )", $txn_ids);
396
-            $deleted = $wpdb->query($query);
397
-        }
398
-        if ($deleted) {
399
-            /**
400
-             * Allows code to do something after the transactions have been deleted.
401
-             */
402
-            do_action('AHEE__EEM_Transaction__delete_junk_transactions__successful_deletion', $txn_ids);
403
-        }
404
-
405
-        return $deleted;
406
-    }
407
-
408
-
409
-    /**
410
-     * @param array $transaction_IDs
411
-     *
412
-     * @return bool
413
-     */
414
-    public static function unset_locked_transactions(array $transaction_IDs)
415
-    {
416
-        $locked_transactions = get_option('ee_locked_transactions', array());
417
-        $update              = false;
418
-        foreach ($transaction_IDs as $TXN_ID) {
419
-            if (isset($locked_transactions[ $TXN_ID ])) {
420
-                unset($locked_transactions[ $TXN_ID ]);
421
-                $update = true;
422
-            }
423
-        }
424
-        if ($update) {
425
-            update_option('ee_locked_transactions', $locked_transactions);
426
-        }
427
-
428
-        return $update;
429
-    }
430
-
431
-
432
-
433
-    /**
434
-     * returns an array of EE_Transaction objects whose timestamp is greater than
435
-     * the current time minus the session lifespan, which defaults to 60 minutes
436
-     *
437
-     * @return EE_Base_Class[]|EE_Transaction[]
438
-     * @throws EE_Error
439
-     * @throws InvalidArgumentException
440
-     * @throws InvalidDataTypeException
441
-     * @throws InvalidInterfaceException
442
-     */
443
-    public function get_transactions_in_progress()
444
-    {
445
-        return $this->_get_transactions_in_progress();
446
-    }
447
-
448
-
449
-
450
-    /**
451
-     * returns an array of EE_Transaction objects whose timestamp is less than
452
-     * the current time minus the session lifespan, which defaults to 60 minutes
453
-     *
454
-     * @return EE_Base_Class[]|EE_Transaction[]
455
-     * @throws EE_Error
456
-     * @throws InvalidArgumentException
457
-     * @throws InvalidDataTypeException
458
-     * @throws InvalidInterfaceException
459
-     */
460
-    public function get_transactions_not_in_progress()
461
-    {
462
-        return $this->_get_transactions_in_progress('<=');
463
-    }
464
-
465
-
466
-
467
-    /**
468
-     * @param string $comparison
469
-     * @return EE_Base_Class[]|EE_Transaction[]
470
-     * @throws EE_Error
471
-     * @throws InvalidArgumentException
472
-     * @throws InvalidDataTypeException
473
-     * @throws InvalidInterfaceException
474
-     */
475
-    private function _get_transactions_in_progress($comparison = '>=')
476
-    {
477
-        $comparison = $comparison === '>=' || $comparison === '<='
478
-            ? $comparison
479
-            : '>=';
480
-        /** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
481
-        $session_lifespan = LoaderFactory::getLoader()->getShared(
482
-            'EventEspresso\core\domain\values\session\SessionLifespan'
483
-        );
484
-        return $this->get_all(
485
-            array(
486
-                array(
487
-                    'TXN_timestamp' => array(
488
-                        $comparison,
489
-                        $session_lifespan->expiration()
490
-                    ),
491
-                    'STS_ID' => array(
492
-                        '!=',
493
-                        EEM_Transaction::complete_status_code
494
-                    ),
495
-                )
496
-            )
497
-        );
498
-    }
389
+			// Glue it together to use inside $wpdb->prepare.
390
+			$format = implode(', ', $placeholders);
391
+
392
+			// let's get deletin'...
393
+			// We got the ids from the original query to get them FROM
394
+			// the db (which is sanitized) so no need to prepare them again.
395
+			$query   = $wpdb->prepare("DELETE FROM " . $this->table() . " WHERE TXN_ID IN ( $format )", $txn_ids);
396
+			$deleted = $wpdb->query($query);
397
+		}
398
+		if ($deleted) {
399
+			/**
400
+			 * Allows code to do something after the transactions have been deleted.
401
+			 */
402
+			do_action('AHEE__EEM_Transaction__delete_junk_transactions__successful_deletion', $txn_ids);
403
+		}
404
+
405
+		return $deleted;
406
+	}
407
+
408
+
409
+	/**
410
+	 * @param array $transaction_IDs
411
+	 *
412
+	 * @return bool
413
+	 */
414
+	public static function unset_locked_transactions(array $transaction_IDs)
415
+	{
416
+		$locked_transactions = get_option('ee_locked_transactions', array());
417
+		$update              = false;
418
+		foreach ($transaction_IDs as $TXN_ID) {
419
+			if (isset($locked_transactions[ $TXN_ID ])) {
420
+				unset($locked_transactions[ $TXN_ID ]);
421
+				$update = true;
422
+			}
423
+		}
424
+		if ($update) {
425
+			update_option('ee_locked_transactions', $locked_transactions);
426
+		}
427
+
428
+		return $update;
429
+	}
430
+
431
+
432
+
433
+	/**
434
+	 * returns an array of EE_Transaction objects whose timestamp is greater than
435
+	 * the current time minus the session lifespan, which defaults to 60 minutes
436
+	 *
437
+	 * @return EE_Base_Class[]|EE_Transaction[]
438
+	 * @throws EE_Error
439
+	 * @throws InvalidArgumentException
440
+	 * @throws InvalidDataTypeException
441
+	 * @throws InvalidInterfaceException
442
+	 */
443
+	public function get_transactions_in_progress()
444
+	{
445
+		return $this->_get_transactions_in_progress();
446
+	}
447
+
448
+
449
+
450
+	/**
451
+	 * returns an array of EE_Transaction objects whose timestamp is less than
452
+	 * the current time minus the session lifespan, which defaults to 60 minutes
453
+	 *
454
+	 * @return EE_Base_Class[]|EE_Transaction[]
455
+	 * @throws EE_Error
456
+	 * @throws InvalidArgumentException
457
+	 * @throws InvalidDataTypeException
458
+	 * @throws InvalidInterfaceException
459
+	 */
460
+	public function get_transactions_not_in_progress()
461
+	{
462
+		return $this->_get_transactions_in_progress('<=');
463
+	}
464
+
465
+
466
+
467
+	/**
468
+	 * @param string $comparison
469
+	 * @return EE_Base_Class[]|EE_Transaction[]
470
+	 * @throws EE_Error
471
+	 * @throws InvalidArgumentException
472
+	 * @throws InvalidDataTypeException
473
+	 * @throws InvalidInterfaceException
474
+	 */
475
+	private function _get_transactions_in_progress($comparison = '>=')
476
+	{
477
+		$comparison = $comparison === '>=' || $comparison === '<='
478
+			? $comparison
479
+			: '>=';
480
+		/** @var EventEspresso\core\domain\values\session\SessionLifespan $session_lifespan */
481
+		$session_lifespan = LoaderFactory::getLoader()->getShared(
482
+			'EventEspresso\core\domain\values\session\SessionLifespan'
483
+		);
484
+		return $this->get_all(
485
+			array(
486
+				array(
487
+					'TXN_timestamp' => array(
488
+						$comparison,
489
+						$session_lifespan->expiration()
490
+					),
491
+					'STS_ID' => array(
492
+						'!=',
493
+						EEM_Transaction::complete_status_code
494
+					),
495
+				)
496
+			)
497
+		);
498
+	}
499 499
 }
Please login to merge, or discard this patch.
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
                 ),
141 141
             )
142 142
         );
143
-        $this->_model_relations        = array(
143
+        $this->_model_relations = array(
144 144
             'Registration'   => new EE_Has_Many_Relation(),
145 145
             'Payment'        => new EE_Has_Many_Relation(),
146 146
             'Status'         => new EE_Belongs_To_Relation(),
@@ -205,7 +205,7 @@  discard block
 block discarded – undo
205 205
             ),
206 206
             OBJECT,
207 207
             array(
208
-                'txnDate' => array('DATE(' . $query_interval . ')', '%s'),
208
+                'txnDate' => array('DATE('.$query_interval.')', '%s'),
209 209
                 'revenue' => array('SUM(TransactionTable.TXN_paid)', '%d')
210 210
             )
211 211
         );
@@ -225,17 +225,17 @@  discard block
 block discarded – undo
225 225
     public function get_revenue_per_event_report($period = '-1 month')
226 226
     {
227 227
         global $wpdb;
228
-        $transaction_table          = $wpdb->prefix . 'esp_transaction';
229
-        $registration_table         = $wpdb->prefix . 'esp_registration';
230
-        $registration_payment_table = $wpdb->prefix . 'esp_registration_payment';
228
+        $transaction_table          = $wpdb->prefix.'esp_transaction';
229
+        $registration_table         = $wpdb->prefix.'esp_registration';
230
+        $registration_payment_table = $wpdb->prefix.'esp_registration_payment';
231 231
         $event_table                = $wpdb->posts;
232
-        $payment_table              = $wpdb->prefix . 'esp_payment';
232
+        $payment_table              = $wpdb->prefix.'esp_payment';
233 233
         $sql_date                   = date('Y-m-d H:i:s', strtotime($period));
234 234
         $approved_payment_status    = EEM_Payment::status_id_approved;
235 235
         $extra_event_on_join        = '';
236 236
         // exclude events not authored by user if permissions in effect
237
-        if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
238
-            $extra_event_on_join = ' AND Event.post_author = ' . get_current_user_id();
237
+        if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
238
+            $extra_event_on_join = ' AND Event.post_author = '.get_current_user_id();
239 239
         }
240 240
 
241 241
         return $wpdb->get_results(
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
     public function update_based_on_payments($transaction_obj_or_id, $save_txn = true)
310 310
     {
311 311
         EE_Error::doing_it_wrong(
312
-            __CLASS__ . '::' . __FUNCTION__,
312
+            __CLASS__.'::'.__FUNCTION__,
313 313
             sprintf(
314 314
                 __('This method is deprecated. Please use "%s" instead', 'event_espresso'),
315 315
                 'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
@@ -359,7 +359,7 @@  discard block
 block discarded – undo
359 359
             array(
360 360
                 0 => array(
361 361
                     'STS_ID'        => EEM_Transaction::failed_status_code,
362
-                    'Payment.PAY_ID' => array( 'IS NULL' ),
362
+                    'Payment.PAY_ID' => array('IS NULL'),
363 363
                     'TXN_timestamp' => array('<', time() - $time_to_leave_alone)
364 364
                 ),
365 365
                 'order_by' => ['TXN_timestamp' => 'ASC'],
@@ -379,7 +379,7 @@  discard block
 block discarded – undo
379 379
             $time_to_leave_alone
380 380
         );
381 381
         // now that we have the ids to delete
382
-        if (! empty($txn_ids) && is_array($txn_ids)) {
382
+        if ( ! empty($txn_ids) && is_array($txn_ids)) {
383 383
             // first, make sure these TXN's are removed the "ee_locked_transactions" array
384 384
             EEM_Transaction::unset_locked_transactions($txn_ids);
385 385
 
@@ -392,7 +392,7 @@  discard block
 block discarded – undo
392 392
             // let's get deletin'...
393 393
             // We got the ids from the original query to get them FROM
394 394
             // the db (which is sanitized) so no need to prepare them again.
395
-            $query   = $wpdb->prepare("DELETE FROM " . $this->table() . " WHERE TXN_ID IN ( $format )", $txn_ids);
395
+            $query   = $wpdb->prepare("DELETE FROM ".$this->table()." WHERE TXN_ID IN ( $format )", $txn_ids);
396 396
             $deleted = $wpdb->query($query);
397 397
         }
398 398
         if ($deleted) {
@@ -416,8 +416,8 @@  discard block
 block discarded – undo
416 416
         $locked_transactions = get_option('ee_locked_transactions', array());
417 417
         $update              = false;
418 418
         foreach ($transaction_IDs as $TXN_ID) {
419
-            if (isset($locked_transactions[ $TXN_ID ])) {
420
-                unset($locked_transactions[ $TXN_ID ]);
419
+            if (isset($locked_transactions[$TXN_ID])) {
420
+                unset($locked_transactions[$TXN_ID]);
421 421
                 $update = true;
422 422
             }
423 423
         }
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -38,103 +38,103 @@
 block discarded – undo
38 38
  * @since           4.0
39 39
  */
40 40
 if (function_exists('espresso_version')) {
41
-    if (! function_exists('espresso_duplicate_plugin_error')) {
42
-        /**
43
-         *    espresso_duplicate_plugin_error
44
-         *    displays if more than one version of EE is activated at the same time
45
-         */
46
-        function espresso_duplicate_plugin_error()
47
-        {
48
-            ?>
41
+	if (! function_exists('espresso_duplicate_plugin_error')) {
42
+		/**
43
+		 *    espresso_duplicate_plugin_error
44
+		 *    displays if more than one version of EE is activated at the same time
45
+		 */
46
+		function espresso_duplicate_plugin_error()
47
+		{
48
+			?>
49 49
             <div class="error">
50 50
                 <p>
51 51
                     <?php
52
-                    echo esc_html__(
53
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
-                        'event_espresso'
55
-                    ); ?>
52
+					echo esc_html__(
53
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
+						'event_espresso'
55
+					); ?>
56 56
                 </p>
57 57
             </div>
58 58
             <?php
59
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
60
-        }
61
-    }
62
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
59
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
60
+		}
61
+	}
62
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
63 63
 } else {
64
-    define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
-        /**
67
-         * espresso_minimum_php_version_error
68
-         *
69
-         * @return void
70
-         */
71
-        function espresso_minimum_php_version_error()
72
-        {
73
-            ?>
64
+	define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
+		/**
67
+		 * espresso_minimum_php_version_error
68
+		 *
69
+		 * @return void
70
+		 */
71
+		function espresso_minimum_php_version_error()
72
+		{
73
+			?>
74 74
             <div class="error">
75 75
                 <p>
76 76
                     <?php
77
-                    printf(
78
-                        esc_html__(
79
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
-                            'event_espresso'
81
-                        ),
82
-                        EE_MIN_PHP_VER_REQUIRED,
83
-                        PHP_VERSION,
84
-                        '<br/>',
85
-                        '<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
-                    );
87
-                    ?>
77
+					printf(
78
+						esc_html__(
79
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
+							'event_espresso'
81
+						),
82
+						EE_MIN_PHP_VER_REQUIRED,
83
+						PHP_VERSION,
84
+						'<br/>',
85
+						'<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
+					);
87
+					?>
88 88
                 </p>
89 89
             </div>
90 90
             <?php
91
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
92
-        }
91
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
92
+		}
93 93
 
94
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
-    } else {
96
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
-        /**
98
-         * espresso_version
99
-         * Returns the plugin version
100
-         *
101
-         * @return string
102
-         */
103
-        function espresso_version()
104
-        {
105
-            return apply_filters('FHEE__espresso__espresso_version', '4.10.3.rc.026');
106
-        }
94
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
+	} else {
96
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
+		/**
98
+		 * espresso_version
99
+		 * Returns the plugin version
100
+		 *
101
+		 * @return string
102
+		 */
103
+		function espresso_version()
104
+		{
105
+			return apply_filters('FHEE__espresso__espresso_version', '4.10.3.rc.026');
106
+		}
107 107
 
108
-        /**
109
-         * espresso_plugin_activation
110
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
-         */
112
-        function espresso_plugin_activation()
113
-        {
114
-            update_option('ee_espresso_activation', true);
115
-        }
108
+		/**
109
+		 * espresso_plugin_activation
110
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
+		 */
112
+		function espresso_plugin_activation()
113
+		{
114
+			update_option('ee_espresso_activation', true);
115
+		}
116 116
 
117
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
117
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
118 118
 
119
-        require_once __DIR__ . '/core/bootstrap_espresso.php';
120
-        bootstrap_espresso();
121
-    }
119
+		require_once __DIR__ . '/core/bootstrap_espresso.php';
120
+		bootstrap_espresso();
121
+	}
122 122
 }
123 123
 if (! function_exists('espresso_deactivate_plugin')) {
124
-    /**
125
-     *    deactivate_plugin
126
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
-     *
128
-     * @access public
129
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
-     * @return    void
131
-     */
132
-    function espresso_deactivate_plugin($plugin_basename = '')
133
-    {
134
-        if (! function_exists('deactivate_plugins')) {
135
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
-        }
137
-        unset($_GET['activate'], $_REQUEST['activate']);
138
-        deactivate_plugins($plugin_basename);
139
-    }
124
+	/**
125
+	 *    deactivate_plugin
126
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
+	 *
128
+	 * @access public
129
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
+	 * @return    void
131
+	 */
132
+	function espresso_deactivate_plugin($plugin_basename = '')
133
+	{
134
+		if (! function_exists('deactivate_plugins')) {
135
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
+		}
137
+		unset($_GET['activate'], $_REQUEST['activate']);
138
+		deactivate_plugins($plugin_basename);
139
+	}
140 140
 }
Please login to merge, or discard this patch.