Completed
Branch apply-extra-txn-fees-to-regs (69a605)
by
unknown
03:37 queued 13s
created
line_item_filters/EE_Specific_Registrations_Line_Item_Filter.class.php 2 patches
Indentation   +217 added lines, -217 removed lines patch added patch discarded remove patch
@@ -14,245 +14,245 @@
 block discarded – undo
14 14
  */
15 15
 class EE_Specific_Registrations_Line_Item_Filter extends EE_Line_Item_Filter_Base
16 16
 {
17
-    /**
18
-     * @var ExtraTxnFeesForLineItemsHandler
19
-     */
20
-    protected $extra_txn_fees_handler;
17
+	/**
18
+	 * @var ExtraTxnFeesForLineItemsHandler
19
+	 */
20
+	protected $extra_txn_fees_handler;
21 21
 
22
-    /**
23
-     * array of line item codes and their corresponding quantities for registrations
24
-     *
25
-     * @var array $_line_item_registrations
26
-     */
27
-    protected $_line_item_registrations = [];
22
+	/**
23
+	 * array of line item codes and their corresponding quantities for registrations
24
+	 *
25
+	 * @var array $_line_item_registrations
26
+	 */
27
+	protected $_line_item_registrations = [];
28 28
 
29
-    /**
30
-     * Just kept in case we want it someday. Currently unused
31
-     *
32
-     * @var EE_Registration[]
33
-     */
34
-    protected $_registrations = [];
29
+	/**
30
+	 * Just kept in case we want it someday. Currently unused
31
+	 *
32
+	 * @var EE_Registration[]
33
+	 */
34
+	protected $_registrations = [];
35 35
 
36
-    /**
37
-     * @var EE_Registration
38
-     */
39
-    protected $_current_registration;
36
+	/**
37
+	 * @var EE_Registration
38
+	 */
39
+	protected $_current_registration;
40 40
 
41
-    /**
42
-     * these reg statuses should NOT increment the line item quantity
43
-     *
44
-     * @var array
45
-     */
46
-    protected $_closed_reg_statuses = [];
41
+	/**
42
+	 * these reg statuses should NOT increment the line item quantity
43
+	 *
44
+	 * @var array
45
+	 */
46
+	protected $_closed_reg_statuses = [];
47 47
 
48
-    /**
49
-     * @var int
50
-     */
51
-    protected $total_reg_count = 0;
48
+	/**
49
+	 * @var int
50
+	 */
51
+	protected $total_reg_count = 0;
52 52
 
53 53
 
54
-    /**
55
-     * EE_Billable_Line_Item_Filter constructor.
56
-     *
57
-     * @param EE_Registration[] $registrations
58
-     * @throws EE_Error
59
-     * @throws ReflectionException
60
-     */
61
-    public function __construct($registrations)
62
-    {
63
-        $this->_registrations = $registrations;
64
-        $this->total_reg_count = count($registrations);
65
-        $this->extra_txn_fees_handler = new ExtraTxnFeesForLineItemsHandler($registrations);
66
-        $this->calculateRegistrationsPerLineItemCode($registrations);
67
-        // these reg statuses should NOT increment the line item quantity
68
-        $this->_closed_reg_statuses = EEM_Registration::closed_reg_statuses();
69
-    }
54
+	/**
55
+	 * EE_Billable_Line_Item_Filter constructor.
56
+	 *
57
+	 * @param EE_Registration[] $registrations
58
+	 * @throws EE_Error
59
+	 * @throws ReflectionException
60
+	 */
61
+	public function __construct($registrations)
62
+	{
63
+		$this->_registrations = $registrations;
64
+		$this->total_reg_count = count($registrations);
65
+		$this->extra_txn_fees_handler = new ExtraTxnFeesForLineItemsHandler($registrations);
66
+		$this->calculateRegistrationsPerLineItemCode($registrations);
67
+		// these reg statuses should NOT increment the line item quantity
68
+		$this->_closed_reg_statuses = EEM_Registration::closed_reg_statuses();
69
+	}
70 70
 
71 71
 
72
-    /**
73
-     * Adjusts quantities for ticket line items according to the registrations provided upon construction,
74
-     * so if a TXN was for 2 of the same ticket, but we are only generating line items for a single registration,
75
-     * then this will change the quantity to 1, and update the total accordingly
76
-     *
77
-     * @param EEI_Line_Item $line_item
78
-     * @return EEI_Line_Item
79
-     * @throws EE_Error
80
-     * @throws ReflectionException
81
-     */
82
-    private function adjustLineItemQuantity(EEI_Line_Item $line_item)
83
-    {
84
-        // is this a ticket ?
85
-        if ($line_item->type() === EEM_Line_Item::type_line_item && $line_item->OBJ_type() === 'Ticket') {
86
-            $this->_current_registration = null;
87
-            $quantity                    = 0;
88
-            // if this ticket is billable at this moment, then we should have a positive quantity
89
-            if (
90
-                isset($this->_line_item_registrations[ $line_item->code() ])
91
-                && is_array($this->_line_item_registrations[ $line_item->code() ])
92
-            ) {
93
-                // set quantity based on number of open registrations for this ticket
94
-                foreach ($this->_line_item_registrations[ $line_item->code() ] as $registration) {
95
-                    if (
96
-                        $registration instanceof EE_Registration
97
-                    ) {
98
-                        $quantity++;
99
-                        $this->_current_registration = $registration;
100
-                    }
101
-                }
102
-            }
72
+	/**
73
+	 * Adjusts quantities for ticket line items according to the registrations provided upon construction,
74
+	 * so if a TXN was for 2 of the same ticket, but we are only generating line items for a single registration,
75
+	 * then this will change the quantity to 1, and update the total accordingly
76
+	 *
77
+	 * @param EEI_Line_Item $line_item
78
+	 * @return EEI_Line_Item
79
+	 * @throws EE_Error
80
+	 * @throws ReflectionException
81
+	 */
82
+	private function adjustLineItemQuantity(EEI_Line_Item $line_item)
83
+	{
84
+		// is this a ticket ?
85
+		if ($line_item->type() === EEM_Line_Item::type_line_item && $line_item->OBJ_type() === 'Ticket') {
86
+			$this->_current_registration = null;
87
+			$quantity                    = 0;
88
+			// if this ticket is billable at this moment, then we should have a positive quantity
89
+			if (
90
+				isset($this->_line_item_registrations[ $line_item->code() ])
91
+				&& is_array($this->_line_item_registrations[ $line_item->code() ])
92
+			) {
93
+				// set quantity based on number of open registrations for this ticket
94
+				foreach ($this->_line_item_registrations[ $line_item->code() ] as $registration) {
95
+					if (
96
+						$registration instanceof EE_Registration
97
+					) {
98
+						$quantity++;
99
+						$this->_current_registration = $registration;
100
+					}
101
+				}
102
+			}
103 103
 
104
-            $line_item->set_quantity($quantity);
105
-            $line_item->set_total($line_item->unit_price() * $line_item->quantity());
106
-        }
107
-        return $line_item;
108
-    }
104
+			$line_item->set_quantity($quantity);
105
+			$line_item->set_total($line_item->unit_price() * $line_item->quantity());
106
+		}
107
+		return $line_item;
108
+	}
109 109
 
110 110
 
111
-    /**
112
-     * @param EEI_Line_Item $line_item
113
-     * @param int           $running_total_for_specific_ticket
114
-     * @param int           $total_child_ticket_quantity
115
-     * @since   $VID:$
116
-     */
117
-    private function adjustLineItemTotalAndQty(
118
-        EEI_Line_Item $line_item,
119
-        $running_total_for_specific_ticket,
120
-        $total_child_ticket_quantity
121
-    ) {
122
-        $line_item->set_total($running_total_for_specific_ticket);
123
-        $new_unit_price = $line_item->quantity()
124
-            ? $running_total_for_specific_ticket / $line_item->quantity()
125
-            : 0;
111
+	/**
112
+	 * @param EEI_Line_Item $line_item
113
+	 * @param int           $running_total_for_specific_ticket
114
+	 * @param int           $total_child_ticket_quantity
115
+	 * @since   $VID:$
116
+	 */
117
+	private function adjustLineItemTotalAndQty(
118
+		EEI_Line_Item $line_item,
119
+		$running_total_for_specific_ticket,
120
+		$total_child_ticket_quantity
121
+	) {
122
+		$line_item->set_total($running_total_for_specific_ticket);
123
+		$new_unit_price = $line_item->quantity()
124
+			? $running_total_for_specific_ticket / $line_item->quantity()
125
+			: 0;
126 126
 
127
-        $line_item->set_unit_price($new_unit_price);
128
-        if ($line_item->OBJ_type() === 'Event') {
129
-            $line_item->set_quantity($total_child_ticket_quantity);
130
-        }
131
-    }
127
+		$line_item->set_unit_price($new_unit_price);
128
+		if ($line_item->OBJ_type() === 'Event') {
129
+			$line_item->set_quantity($total_child_ticket_quantity);
130
+		}
131
+	}
132 132
 
133 133
 
134
-    /**
135
-     * sets the _line_item_registrations from the provided registrations
136
-     *
137
-     * @param EE_Registration[] $registrations
138
-     * @return void
139
-     * @throws EE_Error|ReflectionException
140
-     */
141
-    private function calculateRegistrationsPerLineItemCode($registrations)
142
-    {
143
-        foreach ($registrations as $registration) {
144
-            $line_item_code = EEM_Line_Item::instance()->get_var(
145
-                EEM_Line_Item::instance()->line_item_for_registration_query_params(
146
-                    $registration,
147
-                    ['limit' => 1]
148
-                ),
149
-                'LIN_code'
150
-            );
151
-            if ($line_item_code) {
152
-                if (! isset($this->_line_item_registrations[ $line_item_code ])) {
153
-                    $this->_line_item_registrations[ $line_item_code ] = [];
154
-                }
155
-                $this->_line_item_registrations[ $line_item_code ][ $registration->ID() ] = $registration;
156
-            }
157
-        }
158
-    }
134
+	/**
135
+	 * sets the _line_item_registrations from the provided registrations
136
+	 *
137
+	 * @param EE_Registration[] $registrations
138
+	 * @return void
139
+	 * @throws EE_Error|ReflectionException
140
+	 */
141
+	private function calculateRegistrationsPerLineItemCode($registrations)
142
+	{
143
+		foreach ($registrations as $registration) {
144
+			$line_item_code = EEM_Line_Item::instance()->get_var(
145
+				EEM_Line_Item::instance()->line_item_for_registration_query_params(
146
+					$registration,
147
+					['limit' => 1]
148
+				),
149
+				'LIN_code'
150
+			);
151
+			if ($line_item_code) {
152
+				if (! isset($this->_line_item_registrations[ $line_item_code ])) {
153
+					$this->_line_item_registrations[ $line_item_code ] = [];
154
+				}
155
+				$this->_line_item_registrations[ $line_item_code ][ $registration->ID() ] = $registration;
156
+			}
157
+		}
158
+	}
159 159
 
160 160
 
161
-    /**
162
-     * @param EEI_Line_Item $line_item
163
-     * @param EEI_Line_Item $child_line_item
164
-     * @return bool
165
-     * @throws EE_Error
166
-     * @throws ReflectionException
167
-     * @since   $VID:$
168
-     */
169
-    private function isNonCancelledTicketLineItem(EEI_Line_Item $line_item, EEI_Line_Item $child_line_item)
170
-    {
171
-        return // make sure this item's quantity and total matches its parent
172
-            $line_item->type() === EEM_Line_Item::type_line_item
173
-            && $line_item->OBJ_type() === 'Ticket'
174
-            // but not if it's a percentage modifier
175
-            && ! $child_line_item->is_percent()
176
-            && ! (
177
-                // or a cancellation
178
-                $child_line_item->is_cancelled()
179
-                && ! (
180
-                    // unless it IS a cancellation and the current registration is cancelled
181
-                    $child_line_item->is_cancelled()
182
-                    && $this->_current_registration instanceof EE_Registration
183
-                    && in_array($this->_current_registration->status_ID(), $this->_closed_reg_statuses, true)
184
-                )
185
-            );
186
-    }
161
+	/**
162
+	 * @param EEI_Line_Item $line_item
163
+	 * @param EEI_Line_Item $child_line_item
164
+	 * @return bool
165
+	 * @throws EE_Error
166
+	 * @throws ReflectionException
167
+	 * @since   $VID:$
168
+	 */
169
+	private function isNonCancelledTicketLineItem(EEI_Line_Item $line_item, EEI_Line_Item $child_line_item)
170
+	{
171
+		return // make sure this item's quantity and total matches its parent
172
+			$line_item->type() === EEM_Line_Item::type_line_item
173
+			&& $line_item->OBJ_type() === 'Ticket'
174
+			// but not if it's a percentage modifier
175
+			&& ! $child_line_item->is_percent()
176
+			&& ! (
177
+				// or a cancellation
178
+				$child_line_item->is_cancelled()
179
+				&& ! (
180
+					// unless it IS a cancellation and the current registration is cancelled
181
+					$child_line_item->is_cancelled()
182
+					&& $this->_current_registration instanceof EE_Registration
183
+					&& in_array($this->_current_registration->status_ID(), $this->_closed_reg_statuses, true)
184
+				)
185
+			);
186
+	}
187 187
 
188 188
 
189
-    /**
190
-     * @param EEI_Line_Item $line_item
191
-     * @return bool
192
-     * @since   $VID:$
193
-     */
194
-    private function isNonTicketLineItem(EEI_Line_Item $line_item)
195
-    {
196
-        return $line_item->type() === EEM_Line_Item::type_line_item && $line_item->OBJ_type() !== 'Ticket';
197
-    }
189
+	/**
190
+	 * @param EEI_Line_Item $line_item
191
+	 * @return bool
192
+	 * @since   $VID:$
193
+	 */
194
+	private function isNonTicketLineItem(EEI_Line_Item $line_item)
195
+	{
196
+		return $line_item->type() === EEM_Line_Item::type_line_item && $line_item->OBJ_type() !== 'Ticket';
197
+	}
198 198
 
199 199
 
200
-    /**
201
-     * Creates a duplicate of the line item tree, except only includes billable items
202
-     * and the portion of line items attributed to billable things
203
-     *
204
-     * @param EEI_Line_Item $line_item
205
-     * @return EEI_Line_Item
206
-     * @throws EE_Error|ReflectionException
207
-     */
208
-    public function process(EEI_Line_Item $line_item)
209
-    {
210
-        $this->adjustLineItemQuantity($line_item);
211
-        if (! $line_item->children()) {
212
-            return $line_item;
213
-        }
214
-        // the original running total (taking ALL tickets into account)
215
-        $running_total_for_all_tickets = 0;
216
-        // the new running total (only taking the specified ticket quantities into account)
217
-        $running_total_for_specific_ticket = 0;
218
-        // let's also track the quantity of tickets that pertain to the registrations
219
-        $total_child_ticket_quantity = 0;
220
-        foreach ($line_item->children() as $child_line_item) {
221
-            $original_li_total = $child_line_item->is_percent()
222
-                ? $running_total_for_all_tickets * $child_line_item->percent() / 100
223
-                : $child_line_item->unit_price() * $child_line_item->quantity();
200
+	/**
201
+	 * Creates a duplicate of the line item tree, except only includes billable items
202
+	 * and the portion of line items attributed to billable things
203
+	 *
204
+	 * @param EEI_Line_Item $line_item
205
+	 * @return EEI_Line_Item
206
+	 * @throws EE_Error|ReflectionException
207
+	 */
208
+	public function process(EEI_Line_Item $line_item)
209
+	{
210
+		$this->adjustLineItemQuantity($line_item);
211
+		if (! $line_item->children()) {
212
+			return $line_item;
213
+		}
214
+		// the original running total (taking ALL tickets into account)
215
+		$running_total_for_all_tickets = 0;
216
+		// the new running total (only taking the specified ticket quantities into account)
217
+		$running_total_for_specific_ticket = 0;
218
+		// let's also track the quantity of tickets that pertain to the registrations
219
+		$total_child_ticket_quantity = 0;
220
+		foreach ($line_item->children() as $child_line_item) {
221
+			$original_li_total = $child_line_item->is_percent()
222
+				? $running_total_for_all_tickets * $child_line_item->percent() / 100
223
+				: $child_line_item->unit_price() * $child_line_item->quantity();
224 224
 
225
-            $this->process($child_line_item);
225
+			$this->process($child_line_item);
226 226
 
227
-            $applies_to_primary_registrant = $this->_current_registration instanceof EE_Registration
228
-                                             && $this->_current_registration->is_primary_registrant();
229
-            // If this line item is a normal line item that isn't for a ticket,
230
-            // we want to modify its total (and unit price if not a percentage line item)
231
-            // so it reflects only that portion of the surcharge/discount shared by these registrations
232
-            if ($this->isNonTicketLineItem($child_line_item)) {
233
-                $this->extra_txn_fees_handler->adjustUnitPriceForNonTicketLineItem(
234
-                    $line_item,
235
-                    $child_line_item,
236
-                    $original_li_total,
237
-                    $running_total_for_all_tickets,
238
-                    $running_total_for_specific_ticket,
239
-                    $applies_to_primary_registrant
240
-                );
241
-            } elseif ($this->isNonCancelledTicketLineItem($line_item, $child_line_item)) {
242
-                $this->extra_txn_fees_handler->adjustUnitPriceAndQtyForTicketLineItem(
243
-                    $line_item,
244
-                    $child_line_item,
245
-                    $applies_to_primary_registrant
246
-                );
247
-            }
248
-            $running_total_for_all_tickets += $original_li_total;
249
-            $running_total_for_specific_ticket += $child_line_item->total();
227
+			$applies_to_primary_registrant = $this->_current_registration instanceof EE_Registration
228
+											 && $this->_current_registration->is_primary_registrant();
229
+			// If this line item is a normal line item that isn't for a ticket,
230
+			// we want to modify its total (and unit price if not a percentage line item)
231
+			// so it reflects only that portion of the surcharge/discount shared by these registrations
232
+			if ($this->isNonTicketLineItem($child_line_item)) {
233
+				$this->extra_txn_fees_handler->adjustUnitPriceForNonTicketLineItem(
234
+					$line_item,
235
+					$child_line_item,
236
+					$original_li_total,
237
+					$running_total_for_all_tickets,
238
+					$running_total_for_specific_ticket,
239
+					$applies_to_primary_registrant
240
+				);
241
+			} elseif ($this->isNonCancelledTicketLineItem($line_item, $child_line_item)) {
242
+				$this->extra_txn_fees_handler->adjustUnitPriceAndQtyForTicketLineItem(
243
+					$line_item,
244
+					$child_line_item,
245
+					$applies_to_primary_registrant
246
+				);
247
+			}
248
+			$running_total_for_all_tickets += $original_li_total;
249
+			$running_total_for_specific_ticket += $child_line_item->total();
250 250
 
251
-            if ($child_line_item->OBJ_type() === 'Ticket') {
252
-                $total_child_ticket_quantity += $child_line_item->quantity();
253
-            }
254
-        }
255
-        $this->adjustLineItemTotalAndQty($line_item, $running_total_for_specific_ticket, $total_child_ticket_quantity);
256
-        return $line_item;
257
-    }
251
+			if ($child_line_item->OBJ_type() === 'Ticket') {
252
+				$total_child_ticket_quantity += $child_line_item->quantity();
253
+			}
254
+		}
255
+		$this->adjustLineItemTotalAndQty($line_item, $running_total_for_specific_ticket, $total_child_ticket_quantity);
256
+		return $line_item;
257
+	}
258 258
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -87,11 +87,11 @@  discard block
 block discarded – undo
87 87
             $quantity                    = 0;
88 88
             // if this ticket is billable at this moment, then we should have a positive quantity
89 89
             if (
90
-                isset($this->_line_item_registrations[ $line_item->code() ])
91
-                && is_array($this->_line_item_registrations[ $line_item->code() ])
90
+                isset($this->_line_item_registrations[$line_item->code()])
91
+                && is_array($this->_line_item_registrations[$line_item->code()])
92 92
             ) {
93 93
                 // set quantity based on number of open registrations for this ticket
94
-                foreach ($this->_line_item_registrations[ $line_item->code() ] as $registration) {
94
+                foreach ($this->_line_item_registrations[$line_item->code()] as $registration) {
95 95
                     if (
96 96
                         $registration instanceof EE_Registration
97 97
                     ) {
@@ -149,10 +149,10 @@  discard block
 block discarded – undo
149 149
                 'LIN_code'
150 150
             );
151 151
             if ($line_item_code) {
152
-                if (! isset($this->_line_item_registrations[ $line_item_code ])) {
153
-                    $this->_line_item_registrations[ $line_item_code ] = [];
152
+                if ( ! isset($this->_line_item_registrations[$line_item_code])) {
153
+                    $this->_line_item_registrations[$line_item_code] = [];
154 154
                 }
155
-                $this->_line_item_registrations[ $line_item_code ][ $registration->ID() ] = $registration;
155
+                $this->_line_item_registrations[$line_item_code][$registration->ID()] = $registration;
156 156
             }
157 157
         }
158 158
     }
@@ -208,7 +208,7 @@  discard block
 block discarded – undo
208 208
     public function process(EEI_Line_Item $line_item)
209 209
     {
210 210
         $this->adjustLineItemQuantity($line_item);
211
-        if (! $line_item->children()) {
211
+        if ( ! $line_item->children()) {
212 212
             return $line_item;
213 213
         }
214 214
         // the original running total (taking ALL tickets into account)
Please login to merge, or discard this patch.
EE_Admin_Table_Registration_Line_Item_Display_Strategy.strategy.php 2 patches
Indentation   +233 added lines, -233 removed lines patch added patch discarded remove patch
@@ -13,237 +13,237 @@
 block discarded – undo
13 13
 
14 14
 class EE_Admin_Table_Registration_Line_Item_Display_Strategy extends EE_Admin_Table_Line_Item_Display_Strategy
15 15
 {
16
-    /**
17
-     * @var EE_Registration
18
-     */
19
-    protected $registration;
20
-
21
-
22
-    /**
23
-     * EE_Admin_Table_Registration_Line_Item_Display_Strategy constructor.
24
-     */
25
-    public function __construct()
26
-    {
27
-        $this->registration = isset($options['EE_Registration'])
28
-                              && $options['EE_Registration'] instanceof EE_Registration
29
-            ? $options['EE_Registration']
30
-            : null;
31
-    }
32
-
33
-
34
-    /**
35
-     * Table header for display.
36
-     *
37
-     * @param array $options
38
-     * @return string
39
-     * @since   4.8
40
-     */
41
-    protected function _table_header($options)
42
-    {
43
-        $html = EEH_HTML::table('', '', $options['table_css_class']);
44
-        $html .= EEH_HTML::thead();
45
-        $html .= EEH_HTML::tr();
46
-        $html .= EEH_HTML::th(esc_html__('Name', 'event_espresso'), '', 'jst-left');
47
-        $html .= EEH_HTML::th(esc_html__('Type', 'event_espresso'), '', 'jst-left');
48
-        $html .= EEH_HTML::th(esc_html__('Date(s)', 'event_espresso'), '', 'jst-left');
49
-        $html .= EEH_HTML::th(esc_html__('Amount', 'event_espresso'), '', 'jst-cntr');
50
-        $html .= EEH_HTML::tbody();
51
-        return $html;
52
-    }
53
-
54
-
55
-    /**
56
-     *    _item_row
57
-     *
58
-     * @param EE_Line_Item $line_item
59
-     * @param array        $options
60
-     * @return mixed
61
-     * @throws EE_Error
62
-     * @throws ReflectionException
63
-     */
64
-    protected function _item_row(EE_Line_Item $line_item, $options = [])
65
-    {
66
-        if ($line_item->quantity() === 0) {
67
-            return '';
68
-        }
69
-        $line_item_related_object        = $line_item->get_object();
70
-        $parent_line_item_related_object = $line_item->parent() instanceof EE_Line_Item
71
-            ? $line_item->parent()->get_object()
72
-            : null;
73
-        // start of row
74
-        $row_class = $options['odd'] ? 'item odd' : 'item';
75
-        $html      = EEH_HTML::tr('', '', $row_class);
76
-
77
-
78
-        // Name Column
79
-        $name_link =
80
-            $line_item_related_object instanceof EEI_Admin_Links
81
-                ? $line_item_related_object->get_admin_details_link()
82
-                : '';
83
-
84
-        // related object scope.
85
-        $parent_related_object_name = $parent_line_item_related_object instanceof EEI_Line_Item_Object
86
-            ? $parent_line_item_related_object->name()
87
-            : '';
88
-        $parent_related_object_name = empty($parent_related_object_name) && $line_item->parent() instanceof EE_Line_Item
89
-            ? $line_item->parent()->name()
90
-            : $parent_related_object_name;
91
-        $parent_related_object_link = $parent_line_item_related_object instanceof EEI_Admin_Links
92
-            ? $parent_line_item_related_object->get_admin_details_link()
93
-            : '';
94
-
95
-        $name_html = $line_item_related_object instanceof EEI_Line_Item_Object
96
-            ? $line_item_related_object->name()
97
-            : $line_item->name();
98
-        $name_html = $name_link
99
-            ? '<a href="' . $name_link . '">' . $name_html . '</a>'
100
-            : $name_html;
101
-        $name_html .= $line_item->is_taxable() ? ' *' : '';
102
-
103
-        // maybe preface with icon?
104
-        $name_html = $line_item_related_object instanceof EEI_Has_Icon
105
-            ? $line_item_related_object->get_icon() . $name_html
106
-            : $name_html;
107
-
108
-        $name_html = '<span class="ee-line-item-name linked">' . $name_html . '</span><br>';
109
-        $name_html .= sprintf(
110
-            esc_html_x('%1$sfor the %2$s: %3$s%4$s', 'eg. "for the Event: My Cool Event"', 'event_espresso'),
111
-            '<span class="ee-line-item-related-parent-object">',
112
-            $line_item->parent() instanceof EE_Line_Item
113
-                ? $line_item->parent()->OBJ_type_i18n()
114
-                : esc_html__('Item:', 'event_espresso'),
115
-            $parent_related_object_link
116
-                ? '<a href="' . $parent_related_object_link . '">' . $parent_related_object_name . '</a>'
117
-                : $parent_related_object_name,
118
-            '</span>'
119
-        );
120
-
121
-        $name_html = apply_filters(
122
-            'FHEE__EE_Admin_Table_Registration_Line_Item_Display_Strategy___item_row__name_html',
123
-            $name_html,
124
-            $line_item,
125
-            $options
126
-        );
127
-
128
-        $html .= EEH_HTML::td($name_html, '', 'jst-left');
129
-        // Type Column
130
-        $type_html = $line_item->OBJ_type() ? $line_item->OBJ_type_i18n() : '';
131
-        $type_html .= $this->_get_cancellations($line_item);
132
-        $type_html .= $line_item->OBJ_type() ? '<br />' : '';
133
-        $code      = $line_item_related_object instanceof EEI_Has_Code ? $line_item_related_object->code() : '';
134
-        $type_html .= ! empty($code)
135
-            ? '<span class="ee-line-item-id">' . sprintf(esc_html__('Code: %s', 'event_espresso'), $code) . '</span>'
136
-            : '';
137
-        $html      .= EEH_HTML::td($type_html, '', 'jst-left');
138
-
139
-        // Date column
140
-        $datetime_content = '';
141
-        if ($line_item_related_object instanceof EE_Ticket) {
142
-            $datetimes = $line_item_related_object->datetimes();
143
-            foreach ($datetimes as $datetime) {
144
-                if ($datetime instanceof EE_Datetime) {
145
-                    $datetime_content .= $datetime->get_dtt_display_name() . '<br>';
146
-                }
147
-            }
148
-        }
149
-        $html .= EEH_HTML::td($datetime_content, '', 'jst-left');
150
-
151
-        // Amount Column
152
-        if ($line_item->is_percent()) {
153
-            $html .= EEH_HTML::td($line_item->percent() . '%', '', 'jst-rght');
154
-        } else {
155
-            $html .= EEH_HTML::td($line_item->unit_price_no_code(), '', 'jst-rght');
156
-        }
157
-
158
-
159
-        // finish things off and return
160
-        $html .= EEH_HTML::trx();
161
-        return $html;
162
-    }
163
-
164
-
165
-    /**
166
-     *  _tax_row
167
-     *
168
-     * @param EE_Line_Item $line_item
169
-     * @param array        $options
170
-     * @return mixed
171
-     * @throws EE_Error
172
-     * @throws ReflectionException
173
-     */
174
-    protected function _tax_row(EE_Line_Item $line_item, $options = [])
175
-    {
176
-        // start of row
177
-        $html = EEH_HTML::tr('', 'admin-primary-mbox-taxes-tr');
178
-        // name th
179
-        $html .= EEH_HTML::th(
180
-            $line_item->name() . '(' . $line_item->get_pretty('LIN_percent') . '%)',
181
-            '',
182
-            'jst-rght',
183
-            '',
184
-            ' colspan="3"'
185
-        );
186
-        // total th
187
-        $html .= EEH_HTML::th(EEH_Template::format_currency($line_item->total(), false, false), '', 'jst-rght');
188
-        // end of row
189
-        $html .= EEH_HTML::trx();
190
-        return $html;
191
-    }
192
-
193
-
194
-    /**
195
-     *  _total_row
196
-     *
197
-     * @param EE_Line_Item $line_item
198
-     * @param array        $options
199
-     * @return mixed
200
-     * @throws EE_Error
201
-     * @throws ReflectionException
202
-     */
203
-    protected function _total_row(EE_Line_Item $line_item, $options = [])
204
-    {
205
-        $registration_total = $this->registration instanceof EE_Registration
206
-            ? $this->registration->pretty_final_price()
207
-            : 0;
208
-        // if no valid registration object then we're not going to show the approximate text.
209
-        $total_match = $this->registration instanceof EE_Registration
210
-            ? $this->registration->final_price() === $line_item->total()
211
-            : true;
212
-
213
-        // start of row
214
-        $html = EEH_HTML::tr('', '', 'admin-primary-mbox-total-tr');
215
-        // Total th label
216
-        if ($total_match) {
217
-            $total_label =
218
-                sprintf(
219
-                    esc_html__('This registration\'s total %s:', 'event_espresso'),
220
-                    '(' . EE_Registry::instance()->CFG->currency->code . ')'
221
-                );
222
-        } else {
223
-            $total_label =
224
-                sprintf(
225
-                    esc_html__('This registration\'s approximate total %s', 'event_espresso'),
226
-                    '(' . EE_Registry::instance()->CFG->currency->code . ')'
227
-                );
228
-            $total_label .= '<br>';
229
-            $total_label .= '<p class="ee-footnote-text">'
230
-                            . sprintf(
231
-                                esc_html__(
232
-                                    'The registrations\' share of the transaction total is approximate because it might not be possible to evenly divide the transaction total among each registration, and so some registrations may need to pay a penny more than others.  This registration\'s final share is actually %1$s%2$s%3$s.',
233
-                                    'event_espresso'
234
-                                ),
235
-                                '<strong>',
236
-                                $registration_total,
237
-                                '</strong>'
238
-                            )
239
-                            . '</p>';
240
-        }
241
-        $html .= EEH_HTML::th($total_label, '', 'jst-rght', '', ' colspan="3"');
242
-        // total th
243
-
244
-        $html .= EEH_HTML::th(EEH_Template::format_currency($line_item->total(), false, false), '', 'jst-rght');
245
-        // end of row
246
-        $html .= EEH_HTML::trx();
247
-        return $html;
248
-    }
16
+	/**
17
+	 * @var EE_Registration
18
+	 */
19
+	protected $registration;
20
+
21
+
22
+	/**
23
+	 * EE_Admin_Table_Registration_Line_Item_Display_Strategy constructor.
24
+	 */
25
+	public function __construct()
26
+	{
27
+		$this->registration = isset($options['EE_Registration'])
28
+							  && $options['EE_Registration'] instanceof EE_Registration
29
+			? $options['EE_Registration']
30
+			: null;
31
+	}
32
+
33
+
34
+	/**
35
+	 * Table header for display.
36
+	 *
37
+	 * @param array $options
38
+	 * @return string
39
+	 * @since   4.8
40
+	 */
41
+	protected function _table_header($options)
42
+	{
43
+		$html = EEH_HTML::table('', '', $options['table_css_class']);
44
+		$html .= EEH_HTML::thead();
45
+		$html .= EEH_HTML::tr();
46
+		$html .= EEH_HTML::th(esc_html__('Name', 'event_espresso'), '', 'jst-left');
47
+		$html .= EEH_HTML::th(esc_html__('Type', 'event_espresso'), '', 'jst-left');
48
+		$html .= EEH_HTML::th(esc_html__('Date(s)', 'event_espresso'), '', 'jst-left');
49
+		$html .= EEH_HTML::th(esc_html__('Amount', 'event_espresso'), '', 'jst-cntr');
50
+		$html .= EEH_HTML::tbody();
51
+		return $html;
52
+	}
53
+
54
+
55
+	/**
56
+	 *    _item_row
57
+	 *
58
+	 * @param EE_Line_Item $line_item
59
+	 * @param array        $options
60
+	 * @return mixed
61
+	 * @throws EE_Error
62
+	 * @throws ReflectionException
63
+	 */
64
+	protected function _item_row(EE_Line_Item $line_item, $options = [])
65
+	{
66
+		if ($line_item->quantity() === 0) {
67
+			return '';
68
+		}
69
+		$line_item_related_object        = $line_item->get_object();
70
+		$parent_line_item_related_object = $line_item->parent() instanceof EE_Line_Item
71
+			? $line_item->parent()->get_object()
72
+			: null;
73
+		// start of row
74
+		$row_class = $options['odd'] ? 'item odd' : 'item';
75
+		$html      = EEH_HTML::tr('', '', $row_class);
76
+
77
+
78
+		// Name Column
79
+		$name_link =
80
+			$line_item_related_object instanceof EEI_Admin_Links
81
+				? $line_item_related_object->get_admin_details_link()
82
+				: '';
83
+
84
+		// related object scope.
85
+		$parent_related_object_name = $parent_line_item_related_object instanceof EEI_Line_Item_Object
86
+			? $parent_line_item_related_object->name()
87
+			: '';
88
+		$parent_related_object_name = empty($parent_related_object_name) && $line_item->parent() instanceof EE_Line_Item
89
+			? $line_item->parent()->name()
90
+			: $parent_related_object_name;
91
+		$parent_related_object_link = $parent_line_item_related_object instanceof EEI_Admin_Links
92
+			? $parent_line_item_related_object->get_admin_details_link()
93
+			: '';
94
+
95
+		$name_html = $line_item_related_object instanceof EEI_Line_Item_Object
96
+			? $line_item_related_object->name()
97
+			: $line_item->name();
98
+		$name_html = $name_link
99
+			? '<a href="' . $name_link . '">' . $name_html . '</a>'
100
+			: $name_html;
101
+		$name_html .= $line_item->is_taxable() ? ' *' : '';
102
+
103
+		// maybe preface with icon?
104
+		$name_html = $line_item_related_object instanceof EEI_Has_Icon
105
+			? $line_item_related_object->get_icon() . $name_html
106
+			: $name_html;
107
+
108
+		$name_html = '<span class="ee-line-item-name linked">' . $name_html . '</span><br>';
109
+		$name_html .= sprintf(
110
+			esc_html_x('%1$sfor the %2$s: %3$s%4$s', 'eg. "for the Event: My Cool Event"', 'event_espresso'),
111
+			'<span class="ee-line-item-related-parent-object">',
112
+			$line_item->parent() instanceof EE_Line_Item
113
+				? $line_item->parent()->OBJ_type_i18n()
114
+				: esc_html__('Item:', 'event_espresso'),
115
+			$parent_related_object_link
116
+				? '<a href="' . $parent_related_object_link . '">' . $parent_related_object_name . '</a>'
117
+				: $parent_related_object_name,
118
+			'</span>'
119
+		);
120
+
121
+		$name_html = apply_filters(
122
+			'FHEE__EE_Admin_Table_Registration_Line_Item_Display_Strategy___item_row__name_html',
123
+			$name_html,
124
+			$line_item,
125
+			$options
126
+		);
127
+
128
+		$html .= EEH_HTML::td($name_html, '', 'jst-left');
129
+		// Type Column
130
+		$type_html = $line_item->OBJ_type() ? $line_item->OBJ_type_i18n() : '';
131
+		$type_html .= $this->_get_cancellations($line_item);
132
+		$type_html .= $line_item->OBJ_type() ? '<br />' : '';
133
+		$code      = $line_item_related_object instanceof EEI_Has_Code ? $line_item_related_object->code() : '';
134
+		$type_html .= ! empty($code)
135
+			? '<span class="ee-line-item-id">' . sprintf(esc_html__('Code: %s', 'event_espresso'), $code) . '</span>'
136
+			: '';
137
+		$html      .= EEH_HTML::td($type_html, '', 'jst-left');
138
+
139
+		// Date column
140
+		$datetime_content = '';
141
+		if ($line_item_related_object instanceof EE_Ticket) {
142
+			$datetimes = $line_item_related_object->datetimes();
143
+			foreach ($datetimes as $datetime) {
144
+				if ($datetime instanceof EE_Datetime) {
145
+					$datetime_content .= $datetime->get_dtt_display_name() . '<br>';
146
+				}
147
+			}
148
+		}
149
+		$html .= EEH_HTML::td($datetime_content, '', 'jst-left');
150
+
151
+		// Amount Column
152
+		if ($line_item->is_percent()) {
153
+			$html .= EEH_HTML::td($line_item->percent() . '%', '', 'jst-rght');
154
+		} else {
155
+			$html .= EEH_HTML::td($line_item->unit_price_no_code(), '', 'jst-rght');
156
+		}
157
+
158
+
159
+		// finish things off and return
160
+		$html .= EEH_HTML::trx();
161
+		return $html;
162
+	}
163
+
164
+
165
+	/**
166
+	 *  _tax_row
167
+	 *
168
+	 * @param EE_Line_Item $line_item
169
+	 * @param array        $options
170
+	 * @return mixed
171
+	 * @throws EE_Error
172
+	 * @throws ReflectionException
173
+	 */
174
+	protected function _tax_row(EE_Line_Item $line_item, $options = [])
175
+	{
176
+		// start of row
177
+		$html = EEH_HTML::tr('', 'admin-primary-mbox-taxes-tr');
178
+		// name th
179
+		$html .= EEH_HTML::th(
180
+			$line_item->name() . '(' . $line_item->get_pretty('LIN_percent') . '%)',
181
+			'',
182
+			'jst-rght',
183
+			'',
184
+			' colspan="3"'
185
+		);
186
+		// total th
187
+		$html .= EEH_HTML::th(EEH_Template::format_currency($line_item->total(), false, false), '', 'jst-rght');
188
+		// end of row
189
+		$html .= EEH_HTML::trx();
190
+		return $html;
191
+	}
192
+
193
+
194
+	/**
195
+	 *  _total_row
196
+	 *
197
+	 * @param EE_Line_Item $line_item
198
+	 * @param array        $options
199
+	 * @return mixed
200
+	 * @throws EE_Error
201
+	 * @throws ReflectionException
202
+	 */
203
+	protected function _total_row(EE_Line_Item $line_item, $options = [])
204
+	{
205
+		$registration_total = $this->registration instanceof EE_Registration
206
+			? $this->registration->pretty_final_price()
207
+			: 0;
208
+		// if no valid registration object then we're not going to show the approximate text.
209
+		$total_match = $this->registration instanceof EE_Registration
210
+			? $this->registration->final_price() === $line_item->total()
211
+			: true;
212
+
213
+		// start of row
214
+		$html = EEH_HTML::tr('', '', 'admin-primary-mbox-total-tr');
215
+		// Total th label
216
+		if ($total_match) {
217
+			$total_label =
218
+				sprintf(
219
+					esc_html__('This registration\'s total %s:', 'event_espresso'),
220
+					'(' . EE_Registry::instance()->CFG->currency->code . ')'
221
+				);
222
+		} else {
223
+			$total_label =
224
+				sprintf(
225
+					esc_html__('This registration\'s approximate total %s', 'event_espresso'),
226
+					'(' . EE_Registry::instance()->CFG->currency->code . ')'
227
+				);
228
+			$total_label .= '<br>';
229
+			$total_label .= '<p class="ee-footnote-text">'
230
+							. sprintf(
231
+								esc_html__(
232
+									'The registrations\' share of the transaction total is approximate because it might not be possible to evenly divide the transaction total among each registration, and so some registrations may need to pay a penny more than others.  This registration\'s final share is actually %1$s%2$s%3$s.',
233
+									'event_espresso'
234
+								),
235
+								'<strong>',
236
+								$registration_total,
237
+								'</strong>'
238
+							)
239
+							. '</p>';
240
+		}
241
+		$html .= EEH_HTML::th($total_label, '', 'jst-rght', '', ' colspan="3"');
242
+		// total th
243
+
244
+		$html .= EEH_HTML::th(EEH_Template::format_currency($line_item->total(), false, false), '', 'jst-rght');
245
+		// end of row
246
+		$html .= EEH_HTML::trx();
247
+		return $html;
248
+	}
249 249
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -96,16 +96,16 @@  discard block
 block discarded – undo
96 96
             ? $line_item_related_object->name()
97 97
             : $line_item->name();
98 98
         $name_html = $name_link
99
-            ? '<a href="' . $name_link . '">' . $name_html . '</a>'
99
+            ? '<a href="'.$name_link.'">'.$name_html.'</a>'
100 100
             : $name_html;
101 101
         $name_html .= $line_item->is_taxable() ? ' *' : '';
102 102
 
103 103
         // maybe preface with icon?
104 104
         $name_html = $line_item_related_object instanceof EEI_Has_Icon
105
-            ? $line_item_related_object->get_icon() . $name_html
105
+            ? $line_item_related_object->get_icon().$name_html
106 106
             : $name_html;
107 107
 
108
-        $name_html = '<span class="ee-line-item-name linked">' . $name_html . '</span><br>';
108
+        $name_html = '<span class="ee-line-item-name linked">'.$name_html.'</span><br>';
109 109
         $name_html .= sprintf(
110 110
             esc_html_x('%1$sfor the %2$s: %3$s%4$s', 'eg. "for the Event: My Cool Event"', 'event_espresso'),
111 111
             '<span class="ee-line-item-related-parent-object">',
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
                 ? $line_item->parent()->OBJ_type_i18n()
114 114
                 : esc_html__('Item:', 'event_espresso'),
115 115
             $parent_related_object_link
116
-                ? '<a href="' . $parent_related_object_link . '">' . $parent_related_object_name . '</a>'
116
+                ? '<a href="'.$parent_related_object_link.'">'.$parent_related_object_name.'</a>'
117 117
                 : $parent_related_object_name,
118 118
             '</span>'
119 119
         );
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
         $type_html .= $line_item->OBJ_type() ? '<br />' : '';
133 133
         $code      = $line_item_related_object instanceof EEI_Has_Code ? $line_item_related_object->code() : '';
134 134
         $type_html .= ! empty($code)
135
-            ? '<span class="ee-line-item-id">' . sprintf(esc_html__('Code: %s', 'event_espresso'), $code) . '</span>'
135
+            ? '<span class="ee-line-item-id">'.sprintf(esc_html__('Code: %s', 'event_espresso'), $code).'</span>'
136 136
             : '';
137 137
         $html      .= EEH_HTML::td($type_html, '', 'jst-left');
138 138
 
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
             $datetimes = $line_item_related_object->datetimes();
143 143
             foreach ($datetimes as $datetime) {
144 144
                 if ($datetime instanceof EE_Datetime) {
145
-                    $datetime_content .= $datetime->get_dtt_display_name() . '<br>';
145
+                    $datetime_content .= $datetime->get_dtt_display_name().'<br>';
146 146
                 }
147 147
             }
148 148
         }
@@ -150,7 +150,7 @@  discard block
 block discarded – undo
150 150
 
151 151
         // Amount Column
152 152
         if ($line_item->is_percent()) {
153
-            $html .= EEH_HTML::td($line_item->percent() . '%', '', 'jst-rght');
153
+            $html .= EEH_HTML::td($line_item->percent().'%', '', 'jst-rght');
154 154
         } else {
155 155
             $html .= EEH_HTML::td($line_item->unit_price_no_code(), '', 'jst-rght');
156 156
         }
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
         $html = EEH_HTML::tr('', 'admin-primary-mbox-taxes-tr');
178 178
         // name th
179 179
         $html .= EEH_HTML::th(
180
-            $line_item->name() . '(' . $line_item->get_pretty('LIN_percent') . '%)',
180
+            $line_item->name().'('.$line_item->get_pretty('LIN_percent').'%)',
181 181
             '',
182 182
             'jst-rght',
183 183
             '',
@@ -217,13 +217,13 @@  discard block
 block discarded – undo
217 217
             $total_label =
218 218
                 sprintf(
219 219
                     esc_html__('This registration\'s total %s:', 'event_espresso'),
220
-                    '(' . EE_Registry::instance()->CFG->currency->code . ')'
220
+                    '('.EE_Registry::instance()->CFG->currency->code.')'
221 221
                 );
222 222
         } else {
223 223
             $total_label =
224 224
                 sprintf(
225 225
                     esc_html__('This registration\'s approximate total %s', 'event_espresso'),
226
-                    '(' . EE_Registry::instance()->CFG->currency->code . ')'
226
+                    '('.EE_Registry::instance()->CFG->currency->code.')'
227 227
                 );
228 228
             $total_label .= '<br>';
229 229
             $total_label .= '<p class="ee-footnote-text">'
Please login to merge, or discard this patch.
core/helpers/EEH_Line_Item.helper.php 2 patches
Spacing   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -58,7 +58,7 @@  discard block
 block discarded – undo
58 58
             $items_subtotal_children = $items_subtotal->children();
59 59
             $order = count($items_subtotal_children);
60 60
         }
61
-        $line_item      = EE_Line_Item::new_instance(
61
+        $line_item = EE_Line_Item::new_instance(
62 62
             [
63 63
                 'LIN_name'       => $name,
64 64
                 'LIN_desc'       => $description,
@@ -72,7 +72,7 @@  discard block
 block discarded – undo
72 72
                 'LIN_code'       => $code,
73 73
             ]
74 74
         );
75
-        $line_item      = apply_filters(
75
+        $line_item = apply_filters(
76 76
             'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
77 77
             $line_item,
78 78
             $parent_line_item
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
      */
149 149
     public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
150 150
     {
151
-        if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
151
+        if ( ! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
152 152
             throw new EE_Error(
153 153
                 sprintf(
154 154
                     esc_html__(
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
         // either increment the qty for an existing ticket
164 164
         $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
165 165
         // or add a new one
166
-        if (! $line_item instanceof EE_Line_Item) {
166
+        if ( ! $line_item instanceof EE_Line_Item) {
167 167
             $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
168 168
         }
169 169
         $total_line_item->recalculate_total_including_taxes();
@@ -225,7 +225,7 @@  discard block
 block discarded – undo
225 225
      */
226 226
     public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
227 227
     {
228
-        if (! $line_item->is_percent()) {
228
+        if ( ! $line_item->is_percent()) {
229 229
             $qty += $line_item->quantity();
230 230
             $line_item->set_quantity($qty);
231 231
             $line_item->set_total($line_item->unit_price() * $qty);
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
      */
255 255
     public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
256 256
     {
257
-        if (! $line_item->is_percent()) {
257
+        if ( ! $line_item->is_percent()) {
258 258
             $qty = $line_item->quantity() - $qty;
259 259
             $qty = max($qty, 0);
260 260
             $line_item->set_quantity($qty);
@@ -283,7 +283,7 @@  discard block
 block discarded – undo
283 283
      */
284 284
     public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
285 285
     {
286
-        if (! $line_item->is_percent()) {
286
+        if ( ! $line_item->is_percent()) {
287 287
             $line_item->set_quantity($new_quantity);
288 288
             $line_item->set_total($line_item->unit_price() * $new_quantity);
289 289
             $line_item->save();
@@ -324,7 +324,7 @@  discard block
 block discarded – undo
324 324
         $line_item = EE_Line_Item::new_instance(
325 325
             [
326 326
                 'LIN_name'       => $ticket->name(),
327
-                'LIN_desc'       => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
327
+                'LIN_desc'       => $ticket->description() !== '' ? $ticket->description().' '.$event : $event,
328 328
                 'LIN_unit_price' => $ticket->price(),
329 329
                 'LIN_quantity'   => $qty,
330 330
                 'LIN_is_taxable' => $ticket->taxable(),
@@ -479,7 +479,7 @@  discard block
 block discarded – undo
479 479
                             'event_espresso'
480 480
                         ),
481 481
                         $ticket_line_item->name(),
482
-                        current_time(get_option('date_format') . ' ' . get_option('time_format'))
482
+                        current_time(get_option('date_format').' '.get_option('time_format'))
483 483
                     ),
484 484
                     'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
485 485
                     'LIN_quantity'   => $qty,
@@ -543,7 +543,7 @@  discard block
 block discarded – undo
543 543
         );
544 544
         $cancellation_line_item = reset($cancellation_line_item);
545 545
         // verify that this ticket was indeed previously cancelled
546
-        if (! $cancellation_line_item instanceof EE_Line_Item) {
546
+        if ( ! $cancellation_line_item instanceof EE_Line_Item) {
547 547
             return false;
548 548
         }
549 549
         if ($cancellation_line_item->quantity() > $qty) {
@@ -754,7 +754,7 @@  discard block
 block discarded – undo
754 754
                 'LIN_code'  => 'taxes',
755 755
                 'LIN_name'  => esc_html__('Taxes', 'event_espresso'),
756 756
                 'LIN_type'  => EEM_Line_Item::type_tax_sub_total,
757
-                'LIN_order' => 1000,// this should always come last
757
+                'LIN_order' => 1000, // this should always come last
758 758
             ]
759 759
         );
760 760
         $tax_line_item = apply_filters(
@@ -813,7 +813,7 @@  discard block
 block discarded – undo
813 813
      */
814 814
     public static function get_event_code($event)
815 815
     {
816
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
816
+        return 'event-'.($event instanceof EE_Event ? $event->ID() : '0');
817 817
     }
818 818
 
819 819
 
@@ -862,7 +862,7 @@  discard block
 block discarded – undo
862 862
     public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
863 863
     {
864 864
         $first_datetime = $ticket->first_datetime();
865
-        if (! $first_datetime instanceof EE_Datetime) {
865
+        if ( ! $first_datetime instanceof EE_Datetime) {
866 866
             throw new EE_Error(
867 867
                 sprintf(
868 868
                     esc_html__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'),
@@ -871,7 +871,7 @@  discard block
 block discarded – undo
871 871
             );
872 872
         }
873 873
         $event = $first_datetime->event();
874
-        if (! $event instanceof EE_Event) {
874
+        if ( ! $event instanceof EE_Event) {
875 875
             throw new EE_Error(
876 876
                 sprintf(
877 877
                     esc_html__(
@@ -883,7 +883,7 @@  discard block
 block discarded – undo
883 883
             );
884 884
         }
885 885
         $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
886
-        if (! $events_sub_total instanceof EE_Line_Item) {
886
+        if ( ! $events_sub_total instanceof EE_Line_Item) {
887 887
             throw new EE_Error(
888 888
                 sprintf(
889 889
                     esc_html__(
@@ -919,7 +919,7 @@  discard block
 block discarded – undo
919 919
         $found           = false;
920 920
         foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
921 921
             // default event subtotal, we should only ever find this the first time this method is called
922
-            if (! $event_line_item->OBJ_ID()) {
922
+            if ( ! $event_line_item->OBJ_ID()) {
923 923
                 // let's use this! but first... set the event details
924 924
                 EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
925 925
                 $found = true;
@@ -931,7 +931,7 @@  discard block
 block discarded – undo
931 931
                 break;
932 932
             }
933 933
         }
934
-        if (! $found) {
934
+        if ( ! $found) {
935 935
             // there is no event sub-total yet, so add it
936 936
             $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
937 937
             // create a new "event" subtotal below that
@@ -1018,7 +1018,7 @@  discard block
 block discarded – undo
1018 1018
                         'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
1019 1019
                         $tax_line_item
1020 1020
                     );
1021
-                    $updates       = $taxes_line_item->add_child_line_item($tax_line_item)
1021
+                    $updates = $taxes_line_item->add_child_line_item($tax_line_item)
1022 1022
                         ? true
1023 1023
                         : $updates;
1024 1024
                 }
@@ -1048,7 +1048,7 @@  discard block
 block discarded – undo
1048 1048
     public static function ensure_taxes_applied($total_line_item)
1049 1049
     {
1050 1050
         $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
1051
-        if (! $taxes_subtotal->children()) {
1051
+        if ( ! $taxes_subtotal->children()) {
1052 1052
             self::apply_taxes($total_line_item);
1053 1053
         }
1054 1054
         return $taxes_subtotal->total();
@@ -1114,7 +1114,7 @@  discard block
 block discarded – undo
1114 1114
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1115 1115
 
1116 1116
         // check if only a single line_item_id was passed
1117
-        if (! empty($line_item_codes) && ! is_array($line_item_codes)) {
1117
+        if ( ! empty($line_item_codes) && ! is_array($line_item_codes)) {
1118 1118
             // place single line_item_id in an array to appear as multiple line_item_ids
1119 1119
             $line_item_codes = [$line_item_codes];
1120 1120
         }
@@ -1223,7 +1223,7 @@  discard block
 block discarded – undo
1223 1223
         if ($code_substring_for_whitelist !== null) {
1224 1224
             $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false;
1225 1225
         }
1226
-        if (! $whitelisted && $line_item->is_line_item()) {
1226
+        if ( ! $whitelisted && $line_item->is_line_item()) {
1227 1227
             $line_item->set_is_taxable($taxable);
1228 1228
         }
1229 1229
         foreach ($line_item->children() as $child_line_item) {
@@ -1584,7 +1584,7 @@  discard block
 block discarded – undo
1584 1584
     {
1585 1585
         $linebreak = defined('EE_TESTS_DIR') ? "\n" : '<br />';
1586 1586
         echo $linebreak;
1587
-        if (! $indentation) {
1587
+        if ( ! $indentation) {
1588 1588
             echo $linebreak;
1589 1589
         }
1590 1590
         for ($i = 0; $i < $indentation; $i++) {
@@ -1595,12 +1595,12 @@  discard block
 block discarded – undo
1595 1595
             if ($line_item->is_percent()) {
1596 1596
                 $breakdown = "{$line_item->percent()}%";
1597 1597
             } else {
1598
-                $breakdown = ' $' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1598
+                $breakdown = ' $'."{$line_item->unit_price()} x {$line_item->quantity()}";
1599 1599
             }
1600 1600
         }
1601 1601
         echo wp_kses($line_item->name(), AllowedTags::getAllowedTags());
1602 1602
         echo " [ ID: {$line_item->ID()} · qty: {$line_item->quantity()} ] {$line_item->type()} : ";
1603
-        echo ' $' . (string) $line_item->total();
1603
+        echo ' $'.(string) $line_item->total();
1604 1604
         if ($breakdown) {
1605 1605
             echo " ( {$breakdown} )";
1606 1606
         }
@@ -1613,7 +1613,7 @@  discard block
 block discarded – undo
1613 1613
             }
1614 1614
         }
1615 1615
         if ($line_item->is_total()) {
1616
-            echo $linebreak . $linebreak;
1616
+            echo $linebreak.$linebreak;
1617 1617
         }
1618 1618
     }
1619 1619
 
@@ -1694,8 +1694,8 @@  discard block
 block discarded – undo
1694 1694
                         if ($line_item_id === 'taxable') {
1695 1695
                             continue;
1696 1696
                         }
1697
-                        $taxable_total                   = $running_totals['taxable'][ $line_item_id ];
1698
-                        $running_totals[ $line_item_id ] += ($taxable_total * $tax_percent_decimal);
1697
+                        $taxable_total = $running_totals['taxable'][$line_item_id];
1698
+                        $running_totals[$line_item_id] += ($taxable_total * $tax_percent_decimal);
1699 1699
                     }
1700 1700
                     break;
1701 1701
 
@@ -1703,7 +1703,7 @@  discard block
 block discarded – undo
1703 1703
                     // ticket line items or ????
1704 1704
                     if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
1705 1705
                         // kk it's a ticket
1706
-                        if (isset($running_totals[ $child_line_item->ID() ])) {
1706
+                        if (isset($running_totals[$child_line_item->ID()])) {
1707 1707
                             // huh? that shouldn't happen.
1708 1708
                             $running_totals['total'] += $child_line_item->total();
1709 1709
                         } else {
@@ -1714,19 +1714,19 @@  discard block
 block discarded – undo
1714 1714
                                 $taxable_amount = 0;
1715 1715
                             }
1716 1716
                             // are we only calculating totals for some tickets?
1717
-                            if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) {
1717
+                            if (isset($billable_ticket_quantities[$child_line_item->OBJ_ID()])) {
1718 1718
                                 $quantity                                            =
1719
-                                    $billable_ticket_quantities[ $child_line_item->OBJ_ID() ];
1720
-                                $running_totals[ $child_line_item->ID() ]            = $quantity
1719
+                                    $billable_ticket_quantities[$child_line_item->OBJ_ID()];
1720
+                                $running_totals[$child_line_item->ID()]            = $quantity
1721 1721
                                     ? $child_line_item->unit_price()
1722 1722
                                     : 0;
1723
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $quantity
1723
+                                $running_totals['taxable'][$child_line_item->ID()] = $quantity
1724 1724
                                     ? $taxable_amount
1725 1725
                                     : 0;
1726 1726
                             } else {
1727 1727
                                 $quantity                                            = $child_line_item->quantity();
1728
-                                $running_totals[ $child_line_item->ID() ]            = $child_line_item->unit_price();
1729
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount;
1728
+                                $running_totals[$child_line_item->ID()]            = $child_line_item->unit_price();
1729
+                                $running_totals['taxable'][$child_line_item->ID()] = $taxable_amount;
1730 1730
                             }
1731 1731
                             $running_totals['taxable']['total'] += $taxable_amount * $quantity;
1732 1732
                             $running_totals['total']            += $child_line_item->unit_price() * $quantity;
@@ -1746,12 +1746,12 @@  discard block
 block discarded – undo
1746 1746
                             }
1747 1747
                             // update the running totals
1748 1748
                             // yes this actually even works for the running grand total!
1749
-                            $running_totals[ $line_item_id ] =
1749
+                            $running_totals[$line_item_id] =
1750 1750
                                 $line_items_percent_of_running_total * $this_running_total;
1751 1751
 
1752 1752
                             if ($child_line_item->is_taxable()) {
1753
-                                $running_totals['taxable'][ $line_item_id ] =
1754
-                                    $line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ];
1753
+                                $running_totals['taxable'][$line_item_id] =
1754
+                                    $line_items_percent_of_running_total * $running_totals['taxable'][$line_item_id];
1755 1755
                             }
1756 1756
                         }
1757 1757
                     }
@@ -1779,16 +1779,16 @@  discard block
 block discarded – undo
1779 1779
     ) {
1780 1780
         static $final_prices_per_ticket_line_item = [];
1781 1781
         if (empty($final_prices_per_ticket_line_item)
1782
-            || empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])
1782
+            || empty($final_prices_per_ticket_line_item[$total_line_item->ID()])
1783 1783
         ) {
1784
-            $final_prices_per_ticket_line_item[ $total_line_item->ID() ] =
1784
+            $final_prices_per_ticket_line_item[$total_line_item->ID()] =
1785 1785
                 EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1786 1786
                     $total_line_item
1787 1787
                 );
1788 1788
         }
1789 1789
         // ok now find this new registration's final price
1790
-        if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) {
1791
-            return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ];
1790
+        if (isset($final_prices_per_ticket_line_item[$total_line_item->ID()][$ticket_line_item->ID()])) {
1791
+            return $final_prices_per_ticket_line_item[$total_line_item->ID()][$ticket_line_item->ID()];
1792 1792
         }
1793 1793
         $message = sprintf(
1794 1794
             esc_html__(
@@ -1798,7 +1798,7 @@  discard block
 block discarded – undo
1798 1798
             $ticket_line_item->ID()
1799 1799
         );
1800 1800
         if (WP_DEBUG) {
1801
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1801
+            $message .= '<br>'.print_r($final_prices_per_ticket_line_item, true);
1802 1802
             throw new OutOfRangeException($message);
1803 1803
         }
1804 1804
         EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
Please login to merge, or discard this patch.
Indentation   +2084 added lines, -2084 removed lines patch added patch discarded remove patch
@@ -21,2088 +21,2088 @@
 block discarded – undo
21 21
  */
22 22
 class EEH_Line_Item
23 23
 {
24
-    /**
25
-     * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
26
-     * Does NOT automatically re-calculate the line item totals or update the related transaction.
27
-     * You should call recalculate_total_including_taxes() on the grant total line item after this
28
-     * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
29
-     * to keep the registration final prices in-sync with the transaction's total.
30
-     *
31
-     * @param EE_Line_Item $parent_line_item
32
-     * @param string       $name
33
-     * @param float        $unit_price
34
-     * @param string       $description
35
-     * @param int          $quantity
36
-     * @param boolean      $taxable
37
-     * @param boolean      $code if set to a value, ensures there is only one line item with that code
38
-     * @return boolean success
39
-     * @throws EE_Error
40
-     * @throws InvalidArgumentException
41
-     * @throws InvalidDataTypeException
42
-     * @throws InvalidInterfaceException
43
-     * @throws ReflectionException
44
-     */
45
-    public static function add_unrelated_item(
46
-        EE_Line_Item $parent_line_item,
47
-        $name,
48
-        $unit_price,
49
-        $description = '',
50
-        $quantity = 1,
51
-        $taxable = false,
52
-        $code = null
53
-    ) {
54
-        $items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
55
-        $order = 0;
56
-        if ($items_subtotal instanceof EE_Line_Item) {
57
-            $items_subtotal_children = $items_subtotal->children();
58
-            $order = count($items_subtotal_children);
59
-        }
60
-        $line_item      = EE_Line_Item::new_instance(
61
-            [
62
-                'LIN_name'       => $name,
63
-                'LIN_desc'       => $description,
64
-                'LIN_unit_price' => $unit_price,
65
-                'LIN_quantity'   => $quantity,
66
-                'LIN_percent'    => null,
67
-                'LIN_is_taxable' => $taxable,
68
-                'LIN_order'      => $order,
69
-                'LIN_total'      => (float) $unit_price * (int) $quantity,
70
-                'LIN_type'       => EEM_Line_Item::type_line_item,
71
-                'LIN_code'       => $code,
72
-            ]
73
-        );
74
-        $line_item      = apply_filters(
75
-            'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
76
-            $line_item,
77
-            $parent_line_item
78
-        );
79
-        return self::add_item($parent_line_item, $line_item);
80
-    }
81
-
82
-
83
-    /**
84
-     * Adds a simple item ( unrelated to any other model object) to the total line item,
85
-     * in the correct spot in the line item tree. Does not automatically
86
-     * re-calculate the line item totals, nor update the related transaction, nor upgrade the transaction's
87
-     * registrations' final prices (which should probably change because of this).
88
-     * You should call recalculate_total_including_taxes() on the grand total line item, then
89
-     * update the transaction's total, and EE_Registration_Processor::update_registration_final_prices()
90
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
91
-     *
92
-     * @param EE_Line_Item $parent_line_item
93
-     * @param string       $name
94
-     * @param float        $percentage_amount
95
-     * @param string       $description
96
-     * @param boolean      $taxable
97
-     * @return boolean success
98
-     * @throws EE_Error|ReflectionException
99
-     */
100
-    public static function add_percentage_based_item(
101
-        EE_Line_Item $parent_line_item,
102
-        $name,
103
-        $percentage_amount,
104
-        $description = '',
105
-        $taxable = false
106
-    ) {
107
-        $line_item = EE_Line_Item::new_instance(
108
-            [
109
-                'LIN_name'       => $name,
110
-                'LIN_desc'       => $description,
111
-                'LIN_unit_price' => 0,
112
-                'LIN_percent'    => $percentage_amount,
113
-                'LIN_quantity'   => 1,
114
-                'LIN_is_taxable' => $taxable,
115
-                'LIN_total'      => (float) ($percentage_amount * ($parent_line_item->total() / 100)),
116
-                'LIN_type'       => EEM_Line_Item::type_line_item,
117
-                'LIN_parent'     => $parent_line_item->ID(),
118
-            ]
119
-        );
120
-        $line_item = apply_filters(
121
-            'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
122
-            $line_item
123
-        );
124
-        return $parent_line_item->add_child_line_item($line_item, false);
125
-    }
126
-
127
-
128
-    /**
129
-     * Returns the new line item created by adding a purchase of the ticket
130
-     * ensures that ticket line item is saved, and that cart total has been recalculated.
131
-     * If this ticket has already been purchased, just increments its count.
132
-     * Automatically re-calculates the line item totals and updates the related transaction. But
133
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
134
-     * should probably change because of this).
135
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
136
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
137
-     *
138
-     * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
139
-     * @param EE_Ticket    $ticket
140
-     * @param int          $qty
141
-     * @return EE_Line_Item
142
-     * @throws EE_Error
143
-     * @throws InvalidArgumentException
144
-     * @throws InvalidDataTypeException
145
-     * @throws InvalidInterfaceException
146
-     * @throws ReflectionException
147
-     */
148
-    public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
149
-    {
150
-        if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
151
-            throw new EE_Error(
152
-                sprintf(
153
-                    esc_html__(
154
-                        'A valid line item total is required in order to add tickets. A line item of type "%s" was passed.',
155
-                        'event_espresso'
156
-                    ),
157
-                    $ticket->ID(),
158
-                    $total_line_item->ID()
159
-                )
160
-            );
161
-        }
162
-        // either increment the qty for an existing ticket
163
-        $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
164
-        // or add a new one
165
-        if (! $line_item instanceof EE_Line_Item) {
166
-            $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
167
-        }
168
-        $total_line_item->recalculate_total_including_taxes();
169
-        return $line_item;
170
-    }
171
-
172
-
173
-    /**
174
-     * Returns the new line item created by adding a purchase of the ticket
175
-     *
176
-     * @param EE_Line_Item $total_line_item
177
-     * @param EE_Ticket    $ticket
178
-     * @param int          $qty
179
-     * @return EE_Line_Item
180
-     * @throws EE_Error
181
-     * @throws InvalidArgumentException
182
-     * @throws InvalidDataTypeException
183
-     * @throws InvalidInterfaceException
184
-     * @throws ReflectionException
185
-     */
186
-    public static function increment_ticket_qty_if_already_in_cart(
187
-        EE_Line_Item $total_line_item,
188
-        EE_Ticket $ticket,
189
-        $qty = 1
190
-    ) {
191
-        $line_item = null;
192
-        if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
193
-            $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
194
-            foreach ((array) $ticket_line_items as $ticket_line_item) {
195
-                if (
196
-                    $ticket_line_item instanceof EE_Line_Item
197
-                    && (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID()
198
-                ) {
199
-                    $line_item = $ticket_line_item;
200
-                    break;
201
-                }
202
-            }
203
-        }
204
-        if ($line_item instanceof EE_Line_Item) {
205
-            EEH_Line_Item::increment_quantity($line_item, $qty);
206
-            return $line_item;
207
-        }
208
-        return null;
209
-    }
210
-
211
-
212
-    /**
213
-     * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
214
-     * Does NOT save or recalculate other line items totals
215
-     *
216
-     * @param EE_Line_Item $line_item
217
-     * @param int          $qty
218
-     * @return void
219
-     * @throws EE_Error
220
-     * @throws InvalidArgumentException
221
-     * @throws InvalidDataTypeException
222
-     * @throws InvalidInterfaceException
223
-     * @throws ReflectionException
224
-     */
225
-    public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
226
-    {
227
-        if (! $line_item->is_percent()) {
228
-            $qty += $line_item->quantity();
229
-            $line_item->set_quantity($qty);
230
-            $line_item->set_total($line_item->unit_price() * $qty);
231
-            $line_item->save();
232
-        }
233
-        foreach ($line_item->children() as $child) {
234
-            if ($child->is_sub_line_item()) {
235
-                EEH_Line_Item::update_quantity($child, $qty);
236
-            }
237
-        }
238
-    }
239
-
240
-
241
-    /**
242
-     * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
243
-     * Does NOT save or recalculate other line items totals
244
-     *
245
-     * @param EE_Line_Item $line_item
246
-     * @param int          $qty
247
-     * @return void
248
-     * @throws EE_Error
249
-     * @throws InvalidArgumentException
250
-     * @throws InvalidDataTypeException
251
-     * @throws InvalidInterfaceException
252
-     * @throws ReflectionException
253
-     */
254
-    public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
255
-    {
256
-        if (! $line_item->is_percent()) {
257
-            $qty = $line_item->quantity() - $qty;
258
-            $qty = max($qty, 0);
259
-            $line_item->set_quantity($qty);
260
-            $line_item->set_total($line_item->unit_price() * $qty);
261
-            $line_item->save();
262
-        }
263
-        foreach ($line_item->children() as $child) {
264
-            if ($child->is_sub_line_item()) {
265
-                EEH_Line_Item::update_quantity($child, $qty);
266
-            }
267
-        }
268
-    }
269
-
270
-
271
-    /**
272
-     * Updates the line item and its children's quantities to the specified number.
273
-     * Does NOT save them or recalculate totals.
274
-     *
275
-     * @param EE_Line_Item $line_item
276
-     * @param int          $new_quantity
277
-     * @throws EE_Error
278
-     * @throws InvalidArgumentException
279
-     * @throws InvalidDataTypeException
280
-     * @throws InvalidInterfaceException
281
-     * @throws ReflectionException
282
-     */
283
-    public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
284
-    {
285
-        if (! $line_item->is_percent()) {
286
-            $line_item->set_quantity($new_quantity);
287
-            $line_item->set_total($line_item->unit_price() * $new_quantity);
288
-            $line_item->save();
289
-        }
290
-        foreach ($line_item->children() as $child) {
291
-            if ($child->is_sub_line_item()) {
292
-                EEH_Line_Item::update_quantity($child, $new_quantity);
293
-            }
294
-        }
295
-    }
296
-
297
-
298
-    /**
299
-     * Returns the new line item created by adding a purchase of the ticket
300
-     *
301
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
302
-     * @param EE_Ticket    $ticket
303
-     * @param int          $qty
304
-     * @return EE_Line_Item
305
-     * @throws EE_Error
306
-     * @throws InvalidArgumentException
307
-     * @throws InvalidDataTypeException
308
-     * @throws InvalidInterfaceException
309
-     * @throws ReflectionException
310
-     */
311
-    public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
312
-    {
313
-        $datetimes           = $ticket->datetimes();
314
-        $first_datetime      = reset($datetimes);
315
-        $first_datetime_name = esc_html__('Event', 'event_espresso');
316
-        if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
317
-            $first_datetime_name = $first_datetime->event()->name();
318
-        }
319
-        $event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
320
-        // get event subtotal line
321
-        $events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
322
-        // add $ticket to cart
323
-        $line_item = EE_Line_Item::new_instance(
324
-            [
325
-                'LIN_name'       => $ticket->name(),
326
-                'LIN_desc'       => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
327
-                'LIN_unit_price' => $ticket->price(),
328
-                'LIN_quantity'   => $qty,
329
-                'LIN_is_taxable' => $ticket->taxable(),
330
-                'LIN_order'      => count($events_sub_total->children()),
331
-                'LIN_total'      => $ticket->price() * $qty,
332
-                'LIN_type'       => EEM_Line_Item::type_line_item,
333
-                'OBJ_ID'         => $ticket->ID(),
334
-                'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_TICKET,
335
-            ]
336
-        );
337
-        $line_item = apply_filters(
338
-            'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
339
-            $line_item
340
-        );
341
-        $events_sub_total->add_child_line_item($line_item);
342
-        // now add the sub-line items
343
-        $running_total_for_ticket = 0;
344
-        foreach ($ticket->prices(['order_by' => ['PRC_order' => 'ASC']]) as $price) {
345
-            $sign          = $price->is_discount() ? -1 : 1;
346
-            $price_total   = $price->is_percent()
347
-                ? $running_total_for_ticket * $price->amount() / 100
348
-                : $price->amount() * $qty;
349
-            $sub_line_item = EE_Line_Item::new_instance(
350
-                [
351
-                    'LIN_name'       => $price->name(),
352
-                    'LIN_desc'       => $price->desc(),
353
-                    'LIN_quantity'   => $price->is_percent() ? null : $qty,
354
-                    'LIN_is_taxable' => false,
355
-                    'LIN_order'      => $price->order(),
356
-                    'LIN_total'      => $sign * $price_total,
357
-                    'LIN_type'       => EEM_Line_Item::type_sub_line_item,
358
-                    'OBJ_ID'         => $price->ID(),
359
-                    'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_PRICE,
360
-                ]
361
-            );
362
-            $sub_line_item = apply_filters(
363
-                'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
364
-                $sub_line_item
365
-            );
366
-            if ($price->is_percent()) {
367
-                $sub_line_item->set_percent($sign * $price->amount());
368
-            } else {
369
-                $sub_line_item->set_unit_price($sign * $price->amount());
370
-            }
371
-            $running_total_for_ticket += $price_total;
372
-            $line_item->add_child_line_item($sub_line_item);
373
-        }
374
-        return $line_item;
375
-    }
376
-
377
-
378
-    /**
379
-     * Adds the specified item under the pre-tax-sub-total line item. Automatically
380
-     * re-calculates the line item totals and updates the related transaction. But
381
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
382
-     * should probably change because of this).
383
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
384
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
385
-     *
386
-     * @param EE_Line_Item $total_line_item
387
-     * @param EE_Line_Item $item to be added
388
-     * @return boolean
389
-     * @throws EE_Error
390
-     * @throws InvalidArgumentException
391
-     * @throws InvalidDataTypeException
392
-     * @throws InvalidInterfaceException
393
-     * @throws ReflectionException
394
-     */
395
-    public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item)
396
-    {
397
-        $pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
398
-        if ($pre_tax_subtotal instanceof EE_Line_Item) {
399
-            $success = $pre_tax_subtotal->add_child_line_item($item);
400
-        } else {
401
-            return false;
402
-        }
403
-        $total_line_item->recalculate_total_including_taxes();
404
-        return $success;
405
-    }
406
-
407
-
408
-    /**
409
-     * cancels an existing ticket line item,
410
-     * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
411
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
412
-     *
413
-     * @param EE_Line_Item $ticket_line_item
414
-     * @param int          $qty
415
-     * @return bool success
416
-     * @throws EE_Error
417
-     * @throws InvalidArgumentException
418
-     * @throws InvalidDataTypeException
419
-     * @throws InvalidInterfaceException
420
-     * @throws ReflectionException
421
-     */
422
-    public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
423
-    {
424
-        // validate incoming line_item
425
-        if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) {
426
-            throw new EE_Error(
427
-                sprintf(
428
-                    esc_html__(
429
-                        'The supplied line item must have an Object Type of "Ticket", not %1$s.',
430
-                        'event_espresso'
431
-                    ),
432
-                    $ticket_line_item->type()
433
-                )
434
-            );
435
-        }
436
-        if ($ticket_line_item->quantity() < $qty) {
437
-            throw new EE_Error(
438
-                sprintf(
439
-                    esc_html__(
440
-                        'Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.',
441
-                        'event_espresso'
442
-                    ),
443
-                    $qty,
444
-                    $ticket_line_item->quantity()
445
-                )
446
-            );
447
-        }
448
-        // decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
449
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
450
-        foreach ($ticket_line_item->children() as $child_line_item) {
451
-            if (
452
-                $child_line_item->is_sub_line_item()
453
-                && ! $child_line_item->is_percent()
454
-                && ! $child_line_item->is_cancellation()
455
-            ) {
456
-                $child_line_item->set_quantity($child_line_item->quantity() - $qty);
457
-            }
458
-        }
459
-        // get cancellation sub line item
460
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
461
-            $ticket_line_item,
462
-            EEM_Line_Item::type_cancellation
463
-        );
464
-        $cancellation_line_item = reset($cancellation_line_item);
465
-        // verify that this ticket was indeed previously cancelled
466
-        if ($cancellation_line_item instanceof EE_Line_Item) {
467
-            // increment cancelled quantity
468
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
469
-        } else {
470
-            // create cancellation sub line item
471
-            $cancellation_line_item = EE_Line_Item::new_instance(
472
-                [
473
-                    'LIN_name'       => esc_html__('Cancellation', 'event_espresso'),
474
-                    'LIN_desc'       => sprintf(
475
-                        esc_html_x(
476
-                            'Cancelled %1$s : %2$s',
477
-                            'Cancelled Ticket Name : 2015-01-01 11:11',
478
-                            'event_espresso'
479
-                        ),
480
-                        $ticket_line_item->name(),
481
-                        current_time(get_option('date_format') . ' ' . get_option('time_format'))
482
-                    ),
483
-                    'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
484
-                    'LIN_quantity'   => $qty,
485
-                    'LIN_is_taxable' => $ticket_line_item->is_taxable(),
486
-                    'LIN_order'      => count($ticket_line_item->children()),
487
-                    'LIN_total'      => 0, // $ticket_line_item->unit_price()
488
-                    'LIN_type'       => EEM_Line_Item::type_cancellation,
489
-                ]
490
-            );
491
-            $ticket_line_item->add_child_line_item($cancellation_line_item);
492
-        }
493
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
494
-            // decrement parent line item quantity
495
-            $event_line_item = $ticket_line_item->parent();
496
-            if (
497
-                $event_line_item instanceof EE_Line_Item
498
-                && $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
499
-            ) {
500
-                $event_line_item->set_quantity($event_line_item->quantity() - $qty);
501
-                $event_line_item->save();
502
-            }
503
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
504
-            return true;
505
-        }
506
-        return false;
507
-    }
508
-
509
-
510
-    /**
511
-     * reinstates (un-cancels?) a previously canceled ticket line item,
512
-     * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
513
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
514
-     *
515
-     * @param EE_Line_Item $ticket_line_item
516
-     * @param int          $qty
517
-     * @return bool success
518
-     * @throws EE_Error
519
-     * @throws InvalidArgumentException
520
-     * @throws InvalidDataTypeException
521
-     * @throws InvalidInterfaceException
522
-     * @throws ReflectionException
523
-     */
524
-    public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
525
-    {
526
-        // validate incoming line_item
527
-        if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) {
528
-            throw new EE_Error(
529
-                sprintf(
530
-                    esc_html__(
531
-                        'The supplied line item must have an Object Type of "Ticket", not %1$s.',
532
-                        'event_espresso'
533
-                    ),
534
-                    $ticket_line_item->type()
535
-                )
536
-            );
537
-        }
538
-        // get cancellation sub line item
539
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
540
-            $ticket_line_item,
541
-            EEM_Line_Item::type_cancellation
542
-        );
543
-        $cancellation_line_item = reset($cancellation_line_item);
544
-        // verify that this ticket was indeed previously cancelled
545
-        if (! $cancellation_line_item instanceof EE_Line_Item) {
546
-            return false;
547
-        }
548
-        if ($cancellation_line_item->quantity() > $qty) {
549
-            // decrement cancelled quantity
550
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
551
-        } elseif ($cancellation_line_item->quantity() === $qty) {
552
-            // decrement cancelled quantity in case anyone still has the object kicking around
553
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
554
-            // delete because quantity will end up as 0
555
-            $cancellation_line_item->delete();
556
-            // and attempt to destroy the object,
557
-            // even though PHP won't actually destroy it until it needs the memory
558
-            unset($cancellation_line_item);
559
-        } else {
560
-            // what ?!?! negative quantity ?!?!
561
-            throw new EE_Error(
562
-                sprintf(
563
-                    esc_html__(
564
-                        'Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
565
-                        'event_espresso'
566
-                    ),
567
-                    $qty,
568
-                    $cancellation_line_item->quantity()
569
-                )
570
-            );
571
-        }
572
-        // increment ticket quantity
573
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
574
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
575
-            // increment parent line item quantity
576
-            $event_line_item = $ticket_line_item->parent();
577
-            if (
578
-                $event_line_item instanceof EE_Line_Item
579
-                && $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
580
-            ) {
581
-                $event_line_item->set_quantity($event_line_item->quantity() + $qty);
582
-            }
583
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
584
-            return true;
585
-        }
586
-        return false;
587
-    }
588
-
589
-
590
-    /**
591
-     * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
592
-     * then EE_Line_Item::recalculate_total_including_taxes() on the result
593
-     *
594
-     * @param EE_Line_Item $line_item
595
-     * @return float
596
-     * @throws EE_Error
597
-     * @throws InvalidArgumentException
598
-     * @throws InvalidDataTypeException
599
-     * @throws InvalidInterfaceException
600
-     * @throws ReflectionException
601
-     */
602
-    public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
603
-    {
604
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
605
-        return $grand_total_line_item->recalculate_total_including_taxes();
606
-    }
607
-
608
-
609
-    /**
610
-     * Gets the line item which contains the subtotal of all the items
611
-     *
612
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
613
-     * @return EE_Line_Item
614
-     * @throws EE_Error
615
-     * @throws InvalidArgumentException
616
-     * @throws InvalidDataTypeException
617
-     * @throws InvalidInterfaceException
618
-     * @throws ReflectionException
619
-     */
620
-    public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
621
-    {
622
-        $pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
623
-        return $pre_tax_subtotal instanceof EE_Line_Item
624
-            ? $pre_tax_subtotal
625
-            : self::create_pre_tax_subtotal($total_line_item);
626
-    }
627
-
628
-
629
-    /**
630
-     * Gets the line item for the taxes subtotal
631
-     *
632
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
633
-     * @return EE_Line_Item
634
-     * @throws EE_Error
635
-     * @throws InvalidArgumentException
636
-     * @throws InvalidDataTypeException
637
-     * @throws InvalidInterfaceException
638
-     * @throws ReflectionException
639
-     */
640
-    public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
641
-    {
642
-        $taxes = $total_line_item->get_child_line_item('taxes');
643
-        return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
644
-    }
645
-
646
-
647
-    /**
648
-     * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
649
-     *
650
-     * @param EE_Line_Item   $line_item
651
-     * @param EE_Transaction $transaction
652
-     * @return void
653
-     * @throws EE_Error
654
-     * @throws InvalidArgumentException
655
-     * @throws InvalidDataTypeException
656
-     * @throws InvalidInterfaceException
657
-     * @throws ReflectionException
658
-     */
659
-    public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = null)
660
-    {
661
-        if ($transaction) {
662
-            /** @type EEM_Transaction $EEM_Transaction */
663
-            $EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
664
-            $TXN_ID          = $EEM_Transaction->ensure_is_ID($transaction);
665
-            $line_item->set_TXN_ID($TXN_ID);
666
-        }
667
-    }
668
-
669
-
670
-    /**
671
-     * Creates a new default total line item for the transaction,
672
-     * and its tickets subtotal and taxes subtotal line items (and adds the
673
-     * existing taxes as children of the taxes subtotal line item)
674
-     *
675
-     * @param EE_Transaction $transaction
676
-     * @return EE_Line_Item of type total
677
-     * @throws EE_Error
678
-     * @throws InvalidArgumentException
679
-     * @throws InvalidDataTypeException
680
-     * @throws InvalidInterfaceException
681
-     * @throws ReflectionException
682
-     */
683
-    public static function create_total_line_item($transaction = null)
684
-    {
685
-        $total_line_item = EE_Line_Item::new_instance(
686
-            [
687
-                'LIN_code' => 'total',
688
-                'LIN_name' => esc_html__('Grand Total', 'event_espresso'),
689
-                'LIN_type' => EEM_Line_Item::type_total,
690
-                'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TRANSACTION,
691
-            ]
692
-        );
693
-        $total_line_item = apply_filters(
694
-            'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
695
-            $total_line_item
696
-        );
697
-        self::set_TXN_ID($total_line_item, $transaction);
698
-        self::create_pre_tax_subtotal($total_line_item, $transaction);
699
-        self::create_taxes_subtotal($total_line_item, $transaction);
700
-        return $total_line_item;
701
-    }
702
-
703
-
704
-    /**
705
-     * Creates a default items subtotal line item
706
-     *
707
-     * @param EE_Line_Item   $total_line_item
708
-     * @param EE_Transaction $transaction
709
-     * @return EE_Line_Item
710
-     * @throws EE_Error
711
-     * @throws InvalidArgumentException
712
-     * @throws InvalidDataTypeException
713
-     * @throws InvalidInterfaceException
714
-     * @throws ReflectionException
715
-     */
716
-    protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = null)
717
-    {
718
-        $pre_tax_line_item = EE_Line_Item::new_instance(
719
-            [
720
-                'LIN_code' => 'pre-tax-subtotal',
721
-                'LIN_name' => esc_html__('Pre-Tax Subtotal', 'event_espresso'),
722
-                'LIN_type' => EEM_Line_Item::type_sub_total,
723
-            ]
724
-        );
725
-        $pre_tax_line_item = apply_filters(
726
-            'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
727
-            $pre_tax_line_item
728
-        );
729
-        self::set_TXN_ID($pre_tax_line_item, $transaction);
730
-        $total_line_item->add_child_line_item($pre_tax_line_item);
731
-        self::create_event_subtotal($pre_tax_line_item, $transaction);
732
-        return $pre_tax_line_item;
733
-    }
734
-
735
-
736
-    /**
737
-     * Creates a line item for the taxes subtotal and finds all the tax prices
738
-     * and applies taxes to it
739
-     *
740
-     * @param EE_Line_Item   $total_line_item of type EEM_Line_Item::type_total
741
-     * @param EE_Transaction $transaction
742
-     * @return EE_Line_Item
743
-     * @throws EE_Error
744
-     * @throws InvalidArgumentException
745
-     * @throws InvalidDataTypeException
746
-     * @throws InvalidInterfaceException
747
-     * @throws ReflectionException
748
-     */
749
-    protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
750
-    {
751
-        $tax_line_item = EE_Line_Item::new_instance(
752
-            [
753
-                'LIN_code'  => 'taxes',
754
-                'LIN_name'  => esc_html__('Taxes', 'event_espresso'),
755
-                'LIN_type'  => EEM_Line_Item::type_tax_sub_total,
756
-                'LIN_order' => 1000,// this should always come last
757
-            ]
758
-        );
759
-        $tax_line_item = apply_filters(
760
-            'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
761
-            $tax_line_item
762
-        );
763
-        self::set_TXN_ID($tax_line_item, $transaction);
764
-        $total_line_item->add_child_line_item($tax_line_item);
765
-        // and lastly, add the actual taxes
766
-        self::apply_taxes($total_line_item);
767
-        return $tax_line_item;
768
-    }
769
-
770
-
771
-    /**
772
-     * Creates a default items subtotal line item
773
-     *
774
-     * @param EE_Line_Item   $pre_tax_line_item
775
-     * @param EE_Transaction $transaction
776
-     * @param EE_Event       $event
777
-     * @return EE_Line_Item
778
-     * @throws EE_Error
779
-     * @throws InvalidArgumentException
780
-     * @throws InvalidDataTypeException
781
-     * @throws InvalidInterfaceException
782
-     * @throws ReflectionException
783
-     */
784
-    public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = null, $event = null)
785
-    {
786
-        $event_line_item = EE_Line_Item::new_instance(
787
-            [
788
-                'LIN_code' => self::get_event_code($event),
789
-                'LIN_name' => self::get_event_name($event),
790
-                'LIN_desc' => self::get_event_desc($event),
791
-                'LIN_type' => EEM_Line_Item::type_sub_total,
792
-                'OBJ_type' => EEM_Line_Item::OBJ_TYPE_EVENT,
793
-                'OBJ_ID'   => $event instanceof EE_Event ? $event->ID() : 0,
794
-            ]
795
-        );
796
-        $event_line_item = apply_filters(
797
-            'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
798
-            $event_line_item
799
-        );
800
-        self::set_TXN_ID($event_line_item, $transaction);
801
-        $pre_tax_line_item->add_child_line_item($event_line_item);
802
-        return $event_line_item;
803
-    }
804
-
805
-
806
-    /**
807
-     * Gets what the event ticket's code SHOULD be
808
-     *
809
-     * @param EE_Event $event
810
-     * @return string
811
-     * @throws EE_Error|ReflectionException
812
-     */
813
-    public static function get_event_code($event)
814
-    {
815
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
816
-    }
817
-
818
-
819
-    /**
820
-     * Gets the event name
821
-     *
822
-     * @param EE_Event $event
823
-     * @return string
824
-     * @throws EE_Error
825
-     */
826
-    public static function get_event_name($event)
827
-    {
828
-        return $event instanceof EE_Event
829
-            ? mb_substr($event->name(), 0, 245)
830
-            : esc_html__('Event', 'event_espresso');
831
-    }
832
-
833
-
834
-    /**
835
-     * Gets the event excerpt
836
-     *
837
-     * @param EE_Event $event
838
-     * @return string
839
-     * @throws EE_Error
840
-     */
841
-    public static function get_event_desc($event)
842
-    {
843
-        return $event instanceof EE_Event ? $event->short_description() : '';
844
-    }
845
-
846
-
847
-    /**
848
-     * Given the grand total line item and a ticket, finds the event sub-total
849
-     * line item the ticket's purchase should be added onto
850
-     *
851
-     * @access public
852
-     * @param EE_Line_Item $grand_total the grand total line item
853
-     * @param EE_Ticket    $ticket
854
-     * @return EE_Line_Item
855
-     * @throws EE_Error
856
-     * @throws InvalidArgumentException
857
-     * @throws InvalidDataTypeException
858
-     * @throws InvalidInterfaceException
859
-     * @throws ReflectionException
860
-     */
861
-    public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
862
-    {
863
-        $first_datetime = $ticket->first_datetime();
864
-        if (! $first_datetime instanceof EE_Datetime) {
865
-            throw new EE_Error(
866
-                sprintf(
867
-                    esc_html__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'),
868
-                    $ticket->ID()
869
-                )
870
-            );
871
-        }
872
-        $event = $first_datetime->event();
873
-        if (! $event instanceof EE_Event) {
874
-            throw new EE_Error(
875
-                sprintf(
876
-                    esc_html__(
877
-                        'The supplied ticket (ID %d) has no event data associated with it.',
878
-                        'event_espresso'
879
-                    ),
880
-                    $ticket->ID()
881
-                )
882
-            );
883
-        }
884
-        $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
885
-        if (! $events_sub_total instanceof EE_Line_Item) {
886
-            throw new EE_Error(
887
-                sprintf(
888
-                    esc_html__(
889
-                        'There is no events sub-total for ticket %s on total line item %d',
890
-                        'event_espresso'
891
-                    ),
892
-                    $ticket->ID(),
893
-                    $grand_total->ID()
894
-                )
895
-            );
896
-        }
897
-        return $events_sub_total;
898
-    }
899
-
900
-
901
-    /**
902
-     * Gets the event line item
903
-     *
904
-     * @param EE_Line_Item $grand_total
905
-     * @param EE_Event     $event
906
-     * @return EE_Line_Item for the event subtotal which is a child of $grand_total
907
-     * @throws EE_Error
908
-     * @throws InvalidArgumentException
909
-     * @throws InvalidDataTypeException
910
-     * @throws InvalidInterfaceException
911
-     * @throws ReflectionException
912
-     */
913
-    public static function get_event_line_item(EE_Line_Item $grand_total, $event)
914
-    {
915
-        /** @type EE_Event $event */
916
-        $event           = EEM_Event::instance()->ensure_is_obj($event, true);
917
-        $event_line_item = null;
918
-        $found           = false;
919
-        foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
920
-            // default event subtotal, we should only ever find this the first time this method is called
921
-            if (! $event_line_item->OBJ_ID()) {
922
-                // let's use this! but first... set the event details
923
-                EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
924
-                $found = true;
925
-                break;
926
-            }
927
-            if ($event_line_item->OBJ_ID() === $event->ID()) {
928
-                // found existing line item for this event in the cart, so break out of loop and use this one
929
-                $found = true;
930
-                break;
931
-            }
932
-        }
933
-        if (! $found) {
934
-            // there is no event sub-total yet, so add it
935
-            $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
936
-            // create a new "event" subtotal below that
937
-            $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
938
-            // and set the event details
939
-            EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
940
-        }
941
-        return $event_line_item;
942
-    }
943
-
944
-
945
-    /**
946
-     * Creates a default items subtotal line item
947
-     *
948
-     * @param EE_Line_Item   $event_line_item
949
-     * @param EE_Event       $event
950
-     * @param EE_Transaction $transaction
951
-     * @return void
952
-     * @throws EE_Error
953
-     * @throws InvalidArgumentException
954
-     * @throws InvalidDataTypeException
955
-     * @throws InvalidInterfaceException
956
-     * @throws ReflectionException
957
-     */
958
-    public static function set_event_subtotal_details(
959
-        EE_Line_Item $event_line_item,
960
-        EE_Event $event,
961
-        $transaction = null
962
-    ) {
963
-        if ($event instanceof EE_Event) {
964
-            $event_line_item->set_code(self::get_event_code($event));
965
-            $event_line_item->set_name(self::get_event_name($event));
966
-            $event_line_item->set_desc(self::get_event_desc($event));
967
-            $event_line_item->set_OBJ_ID($event->ID());
968
-        }
969
-        self::set_TXN_ID($event_line_item, $transaction);
970
-    }
971
-
972
-
973
-    /**
974
-     * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
975
-     * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
976
-     * any old taxes are removed
977
-     *
978
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
979
-     * @param bool         $update_txn_status
980
-     * @return bool
981
-     * @throws EE_Error
982
-     * @throws InvalidArgumentException
983
-     * @throws InvalidDataTypeException
984
-     * @throws InvalidInterfaceException
985
-     * @throws ReflectionException
986
-     * @throws RuntimeException
987
-     */
988
-    public static function apply_taxes(EE_Line_Item $total_line_item, $update_txn_status = false)
989
-    {
990
-        /** @type EEM_Price $EEM_Price */
991
-        $EEM_Price = EE_Registry::instance()->load_model('Price');
992
-        // get array of taxes via Price Model
993
-        $ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes();
994
-        ksort($ordered_taxes);
995
-        $taxes_line_item = self::get_taxes_subtotal($total_line_item);
996
-        // just to be safe, remove its old tax line items
997
-        $deleted = $taxes_line_item->delete_children_line_items();
998
-        $updates = false;
999
-        // loop thru taxes
1000
-        foreach ($ordered_taxes as $order => $taxes) {
1001
-            foreach ($taxes as $tax) {
1002
-                if ($tax instanceof EE_Price) {
1003
-                    $tax_line_item = EE_Line_Item::new_instance(
1004
-                        [
1005
-                            'LIN_name'       => $tax->name(),
1006
-                            'LIN_desc'       => $tax->desc(),
1007
-                            'LIN_percent'    => $tax->amount(),
1008
-                            'LIN_is_taxable' => false,
1009
-                            'LIN_order'      => $order,
1010
-                            'LIN_total'      => 0,
1011
-                            'LIN_type'       => EEM_Line_Item::type_tax,
1012
-                            'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_PRICE,
1013
-                            'OBJ_ID'         => $tax->ID(),
1014
-                        ]
1015
-                    );
1016
-                    $tax_line_item = apply_filters(
1017
-                        'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
1018
-                        $tax_line_item
1019
-                    );
1020
-                    $updates       = $taxes_line_item->add_child_line_item($tax_line_item)
1021
-                        ? true
1022
-                        : $updates;
1023
-                }
1024
-            }
1025
-        }
1026
-        // only recalculate totals if something changed
1027
-        if ($deleted || $updates) {
1028
-            $total_line_item->recalculate_total_including_taxes($update_txn_status);
1029
-            return true;
1030
-        }
1031
-        return false;
1032
-    }
1033
-
1034
-
1035
-    /**
1036
-     * Ensures that taxes have been applied to the order, if not applies them.
1037
-     * Returns the total amount of tax
1038
-     *
1039
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
1040
-     * @return float
1041
-     * @throws EE_Error
1042
-     * @throws InvalidArgumentException
1043
-     * @throws InvalidDataTypeException
1044
-     * @throws InvalidInterfaceException
1045
-     * @throws ReflectionException
1046
-     */
1047
-    public static function ensure_taxes_applied($total_line_item)
1048
-    {
1049
-        $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
1050
-        if (! $taxes_subtotal->children()) {
1051
-            self::apply_taxes($total_line_item);
1052
-        }
1053
-        return $taxes_subtotal->total();
1054
-    }
1055
-
1056
-
1057
-    /**
1058
-     * Deletes ALL children of the passed line item
1059
-     *
1060
-     * @param EE_Line_Item $parent_line_item
1061
-     * @return bool
1062
-     * @throws EE_Error
1063
-     * @throws InvalidArgumentException
1064
-     * @throws InvalidDataTypeException
1065
-     * @throws InvalidInterfaceException
1066
-     * @throws ReflectionException
1067
-     */
1068
-    public static function delete_all_child_items(EE_Line_Item $parent_line_item)
1069
-    {
1070
-        $deleted = 0;
1071
-        foreach ($parent_line_item->children() as $child_line_item) {
1072
-            if ($child_line_item instanceof EE_Line_Item) {
1073
-                $deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
1074
-                if ($child_line_item->ID()) {
1075
-                    $child_line_item->delete();
1076
-                    unset($child_line_item);
1077
-                } else {
1078
-                    $parent_line_item->delete_child_line_item($child_line_item->code());
1079
-                }
1080
-                $deleted++;
1081
-            }
1082
-        }
1083
-        return $deleted;
1084
-    }
1085
-
1086
-
1087
-    /**
1088
-     * Deletes the line items as indicated by the line item code(s) provided,
1089
-     * regardless of where they're found in the line item tree. Automatically
1090
-     * re-calculates the line item totals and updates the related transaction. But
1091
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
1092
-     * should probably change because of this).
1093
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
1094
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
1095
-     *
1096
-     * @param EE_Line_Item      $total_line_item of type EEM_Line_Item::type_total
1097
-     * @param array|bool|string $line_item_codes
1098
-     * @return int number of items successfully removed
1099
-     * @throws EE_Error|ReflectionException
1100
-     */
1101
-    public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = false)
1102
-    {
1103
-        if ($total_line_item->type() !== EEM_Line_Item::type_total) {
1104
-            EE_Error::doing_it_wrong(
1105
-                'EEH_Line_Item::delete_items',
1106
-                esc_html__(
1107
-                    'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
1108
-                    'event_espresso'
1109
-                ),
1110
-                '4.6.18'
1111
-            );
1112
-        }
1113
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1114
-
1115
-        // check if only a single line_item_id was passed
1116
-        if (! empty($line_item_codes) && ! is_array($line_item_codes)) {
1117
-            // place single line_item_id in an array to appear as multiple line_item_ids
1118
-            $line_item_codes = [$line_item_codes];
1119
-        }
1120
-        $removals = 0;
1121
-        // cycle thru line_item_ids
1122
-        foreach ($line_item_codes as $line_item_id) {
1123
-            $removals += $total_line_item->delete_child_line_item($line_item_id);
1124
-        }
1125
-
1126
-        if ($removals > 0) {
1127
-            $total_line_item->recalculate_taxes_and_tax_total();
1128
-            return $removals;
1129
-        } else {
1130
-            return false;
1131
-        }
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     * Overwrites the previous tax by clearing out the old taxes, and creates a new
1137
-     * tax and updates the total line item accordingly
1138
-     *
1139
-     * @param EE_Line_Item $total_line_item
1140
-     * @param float        $amount
1141
-     * @param string       $name
1142
-     * @param string       $description
1143
-     * @param string       $code
1144
-     * @param boolean      $add_to_existing_line_item
1145
-     *                          if true, and a duplicate line item with the same code is found,
1146
-     *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
1147
-     * @return EE_Line_Item the new tax line item created
1148
-     * @throws EE_Error
1149
-     * @throws InvalidArgumentException
1150
-     * @throws InvalidDataTypeException
1151
-     * @throws InvalidInterfaceException
1152
-     * @throws ReflectionException
1153
-     */
1154
-    public static function set_total_tax_to(
1155
-        EE_Line_Item $total_line_item,
1156
-        $amount,
1157
-        $name = null,
1158
-        $description = null,
1159
-        $code = null,
1160
-        $add_to_existing_line_item = false
1161
-    ) {
1162
-        $tax_subtotal  = self::get_taxes_subtotal($total_line_item);
1163
-        $taxable_total = $total_line_item->taxable_total();
1164
-
1165
-        if ($add_to_existing_line_item) {
1166
-            $new_tax = $tax_subtotal->get_child_line_item($code);
1167
-            EEM_Line_Item::instance()->delete(
1168
-                [['LIN_code' => ['!=', $code], 'LIN_parent' => $tax_subtotal->ID()]]
1169
-            );
1170
-        } else {
1171
-            $new_tax = null;
1172
-            $tax_subtotal->delete_children_line_items();
1173
-        }
1174
-        if ($new_tax) {
1175
-            $new_tax->set_total($new_tax->total() + $amount);
1176
-            $new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
1177
-        } else {
1178
-            // no existing tax item. Create it
1179
-            $new_tax = EE_Line_Item::new_instance(
1180
-                [
1181
-                    'TXN_ID'      => $total_line_item->TXN_ID(),
1182
-                    'LIN_name'    => $name ? $name : esc_html__('Tax', 'event_espresso'),
1183
-                    'LIN_desc'    => $description ? $description : '',
1184
-                    'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
1185
-                    'LIN_total'   => $amount,
1186
-                    'LIN_parent'  => $tax_subtotal->ID(),
1187
-                    'LIN_type'    => EEM_Line_Item::type_tax,
1188
-                    'LIN_code'    => $code,
1189
-                ]
1190
-            );
1191
-        }
1192
-
1193
-        $new_tax = apply_filters(
1194
-            'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
1195
-            $new_tax,
1196
-            $total_line_item
1197
-        );
1198
-        $new_tax->save();
1199
-        $tax_subtotal->set_total($new_tax->total());
1200
-        $tax_subtotal->save();
1201
-        $total_line_item->recalculate_total_including_taxes();
1202
-        return $new_tax;
1203
-    }
1204
-
1205
-
1206
-    /**
1207
-     * Makes all the line items which are children of $line_item taxable (or not).
1208
-     * Does NOT save the line items
1209
-     *
1210
-     * @param EE_Line_Item $line_item
1211
-     * @param boolean      $taxable
1212
-     * @param string       $code_substring_for_whitelist if this string is part of the line item's code
1213
-     *                                                   it will be whitelisted (ie, except from becoming taxable)
1214
-     * @throws EE_Error|ReflectionException
1215
-     */
1216
-    public static function set_line_items_taxable(
1217
-        EE_Line_Item $line_item,
1218
-        $taxable = true,
1219
-        $code_substring_for_whitelist = null
1220
-    ) {
1221
-        $whitelisted = false;
1222
-        if ($code_substring_for_whitelist !== null) {
1223
-            $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false;
1224
-        }
1225
-        if (! $whitelisted && $line_item->is_line_item()) {
1226
-            $line_item->set_is_taxable($taxable);
1227
-        }
1228
-        foreach ($line_item->children() as $child_line_item) {
1229
-            EEH_Line_Item::set_line_items_taxable(
1230
-                $child_line_item,
1231
-                $taxable,
1232
-                $code_substring_for_whitelist
1233
-            );
1234
-        }
1235
-    }
1236
-
1237
-
1238
-    /**
1239
-     * Gets all descendants that are event subtotals
1240
-     *
1241
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1242
-     * @return EE_Line_Item[]
1243
-     * @throws EE_Error|ReflectionException
1244
-     * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1245
-     */
1246
-    public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1247
-    {
1248
-        return self::get_subtotals_of_object_type($parent_line_item, EEM_Line_Item::OBJ_TYPE_EVENT);
1249
-    }
1250
-
1251
-
1252
-    /**
1253
-     * Gets all descendants subtotals that match the supplied object type
1254
-     *
1255
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1256
-     * @param string       $obj_type
1257
-     * @return EE_Line_Item[]
1258
-     * @throws EE_Error|ReflectionException
1259
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1260
-     */
1261
-    public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1262
-    {
1263
-        return self::_get_descendants_by_type_and_object_type(
1264
-            $parent_line_item,
1265
-            EEM_Line_Item::type_sub_total,
1266
-            $obj_type
1267
-        );
1268
-    }
1269
-
1270
-
1271
-    /**
1272
-     * Gets all descendants that are tickets
1273
-     *
1274
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1275
-     * @return EE_Line_Item[]
1276
-     * @throws EE_Error|ReflectionException
1277
-     * @uses  EEH_Line_Item::get_line_items_of_object_type()
1278
-     */
1279
-    public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1280
-    {
1281
-        return self::get_line_items_of_object_type(
1282
-            $parent_line_item,
1283
-            EEM_Line_Item::OBJ_TYPE_TICKET
1284
-        );
1285
-    }
1286
-
1287
-
1288
-    /**
1289
-     * Gets all descendants subtotals that match the supplied object type
1290
-     *
1291
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1292
-     * @param string       $obj_type
1293
-     * @return EE_Line_Item[]
1294
-     * @throws EE_Error|ReflectionException
1295
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1296
-     */
1297
-    public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1298
-    {
1299
-        return self::_get_descendants_by_type_and_object_type(
1300
-            $parent_line_item,
1301
-            EEM_Line_Item::type_line_item,
1302
-            $obj_type
1303
-        );
1304
-    }
1305
-
1306
-
1307
-    /**
1308
-     * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1309
-     *
1310
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1311
-     * @return EE_Line_Item[]
1312
-     * @throws EE_Error|ReflectionException
1313
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1314
-     */
1315
-    public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1316
-    {
1317
-        return EEH_Line_Item::get_descendants_of_type(
1318
-            $parent_line_item,
1319
-            EEM_Line_Item::type_tax
1320
-        );
1321
-    }
1322
-
1323
-
1324
-    /**
1325
-     * Gets all the real items purchased which are children of this item
1326
-     *
1327
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1328
-     * @return EE_Line_Item[]
1329
-     * @throws EE_Error|ReflectionException
1330
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1331
-     */
1332
-    public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1333
-    {
1334
-        return EEH_Line_Item::get_descendants_of_type(
1335
-            $parent_line_item,
1336
-            EEM_Line_Item::type_line_item
1337
-        );
1338
-    }
1339
-
1340
-
1341
-    /**
1342
-     * Gets all descendants of supplied line item that match the supplied line item type
1343
-     *
1344
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1345
-     * @param string       $line_item_type   one of the EEM_Line_Item constants
1346
-     * @return EE_Line_Item[]
1347
-     * @throws EE_Error|ReflectionException
1348
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1349
-     */
1350
-    public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1351
-    {
1352
-        return self::_get_descendants_by_type_and_object_type(
1353
-            $parent_line_item,
1354
-            $line_item_type,
1355
-            null
1356
-        );
1357
-    }
1358
-
1359
-
1360
-    /**
1361
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type
1362
-     * as well
1363
-     *
1364
-     * @param EE_Line_Item  $parent_line_item - the line item to find descendants of
1365
-     * @param string        $line_item_type   one of the EEM_Line_Item constants
1366
-     * @param string | NULL $obj_type         object model class name (minus prefix) or NULL to ignore object type when
1367
-     *                                        searching
1368
-     * @return EE_Line_Item[]
1369
-     * @throws EE_Error|ReflectionException
1370
-     */
1371
-    protected static function _get_descendants_by_type_and_object_type(
1372
-        EE_Line_Item $parent_line_item,
1373
-        $line_item_type,
1374
-        $obj_type = null
1375
-    ) {
1376
-        $objects = [];
1377
-        $child_objects = [];
1378
-        foreach ($parent_line_item->children() as $child_line_item) {
1379
-            if ($child_line_item instanceof EE_Line_Item) {
1380
-                if (
1381
-                    $child_line_item->type() === $line_item_type
1382
-                    && (
1383
-                        $child_line_item->OBJ_type() === $obj_type || $obj_type === null
1384
-                    )
1385
-                ) {
1386
-                    $objects[] = $child_line_item;
1387
-                } else {
1388
-                    // go-through-all-its children looking for more matches
1389
-                    $child_objects[] = self::_get_descendants_by_type_and_object_type(
1390
-                        $child_line_item,
1391
-                        $line_item_type,
1392
-                        $obj_type
1393
-                    );
1394
-                }
1395
-            }
1396
-        }
1397
-        return array_merge([], $objects, ...$child_objects);
1398
-    }
1399
-
1400
-
1401
-    /**
1402
-     * Gets all descendants subtotals that match the supplied object type
1403
-     *
1404
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1405
-     * @param string       $OBJ_type         object type (like Event)
1406
-     * @param array        $OBJ_IDs          array of OBJ_IDs
1407
-     * @return EE_Line_Item[]
1408
-     * @throws EE_Error|ReflectionException
1409
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1410
-     */
1411
-    public static function get_line_items_by_object_type_and_IDs(
1412
-        EE_Line_Item $parent_line_item,
1413
-        $OBJ_type = '',
1414
-        $OBJ_IDs = []
1415
-    ) {
1416
-        return self::_get_descendants_by_object_type_and_object_ID(
1417
-            $parent_line_item,
1418
-            $OBJ_type,
1419
-            $OBJ_IDs
1420
-        );
1421
-    }
1422
-
1423
-
1424
-    /**
1425
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type
1426
-     * as well
1427
-     *
1428
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1429
-     * @param string       $OBJ_type         object type (like Event)
1430
-     * @param array        $OBJ_IDs          array of OBJ_IDs
1431
-     * @return EE_Line_Item[]
1432
-     * @throws EE_Error|ReflectionException
1433
-     */
1434
-    protected static function _get_descendants_by_object_type_and_object_ID(
1435
-        EE_Line_Item $parent_line_item,
1436
-        $OBJ_type,
1437
-        $OBJ_IDs
1438
-    ) {
1439
-        $objects = [];
1440
-        $child_objects = [];
1441
-        foreach ($parent_line_item->children() as $child_line_item) {
1442
-            if ($child_line_item instanceof EE_Line_Item) {
1443
-                if (
1444
-                    $child_line_item->OBJ_type() === $OBJ_type
1445
-                    && is_array($OBJ_IDs)
1446
-                    && in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1447
-                ) {
1448
-                    $objects[] = $child_line_item;
1449
-                } else {
1450
-                    // go-through-all-its children looking for more matches
1451
-                    $child_objects[] = self::_get_descendants_by_object_type_and_object_ID(
1452
-                        $child_line_item,
1453
-                        $OBJ_type,
1454
-                        $OBJ_IDs
1455
-                    );
1456
-                }
1457
-            }
1458
-        }
1459
-        return array_merge([], $objects, ...$child_objects);
1460
-    }
1461
-
1462
-
1463
-    /**
1464
-     * Uses a breadth-first-search in order to find the nearest descendant of
1465
-     * the specified type and returns it, else NULL
1466
-     *
1467
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1468
-     * @param string       $type             like one of the EEM_Line_Item::type_*
1469
-     * @return EE_Line_Item
1470
-     * @throws EE_Error
1471
-     * @throws InvalidArgumentException
1472
-     * @throws InvalidDataTypeException
1473
-     * @throws InvalidInterfaceException
1474
-     * @throws ReflectionException
1475
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1476
-     */
1477
-    public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1478
-    {
1479
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1480
-    }
1481
-
1482
-
1483
-    /**
1484
-     * Uses a breadth-first-search in order to find the nearest descendant
1485
-     * having the specified LIN_code and returns it, else NULL
1486
-     *
1487
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1488
-     * @param string       $code             any value used for LIN_code
1489
-     * @return EE_Line_Item
1490
-     * @throws EE_Error
1491
-     * @throws InvalidArgumentException
1492
-     * @throws InvalidDataTypeException
1493
-     * @throws InvalidInterfaceException
1494
-     * @throws ReflectionException
1495
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1496
-     */
1497
-    public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1498
-    {
1499
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1500
-    }
1501
-
1502
-
1503
-    /**
1504
-     * Uses a breadth-first-search in order to find the nearest descendant
1505
-     * having the specified LIN_code and returns it, else NULL
1506
-     *
1507
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1508
-     * @param string       $search_field     name of EE_Line_Item property
1509
-     * @param string       $value            any value stored in $search_field
1510
-     * @return EE_Line_Item
1511
-     * @throws EE_Error
1512
-     * @throws InvalidArgumentException
1513
-     * @throws InvalidDataTypeException
1514
-     * @throws InvalidInterfaceException
1515
-     * @throws ReflectionException
1516
-     */
1517
-    protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1518
-    {
1519
-        foreach ($parent_line_item->children() as $child) {
1520
-            if ($child->get($search_field) == $value) {
1521
-                return $child;
1522
-            }
1523
-        }
1524
-        foreach ($parent_line_item->children() as $child) {
1525
-            $descendant_found = self::_get_nearest_descendant(
1526
-                $child,
1527
-                $search_field,
1528
-                $value
1529
-            );
1530
-            if ($descendant_found) {
1531
-                return $descendant_found;
1532
-            }
1533
-        }
1534
-        return null;
1535
-    }
1536
-
1537
-
1538
-    /**
1539
-     * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1540
-     * else recursively walks up the line item tree until a parent of type total is found,
1541
-     *
1542
-     * @param EE_Line_Item $line_item
1543
-     * @return EE_Line_Item
1544
-     * @throws EE_Error|ReflectionException
1545
-     */
1546
-    public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item)
1547
-    {
1548
-        if ($line_item->TXN_ID()) {
1549
-            $total_line_item = $line_item->transaction()->total_line_item(false);
1550
-            if ($total_line_item instanceof EE_Line_Item) {
1551
-                return $total_line_item;
1552
-            }
1553
-        } else {
1554
-            $line_item_parent = $line_item->parent();
1555
-            if ($line_item_parent instanceof EE_Line_Item) {
1556
-                if ($line_item_parent->is_total()) {
1557
-                    return $line_item_parent;
1558
-                }
1559
-                return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1560
-            }
1561
-        }
1562
-        throw new EE_Error(
1563
-            sprintf(
1564
-                esc_html__(
1565
-                    'A valid grand total for line item %1$d was not found.',
1566
-                    'event_espresso'
1567
-                ),
1568
-                $line_item->ID()
1569
-            )
1570
-        );
1571
-    }
1572
-
1573
-
1574
-    /**
1575
-     * Prints out a representation of the line item tree
1576
-     *
1577
-     * @param EE_Line_Item $line_item
1578
-     * @param int          $indentation
1579
-     * @return void
1580
-     * @throws EE_Error|ReflectionException
1581
-     */
1582
-    public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1583
-    {
1584
-        $linebreak = defined('EE_TESTS_DIR') ? "\n" : '<br />';
1585
-        echo $linebreak;
1586
-        if (! $indentation) {
1587
-            echo $linebreak;
1588
-        }
1589
-        for ($i = 0; $i < $indentation; $i++) {
1590
-            echo ' . ';
1591
-        }
1592
-        $breakdown = '';
1593
-        if ($line_item->is_line_item()) {
1594
-            if ($line_item->is_percent()) {
1595
-                $breakdown = "{$line_item->percent()}%";
1596
-            } else {
1597
-                $breakdown = ' $' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1598
-            }
1599
-        }
1600
-        echo wp_kses($line_item->name(), AllowedTags::getAllowedTags());
1601
-        echo " [ ID: {$line_item->ID()} · qty: {$line_item->quantity()} ] {$line_item->type()} : ";
1602
-        echo ' $' . (string) $line_item->total();
1603
-        if ($breakdown) {
1604
-            echo " ( {$breakdown} )";
1605
-        }
1606
-        if ($line_item->is_taxable()) {
1607
-            echo '  * taxable';
1608
-        }
1609
-        if ($line_item->children()) {
1610
-            foreach ($line_item->children() as $child) {
1611
-                self::visualize($child, $indentation + 1);
1612
-            }
1613
-        }
1614
-        if ($line_item->is_total()) {
1615
-            echo $linebreak . $linebreak;
1616
-        }
1617
-    }
1618
-
1619
-
1620
-    /**
1621
-     * Calculates the registration's final price, taking into account that they
1622
-     * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1623
-     * and receive a portion of any transaction-wide discounts.
1624
-     * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1625
-     * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1626
-     * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1627
-     * and brent's final price should be $5.50.
1628
-     * In order to do this, we basically need to traverse the line item tree calculating
1629
-     * the running totals (just as if we were recalculating the total), but when we identify
1630
-     * regular line items, we need to keep track of their share of the grand total.
1631
-     * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1632
-     * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1633
-     * when there are non-taxable items; otherwise they would be the same)
1634
-     *
1635
-     * @param EE_Line_Item $line_item
1636
-     * @param array        $billable_ticket_quantities  array of EE_Ticket IDs and their corresponding quantity
1637
-     *                                                  that can be included in price calculations at this moment
1638
-     * @return array                                    keys are line items for tickets IDs and values are their share
1639
-     *                                                  of the running total, plus the key 'total',
1640
-     *                                                  and 'taxable' which also has keys of all the ticket IDs.
1641
-     *                                                      ex:
1642
-     *                                                      [
1643
-     *                                                          12 => 4.3,
1644
-     *                                                          23 => 8.0,
1645
-     *                                                          'total' => 16.6,
1646
-     *                                                          'taxable' => [
1647
-     *                                                              12 => 10,
1648
-     *                                                              23 => 4
1649
-     *                                                          ]
1650
-     *                                                      ]
1651
-     *                                                  So to find which registrations have which final price,
1652
-     *                                                  we need to find which line item is theirs, via:
1653
-     *                                                  `EEM_Line_Item::instance()->get_line_item_for_registration(
1654
-     *                                                      $registration
1655
-     *                                                  );`
1656
-     * @throws EE_Error
1657
-     * @throws InvalidArgumentException
1658
-     * @throws InvalidDataTypeException
1659
-     * @throws InvalidInterfaceException
1660
-     * @throws ReflectionException
1661
-     */
1662
-    public static function calculate_reg_final_prices_per_line_item(
1663
-        EE_Line_Item $line_item,
1664
-        $billable_ticket_quantities = []
1665
-    ) {
1666
-        $running_totals = [
1667
-            'total'   => 0,
1668
-            'taxable' => ['total' => 0],
1669
-        ];
1670
-        foreach ($line_item->children() as $child_line_item) {
1671
-            switch ($child_line_item->type()) {
1672
-                case EEM_Line_Item::type_sub_total:
1673
-                    $running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1674
-                        $child_line_item,
1675
-                        $billable_ticket_quantities
1676
-                    );
1677
-                    // combine arrays but preserve numeric keys
1678
-                    $running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1679
-                    $running_totals['total']            += $running_totals_from_subtotal['total'];
1680
-                    $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1681
-                    break;
1682
-
1683
-                case EEM_Line_Item::type_tax_sub_total:
1684
-                    // find how much the taxes percentage is
1685
-                    if ($child_line_item->percent() !== 0) {
1686
-                        $tax_percent_decimal = $child_line_item->percent() / 100;
1687
-                    } else {
1688
-                        $tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1689
-                    }
1690
-                    // and apply to all the taxable totals, and add to the pretax totals
1691
-                    foreach ($running_totals as $line_item_id => $this_running_total) {
1692
-                        // "total" and "taxable" array key is an exception
1693
-                        if ($line_item_id === 'taxable') {
1694
-                            continue;
1695
-                        }
1696
-                        $taxable_total                   = $running_totals['taxable'][ $line_item_id ];
1697
-                        $running_totals[ $line_item_id ] += ($taxable_total * $tax_percent_decimal);
1698
-                    }
1699
-                    break;
1700
-
1701
-                case EEM_Line_Item::type_line_item:
1702
-                    // ticket line items or ????
1703
-                    if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
1704
-                        // kk it's a ticket
1705
-                        if (isset($running_totals[ $child_line_item->ID() ])) {
1706
-                            // huh? that shouldn't happen.
1707
-                            $running_totals['total'] += $child_line_item->total();
1708
-                        } else {
1709
-                            // its not in our running totals yet. great.
1710
-                            if ($child_line_item->is_taxable()) {
1711
-                                $taxable_amount = $child_line_item->unit_price();
1712
-                            } else {
1713
-                                $taxable_amount = 0;
1714
-                            }
1715
-                            // are we only calculating totals for some tickets?
1716
-                            if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) {
1717
-                                $quantity                                            =
1718
-                                    $billable_ticket_quantities[ $child_line_item->OBJ_ID() ];
1719
-                                $running_totals[ $child_line_item->ID() ]            = $quantity
1720
-                                    ? $child_line_item->unit_price()
1721
-                                    : 0;
1722
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $quantity
1723
-                                    ? $taxable_amount
1724
-                                    : 0;
1725
-                            } else {
1726
-                                $quantity                                            = $child_line_item->quantity();
1727
-                                $running_totals[ $child_line_item->ID() ]            = $child_line_item->unit_price();
1728
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount;
1729
-                            }
1730
-                            $running_totals['taxable']['total'] += $taxable_amount * $quantity;
1731
-                            $running_totals['total']            += $child_line_item->unit_price() * $quantity;
1732
-                        }
1733
-                    } else {
1734
-                        // it's some other type of item added to the cart
1735
-                        // it should affect the running totals
1736
-                        // basically we want to convert it into a PERCENT modifier. Because
1737
-                        // more clearly affect all registration's final price equally
1738
-                        $line_items_percent_of_running_total = $running_totals['total'] > 0
1739
-                            ? ($child_line_item->total() / $running_totals['total']) + 1
1740
-                            : 1;
1741
-                        foreach ($running_totals as $line_item_id => $this_running_total) {
1742
-                            // the "taxable" array key is an exception
1743
-                            if ($line_item_id === 'taxable') {
1744
-                                continue;
1745
-                            }
1746
-                            // update the running totals
1747
-                            // yes this actually even works for the running grand total!
1748
-                            $running_totals[ $line_item_id ] =
1749
-                                $line_items_percent_of_running_total * $this_running_total;
1750
-
1751
-                            if ($child_line_item->is_taxable()) {
1752
-                                $running_totals['taxable'][ $line_item_id ] =
1753
-                                    $line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ];
1754
-                            }
1755
-                        }
1756
-                    }
1757
-                    break;
1758
-            }
1759
-        }
1760
-        return $running_totals;
1761
-    }
1762
-
1763
-
1764
-    /**
1765
-     * @param EE_Line_Item $total_line_item
1766
-     * @param EE_Line_Item $ticket_line_item
1767
-     * @return float | null
1768
-     * @throws EE_Error
1769
-     * @throws InvalidArgumentException
1770
-     * @throws InvalidDataTypeException
1771
-     * @throws InvalidInterfaceException
1772
-     * @throws OutOfRangeException
1773
-     * @throws ReflectionException
1774
-     */
1775
-    public static function calculate_final_price_for_ticket_line_item(
1776
-        EE_Line_Item $total_line_item,
1777
-        EE_Line_Item $ticket_line_item
1778
-    ) {
1779
-        static $final_prices_per_ticket_line_item = [];
1780
-        if (empty($final_prices_per_ticket_line_item)
1781
-            || empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])
1782
-        ) {
1783
-            $final_prices_per_ticket_line_item[ $total_line_item->ID() ] =
1784
-                EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1785
-                    $total_line_item
1786
-                );
1787
-        }
1788
-        // ok now find this new registration's final price
1789
-        if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) {
1790
-            return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ];
1791
-        }
1792
-        $message = sprintf(
1793
-            esc_html__(
1794
-                'The final price for the ticket line item (ID:%1$d) could not be calculated.',
1795
-                'event_espresso'
1796
-            ),
1797
-            $ticket_line_item->ID()
1798
-        );
1799
-        if (WP_DEBUG) {
1800
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1801
-            throw new OutOfRangeException($message);
1802
-        }
1803
-        EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1804
-        return null;
1805
-    }
1806
-
1807
-
1808
-    /**
1809
-     * Creates a duplicate of the line item tree, except only includes billable items
1810
-     * and the portion of line items attributed to billable things
1811
-     *
1812
-     * @param EE_Line_Item      $line_item
1813
-     * @param EE_Registration[] $registrations
1814
-     * @return EE_Line_Item
1815
-     * @throws EE_Error
1816
-     * @throws InvalidArgumentException
1817
-     * @throws InvalidDataTypeException
1818
-     * @throws InvalidInterfaceException
1819
-     * @throws ReflectionException
1820
-     */
1821
-    public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1822
-    {
1823
-        $copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1824
-        foreach ($line_item->children() as $child_li) {
1825
-            $copy_li->add_child_line_item(
1826
-                EEH_Line_Item::billable_line_item_tree($child_li, $registrations)
1827
-            );
1828
-        }
1829
-        // if this is the grand total line item, make sure the totals all add up
1830
-        // (we could have duplicated this logic AS we copied the line items, but
1831
-        // it seems DRYer this way)
1832
-        if ($copy_li->type() === EEM_Line_Item::type_total) {
1833
-            $copy_li->recalculate_total_including_taxes();
1834
-        }
1835
-        return $copy_li;
1836
-    }
1837
-
1838
-
1839
-    /**
1840
-     * Creates a new, unsaved line item from $line_item that factors in the
1841
-     * number of billable registrations on $registrations.
1842
-     *
1843
-     * @param EE_Line_Item      $line_item
1844
-     * @param EE_Registration[] $registrations
1845
-     * @return EE_Line_Item
1846
-     * @throws EE_Error
1847
-     * @throws InvalidArgumentException
1848
-     * @throws InvalidDataTypeException
1849
-     * @throws InvalidInterfaceException
1850
-     * @throws ReflectionException
1851
-     */
1852
-    public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1853
-    {
1854
-        $new_li_fields = $line_item->model_field_array();
1855
-        if (
1856
-            $line_item->type() === EEM_Line_Item::type_line_item &&
1857
-            $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1858
-        ) {
1859
-            $count = 0;
1860
-            foreach ($registrations as $registration) {
1861
-                if (
1862
-                    $line_item->OBJ_ID() === $registration->ticket_ID() &&
1863
-                    in_array(
1864
-                        $registration->status_ID(),
1865
-                        EEM_Registration::reg_statuses_that_allow_payment(),
1866
-                        true
1867
-                    )
1868
-                ) {
1869
-                    $count++;
1870
-                }
1871
-            }
1872
-            $new_li_fields['LIN_quantity'] = $count;
1873
-        }
1874
-        // don't set the total. We'll leave that up to the code that calculates it
1875
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1876
-        return EE_Line_Item::new_instance($new_li_fields);
1877
-    }
1878
-
1879
-
1880
-    /**
1881
-     * Returns a modified line item tree where all the subtotals which have a total of 0
1882
-     * are removed, and line items with a quantity of 0
1883
-     *
1884
-     * @param EE_Line_Item $line_item |null
1885
-     * @return EE_Line_Item|null
1886
-     * @throws EE_Error
1887
-     * @throws InvalidArgumentException
1888
-     * @throws InvalidDataTypeException
1889
-     * @throws InvalidInterfaceException
1890
-     * @throws ReflectionException
1891
-     */
1892
-    public static function non_empty_line_items(EE_Line_Item $line_item)
1893
-    {
1894
-        $copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1895
-        if ($copied_li === null) {
1896
-            return null;
1897
-        }
1898
-        // if this is an event subtotal, we want to only include it if it
1899
-        // has a non-zero total and at least one ticket line item child
1900
-        $ticket_children = 0;
1901
-        foreach ($line_item->children() as $child_li) {
1902
-            $child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1903
-            if ($child_li_copy !== null) {
1904
-                $copied_li->add_child_line_item($child_li_copy);
1905
-                if (
1906
-                    $child_li_copy->type() === EEM_Line_Item::type_line_item &&
1907
-                    $child_li_copy->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1908
-                ) {
1909
-                    $ticket_children++;
1910
-                }
1911
-            }
1912
-        }
1913
-        // if this is an event subtotal with NO ticket children
1914
-        // we basically want to ignore it
1915
-        if (
1916
-            $ticket_children === 0
1917
-            && $line_item->type() === EEM_Line_Item::type_sub_total
1918
-            && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
1919
-            && $line_item->total() === 0
1920
-        ) {
1921
-            return null;
1922
-        }
1923
-        return $copied_li;
1924
-    }
1925
-
1926
-
1927
-    /**
1928
-     * Creates a new, unsaved line item, but if it's a ticket line item
1929
-     * with a total of 0, or a subtotal of 0, returns null instead
1930
-     *
1931
-     * @param EE_Line_Item $line_item
1932
-     * @return EE_Line_Item
1933
-     * @throws EE_Error
1934
-     * @throws InvalidArgumentException
1935
-     * @throws InvalidDataTypeException
1936
-     * @throws InvalidInterfaceException
1937
-     * @throws ReflectionException
1938
-     */
1939
-    public static function non_empty_line_item(EE_Line_Item $line_item)
1940
-    {
1941
-        if (
1942
-            $line_item->type() === EEM_Line_Item::type_line_item
1943
-            && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1944
-            && $line_item->quantity() === 0
1945
-        ) {
1946
-            return null;
1947
-        }
1948
-        $new_li_fields = $line_item->model_field_array();
1949
-        // don't set the total. We'll leave that up to the code that calculates it
1950
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1951
-        return EE_Line_Item::new_instance($new_li_fields);
1952
-    }
1953
-
1954
-
1955
-    /**
1956
-     * Cycles through all of the ticket line items for the supplied total line item
1957
-     * and ensures that the line item's "is_taxable" field matches that of its corresponding ticket
1958
-     *
1959
-     * @param EE_Line_Item $total_line_item
1960
-     * @throws EE_Error
1961
-     * @throws InvalidArgumentException
1962
-     * @throws InvalidDataTypeException
1963
-     * @throws InvalidInterfaceException
1964
-     * @throws ReflectionException
1965
-     * @since 4.9.79.p
1966
-     */
1967
-    public static function resetIsTaxableForTickets(EE_Line_Item $total_line_item)
1968
-    {
1969
-        $ticket_line_items = self::get_ticket_line_items($total_line_item);
1970
-        foreach ($ticket_line_items as $ticket_line_item) {
1971
-            if (
1972
-                $ticket_line_item instanceof EE_Line_Item
1973
-                && $ticket_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1974
-            ) {
1975
-                $ticket = $ticket_line_item->ticket();
1976
-                if ($ticket instanceof EE_Ticket && $ticket->taxable() !== $ticket_line_item->is_taxable()) {
1977
-                    $ticket_line_item->set_is_taxable($ticket->taxable());
1978
-                    $ticket_line_item->save();
1979
-                }
1980
-            }
1981
-        }
1982
-    }
1983
-
1984
-
1985
-
1986
-    /**************************************** @DEPRECATED METHODS *************************************** */
1987
-    /**
1988
-     * @param EE_Line_Item $total_line_item
1989
-     * @return EE_Line_Item
1990
-     * @throws EE_Error
1991
-     * @throws InvalidArgumentException
1992
-     * @throws InvalidDataTypeException
1993
-     * @throws InvalidInterfaceException
1994
-     * @throws ReflectionException
1995
-     * @deprecated
1996
-     */
1997
-    public static function get_items_subtotal(EE_Line_Item $total_line_item)
1998
-    {
1999
-        EE_Error::doing_it_wrong(
2000
-            'EEH_Line_Item::get_items_subtotal()',
2001
-            sprintf(
2002
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2003
-                'EEH_Line_Item::get_pre_tax_subtotal()'
2004
-            ),
2005
-            '4.6.0'
2006
-        );
2007
-        return self::get_pre_tax_subtotal($total_line_item);
2008
-    }
2009
-
2010
-
2011
-    /**
2012
-     * @param EE_Transaction $transaction
2013
-     * @return EE_Line_Item
2014
-     * @throws EE_Error
2015
-     * @throws InvalidArgumentException
2016
-     * @throws InvalidDataTypeException
2017
-     * @throws InvalidInterfaceException
2018
-     * @throws ReflectionException
2019
-     * @deprecated
2020
-     */
2021
-    public static function create_default_total_line_item($transaction = null)
2022
-    {
2023
-        EE_Error::doing_it_wrong(
2024
-            'EEH_Line_Item::create_default_total_line_item()',
2025
-            sprintf(
2026
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2027
-                'EEH_Line_Item::create_total_line_item()'
2028
-            ),
2029
-            '4.6.0'
2030
-        );
2031
-        return self::create_total_line_item($transaction);
2032
-    }
2033
-
2034
-
2035
-    /**
2036
-     * @param EE_Line_Item   $total_line_item
2037
-     * @param EE_Transaction $transaction
2038
-     * @return EE_Line_Item
2039
-     * @throws EE_Error
2040
-     * @throws InvalidArgumentException
2041
-     * @throws InvalidDataTypeException
2042
-     * @throws InvalidInterfaceException
2043
-     * @throws ReflectionException
2044
-     * @deprecated
2045
-     */
2046
-    public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2047
-    {
2048
-        EE_Error::doing_it_wrong(
2049
-            'EEH_Line_Item::create_default_tickets_subtotal()',
2050
-            sprintf(
2051
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2052
-                'EEH_Line_Item::create_pre_tax_subtotal()'
2053
-            ),
2054
-            '4.6.0'
2055
-        );
2056
-        return self::create_pre_tax_subtotal($total_line_item, $transaction);
2057
-    }
2058
-
2059
-
2060
-    /**
2061
-     * @param EE_Line_Item   $total_line_item
2062
-     * @param EE_Transaction $transaction
2063
-     * @return EE_Line_Item
2064
-     * @throws EE_Error
2065
-     * @throws InvalidArgumentException
2066
-     * @throws InvalidDataTypeException
2067
-     * @throws InvalidInterfaceException
2068
-     * @throws ReflectionException
2069
-     * @deprecated
2070
-     */
2071
-    public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2072
-    {
2073
-        EE_Error::doing_it_wrong(
2074
-            'EEH_Line_Item::create_default_taxes_subtotal()',
2075
-            sprintf(
2076
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2077
-                'EEH_Line_Item::create_taxes_subtotal()'
2078
-            ),
2079
-            '4.6.0'
2080
-        );
2081
-        return self::create_taxes_subtotal($total_line_item, $transaction);
2082
-    }
2083
-
2084
-
2085
-    /**
2086
-     * @param EE_Line_Item   $total_line_item
2087
-     * @param EE_Transaction $transaction
2088
-     * @return EE_Line_Item
2089
-     * @throws EE_Error
2090
-     * @throws InvalidArgumentException
2091
-     * @throws InvalidDataTypeException
2092
-     * @throws InvalidInterfaceException
2093
-     * @throws ReflectionException
2094
-     * @deprecated
2095
-     */
2096
-    public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2097
-    {
2098
-        EE_Error::doing_it_wrong(
2099
-            'EEH_Line_Item::create_default_event_subtotal()',
2100
-            sprintf(
2101
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2102
-                'EEH_Line_Item::create_event_subtotal()'
2103
-            ),
2104
-            '4.6.0'
2105
-        );
2106
-        return self::create_event_subtotal($total_line_item, $transaction);
2107
-    }
24
+	/**
25
+	 * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
26
+	 * Does NOT automatically re-calculate the line item totals or update the related transaction.
27
+	 * You should call recalculate_total_including_taxes() on the grant total line item after this
28
+	 * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
29
+	 * to keep the registration final prices in-sync with the transaction's total.
30
+	 *
31
+	 * @param EE_Line_Item $parent_line_item
32
+	 * @param string       $name
33
+	 * @param float        $unit_price
34
+	 * @param string       $description
35
+	 * @param int          $quantity
36
+	 * @param boolean      $taxable
37
+	 * @param boolean      $code if set to a value, ensures there is only one line item with that code
38
+	 * @return boolean success
39
+	 * @throws EE_Error
40
+	 * @throws InvalidArgumentException
41
+	 * @throws InvalidDataTypeException
42
+	 * @throws InvalidInterfaceException
43
+	 * @throws ReflectionException
44
+	 */
45
+	public static function add_unrelated_item(
46
+		EE_Line_Item $parent_line_item,
47
+		$name,
48
+		$unit_price,
49
+		$description = '',
50
+		$quantity = 1,
51
+		$taxable = false,
52
+		$code = null
53
+	) {
54
+		$items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
55
+		$order = 0;
56
+		if ($items_subtotal instanceof EE_Line_Item) {
57
+			$items_subtotal_children = $items_subtotal->children();
58
+			$order = count($items_subtotal_children);
59
+		}
60
+		$line_item      = EE_Line_Item::new_instance(
61
+			[
62
+				'LIN_name'       => $name,
63
+				'LIN_desc'       => $description,
64
+				'LIN_unit_price' => $unit_price,
65
+				'LIN_quantity'   => $quantity,
66
+				'LIN_percent'    => null,
67
+				'LIN_is_taxable' => $taxable,
68
+				'LIN_order'      => $order,
69
+				'LIN_total'      => (float) $unit_price * (int) $quantity,
70
+				'LIN_type'       => EEM_Line_Item::type_line_item,
71
+				'LIN_code'       => $code,
72
+			]
73
+		);
74
+		$line_item      = apply_filters(
75
+			'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
76
+			$line_item,
77
+			$parent_line_item
78
+		);
79
+		return self::add_item($parent_line_item, $line_item);
80
+	}
81
+
82
+
83
+	/**
84
+	 * Adds a simple item ( unrelated to any other model object) to the total line item,
85
+	 * in the correct spot in the line item tree. Does not automatically
86
+	 * re-calculate the line item totals, nor update the related transaction, nor upgrade the transaction's
87
+	 * registrations' final prices (which should probably change because of this).
88
+	 * You should call recalculate_total_including_taxes() on the grand total line item, then
89
+	 * update the transaction's total, and EE_Registration_Processor::update_registration_final_prices()
90
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
91
+	 *
92
+	 * @param EE_Line_Item $parent_line_item
93
+	 * @param string       $name
94
+	 * @param float        $percentage_amount
95
+	 * @param string       $description
96
+	 * @param boolean      $taxable
97
+	 * @return boolean success
98
+	 * @throws EE_Error|ReflectionException
99
+	 */
100
+	public static function add_percentage_based_item(
101
+		EE_Line_Item $parent_line_item,
102
+		$name,
103
+		$percentage_amount,
104
+		$description = '',
105
+		$taxable = false
106
+	) {
107
+		$line_item = EE_Line_Item::new_instance(
108
+			[
109
+				'LIN_name'       => $name,
110
+				'LIN_desc'       => $description,
111
+				'LIN_unit_price' => 0,
112
+				'LIN_percent'    => $percentage_amount,
113
+				'LIN_quantity'   => 1,
114
+				'LIN_is_taxable' => $taxable,
115
+				'LIN_total'      => (float) ($percentage_amount * ($parent_line_item->total() / 100)),
116
+				'LIN_type'       => EEM_Line_Item::type_line_item,
117
+				'LIN_parent'     => $parent_line_item->ID(),
118
+			]
119
+		);
120
+		$line_item = apply_filters(
121
+			'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
122
+			$line_item
123
+		);
124
+		return $parent_line_item->add_child_line_item($line_item, false);
125
+	}
126
+
127
+
128
+	/**
129
+	 * Returns the new line item created by adding a purchase of the ticket
130
+	 * ensures that ticket line item is saved, and that cart total has been recalculated.
131
+	 * If this ticket has already been purchased, just increments its count.
132
+	 * Automatically re-calculates the line item totals and updates the related transaction. But
133
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
134
+	 * should probably change because of this).
135
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
136
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
137
+	 *
138
+	 * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
139
+	 * @param EE_Ticket    $ticket
140
+	 * @param int          $qty
141
+	 * @return EE_Line_Item
142
+	 * @throws EE_Error
143
+	 * @throws InvalidArgumentException
144
+	 * @throws InvalidDataTypeException
145
+	 * @throws InvalidInterfaceException
146
+	 * @throws ReflectionException
147
+	 */
148
+	public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
149
+	{
150
+		if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
151
+			throw new EE_Error(
152
+				sprintf(
153
+					esc_html__(
154
+						'A valid line item total is required in order to add tickets. A line item of type "%s" was passed.',
155
+						'event_espresso'
156
+					),
157
+					$ticket->ID(),
158
+					$total_line_item->ID()
159
+				)
160
+			);
161
+		}
162
+		// either increment the qty for an existing ticket
163
+		$line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
164
+		// or add a new one
165
+		if (! $line_item instanceof EE_Line_Item) {
166
+			$line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
167
+		}
168
+		$total_line_item->recalculate_total_including_taxes();
169
+		return $line_item;
170
+	}
171
+
172
+
173
+	/**
174
+	 * Returns the new line item created by adding a purchase of the ticket
175
+	 *
176
+	 * @param EE_Line_Item $total_line_item
177
+	 * @param EE_Ticket    $ticket
178
+	 * @param int          $qty
179
+	 * @return EE_Line_Item
180
+	 * @throws EE_Error
181
+	 * @throws InvalidArgumentException
182
+	 * @throws InvalidDataTypeException
183
+	 * @throws InvalidInterfaceException
184
+	 * @throws ReflectionException
185
+	 */
186
+	public static function increment_ticket_qty_if_already_in_cart(
187
+		EE_Line_Item $total_line_item,
188
+		EE_Ticket $ticket,
189
+		$qty = 1
190
+	) {
191
+		$line_item = null;
192
+		if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
193
+			$ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
194
+			foreach ((array) $ticket_line_items as $ticket_line_item) {
195
+				if (
196
+					$ticket_line_item instanceof EE_Line_Item
197
+					&& (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID()
198
+				) {
199
+					$line_item = $ticket_line_item;
200
+					break;
201
+				}
202
+			}
203
+		}
204
+		if ($line_item instanceof EE_Line_Item) {
205
+			EEH_Line_Item::increment_quantity($line_item, $qty);
206
+			return $line_item;
207
+		}
208
+		return null;
209
+	}
210
+
211
+
212
+	/**
213
+	 * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
214
+	 * Does NOT save or recalculate other line items totals
215
+	 *
216
+	 * @param EE_Line_Item $line_item
217
+	 * @param int          $qty
218
+	 * @return void
219
+	 * @throws EE_Error
220
+	 * @throws InvalidArgumentException
221
+	 * @throws InvalidDataTypeException
222
+	 * @throws InvalidInterfaceException
223
+	 * @throws ReflectionException
224
+	 */
225
+	public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
226
+	{
227
+		if (! $line_item->is_percent()) {
228
+			$qty += $line_item->quantity();
229
+			$line_item->set_quantity($qty);
230
+			$line_item->set_total($line_item->unit_price() * $qty);
231
+			$line_item->save();
232
+		}
233
+		foreach ($line_item->children() as $child) {
234
+			if ($child->is_sub_line_item()) {
235
+				EEH_Line_Item::update_quantity($child, $qty);
236
+			}
237
+		}
238
+	}
239
+
240
+
241
+	/**
242
+	 * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
243
+	 * Does NOT save or recalculate other line items totals
244
+	 *
245
+	 * @param EE_Line_Item $line_item
246
+	 * @param int          $qty
247
+	 * @return void
248
+	 * @throws EE_Error
249
+	 * @throws InvalidArgumentException
250
+	 * @throws InvalidDataTypeException
251
+	 * @throws InvalidInterfaceException
252
+	 * @throws ReflectionException
253
+	 */
254
+	public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
255
+	{
256
+		if (! $line_item->is_percent()) {
257
+			$qty = $line_item->quantity() - $qty;
258
+			$qty = max($qty, 0);
259
+			$line_item->set_quantity($qty);
260
+			$line_item->set_total($line_item->unit_price() * $qty);
261
+			$line_item->save();
262
+		}
263
+		foreach ($line_item->children() as $child) {
264
+			if ($child->is_sub_line_item()) {
265
+				EEH_Line_Item::update_quantity($child, $qty);
266
+			}
267
+		}
268
+	}
269
+
270
+
271
+	/**
272
+	 * Updates the line item and its children's quantities to the specified number.
273
+	 * Does NOT save them or recalculate totals.
274
+	 *
275
+	 * @param EE_Line_Item $line_item
276
+	 * @param int          $new_quantity
277
+	 * @throws EE_Error
278
+	 * @throws InvalidArgumentException
279
+	 * @throws InvalidDataTypeException
280
+	 * @throws InvalidInterfaceException
281
+	 * @throws ReflectionException
282
+	 */
283
+	public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
284
+	{
285
+		if (! $line_item->is_percent()) {
286
+			$line_item->set_quantity($new_quantity);
287
+			$line_item->set_total($line_item->unit_price() * $new_quantity);
288
+			$line_item->save();
289
+		}
290
+		foreach ($line_item->children() as $child) {
291
+			if ($child->is_sub_line_item()) {
292
+				EEH_Line_Item::update_quantity($child, $new_quantity);
293
+			}
294
+		}
295
+	}
296
+
297
+
298
+	/**
299
+	 * Returns the new line item created by adding a purchase of the ticket
300
+	 *
301
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
302
+	 * @param EE_Ticket    $ticket
303
+	 * @param int          $qty
304
+	 * @return EE_Line_Item
305
+	 * @throws EE_Error
306
+	 * @throws InvalidArgumentException
307
+	 * @throws InvalidDataTypeException
308
+	 * @throws InvalidInterfaceException
309
+	 * @throws ReflectionException
310
+	 */
311
+	public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
312
+	{
313
+		$datetimes           = $ticket->datetimes();
314
+		$first_datetime      = reset($datetimes);
315
+		$first_datetime_name = esc_html__('Event', 'event_espresso');
316
+		if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
317
+			$first_datetime_name = $first_datetime->event()->name();
318
+		}
319
+		$event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
320
+		// get event subtotal line
321
+		$events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
322
+		// add $ticket to cart
323
+		$line_item = EE_Line_Item::new_instance(
324
+			[
325
+				'LIN_name'       => $ticket->name(),
326
+				'LIN_desc'       => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
327
+				'LIN_unit_price' => $ticket->price(),
328
+				'LIN_quantity'   => $qty,
329
+				'LIN_is_taxable' => $ticket->taxable(),
330
+				'LIN_order'      => count($events_sub_total->children()),
331
+				'LIN_total'      => $ticket->price() * $qty,
332
+				'LIN_type'       => EEM_Line_Item::type_line_item,
333
+				'OBJ_ID'         => $ticket->ID(),
334
+				'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_TICKET,
335
+			]
336
+		);
337
+		$line_item = apply_filters(
338
+			'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
339
+			$line_item
340
+		);
341
+		$events_sub_total->add_child_line_item($line_item);
342
+		// now add the sub-line items
343
+		$running_total_for_ticket = 0;
344
+		foreach ($ticket->prices(['order_by' => ['PRC_order' => 'ASC']]) as $price) {
345
+			$sign          = $price->is_discount() ? -1 : 1;
346
+			$price_total   = $price->is_percent()
347
+				? $running_total_for_ticket * $price->amount() / 100
348
+				: $price->amount() * $qty;
349
+			$sub_line_item = EE_Line_Item::new_instance(
350
+				[
351
+					'LIN_name'       => $price->name(),
352
+					'LIN_desc'       => $price->desc(),
353
+					'LIN_quantity'   => $price->is_percent() ? null : $qty,
354
+					'LIN_is_taxable' => false,
355
+					'LIN_order'      => $price->order(),
356
+					'LIN_total'      => $sign * $price_total,
357
+					'LIN_type'       => EEM_Line_Item::type_sub_line_item,
358
+					'OBJ_ID'         => $price->ID(),
359
+					'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_PRICE,
360
+				]
361
+			);
362
+			$sub_line_item = apply_filters(
363
+				'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
364
+				$sub_line_item
365
+			);
366
+			if ($price->is_percent()) {
367
+				$sub_line_item->set_percent($sign * $price->amount());
368
+			} else {
369
+				$sub_line_item->set_unit_price($sign * $price->amount());
370
+			}
371
+			$running_total_for_ticket += $price_total;
372
+			$line_item->add_child_line_item($sub_line_item);
373
+		}
374
+		return $line_item;
375
+	}
376
+
377
+
378
+	/**
379
+	 * Adds the specified item under the pre-tax-sub-total line item. Automatically
380
+	 * re-calculates the line item totals and updates the related transaction. But
381
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
382
+	 * should probably change because of this).
383
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
384
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
385
+	 *
386
+	 * @param EE_Line_Item $total_line_item
387
+	 * @param EE_Line_Item $item to be added
388
+	 * @return boolean
389
+	 * @throws EE_Error
390
+	 * @throws InvalidArgumentException
391
+	 * @throws InvalidDataTypeException
392
+	 * @throws InvalidInterfaceException
393
+	 * @throws ReflectionException
394
+	 */
395
+	public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item)
396
+	{
397
+		$pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
398
+		if ($pre_tax_subtotal instanceof EE_Line_Item) {
399
+			$success = $pre_tax_subtotal->add_child_line_item($item);
400
+		} else {
401
+			return false;
402
+		}
403
+		$total_line_item->recalculate_total_including_taxes();
404
+		return $success;
405
+	}
406
+
407
+
408
+	/**
409
+	 * cancels an existing ticket line item,
410
+	 * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
411
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
412
+	 *
413
+	 * @param EE_Line_Item $ticket_line_item
414
+	 * @param int          $qty
415
+	 * @return bool success
416
+	 * @throws EE_Error
417
+	 * @throws InvalidArgumentException
418
+	 * @throws InvalidDataTypeException
419
+	 * @throws InvalidInterfaceException
420
+	 * @throws ReflectionException
421
+	 */
422
+	public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
423
+	{
424
+		// validate incoming line_item
425
+		if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) {
426
+			throw new EE_Error(
427
+				sprintf(
428
+					esc_html__(
429
+						'The supplied line item must have an Object Type of "Ticket", not %1$s.',
430
+						'event_espresso'
431
+					),
432
+					$ticket_line_item->type()
433
+				)
434
+			);
435
+		}
436
+		if ($ticket_line_item->quantity() < $qty) {
437
+			throw new EE_Error(
438
+				sprintf(
439
+					esc_html__(
440
+						'Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.',
441
+						'event_espresso'
442
+					),
443
+					$qty,
444
+					$ticket_line_item->quantity()
445
+				)
446
+			);
447
+		}
448
+		// decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
449
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
450
+		foreach ($ticket_line_item->children() as $child_line_item) {
451
+			if (
452
+				$child_line_item->is_sub_line_item()
453
+				&& ! $child_line_item->is_percent()
454
+				&& ! $child_line_item->is_cancellation()
455
+			) {
456
+				$child_line_item->set_quantity($child_line_item->quantity() - $qty);
457
+			}
458
+		}
459
+		// get cancellation sub line item
460
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
461
+			$ticket_line_item,
462
+			EEM_Line_Item::type_cancellation
463
+		);
464
+		$cancellation_line_item = reset($cancellation_line_item);
465
+		// verify that this ticket was indeed previously cancelled
466
+		if ($cancellation_line_item instanceof EE_Line_Item) {
467
+			// increment cancelled quantity
468
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
469
+		} else {
470
+			// create cancellation sub line item
471
+			$cancellation_line_item = EE_Line_Item::new_instance(
472
+				[
473
+					'LIN_name'       => esc_html__('Cancellation', 'event_espresso'),
474
+					'LIN_desc'       => sprintf(
475
+						esc_html_x(
476
+							'Cancelled %1$s : %2$s',
477
+							'Cancelled Ticket Name : 2015-01-01 11:11',
478
+							'event_espresso'
479
+						),
480
+						$ticket_line_item->name(),
481
+						current_time(get_option('date_format') . ' ' . get_option('time_format'))
482
+					),
483
+					'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
484
+					'LIN_quantity'   => $qty,
485
+					'LIN_is_taxable' => $ticket_line_item->is_taxable(),
486
+					'LIN_order'      => count($ticket_line_item->children()),
487
+					'LIN_total'      => 0, // $ticket_line_item->unit_price()
488
+					'LIN_type'       => EEM_Line_Item::type_cancellation,
489
+				]
490
+			);
491
+			$ticket_line_item->add_child_line_item($cancellation_line_item);
492
+		}
493
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
494
+			// decrement parent line item quantity
495
+			$event_line_item = $ticket_line_item->parent();
496
+			if (
497
+				$event_line_item instanceof EE_Line_Item
498
+				&& $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
499
+			) {
500
+				$event_line_item->set_quantity($event_line_item->quantity() - $qty);
501
+				$event_line_item->save();
502
+			}
503
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
504
+			return true;
505
+		}
506
+		return false;
507
+	}
508
+
509
+
510
+	/**
511
+	 * reinstates (un-cancels?) a previously canceled ticket line item,
512
+	 * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
513
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
514
+	 *
515
+	 * @param EE_Line_Item $ticket_line_item
516
+	 * @param int          $qty
517
+	 * @return bool success
518
+	 * @throws EE_Error
519
+	 * @throws InvalidArgumentException
520
+	 * @throws InvalidDataTypeException
521
+	 * @throws InvalidInterfaceException
522
+	 * @throws ReflectionException
523
+	 */
524
+	public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
525
+	{
526
+		// validate incoming line_item
527
+		if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) {
528
+			throw new EE_Error(
529
+				sprintf(
530
+					esc_html__(
531
+						'The supplied line item must have an Object Type of "Ticket", not %1$s.',
532
+						'event_espresso'
533
+					),
534
+					$ticket_line_item->type()
535
+				)
536
+			);
537
+		}
538
+		// get cancellation sub line item
539
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
540
+			$ticket_line_item,
541
+			EEM_Line_Item::type_cancellation
542
+		);
543
+		$cancellation_line_item = reset($cancellation_line_item);
544
+		// verify that this ticket was indeed previously cancelled
545
+		if (! $cancellation_line_item instanceof EE_Line_Item) {
546
+			return false;
547
+		}
548
+		if ($cancellation_line_item->quantity() > $qty) {
549
+			// decrement cancelled quantity
550
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
551
+		} elseif ($cancellation_line_item->quantity() === $qty) {
552
+			// decrement cancelled quantity in case anyone still has the object kicking around
553
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
554
+			// delete because quantity will end up as 0
555
+			$cancellation_line_item->delete();
556
+			// and attempt to destroy the object,
557
+			// even though PHP won't actually destroy it until it needs the memory
558
+			unset($cancellation_line_item);
559
+		} else {
560
+			// what ?!?! negative quantity ?!?!
561
+			throw new EE_Error(
562
+				sprintf(
563
+					esc_html__(
564
+						'Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
565
+						'event_espresso'
566
+					),
567
+					$qty,
568
+					$cancellation_line_item->quantity()
569
+				)
570
+			);
571
+		}
572
+		// increment ticket quantity
573
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
574
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
575
+			// increment parent line item quantity
576
+			$event_line_item = $ticket_line_item->parent();
577
+			if (
578
+				$event_line_item instanceof EE_Line_Item
579
+				&& $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
580
+			) {
581
+				$event_line_item->set_quantity($event_line_item->quantity() + $qty);
582
+			}
583
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
584
+			return true;
585
+		}
586
+		return false;
587
+	}
588
+
589
+
590
+	/**
591
+	 * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
592
+	 * then EE_Line_Item::recalculate_total_including_taxes() on the result
593
+	 *
594
+	 * @param EE_Line_Item $line_item
595
+	 * @return float
596
+	 * @throws EE_Error
597
+	 * @throws InvalidArgumentException
598
+	 * @throws InvalidDataTypeException
599
+	 * @throws InvalidInterfaceException
600
+	 * @throws ReflectionException
601
+	 */
602
+	public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
603
+	{
604
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
605
+		return $grand_total_line_item->recalculate_total_including_taxes();
606
+	}
607
+
608
+
609
+	/**
610
+	 * Gets the line item which contains the subtotal of all the items
611
+	 *
612
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
613
+	 * @return EE_Line_Item
614
+	 * @throws EE_Error
615
+	 * @throws InvalidArgumentException
616
+	 * @throws InvalidDataTypeException
617
+	 * @throws InvalidInterfaceException
618
+	 * @throws ReflectionException
619
+	 */
620
+	public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
621
+	{
622
+		$pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
623
+		return $pre_tax_subtotal instanceof EE_Line_Item
624
+			? $pre_tax_subtotal
625
+			: self::create_pre_tax_subtotal($total_line_item);
626
+	}
627
+
628
+
629
+	/**
630
+	 * Gets the line item for the taxes subtotal
631
+	 *
632
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
633
+	 * @return EE_Line_Item
634
+	 * @throws EE_Error
635
+	 * @throws InvalidArgumentException
636
+	 * @throws InvalidDataTypeException
637
+	 * @throws InvalidInterfaceException
638
+	 * @throws ReflectionException
639
+	 */
640
+	public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
641
+	{
642
+		$taxes = $total_line_item->get_child_line_item('taxes');
643
+		return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
644
+	}
645
+
646
+
647
+	/**
648
+	 * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
649
+	 *
650
+	 * @param EE_Line_Item   $line_item
651
+	 * @param EE_Transaction $transaction
652
+	 * @return void
653
+	 * @throws EE_Error
654
+	 * @throws InvalidArgumentException
655
+	 * @throws InvalidDataTypeException
656
+	 * @throws InvalidInterfaceException
657
+	 * @throws ReflectionException
658
+	 */
659
+	public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = null)
660
+	{
661
+		if ($transaction) {
662
+			/** @type EEM_Transaction $EEM_Transaction */
663
+			$EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
664
+			$TXN_ID          = $EEM_Transaction->ensure_is_ID($transaction);
665
+			$line_item->set_TXN_ID($TXN_ID);
666
+		}
667
+	}
668
+
669
+
670
+	/**
671
+	 * Creates a new default total line item for the transaction,
672
+	 * and its tickets subtotal and taxes subtotal line items (and adds the
673
+	 * existing taxes as children of the taxes subtotal line item)
674
+	 *
675
+	 * @param EE_Transaction $transaction
676
+	 * @return EE_Line_Item of type total
677
+	 * @throws EE_Error
678
+	 * @throws InvalidArgumentException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws InvalidInterfaceException
681
+	 * @throws ReflectionException
682
+	 */
683
+	public static function create_total_line_item($transaction = null)
684
+	{
685
+		$total_line_item = EE_Line_Item::new_instance(
686
+			[
687
+				'LIN_code' => 'total',
688
+				'LIN_name' => esc_html__('Grand Total', 'event_espresso'),
689
+				'LIN_type' => EEM_Line_Item::type_total,
690
+				'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TRANSACTION,
691
+			]
692
+		);
693
+		$total_line_item = apply_filters(
694
+			'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
695
+			$total_line_item
696
+		);
697
+		self::set_TXN_ID($total_line_item, $transaction);
698
+		self::create_pre_tax_subtotal($total_line_item, $transaction);
699
+		self::create_taxes_subtotal($total_line_item, $transaction);
700
+		return $total_line_item;
701
+	}
702
+
703
+
704
+	/**
705
+	 * Creates a default items subtotal line item
706
+	 *
707
+	 * @param EE_Line_Item   $total_line_item
708
+	 * @param EE_Transaction $transaction
709
+	 * @return EE_Line_Item
710
+	 * @throws EE_Error
711
+	 * @throws InvalidArgumentException
712
+	 * @throws InvalidDataTypeException
713
+	 * @throws InvalidInterfaceException
714
+	 * @throws ReflectionException
715
+	 */
716
+	protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = null)
717
+	{
718
+		$pre_tax_line_item = EE_Line_Item::new_instance(
719
+			[
720
+				'LIN_code' => 'pre-tax-subtotal',
721
+				'LIN_name' => esc_html__('Pre-Tax Subtotal', 'event_espresso'),
722
+				'LIN_type' => EEM_Line_Item::type_sub_total,
723
+			]
724
+		);
725
+		$pre_tax_line_item = apply_filters(
726
+			'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
727
+			$pre_tax_line_item
728
+		);
729
+		self::set_TXN_ID($pre_tax_line_item, $transaction);
730
+		$total_line_item->add_child_line_item($pre_tax_line_item);
731
+		self::create_event_subtotal($pre_tax_line_item, $transaction);
732
+		return $pre_tax_line_item;
733
+	}
734
+
735
+
736
+	/**
737
+	 * Creates a line item for the taxes subtotal and finds all the tax prices
738
+	 * and applies taxes to it
739
+	 *
740
+	 * @param EE_Line_Item   $total_line_item of type EEM_Line_Item::type_total
741
+	 * @param EE_Transaction $transaction
742
+	 * @return EE_Line_Item
743
+	 * @throws EE_Error
744
+	 * @throws InvalidArgumentException
745
+	 * @throws InvalidDataTypeException
746
+	 * @throws InvalidInterfaceException
747
+	 * @throws ReflectionException
748
+	 */
749
+	protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
750
+	{
751
+		$tax_line_item = EE_Line_Item::new_instance(
752
+			[
753
+				'LIN_code'  => 'taxes',
754
+				'LIN_name'  => esc_html__('Taxes', 'event_espresso'),
755
+				'LIN_type'  => EEM_Line_Item::type_tax_sub_total,
756
+				'LIN_order' => 1000,// this should always come last
757
+			]
758
+		);
759
+		$tax_line_item = apply_filters(
760
+			'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
761
+			$tax_line_item
762
+		);
763
+		self::set_TXN_ID($tax_line_item, $transaction);
764
+		$total_line_item->add_child_line_item($tax_line_item);
765
+		// and lastly, add the actual taxes
766
+		self::apply_taxes($total_line_item);
767
+		return $tax_line_item;
768
+	}
769
+
770
+
771
+	/**
772
+	 * Creates a default items subtotal line item
773
+	 *
774
+	 * @param EE_Line_Item   $pre_tax_line_item
775
+	 * @param EE_Transaction $transaction
776
+	 * @param EE_Event       $event
777
+	 * @return EE_Line_Item
778
+	 * @throws EE_Error
779
+	 * @throws InvalidArgumentException
780
+	 * @throws InvalidDataTypeException
781
+	 * @throws InvalidInterfaceException
782
+	 * @throws ReflectionException
783
+	 */
784
+	public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = null, $event = null)
785
+	{
786
+		$event_line_item = EE_Line_Item::new_instance(
787
+			[
788
+				'LIN_code' => self::get_event_code($event),
789
+				'LIN_name' => self::get_event_name($event),
790
+				'LIN_desc' => self::get_event_desc($event),
791
+				'LIN_type' => EEM_Line_Item::type_sub_total,
792
+				'OBJ_type' => EEM_Line_Item::OBJ_TYPE_EVENT,
793
+				'OBJ_ID'   => $event instanceof EE_Event ? $event->ID() : 0,
794
+			]
795
+		);
796
+		$event_line_item = apply_filters(
797
+			'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
798
+			$event_line_item
799
+		);
800
+		self::set_TXN_ID($event_line_item, $transaction);
801
+		$pre_tax_line_item->add_child_line_item($event_line_item);
802
+		return $event_line_item;
803
+	}
804
+
805
+
806
+	/**
807
+	 * Gets what the event ticket's code SHOULD be
808
+	 *
809
+	 * @param EE_Event $event
810
+	 * @return string
811
+	 * @throws EE_Error|ReflectionException
812
+	 */
813
+	public static function get_event_code($event)
814
+	{
815
+		return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
816
+	}
817
+
818
+
819
+	/**
820
+	 * Gets the event name
821
+	 *
822
+	 * @param EE_Event $event
823
+	 * @return string
824
+	 * @throws EE_Error
825
+	 */
826
+	public static function get_event_name($event)
827
+	{
828
+		return $event instanceof EE_Event
829
+			? mb_substr($event->name(), 0, 245)
830
+			: esc_html__('Event', 'event_espresso');
831
+	}
832
+
833
+
834
+	/**
835
+	 * Gets the event excerpt
836
+	 *
837
+	 * @param EE_Event $event
838
+	 * @return string
839
+	 * @throws EE_Error
840
+	 */
841
+	public static function get_event_desc($event)
842
+	{
843
+		return $event instanceof EE_Event ? $event->short_description() : '';
844
+	}
845
+
846
+
847
+	/**
848
+	 * Given the grand total line item and a ticket, finds the event sub-total
849
+	 * line item the ticket's purchase should be added onto
850
+	 *
851
+	 * @access public
852
+	 * @param EE_Line_Item $grand_total the grand total line item
853
+	 * @param EE_Ticket    $ticket
854
+	 * @return EE_Line_Item
855
+	 * @throws EE_Error
856
+	 * @throws InvalidArgumentException
857
+	 * @throws InvalidDataTypeException
858
+	 * @throws InvalidInterfaceException
859
+	 * @throws ReflectionException
860
+	 */
861
+	public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
862
+	{
863
+		$first_datetime = $ticket->first_datetime();
864
+		if (! $first_datetime instanceof EE_Datetime) {
865
+			throw new EE_Error(
866
+				sprintf(
867
+					esc_html__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'),
868
+					$ticket->ID()
869
+				)
870
+			);
871
+		}
872
+		$event = $first_datetime->event();
873
+		if (! $event instanceof EE_Event) {
874
+			throw new EE_Error(
875
+				sprintf(
876
+					esc_html__(
877
+						'The supplied ticket (ID %d) has no event data associated with it.',
878
+						'event_espresso'
879
+					),
880
+					$ticket->ID()
881
+				)
882
+			);
883
+		}
884
+		$events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
885
+		if (! $events_sub_total instanceof EE_Line_Item) {
886
+			throw new EE_Error(
887
+				sprintf(
888
+					esc_html__(
889
+						'There is no events sub-total for ticket %s on total line item %d',
890
+						'event_espresso'
891
+					),
892
+					$ticket->ID(),
893
+					$grand_total->ID()
894
+				)
895
+			);
896
+		}
897
+		return $events_sub_total;
898
+	}
899
+
900
+
901
+	/**
902
+	 * Gets the event line item
903
+	 *
904
+	 * @param EE_Line_Item $grand_total
905
+	 * @param EE_Event     $event
906
+	 * @return EE_Line_Item for the event subtotal which is a child of $grand_total
907
+	 * @throws EE_Error
908
+	 * @throws InvalidArgumentException
909
+	 * @throws InvalidDataTypeException
910
+	 * @throws InvalidInterfaceException
911
+	 * @throws ReflectionException
912
+	 */
913
+	public static function get_event_line_item(EE_Line_Item $grand_total, $event)
914
+	{
915
+		/** @type EE_Event $event */
916
+		$event           = EEM_Event::instance()->ensure_is_obj($event, true);
917
+		$event_line_item = null;
918
+		$found           = false;
919
+		foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
920
+			// default event subtotal, we should only ever find this the first time this method is called
921
+			if (! $event_line_item->OBJ_ID()) {
922
+				// let's use this! but first... set the event details
923
+				EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
924
+				$found = true;
925
+				break;
926
+			}
927
+			if ($event_line_item->OBJ_ID() === $event->ID()) {
928
+				// found existing line item for this event in the cart, so break out of loop and use this one
929
+				$found = true;
930
+				break;
931
+			}
932
+		}
933
+		if (! $found) {
934
+			// there is no event sub-total yet, so add it
935
+			$pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
936
+			// create a new "event" subtotal below that
937
+			$event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
938
+			// and set the event details
939
+			EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
940
+		}
941
+		return $event_line_item;
942
+	}
943
+
944
+
945
+	/**
946
+	 * Creates a default items subtotal line item
947
+	 *
948
+	 * @param EE_Line_Item   $event_line_item
949
+	 * @param EE_Event       $event
950
+	 * @param EE_Transaction $transaction
951
+	 * @return void
952
+	 * @throws EE_Error
953
+	 * @throws InvalidArgumentException
954
+	 * @throws InvalidDataTypeException
955
+	 * @throws InvalidInterfaceException
956
+	 * @throws ReflectionException
957
+	 */
958
+	public static function set_event_subtotal_details(
959
+		EE_Line_Item $event_line_item,
960
+		EE_Event $event,
961
+		$transaction = null
962
+	) {
963
+		if ($event instanceof EE_Event) {
964
+			$event_line_item->set_code(self::get_event_code($event));
965
+			$event_line_item->set_name(self::get_event_name($event));
966
+			$event_line_item->set_desc(self::get_event_desc($event));
967
+			$event_line_item->set_OBJ_ID($event->ID());
968
+		}
969
+		self::set_TXN_ID($event_line_item, $transaction);
970
+	}
971
+
972
+
973
+	/**
974
+	 * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
975
+	 * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
976
+	 * any old taxes are removed
977
+	 *
978
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
979
+	 * @param bool         $update_txn_status
980
+	 * @return bool
981
+	 * @throws EE_Error
982
+	 * @throws InvalidArgumentException
983
+	 * @throws InvalidDataTypeException
984
+	 * @throws InvalidInterfaceException
985
+	 * @throws ReflectionException
986
+	 * @throws RuntimeException
987
+	 */
988
+	public static function apply_taxes(EE_Line_Item $total_line_item, $update_txn_status = false)
989
+	{
990
+		/** @type EEM_Price $EEM_Price */
991
+		$EEM_Price = EE_Registry::instance()->load_model('Price');
992
+		// get array of taxes via Price Model
993
+		$ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes();
994
+		ksort($ordered_taxes);
995
+		$taxes_line_item = self::get_taxes_subtotal($total_line_item);
996
+		// just to be safe, remove its old tax line items
997
+		$deleted = $taxes_line_item->delete_children_line_items();
998
+		$updates = false;
999
+		// loop thru taxes
1000
+		foreach ($ordered_taxes as $order => $taxes) {
1001
+			foreach ($taxes as $tax) {
1002
+				if ($tax instanceof EE_Price) {
1003
+					$tax_line_item = EE_Line_Item::new_instance(
1004
+						[
1005
+							'LIN_name'       => $tax->name(),
1006
+							'LIN_desc'       => $tax->desc(),
1007
+							'LIN_percent'    => $tax->amount(),
1008
+							'LIN_is_taxable' => false,
1009
+							'LIN_order'      => $order,
1010
+							'LIN_total'      => 0,
1011
+							'LIN_type'       => EEM_Line_Item::type_tax,
1012
+							'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_PRICE,
1013
+							'OBJ_ID'         => $tax->ID(),
1014
+						]
1015
+					);
1016
+					$tax_line_item = apply_filters(
1017
+						'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
1018
+						$tax_line_item
1019
+					);
1020
+					$updates       = $taxes_line_item->add_child_line_item($tax_line_item)
1021
+						? true
1022
+						: $updates;
1023
+				}
1024
+			}
1025
+		}
1026
+		// only recalculate totals if something changed
1027
+		if ($deleted || $updates) {
1028
+			$total_line_item->recalculate_total_including_taxes($update_txn_status);
1029
+			return true;
1030
+		}
1031
+		return false;
1032
+	}
1033
+
1034
+
1035
+	/**
1036
+	 * Ensures that taxes have been applied to the order, if not applies them.
1037
+	 * Returns the total amount of tax
1038
+	 *
1039
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
1040
+	 * @return float
1041
+	 * @throws EE_Error
1042
+	 * @throws InvalidArgumentException
1043
+	 * @throws InvalidDataTypeException
1044
+	 * @throws InvalidInterfaceException
1045
+	 * @throws ReflectionException
1046
+	 */
1047
+	public static function ensure_taxes_applied($total_line_item)
1048
+	{
1049
+		$taxes_subtotal = self::get_taxes_subtotal($total_line_item);
1050
+		if (! $taxes_subtotal->children()) {
1051
+			self::apply_taxes($total_line_item);
1052
+		}
1053
+		return $taxes_subtotal->total();
1054
+	}
1055
+
1056
+
1057
+	/**
1058
+	 * Deletes ALL children of the passed line item
1059
+	 *
1060
+	 * @param EE_Line_Item $parent_line_item
1061
+	 * @return bool
1062
+	 * @throws EE_Error
1063
+	 * @throws InvalidArgumentException
1064
+	 * @throws InvalidDataTypeException
1065
+	 * @throws InvalidInterfaceException
1066
+	 * @throws ReflectionException
1067
+	 */
1068
+	public static function delete_all_child_items(EE_Line_Item $parent_line_item)
1069
+	{
1070
+		$deleted = 0;
1071
+		foreach ($parent_line_item->children() as $child_line_item) {
1072
+			if ($child_line_item instanceof EE_Line_Item) {
1073
+				$deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
1074
+				if ($child_line_item->ID()) {
1075
+					$child_line_item->delete();
1076
+					unset($child_line_item);
1077
+				} else {
1078
+					$parent_line_item->delete_child_line_item($child_line_item->code());
1079
+				}
1080
+				$deleted++;
1081
+			}
1082
+		}
1083
+		return $deleted;
1084
+	}
1085
+
1086
+
1087
+	/**
1088
+	 * Deletes the line items as indicated by the line item code(s) provided,
1089
+	 * regardless of where they're found in the line item tree. Automatically
1090
+	 * re-calculates the line item totals and updates the related transaction. But
1091
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
1092
+	 * should probably change because of this).
1093
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
1094
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
1095
+	 *
1096
+	 * @param EE_Line_Item      $total_line_item of type EEM_Line_Item::type_total
1097
+	 * @param array|bool|string $line_item_codes
1098
+	 * @return int number of items successfully removed
1099
+	 * @throws EE_Error|ReflectionException
1100
+	 */
1101
+	public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = false)
1102
+	{
1103
+		if ($total_line_item->type() !== EEM_Line_Item::type_total) {
1104
+			EE_Error::doing_it_wrong(
1105
+				'EEH_Line_Item::delete_items',
1106
+				esc_html__(
1107
+					'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
1108
+					'event_espresso'
1109
+				),
1110
+				'4.6.18'
1111
+			);
1112
+		}
1113
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1114
+
1115
+		// check if only a single line_item_id was passed
1116
+		if (! empty($line_item_codes) && ! is_array($line_item_codes)) {
1117
+			// place single line_item_id in an array to appear as multiple line_item_ids
1118
+			$line_item_codes = [$line_item_codes];
1119
+		}
1120
+		$removals = 0;
1121
+		// cycle thru line_item_ids
1122
+		foreach ($line_item_codes as $line_item_id) {
1123
+			$removals += $total_line_item->delete_child_line_item($line_item_id);
1124
+		}
1125
+
1126
+		if ($removals > 0) {
1127
+			$total_line_item->recalculate_taxes_and_tax_total();
1128
+			return $removals;
1129
+		} else {
1130
+			return false;
1131
+		}
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 * Overwrites the previous tax by clearing out the old taxes, and creates a new
1137
+	 * tax and updates the total line item accordingly
1138
+	 *
1139
+	 * @param EE_Line_Item $total_line_item
1140
+	 * @param float        $amount
1141
+	 * @param string       $name
1142
+	 * @param string       $description
1143
+	 * @param string       $code
1144
+	 * @param boolean      $add_to_existing_line_item
1145
+	 *                          if true, and a duplicate line item with the same code is found,
1146
+	 *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
1147
+	 * @return EE_Line_Item the new tax line item created
1148
+	 * @throws EE_Error
1149
+	 * @throws InvalidArgumentException
1150
+	 * @throws InvalidDataTypeException
1151
+	 * @throws InvalidInterfaceException
1152
+	 * @throws ReflectionException
1153
+	 */
1154
+	public static function set_total_tax_to(
1155
+		EE_Line_Item $total_line_item,
1156
+		$amount,
1157
+		$name = null,
1158
+		$description = null,
1159
+		$code = null,
1160
+		$add_to_existing_line_item = false
1161
+	) {
1162
+		$tax_subtotal  = self::get_taxes_subtotal($total_line_item);
1163
+		$taxable_total = $total_line_item->taxable_total();
1164
+
1165
+		if ($add_to_existing_line_item) {
1166
+			$new_tax = $tax_subtotal->get_child_line_item($code);
1167
+			EEM_Line_Item::instance()->delete(
1168
+				[['LIN_code' => ['!=', $code], 'LIN_parent' => $tax_subtotal->ID()]]
1169
+			);
1170
+		} else {
1171
+			$new_tax = null;
1172
+			$tax_subtotal->delete_children_line_items();
1173
+		}
1174
+		if ($new_tax) {
1175
+			$new_tax->set_total($new_tax->total() + $amount);
1176
+			$new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
1177
+		} else {
1178
+			// no existing tax item. Create it
1179
+			$new_tax = EE_Line_Item::new_instance(
1180
+				[
1181
+					'TXN_ID'      => $total_line_item->TXN_ID(),
1182
+					'LIN_name'    => $name ? $name : esc_html__('Tax', 'event_espresso'),
1183
+					'LIN_desc'    => $description ? $description : '',
1184
+					'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
1185
+					'LIN_total'   => $amount,
1186
+					'LIN_parent'  => $tax_subtotal->ID(),
1187
+					'LIN_type'    => EEM_Line_Item::type_tax,
1188
+					'LIN_code'    => $code,
1189
+				]
1190
+			);
1191
+		}
1192
+
1193
+		$new_tax = apply_filters(
1194
+			'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
1195
+			$new_tax,
1196
+			$total_line_item
1197
+		);
1198
+		$new_tax->save();
1199
+		$tax_subtotal->set_total($new_tax->total());
1200
+		$tax_subtotal->save();
1201
+		$total_line_item->recalculate_total_including_taxes();
1202
+		return $new_tax;
1203
+	}
1204
+
1205
+
1206
+	/**
1207
+	 * Makes all the line items which are children of $line_item taxable (or not).
1208
+	 * Does NOT save the line items
1209
+	 *
1210
+	 * @param EE_Line_Item $line_item
1211
+	 * @param boolean      $taxable
1212
+	 * @param string       $code_substring_for_whitelist if this string is part of the line item's code
1213
+	 *                                                   it will be whitelisted (ie, except from becoming taxable)
1214
+	 * @throws EE_Error|ReflectionException
1215
+	 */
1216
+	public static function set_line_items_taxable(
1217
+		EE_Line_Item $line_item,
1218
+		$taxable = true,
1219
+		$code_substring_for_whitelist = null
1220
+	) {
1221
+		$whitelisted = false;
1222
+		if ($code_substring_for_whitelist !== null) {
1223
+			$whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false;
1224
+		}
1225
+		if (! $whitelisted && $line_item->is_line_item()) {
1226
+			$line_item->set_is_taxable($taxable);
1227
+		}
1228
+		foreach ($line_item->children() as $child_line_item) {
1229
+			EEH_Line_Item::set_line_items_taxable(
1230
+				$child_line_item,
1231
+				$taxable,
1232
+				$code_substring_for_whitelist
1233
+			);
1234
+		}
1235
+	}
1236
+
1237
+
1238
+	/**
1239
+	 * Gets all descendants that are event subtotals
1240
+	 *
1241
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1242
+	 * @return EE_Line_Item[]
1243
+	 * @throws EE_Error|ReflectionException
1244
+	 * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1245
+	 */
1246
+	public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1247
+	{
1248
+		return self::get_subtotals_of_object_type($parent_line_item, EEM_Line_Item::OBJ_TYPE_EVENT);
1249
+	}
1250
+
1251
+
1252
+	/**
1253
+	 * Gets all descendants subtotals that match the supplied object type
1254
+	 *
1255
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1256
+	 * @param string       $obj_type
1257
+	 * @return EE_Line_Item[]
1258
+	 * @throws EE_Error|ReflectionException
1259
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1260
+	 */
1261
+	public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1262
+	{
1263
+		return self::_get_descendants_by_type_and_object_type(
1264
+			$parent_line_item,
1265
+			EEM_Line_Item::type_sub_total,
1266
+			$obj_type
1267
+		);
1268
+	}
1269
+
1270
+
1271
+	/**
1272
+	 * Gets all descendants that are tickets
1273
+	 *
1274
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1275
+	 * @return EE_Line_Item[]
1276
+	 * @throws EE_Error|ReflectionException
1277
+	 * @uses  EEH_Line_Item::get_line_items_of_object_type()
1278
+	 */
1279
+	public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1280
+	{
1281
+		return self::get_line_items_of_object_type(
1282
+			$parent_line_item,
1283
+			EEM_Line_Item::OBJ_TYPE_TICKET
1284
+		);
1285
+	}
1286
+
1287
+
1288
+	/**
1289
+	 * Gets all descendants subtotals that match the supplied object type
1290
+	 *
1291
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1292
+	 * @param string       $obj_type
1293
+	 * @return EE_Line_Item[]
1294
+	 * @throws EE_Error|ReflectionException
1295
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1296
+	 */
1297
+	public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1298
+	{
1299
+		return self::_get_descendants_by_type_and_object_type(
1300
+			$parent_line_item,
1301
+			EEM_Line_Item::type_line_item,
1302
+			$obj_type
1303
+		);
1304
+	}
1305
+
1306
+
1307
+	/**
1308
+	 * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1309
+	 *
1310
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1311
+	 * @return EE_Line_Item[]
1312
+	 * @throws EE_Error|ReflectionException
1313
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1314
+	 */
1315
+	public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1316
+	{
1317
+		return EEH_Line_Item::get_descendants_of_type(
1318
+			$parent_line_item,
1319
+			EEM_Line_Item::type_tax
1320
+		);
1321
+	}
1322
+
1323
+
1324
+	/**
1325
+	 * Gets all the real items purchased which are children of this item
1326
+	 *
1327
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1328
+	 * @return EE_Line_Item[]
1329
+	 * @throws EE_Error|ReflectionException
1330
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1331
+	 */
1332
+	public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1333
+	{
1334
+		return EEH_Line_Item::get_descendants_of_type(
1335
+			$parent_line_item,
1336
+			EEM_Line_Item::type_line_item
1337
+		);
1338
+	}
1339
+
1340
+
1341
+	/**
1342
+	 * Gets all descendants of supplied line item that match the supplied line item type
1343
+	 *
1344
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1345
+	 * @param string       $line_item_type   one of the EEM_Line_Item constants
1346
+	 * @return EE_Line_Item[]
1347
+	 * @throws EE_Error|ReflectionException
1348
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1349
+	 */
1350
+	public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1351
+	{
1352
+		return self::_get_descendants_by_type_and_object_type(
1353
+			$parent_line_item,
1354
+			$line_item_type,
1355
+			null
1356
+		);
1357
+	}
1358
+
1359
+
1360
+	/**
1361
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type
1362
+	 * as well
1363
+	 *
1364
+	 * @param EE_Line_Item  $parent_line_item - the line item to find descendants of
1365
+	 * @param string        $line_item_type   one of the EEM_Line_Item constants
1366
+	 * @param string | NULL $obj_type         object model class name (minus prefix) or NULL to ignore object type when
1367
+	 *                                        searching
1368
+	 * @return EE_Line_Item[]
1369
+	 * @throws EE_Error|ReflectionException
1370
+	 */
1371
+	protected static function _get_descendants_by_type_and_object_type(
1372
+		EE_Line_Item $parent_line_item,
1373
+		$line_item_type,
1374
+		$obj_type = null
1375
+	) {
1376
+		$objects = [];
1377
+		$child_objects = [];
1378
+		foreach ($parent_line_item->children() as $child_line_item) {
1379
+			if ($child_line_item instanceof EE_Line_Item) {
1380
+				if (
1381
+					$child_line_item->type() === $line_item_type
1382
+					&& (
1383
+						$child_line_item->OBJ_type() === $obj_type || $obj_type === null
1384
+					)
1385
+				) {
1386
+					$objects[] = $child_line_item;
1387
+				} else {
1388
+					// go-through-all-its children looking for more matches
1389
+					$child_objects[] = self::_get_descendants_by_type_and_object_type(
1390
+						$child_line_item,
1391
+						$line_item_type,
1392
+						$obj_type
1393
+					);
1394
+				}
1395
+			}
1396
+		}
1397
+		return array_merge([], $objects, ...$child_objects);
1398
+	}
1399
+
1400
+
1401
+	/**
1402
+	 * Gets all descendants subtotals that match the supplied object type
1403
+	 *
1404
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1405
+	 * @param string       $OBJ_type         object type (like Event)
1406
+	 * @param array        $OBJ_IDs          array of OBJ_IDs
1407
+	 * @return EE_Line_Item[]
1408
+	 * @throws EE_Error|ReflectionException
1409
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1410
+	 */
1411
+	public static function get_line_items_by_object_type_and_IDs(
1412
+		EE_Line_Item $parent_line_item,
1413
+		$OBJ_type = '',
1414
+		$OBJ_IDs = []
1415
+	) {
1416
+		return self::_get_descendants_by_object_type_and_object_ID(
1417
+			$parent_line_item,
1418
+			$OBJ_type,
1419
+			$OBJ_IDs
1420
+		);
1421
+	}
1422
+
1423
+
1424
+	/**
1425
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type
1426
+	 * as well
1427
+	 *
1428
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1429
+	 * @param string       $OBJ_type         object type (like Event)
1430
+	 * @param array        $OBJ_IDs          array of OBJ_IDs
1431
+	 * @return EE_Line_Item[]
1432
+	 * @throws EE_Error|ReflectionException
1433
+	 */
1434
+	protected static function _get_descendants_by_object_type_and_object_ID(
1435
+		EE_Line_Item $parent_line_item,
1436
+		$OBJ_type,
1437
+		$OBJ_IDs
1438
+	) {
1439
+		$objects = [];
1440
+		$child_objects = [];
1441
+		foreach ($parent_line_item->children() as $child_line_item) {
1442
+			if ($child_line_item instanceof EE_Line_Item) {
1443
+				if (
1444
+					$child_line_item->OBJ_type() === $OBJ_type
1445
+					&& is_array($OBJ_IDs)
1446
+					&& in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1447
+				) {
1448
+					$objects[] = $child_line_item;
1449
+				} else {
1450
+					// go-through-all-its children looking for more matches
1451
+					$child_objects[] = self::_get_descendants_by_object_type_and_object_ID(
1452
+						$child_line_item,
1453
+						$OBJ_type,
1454
+						$OBJ_IDs
1455
+					);
1456
+				}
1457
+			}
1458
+		}
1459
+		return array_merge([], $objects, ...$child_objects);
1460
+	}
1461
+
1462
+
1463
+	/**
1464
+	 * Uses a breadth-first-search in order to find the nearest descendant of
1465
+	 * the specified type and returns it, else NULL
1466
+	 *
1467
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1468
+	 * @param string       $type             like one of the EEM_Line_Item::type_*
1469
+	 * @return EE_Line_Item
1470
+	 * @throws EE_Error
1471
+	 * @throws InvalidArgumentException
1472
+	 * @throws InvalidDataTypeException
1473
+	 * @throws InvalidInterfaceException
1474
+	 * @throws ReflectionException
1475
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1476
+	 */
1477
+	public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1478
+	{
1479
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1480
+	}
1481
+
1482
+
1483
+	/**
1484
+	 * Uses a breadth-first-search in order to find the nearest descendant
1485
+	 * having the specified LIN_code and returns it, else NULL
1486
+	 *
1487
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1488
+	 * @param string       $code             any value used for LIN_code
1489
+	 * @return EE_Line_Item
1490
+	 * @throws EE_Error
1491
+	 * @throws InvalidArgumentException
1492
+	 * @throws InvalidDataTypeException
1493
+	 * @throws InvalidInterfaceException
1494
+	 * @throws ReflectionException
1495
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1496
+	 */
1497
+	public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1498
+	{
1499
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1500
+	}
1501
+
1502
+
1503
+	/**
1504
+	 * Uses a breadth-first-search in order to find the nearest descendant
1505
+	 * having the specified LIN_code and returns it, else NULL
1506
+	 *
1507
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1508
+	 * @param string       $search_field     name of EE_Line_Item property
1509
+	 * @param string       $value            any value stored in $search_field
1510
+	 * @return EE_Line_Item
1511
+	 * @throws EE_Error
1512
+	 * @throws InvalidArgumentException
1513
+	 * @throws InvalidDataTypeException
1514
+	 * @throws InvalidInterfaceException
1515
+	 * @throws ReflectionException
1516
+	 */
1517
+	protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1518
+	{
1519
+		foreach ($parent_line_item->children() as $child) {
1520
+			if ($child->get($search_field) == $value) {
1521
+				return $child;
1522
+			}
1523
+		}
1524
+		foreach ($parent_line_item->children() as $child) {
1525
+			$descendant_found = self::_get_nearest_descendant(
1526
+				$child,
1527
+				$search_field,
1528
+				$value
1529
+			);
1530
+			if ($descendant_found) {
1531
+				return $descendant_found;
1532
+			}
1533
+		}
1534
+		return null;
1535
+	}
1536
+
1537
+
1538
+	/**
1539
+	 * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1540
+	 * else recursively walks up the line item tree until a parent of type total is found,
1541
+	 *
1542
+	 * @param EE_Line_Item $line_item
1543
+	 * @return EE_Line_Item
1544
+	 * @throws EE_Error|ReflectionException
1545
+	 */
1546
+	public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item)
1547
+	{
1548
+		if ($line_item->TXN_ID()) {
1549
+			$total_line_item = $line_item->transaction()->total_line_item(false);
1550
+			if ($total_line_item instanceof EE_Line_Item) {
1551
+				return $total_line_item;
1552
+			}
1553
+		} else {
1554
+			$line_item_parent = $line_item->parent();
1555
+			if ($line_item_parent instanceof EE_Line_Item) {
1556
+				if ($line_item_parent->is_total()) {
1557
+					return $line_item_parent;
1558
+				}
1559
+				return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1560
+			}
1561
+		}
1562
+		throw new EE_Error(
1563
+			sprintf(
1564
+				esc_html__(
1565
+					'A valid grand total for line item %1$d was not found.',
1566
+					'event_espresso'
1567
+				),
1568
+				$line_item->ID()
1569
+			)
1570
+		);
1571
+	}
1572
+
1573
+
1574
+	/**
1575
+	 * Prints out a representation of the line item tree
1576
+	 *
1577
+	 * @param EE_Line_Item $line_item
1578
+	 * @param int          $indentation
1579
+	 * @return void
1580
+	 * @throws EE_Error|ReflectionException
1581
+	 */
1582
+	public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1583
+	{
1584
+		$linebreak = defined('EE_TESTS_DIR') ? "\n" : '<br />';
1585
+		echo $linebreak;
1586
+		if (! $indentation) {
1587
+			echo $linebreak;
1588
+		}
1589
+		for ($i = 0; $i < $indentation; $i++) {
1590
+			echo ' . ';
1591
+		}
1592
+		$breakdown = '';
1593
+		if ($line_item->is_line_item()) {
1594
+			if ($line_item->is_percent()) {
1595
+				$breakdown = "{$line_item->percent()}%";
1596
+			} else {
1597
+				$breakdown = ' $' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1598
+			}
1599
+		}
1600
+		echo wp_kses($line_item->name(), AllowedTags::getAllowedTags());
1601
+		echo " [ ID: {$line_item->ID()} · qty: {$line_item->quantity()} ] {$line_item->type()} : ";
1602
+		echo ' $' . (string) $line_item->total();
1603
+		if ($breakdown) {
1604
+			echo " ( {$breakdown} )";
1605
+		}
1606
+		if ($line_item->is_taxable()) {
1607
+			echo '  * taxable';
1608
+		}
1609
+		if ($line_item->children()) {
1610
+			foreach ($line_item->children() as $child) {
1611
+				self::visualize($child, $indentation + 1);
1612
+			}
1613
+		}
1614
+		if ($line_item->is_total()) {
1615
+			echo $linebreak . $linebreak;
1616
+		}
1617
+	}
1618
+
1619
+
1620
+	/**
1621
+	 * Calculates the registration's final price, taking into account that they
1622
+	 * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1623
+	 * and receive a portion of any transaction-wide discounts.
1624
+	 * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1625
+	 * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1626
+	 * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1627
+	 * and brent's final price should be $5.50.
1628
+	 * In order to do this, we basically need to traverse the line item tree calculating
1629
+	 * the running totals (just as if we were recalculating the total), but when we identify
1630
+	 * regular line items, we need to keep track of their share of the grand total.
1631
+	 * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1632
+	 * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1633
+	 * when there are non-taxable items; otherwise they would be the same)
1634
+	 *
1635
+	 * @param EE_Line_Item $line_item
1636
+	 * @param array        $billable_ticket_quantities  array of EE_Ticket IDs and their corresponding quantity
1637
+	 *                                                  that can be included in price calculations at this moment
1638
+	 * @return array                                    keys are line items for tickets IDs and values are their share
1639
+	 *                                                  of the running total, plus the key 'total',
1640
+	 *                                                  and 'taxable' which also has keys of all the ticket IDs.
1641
+	 *                                                      ex:
1642
+	 *                                                      [
1643
+	 *                                                          12 => 4.3,
1644
+	 *                                                          23 => 8.0,
1645
+	 *                                                          'total' => 16.6,
1646
+	 *                                                          'taxable' => [
1647
+	 *                                                              12 => 10,
1648
+	 *                                                              23 => 4
1649
+	 *                                                          ]
1650
+	 *                                                      ]
1651
+	 *                                                  So to find which registrations have which final price,
1652
+	 *                                                  we need to find which line item is theirs, via:
1653
+	 *                                                  `EEM_Line_Item::instance()->get_line_item_for_registration(
1654
+	 *                                                      $registration
1655
+	 *                                                  );`
1656
+	 * @throws EE_Error
1657
+	 * @throws InvalidArgumentException
1658
+	 * @throws InvalidDataTypeException
1659
+	 * @throws InvalidInterfaceException
1660
+	 * @throws ReflectionException
1661
+	 */
1662
+	public static function calculate_reg_final_prices_per_line_item(
1663
+		EE_Line_Item $line_item,
1664
+		$billable_ticket_quantities = []
1665
+	) {
1666
+		$running_totals = [
1667
+			'total'   => 0,
1668
+			'taxable' => ['total' => 0],
1669
+		];
1670
+		foreach ($line_item->children() as $child_line_item) {
1671
+			switch ($child_line_item->type()) {
1672
+				case EEM_Line_Item::type_sub_total:
1673
+					$running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1674
+						$child_line_item,
1675
+						$billable_ticket_quantities
1676
+					);
1677
+					// combine arrays but preserve numeric keys
1678
+					$running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1679
+					$running_totals['total']            += $running_totals_from_subtotal['total'];
1680
+					$running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1681
+					break;
1682
+
1683
+				case EEM_Line_Item::type_tax_sub_total:
1684
+					// find how much the taxes percentage is
1685
+					if ($child_line_item->percent() !== 0) {
1686
+						$tax_percent_decimal = $child_line_item->percent() / 100;
1687
+					} else {
1688
+						$tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1689
+					}
1690
+					// and apply to all the taxable totals, and add to the pretax totals
1691
+					foreach ($running_totals as $line_item_id => $this_running_total) {
1692
+						// "total" and "taxable" array key is an exception
1693
+						if ($line_item_id === 'taxable') {
1694
+							continue;
1695
+						}
1696
+						$taxable_total                   = $running_totals['taxable'][ $line_item_id ];
1697
+						$running_totals[ $line_item_id ] += ($taxable_total * $tax_percent_decimal);
1698
+					}
1699
+					break;
1700
+
1701
+				case EEM_Line_Item::type_line_item:
1702
+					// ticket line items or ????
1703
+					if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
1704
+						// kk it's a ticket
1705
+						if (isset($running_totals[ $child_line_item->ID() ])) {
1706
+							// huh? that shouldn't happen.
1707
+							$running_totals['total'] += $child_line_item->total();
1708
+						} else {
1709
+							// its not in our running totals yet. great.
1710
+							if ($child_line_item->is_taxable()) {
1711
+								$taxable_amount = $child_line_item->unit_price();
1712
+							} else {
1713
+								$taxable_amount = 0;
1714
+							}
1715
+							// are we only calculating totals for some tickets?
1716
+							if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) {
1717
+								$quantity                                            =
1718
+									$billable_ticket_quantities[ $child_line_item->OBJ_ID() ];
1719
+								$running_totals[ $child_line_item->ID() ]            = $quantity
1720
+									? $child_line_item->unit_price()
1721
+									: 0;
1722
+								$running_totals['taxable'][ $child_line_item->ID() ] = $quantity
1723
+									? $taxable_amount
1724
+									: 0;
1725
+							} else {
1726
+								$quantity                                            = $child_line_item->quantity();
1727
+								$running_totals[ $child_line_item->ID() ]            = $child_line_item->unit_price();
1728
+								$running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount;
1729
+							}
1730
+							$running_totals['taxable']['total'] += $taxable_amount * $quantity;
1731
+							$running_totals['total']            += $child_line_item->unit_price() * $quantity;
1732
+						}
1733
+					} else {
1734
+						// it's some other type of item added to the cart
1735
+						// it should affect the running totals
1736
+						// basically we want to convert it into a PERCENT modifier. Because
1737
+						// more clearly affect all registration's final price equally
1738
+						$line_items_percent_of_running_total = $running_totals['total'] > 0
1739
+							? ($child_line_item->total() / $running_totals['total']) + 1
1740
+							: 1;
1741
+						foreach ($running_totals as $line_item_id => $this_running_total) {
1742
+							// the "taxable" array key is an exception
1743
+							if ($line_item_id === 'taxable') {
1744
+								continue;
1745
+							}
1746
+							// update the running totals
1747
+							// yes this actually even works for the running grand total!
1748
+							$running_totals[ $line_item_id ] =
1749
+								$line_items_percent_of_running_total * $this_running_total;
1750
+
1751
+							if ($child_line_item->is_taxable()) {
1752
+								$running_totals['taxable'][ $line_item_id ] =
1753
+									$line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ];
1754
+							}
1755
+						}
1756
+					}
1757
+					break;
1758
+			}
1759
+		}
1760
+		return $running_totals;
1761
+	}
1762
+
1763
+
1764
+	/**
1765
+	 * @param EE_Line_Item $total_line_item
1766
+	 * @param EE_Line_Item $ticket_line_item
1767
+	 * @return float | null
1768
+	 * @throws EE_Error
1769
+	 * @throws InvalidArgumentException
1770
+	 * @throws InvalidDataTypeException
1771
+	 * @throws InvalidInterfaceException
1772
+	 * @throws OutOfRangeException
1773
+	 * @throws ReflectionException
1774
+	 */
1775
+	public static function calculate_final_price_for_ticket_line_item(
1776
+		EE_Line_Item $total_line_item,
1777
+		EE_Line_Item $ticket_line_item
1778
+	) {
1779
+		static $final_prices_per_ticket_line_item = [];
1780
+		if (empty($final_prices_per_ticket_line_item)
1781
+			|| empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])
1782
+		) {
1783
+			$final_prices_per_ticket_line_item[ $total_line_item->ID() ] =
1784
+				EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1785
+					$total_line_item
1786
+				);
1787
+		}
1788
+		// ok now find this new registration's final price
1789
+		if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) {
1790
+			return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ];
1791
+		}
1792
+		$message = sprintf(
1793
+			esc_html__(
1794
+				'The final price for the ticket line item (ID:%1$d) could not be calculated.',
1795
+				'event_espresso'
1796
+			),
1797
+			$ticket_line_item->ID()
1798
+		);
1799
+		if (WP_DEBUG) {
1800
+			$message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1801
+			throw new OutOfRangeException($message);
1802
+		}
1803
+		EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1804
+		return null;
1805
+	}
1806
+
1807
+
1808
+	/**
1809
+	 * Creates a duplicate of the line item tree, except only includes billable items
1810
+	 * and the portion of line items attributed to billable things
1811
+	 *
1812
+	 * @param EE_Line_Item      $line_item
1813
+	 * @param EE_Registration[] $registrations
1814
+	 * @return EE_Line_Item
1815
+	 * @throws EE_Error
1816
+	 * @throws InvalidArgumentException
1817
+	 * @throws InvalidDataTypeException
1818
+	 * @throws InvalidInterfaceException
1819
+	 * @throws ReflectionException
1820
+	 */
1821
+	public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1822
+	{
1823
+		$copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1824
+		foreach ($line_item->children() as $child_li) {
1825
+			$copy_li->add_child_line_item(
1826
+				EEH_Line_Item::billable_line_item_tree($child_li, $registrations)
1827
+			);
1828
+		}
1829
+		// if this is the grand total line item, make sure the totals all add up
1830
+		// (we could have duplicated this logic AS we copied the line items, but
1831
+		// it seems DRYer this way)
1832
+		if ($copy_li->type() === EEM_Line_Item::type_total) {
1833
+			$copy_li->recalculate_total_including_taxes();
1834
+		}
1835
+		return $copy_li;
1836
+	}
1837
+
1838
+
1839
+	/**
1840
+	 * Creates a new, unsaved line item from $line_item that factors in the
1841
+	 * number of billable registrations on $registrations.
1842
+	 *
1843
+	 * @param EE_Line_Item      $line_item
1844
+	 * @param EE_Registration[] $registrations
1845
+	 * @return EE_Line_Item
1846
+	 * @throws EE_Error
1847
+	 * @throws InvalidArgumentException
1848
+	 * @throws InvalidDataTypeException
1849
+	 * @throws InvalidInterfaceException
1850
+	 * @throws ReflectionException
1851
+	 */
1852
+	public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1853
+	{
1854
+		$new_li_fields = $line_item->model_field_array();
1855
+		if (
1856
+			$line_item->type() === EEM_Line_Item::type_line_item &&
1857
+			$line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1858
+		) {
1859
+			$count = 0;
1860
+			foreach ($registrations as $registration) {
1861
+				if (
1862
+					$line_item->OBJ_ID() === $registration->ticket_ID() &&
1863
+					in_array(
1864
+						$registration->status_ID(),
1865
+						EEM_Registration::reg_statuses_that_allow_payment(),
1866
+						true
1867
+					)
1868
+				) {
1869
+					$count++;
1870
+				}
1871
+			}
1872
+			$new_li_fields['LIN_quantity'] = $count;
1873
+		}
1874
+		// don't set the total. We'll leave that up to the code that calculates it
1875
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1876
+		return EE_Line_Item::new_instance($new_li_fields);
1877
+	}
1878
+
1879
+
1880
+	/**
1881
+	 * Returns a modified line item tree where all the subtotals which have a total of 0
1882
+	 * are removed, and line items with a quantity of 0
1883
+	 *
1884
+	 * @param EE_Line_Item $line_item |null
1885
+	 * @return EE_Line_Item|null
1886
+	 * @throws EE_Error
1887
+	 * @throws InvalidArgumentException
1888
+	 * @throws InvalidDataTypeException
1889
+	 * @throws InvalidInterfaceException
1890
+	 * @throws ReflectionException
1891
+	 */
1892
+	public static function non_empty_line_items(EE_Line_Item $line_item)
1893
+	{
1894
+		$copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1895
+		if ($copied_li === null) {
1896
+			return null;
1897
+		}
1898
+		// if this is an event subtotal, we want to only include it if it
1899
+		// has a non-zero total and at least one ticket line item child
1900
+		$ticket_children = 0;
1901
+		foreach ($line_item->children() as $child_li) {
1902
+			$child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1903
+			if ($child_li_copy !== null) {
1904
+				$copied_li->add_child_line_item($child_li_copy);
1905
+				if (
1906
+					$child_li_copy->type() === EEM_Line_Item::type_line_item &&
1907
+					$child_li_copy->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1908
+				) {
1909
+					$ticket_children++;
1910
+				}
1911
+			}
1912
+		}
1913
+		// if this is an event subtotal with NO ticket children
1914
+		// we basically want to ignore it
1915
+		if (
1916
+			$ticket_children === 0
1917
+			&& $line_item->type() === EEM_Line_Item::type_sub_total
1918
+			&& $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
1919
+			&& $line_item->total() === 0
1920
+		) {
1921
+			return null;
1922
+		}
1923
+		return $copied_li;
1924
+	}
1925
+
1926
+
1927
+	/**
1928
+	 * Creates a new, unsaved line item, but if it's a ticket line item
1929
+	 * with a total of 0, or a subtotal of 0, returns null instead
1930
+	 *
1931
+	 * @param EE_Line_Item $line_item
1932
+	 * @return EE_Line_Item
1933
+	 * @throws EE_Error
1934
+	 * @throws InvalidArgumentException
1935
+	 * @throws InvalidDataTypeException
1936
+	 * @throws InvalidInterfaceException
1937
+	 * @throws ReflectionException
1938
+	 */
1939
+	public static function non_empty_line_item(EE_Line_Item $line_item)
1940
+	{
1941
+		if (
1942
+			$line_item->type() === EEM_Line_Item::type_line_item
1943
+			&& $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1944
+			&& $line_item->quantity() === 0
1945
+		) {
1946
+			return null;
1947
+		}
1948
+		$new_li_fields = $line_item->model_field_array();
1949
+		// don't set the total. We'll leave that up to the code that calculates it
1950
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1951
+		return EE_Line_Item::new_instance($new_li_fields);
1952
+	}
1953
+
1954
+
1955
+	/**
1956
+	 * Cycles through all of the ticket line items for the supplied total line item
1957
+	 * and ensures that the line item's "is_taxable" field matches that of its corresponding ticket
1958
+	 *
1959
+	 * @param EE_Line_Item $total_line_item
1960
+	 * @throws EE_Error
1961
+	 * @throws InvalidArgumentException
1962
+	 * @throws InvalidDataTypeException
1963
+	 * @throws InvalidInterfaceException
1964
+	 * @throws ReflectionException
1965
+	 * @since 4.9.79.p
1966
+	 */
1967
+	public static function resetIsTaxableForTickets(EE_Line_Item $total_line_item)
1968
+	{
1969
+		$ticket_line_items = self::get_ticket_line_items($total_line_item);
1970
+		foreach ($ticket_line_items as $ticket_line_item) {
1971
+			if (
1972
+				$ticket_line_item instanceof EE_Line_Item
1973
+				&& $ticket_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1974
+			) {
1975
+				$ticket = $ticket_line_item->ticket();
1976
+				if ($ticket instanceof EE_Ticket && $ticket->taxable() !== $ticket_line_item->is_taxable()) {
1977
+					$ticket_line_item->set_is_taxable($ticket->taxable());
1978
+					$ticket_line_item->save();
1979
+				}
1980
+			}
1981
+		}
1982
+	}
1983
+
1984
+
1985
+
1986
+	/**************************************** @DEPRECATED METHODS *************************************** */
1987
+	/**
1988
+	 * @param EE_Line_Item $total_line_item
1989
+	 * @return EE_Line_Item
1990
+	 * @throws EE_Error
1991
+	 * @throws InvalidArgumentException
1992
+	 * @throws InvalidDataTypeException
1993
+	 * @throws InvalidInterfaceException
1994
+	 * @throws ReflectionException
1995
+	 * @deprecated
1996
+	 */
1997
+	public static function get_items_subtotal(EE_Line_Item $total_line_item)
1998
+	{
1999
+		EE_Error::doing_it_wrong(
2000
+			'EEH_Line_Item::get_items_subtotal()',
2001
+			sprintf(
2002
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2003
+				'EEH_Line_Item::get_pre_tax_subtotal()'
2004
+			),
2005
+			'4.6.0'
2006
+		);
2007
+		return self::get_pre_tax_subtotal($total_line_item);
2008
+	}
2009
+
2010
+
2011
+	/**
2012
+	 * @param EE_Transaction $transaction
2013
+	 * @return EE_Line_Item
2014
+	 * @throws EE_Error
2015
+	 * @throws InvalidArgumentException
2016
+	 * @throws InvalidDataTypeException
2017
+	 * @throws InvalidInterfaceException
2018
+	 * @throws ReflectionException
2019
+	 * @deprecated
2020
+	 */
2021
+	public static function create_default_total_line_item($transaction = null)
2022
+	{
2023
+		EE_Error::doing_it_wrong(
2024
+			'EEH_Line_Item::create_default_total_line_item()',
2025
+			sprintf(
2026
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2027
+				'EEH_Line_Item::create_total_line_item()'
2028
+			),
2029
+			'4.6.0'
2030
+		);
2031
+		return self::create_total_line_item($transaction);
2032
+	}
2033
+
2034
+
2035
+	/**
2036
+	 * @param EE_Line_Item   $total_line_item
2037
+	 * @param EE_Transaction $transaction
2038
+	 * @return EE_Line_Item
2039
+	 * @throws EE_Error
2040
+	 * @throws InvalidArgumentException
2041
+	 * @throws InvalidDataTypeException
2042
+	 * @throws InvalidInterfaceException
2043
+	 * @throws ReflectionException
2044
+	 * @deprecated
2045
+	 */
2046
+	public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2047
+	{
2048
+		EE_Error::doing_it_wrong(
2049
+			'EEH_Line_Item::create_default_tickets_subtotal()',
2050
+			sprintf(
2051
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2052
+				'EEH_Line_Item::create_pre_tax_subtotal()'
2053
+			),
2054
+			'4.6.0'
2055
+		);
2056
+		return self::create_pre_tax_subtotal($total_line_item, $transaction);
2057
+	}
2058
+
2059
+
2060
+	/**
2061
+	 * @param EE_Line_Item   $total_line_item
2062
+	 * @param EE_Transaction $transaction
2063
+	 * @return EE_Line_Item
2064
+	 * @throws EE_Error
2065
+	 * @throws InvalidArgumentException
2066
+	 * @throws InvalidDataTypeException
2067
+	 * @throws InvalidInterfaceException
2068
+	 * @throws ReflectionException
2069
+	 * @deprecated
2070
+	 */
2071
+	public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2072
+	{
2073
+		EE_Error::doing_it_wrong(
2074
+			'EEH_Line_Item::create_default_taxes_subtotal()',
2075
+			sprintf(
2076
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2077
+				'EEH_Line_Item::create_taxes_subtotal()'
2078
+			),
2079
+			'4.6.0'
2080
+		);
2081
+		return self::create_taxes_subtotal($total_line_item, $transaction);
2082
+	}
2083
+
2084
+
2085
+	/**
2086
+	 * @param EE_Line_Item   $total_line_item
2087
+	 * @param EE_Transaction $transaction
2088
+	 * @return EE_Line_Item
2089
+	 * @throws EE_Error
2090
+	 * @throws InvalidArgumentException
2091
+	 * @throws InvalidDataTypeException
2092
+	 * @throws InvalidInterfaceException
2093
+	 * @throws ReflectionException
2094
+	 * @deprecated
2095
+	 */
2096
+	public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2097
+	{
2098
+		EE_Error::doing_it_wrong(
2099
+			'EEH_Line_Item::create_default_event_subtotal()',
2100
+			sprintf(
2101
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2102
+				'EEH_Line_Item::create_event_subtotal()'
2103
+			),
2104
+			'4.6.0'
2105
+		);
2106
+		return self::create_event_subtotal($total_line_item, $transaction);
2107
+	}
2108 2108
 }
Please login to merge, or discard this patch.
core/helpers/EEH_Debug_Tools.helper.php 2 patches
Spacing   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -39,7 +39,7 @@  discard block
 block discarded – undo
39 39
     public static function instance()
40 40
     {
41 41
         // check if class object is instantiated, and instantiated properly
42
-        if (! self::$_instance instanceof EEH_Debug_Tools) {
42
+        if ( ! self::$_instance instanceof EEH_Debug_Tools) {
43 43
             self::$_instance = new self();
44 44
         }
45 45
         return self::$_instance;
@@ -56,14 +56,14 @@  discard block
 block discarded – undo
56 56
         if (
57 57
             defined('EE_LOAD_KINT')
58 58
             && ! class_exists('Kint')
59
-            && file_exists(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php')
59
+            && file_exists(EE_PLUGIN_DIR_PATH.'tests/kint/Kint.class.php')
60 60
         ) {
61 61
             // despite EE4 having a check for an existing copy of the Kint debugging class,
62 62
             // if another plugin was loaded AFTER EE4 and they did NOT perform a similar check,
63 63
             // then hilarity would ensue as PHP throws a "Cannot redeclare class Kint" error
64 64
             // so we've moved it to our test folder so that it is not included with production releases
65 65
             // plz use https://wordpress.org/plugins/kint-debugger/  if testing production versions of EE
66
-            require_once(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php');
66
+            require_once(EE_PLUGIN_DIR_PATH.'tests/kint/Kint.class.php');
67 67
         }
68 68
         $plugin = basename(EE_PLUGIN_DIR_PATH);
69 69
         add_action("activate_{$plugin}", array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
@@ -80,7 +80,7 @@  discard block
 block discarded – undo
80 80
      */
81 81
     public static function show_db_name()
82 82
     {
83
-        if (! defined('DOING_AJAX') && (defined('EE_ERROR_EMAILS') && EE_ERROR_EMAILS)) {
83
+        if ( ! defined('DOING_AJAX') && (defined('EE_ERROR_EMAILS') && EE_ERROR_EMAILS)) {
84 84
             echo '<p style="font-size:10px;font-weight:normal;color:#E76700;margin: 1em 2em; text-align: right;">DB_NAME: '
85 85
                  . DB_NAME
86 86
                  . '</p>';
@@ -130,12 +130,12 @@  discard block
 block discarded – undo
130 130
         global $wp_filter;
131 131
         echo '<br/><br/><br/><h3>Hooked Functions</h3>';
132 132
         if ($tag) {
133
-            $hook[ $tag ] = $wp_filter[ $tag ];
134
-            if (! is_array($hook[ $tag ])) {
133
+            $hook[$tag] = $wp_filter[$tag];
134
+            if ( ! is_array($hook[$tag])) {
135 135
                 trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
136 136
                 return;
137 137
             }
138
-            echo '<h5>For Tag: ' . esc_html($tag) . '</h5>';
138
+            echo '<h5>For Tag: '.esc_html($tag).'</h5>';
139 139
         } else {
140 140
             $hook = is_array($wp_filter) ? $wp_filter : array($wp_filter);
141 141
             ksort($hook);
@@ -165,12 +165,12 @@  discard block
 block discarded – undo
165 165
     {
166 166
         $filters = array();
167 167
         global $wp_filter;
168
-        if (isset($wp_filter[ $hook_name ])) {
169
-            $filters[ $hook_name ] = array();
170
-            foreach ($wp_filter[ $hook_name ] as $priority => $callbacks) {
171
-                $filters[ $hook_name ][ $priority ] = array();
168
+        if (isset($wp_filter[$hook_name])) {
169
+            $filters[$hook_name] = array();
170
+            foreach ($wp_filter[$hook_name] as $priority => $callbacks) {
171
+                $filters[$hook_name][$priority] = array();
172 172
                 foreach ($callbacks as $callback) {
173
-                    $filters[ $hook_name ][ $priority ][] = $callback['function'];
173
+                    $filters[$hook_name][$priority][] = $callback['function'];
174 174
                 }
175 175
             }
176 176
         }
@@ -189,17 +189,17 @@  discard block
 block discarded – undo
189 189
     {
190 190
         if (WP_DEBUG) {
191 191
             $activation_errors = ob_get_contents();
192
-            if (! empty($activation_errors)) {
193
-                $activation_errors = date('Y-m-d H:i:s') . "\n" . $activation_errors;
192
+            if ( ! empty($activation_errors)) {
193
+                $activation_errors = date('Y-m-d H:i:s')."\n".$activation_errors;
194 194
             }
195
-            espresso_load_required('EEH_File', EE_HELPERS . 'EEH_File.helper.php');
195
+            espresso_load_required('EEH_File', EE_HELPERS.'EEH_File.helper.php');
196 196
             if (class_exists('EEH_File')) {
197 197
                 try {
198 198
                     EEH_File::ensure_file_exists_and_is_writable(
199
-                        EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html'
199
+                        EVENT_ESPRESSO_UPLOAD_DIR.'logs/espresso_plugin_activation_errors.html'
200 200
                     );
201 201
                     EEH_File::write_to_file(
202
-                        EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
202
+                        EVENT_ESPRESSO_UPLOAD_DIR.'logs/espresso_plugin_activation_errors.html',
203 203
                         $activation_errors
204 204
                     );
205 205
                 } catch (EE_Error $e) {
@@ -219,11 +219,11 @@  discard block
 block discarded – undo
219 219
             } else {
220 220
                 // old school attempt
221 221
                 file_put_contents(
222
-                    EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
222
+                    EVENT_ESPRESSO_UPLOAD_DIR.'logs/espresso_plugin_activation_errors.html',
223 223
                     $activation_errors
224 224
                 );
225 225
             }
226
-            $activation_errors = get_option('ee_plugin_activation_errors', '') . $activation_errors;
226
+            $activation_errors = get_option('ee_plugin_activation_errors', '').$activation_errors;
227 227
             update_option('ee_plugin_activation_errors', $activation_errors);
228 228
         }
229 229
     }
@@ -283,7 +283,7 @@  discard block
 block discarded – undo
283 283
         // don't trigger error if doing ajax,
284 284
         // instead we'll add a transient EE_Error notice that in theory should show on the next request.
285 285
         if (defined('DOING_AJAX') && DOING_AJAX) {
286
-            $error_message .= ' ' . esc_html__(
286
+            $error_message .= ' '.esc_html__(
287 287
                 'This is a doing_it_wrong message that was triggered during an ajax request.  The request params on this request were: ',
288 288
                 'event_espresso'
289 289
             );
@@ -326,18 +326,18 @@  discard block
 block discarded – undo
326 326
         $debug_key = 'EE_DEBUG_SPCO'
327 327
     ) {
328 328
         if (WP_DEBUG) {
329
-            $debug_key = $debug_key . '_' . EE_Session::instance()->id();
329
+            $debug_key = $debug_key.'_'.EE_Session::instance()->id();
330 330
             $debug_data = get_option($debug_key, array());
331 331
             $default_data = array(
332
-                $class => $func . '() : ' . $line,
332
+                $class => $func.'() : '.$line,
333 333
             );
334 334
             // don't serialize objects
335 335
             $info = self::strip_objects($info);
336 336
             $index = ! empty($debug_index) ? $debug_index : 0;
337
-            if (! isset($debug_data[ $index ])) {
338
-                $debug_data[ $index ] = array();
337
+            if ( ! isset($debug_data[$index])) {
338
+                $debug_data[$index] = array();
339 339
             }
340
-            $debug_data[ $index ][ microtime() ] = array_merge($default_data, $info);
340
+            $debug_data[$index][microtime()] = array_merge($default_data, $info);
341 341
             update_option($debug_key, $debug_data);
342 342
         }
343 343
     }
@@ -354,20 +354,20 @@  discard block
 block discarded – undo
354 354
     {
355 355
         foreach ($info as $key => $value) {
356 356
             if (is_array($value)) {
357
-                $info[ $key ] = self::strip_objects($value);
357
+                $info[$key] = self::strip_objects($value);
358 358
             } elseif (is_object($value)) {
359 359
                 $object_class = get_class($value);
360
-                $info[ $object_class ] = array();
361
-                $info[ $object_class ]['ID'] = method_exists($value, 'ID') ? $value->ID() : spl_object_hash($value);
360
+                $info[$object_class] = array();
361
+                $info[$object_class]['ID'] = method_exists($value, 'ID') ? $value->ID() : spl_object_hash($value);
362 362
                 if (method_exists($value, 'ID')) {
363
-                    $info[ $object_class ]['ID'] = $value->ID();
363
+                    $info[$object_class]['ID'] = $value->ID();
364 364
                 }
365 365
                 if (method_exists($value, 'status')) {
366
-                    $info[ $object_class ]['status'] = $value->status();
366
+                    $info[$object_class]['status'] = $value->status();
367 367
                 } elseif (method_exists($value, 'status_ID')) {
368
-                    $info[ $object_class ]['status'] = $value->status_ID();
368
+                    $info[$object_class]['status'] = $value->status_ID();
369 369
                 }
370
-                unset($info[ $key ]);
370
+                unset($info[$key]);
371 371
             }
372 372
         }
373 373
         return (array) $info;
@@ -401,8 +401,8 @@  discard block
 block discarded – undo
401 401
         $result = EEH_Debug_Tools::headingSpacer($heading_tag);
402 402
         $result .= EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
403 403
         $result .= $is_method
404
-            ? EEH_Debug_Tools::grey_span('::') . EEH_Debug_Tools::orange_span($var . '()')
405
-            : EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span($var);
404
+            ? EEH_Debug_Tools::grey_span('::').EEH_Debug_Tools::orange_span($var.'()')
405
+            : EEH_Debug_Tools::grey_span(' : ').EEH_Debug_Tools::orange_span($var);
406 406
         $result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
407 407
         $result .= EEH_Debug_Tools::headingX($heading_tag);
408 408
         if ($die) {
@@ -457,11 +457,11 @@  discard block
 block discarded – undo
457 457
             if ($heading_tag === 'h1' || $heading_tag === 'h2') {
458 458
                 $heading .= self::lineBreak();
459 459
             }
460
-            $heading .= self::lineBreak() . "{$line}) {$var_name}";
460
+            $heading .= self::lineBreak()."{$line}) {$var_name}";
461 461
             return $heading;
462 462
         }
463 463
         $margin = "25px 0 0 {$margin}";
464
-        return '<' . $heading_tag . ' style="color:#2EA2CC; margin:' . $margin . ';"><b>' . $var_name . '</b>';
464
+        return '<'.$heading_tag.' style="color:#2EA2CC; margin:'.$margin.';"><b>'.$var_name.'</b>';
465 465
     }
466 466
 
467 467
 
@@ -475,7 +475,7 @@  discard block
 block discarded – undo
475 475
         if (EEH_Debug_Tools::plainOutput()) {
476 476
             return '';
477 477
         }
478
-        return '</' . $heading_tag . '>';
478
+        return '</'.$heading_tag.'>';
479 479
     }
480 480
 
481 481
 
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
         if (EEH_Debug_Tools::plainOutput()) {
490 490
             return $content;
491 491
         }
492
-        return '<span style="color:#999">' . $content . '</span>';
492
+        return '<span style="color:#999">'.$content.'</span>';
493 493
     }
494 494
 
495 495
 
@@ -529,7 +529,7 @@  discard block
 block discarded – undo
529 529
         if (EEH_Debug_Tools::plainOutput()) {
530 530
             return $content;
531 531
         }
532
-        return '<span style="color:#E76700">' . $content . '</span>';
532
+        return '<span style="color:#E76700">'.$content.'</span>';
533 533
     }
534 534
 
535 535
 
@@ -546,8 +546,8 @@  discard block
 block discarded – undo
546 546
         if (EEH_Debug_Tools::plainOutput()) {
547 547
             return str_replace("\n", '', $var);
548 548
         }
549
-        return '<pre style="background: #fff; color:#999; display: inline-block; margin: 0 1em; padding: 0;">' .
550
-               $var . '</pre>';
549
+        return '<pre style="background: #fff; color:#999; display: inline-block; margin: 0 1em; padding: 0;">'.
550
+               $var.'</pre>';
551 551
     }
552 552
 
553 553
 
@@ -588,7 +588,7 @@  discard block
 block discarded – undo
588 588
         $heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
589 589
         $result = EEH_Debug_Tools::headingSpacer($heading_tag);
590 590
         $result .= EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
591
-        $result .= EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span(
591
+        $result .= EEH_Debug_Tools::grey_span(' : ').EEH_Debug_Tools::orange_span(
592 592
             EEH_Debug_Tools::pre_span($var)
593 593
         );
594 594
         $result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
Please login to merge, or discard this patch.
Indentation   +683 added lines, -683 removed lines patch added patch discarded remove patch
@@ -15,674 +15,674 @@  discard block
 block discarded – undo
15 15
  */
16 16
 class EEH_Debug_Tools
17 17
 {
18
-    /**
19
-     *    instance of the EEH_Autoloader object
20
-     *
21
-     * @var    $_instance
22
-     * @access    private
23
-     */
24
-    private static $_instance;
25
-
26
-    /**
27
-     * @var array
28
-     */
29
-    protected $_memory_usage_points = array();
30
-
31
-
32
-
33
-    /**
34
-     * @singleton method used to instantiate class object
35
-     * @access    public
36
-     * @return EEH_Debug_Tools
37
-     */
38
-    public static function instance()
39
-    {
40
-        // check if class object is instantiated, and instantiated properly
41
-        if (! self::$_instance instanceof EEH_Debug_Tools) {
42
-            self::$_instance = new self();
43
-        }
44
-        return self::$_instance;
45
-    }
46
-
47
-
48
-
49
-    /**
50
-     * private class constructor
51
-     */
52
-    private function __construct()
53
-    {
54
-        // load Kint PHP debugging library
55
-        if (
56
-            defined('EE_LOAD_KINT')
57
-            && ! class_exists('Kint')
58
-            && file_exists(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php')
59
-        ) {
60
-            // despite EE4 having a check for an existing copy of the Kint debugging class,
61
-            // if another plugin was loaded AFTER EE4 and they did NOT perform a similar check,
62
-            // then hilarity would ensue as PHP throws a "Cannot redeclare class Kint" error
63
-            // so we've moved it to our test folder so that it is not included with production releases
64
-            // plz use https://wordpress.org/plugins/kint-debugger/  if testing production versions of EE
65
-            require_once(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php');
66
-        }
67
-        $plugin = basename(EE_PLUGIN_DIR_PATH);
68
-        add_action("activate_{$plugin}", array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
69
-        add_action('activated_plugin', array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
70
-        add_action('shutdown', array('EEH_Debug_Tools', 'show_db_name'));
71
-    }
72
-
73
-
74
-
75
-    /**
76
-     *    show_db_name
77
-     *
78
-     * @return void
79
-     */
80
-    public static function show_db_name()
81
-    {
82
-        if (! defined('DOING_AJAX') && (defined('EE_ERROR_EMAILS') && EE_ERROR_EMAILS)) {
83
-            echo '<p style="font-size:10px;font-weight:normal;color:#E76700;margin: 1em 2em; text-align: right;">DB_NAME: '
84
-                 . DB_NAME
85
-                 . '</p>';
86
-        }
87
-        if (EE_DEBUG) {
88
-            Benchmark::displayResults();
89
-        }
90
-    }
91
-
92
-
93
-
94
-    /**
95
-     *    dump EE_Session object at bottom of page after everything else has happened
96
-     *
97
-     * @return void
98
-     */
99
-    public function espresso_session_footer_dump()
100
-    {
101
-        if (
102
-            (defined('WP_DEBUG') && WP_DEBUG)
103
-            && ! defined('DOING_AJAX')
104
-            && class_exists('Kint')
105
-            && function_exists('wp_get_current_user')
106
-            && current_user_can('update_core')
107
-            && class_exists('EE_Registry')
108
-        ) {
109
-            Kint::dump(EE_Registry::instance()->SSN->id());
110
-            Kint::dump(EE_Registry::instance()->SSN);
111
-            //          Kint::dump( EE_Registry::instance()->SSN->get_session_data('cart')->get_tickets() );
112
-            $this->espresso_list_hooked_functions();
113
-            Benchmark::displayResults();
114
-        }
115
-    }
116
-
117
-
118
-
119
-    /**
120
-     *    List All Hooked Functions
121
-     *    to list all functions for a specific hook, add ee_list_hooks={hook-name} to URL
122
-     *    http://wp.smashingmagazine.com/2009/08/18/10-useful-wordpress-hook-hacks/
123
-     *
124
-     * @param string $tag
125
-     * @return void
126
-     */
127
-    public function espresso_list_hooked_functions($tag = '')
128
-    {
129
-        global $wp_filter;
130
-        echo '<br/><br/><br/><h3>Hooked Functions</h3>';
131
-        if ($tag) {
132
-            $hook[ $tag ] = $wp_filter[ $tag ];
133
-            if (! is_array($hook[ $tag ])) {
134
-                trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
135
-                return;
136
-            }
137
-            echo '<h5>For Tag: ' . esc_html($tag) . '</h5>';
138
-        } else {
139
-            $hook = is_array($wp_filter) ? $wp_filter : array($wp_filter);
140
-            ksort($hook);
141
-        }
142
-        foreach ($hook as $tag_name => $priorities) {
143
-            echo "<br />&gt;&gt;&gt;&gt;&gt;\t<strong>esc_html($tag_name)</strong><br />";
144
-            ksort($priorities);
145
-            foreach ($priorities as $priority => $function) {
146
-                echo esc_html($priority);
147
-                foreach ($function as $name => $properties) {
148
-                    $name = esc_html($name);
149
-                    echo "\t$name<br />";
150
-                }
151
-            }
152
-        }
153
-    }
154
-
155
-
156
-
157
-    /**
158
-     *    registered_filter_callbacks
159
-     *
160
-     * @param string $hook_name
161
-     * @return array
162
-     */
163
-    public static function registered_filter_callbacks($hook_name = '')
164
-    {
165
-        $filters = array();
166
-        global $wp_filter;
167
-        if (isset($wp_filter[ $hook_name ])) {
168
-            $filters[ $hook_name ] = array();
169
-            foreach ($wp_filter[ $hook_name ] as $priority => $callbacks) {
170
-                $filters[ $hook_name ][ $priority ] = array();
171
-                foreach ($callbacks as $callback) {
172
-                    $filters[ $hook_name ][ $priority ][] = $callback['function'];
173
-                }
174
-            }
175
-        }
176
-        return $filters;
177
-    }
178
-
179
-
180
-
181
-    /**
182
-     *    captures plugin activation errors for debugging
183
-     *
184
-     * @return void
185
-     * @throws EE_Error
186
-     */
187
-    public static function ee_plugin_activation_errors()
188
-    {
189
-        if (WP_DEBUG) {
190
-            $activation_errors = ob_get_contents();
191
-            if (! empty($activation_errors)) {
192
-                $activation_errors = date('Y-m-d H:i:s') . "\n" . $activation_errors;
193
-            }
194
-            espresso_load_required('EEH_File', EE_HELPERS . 'EEH_File.helper.php');
195
-            if (class_exists('EEH_File')) {
196
-                try {
197
-                    EEH_File::ensure_file_exists_and_is_writable(
198
-                        EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html'
199
-                    );
200
-                    EEH_File::write_to_file(
201
-                        EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
202
-                        $activation_errors
203
-                    );
204
-                } catch (EE_Error $e) {
205
-                    EE_Error::add_error(
206
-                        sprintf(
207
-                            esc_html__(
208
-                                'The Event Espresso activation errors file could not be setup because: %s',
209
-                                'event_espresso'
210
-                            ),
211
-                            $e->getMessage()
212
-                        ),
213
-                        __FILE__,
214
-                        __FUNCTION__,
215
-                        __LINE__
216
-                    );
217
-                }
218
-            } else {
219
-                // old school attempt
220
-                file_put_contents(
221
-                    EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
222
-                    $activation_errors
223
-                );
224
-            }
225
-            $activation_errors = get_option('ee_plugin_activation_errors', '') . $activation_errors;
226
-            update_option('ee_plugin_activation_errors', $activation_errors);
227
-        }
228
-    }
229
-
230
-
231
-
232
-    /**
233
-     * This basically mimics the WordPress _doing_it_wrong() function except adds our own messaging etc.
234
-     * Very useful for providing helpful messages to developers when the method of doing something has been deprecated,
235
-     * or we want to make sure they use something the right way.
236
-     *
237
-     * @access public
238
-     * @param string $function      The function that was called
239
-     * @param string $message       A message explaining what has been done incorrectly
240
-     * @param string $version       The version of Event Espresso where the error was added
241
-     * @param string $applies_when  a version string for when you want the doing_it_wrong notice to begin appearing
242
-     *                              for a deprecated function. This allows deprecation to occur during one version,
243
-     *                              but not have any notices appear until a later version. This allows developers
244
-     *                              extra time to update their code before notices appear.
245
-     * @param int    $error_type
246
-     * @uses   trigger_error()
247
-     */
248
-    public function doing_it_wrong(
249
-        $function,
250
-        $message,
251
-        $version,
252
-        $applies_when = '',
253
-        $error_type = null
254
-    ) {
255
-        $applies_when = ! empty($applies_when) ? $applies_when : espresso_version();
256
-        $error_type = $error_type !== null ? $error_type : E_USER_NOTICE;
257
-        // because we swapped the parameter order around for the last two params,
258
-        // let's verify that some third party isn't still passing an error type value for the third param
259
-        if (is_int($applies_when)) {
260
-            $error_type = $applies_when;
261
-            $applies_when = espresso_version();
262
-        }
263
-        // if not displaying notices yet, then just leave
264
-        if (version_compare(espresso_version(), $applies_when, '<')) {
265
-            return;
266
-        }
267
-        do_action('AHEE__EEH_Debug_Tools__doing_it_wrong_run', $function, $message, $version);
268
-        $version = $version === null
269
-            ? ''
270
-            : sprintf(
271
-                esc_html__('(This message was added in version %s of Event Espresso)', 'event_espresso'),
272
-                $version
273
-            );
274
-        $error_message = sprintf(
275
-            esc_html__('%1$s was called %2$sincorrectly%3$s. %4$s %5$s', 'event_espresso'),
276
-            $function,
277
-            '<strong>',
278
-            '</strong>',
279
-            $message,
280
-            $version
281
-        );
282
-        // don't trigger error if doing ajax,
283
-        // instead we'll add a transient EE_Error notice that in theory should show on the next request.
284
-        if (defined('DOING_AJAX') && DOING_AJAX) {
285
-            $error_message .= ' ' . esc_html__(
286
-                'This is a doing_it_wrong message that was triggered during an ajax request.  The request params on this request were: ',
287
-                'event_espresso'
288
-            );
289
-            $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
290
-            $error_message .= '<ul><li>';
291
-            $error_message .= implode('</li><li>', $request->requestParams());
292
-            $error_message .= '</ul>';
293
-            EE_Error::add_error($error_message, 'debug::doing_it_wrong', $function, '42');
294
-            // now we set this on the transient so it shows up on the next request.
295
-            EE_Error::get_notices(false, true);
296
-        } else {
297
-            trigger_error($error_message, $error_type);
298
-        }
299
-    }
300
-
301
-
302
-
303
-
304
-    /**
305
-     * Logger helpers
306
-     */
307
-    /**
308
-     * debug
309
-     *
310
-     * @param string $class
311
-     * @param string $func
312
-     * @param string $line
313
-     * @param array  $info
314
-     * @param bool   $display_request
315
-     * @param string $debug_index
316
-     * @param string $debug_key
317
-     */
318
-    public static function log(
319
-        $class = '',
320
-        $func = '',
321
-        $line = '',
322
-        $info = array(),
323
-        $display_request = false,
324
-        $debug_index = '',
325
-        $debug_key = 'EE_DEBUG_SPCO'
326
-    ) {
327
-        if (WP_DEBUG) {
328
-            $debug_key = $debug_key . '_' . EE_Session::instance()->id();
329
-            $debug_data = get_option($debug_key, array());
330
-            $default_data = array(
331
-                $class => $func . '() : ' . $line,
332
-            );
333
-            // don't serialize objects
334
-            $info = self::strip_objects($info);
335
-            $index = ! empty($debug_index) ? $debug_index : 0;
336
-            if (! isset($debug_data[ $index ])) {
337
-                $debug_data[ $index ] = array();
338
-            }
339
-            $debug_data[ $index ][ microtime() ] = array_merge($default_data, $info);
340
-            update_option($debug_key, $debug_data);
341
-        }
342
-    }
343
-
344
-
345
-
346
-    /**
347
-     * strip_objects
348
-     *
349
-     * @param array $info
350
-     * @return array
351
-     */
352
-    public static function strip_objects($info = array())
353
-    {
354
-        foreach ($info as $key => $value) {
355
-            if (is_array($value)) {
356
-                $info[ $key ] = self::strip_objects($value);
357
-            } elseif (is_object($value)) {
358
-                $object_class = get_class($value);
359
-                $info[ $object_class ] = array();
360
-                $info[ $object_class ]['ID'] = method_exists($value, 'ID') ? $value->ID() : spl_object_hash($value);
361
-                if (method_exists($value, 'ID')) {
362
-                    $info[ $object_class ]['ID'] = $value->ID();
363
-                }
364
-                if (method_exists($value, 'status')) {
365
-                    $info[ $object_class ]['status'] = $value->status();
366
-                } elseif (method_exists($value, 'status_ID')) {
367
-                    $info[ $object_class ]['status'] = $value->status_ID();
368
-                }
369
-                unset($info[ $key ]);
370
-            }
371
-        }
372
-        return (array) $info;
373
-    }
374
-
375
-
376
-
377
-    /**
378
-     * @param mixed      $var
379
-     * @param string     $var_name
380
-     * @param string     $file
381
-     * @param int|string $line
382
-     * @param int|string $heading_tag
383
-     * @param bool       $die
384
-     * @param string     $margin
385
-     */
386
-    public static function printv(
387
-        $var,
388
-        $var_name = '',
389
-        $file = '',
390
-        $line = '',
391
-        $heading_tag = 5,
392
-        $die = false,
393
-        $margin = ''
394
-    ) {
395
-        $var_name = ! $var_name ? 'string' : $var_name;
396
-        $var_name = ucwords(str_replace('$', '', $var_name));
397
-        $is_method = method_exists($var_name, $var);
398
-        $var_name = ucwords(str_replace('_', ' ', $var_name));
399
-        $heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
400
-        $result = EEH_Debug_Tools::headingSpacer($heading_tag);
401
-        $result .= EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
402
-        $result .= $is_method
403
-            ? EEH_Debug_Tools::grey_span('::') . EEH_Debug_Tools::orange_span($var . '()')
404
-            : EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span($var);
405
-        $result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
406
-        $result .= EEH_Debug_Tools::headingX($heading_tag);
407
-        if ($die) {
408
-            die($result);
409
-        }
410
-        echo wp_kses($result, AllowedTags::getWithFormTags());
411
-    }
412
-
413
-
414
-    protected static function headingTag($heading_tag)
415
-    {
416
-        $heading_tag = absint($heading_tag);
417
-        return $heading_tag > 0 && $heading_tag < 7 ? "h{$heading_tag}" : 'h5';
418
-    }
419
-
420
-    protected static function headingSpacer($heading_tag)
421
-    {
422
-        return EEH_Debug_Tools::plainOutput() && ($heading_tag === 'h1' || $heading_tag === 'h2')
423
-            ? self::lineBreak()
424
-            : '';
425
-    }
426
-
427
-
428
-    protected static function lineBreak()
429
-    {
430
-        return defined('DOING_AJAX') && DOING_AJAX ? '<br />' : "\n";
431
-    }
432
-
433
-
434
-    protected static function plainOutput()
435
-    {
436
-        return defined('EE_TESTS_DIR')
437
-               || (defined('DOING_AJAX') && DOING_AJAX)
438
-               || (
439
-                   isset($_SERVER['REQUEST_URI'])
440
-                   && strpos(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), 'wp-json') !== false
441
-               );
442
-    }
443
-
444
-
445
-    /**
446
-     * @param string $var_name
447
-     * @param string $heading_tag
448
-     * @param string $margin
449
-     * @param int    $line
450
-     * @return string
451
-     */
452
-    protected static function heading($var_name = '', $heading_tag = 'h5', $margin = '', $line = 0)
453
-    {
454
-        if (EEH_Debug_Tools::plainOutput()) {
455
-            $heading = '';
456
-            if ($heading_tag === 'h1' || $heading_tag === 'h2') {
457
-                $heading .= self::lineBreak();
458
-            }
459
-            $heading .= self::lineBreak() . "{$line}) {$var_name}";
460
-            return $heading;
461
-        }
462
-        $margin = "25px 0 0 {$margin}";
463
-        return '<' . $heading_tag . ' style="color:#2EA2CC; margin:' . $margin . ';"><b>' . $var_name . '</b>';
464
-    }
465
-
466
-
467
-
468
-    /**
469
-     * @param string $heading_tag
470
-     * @return string
471
-     */
472
-    protected static function headingX($heading_tag = 'h5')
473
-    {
474
-        if (EEH_Debug_Tools::plainOutput()) {
475
-            return '';
476
-        }
477
-        return '</' . $heading_tag . '>';
478
-    }
479
-
480
-
481
-
482
-    /**
483
-     * @param string $content
484
-     * @return string
485
-     */
486
-    protected static function grey_span($content = '')
487
-    {
488
-        if (EEH_Debug_Tools::plainOutput()) {
489
-            return $content;
490
-        }
491
-        return '<span style="color:#999">' . $content . '</span>';
492
-    }
493
-
494
-
495
-
496
-    /**
497
-     * @param string $file
498
-     * @param int    $line
499
-     * @return string
500
-     */
501
-    protected static function file_and_line($file, $line, $heading_tag)
502
-    {
503
-        if ($file === '' || $line === '') {
504
-            return '';
505
-        }
506
-        $file = str_replace(EE_PLUGIN_DIR_PATH, '/', $file);
507
-        if (EEH_Debug_Tools::plainOutput()) {
508
-            if ($heading_tag === 'h1' || $heading_tag === 'h2') {
509
-                return " ({$file})";
510
-            }
511
-            return '';
512
-        }
513
-        return '<br /><span style="font-size:9px;font-weight:normal;color:#666;line-height: 12px;">'
514
-               . $file
515
-               . '<br />line no: '
516
-               . $line
517
-               . '</span>';
518
-    }
519
-
520
-
521
-
522
-    /**
523
-     * @param string $content
524
-     * @return string
525
-     */
526
-    protected static function orange_span($content = '')
527
-    {
528
-        if (EEH_Debug_Tools::plainOutput()) {
529
-            return $content;
530
-        }
531
-        return '<span style="color:#E76700">' . $content . '</span>';
532
-    }
533
-
534
-
535
-
536
-    /**
537
-     * @param mixed $var
538
-     * @return string
539
-     */
540
-    protected static function pre_span($var)
541
-    {
542
-        ob_start();
543
-        var_dump($var);
544
-        $var = ob_get_clean();
545
-        if (EEH_Debug_Tools::plainOutput()) {
546
-            return str_replace("\n", '', $var);
547
-        }
548
-        return '<pre style="background: #fff; color:#999; display: inline-block; margin: 0 1em; padding: 0;">' .
549
-               $var . '</pre>';
550
-    }
551
-
552
-
553
-
554
-    /**
555
-     * @param mixed      $var
556
-     * @param string     $var_name
557
-     * @param string     $file
558
-     * @param int|string $line
559
-     * @param int|string $heading_tag
560
-     * @param bool       $die
561
-     */
562
-    public static function printr(
563
-        $var,
564
-        $var_name = '',
565
-        $file = '',
566
-        $line = '',
567
-        $heading_tag = 5,
568
-        $die = false
569
-    ) {
570
-        // return;
571
-        $file = str_replace(rtrim(ABSPATH, '\\/'), '', $file);
572
-        $margin = is_admin() ? ' 180px' : '0';
573
-        if (is_string($var)) {
574
-            EEH_Debug_Tools::printv($var, $var_name, $file, $line, $heading_tag, $die, $margin);
575
-            return;
576
-        }
577
-        if (is_object($var)) {
578
-            $var_name = ! $var_name ? 'object' : $var_name;
579
-        } elseif (is_array($var)) {
580
-            $var_name = ! $var_name ? 'array' : $var_name;
581
-        } elseif (is_numeric($var)) {
582
-            $var_name = ! $var_name ? 'numeric' : $var_name;
583
-        } elseif ($var === null) {
584
-            $var_name = ! $var_name ? 'null' : $var_name;
585
-        }
586
-        $var_name = ucwords(str_replace(array('$', '_'), array('', ' '), $var_name));
587
-        $heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
588
-        $result = EEH_Debug_Tools::headingSpacer($heading_tag);
589
-        $result .= EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
590
-        $result .= EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span(
591
-            EEH_Debug_Tools::pre_span($var)
592
-        );
593
-        $result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
594
-        $result .= EEH_Debug_Tools::headingX($heading_tag);
595
-        if ($die) {
596
-            die($result);
597
-        }
598
-        echo wp_kses($result, AllowedTags::getWithFormTags());
599
-    }
600
-
601
-
602
-
603
-    /******************** deprecated ********************/
604
-
605
-
606
-
607
-    /**
608
-     * @deprecated 4.9.39.rc.034
609
-     */
610
-    public function reset_times()
611
-    {
612
-        Benchmark::resetTimes();
613
-    }
614
-
615
-
616
-
617
-    /**
618
-     * @deprecated 4.9.39.rc.034
619
-     * @param null $timer_name
620
-     */
621
-    public function start_timer($timer_name = null)
622
-    {
623
-        Benchmark::startTimer($timer_name);
624
-    }
625
-
626
-
627
-
628
-    /**
629
-     * @deprecated 4.9.39.rc.034
630
-     * @param string $timer_name
631
-     */
632
-    public function stop_timer($timer_name = '')
633
-    {
634
-        Benchmark::stopTimer($timer_name);
635
-    }
636
-
637
-
638
-
639
-    /**
640
-     * @deprecated 4.9.39.rc.034
641
-     * @param string  $label      The label to show for this time eg "Start of calling Some_Class::some_function"
642
-     * @param boolean $output_now whether to echo now, or wait until EEH_Debug_Tools::show_times() is called
643
-     * @return void
644
-     */
645
-    public function measure_memory($label, $output_now = false)
646
-    {
647
-        Benchmark::measureMemory($label, $output_now);
648
-    }
649
-
650
-
651
-
652
-    /**
653
-     * @deprecated 4.9.39.rc.034
654
-     * @param int $size
655
-     * @return string
656
-     */
657
-    public function convert($size)
658
-    {
659
-        return Benchmark::convert($size);
660
-    }
661
-
662
-
663
-
664
-    /**
665
-     * @deprecated 4.9.39.rc.034
666
-     * @param bool $output_now
667
-     * @return string
668
-     */
669
-    public function show_times($output_now = true)
670
-    {
671
-        return Benchmark::displayResults($output_now);
672
-    }
673
-
674
-
675
-
676
-    /**
677
-     * @deprecated 4.9.39.rc.034
678
-     * @param string $timer_name
679
-     * @param float  $total_time
680
-     * @return string
681
-     */
682
-    public function format_time($timer_name, $total_time)
683
-    {
684
-        return Benchmark::formatTime($timer_name, $total_time);
685
-    }
18
+	/**
19
+	 *    instance of the EEH_Autoloader object
20
+	 *
21
+	 * @var    $_instance
22
+	 * @access    private
23
+	 */
24
+	private static $_instance;
25
+
26
+	/**
27
+	 * @var array
28
+	 */
29
+	protected $_memory_usage_points = array();
30
+
31
+
32
+
33
+	/**
34
+	 * @singleton method used to instantiate class object
35
+	 * @access    public
36
+	 * @return EEH_Debug_Tools
37
+	 */
38
+	public static function instance()
39
+	{
40
+		// check if class object is instantiated, and instantiated properly
41
+		if (! self::$_instance instanceof EEH_Debug_Tools) {
42
+			self::$_instance = new self();
43
+		}
44
+		return self::$_instance;
45
+	}
46
+
47
+
48
+
49
+	/**
50
+	 * private class constructor
51
+	 */
52
+	private function __construct()
53
+	{
54
+		// load Kint PHP debugging library
55
+		if (
56
+			defined('EE_LOAD_KINT')
57
+			&& ! class_exists('Kint')
58
+			&& file_exists(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php')
59
+		) {
60
+			// despite EE4 having a check for an existing copy of the Kint debugging class,
61
+			// if another plugin was loaded AFTER EE4 and they did NOT perform a similar check,
62
+			// then hilarity would ensue as PHP throws a "Cannot redeclare class Kint" error
63
+			// so we've moved it to our test folder so that it is not included with production releases
64
+			// plz use https://wordpress.org/plugins/kint-debugger/  if testing production versions of EE
65
+			require_once(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php');
66
+		}
67
+		$plugin = basename(EE_PLUGIN_DIR_PATH);
68
+		add_action("activate_{$plugin}", array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
69
+		add_action('activated_plugin', array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
70
+		add_action('shutdown', array('EEH_Debug_Tools', 'show_db_name'));
71
+	}
72
+
73
+
74
+
75
+	/**
76
+	 *    show_db_name
77
+	 *
78
+	 * @return void
79
+	 */
80
+	public static function show_db_name()
81
+	{
82
+		if (! defined('DOING_AJAX') && (defined('EE_ERROR_EMAILS') && EE_ERROR_EMAILS)) {
83
+			echo '<p style="font-size:10px;font-weight:normal;color:#E76700;margin: 1em 2em; text-align: right;">DB_NAME: '
84
+				 . DB_NAME
85
+				 . '</p>';
86
+		}
87
+		if (EE_DEBUG) {
88
+			Benchmark::displayResults();
89
+		}
90
+	}
91
+
92
+
93
+
94
+	/**
95
+	 *    dump EE_Session object at bottom of page after everything else has happened
96
+	 *
97
+	 * @return void
98
+	 */
99
+	public function espresso_session_footer_dump()
100
+	{
101
+		if (
102
+			(defined('WP_DEBUG') && WP_DEBUG)
103
+			&& ! defined('DOING_AJAX')
104
+			&& class_exists('Kint')
105
+			&& function_exists('wp_get_current_user')
106
+			&& current_user_can('update_core')
107
+			&& class_exists('EE_Registry')
108
+		) {
109
+			Kint::dump(EE_Registry::instance()->SSN->id());
110
+			Kint::dump(EE_Registry::instance()->SSN);
111
+			//          Kint::dump( EE_Registry::instance()->SSN->get_session_data('cart')->get_tickets() );
112
+			$this->espresso_list_hooked_functions();
113
+			Benchmark::displayResults();
114
+		}
115
+	}
116
+
117
+
118
+
119
+	/**
120
+	 *    List All Hooked Functions
121
+	 *    to list all functions for a specific hook, add ee_list_hooks={hook-name} to URL
122
+	 *    http://wp.smashingmagazine.com/2009/08/18/10-useful-wordpress-hook-hacks/
123
+	 *
124
+	 * @param string $tag
125
+	 * @return void
126
+	 */
127
+	public function espresso_list_hooked_functions($tag = '')
128
+	{
129
+		global $wp_filter;
130
+		echo '<br/><br/><br/><h3>Hooked Functions</h3>';
131
+		if ($tag) {
132
+			$hook[ $tag ] = $wp_filter[ $tag ];
133
+			if (! is_array($hook[ $tag ])) {
134
+				trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
135
+				return;
136
+			}
137
+			echo '<h5>For Tag: ' . esc_html($tag) . '</h5>';
138
+		} else {
139
+			$hook = is_array($wp_filter) ? $wp_filter : array($wp_filter);
140
+			ksort($hook);
141
+		}
142
+		foreach ($hook as $tag_name => $priorities) {
143
+			echo "<br />&gt;&gt;&gt;&gt;&gt;\t<strong>esc_html($tag_name)</strong><br />";
144
+			ksort($priorities);
145
+			foreach ($priorities as $priority => $function) {
146
+				echo esc_html($priority);
147
+				foreach ($function as $name => $properties) {
148
+					$name = esc_html($name);
149
+					echo "\t$name<br />";
150
+				}
151
+			}
152
+		}
153
+	}
154
+
155
+
156
+
157
+	/**
158
+	 *    registered_filter_callbacks
159
+	 *
160
+	 * @param string $hook_name
161
+	 * @return array
162
+	 */
163
+	public static function registered_filter_callbacks($hook_name = '')
164
+	{
165
+		$filters = array();
166
+		global $wp_filter;
167
+		if (isset($wp_filter[ $hook_name ])) {
168
+			$filters[ $hook_name ] = array();
169
+			foreach ($wp_filter[ $hook_name ] as $priority => $callbacks) {
170
+				$filters[ $hook_name ][ $priority ] = array();
171
+				foreach ($callbacks as $callback) {
172
+					$filters[ $hook_name ][ $priority ][] = $callback['function'];
173
+				}
174
+			}
175
+		}
176
+		return $filters;
177
+	}
178
+
179
+
180
+
181
+	/**
182
+	 *    captures plugin activation errors for debugging
183
+	 *
184
+	 * @return void
185
+	 * @throws EE_Error
186
+	 */
187
+	public static function ee_plugin_activation_errors()
188
+	{
189
+		if (WP_DEBUG) {
190
+			$activation_errors = ob_get_contents();
191
+			if (! empty($activation_errors)) {
192
+				$activation_errors = date('Y-m-d H:i:s') . "\n" . $activation_errors;
193
+			}
194
+			espresso_load_required('EEH_File', EE_HELPERS . 'EEH_File.helper.php');
195
+			if (class_exists('EEH_File')) {
196
+				try {
197
+					EEH_File::ensure_file_exists_and_is_writable(
198
+						EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html'
199
+					);
200
+					EEH_File::write_to_file(
201
+						EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
202
+						$activation_errors
203
+					);
204
+				} catch (EE_Error $e) {
205
+					EE_Error::add_error(
206
+						sprintf(
207
+							esc_html__(
208
+								'The Event Espresso activation errors file could not be setup because: %s',
209
+								'event_espresso'
210
+							),
211
+							$e->getMessage()
212
+						),
213
+						__FILE__,
214
+						__FUNCTION__,
215
+						__LINE__
216
+					);
217
+				}
218
+			} else {
219
+				// old school attempt
220
+				file_put_contents(
221
+					EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
222
+					$activation_errors
223
+				);
224
+			}
225
+			$activation_errors = get_option('ee_plugin_activation_errors', '') . $activation_errors;
226
+			update_option('ee_plugin_activation_errors', $activation_errors);
227
+		}
228
+	}
229
+
230
+
231
+
232
+	/**
233
+	 * This basically mimics the WordPress _doing_it_wrong() function except adds our own messaging etc.
234
+	 * Very useful for providing helpful messages to developers when the method of doing something has been deprecated,
235
+	 * or we want to make sure they use something the right way.
236
+	 *
237
+	 * @access public
238
+	 * @param string $function      The function that was called
239
+	 * @param string $message       A message explaining what has been done incorrectly
240
+	 * @param string $version       The version of Event Espresso where the error was added
241
+	 * @param string $applies_when  a version string for when you want the doing_it_wrong notice to begin appearing
242
+	 *                              for a deprecated function. This allows deprecation to occur during one version,
243
+	 *                              but not have any notices appear until a later version. This allows developers
244
+	 *                              extra time to update their code before notices appear.
245
+	 * @param int    $error_type
246
+	 * @uses   trigger_error()
247
+	 */
248
+	public function doing_it_wrong(
249
+		$function,
250
+		$message,
251
+		$version,
252
+		$applies_when = '',
253
+		$error_type = null
254
+	) {
255
+		$applies_when = ! empty($applies_when) ? $applies_when : espresso_version();
256
+		$error_type = $error_type !== null ? $error_type : E_USER_NOTICE;
257
+		// because we swapped the parameter order around for the last two params,
258
+		// let's verify that some third party isn't still passing an error type value for the third param
259
+		if (is_int($applies_when)) {
260
+			$error_type = $applies_when;
261
+			$applies_when = espresso_version();
262
+		}
263
+		// if not displaying notices yet, then just leave
264
+		if (version_compare(espresso_version(), $applies_when, '<')) {
265
+			return;
266
+		}
267
+		do_action('AHEE__EEH_Debug_Tools__doing_it_wrong_run', $function, $message, $version);
268
+		$version = $version === null
269
+			? ''
270
+			: sprintf(
271
+				esc_html__('(This message was added in version %s of Event Espresso)', 'event_espresso'),
272
+				$version
273
+			);
274
+		$error_message = sprintf(
275
+			esc_html__('%1$s was called %2$sincorrectly%3$s. %4$s %5$s', 'event_espresso'),
276
+			$function,
277
+			'<strong>',
278
+			'</strong>',
279
+			$message,
280
+			$version
281
+		);
282
+		// don't trigger error if doing ajax,
283
+		// instead we'll add a transient EE_Error notice that in theory should show on the next request.
284
+		if (defined('DOING_AJAX') && DOING_AJAX) {
285
+			$error_message .= ' ' . esc_html__(
286
+				'This is a doing_it_wrong message that was triggered during an ajax request.  The request params on this request were: ',
287
+				'event_espresso'
288
+			);
289
+			$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
290
+			$error_message .= '<ul><li>';
291
+			$error_message .= implode('</li><li>', $request->requestParams());
292
+			$error_message .= '</ul>';
293
+			EE_Error::add_error($error_message, 'debug::doing_it_wrong', $function, '42');
294
+			// now we set this on the transient so it shows up on the next request.
295
+			EE_Error::get_notices(false, true);
296
+		} else {
297
+			trigger_error($error_message, $error_type);
298
+		}
299
+	}
300
+
301
+
302
+
303
+
304
+	/**
305
+	 * Logger helpers
306
+	 */
307
+	/**
308
+	 * debug
309
+	 *
310
+	 * @param string $class
311
+	 * @param string $func
312
+	 * @param string $line
313
+	 * @param array  $info
314
+	 * @param bool   $display_request
315
+	 * @param string $debug_index
316
+	 * @param string $debug_key
317
+	 */
318
+	public static function log(
319
+		$class = '',
320
+		$func = '',
321
+		$line = '',
322
+		$info = array(),
323
+		$display_request = false,
324
+		$debug_index = '',
325
+		$debug_key = 'EE_DEBUG_SPCO'
326
+	) {
327
+		if (WP_DEBUG) {
328
+			$debug_key = $debug_key . '_' . EE_Session::instance()->id();
329
+			$debug_data = get_option($debug_key, array());
330
+			$default_data = array(
331
+				$class => $func . '() : ' . $line,
332
+			);
333
+			// don't serialize objects
334
+			$info = self::strip_objects($info);
335
+			$index = ! empty($debug_index) ? $debug_index : 0;
336
+			if (! isset($debug_data[ $index ])) {
337
+				$debug_data[ $index ] = array();
338
+			}
339
+			$debug_data[ $index ][ microtime() ] = array_merge($default_data, $info);
340
+			update_option($debug_key, $debug_data);
341
+		}
342
+	}
343
+
344
+
345
+
346
+	/**
347
+	 * strip_objects
348
+	 *
349
+	 * @param array $info
350
+	 * @return array
351
+	 */
352
+	public static function strip_objects($info = array())
353
+	{
354
+		foreach ($info as $key => $value) {
355
+			if (is_array($value)) {
356
+				$info[ $key ] = self::strip_objects($value);
357
+			} elseif (is_object($value)) {
358
+				$object_class = get_class($value);
359
+				$info[ $object_class ] = array();
360
+				$info[ $object_class ]['ID'] = method_exists($value, 'ID') ? $value->ID() : spl_object_hash($value);
361
+				if (method_exists($value, 'ID')) {
362
+					$info[ $object_class ]['ID'] = $value->ID();
363
+				}
364
+				if (method_exists($value, 'status')) {
365
+					$info[ $object_class ]['status'] = $value->status();
366
+				} elseif (method_exists($value, 'status_ID')) {
367
+					$info[ $object_class ]['status'] = $value->status_ID();
368
+				}
369
+				unset($info[ $key ]);
370
+			}
371
+		}
372
+		return (array) $info;
373
+	}
374
+
375
+
376
+
377
+	/**
378
+	 * @param mixed      $var
379
+	 * @param string     $var_name
380
+	 * @param string     $file
381
+	 * @param int|string $line
382
+	 * @param int|string $heading_tag
383
+	 * @param bool       $die
384
+	 * @param string     $margin
385
+	 */
386
+	public static function printv(
387
+		$var,
388
+		$var_name = '',
389
+		$file = '',
390
+		$line = '',
391
+		$heading_tag = 5,
392
+		$die = false,
393
+		$margin = ''
394
+	) {
395
+		$var_name = ! $var_name ? 'string' : $var_name;
396
+		$var_name = ucwords(str_replace('$', '', $var_name));
397
+		$is_method = method_exists($var_name, $var);
398
+		$var_name = ucwords(str_replace('_', ' ', $var_name));
399
+		$heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
400
+		$result = EEH_Debug_Tools::headingSpacer($heading_tag);
401
+		$result .= EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
402
+		$result .= $is_method
403
+			? EEH_Debug_Tools::grey_span('::') . EEH_Debug_Tools::orange_span($var . '()')
404
+			: EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span($var);
405
+		$result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
406
+		$result .= EEH_Debug_Tools::headingX($heading_tag);
407
+		if ($die) {
408
+			die($result);
409
+		}
410
+		echo wp_kses($result, AllowedTags::getWithFormTags());
411
+	}
412
+
413
+
414
+	protected static function headingTag($heading_tag)
415
+	{
416
+		$heading_tag = absint($heading_tag);
417
+		return $heading_tag > 0 && $heading_tag < 7 ? "h{$heading_tag}" : 'h5';
418
+	}
419
+
420
+	protected static function headingSpacer($heading_tag)
421
+	{
422
+		return EEH_Debug_Tools::plainOutput() && ($heading_tag === 'h1' || $heading_tag === 'h2')
423
+			? self::lineBreak()
424
+			: '';
425
+	}
426
+
427
+
428
+	protected static function lineBreak()
429
+	{
430
+		return defined('DOING_AJAX') && DOING_AJAX ? '<br />' : "\n";
431
+	}
432
+
433
+
434
+	protected static function plainOutput()
435
+	{
436
+		return defined('EE_TESTS_DIR')
437
+			   || (defined('DOING_AJAX') && DOING_AJAX)
438
+			   || (
439
+				   isset($_SERVER['REQUEST_URI'])
440
+				   && strpos(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), 'wp-json') !== false
441
+			   );
442
+	}
443
+
444
+
445
+	/**
446
+	 * @param string $var_name
447
+	 * @param string $heading_tag
448
+	 * @param string $margin
449
+	 * @param int    $line
450
+	 * @return string
451
+	 */
452
+	protected static function heading($var_name = '', $heading_tag = 'h5', $margin = '', $line = 0)
453
+	{
454
+		if (EEH_Debug_Tools::plainOutput()) {
455
+			$heading = '';
456
+			if ($heading_tag === 'h1' || $heading_tag === 'h2') {
457
+				$heading .= self::lineBreak();
458
+			}
459
+			$heading .= self::lineBreak() . "{$line}) {$var_name}";
460
+			return $heading;
461
+		}
462
+		$margin = "25px 0 0 {$margin}";
463
+		return '<' . $heading_tag . ' style="color:#2EA2CC; margin:' . $margin . ';"><b>' . $var_name . '</b>';
464
+	}
465
+
466
+
467
+
468
+	/**
469
+	 * @param string $heading_tag
470
+	 * @return string
471
+	 */
472
+	protected static function headingX($heading_tag = 'h5')
473
+	{
474
+		if (EEH_Debug_Tools::plainOutput()) {
475
+			return '';
476
+		}
477
+		return '</' . $heading_tag . '>';
478
+	}
479
+
480
+
481
+
482
+	/**
483
+	 * @param string $content
484
+	 * @return string
485
+	 */
486
+	protected static function grey_span($content = '')
487
+	{
488
+		if (EEH_Debug_Tools::plainOutput()) {
489
+			return $content;
490
+		}
491
+		return '<span style="color:#999">' . $content . '</span>';
492
+	}
493
+
494
+
495
+
496
+	/**
497
+	 * @param string $file
498
+	 * @param int    $line
499
+	 * @return string
500
+	 */
501
+	protected static function file_and_line($file, $line, $heading_tag)
502
+	{
503
+		if ($file === '' || $line === '') {
504
+			return '';
505
+		}
506
+		$file = str_replace(EE_PLUGIN_DIR_PATH, '/', $file);
507
+		if (EEH_Debug_Tools::plainOutput()) {
508
+			if ($heading_tag === 'h1' || $heading_tag === 'h2') {
509
+				return " ({$file})";
510
+			}
511
+			return '';
512
+		}
513
+		return '<br /><span style="font-size:9px;font-weight:normal;color:#666;line-height: 12px;">'
514
+			   . $file
515
+			   . '<br />line no: '
516
+			   . $line
517
+			   . '</span>';
518
+	}
519
+
520
+
521
+
522
+	/**
523
+	 * @param string $content
524
+	 * @return string
525
+	 */
526
+	protected static function orange_span($content = '')
527
+	{
528
+		if (EEH_Debug_Tools::plainOutput()) {
529
+			return $content;
530
+		}
531
+		return '<span style="color:#E76700">' . $content . '</span>';
532
+	}
533
+
534
+
535
+
536
+	/**
537
+	 * @param mixed $var
538
+	 * @return string
539
+	 */
540
+	protected static function pre_span($var)
541
+	{
542
+		ob_start();
543
+		var_dump($var);
544
+		$var = ob_get_clean();
545
+		if (EEH_Debug_Tools::plainOutput()) {
546
+			return str_replace("\n", '', $var);
547
+		}
548
+		return '<pre style="background: #fff; color:#999; display: inline-block; margin: 0 1em; padding: 0;">' .
549
+			   $var . '</pre>';
550
+	}
551
+
552
+
553
+
554
+	/**
555
+	 * @param mixed      $var
556
+	 * @param string     $var_name
557
+	 * @param string     $file
558
+	 * @param int|string $line
559
+	 * @param int|string $heading_tag
560
+	 * @param bool       $die
561
+	 */
562
+	public static function printr(
563
+		$var,
564
+		$var_name = '',
565
+		$file = '',
566
+		$line = '',
567
+		$heading_tag = 5,
568
+		$die = false
569
+	) {
570
+		// return;
571
+		$file = str_replace(rtrim(ABSPATH, '\\/'), '', $file);
572
+		$margin = is_admin() ? ' 180px' : '0';
573
+		if (is_string($var)) {
574
+			EEH_Debug_Tools::printv($var, $var_name, $file, $line, $heading_tag, $die, $margin);
575
+			return;
576
+		}
577
+		if (is_object($var)) {
578
+			$var_name = ! $var_name ? 'object' : $var_name;
579
+		} elseif (is_array($var)) {
580
+			$var_name = ! $var_name ? 'array' : $var_name;
581
+		} elseif (is_numeric($var)) {
582
+			$var_name = ! $var_name ? 'numeric' : $var_name;
583
+		} elseif ($var === null) {
584
+			$var_name = ! $var_name ? 'null' : $var_name;
585
+		}
586
+		$var_name = ucwords(str_replace(array('$', '_'), array('', ' '), $var_name));
587
+		$heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
588
+		$result = EEH_Debug_Tools::headingSpacer($heading_tag);
589
+		$result .= EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
590
+		$result .= EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span(
591
+			EEH_Debug_Tools::pre_span($var)
592
+		);
593
+		$result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
594
+		$result .= EEH_Debug_Tools::headingX($heading_tag);
595
+		if ($die) {
596
+			die($result);
597
+		}
598
+		echo wp_kses($result, AllowedTags::getWithFormTags());
599
+	}
600
+
601
+
602
+
603
+	/******************** deprecated ********************/
604
+
605
+
606
+
607
+	/**
608
+	 * @deprecated 4.9.39.rc.034
609
+	 */
610
+	public function reset_times()
611
+	{
612
+		Benchmark::resetTimes();
613
+	}
614
+
615
+
616
+
617
+	/**
618
+	 * @deprecated 4.9.39.rc.034
619
+	 * @param null $timer_name
620
+	 */
621
+	public function start_timer($timer_name = null)
622
+	{
623
+		Benchmark::startTimer($timer_name);
624
+	}
625
+
626
+
627
+
628
+	/**
629
+	 * @deprecated 4.9.39.rc.034
630
+	 * @param string $timer_name
631
+	 */
632
+	public function stop_timer($timer_name = '')
633
+	{
634
+		Benchmark::stopTimer($timer_name);
635
+	}
636
+
637
+
638
+
639
+	/**
640
+	 * @deprecated 4.9.39.rc.034
641
+	 * @param string  $label      The label to show for this time eg "Start of calling Some_Class::some_function"
642
+	 * @param boolean $output_now whether to echo now, or wait until EEH_Debug_Tools::show_times() is called
643
+	 * @return void
644
+	 */
645
+	public function measure_memory($label, $output_now = false)
646
+	{
647
+		Benchmark::measureMemory($label, $output_now);
648
+	}
649
+
650
+
651
+
652
+	/**
653
+	 * @deprecated 4.9.39.rc.034
654
+	 * @param int $size
655
+	 * @return string
656
+	 */
657
+	public function convert($size)
658
+	{
659
+		return Benchmark::convert($size);
660
+	}
661
+
662
+
663
+
664
+	/**
665
+	 * @deprecated 4.9.39.rc.034
666
+	 * @param bool $output_now
667
+	 * @return string
668
+	 */
669
+	public function show_times($output_now = true)
670
+	{
671
+		return Benchmark::displayResults($output_now);
672
+	}
673
+
674
+
675
+
676
+	/**
677
+	 * @deprecated 4.9.39.rc.034
678
+	 * @param string $timer_name
679
+	 * @param float  $total_time
680
+	 * @return string
681
+	 */
682
+	public function format_time($timer_name, $total_time)
683
+	{
684
+		return Benchmark::formatTime($timer_name, $total_time);
685
+	}
686 686
 }
687 687
 
688 688
 
@@ -692,31 +692,31 @@  discard block
 block discarded – undo
692 692
  * Plugin URI: http://upthemes.com/plugins/kint-debugger/
693 693
  */
694 694
 if (class_exists('Kint') && ! function_exists('dump_wp_query')) {
695
-    function dump_wp_query()
696
-    {
697
-        global $wp_query;
698
-        d($wp_query);
699
-    }
695
+	function dump_wp_query()
696
+	{
697
+		global $wp_query;
698
+		d($wp_query);
699
+	}
700 700
 }
701 701
 /**
702 702
  * borrowed from Kint Debugger
703 703
  * Plugin URI: http://upthemes.com/plugins/kint-debugger/
704 704
  */
705 705
 if (class_exists('Kint') && ! function_exists('dump_wp')) {
706
-    function dump_wp()
707
-    {
708
-        global $wp;
709
-        d($wp);
710
-    }
706
+	function dump_wp()
707
+	{
708
+		global $wp;
709
+		d($wp);
710
+	}
711 711
 }
712 712
 /**
713 713
  * borrowed from Kint Debugger
714 714
  * Plugin URI: http://upthemes.com/plugins/kint-debugger/
715 715
  */
716 716
 if (class_exists('Kint') && ! function_exists('dump_post')) {
717
-    function dump_post()
718
-    {
719
-        global $post;
720
-        d($post);
721
-    }
717
+	function dump_post()
718
+	{
719
+		global $post;
720
+		d($post);
721
+	}
722 722
 }
Please login to merge, or discard this patch.
core/interfaces/line_items/EEI_Line_Item.interface.php 1 patch
Indentation   +175 added lines, -175 removed lines patch added patch discarded remove patch
@@ -10,238 +10,238 @@
 block discarded – undo
10 10
  */
11 11
 interface EEI_Line_Item
12 12
 {
13
-    /**
14
-     * Gets item_id
15
-     *
16
-     * @return string
17
-     */
18
-    public function ID();
13
+	/**
14
+	 * Gets item_id
15
+	 *
16
+	 * @return string
17
+	 */
18
+	public function ID();
19 19
 
20
-    /**
21
-     * generic setter
22
-     * @param    string $field_name
23
-     * @param    mixed  $field_value
24
-     * @param bool      $use_default
25
-     */
26
-    public function set($field_name, $field_value, $use_default = false);
20
+	/**
21
+	 * generic setter
22
+	 * @param    string $field_name
23
+	 * @param    mixed  $field_value
24
+	 * @param bool      $use_default
25
+	 */
26
+	public function set($field_name, $field_value, $use_default = false);
27 27
 
28 28
 
29 29
 
30
-    /**
31
-     * @return string
32
-     */
33
-    public function name();
30
+	/**
31
+	 * @return string
32
+	 */
33
+	public function name();
34 34
 
35 35
 
36 36
 
37
-    /**
38
-     * @return string
39
-     */
40
-    public function desc();
37
+	/**
38
+	 * @return string
39
+	 */
40
+	public function desc();
41 41
 
42 42
 
43 43
 
44
-    /**
45
-     * The unit price for the items of this line item
46
-     * @return float
47
-     */
48
-    public function unit_price();
44
+	/**
45
+	 * The unit price for the items of this line item
46
+	 * @return float
47
+	 */
48
+	public function unit_price();
49 49
 
50 50
 
51 51
 
52
-    /**
53
-     * Returns the number of items in this line item
54
-     * @return int
55
-     */
56
-    public function quantity();
52
+	/**
53
+	 * Returns the number of items in this line item
54
+	 * @return int
55
+	 */
56
+	public function quantity();
57 57
 
58 58
 
59 59
 
60
-    /**
61
-     * Sets quantity
62
-     * @param int $quantity
63
-     */
64
-    public function set_quantity($quantity);
60
+	/**
61
+	 * Sets quantity
62
+	 * @param int $quantity
63
+	 */
64
+	public function set_quantity($quantity);
65 65
 
66 66
 
67 67
 
68
-    /**
69
-     * Returns the total amount due for this line item
70
-     * (usually quantity x unit_price)
71
-     * @return float
72
-     */
73
-    public function total();
68
+	/**
69
+	 * Returns the total amount due for this line item
70
+	 * (usually quantity x unit_price)
71
+	 * @return float
72
+	 */
73
+	public function total();
74 74
 
75 75
 
76 76
 
77
-    /**
78
-     * Gets all teh children line items of type 'line-item'
79
-     * @return EEI_Line_Item[]
80
-     */
81
-    public function get_items();
77
+	/**
78
+	 * Gets all teh children line items of type 'line-item'
79
+	 * @return EEI_Line_Item[]
80
+	 */
81
+	public function get_items();
82 82
 
83 83
 
84 84
 
85
-    /**
86
-     * Gets the total for all the items purchased only
87
-     * @return float
88
-     */
89
-    public function get_items_total();
85
+	/**
86
+	 * Gets the total for all the items purchased only
87
+	 * @return float
88
+	 */
89
+	public function get_items_total();
90 90
 
91 91
 
92 92
 
93
-    /**
94
-     * Gets all the children line items of type 'tax'
95
-     * @return EEI_Line_Item[]
96
-     */
97
-    public function tax_descendants();
93
+	/**
94
+	 * Gets all the children line items of type 'tax'
95
+	 * @return EEI_Line_Item[]
96
+	 */
97
+	public function tax_descendants();
98 98
 
99 99
 
100 100
 
101
-    /**
102
-     * Gets the total amount of the tax sub-line items
103
-     * @return float
104
-     */
105
-    public function get_total_tax();
101
+	/**
102
+	 * Gets the total amount of the tax sub-line items
103
+	 * @return float
104
+	 */
105
+	public function get_total_tax();
106 106
 
107 107
 
108 108
 
109
-    /**
110
-     * Returns the name of the event the ticket is for
111
-     * @return string
112
-     */
113
-    public function ticket_event_name();
109
+	/**
110
+	 * Returns the name of the event the ticket is for
111
+	 * @return string
112
+	 */
113
+	public function ticket_event_name();
114 114
 
115 115
 
116 116
 
117
-    /**
118
-     * Saves this line item to the DB, and recursively saves its descendants.
119
-     * Also sets the transaction on this line item and all its descendants before saving
120
-     * @param int $txn_id if none is provided, assumes $this->TXN_ID()
121
-     * @return int count of items saved
122
-     */
123
-    public function save_this_and_descendants_to_txn($txn_id = null);
117
+	/**
118
+	 * Saves this line item to the DB, and recursively saves its descendants.
119
+	 * Also sets the transaction on this line item and all its descendants before saving
120
+	 * @param int $txn_id if none is provided, assumes $this->TXN_ID()
121
+	 * @return int count of items saved
122
+	 */
123
+	public function save_this_and_descendants_to_txn($txn_id = null);
124 124
 
125 125
 
126 126
 
127
-    /**
128
-     * Indicates whether or not taxes should apply to this line item
129
-     * @return boolean
130
-     */
131
-    public function is_taxable();
127
+	/**
128
+	 * Indicates whether or not taxes should apply to this line item
129
+	 * @return boolean
130
+	 */
131
+	public function is_taxable();
132 132
 
133 133
 
134 134
 
135
-    /**
136
-     * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
137
-     * @return EEI_Line_Item[]
138
-     */
139
-    public function children();
135
+	/**
136
+	 * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
137
+	 * @return EEI_Line_Item[]
138
+	 */
139
+	public function children();
140 140
 
141 141
 
142 142
 
143
-    /**
144
-     * Adds the line item as a child to this line item. If there is another child line
145
-     * item with the same LIN_code, it is overwritten by this new one
146
-     * @param EEI_Line_Item $line_item
147
-     * @param bool         $set_order
148
-     * @return bool true for success, false for fail
149
-     */
150
-    public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true);
143
+	/**
144
+	 * Adds the line item as a child to this line item. If there is another child line
145
+	 * item with the same LIN_code, it is overwritten by this new one
146
+	 * @param EEI_Line_Item $line_item
147
+	 * @param bool         $set_order
148
+	 * @return bool true for success, false for fail
149
+	 */
150
+	public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true);
151 151
 
152 152
 
153 153
 
154
-    /**
155
-     * Gets the line item type
156
-     * @return string
157
-     */
158
-    public function type();
154
+	/**
155
+	 * Gets the line item type
156
+	 * @return string
157
+	 */
158
+	public function type();
159 159
 
160 160
 
161 161
 
162
-    /**
163
-     * Gets item_id
164
-     * @return string
165
-     */
166
-    public function OBJ_ID();
162
+	/**
163
+	 * Gets item_id
164
+	 * @return string
165
+	 */
166
+	public function OBJ_ID();
167 167
 
168 168
 
169 169
 
170
-    /**
171
-     * Gets the related item type ( like: Ticket or Event )
172
-     * @return string
173
-     */
174
-    public function OBJ_type();
170
+	/**
171
+	 * Gets the related item type ( like: Ticket or Event )
172
+	 * @return string
173
+	 */
174
+	public function OBJ_type();
175 175
 
176 176
 
177 177
 
178
-    /**
179
-     * Gets the final total on this item, taking taxes into account.
180
-     * Has the side-effect of setting the sub-total as it was just calculated.
181
-     * If this is used on a grand-total line item, also updates the transaction's
182
-     * TXN_total
183
-     * @return float
184
-     */
185
-    public function recalculate_total_including_taxes();
178
+	/**
179
+	 * Gets the final total on this item, taking taxes into account.
180
+	 * Has the side-effect of setting the sub-total as it was just calculated.
181
+	 * If this is used on a grand-total line item, also updates the transaction's
182
+	 * TXN_total
183
+	 * @return float
184
+	 */
185
+	public function recalculate_total_including_taxes();
186 186
 
187
-    /**
188
-     * Checks if this item is a percentage modifier or not
189
-     * @throws EE_Error
190
-     * @return boolean
191
-     */
192
-    public function is_percent();
193
-
194
-    /**
195
-     * Gets percent (between 100-.001)
196
-     * @return float
197
-     */
198
-    public function percent();
199
-
200
-    /**
201
-     * Sets total
202
-     * @param float $total
203
-     * @return boolean
204
-     */
205
-    public function set_total($total);
206
-
207
-    /**
208
-     * Sets unit_price
209
-     * @param float $unit_price
210
-     * @return boolean
211
-     */
212
-    public function set_unit_price($unit_price);
213
-
214
-    /**
215
-     * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
216
-     * HAS NOT been saved to the DB, removes the child line item with index $code.
217
-     * Also searches through the child's children for a matching line item. However, once a line item has been found
218
-     * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be deleted)
219
-     * @param string $code
220
-     * @param bool $stop_search_once_found
221
-     * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to the DB yet)
222
-     */
223
-    public function delete_child_line_item($code, $stop_search_once_found = true);
224
-
225
-    /**
226
-     * Forgets the cached model of the given relation Name. So the next time we request it,
227
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
228
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
229
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
230
-     * @param string $relationName                         one of the keys in the _model_relations array on the model. Eg 'Registration'
231
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
232
-     * if you intend to use $clear_all = TRUE, or the relation only has 1 object anyways (ie, it's a BelongsToRelation)
233
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if this is HasMany or HABTM.
234
-     * @throws EE_Error
235
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a relation from all
236
-     */
237
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false);
238
-
239
-    /**
240
-     * Sets whether or not this model object should be allowed to be saved to the DB.
241
-     * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
242
-     * you got new information that somehow made you change your mind.
243
-     * @param boolean $allow_persist
244
-     * @return boolean
245
-     */
246
-    public function set_allow_persist($allow_persist);
187
+	/**
188
+	 * Checks if this item is a percentage modifier or not
189
+	 * @throws EE_Error
190
+	 * @return boolean
191
+	 */
192
+	public function is_percent();
193
+
194
+	/**
195
+	 * Gets percent (between 100-.001)
196
+	 * @return float
197
+	 */
198
+	public function percent();
199
+
200
+	/**
201
+	 * Sets total
202
+	 * @param float $total
203
+	 * @return boolean
204
+	 */
205
+	public function set_total($total);
206
+
207
+	/**
208
+	 * Sets unit_price
209
+	 * @param float $unit_price
210
+	 * @return boolean
211
+	 */
212
+	public function set_unit_price($unit_price);
213
+
214
+	/**
215
+	 * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
216
+	 * HAS NOT been saved to the DB, removes the child line item with index $code.
217
+	 * Also searches through the child's children for a matching line item. However, once a line item has been found
218
+	 * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be deleted)
219
+	 * @param string $code
220
+	 * @param bool $stop_search_once_found
221
+	 * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to the DB yet)
222
+	 */
223
+	public function delete_child_line_item($code, $stop_search_once_found = true);
224
+
225
+	/**
226
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
227
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
228
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
229
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
230
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model. Eg 'Registration'
231
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
232
+	 * if you intend to use $clear_all = TRUE, or the relation only has 1 object anyways (ie, it's a BelongsToRelation)
233
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if this is HasMany or HABTM.
234
+	 * @throws EE_Error
235
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a relation from all
236
+	 */
237
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false);
238
+
239
+	/**
240
+	 * Sets whether or not this model object should be allowed to be saved to the DB.
241
+	 * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
242
+	 * you got new information that somehow made you change your mind.
243
+	 * @param boolean $allow_persist
244
+	 * @return boolean
245
+	 */
246
+	public function set_allow_persist($allow_persist);
247 247
 }
Please login to merge, or discard this patch.
line_item_filters/EE_Single_Registration_Line_Item_Filter.class.php 1 patch
Indentation   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -15,14 +15,14 @@
 block discarded – undo
15 15
  */
16 16
 class EE_Single_Registration_Line_Item_Filter extends EE_Specific_Registrations_Line_Item_Filter
17 17
 {
18
-    /**
19
-     *
20
-     * @param EE_Registration $registration
21
-     * @throws EE_Error
22
-     * @throws ReflectionException
23
-     */
24
-    public function __construct($registration)
25
-    {
26
-        parent::__construct([$registration]);
27
-    }
18
+	/**
19
+	 *
20
+	 * @param EE_Registration $registration
21
+	 * @throws EE_Error
22
+	 * @throws ReflectionException
23
+	 */
24
+	public function __construct($registration)
25
+	{
26
+		parent::__construct([$registration]);
27
+	}
28 28
 }
Please login to merge, or discard this patch.
core/libraries/line_item_filters/EE_Billable_Line_Item_Filter.class.php 1 patch
Indentation   +47 added lines, -47 removed lines patch added patch discarded remove patch
@@ -15,53 +15,53 @@
 block discarded – undo
15 15
  */
16 16
 class EE_Billable_Line_Item_Filter extends EE_Specific_Registrations_Line_Item_Filter
17 17
 {
18
-    /**
19
-     * EE_Billable_Line_Item_Filter constructor.
20
-     *
21
-     * @param EE_Registration[] $registrations
22
-     * @throws EE_Error
23
-     * @throws ReflectionException
24
-     */
25
-    public function __construct($registrations)
26
-    {
27
-        parent::__construct($this->_remove_unbillable_registrations($registrations));
28
-    }
18
+	/**
19
+	 * EE_Billable_Line_Item_Filter constructor.
20
+	 *
21
+	 * @param EE_Registration[] $registrations
22
+	 * @throws EE_Error
23
+	 * @throws ReflectionException
24
+	 */
25
+	public function __construct($registrations)
26
+	{
27
+		parent::__construct($this->_remove_unbillable_registrations($registrations));
28
+	}
29 29
 
30 30
 
31
-    /**
32
-     *    _calculate_billable_ticket_quantities_from_registrations
33
-     * compiles a list of EE_Tickets for each event in the passed array
34
-     *
35
-     * @access protected
36
-     * @param EE_Registration[] $registrations
37
-     * @return mixed
38
-     * @throws EE_Error
39
-     * @throws ReflectionException
40
-     */
41
-    protected function _remove_unbillable_registrations($registrations = array())
42
-    {
43
-        if (! empty($registrations)) {
44
-            // these reg statuses require payment (if event is not free)
45
-            $requires_payment = EEM_Registration::reg_statuses_that_allow_payment();
46
-            foreach ($registrations as $key => $registration) {
47
-                if (! $registration instanceof EE_Registration) {
48
-                    continue;
49
-                }
50
-                // are we billing for this registration at this moment ?
51
-                if (
52
-                    !
53
-                    $registration->owes_monies_and_can_pay($requires_payment) &&
54
-                    ! (
55
-                        // free registration with valid reg status
56
-                        $registration->final_price() == 0 &&
57
-                        in_array($registration->status_ID(), $requires_payment)
58
-                    )
59
-                ) {
60
-                    // not billable. remove it
61
-                    unset($registrations[ $key ]);
62
-                }
63
-            }
64
-        }
65
-        return $registrations;
66
-    }
31
+	/**
32
+	 *    _calculate_billable_ticket_quantities_from_registrations
33
+	 * compiles a list of EE_Tickets for each event in the passed array
34
+	 *
35
+	 * @access protected
36
+	 * @param EE_Registration[] $registrations
37
+	 * @return mixed
38
+	 * @throws EE_Error
39
+	 * @throws ReflectionException
40
+	 */
41
+	protected function _remove_unbillable_registrations($registrations = array())
42
+	{
43
+		if (! empty($registrations)) {
44
+			// these reg statuses require payment (if event is not free)
45
+			$requires_payment = EEM_Registration::reg_statuses_that_allow_payment();
46
+			foreach ($registrations as $key => $registration) {
47
+				if (! $registration instanceof EE_Registration) {
48
+					continue;
49
+				}
50
+				// are we billing for this registration at this moment ?
51
+				if (
52
+					!
53
+					$registration->owes_monies_and_can_pay($requires_payment) &&
54
+					! (
55
+						// free registration with valid reg status
56
+						$registration->final_price() == 0 &&
57
+						in_array($registration->status_ID(), $requires_payment)
58
+					)
59
+				) {
60
+					// not billable. remove it
61
+					unset($registrations[ $key ]);
62
+				}
63
+			}
64
+		}
65
+		return $registrations;
66
+	}
67 67
 }
Please login to merge, or discard this patch.
core/libraries/line_item_filters/EE_Line_Item_Filter_Processor.class.php 1 patch
Indentation   +77 added lines, -77 removed lines patch added patch discarded remove patch
@@ -35,90 +35,90 @@
 block discarded – undo
35 35
  */
36 36
 class EE_Line_Item_Filter_Processor
37 37
 {
38
-    /**
39
-     * @type EE_Line_Item_Filter_Collection $line_item_filters
40
-     */
41
-    protected $line_item_filters;
38
+	/**
39
+	 * @type EE_Line_Item_Filter_Collection $line_item_filters
40
+	 */
41
+	protected $line_item_filters;
42 42
 
43
-    /**
44
-     * @type EEI_Line_Item $grand_total_line_item
45
-     */
46
-    protected $grand_total_line_item;
43
+	/**
44
+	 * @type EEI_Line_Item $grand_total_line_item
45
+	 */
46
+	protected $grand_total_line_item;
47 47
 
48 48
 
49
-    /**
50
-     * EE_Line_Item_Filter_Processor constructor.
51
-     *
52
-     * @param EE_Line_Item_Filter_Collection $line_item_filters
53
-     * @param EEI_Line_Item                  $grand_total_line_item
54
-     * @throws EE_Error
55
-     */
56
-    public function __construct(EE_Line_Item_Filter_Collection $line_item_filters, EEI_Line_Item $grand_total_line_item)
57
-    {
58
-        $this->line_item_filters = $line_item_filters;
59
-        if ($grand_total_line_item->type() !== EEM_Line_Item::type_total) {
60
-            throw new EE_Error(esc_html__('A Line Item of the type total is required', 'event_espresso'));
61
-        }
62
-        $this->grand_total_line_item = $this->clone_and_reset_line_item_tree($grand_total_line_item);
63
-    }
49
+	/**
50
+	 * EE_Line_Item_Filter_Processor constructor.
51
+	 *
52
+	 * @param EE_Line_Item_Filter_Collection $line_item_filters
53
+	 * @param EEI_Line_Item                  $grand_total_line_item
54
+	 * @throws EE_Error
55
+	 */
56
+	public function __construct(EE_Line_Item_Filter_Collection $line_item_filters, EEI_Line_Item $grand_total_line_item)
57
+	{
58
+		$this->line_item_filters = $line_item_filters;
59
+		if ($grand_total_line_item->type() !== EEM_Line_Item::type_total) {
60
+			throw new EE_Error(esc_html__('A Line Item of the type total is required', 'event_espresso'));
61
+		}
62
+		$this->grand_total_line_item = $this->clone_and_reset_line_item_tree($grand_total_line_item);
63
+	}
64 64
 
65 65
 
66
-    /**
67
-     * clone_and_reset_line_item_tree
68
-     *
69
-     * @param EEI_Line_Item $line_item
70
-     * @return EEI_Line_Item
71
-     * @throws EE_Error
72
-     */
73
-    protected function clone_and_reset_line_item_tree(EEI_Line_Item $line_item)
74
-    {
75
-        $cloned_line_item = $this->clone_and_reset_line_item($line_item);
76
-        foreach ($line_item->children() as $child_line_item) {
77
-            $cloned_line_item->add_child_line_item($this->clone_and_reset_line_item_tree($child_line_item));
78
-        }
79
-        return $cloned_line_item;
80
-    }
66
+	/**
67
+	 * clone_and_reset_line_item_tree
68
+	 *
69
+	 * @param EEI_Line_Item $line_item
70
+	 * @return EEI_Line_Item
71
+	 * @throws EE_Error
72
+	 */
73
+	protected function clone_and_reset_line_item_tree(EEI_Line_Item $line_item)
74
+	{
75
+		$cloned_line_item = $this->clone_and_reset_line_item($line_item);
76
+		foreach ($line_item->children() as $child_line_item) {
77
+			$cloned_line_item->add_child_line_item($this->clone_and_reset_line_item_tree($child_line_item));
78
+		}
79
+		return $cloned_line_item;
80
+	}
81 81
 
82 82
 
83
-    /**
84
-     * clone_and_reset_line_item
85
-     *
86
-     * clones the incoming object
87
-     * resets any fields that represent database primary keys
88
-     * resets total
89
-     *
90
-     * @param EEI_Line_Item $line_item
91
-     * @return EEI_Line_Item
92
-     * @throws EE_Error
93
-     */
94
-    protected function clone_and_reset_line_item(EEI_Line_Item $line_item)
95
-    {
96
-        // we don't actually want to work with the original line item, so clone it
97
-        $cloned_line_item = clone $line_item;
98
-        $cloned_line_item->set('LIN_ID', null);
99
-        $cloned_line_item->set('LIN_parent', null);
100
-        $cloned_line_item->clear_related_line_item_cache();
101
-        foreach (array_keys(EEM_Line_Item::instance()->relation_settings()) as $relation_name) {
102
-            $cloned_line_item->clear_cache($relation_name, null, true);
103
-        }
104
-        $cloned_line_item->set_allow_persist(false);
105
-        return $cloned_line_item;
106
-    }
83
+	/**
84
+	 * clone_and_reset_line_item
85
+	 *
86
+	 * clones the incoming object
87
+	 * resets any fields that represent database primary keys
88
+	 * resets total
89
+	 *
90
+	 * @param EEI_Line_Item $line_item
91
+	 * @return EEI_Line_Item
92
+	 * @throws EE_Error
93
+	 */
94
+	protected function clone_and_reset_line_item(EEI_Line_Item $line_item)
95
+	{
96
+		// we don't actually want to work with the original line item, so clone it
97
+		$cloned_line_item = clone $line_item;
98
+		$cloned_line_item->set('LIN_ID', null);
99
+		$cloned_line_item->set('LIN_parent', null);
100
+		$cloned_line_item->clear_related_line_item_cache();
101
+		foreach (array_keys(EEM_Line_Item::instance()->relation_settings()) as $relation_name) {
102
+			$cloned_line_item->clear_cache($relation_name, null, true);
103
+		}
104
+		$cloned_line_item->set_allow_persist(false);
105
+		return $cloned_line_item;
106
+	}
107 107
 
108 108
 
109
-    /**
110
-     * process
111
-     *
112
-     * @return EEI_Line_Item
113
-     */
114
-    public function process()
115
-    {
116
-        $this->line_item_filters->rewind();
117
-        while ($this->line_item_filters->valid()) {
118
-            $this->grand_total_line_item = $this->line_item_filters->current()->process($this->grand_total_line_item);
119
-            $this->line_item_filters->next();
120
-        }
121
-        $this->grand_total_line_item->recalculate_total_including_taxes();
122
-        return $this->grand_total_line_item;
123
-    }
109
+	/**
110
+	 * process
111
+	 *
112
+	 * @return EEI_Line_Item
113
+	 */
114
+	public function process()
115
+	{
116
+		$this->line_item_filters->rewind();
117
+		while ($this->line_item_filters->valid()) {
118
+			$this->grand_total_line_item = $this->line_item_filters->current()->process($this->grand_total_line_item);
119
+			$this->line_item_filters->next();
120
+		}
121
+		$this->grand_total_line_item->recalculate_total_including_taxes();
122
+		return $this->grand_total_line_item;
123
+	}
124 124
 }
Please login to merge, or discard this patch.
reg_steps/payment_options/EE_SPCO_Reg_Step_Payment_Options.class.php 2 patches
Indentation   +2827 added lines, -2827 removed lines patch added patch discarded remove patch
@@ -20,2831 +20,2831 @@
 block discarded – undo
20 20
  */
21 21
 class EE_SPCO_Reg_Step_Payment_Options extends EE_SPCO_Reg_Step
22 22
 {
23
-    /**
24
-     * @var EE_Line_Item_Display $Line_Item_Display
25
-     */
26
-    protected $line_item_display;
27
-
28
-    /**
29
-     * @var boolean $handle_IPN_in_this_request
30
-     */
31
-    protected $handle_IPN_in_this_request = false;
32
-
33
-
34
-    /**
35
-     *    set_hooks - for hooking into EE Core, other modules, etc
36
-     *
37
-     * @access    public
38
-     * @return    void
39
-     */
40
-    public static function set_hooks()
41
-    {
42
-        add_filter(
43
-            'FHEE__SPCO__EE_Line_Item_Filter_Collection',
44
-            ['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
45
-        );
46
-        add_action(
47
-            'wp_ajax_switch_spco_billing_form',
48
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
49
-        );
50
-        add_action(
51
-            'wp_ajax_nopriv_switch_spco_billing_form',
52
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
53
-        );
54
-        add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
55
-        add_action(
56
-            'wp_ajax_nopriv_save_payer_details',
57
-            ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
58
-        );
59
-        add_action(
60
-            'wp_ajax_get_transaction_details_for_gateways',
61
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
62
-        );
63
-        add_action(
64
-            'wp_ajax_nopriv_get_transaction_details_for_gateways',
65
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
66
-        );
67
-        add_filter(
68
-            'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
69
-            ['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method'],
70
-            10,
71
-            1
72
-        );
73
-    }
74
-
75
-
76
-    /**
77
-     *    ajax switch_spco_billing_form
78
-     *
79
-     */
80
-    public static function switch_spco_billing_form()
81
-    {
82
-        EED_Single_Page_Checkout::process_ajax_request('switch_payment_method');
83
-    }
84
-
85
-
86
-    /**
87
-     *    ajax save_payer_details
88
-     *
89
-     */
90
-    public static function save_payer_details()
91
-    {
92
-        EED_Single_Page_Checkout::process_ajax_request('save_payer_details_via_ajax');
93
-    }
94
-
95
-
96
-    /**
97
-     *    ajax get_transaction_details
98
-     *
99
-     */
100
-    public static function get_transaction_details()
101
-    {
102
-        EED_Single_Page_Checkout::process_ajax_request('get_transaction_details_for_gateways');
103
-    }
104
-
105
-
106
-    /**
107
-     * bypass_recaptcha_for_load_payment_method
108
-     *
109
-     * @access public
110
-     * @return array
111
-     * @throws InvalidArgumentException
112
-     * @throws InvalidDataTypeException
113
-     * @throws InvalidInterfaceException
114
-     */
115
-    public static function bypass_recaptcha_for_load_payment_method()
116
-    {
117
-        return [
118
-            'EESID'  => EE_Registry::instance()->SSN->id(),
119
-            'step'   => 'payment_options',
120
-            'action' => 'spco_billing_form',
121
-        ];
122
-    }
123
-
124
-
125
-    /**
126
-     *    class constructor
127
-     *
128
-     * @access    public
129
-     * @param EE_Checkout $checkout
130
-     */
131
-    public function __construct(EE_Checkout $checkout)
132
-    {
133
-        $this->request   = EED_Single_Page_Checkout::getRequest();
134
-        $this->_slug     = 'payment_options';
135
-        $this->_name     = esc_html__('Payment Options', 'event_espresso');
136
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
137
-        $this->checkout  = $checkout;
138
-        $this->_reset_success_message();
139
-        $this->set_instructions(
140
-            esc_html__(
141
-                'Please select a method of payment and provide any necessary billing information before proceeding.',
142
-                'event_espresso'
143
-            )
144
-        );
145
-    }
146
-
147
-
148
-    /**
149
-     * @return null
150
-     */
151
-    public function line_item_display()
152
-    {
153
-        return $this->line_item_display;
154
-    }
155
-
156
-
157
-    /**
158
-     * @param null $line_item_display
159
-     */
160
-    public function set_line_item_display($line_item_display)
161
-    {
162
-        $this->line_item_display = $line_item_display;
163
-    }
164
-
165
-
166
-    /**
167
-     * @return boolean
168
-     */
169
-    public function handle_IPN_in_this_request()
170
-    {
171
-        return $this->handle_IPN_in_this_request;
172
-    }
173
-
174
-
175
-    /**
176
-     * @param boolean $handle_IPN_in_this_request
177
-     */
178
-    public function set_handle_IPN_in_this_request($handle_IPN_in_this_request)
179
-    {
180
-        $this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
181
-    }
182
-
183
-
184
-    /**
185
-     * translate_js_strings
186
-     *
187
-     * @return void
188
-     */
189
-    public function translate_js_strings()
190
-    {
191
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
192
-            'Please select a method of payment in order to continue.',
193
-            'event_espresso'
194
-        );
195
-        EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
196
-            'A valid method of payment could not be determined. Please refresh the page and try again.',
197
-            'event_espresso'
198
-        );
199
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
200
-            'Forwarding to Secure Payment Provider.',
201
-            'event_espresso'
202
-        );
203
-    }
204
-
205
-
206
-    /**
207
-     * enqueue_styles_and_scripts
208
-     *
209
-     * @return void
210
-     * @throws EE_Error
211
-     * @throws InvalidArgumentException
212
-     * @throws InvalidDataTypeException
213
-     * @throws InvalidInterfaceException
214
-     * @throws ReflectionException
215
-     */
216
-    public function enqueue_styles_and_scripts()
217
-    {
218
-        $transaction = $this->checkout->transaction;
219
-        // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
220
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
221
-            return;
222
-        }
223
-        foreach (
224
-            EEM_Payment_Method::instance()->get_all_for_transaction(
225
-                $transaction,
226
-                EEM_Payment_Method::scope_cart
227
-            ) as $payment_method
228
-        ) {
229
-            $type_obj = $payment_method->type_obj();
230
-            if ($type_obj instanceof EE_PMT_Base) {
231
-                $billing_form = $type_obj->generate_new_billing_form($transaction);
232
-                if ($billing_form instanceof EE_Form_Section_Proper) {
233
-                    $billing_form->enqueue_js();
234
-                }
235
-            }
236
-        }
237
-    }
238
-
239
-
240
-    /**
241
-     * initialize_reg_step
242
-     *
243
-     * @return bool
244
-     * @throws EE_Error
245
-     * @throws InvalidArgumentException
246
-     * @throws ReflectionException
247
-     * @throws InvalidDataTypeException
248
-     * @throws InvalidInterfaceException
249
-     */
250
-    public function initialize_reg_step()
251
-    {
252
-        // TODO: if /when we implement donations, then this will need overriding
253
-        if (
254
-            // don't need payment options for:
255
-            // registrations made via the admin
256
-            // completed transactions
257
-            // overpaid transactions
258
-            // $ 0.00 transactions(no payment required)
259
-            ! $this->checkout->payment_required()
260
-            // but do NOT remove if current action being called belongs to this reg step
261
-            && ! is_callable([$this, $this->checkout->action])
262
-            && ! $this->completed()
263
-        ) {
264
-            // and if so, then we no longer need the Payment Options step
265
-            if ($this->is_current_step()) {
266
-                $this->checkout->generate_reg_form = false;
267
-            }
268
-            $this->checkout->remove_reg_step($this->_slug);
269
-            // DEBUG LOG
270
-            // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
271
-            return false;
272
-        }
273
-        // load EEM_Payment_Method
274
-        EE_Registry::instance()->load_model('Payment_Method');
275
-        // get all active payment methods
276
-        $this->checkout->available_payment_methods = EEM_Payment_Method::instance()->get_all_for_transaction(
277
-            $this->checkout->transaction,
278
-            EEM_Payment_Method::scope_cart
279
-        );
280
-        return true;
281
-    }
282
-
283
-
284
-    /**
285
-     * @return EE_Form_Section_Proper
286
-     * @throws EE_Error
287
-     * @throws InvalidArgumentException
288
-     * @throws ReflectionException
289
-     * @throws EntityNotFoundException
290
-     * @throws InvalidDataTypeException
291
-     * @throws InvalidInterfaceException
292
-     * @throws InvalidStatusException
293
-     */
294
-    public function generate_reg_form()
295
-    {
296
-        // reset in case someone changes their mind
297
-        $this->_reset_selected_method_of_payment();
298
-        // set some defaults
299
-        $this->checkout->selected_method_of_payment = 'payments_closed';
300
-        $registrations_requiring_payment            = [];
301
-        $registrations_for_free_events              = [];
302
-        $registrations_requiring_pre_approval       = [];
303
-        $sold_out_events                            = [];
304
-        $insufficient_spaces_available              = [];
305
-        $no_payment_required                        = true;
306
-        // loop thru registrations to gather info
307
-        $registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
308
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
309
-            $registrations,
310
-            $this->checkout->revisit
311
-        );
312
-        $extra_txn_fees_handler = new ExtraTxnFeesForRegistrantsHandler($registrations);
313
-        $extra_txn_fees_handler->applyExtraFeesToRegistrants();
314
-        foreach ($registrations as $REG_ID => $registration) {
315
-            /** @var $registration EE_Registration */
316
-            // has this registration lost it's space ?
317
-            if (isset($ejected_registrations[ $REG_ID ])) {
318
-                if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
319
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
320
-                } else {
321
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
322
-                }
323
-                continue;
324
-            }
325
-            // event requires admin approval
326
-            if ($registration->status_ID() === EEM_Registration::status_id_not_approved) {
327
-                // add event to list of events with pre-approval reg status
328
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
329
-                do_action(
330
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
331
-                    $registration->event(),
332
-                    $this
333
-                );
334
-                continue;
335
-            }
336
-            if (
337
-                $this->checkout->revisit
338
-                && $registration->status_ID() !== EEM_Registration::status_id_approved
339
-                && (
340
-                    $registration->event()->is_sold_out()
341
-                    || $registration->event()->is_sold_out(true)
342
-                )
343
-            ) {
344
-                // add event to list of events that are sold out
345
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
346
-                do_action(
347
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
348
-                    $registration->event(),
349
-                    $this
350
-                );
351
-                continue;
352
-            }
353
-            // are they allowed to pay now and is there monies owing?
354
-            if ($registration->owes_monies_and_can_pay()) {
355
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
356
-                do_action(
357
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
358
-                    $registration->event(),
359
-                    $this
360
-                );
361
-            } elseif (
362
-                ! $this->checkout->revisit
363
-                      && $registration->status_ID() !== EEM_Registration::status_id_not_approved
364
-                      && $registration->ticket()->is_free()
365
-            ) {
366
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
367
-            }
368
-        }
369
-        $subsections = [];
370
-        // now decide which template to load
371
-        if (! empty($sold_out_events)) {
372
-            $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
373
-        }
374
-        if (! empty($insufficient_spaces_available)) {
375
-            $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
376
-                $insufficient_spaces_available
377
-            );
378
-        }
379
-        if (! empty($registrations_requiring_pre_approval)) {
380
-            $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
381
-                $registrations_requiring_pre_approval
382
-            );
383
-        }
384
-        if (! empty($registrations_for_free_events)) {
385
-            $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
386
-        }
387
-        if (! empty($registrations_requiring_payment)) {
388
-            if ($this->checkout->amount_owing > 0) {
389
-                // autoload Line_Item_Display classes
390
-                EEH_Autoloader::register_line_item_filter_autoloaders();
391
-                $line_item_filter_processor = new EE_Line_Item_Filter_Processor(
392
-                    apply_filters(
393
-                        'FHEE__SPCO__EE_Line_Item_Filter_Collection',
394
-                        new EE_Line_Item_Filter_Collection()
395
-                    ),
396
-                    $this->checkout->cart->get_grand_total()
397
-                );
398
-                /** @var EE_Line_Item $filtered_line_item_tree */
399
-                $filtered_line_item_tree = $line_item_filter_processor->process();
400
-                EEH_Autoloader::register_line_item_display_autoloaders();
401
-                $this->set_line_item_display(new EE_Line_Item_Display('spco'));
402
-                $subsections['payment_options'] = $this->_display_payment_options(
403
-                    $this->line_item_display->display_line_item(
404
-                        $filtered_line_item_tree,
405
-                        ['registrations' => $registrations]
406
-                    )
407
-                );
408
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
409
-                $this->_apply_registration_payments_to_amount_owing($registrations);
410
-            }
411
-            $no_payment_required = false;
412
-        } else {
413
-            $this->_hide_reg_step_submit_button_if_revisit();
414
-        }
415
-        $this->_save_selected_method_of_payment();
416
-
417
-        $subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
418
-        $subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
419
-
420
-        return new EE_Form_Section_Proper(
421
-            [
422
-                'name'            => $this->reg_form_name(),
423
-                'html_id'         => $this->reg_form_name(),
424
-                'subsections'     => $subsections,
425
-                'layout_strategy' => new EE_No_Layout(),
426
-            ]
427
-        );
428
-    }
429
-
430
-
431
-    /**
432
-     * add line item filters required for this reg step
433
-     * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
434
-     *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
435
-     *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
436
-     *        payment options reg step, can apply these filters via the following: apply_filters(
437
-     *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
438
-     *        filter collection by passing that instead of instantiating a new collection
439
-     *
440
-     * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
441
-     * @return EE_Line_Item_Filter_Collection
442
-     * @throws EE_Error
443
-     * @throws InvalidArgumentException
444
-     * @throws ReflectionException
445
-     * @throws EntityNotFoundException
446
-     * @throws InvalidDataTypeException
447
-     * @throws InvalidInterfaceException
448
-     * @throws InvalidStatusException
449
-     */
450
-    public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
451
-    {
452
-        if (! EE_Registry::instance()->SSN instanceof EE_Session
453
-            || ! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout
454
-            || ! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction
455
-        ) {
456
-            return $line_item_filter_collection;
457
-        }
458
-        $line_item_filter_collection->add(
459
-            new EE_Billable_Line_Item_Filter(
460
-                EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
461
-                    EE_Registry::instance()->SSN->checkout()->transaction->registrations(
462
-                        EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
463
-                    )
464
-                )
465
-            )
466
-        );
467
-        $line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
468
-        return $line_item_filter_collection;
469
-    }
470
-
471
-
472
-    /**
473
-     * remove_ejected_registrations
474
-     * if a registrant has lost their potential space at an event due to lack of payment,
475
-     * then this method removes them from the list of registrations being paid for during this request
476
-     *
477
-     * @param EE_Registration[] $registrations
478
-     * @return EE_Registration[]
479
-     * @throws EE_Error
480
-     * @throws InvalidArgumentException
481
-     * @throws ReflectionException
482
-     * @throws EntityNotFoundException
483
-     * @throws InvalidDataTypeException
484
-     * @throws InvalidInterfaceException
485
-     * @throws InvalidStatusException
486
-     */
487
-    public static function remove_ejected_registrations(array $registrations)
488
-    {
489
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
490
-            $registrations,
491
-            EE_Registry::instance()->SSN->checkout()->revisit
492
-        );
493
-        foreach ($registrations as $REG_ID => $registration) {
494
-            // has this registration lost it's space ?
495
-            if (isset($ejected_registrations[ $REG_ID ])) {
496
-                unset($registrations[ $REG_ID ]);
497
-            }
498
-        }
499
-        return $registrations;
500
-    }
501
-
502
-
503
-    /**
504
-     * find_registrations_that_lost_their_space
505
-     * If a registrant chooses an offline payment method like Invoice,
506
-     * then no space is reserved for them at the event until they fully pay fo that site
507
-     * (unless the event's default reg status is set to APPROVED)
508
-     * if a registrant then later returns to pay, but the number of spaces available has been reduced due to sales,
509
-     * then this method will determine which registrations have lost the ability to complete the reg process.
510
-     *
511
-     * @param EE_Registration[] $registrations
512
-     * @param bool              $revisit
513
-     * @return array
514
-     * @throws EE_Error
515
-     * @throws InvalidArgumentException
516
-     * @throws ReflectionException
517
-     * @throws EntityNotFoundException
518
-     * @throws InvalidDataTypeException
519
-     * @throws InvalidInterfaceException
520
-     * @throws InvalidStatusException
521
-     */
522
-    public static function find_registrations_that_lost_their_space(array $registrations, $revisit = false)
523
-    {
524
-        // registrations per event
525
-        $event_reg_count = [];
526
-        // spaces left per event
527
-        $event_spaces_remaining = [];
528
-        // tickets left sorted by ID
529
-        $tickets_remaining = [];
530
-        // registrations that have lost their space
531
-        $ejected_registrations = [];
532
-        foreach ($registrations as $REG_ID => $registration) {
533
-            if (
534
-                $registration->status_ID() === EEM_Registration::status_id_approved
535
-                || apply_filters(
536
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__find_registrations_that_lost_their_space__allow_reg_payment',
537
-                    false,
538
-                    $registration,
539
-                    $revisit
540
-                )
541
-            ) {
542
-                continue;
543
-            }
544
-            $EVT_ID = $registration->event_ID();
545
-            $ticket = $registration->ticket();
546
-            if (! isset($tickets_remaining[ $ticket->ID() ])) {
547
-                $tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
548
-            }
549
-            if ($tickets_remaining[ $ticket->ID() ] > 0) {
550
-                if (! isset($event_reg_count[ $EVT_ID ])) {
551
-                    $event_reg_count[ $EVT_ID ] = 0;
552
-                }
553
-                $event_reg_count[ $EVT_ID ]++;
554
-                if (! isset($event_spaces_remaining[ $EVT_ID ])) {
555
-                    $event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
556
-                }
557
-            }
558
-            if (
559
-                $revisit
560
-                && ($tickets_remaining[ $ticket->ID() ] === 0
561
-                    || $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
562
-                )
563
-            ) {
564
-                $ejected_registrations[ $REG_ID ] = $registration->event();
565
-                if ($registration->status_ID() !== EEM_Registration::status_id_wait_list) {
566
-                    /** @type EE_Registration_Processor $registration_processor */
567
-                    $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
568
-                    // at this point, we should have enough details about the registrant to consider the registration
569
-                    // NOT incomplete
570
-                    $registration_processor->manually_update_registration_status(
571
-                        $registration,
572
-                        EEM_Registration::status_id_wait_list
573
-                    );
574
-                }
575
-            }
576
-        }
577
-        return $ejected_registrations;
578
-    }
579
-
580
-
581
-    /**
582
-     * _hide_reg_step_submit_button
583
-     * removes the html for the reg step submit button
584
-     * by replacing it with an empty string via filter callback
585
-     *
586
-     * @return void
587
-     */
588
-    protected function _adjust_registration_status_if_event_old_sold()
589
-    {
590
-    }
591
-
592
-
593
-    /**
594
-     * _hide_reg_step_submit_button
595
-     * removes the html for the reg step submit button
596
-     * by replacing it with an empty string via filter callback
597
-     *
598
-     * @return void
599
-     */
600
-    protected function _hide_reg_step_submit_button_if_revisit()
601
-    {
602
-        if ($this->checkout->revisit) {
603
-            add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
604
-        }
605
-    }
606
-
607
-
608
-    /**
609
-     * sold_out_events
610
-     * displays notices regarding events that have sold out since hte registrant first signed up
611
-     *
612
-     * @param EE_Event[] $sold_out_events_array
613
-     * @return EE_Form_Section_Proper
614
-     * @throws EE_Error
615
-     */
616
-    private function _sold_out_events($sold_out_events_array = [])
617
-    {
618
-        // set some defaults
619
-        $this->checkout->selected_method_of_payment = 'events_sold_out';
620
-        $sold_out_events                            = '';
621
-        foreach ($sold_out_events_array as $sold_out_event) {
622
-            $sold_out_events .= EEH_HTML::li(
623
-                EEH_HTML::span(
624
-                    '  ' . $sold_out_event->name(),
625
-                    '',
626
-                    'dashicons dashicons-marker ee-icon-size-16 pink-text'
627
-                )
628
-            );
629
-        }
630
-        return new EE_Form_Section_Proper(
631
-            [
632
-                'layout_strategy' => new EE_Template_Layout(
633
-                    [
634
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
635
-                                                  . $this->_slug
636
-                                                  . '/sold_out_events.template.php',
637
-                        'template_args'        => apply_filters(
638
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
639
-                            [
640
-                                'sold_out_events'     => $sold_out_events,
641
-                                'sold_out_events_msg' => apply_filters(
642
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
643
-                                    sprintf(
644
-                                        esc_html__(
645
-                                            'It appears that the event you were about to make a payment for has sold out since you first registered. If you have already made a partial payment towards this event, please contact the event administrator for a refund.%3$s%3$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%2$s',
646
-                                            'event_espresso'
647
-                                        ),
648
-                                        '<strong>',
649
-                                        '</strong>',
650
-                                        '<br />'
651
-                                    )
652
-                                ),
653
-                            ]
654
-                        ),
655
-                    ]
656
-                ),
657
-            ]
658
-        );
659
-    }
660
-
661
-
662
-    /**
663
-     * _insufficient_spaces_available
664
-     * displays notices regarding events that do not have enough remaining spaces
665
-     * to satisfy the current number of registrations looking to pay
666
-     *
667
-     * @param EE_Event[] $insufficient_spaces_events_array
668
-     * @return EE_Form_Section_Proper
669
-     * @throws EE_Error
670
-     * @throws ReflectionException
671
-     */
672
-    private function _insufficient_spaces_available($insufficient_spaces_events_array = [])
673
-    {
674
-        // set some defaults
675
-        $this->checkout->selected_method_of_payment = 'invoice';
676
-        $insufficient_space_events                  = '';
677
-        foreach ($insufficient_spaces_events_array as $event) {
678
-            if ($event instanceof EE_Event) {
679
-                $insufficient_space_events .= EEH_HTML::li(
680
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
681
-                );
682
-            }
683
-        }
684
-        return new EE_Form_Section_Proper(
685
-            [
686
-                'subsections'     => [
687
-                    'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
688
-                    'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
689
-                ],
690
-                'layout_strategy' => new EE_Template_Layout(
691
-                    [
692
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
693
-                                                  . $this->_slug
694
-                                                  . '/sold_out_events.template.php',
695
-                        'template_args'        => apply_filters(
696
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
697
-                            [
698
-                                'sold_out_events'     => $insufficient_space_events,
699
-                                'sold_out_events_msg' => apply_filters(
700
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
701
-                                    esc_html__(
702
-                                        'It appears that the event you were about to make a payment for has sold additional tickets since you first registered, and there are no longer enough spaces left to accommodate your selections. You may continue to pay and secure the available space(s) remaining, or simply cancel if you no longer wish to purchase. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
703
-                                        'event_espresso'
704
-                                    )
705
-                                ),
706
-                            ]
707
-                        ),
708
-                    ]
709
-                ),
710
-            ]
711
-        );
712
-    }
713
-
714
-
715
-    /**
716
-     * registrations_requiring_pre_approval
717
-     *
718
-     * @param array $registrations_requiring_pre_approval
719
-     * @return EE_Form_Section_Proper
720
-     * @throws EE_Error
721
-     * @throws EntityNotFoundException
722
-     * @throws ReflectionException
723
-     */
724
-    private function _registrations_requiring_pre_approval($registrations_requiring_pre_approval = [])
725
-    {
726
-        $events_requiring_pre_approval = [];
727
-        foreach ($registrations_requiring_pre_approval as $registration) {
728
-            if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
729
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
730
-                    EEH_HTML::span(
731
-                        '',
732
-                        '',
733
-                        'dashicons dashicons-marker ee-icon-size-16 orange-text'
734
-                    )
735
-                    . EEH_HTML::span($registration->event()->name(), '', 'orange-text')
736
-                );
737
-            }
738
-        }
739
-        return new EE_Form_Section_Proper(
740
-            [
741
-                'layout_strategy' => new EE_Template_Layout(
742
-                    [
743
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
744
-                                                  . $this->_slug
745
-                                                  . '/events_requiring_pre_approval.template.php', // layout_template
746
-                        'template_args'        => apply_filters(
747
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
748
-                            [
749
-                                'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
750
-                                'events_requiring_pre_approval_msg' => apply_filters(
751
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
752
-                                    esc_html__(
753
-                                        'The following events do not require payment at this time and will not be billed during this transaction. Billing will only occur after the attendee has been approved by the event organizer. You will be notified when your registration has been processed. If this is a free event, then no billing will occur.',
754
-                                        'event_espresso'
755
-                                    )
756
-                                ),
757
-                            ]
758
-                        ),
759
-                    ]
760
-                ),
761
-            ]
762
-        );
763
-    }
764
-
765
-
766
-    /**
767
-     * _no_payment_required
768
-     *
769
-     * @param EE_Event[] $registrations_for_free_events
770
-     * @return EE_Form_Section_Proper
771
-     * @throws EE_Error
772
-     */
773
-    private function _no_payment_required($registrations_for_free_events = [])
774
-    {
775
-        // set some defaults
776
-        $this->checkout->selected_method_of_payment = 'no_payment_required';
777
-        // generate no_payment_required form
778
-        return new EE_Form_Section_Proper(
779
-            [
780
-                'layout_strategy' => new EE_Template_Layout(
781
-                    [
782
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
783
-                                                  . $this->_slug
784
-                                                  . '/no_payment_required.template.php', // layout_template
785
-                        'template_args'        => apply_filters(
786
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
787
-                            [
788
-                                'revisit'                       => $this->checkout->revisit,
789
-                                'registrations'                 => [],
790
-                                'ticket_count'                  => [],
791
-                                'registrations_for_free_events' => $registrations_for_free_events,
792
-                                'no_payment_required_msg'       => EEH_HTML::p(
793
-                                    esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
794
-                                ),
795
-                            ]
796
-                        ),
797
-                    ]
798
-                ),
799
-            ]
800
-        );
801
-    }
802
-
803
-
804
-    /**
805
-     * _display_payment_options
806
-     *
807
-     * @param string $transaction_details
808
-     * @return EE_Form_Section_Proper
809
-     * @throws EE_Error
810
-     * @throws InvalidArgumentException
811
-     * @throws InvalidDataTypeException
812
-     * @throws InvalidInterfaceException
813
-     */
814
-    private function _display_payment_options($transaction_details = '')
815
-    {
816
-        // has method_of_payment been set by no-js user?
817
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
818
-        // build payment options form
819
-        return apply_filters(
820
-            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
821
-            new EE_Form_Section_Proper(
822
-                [
823
-                    'subsections'     => [
824
-                        'before_payment_options' => apply_filters(
825
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
826
-                            new EE_Form_Section_Proper(
827
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
828
-                            )
829
-                        ),
830
-                        'payment_options'        => $this->_setup_payment_options(),
831
-                        'after_payment_options'  => apply_filters(
832
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
833
-                            new EE_Form_Section_Proper(
834
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
835
-                            )
836
-                        ),
837
-                    ],
838
-                    'layout_strategy' => new EE_Template_Layout(
839
-                        [
840
-                            'layout_template_file' => $this->_template,
841
-                            'template_args'        => apply_filters(
842
-                                'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
843
-                                [
844
-                                    'reg_count'                 => $this->line_item_display->total_items(),
845
-                                    'transaction_details'       => $transaction_details,
846
-                                    'available_payment_methods' => [],
847
-                                ]
848
-                            ),
849
-                        ]
850
-                    ),
851
-                ]
852
-            )
853
-        );
854
-    }
855
-
856
-
857
-    /**
858
-     * _extra_hidden_inputs
859
-     *
860
-     * @param bool $no_payment_required
861
-     * @return EE_Form_Section_Proper
862
-     * @throws EE_Error
863
-     * @throws ReflectionException
864
-     */
865
-    private function _extra_hidden_inputs($no_payment_required = true)
866
-    {
867
-        return new EE_Form_Section_Proper(
868
-            [
869
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
870
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
871
-                'subsections'     => [
872
-                    'spco_no_payment_required' => new EE_Hidden_Input(
873
-                        [
874
-                            'normalization_strategy' => new EE_Boolean_Normalization(),
875
-                            'html_name'              => 'spco_no_payment_required',
876
-                            'html_id'                => 'spco-no-payment-required-payment_options',
877
-                            'default'                => $no_payment_required,
878
-                        ]
879
-                    ),
880
-                    'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
881
-                        [
882
-                            'normalization_strategy' => new EE_Int_Normalization(),
883
-                            'html_name'              => 'spco_transaction_id',
884
-                            'html_id'                => 'spco-transaction-id',
885
-                            'default'                => $this->checkout->transaction->ID(),
886
-                        ]
887
-                    ),
888
-                ],
889
-            ]
890
-        );
891
-    }
892
-
893
-
894
-    /**
895
-     *    _apply_registration_payments_to_amount_owing
896
-     *
897
-     * @param array $registrations
898
-     * @throws EE_Error|ReflectionException
899
-     */
900
-    protected function _apply_registration_payments_to_amount_owing(array $registrations)
901
-    {
902
-        $payments = [];
903
-        foreach ($registrations as $registration) {
904
-            if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
905
-                $payments += $registration->registration_payments();
906
-            }
907
-        }
908
-        if (! empty($payments)) {
909
-            foreach ($payments as $payment) {
910
-                if ($payment instanceof EE_Registration_Payment) {
911
-                    $this->checkout->amount_owing -= $payment->amount();
912
-                }
913
-            }
914
-        }
915
-    }
916
-
917
-
918
-    /**
919
-     *    _reset_selected_method_of_payment
920
-     *
921
-     * @access    private
922
-     * @param bool $force_reset
923
-     * @return void
924
-     * @throws InvalidArgumentException
925
-     * @throws InvalidDataTypeException
926
-     * @throws InvalidInterfaceException
927
-     */
928
-    private function _reset_selected_method_of_payment($force_reset = false)
929
-    {
930
-        /** @var RequestInterface $request */
931
-        $request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
932
-        $reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
933
-        if ($reset_payment_method) {
934
-            $this->checkout->selected_method_of_payment = null;
935
-            $this->checkout->payment_method             = null;
936
-            $this->checkout->billing_form               = null;
937
-            $this->_save_selected_method_of_payment();
938
-        }
939
-    }
940
-
941
-
942
-    /**
943
-     * _save_selected_method_of_payment
944
-     * stores the selected_method_of_payment in the session
945
-     * so that it's available for all subsequent requests including AJAX
946
-     *
947
-     * @access        private
948
-     * @param string $selected_method_of_payment
949
-     * @return void
950
-     * @throws InvalidArgumentException
951
-     * @throws InvalidDataTypeException
952
-     * @throws InvalidInterfaceException
953
-     */
954
-    private function _save_selected_method_of_payment($selected_method_of_payment = '')
955
-    {
956
-        $selected_method_of_payment = ! empty($selected_method_of_payment)
957
-            ? $selected_method_of_payment
958
-            : $this->checkout->selected_method_of_payment;
959
-        EE_Registry::instance()->SSN->set_session_data(
960
-            ['selected_method_of_payment' => $selected_method_of_payment]
961
-        );
962
-    }
963
-
964
-
965
-    /**
966
-     * _setup_payment_options
967
-     *
968
-     * @return EE_Form_Section_Proper
969
-     * @throws EE_Error
970
-     * @throws InvalidArgumentException
971
-     * @throws InvalidDataTypeException
972
-     * @throws InvalidInterfaceException
973
-     */
974
-    public function _setup_payment_options()
975
-    {
976
-        // load payment method classes
977
-        $this->checkout->available_payment_methods = $this->_get_available_payment_methods();
978
-        if (empty($this->checkout->available_payment_methods)) {
979
-            EE_Error::add_error(
980
-                apply_filters(
981
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
982
-                    sprintf(
983
-                        esc_html__(
984
-                            'Sorry, you cannot complete your purchase because a payment method is not active.%1$s Please contact %2$s for assistance and provide a description of the problem.',
985
-                            'event_espresso'
986
-                        ),
987
-                        '<br>',
988
-                        EE_Registry::instance()->CFG->organization->get_pretty('email')
989
-                    )
990
-                ),
991
-                __FILE__,
992
-                __FUNCTION__,
993
-                __LINE__
994
-            );
995
-        }
996
-        // switch up header depending on number of available payment methods
997
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
998
-            ? apply_filters(
999
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
1000
-                esc_html__('Please Select Your Method of Payment', 'event_espresso')
1001
-            )
1002
-            : apply_filters(
1003
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
1004
-                esc_html__('Method of Payment', 'event_espresso')
1005
-            );
1006
-        $available_payment_methods = [
1007
-            // display the "Payment Method" header
1008
-            'payment_method_header' => new EE_Form_Section_HTML(
1009
-                apply_filters(
1010
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
1011
-                    EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
1012
-                    $payment_method_header
1013
-                )
1014
-            ),
1015
-        ];
1016
-        // the list of actual payment methods ( invoice, paypal, etc ) in a  ( slug => HTML )  format
1017
-        $available_payment_method_options = [];
1018
-        $default_payment_method_option    = [];
1019
-        // additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
1020
-        $payment_methods_billing_info = [
1021
-            new EE_Form_Section_HTML(
1022
-                EEH_HTML::div('<br />', '', '', 'clear:both;')
1023
-            ),
1024
-        ];
1025
-        // loop through payment methods
1026
-        foreach ($this->checkout->available_payment_methods as $payment_method) {
1027
-            if ($payment_method instanceof EE_Payment_Method) {
1028
-                $payment_method_button = EEH_HTML::img(
1029
-                    $payment_method->button_url(),
1030
-                    $payment_method->name(),
1031
-                    'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1032
-                    'spco-payment-method-btn-img'
1033
-                );
1034
-                // check if any payment methods are set as default
1035
-                // if payment method is already selected OR nothing is selected and this payment method should be
1036
-                // open_by_default
1037
-                if (
1038
-                    ($this->checkout->selected_method_of_payment === $payment_method->slug())
1039
-                    || (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1040
-                ) {
1041
-                    $this->checkout->selected_method_of_payment = $payment_method->slug();
1042
-                    $this->_save_selected_method_of_payment();
1043
-                    $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1044
-                } else {
1045
-                    $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1046
-                }
1047
-                $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1048
-                    $this->_payment_method_billing_info(
1049
-                        $payment_method
1050
-                    );
1051
-            }
1052
-        }
1053
-        // prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1054
-        // of PMs
1055
-        $available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1056
-        // now generate the actual form  inputs
1057
-        $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1058
-            $available_payment_method_options
1059
-        );
1060
-        $available_payment_methods                              += $payment_methods_billing_info;
1061
-        // build the available payment methods form
1062
-        return new EE_Form_Section_Proper(
1063
-            [
1064
-                'html_id'         => 'spco-available-methods-of-payment-dv',
1065
-                'subsections'     => $available_payment_methods,
1066
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1067
-            ]
1068
-        );
1069
-    }
1070
-
1071
-
1072
-    /**
1073
-     * _get_available_payment_methods
1074
-     *
1075
-     * @return EE_Payment_Method[]
1076
-     * @throws EE_Error
1077
-     * @throws InvalidArgumentException
1078
-     * @throws InvalidDataTypeException
1079
-     * @throws InvalidInterfaceException
1080
-     */
1081
-    protected function _get_available_payment_methods()
1082
-    {
1083
-        if (! empty($this->checkout->available_payment_methods)) {
1084
-            return $this->checkout->available_payment_methods;
1085
-        }
1086
-        $available_payment_methods = [];
1087
-        $EEM_Payment_Method        = EEM_Payment_Method::instance();
1088
-        // get all active payment methods
1089
-        $payment_methods = $EEM_Payment_Method->get_all_for_transaction(
1090
-            $this->checkout->transaction,
1091
-            EEM_Payment_Method::scope_cart
1092
-        );
1093
-        foreach ($payment_methods as $payment_method) {
1094
-            if ($payment_method instanceof EE_Payment_Method) {
1095
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1096
-            }
1097
-        }
1098
-        return $available_payment_methods;
1099
-    }
1100
-
1101
-
1102
-    /**
1103
-     *    _available_payment_method_inputs
1104
-     *
1105
-     * @access    private
1106
-     * @param array $available_payment_method_options
1107
-     * @return    EE_Form_Section_Proper
1108
-     * @throws EE_Error
1109
-     * @throws EE_Error
1110
-     */
1111
-    private function _available_payment_method_inputs($available_payment_method_options = [])
1112
-    {
1113
-        // generate inputs
1114
-        return new EE_Form_Section_Proper(
1115
-            [
1116
-                'html_id'         => 'ee-available-payment-method-inputs',
1117
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1118
-                'subsections'     => [
1119
-                    '' => new EE_Radio_Button_Input(
1120
-                        $available_payment_method_options,
1121
-                        [
1122
-                            'html_name'          => 'selected_method_of_payment',
1123
-                            'html_class'         => 'spco-payment-method',
1124
-                            'default'            => $this->checkout->selected_method_of_payment,
1125
-                            'label_size'         => 11,
1126
-                            'enforce_label_size' => true,
1127
-                        ]
1128
-                    ),
1129
-                ],
1130
-            ]
1131
-        );
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     *    _payment_method_billing_info
1137
-     *
1138
-     * @access    private
1139
-     * @param EE_Payment_Method $payment_method
1140
-     * @return EE_Form_Section_Proper
1141
-     * @throws EE_Error
1142
-     * @throws InvalidArgumentException
1143
-     * @throws InvalidDataTypeException
1144
-     * @throws InvalidInterfaceException
1145
-     */
1146
-    private function _payment_method_billing_info(EE_Payment_Method $payment_method)
1147
-    {
1148
-        $currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1149
-        // generate the billing form for payment method
1150
-        $billing_form                 = $currently_selected
1151
-            ? $this->_get_billing_form_for_payment_method($payment_method)
1152
-            : new EE_Form_Section_HTML();
1153
-        $this->checkout->billing_form = $currently_selected
1154
-            ? $billing_form
1155
-            : $this->checkout->billing_form;
1156
-        // it's all in the details
1157
-        $info_html = EEH_HTML::h3(
1158
-            esc_html__('Important information regarding your payment', 'event_espresso'),
1159
-            '',
1160
-            'spco-payment-method-hdr'
1161
-        );
1162
-        // add some info regarding the step, either from what's saved in the admin,
1163
-        // or a default string depending on whether the PM has a billing form or not
1164
-        if ($payment_method->description()) {
1165
-            $payment_method_info = $payment_method->description();
1166
-        } elseif ($billing_form instanceof EE_Billing_Info_Form) {
1167
-            $payment_method_info = sprintf(
1168
-                esc_html__(
1169
-                    'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1170
-                    'event_espresso'
1171
-                ),
1172
-                $this->submit_button_text()
1173
-            );
1174
-        } else {
1175
-            $payment_method_info = sprintf(
1176
-                esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1177
-                $this->submit_button_text()
1178
-            );
1179
-        }
1180
-        $info_html .= EEH_HTML::div(
1181
-            apply_filters(
1182
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1183
-                $payment_method_info
1184
-            ),
1185
-            '',
1186
-            'spco-payment-method-desc ee-attention'
1187
-        );
1188
-        return new EE_Form_Section_Proper(
1189
-            [
1190
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1191
-                'html_class'      => 'spco-payment-method-info-dv',
1192
-                // only display the selected or default PM
1193
-                'html_style'      => $currently_selected ? '' : 'display:none;',
1194
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1195
-                'subsections'     => [
1196
-                    'info'         => new EE_Form_Section_HTML($info_html),
1197
-                    'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1198
-                ],
1199
-            ]
1200
-        );
1201
-    }
1202
-
1203
-
1204
-    /**
1205
-     * get_billing_form_html_for_payment_method
1206
-     *
1207
-     * @return bool
1208
-     * @throws EE_Error
1209
-     * @throws InvalidArgumentException
1210
-     * @throws ReflectionException
1211
-     * @throws InvalidDataTypeException
1212
-     * @throws InvalidInterfaceException
1213
-     */
1214
-    public function get_billing_form_html_for_payment_method()
1215
-    {
1216
-        // how have they chosen to pay?
1217
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1218
-        $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1219
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1220
-            return false;
1221
-        }
1222
-        if (
1223
-            apply_filters(
1224
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1225
-                false
1226
-            )
1227
-        ) {
1228
-            EE_Error::add_success(
1229
-                apply_filters(
1230
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1231
-                    sprintf(
1232
-                        esc_html__(
1233
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1234
-                            'event_espresso'
1235
-                        ),
1236
-                        $this->checkout->payment_method->name()
1237
-                    )
1238
-                )
1239
-            );
1240
-        }
1241
-        // now generate billing form for selected method of payment
1242
-        $payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1243
-        // fill form with attendee info if applicable
1244
-        if (
1245
-            $payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1246
-            && $this->checkout->transaction_has_primary_registrant()
1247
-        ) {
1248
-            $payment_method_billing_form->populate_from_attendee(
1249
-                $this->checkout->transaction->primary_registration()->attendee()
1250
-            );
1251
-        }
1252
-        // and debug content
1253
-        if (
1254
-            $payment_method_billing_form instanceof EE_Billing_Info_Form
1255
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1256
-        ) {
1257
-            $payment_method_billing_form =
1258
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1259
-                    $payment_method_billing_form
1260
-                );
1261
-        }
1262
-        $billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1263
-            ? $payment_method_billing_form->get_html()
1264
-            : '';
1265
-        $this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1266
-        // localize validation rules for main form
1267
-        $this->checkout->current_step->reg_form->localize_validation_rules();
1268
-        $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1269
-        return true;
1270
-    }
1271
-
1272
-
1273
-    /**
1274
-     * _get_billing_form_for_payment_method
1275
-     *
1276
-     * @param EE_Payment_Method $payment_method
1277
-     * @return EE_Billing_Info_Form|EE_Form_Section_HTML
1278
-     * @throws EE_Error
1279
-     * @throws InvalidArgumentException
1280
-     * @throws InvalidDataTypeException
1281
-     * @throws InvalidInterfaceException
1282
-     */
1283
-    private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1284
-    {
1285
-        $billing_form = $payment_method->type_obj()->billing_form(
1286
-            $this->checkout->transaction,
1287
-            ['amount_owing' => $this->checkout->amount_owing]
1288
-        );
1289
-        if ($billing_form instanceof EE_Billing_Info_Form) {
1290
-            if (
1291
-                apply_filters(
1292
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1293
-                    false
1294
-                )
1295
-                && $this->request->requestParamIsSet('payment_method')
1296
-            ) {
1297
-                EE_Error::add_success(
1298
-                    apply_filters(
1299
-                        'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1300
-                        sprintf(
1301
-                            esc_html__(
1302
-                                'You have selected "%s" as your method of payment. Please note the important payment information below.',
1303
-                                'event_espresso'
1304
-                            ),
1305
-                            $payment_method->name()
1306
-                        )
1307
-                    )
1308
-                );
1309
-            }
1310
-            return apply_filters(
1311
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1312
-                $billing_form,
1313
-                $payment_method
1314
-            );
1315
-        }
1316
-        // no actual billing form, so return empty HTML form section
1317
-        return new EE_Form_Section_HTML();
1318
-    }
1319
-
1320
-
1321
-    /**
1322
-     * _get_selected_method_of_payment
1323
-     *
1324
-     * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1325
-     *                          is not found in the incoming request
1326
-     * @param string  $request_param
1327
-     * @return NULL|string
1328
-     * @throws EE_Error
1329
-     * @throws InvalidArgumentException
1330
-     * @throws InvalidDataTypeException
1331
-     * @throws InvalidInterfaceException
1332
-     */
1333
-    private function _get_selected_method_of_payment(
1334
-        $required = false,
1335
-        $request_param = 'selected_method_of_payment'
1336
-    ) {
1337
-        // is selected_method_of_payment set in the request ?
1338
-        $selected_method_of_payment = $this->request->getRequestParam($request_param);
1339
-        if ($selected_method_of_payment) {
1340
-            // sanitize it
1341
-            $selected_method_of_payment = is_array($selected_method_of_payment)
1342
-                ? array_shift($selected_method_of_payment)
1343
-                : $selected_method_of_payment;
1344
-            $selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1345
-            // store it in the session so that it's available for all subsequent requests including AJAX
1346
-            $this->_save_selected_method_of_payment($selected_method_of_payment);
1347
-        } else {
1348
-            // or is is set in the session ?
1349
-            $selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1350
-                'selected_method_of_payment'
1351
-            );
1352
-        }
1353
-        // do ya really really gotta have it?
1354
-        if (empty($selected_method_of_payment) && $required) {
1355
-            EE_Error::add_error(
1356
-                sprintf(
1357
-                    esc_html__(
1358
-                        'The selected method of payment could not be determined.%sPlease ensure that you have selected one before proceeding.%sIf you continue to experience difficulties, then refresh your browser and try again, or contact %s for assistance.',
1359
-                        'event_espresso'
1360
-                    ),
1361
-                    '<br/>',
1362
-                    '<br/>',
1363
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
1364
-                ),
1365
-                __FILE__,
1366
-                __FUNCTION__,
1367
-                __LINE__
1368
-            );
1369
-            return null;
1370
-        }
1371
-        return $selected_method_of_payment;
1372
-    }
1373
-
1374
-
1375
-
1376
-
1377
-
1378
-
1379
-    /********************************************************************************************************/
1380
-    /***********************************  SWITCH PAYMENT METHOD  ************************************/
1381
-    /********************************************************************************************************/
1382
-    /**
1383
-     * switch_payment_method
1384
-     *
1385
-     * @return bool
1386
-     * @throws EE_Error
1387
-     * @throws InvalidArgumentException
1388
-     * @throws InvalidDataTypeException
1389
-     * @throws InvalidInterfaceException
1390
-     * @throws ReflectionException
1391
-     */
1392
-    public function switch_payment_method()
1393
-    {
1394
-        if (! $this->_verify_payment_method_is_set()) {
1395
-            return false;
1396
-        }
1397
-        if (
1398
-            apply_filters(
1399
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1400
-                false
1401
-            )
1402
-        ) {
1403
-            EE_Error::add_success(
1404
-                apply_filters(
1405
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1406
-                    sprintf(
1407
-                        esc_html__(
1408
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1409
-                            'event_espresso'
1410
-                        ),
1411
-                        $this->checkout->payment_method->name()
1412
-                    )
1413
-                )
1414
-            );
1415
-        }
1416
-        // generate billing form for selected method of payment if it hasn't been done already
1417
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1418
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1419
-                $this->checkout->payment_method
1420
-            );
1421
-        }
1422
-        // fill form with attendee info if applicable
1423
-        if (
1424
-            apply_filters(
1425
-                'FHEE__populate_billing_form_fields_from_attendee',
1426
-                (
1427
-                $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1428
-                && $this->checkout->transaction_has_primary_registrant()
1429
-                ),
1430
-                $this->checkout->billing_form,
1431
-                $this->checkout->transaction
1432
-            )
1433
-        ) {
1434
-            $this->checkout->billing_form->populate_from_attendee(
1435
-                $this->checkout->transaction->primary_registration()->attendee()
1436
-            );
1437
-        }
1438
-        // and debug content
1439
-        if (
1440
-            $this->checkout->billing_form instanceof EE_Billing_Info_Form
1441
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1442
-        ) {
1443
-            $this->checkout->billing_form =
1444
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1445
-                    $this->checkout->billing_form
1446
-                );
1447
-        }
1448
-        // get html and validation rules for form
1449
-        if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1450
-            $this->checkout->json_response->set_return_data(
1451
-                ['payment_method_info' => $this->checkout->billing_form->get_html()]
1452
-            );
1453
-            // localize validation rules for main form
1454
-            $this->checkout->billing_form->localize_validation_rules(true);
1455
-            $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1456
-        } else {
1457
-            $this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1458
-        }
1459
-        // prevents advancement to next step
1460
-        $this->checkout->continue_reg = false;
1461
-        return true;
1462
-    }
1463
-
1464
-
1465
-    /**
1466
-     * _verify_payment_method_is_set
1467
-     *
1468
-     * @return bool
1469
-     * @throws EE_Error
1470
-     * @throws InvalidArgumentException
1471
-     * @throws ReflectionException
1472
-     * @throws InvalidDataTypeException
1473
-     * @throws InvalidInterfaceException
1474
-     */
1475
-    protected function _verify_payment_method_is_set()
1476
-    {
1477
-        // generate billing form for selected method of payment if it hasn't been done already
1478
-        if (empty($this->checkout->selected_method_of_payment)) {
1479
-            // how have they chosen to pay?
1480
-            $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1481
-        } else {
1482
-            // choose your own adventure based on method_of_payment
1483
-            switch ($this->checkout->selected_method_of_payment) {
1484
-                case 'events_sold_out':
1485
-                    EE_Error::add_attention(
1486
-                        apply_filters(
1487
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1488
-                            esc_html__(
1489
-                                'It appears that the event you were about to make a payment for has sold out since this form first loaded. Please contact the event administrator if you believe this is an error.',
1490
-                                'event_espresso'
1491
-                            )
1492
-                        ),
1493
-                        __FILE__,
1494
-                        __FUNCTION__,
1495
-                        __LINE__
1496
-                    );
1497
-                    return false;
1498
-                case 'payments_closed':
1499
-                    EE_Error::add_attention(
1500
-                        apply_filters(
1501
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1502
-                            esc_html__(
1503
-                                'It appears that the event you were about to make a payment for is not accepting payments at this time. Please contact the event administrator if you believe this is an error.',
1504
-                                'event_espresso'
1505
-                            )
1506
-                        ),
1507
-                        __FILE__,
1508
-                        __FUNCTION__,
1509
-                        __LINE__
1510
-                    );
1511
-                    return false;
1512
-                case 'no_payment_required':
1513
-                    EE_Error::add_attention(
1514
-                        apply_filters(
1515
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1516
-                            esc_html__(
1517
-                                'It appears that the event you were about to make a payment for does not require payment. Please contact the event administrator if you believe this is an error.',
1518
-                                'event_espresso'
1519
-                            )
1520
-                        ),
1521
-                        __FILE__,
1522
-                        __FUNCTION__,
1523
-                        __LINE__
1524
-                    );
1525
-                    return false;
1526
-                default:
1527
-            }
1528
-        }
1529
-        // verify payment method
1530
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1531
-            // get payment method for selected method of payment
1532
-            $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1533
-        }
1534
-        return $this->checkout->payment_method instanceof EE_Payment_Method;
1535
-    }
1536
-
1537
-
1538
-
1539
-    /********************************************************************************************************/
1540
-    /***************************************  SAVE PAYER DETAILS  ****************************************/
1541
-    /********************************************************************************************************/
1542
-    /**
1543
-     * save_payer_details_via_ajax
1544
-     *
1545
-     * @return void
1546
-     * @throws EE_Error
1547
-     * @throws InvalidArgumentException
1548
-     * @throws ReflectionException
1549
-     * @throws RuntimeException
1550
-     * @throws InvalidDataTypeException
1551
-     * @throws InvalidInterfaceException
1552
-     */
1553
-    public function save_payer_details_via_ajax()
1554
-    {
1555
-        if (! $this->_verify_payment_method_is_set()) {
1556
-            return;
1557
-        }
1558
-        // generate billing form for selected method of payment if it hasn't been done already
1559
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1560
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1561
-                $this->checkout->payment_method
1562
-            );
1563
-        }
1564
-        // generate primary attendee from payer info if applicable
1565
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1566
-            $attendee = $this->_create_attendee_from_request_data();
1567
-            if ($attendee instanceof EE_Attendee) {
1568
-                foreach ($this->checkout->transaction->registrations() as $registration) {
1569
-                    if ($registration->is_primary_registrant()) {
1570
-                        $this->checkout->primary_attendee_obj = $attendee;
1571
-                        $registration->_add_relation_to($attendee, 'Attendee');
1572
-                        $registration->set_attendee_id($attendee->ID());
1573
-                        $registration->update_cache_after_object_save('Attendee', $attendee);
1574
-                    }
1575
-                }
1576
-            }
1577
-        }
1578
-    }
1579
-
1580
-
1581
-    /**
1582
-     * create_attendee_from_request_data
1583
-     * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1584
-     *
1585
-     * @return EE_Attendee
1586
-     * @throws EE_Error
1587
-     * @throws InvalidArgumentException
1588
-     * @throws ReflectionException
1589
-     * @throws InvalidDataTypeException
1590
-     * @throws InvalidInterfaceException
1591
-     */
1592
-    protected function _create_attendee_from_request_data()
1593
-    {
1594
-        // get State ID
1595
-        $STA_ID = $this->request->getRequestParam('state');
1596
-        if (! empty($STA_ID)) {
1597
-            // can we get state object from name ?
1598
-            EE_Registry::instance()->load_model('State');
1599
-            $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1600
-            $STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1601
-        }
1602
-        // get Country ISO
1603
-        $CNT_ISO = $this->request->getRequestParam('country');
1604
-        if (! empty($CNT_ISO)) {
1605
-            // can we get country object from name ?
1606
-            EE_Registry::instance()->load_model('Country');
1607
-            $country = EEM_Country::instance()->get_col(
1608
-                [['CNT_name' => $CNT_ISO], 'limit' => 1],
1609
-                'CNT_ISO'
1610
-            );
1611
-            $CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1612
-        }
1613
-        // grab attendee data
1614
-        $attendee_data = [
1615
-            'ATT_fname'    => $this->request->getRequestParam('first_name'),
1616
-            'ATT_lname'    => $this->request->getRequestParam('last_name'),
1617
-            'ATT_email'    => $this->request->getRequestParam('email'),
1618
-            'ATT_address'  => $this->request->getRequestParam('address'),
1619
-            'ATT_address2' => $this->request->getRequestParam('address2'),
1620
-            'ATT_city'     => $this->request->getRequestParam('city'),
1621
-            'STA_ID'       => $STA_ID,
1622
-            'CNT_ISO'      => $CNT_ISO,
1623
-            'ATT_zip'      => $this->request->getRequestParam('zip'),
1624
-            'ATT_phone'    => $this->request->getRequestParam('phone'),
1625
-        ];
1626
-        // validate the email address since it is the most important piece of info
1627
-        if (empty($attendee_data['ATT_email'])) {
1628
-            EE_Error::add_error(
1629
-                esc_html__('An invalid email address was submitted.', 'event_espresso'),
1630
-                __FILE__,
1631
-                __FUNCTION__,
1632
-                __LINE__
1633
-            );
1634
-        }
1635
-        // does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1636
-        // AND email address
1637
-        if (
1638
-            ! empty($attendee_data['ATT_fname'])
1639
-            && ! empty($attendee_data['ATT_lname'])
1640
-            && ! empty($attendee_data['ATT_email'])
1641
-        ) {
1642
-            $existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1643
-                [
1644
-                    'ATT_fname' => $attendee_data['ATT_fname'],
1645
-                    'ATT_lname' => $attendee_data['ATT_lname'],
1646
-                    'ATT_email' => $attendee_data['ATT_email'],
1647
-                ]
1648
-            );
1649
-            if ($existing_attendee instanceof EE_Attendee) {
1650
-                return $existing_attendee;
1651
-            }
1652
-        }
1653
-        // no existing attendee? kk let's create a new one
1654
-        // kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1655
-        // don't exist
1656
-        $attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1657
-            ? $attendee_data['ATT_fname']
1658
-            : $attendee_data['ATT_email'];
1659
-        $attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1660
-            ? $attendee_data['ATT_lname']
1661
-            : $attendee_data['ATT_email'];
1662
-        return EE_Attendee::new_instance($attendee_data);
1663
-    }
1664
-
1665
-
1666
-
1667
-    /********************************************************************************************************/
1668
-    /****************************************  PROCESS REG STEP  *****************************************/
1669
-    /********************************************************************************************************/
1670
-    /**
1671
-     * process_reg_step
1672
-     *
1673
-     * @return bool
1674
-     * @throws EE_Error
1675
-     * @throws InvalidArgumentException
1676
-     * @throws ReflectionException
1677
-     * @throws EntityNotFoundException
1678
-     * @throws InvalidDataTypeException
1679
-     * @throws InvalidInterfaceException
1680
-     * @throws InvalidStatusException
1681
-     */
1682
-    public function process_reg_step()
1683
-    {
1684
-        // how have they chosen to pay?
1685
-        $this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1686
-            ? 'no_payment_required'
1687
-            : $this->_get_selected_method_of_payment(true);
1688
-        // choose your own adventure based on method_of_payment
1689
-        switch ($this->checkout->selected_method_of_payment) {
1690
-            case 'events_sold_out':
1691
-                $this->checkout->redirect     = true;
1692
-                $this->checkout->redirect_url = $this->checkout->cancel_page_url;
1693
-                $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1694
-                // mark this reg step as completed
1695
-                $this->set_completed();
1696
-                return false;
1697
-
1698
-            case 'payments_closed':
1699
-                if (
1700
-                    apply_filters(
1701
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1702
-                        false
1703
-                    )
1704
-                ) {
1705
-                    EE_Error::add_success(
1706
-                        esc_html__('no payment required at this time.', 'event_espresso'),
1707
-                        __FILE__,
1708
-                        __FUNCTION__,
1709
-                        __LINE__
1710
-                    );
1711
-                }
1712
-                // mark this reg step as completed
1713
-                $this->set_completed();
1714
-                return true;
1715
-
1716
-            case 'no_payment_required':
1717
-                if (
1718
-                    apply_filters(
1719
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1720
-                        false
1721
-                    )
1722
-                ) {
1723
-                    EE_Error::add_success(
1724
-                        esc_html__('no payment required.', 'event_espresso'),
1725
-                        __FILE__,
1726
-                        __FUNCTION__,
1727
-                        __LINE__
1728
-                    );
1729
-                }
1730
-                // mark this reg step as completed
1731
-                $this->set_completed();
1732
-                return true;
1733
-
1734
-            default:
1735
-                $registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1736
-                    EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1737
-                );
1738
-                $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1739
-                    $registrations,
1740
-                    EE_Registry::instance()->SSN->checkout()->revisit
1741
-                );
1742
-                // calculate difference between the two arrays
1743
-                $registrations = array_diff($registrations, $ejected_registrations);
1744
-                if (empty($registrations)) {
1745
-                    $this->_redirect_because_event_sold_out();
1746
-                    return false;
1747
-                }
1748
-                $payment = $this->_process_payment();
1749
-                if ($payment instanceof EE_Payment) {
1750
-                    $this->checkout->continue_reg = true;
1751
-                    $this->_maybe_set_completed($payment);
1752
-                } else {
1753
-                    $this->checkout->continue_reg = false;
1754
-                }
1755
-                return $payment instanceof EE_Payment;
1756
-        }
1757
-    }
1758
-
1759
-
1760
-    /**
1761
-     * _redirect_because_event_sold_out
1762
-     *
1763
-     * @return void
1764
-     */
1765
-    protected function _redirect_because_event_sold_out()
1766
-    {
1767
-        $this->checkout->continue_reg = false;
1768
-        // set redirect URL
1769
-        $this->checkout->redirect_url = add_query_arg(
1770
-            ['e_reg_url_link' => $this->checkout->reg_url_link],
1771
-            $this->checkout->current_step->reg_step_url()
1772
-        );
1773
-        $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1774
-    }
1775
-
1776
-
1777
-    /**
1778
-     * @param EE_Payment $payment
1779
-     * @return void
1780
-     * @throws EE_Error
1781
-     */
1782
-    protected function _maybe_set_completed(EE_Payment $payment)
1783
-    {
1784
-        // Do we need to redirect them? If so, there's more work to be done.
1785
-        if (! $payment->redirect_url()) {
1786
-            $this->set_completed();
1787
-        }
1788
-    }
1789
-
1790
-
1791
-    /**
1792
-     *    update_reg_step
1793
-     *    this is the final step after a user  revisits the site to retry a payment
1794
-     *
1795
-     * @return bool
1796
-     * @throws EE_Error
1797
-     * @throws InvalidArgumentException
1798
-     * @throws ReflectionException
1799
-     * @throws EntityNotFoundException
1800
-     * @throws InvalidDataTypeException
1801
-     * @throws InvalidInterfaceException
1802
-     * @throws InvalidStatusException
1803
-     */
1804
-    public function update_reg_step()
1805
-    {
1806
-        $success = true;
1807
-        // if payment required
1808
-        if ($this->checkout->transaction->total() > 0) {
1809
-            do_action(
1810
-                'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1811
-                $this->checkout->transaction
1812
-            );
1813
-            // attempt payment via payment method
1814
-            $success = $this->process_reg_step();
1815
-        }
1816
-        if ($success && ! $this->checkout->redirect) {
1817
-            $this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1818
-                $this->checkout->transaction->ID()
1819
-            );
1820
-            // set return URL
1821
-            $this->checkout->redirect_url = add_query_arg(
1822
-                ['e_reg_url_link' => $this->checkout->reg_url_link],
1823
-                $this->checkout->thank_you_page_url
1824
-            );
1825
-        }
1826
-        return $success;
1827
-    }
1828
-
1829
-
1830
-    /**
1831
-     * @return EE_Payment|null
1832
-     * @throws EE_Error
1833
-     * @throws InvalidArgumentException
1834
-     * @throws ReflectionException
1835
-     * @throws RuntimeException
1836
-     * @throws InvalidDataTypeException
1837
-     * @throws InvalidInterfaceException
1838
-     */
1839
-    private function _process_payment()
1840
-    {
1841
-        // basically confirm that the event hasn't sold out since they hit the page
1842
-        if (! $this->_last_second_ticket_verifications()) {
1843
-            return null;
1844
-        }
1845
-        // ya gotta make a choice man
1846
-        if (empty($this->checkout->selected_method_of_payment)) {
1847
-            $this->checkout->json_response->set_plz_select_method_of_payment(
1848
-                esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1849
-            );
1850
-            return null;
1851
-        }
1852
-        // get EE_Payment_Method object
1853
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1854
-            return null;
1855
-        }
1856
-        // setup billing form
1857
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1858
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1859
-                $this->checkout->payment_method
1860
-            );
1861
-            // bad billing form ?
1862
-            if (! $this->_billing_form_is_valid()) {
1863
-                return null;
1864
-            }
1865
-        }
1866
-        // ensure primary registrant has been fully processed
1867
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1868
-            return null;
1869
-        }
1870
-        // if session is close to expiring (under 10 minutes by default)
1871
-        if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1872
-            // add some time to session expiration so that payment can be completed
1873
-            EE_Registry::instance()->SSN->extend_expiration();
1874
-        }
1875
-        /** @type EE_Transaction_Processor $transaction_processor */
1876
-        // $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1877
-        // in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1878
-        // for events with a default reg status of Approved
1879
-        // $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1880
-        //      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1881
-        // );
1882
-        // attempt payment
1883
-        $payment = $this->_attempt_payment($this->checkout->payment_method);
1884
-        // process results
1885
-        $payment = $this->_validate_payment($payment);
1886
-        $payment = $this->_post_payment_processing($payment);
1887
-        // verify payment
1888
-        if ($payment instanceof EE_Payment) {
1889
-            // store that for later
1890
-            $this->checkout->payment = $payment;
1891
-            // we can also consider the TXN to not have been failed, so temporarily upgrade it's status to abandoned
1892
-            $this->checkout->transaction->toggle_failed_transaction_status();
1893
-            $payment_status = $payment->status();
1894
-            if (
1895
-                $payment_status === EEM_Payment::status_id_approved
1896
-                || $payment_status === EEM_Payment::status_id_pending
1897
-            ) {
1898
-                return $payment;
1899
-            }
1900
-            return null;
1901
-        }
1902
-        if ($payment === true) {
1903
-            // please note that offline payment methods will NOT make a payment,
1904
-            // but instead just mark themselves as the PMD_ID on the transaction, and return true
1905
-            $this->checkout->payment = $payment;
1906
-            return $payment;
1907
-        }
1908
-        // where's my money?
1909
-        return null;
1910
-    }
1911
-
1912
-
1913
-    /**
1914
-     * _last_second_ticket_verifications
1915
-     *
1916
-     * @return bool
1917
-     * @throws EE_Error
1918
-     * @throws ReflectionException
1919
-     */
1920
-    protected function _last_second_ticket_verifications()
1921
-    {
1922
-        // don't bother re-validating if not a return visit
1923
-        if (! $this->checkout->revisit) {
1924
-            return true;
1925
-        }
1926
-        $registrations = $this->checkout->transaction->registrations();
1927
-        if (empty($registrations)) {
1928
-            return false;
1929
-        }
1930
-        foreach ($registrations as $registration) {
1931
-            if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1932
-                $event = $registration->event_obj();
1933
-                if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1934
-                    EE_Error::add_error(
1935
-                        apply_filters(
1936
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1937
-                            sprintf(
1938
-                                esc_html__(
1939
-                                    'It appears that the %1$s event that you were about to make a payment for has sold out since you first registered and/or arrived at this page. Please refresh the page and try again. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
1940
-                                    'event_espresso'
1941
-                                ),
1942
-                                $event->name()
1943
-                            )
1944
-                        ),
1945
-                        __FILE__,
1946
-                        __FUNCTION__,
1947
-                        __LINE__
1948
-                    );
1949
-                    return false;
1950
-                }
1951
-            }
1952
-        }
1953
-        return true;
1954
-    }
1955
-
1956
-
1957
-    /**
1958
-     * redirect_form
1959
-     *
1960
-     * @return bool
1961
-     * @throws EE_Error
1962
-     * @throws InvalidArgumentException
1963
-     * @throws ReflectionException
1964
-     * @throws InvalidDataTypeException
1965
-     * @throws InvalidInterfaceException
1966
-     */
1967
-    public function redirect_form()
1968
-    {
1969
-        $payment_method_billing_info = $this->_payment_method_billing_info(
1970
-            $this->_get_payment_method_for_selected_method_of_payment()
1971
-        );
1972
-        $html                        = $payment_method_billing_info->get_html();
1973
-        $html                        .= $this->checkout->redirect_form;
1974
-        /** @var ResponseInterface $response */
1975
-        $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1976
-        $response->addOutput($html);
1977
-        return true;
1978
-    }
1979
-
1980
-
1981
-    /**
1982
-     * _billing_form_is_valid
1983
-     *
1984
-     * @return bool
1985
-     * @throws EE_Error
1986
-     */
1987
-    private function _billing_form_is_valid()
1988
-    {
1989
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1990
-            return true;
1991
-        }
1992
-        if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1993
-            if ($this->checkout->billing_form->was_submitted()) {
1994
-                $this->checkout->billing_form->receive_form_submission();
1995
-                if ($this->checkout->billing_form->is_valid()) {
1996
-                    return true;
1997
-                }
1998
-                $validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
1999
-                $error_strings     = [];
2000
-                foreach ($validation_errors as $validation_error) {
2001
-                    if ($validation_error instanceof EE_Validation_Error) {
2002
-                        $form_section = $validation_error->get_form_section();
2003
-                        if ($form_section instanceof EE_Form_Input_Base) {
2004
-                            $label = $form_section->html_label_text();
2005
-                        } elseif ($form_section instanceof EE_Form_Section_Base) {
2006
-                            $label = $form_section->name();
2007
-                        } else {
2008
-                            $label = esc_html__('Validation Error', 'event_espresso');
2009
-                        }
2010
-                        $error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
2011
-                    }
2012
-                }
2013
-                EE_Error::add_error(
2014
-                    sprintf(
2015
-                        esc_html__(
2016
-                            'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
2017
-                            'event_espresso'
2018
-                        ),
2019
-                        '<br/>',
2020
-                        implode('<br/>', $error_strings)
2021
-                    ),
2022
-                    __FILE__,
2023
-                    __FUNCTION__,
2024
-                    __LINE__
2025
-                );
2026
-            } else {
2027
-                EE_Error::add_error(
2028
-                    esc_html__(
2029
-                        'The billing form was not submitted or something prevented it\'s submission.',
2030
-                        'event_espresso'
2031
-                    ),
2032
-                    __FILE__,
2033
-                    __FUNCTION__,
2034
-                    __LINE__
2035
-                );
2036
-            }
2037
-        } else {
2038
-            EE_Error::add_error(
2039
-                esc_html__(
2040
-                    'The submitted billing form is invalid possibly due to a technical reason.',
2041
-                    'event_espresso'
2042
-                ),
2043
-                __FILE__,
2044
-                __FUNCTION__,
2045
-                __LINE__
2046
-            );
2047
-        }
2048
-        return false;
2049
-    }
2050
-
2051
-
2052
-    /**
2053
-     * _setup_primary_registrant_prior_to_payment
2054
-     * ensures that the primary registrant has a valid attendee object created with the critical details populated
2055
-     * (first & last name & email) and that both the transaction object and primary registration object have been saved
2056
-     * plz note that any other registrations will NOT be saved at this point (because they may not have any details
2057
-     * yet)
2058
-     *
2059
-     * @return bool
2060
-     * @throws EE_Error
2061
-     * @throws InvalidArgumentException
2062
-     * @throws ReflectionException
2063
-     * @throws RuntimeException
2064
-     * @throws InvalidDataTypeException
2065
-     * @throws InvalidInterfaceException
2066
-     */
2067
-    private function _setup_primary_registrant_prior_to_payment()
2068
-    {
2069
-        // check if transaction has a primary registrant and that it has a related Attendee object
2070
-        // if not, then we need to at least gather some primary registrant data before attempting payment
2071
-        if (
2072
-            $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2073
-            && ! $this->checkout->transaction_has_primary_registrant()
2074
-            && ! $this->_capture_primary_registration_data_from_billing_form()
2075
-        ) {
2076
-            return false;
2077
-        }
2078
-        // because saving an object clears it's cache, we need to do the chevy shuffle
2079
-        // grab the primary_registration object
2080
-        $primary_registration = $this->checkout->transaction->primary_registration();
2081
-        // at this point we'll consider a TXN to not have been failed
2082
-        $this->checkout->transaction->toggle_failed_transaction_status();
2083
-        // save the TXN ( which clears cached copy of primary_registration)
2084
-        $this->checkout->transaction->save();
2085
-        // grab TXN ID and save it to the primary_registration
2086
-        $primary_registration->set_transaction_id($this->checkout->transaction->ID());
2087
-        // save what we have so far
2088
-        $primary_registration->save();
2089
-        return true;
2090
-    }
2091
-
2092
-
2093
-    /**
2094
-     * _capture_primary_registration_data_from_billing_form
2095
-     *
2096
-     * @return bool
2097
-     * @throws EE_Error
2098
-     * @throws InvalidArgumentException
2099
-     * @throws ReflectionException
2100
-     * @throws InvalidDataTypeException
2101
-     * @throws InvalidInterfaceException
2102
-     */
2103
-    private function _capture_primary_registration_data_from_billing_form()
2104
-    {
2105
-        // convert billing form data into an attendee
2106
-        $this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2107
-        if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2108
-            EE_Error::add_error(
2109
-                sprintf(
2110
-                    esc_html__(
2111
-                        'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2112
-                        'event_espresso'
2113
-                    ),
2114
-                    '<br/>',
2115
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2116
-                ),
2117
-                __FILE__,
2118
-                __FUNCTION__,
2119
-                __LINE__
2120
-            );
2121
-            return false;
2122
-        }
2123
-        $primary_registration = $this->checkout->transaction->primary_registration();
2124
-        if (! $primary_registration instanceof EE_Registration) {
2125
-            EE_Error::add_error(
2126
-                sprintf(
2127
-                    esc_html__(
2128
-                        'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2129
-                        'event_espresso'
2130
-                    ),
2131
-                    '<br/>',
2132
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2133
-                ),
2134
-                __FILE__,
2135
-                __FUNCTION__,
2136
-                __LINE__
2137
-            );
2138
-            return false;
2139
-        }
2140
-        if (
2141
-            ! $primary_registration->_add_relation_to($this->checkout->primary_attendee_obj, 'Attendee')
2142
-              instanceof
2143
-              EE_Attendee
2144
-        ) {
2145
-            EE_Error::add_error(
2146
-                sprintf(
2147
-                    esc_html__(
2148
-                        'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2149
-                        'event_espresso'
2150
-                    ),
2151
-                    '<br/>',
2152
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2153
-                ),
2154
-                __FILE__,
2155
-                __FUNCTION__,
2156
-                __LINE__
2157
-            );
2158
-            return false;
2159
-        }
2160
-        /** @type EE_Registration_Processor $registration_processor */
2161
-        $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2162
-        // at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2163
-        $registration_processor->toggle_incomplete_registration_status_to_default($primary_registration);
2164
-        return true;
2165
-    }
2166
-
2167
-
2168
-    /**
2169
-     * _get_payment_method_for_selected_method_of_payment
2170
-     * retrieves a valid payment method
2171
-     *
2172
-     * @return EE_Payment_Method
2173
-     * @throws EE_Error
2174
-     * @throws InvalidArgumentException
2175
-     * @throws ReflectionException
2176
-     * @throws InvalidDataTypeException
2177
-     * @throws InvalidInterfaceException
2178
-     */
2179
-    private function _get_payment_method_for_selected_method_of_payment()
2180
-    {
2181
-        if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2182
-            $this->_redirect_because_event_sold_out();
2183
-            return null;
2184
-        }
2185
-        // get EE_Payment_Method object
2186
-        if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2187
-            $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2188
-        } else {
2189
-            // load EEM_Payment_Method
2190
-            EE_Registry::instance()->load_model('Payment_Method');
2191
-            $EEM_Payment_Method = EEM_Payment_Method::instance();
2192
-            $payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2193
-        }
2194
-        // verify $payment_method
2195
-        if (! $payment_method instanceof EE_Payment_Method) {
2196
-            // not a payment
2197
-            EE_Error::add_error(
2198
-                sprintf(
2199
-                    esc_html__(
2200
-                        'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2201
-                        'event_espresso'
2202
-                    ),
2203
-                    '<br/>',
2204
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2205
-                ),
2206
-                __FILE__,
2207
-                __FUNCTION__,
2208
-                __LINE__
2209
-            );
2210
-            return null;
2211
-        }
2212
-        // and verify it has a valid Payment_Method Type object
2213
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2214
-            // not a payment
2215
-            EE_Error::add_error(
2216
-                sprintf(
2217
-                    esc_html__(
2218
-                        'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2219
-                        'event_espresso'
2220
-                    ),
2221
-                    '<br/>',
2222
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2223
-                ),
2224
-                __FILE__,
2225
-                __FUNCTION__,
2226
-                __LINE__
2227
-            );
2228
-            return null;
2229
-        }
2230
-        return $payment_method;
2231
-    }
2232
-
2233
-
2234
-    /**
2235
-     *    _attempt_payment
2236
-     *
2237
-     * @access    private
2238
-     * @type    EE_Payment_Method $payment_method
2239
-     * @return mixed EE_Payment | boolean
2240
-     * @throws EE_Error
2241
-     * @throws InvalidArgumentException
2242
-     * @throws ReflectionException
2243
-     * @throws InvalidDataTypeException
2244
-     * @throws InvalidInterfaceException
2245
-     */
2246
-    private function _attempt_payment(EE_Payment_Method $payment_method)
2247
-    {
2248
-        $payment = null;
2249
-        $this->checkout->transaction->save();
2250
-        $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2251
-        if (! $payment_processor instanceof EE_Payment_Processor) {
2252
-            return false;
2253
-        }
2254
-        try {
2255
-            $payment_processor->set_revisit($this->checkout->revisit);
2256
-            // generate payment object
2257
-            $payment = $payment_processor->process_payment(
2258
-                $payment_method,
2259
-                $this->checkout->transaction,
2260
-                $this->checkout->amount_owing,
2261
-                $this->checkout->billing_form,
2262
-                $this->_get_return_url($payment_method),
2263
-                'CART',
2264
-                $this->checkout->admin_request,
2265
-                true,
2266
-                $this->reg_step_url()
2267
-            );
2268
-        } catch (Exception $e) {
2269
-            $this->_handle_payment_processor_exception($e);
2270
-        }
2271
-        return $payment;
2272
-    }
2273
-
2274
-
2275
-    /**
2276
-     * _handle_payment_processor_exception
2277
-     *
2278
-     * @param Exception $e
2279
-     * @return void
2280
-     * @throws EE_Error
2281
-     * @throws InvalidArgumentException
2282
-     * @throws InvalidDataTypeException
2283
-     * @throws InvalidInterfaceException
2284
-     */
2285
-    protected function _handle_payment_processor_exception(Exception $e)
2286
-    {
2287
-        EE_Error::add_error(
2288
-            sprintf(
2289
-                esc_html__(
2290
-                    'The payment could not br processed due to a technical issue.%1$sPlease try again or contact %2$s for assistance.||The following Exception was thrown in %4$s on line %5$s:%1$s%3$s',
2291
-                    'event_espresso'
2292
-                ),
2293
-                '<br/>',
2294
-                EE_Registry::instance()->CFG->organization->get_pretty('email'),
2295
-                $e->getMessage(),
2296
-                $e->getFile(),
2297
-                $e->getLine()
2298
-            ),
2299
-            __FILE__,
2300
-            __FUNCTION__,
2301
-            __LINE__
2302
-        );
2303
-    }
2304
-
2305
-
2306
-    /**
2307
-     * _get_return_url
2308
-     *
2309
-     * @param EE_Payment_Method $payment_method
2310
-     * @return string
2311
-     * @throws EE_Error
2312
-     * @throws ReflectionException
2313
-     */
2314
-    protected function _get_return_url(EE_Payment_Method $payment_method)
2315
-    {
2316
-        $return_url = '';
2317
-        switch ($payment_method->type_obj()->payment_occurs()) {
2318
-            case EE_PMT_Base::offsite:
2319
-                $return_url = add_query_arg(
2320
-                    [
2321
-                        'action'                     => 'process_gateway_response',
2322
-                        'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2323
-                        'spco_txn'                   => $this->checkout->transaction->ID(),
2324
-                    ],
2325
-                    $this->reg_step_url()
2326
-                );
2327
-                break;
2328
-            case EE_PMT_Base::onsite:
2329
-            case EE_PMT_Base::offline:
2330
-                $return_url = $this->checkout->next_step->reg_step_url();
2331
-                break;
2332
-        }
2333
-        return $return_url;
2334
-    }
2335
-
2336
-
2337
-    /**
2338
-     * _validate_payment
2339
-     *
2340
-     * @param EE_Payment $payment
2341
-     * @return EE_Payment|FALSE
2342
-     * @throws EE_Error
2343
-     * @throws InvalidArgumentException
2344
-     * @throws InvalidDataTypeException
2345
-     * @throws InvalidInterfaceException
2346
-     */
2347
-    private function _validate_payment($payment = null)
2348
-    {
2349
-        if ($this->checkout->payment_method->is_off_line()) {
2350
-            return true;
2351
-        }
2352
-        // verify payment object
2353
-        if (! $payment instanceof EE_Payment) {
2354
-            // not a payment
2355
-            EE_Error::add_error(
2356
-                sprintf(
2357
-                    esc_html__(
2358
-                        'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2359
-                        'event_espresso'
2360
-                    ),
2361
-                    '<br/>',
2362
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2363
-                ),
2364
-                __FILE__,
2365
-                __FUNCTION__,
2366
-                __LINE__
2367
-            );
2368
-            return false;
2369
-        }
2370
-        return $payment;
2371
-    }
2372
-
2373
-
2374
-    /**
2375
-     * _post_payment_processing
2376
-     *
2377
-     * @param EE_Payment|bool $payment
2378
-     * @return bool|EE_Payment
2379
-     * @throws EE_Error
2380
-     * @throws InvalidArgumentException
2381
-     * @throws InvalidDataTypeException
2382
-     * @throws InvalidInterfaceException
2383
-     * @throws ReflectionException
2384
-     */
2385
-    private function _post_payment_processing($payment = null)
2386
-    {
2387
-        // Off-Line payment?
2388
-        if ($payment === true) {
2389
-            return true;
2390
-        }
2391
-        if ($payment instanceof EE_Payment) {
2392
-            // Should the user be redirected?
2393
-            if ($payment->redirect_url()) {
2394
-                do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2395
-                $this->checkout->redirect      = true;
2396
-                $this->checkout->redirect_form = $payment->redirect_form();
2397
-                $this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2398
-                // set JSON response
2399
-                $this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2400
-                // and lastly, let's bump the payment status to pending
2401
-                $payment->set_status(EEM_Payment::status_id_pending);
2402
-                $payment->save();
2403
-            } elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2404
-                // User shouldn't be redirected. So let's process it here.
2405
-                // $this->_setup_redirect_for_next_step();
2406
-                $this->checkout->continue_reg = false;
2407
-            }
2408
-            return $payment;
2409
-        }
2410
-        // ummm ya... not Off-Line, not On-Site, not off-Site ????
2411
-        $this->checkout->continue_reg = false;
2412
-        return false;
2413
-    }
2414
-
2415
-
2416
-    /**
2417
-     *    _process_payment_status
2418
-     *
2419
-     * @type    EE_Payment $payment
2420
-     * @param string       $payment_occurs
2421
-     * @return bool
2422
-     * @throws EE_Error
2423
-     * @throws InvalidArgumentException
2424
-     * @throws InvalidDataTypeException
2425
-     * @throws InvalidInterfaceException
2426
-     */
2427
-    private function _process_payment_status($payment, $payment_occurs = EE_PMT_Base::offline)
2428
-    {
2429
-        // off-line payment? carry on
2430
-        if ($payment_occurs === EE_PMT_Base::offline) {
2431
-            return true;
2432
-        }
2433
-        // verify payment validity
2434
-        if ($payment instanceof EE_Payment) {
2435
-            do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2436
-            $msg = $payment->gateway_response();
2437
-            // check results
2438
-            switch ($payment->status()) {
2439
-                // good payment
2440
-                case EEM_Payment::status_id_approved:
2441
-                    EE_Error::add_success(
2442
-                        esc_html__('Your payment was processed successfully.', 'event_espresso'),
2443
-                        __FILE__,
2444
-                        __FUNCTION__,
2445
-                        __LINE__
2446
-                    );
2447
-                    return true;
2448
-                // slow payment
2449
-                case EEM_Payment::status_id_pending:
2450
-                    if (empty($msg)) {
2451
-                        $msg = esc_html__(
2452
-                            'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2453
-                            'event_espresso'
2454
-                        );
2455
-                    }
2456
-                    EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2457
-                    return true;
2458
-                // don't wanna payment
2459
-                case EEM_Payment::status_id_cancelled:
2460
-                    if (empty($msg)) {
2461
-                        $msg = _n(
2462
-                            'Payment cancelled. Please try again.',
2463
-                            'Payment cancelled. Please try again or select another method of payment.',
2464
-                            count($this->checkout->available_payment_methods),
2465
-                            'event_espresso'
2466
-                        );
2467
-                    }
2468
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2469
-                    return false;
2470
-                // not enough payment
2471
-                case EEM_Payment::status_id_declined:
2472
-                    if (empty($msg)) {
2473
-                        $msg = _n(
2474
-                            'We\'re sorry but your payment was declined. Please try again.',
2475
-                            'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2476
-                            count($this->checkout->available_payment_methods),
2477
-                            'event_espresso'
2478
-                        );
2479
-                    }
2480
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2481
-                    return false;
2482
-                // bad payment
2483
-                case EEM_Payment::status_id_failed:
2484
-                    if (! empty($msg)) {
2485
-                        EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2486
-                        return false;
2487
-                    }
2488
-                    // default to error below
2489
-                    break;
2490
-            }
2491
-        }
2492
-        // off-site payment gateway responses are too unreliable, so let's just assume that
2493
-        // the payment processing is just running slower than the registrant's request
2494
-        if ($payment_occurs === EE_PMT_Base::offsite) {
2495
-            return true;
2496
-        }
2497
-        EE_Error::add_error(
2498
-            sprintf(
2499
-                esc_html__(
2500
-                    'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2501
-                    'event_espresso'
2502
-                ),
2503
-                '<br/>',
2504
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
2505
-            ),
2506
-            __FILE__,
2507
-            __FUNCTION__,
2508
-            __LINE__
2509
-        );
2510
-        return false;
2511
-    }
2512
-
2513
-
2514
-
2515
-
2516
-
2517
-
2518
-    /********************************************************************************************************/
2519
-    /**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2520
-    /********************************************************************************************************/
2521
-    /**
2522
-     * process_gateway_response
2523
-     * this is the return point for Off-Site Payment Methods
2524
-     * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2525
-     * otherwise, it will load up the last payment made for the TXN.
2526
-     * If the payment retrieved looks good, it will then either:
2527
-     *    complete the current step and allow advancement to the next reg step
2528
-     *        or present the payment options again
2529
-     *
2530
-     * @return bool
2531
-     * @throws EE_Error
2532
-     * @throws InvalidArgumentException
2533
-     * @throws ReflectionException
2534
-     * @throws InvalidDataTypeException
2535
-     * @throws InvalidInterfaceException
2536
-     */
2537
-    public function process_gateway_response()
2538
-    {
2539
-        // how have they chosen to pay?
2540
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2541
-        // get EE_Payment_Method object
2542
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2543
-            $this->checkout->continue_reg = false;
2544
-            return false;
2545
-        }
2546
-        if (! $this->checkout->payment_method->is_off_site()) {
2547
-            return false;
2548
-        }
2549
-        $this->_validate_offsite_return();
2550
-        // verify TXN
2551
-        if ($this->checkout->transaction instanceof EE_Transaction) {
2552
-            $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2553
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2554
-                $this->checkout->continue_reg = false;
2555
-                return false;
2556
-            }
2557
-            $payment = $this->_process_off_site_payment($gateway);
2558
-            $payment = $this->_process_cancelled_payments($payment);
2559
-            $payment = $this->_validate_payment($payment);
2560
-            // if payment was not declined by the payment gateway or cancelled by the registrant
2561
-            if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2562
-                // $this->_setup_redirect_for_next_step();
2563
-                // store that for later
2564
-                $this->checkout->payment = $payment;
2565
-                // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2566
-                // because we will complete this step during the IPN processing then
2567
-                if (! $this->handle_IPN_in_this_request()) {
2568
-                    $this->set_completed();
2569
-                }
2570
-                return true;
2571
-            }
2572
-        }
2573
-        // DEBUG LOG
2574
-        // $this->checkout->log(
2575
-        //     __CLASS__,
2576
-        //     __FUNCTION__,
2577
-        //     __LINE__,
2578
-        //     array('payment' => $payment)
2579
-        // );
2580
-        $this->checkout->continue_reg = false;
2581
-        return false;
2582
-    }
2583
-
2584
-
2585
-    /**
2586
-     * _validate_return
2587
-     *
2588
-     * @return void
2589
-     * @throws EE_Error
2590
-     * @throws InvalidArgumentException
2591
-     * @throws InvalidDataTypeException
2592
-     * @throws InvalidInterfaceException
2593
-     * @throws ReflectionException
2594
-     */
2595
-    private function _validate_offsite_return()
2596
-    {
2597
-        $TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2598
-        if ($TXN_ID !== $this->checkout->transaction->ID()) {
2599
-            // Houston... we might have a problem
2600
-            $invalid_TXN = false;
2601
-            // first gather some info
2602
-            $valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2603
-            $primary_registrant = $valid_TXN instanceof EE_Transaction
2604
-                ? $valid_TXN->primary_registration()
2605
-                : null;
2606
-            // let's start by retrieving the cart for this TXN
2607
-            $cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2608
-            if ($cart instanceof EE_Cart) {
2609
-                // verify that the current cart has tickets
2610
-                $tickets = $cart->get_tickets();
2611
-                if (empty($tickets)) {
2612
-                    $invalid_TXN = true;
2613
-                }
2614
-            } else {
2615
-                $invalid_TXN = true;
2616
-            }
2617
-            $valid_TXN_SID = $primary_registrant instanceof EE_Registration
2618
-                ? $primary_registrant->session_ID()
2619
-                : null;
2620
-            // validate current Session ID and compare against valid TXN session ID
2621
-            if (
2622
-                $invalid_TXN // if this is already true, then skip other checks
2623
-                || EE_Session::instance()->id() === null
2624
-                || (
2625
-                    // WARNING !!!
2626
-                    // this could be PayPal sending back duplicate requests (ya they do that)
2627
-                    // or it **could** mean someone is simply registering AGAIN after having just done so
2628
-                    // so now we need to determine if this current TXN looks valid or not
2629
-                    // and whether this reg step has even been started ?
2630
-                    EE_Session::instance()->id() === $valid_TXN_SID
2631
-                    // really? you're half way through this reg step, but you never started it ?
2632
-                    && $this->checkout->transaction->reg_step_completed($this->slug()) === false
2633
-                )
2634
-            ) {
2635
-                $invalid_TXN = true;
2636
-            }
2637
-            if ($invalid_TXN) {
2638
-                // is the valid TXN completed ?
2639
-                if ($valid_TXN instanceof EE_Transaction) {
2640
-                    // has this step even been started ?
2641
-                    $reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2642
-                    if ($reg_step_completed !== false && $reg_step_completed !== true) {
2643
-                        // so it **looks** like this is a double request from PayPal
2644
-                        // so let's try to pick up where we left off
2645
-                        $this->checkout->transaction = $valid_TXN;
2646
-                        $this->checkout->refresh_all_entities(true);
2647
-                        return;
2648
-                    }
2649
-                }
2650
-                // you appear to be lost?
2651
-                $this->_redirect_wayward_request($primary_registrant);
2652
-            }
2653
-        }
2654
-    }
2655
-
2656
-
2657
-    /**
2658
-     * _redirect_wayward_request
2659
-     *
2660
-     * @param EE_Registration|null $primary_registrant
2661
-     * @return void
2662
-     * @throws EE_Error
2663
-     * @throws InvalidArgumentException
2664
-     * @throws InvalidDataTypeException
2665
-     * @throws InvalidInterfaceException
2666
-     * @throws ReflectionException
2667
-     */
2668
-    private function _redirect_wayward_request(EE_Registration $primary_registrant)
2669
-    {
2670
-        if (! $primary_registrant instanceof EE_Registration) {
2671
-            // try redirecting based on the current TXN
2672
-            $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2673
-                ? $this->checkout->transaction->primary_registration()
2674
-                : null;
2675
-        }
2676
-        if (! $primary_registrant instanceof EE_Registration) {
2677
-            EE_Error::add_error(
2678
-                sprintf(
2679
-                    esc_html__(
2680
-                        'Invalid information was received from the Off-Site Payment Processor and your Transaction details could not be retrieved from the database.%1$sPlease try again or contact %2$s for assistance.',
2681
-                        'event_espresso'
2682
-                    ),
2683
-                    '<br/>',
2684
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2685
-                ),
2686
-                __FILE__,
2687
-                __FUNCTION__,
2688
-                __LINE__
2689
-            );
2690
-            return;
2691
-        }
2692
-        // make sure transaction is not locked
2693
-        $this->checkout->transaction->unlock();
2694
-        wp_safe_redirect(
2695
-            add_query_arg(
2696
-                [
2697
-                    'e_reg_url_link' => $primary_registrant->reg_url_link(),
2698
-                ],
2699
-                $this->checkout->thank_you_page_url
2700
-            )
2701
-        );
2702
-        exit();
2703
-    }
2704
-
2705
-
2706
-    /**
2707
-     * _process_off_site_payment
2708
-     *
2709
-     * @param EE_Offsite_Gateway $gateway
2710
-     * @return EE_Payment
2711
-     * @throws EE_Error
2712
-     * @throws InvalidArgumentException
2713
-     * @throws InvalidDataTypeException
2714
-     * @throws InvalidInterfaceException
2715
-     * @throws ReflectionException
2716
-     */
2717
-    private function _process_off_site_payment(EE_Offsite_Gateway $gateway)
2718
-    {
2719
-        try {
2720
-            $request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2721
-            $request_data = $request->requestParams();
2722
-            // if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2723
-            $this->set_handle_IPN_in_this_request(
2724
-                $gateway->handle_IPN_in_this_request($request_data, false)
2725
-            );
2726
-            if ($this->handle_IPN_in_this_request()) {
2727
-                // get payment details and process results
2728
-                /** @type EE_Payment_Processor $payment_processor */
2729
-                $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2730
-                $payment           = $payment_processor->process_ipn(
2731
-                    $request_data,
2732
-                    $this->checkout->transaction,
2733
-                    $this->checkout->payment_method,
2734
-                    true,
2735
-                    false
2736
-                );
2737
-                // $payment_source = 'process_ipn';
2738
-            } else {
2739
-                $payment = $this->checkout->transaction->last_payment();
2740
-                // $payment_source = 'last_payment';
2741
-            }
2742
-        } catch (Exception $e) {
2743
-            // let's just eat the exception and try to move on using any previously set payment info
2744
-            $payment = $this->checkout->transaction->last_payment();
2745
-            // $payment_source = 'last_payment after Exception';
2746
-            // but if we STILL don't have a payment object
2747
-            if (! $payment instanceof EE_Payment) {
2748
-                // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2749
-                $this->_handle_payment_processor_exception($e);
2750
-            }
2751
-        }
2752
-        return $payment;
2753
-    }
2754
-
2755
-
2756
-    /**
2757
-     * _process_cancelled_payments
2758
-     * just makes sure that the payment status gets updated correctly
2759
-     * so tha tan error isn't generated during payment validation
2760
-     *
2761
-     * @param EE_Payment $payment
2762
-     * @return EE_Payment|null
2763
-     * @throws EE_Error
2764
-     */
2765
-    private function _process_cancelled_payments($payment = null)
2766
-    {
2767
-        if (
2768
-            $payment instanceof EE_Payment
2769
-            && $this->request->requestParamIsSet('ee_cancel_payment')
2770
-            && $payment->status() === EEM_Payment::status_id_failed
2771
-        ) {
2772
-            $payment->set_status(EEM_Payment::status_id_cancelled);
2773
-        }
2774
-        return $payment;
2775
-    }
2776
-
2777
-
2778
-    /**
2779
-     *    get_transaction_details_for_gateways
2780
-     *
2781
-     * @access    public
2782
-     * @return void
2783
-     * @throws EE_Error
2784
-     * @throws InvalidArgumentException
2785
-     * @throws ReflectionException
2786
-     * @throws InvalidDataTypeException
2787
-     * @throws InvalidInterfaceException
2788
-     */
2789
-    public function get_transaction_details_for_gateways()
2790
-    {
2791
-        $txn_details = [];
2792
-        // ya gotta make a choice man
2793
-        if (empty($this->checkout->selected_method_of_payment)) {
2794
-            $txn_details = [
2795
-                'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2796
-            ];
2797
-        }
2798
-        // get EE_Payment_Method object
2799
-        if (
2800
-            empty($txn_details)
2801
-            && ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2802
-        ) {
2803
-            $txn_details = [
2804
-                'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2805
-                'error'                      => esc_html__(
2806
-                    'A valid Payment Method could not be determined.',
2807
-                    'event_espresso'
2808
-                ),
2809
-            ];
2810
-        }
2811
-        if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2812
-            $return_url  = $this->_get_return_url($this->checkout->payment_method);
2813
-            $txn_details = [
2814
-                'TXN_ID'         => $this->checkout->transaction->ID(),
2815
-                'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2816
-                'TXN_total'      => $this->checkout->transaction->total(),
2817
-                'TXN_paid'       => $this->checkout->transaction->paid(),
2818
-                'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2819
-                'STS_ID'         => $this->checkout->transaction->status_ID(),
2820
-                'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2821
-                'payment_amount' => $this->checkout->amount_owing,
2822
-                'return_url'     => $return_url,
2823
-                'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2824
-                'notify_url'     => EE_Config::instance()->core->txn_page_url(
2825
-                    [
2826
-                        'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2827
-                        'ee_payment_method' => $this->checkout->payment_method->slug(),
2828
-                    ]
2829
-                ),
2830
-            ];
2831
-        }
2832
-        echo wp_json_encode($txn_details);
2833
-        exit();
2834
-    }
2835
-
2836
-
2837
-    /**
2838
-     *    __sleep
2839
-     * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2840
-     * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2841
-     * reg form, because if needed, it will be regenerated anyways
2842
-     *
2843
-     * @return array
2844
-     */
2845
-    public function __sleep()
2846
-    {
2847
-        // remove the reg form and the checkout
2848
-        return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2849
-    }
23
+	/**
24
+	 * @var EE_Line_Item_Display $Line_Item_Display
25
+	 */
26
+	protected $line_item_display;
27
+
28
+	/**
29
+	 * @var boolean $handle_IPN_in_this_request
30
+	 */
31
+	protected $handle_IPN_in_this_request = false;
32
+
33
+
34
+	/**
35
+	 *    set_hooks - for hooking into EE Core, other modules, etc
36
+	 *
37
+	 * @access    public
38
+	 * @return    void
39
+	 */
40
+	public static function set_hooks()
41
+	{
42
+		add_filter(
43
+			'FHEE__SPCO__EE_Line_Item_Filter_Collection',
44
+			['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
45
+		);
46
+		add_action(
47
+			'wp_ajax_switch_spco_billing_form',
48
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
49
+		);
50
+		add_action(
51
+			'wp_ajax_nopriv_switch_spco_billing_form',
52
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
53
+		);
54
+		add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
55
+		add_action(
56
+			'wp_ajax_nopriv_save_payer_details',
57
+			['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
58
+		);
59
+		add_action(
60
+			'wp_ajax_get_transaction_details_for_gateways',
61
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
62
+		);
63
+		add_action(
64
+			'wp_ajax_nopriv_get_transaction_details_for_gateways',
65
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
66
+		);
67
+		add_filter(
68
+			'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
69
+			['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method'],
70
+			10,
71
+			1
72
+		);
73
+	}
74
+
75
+
76
+	/**
77
+	 *    ajax switch_spco_billing_form
78
+	 *
79
+	 */
80
+	public static function switch_spco_billing_form()
81
+	{
82
+		EED_Single_Page_Checkout::process_ajax_request('switch_payment_method');
83
+	}
84
+
85
+
86
+	/**
87
+	 *    ajax save_payer_details
88
+	 *
89
+	 */
90
+	public static function save_payer_details()
91
+	{
92
+		EED_Single_Page_Checkout::process_ajax_request('save_payer_details_via_ajax');
93
+	}
94
+
95
+
96
+	/**
97
+	 *    ajax get_transaction_details
98
+	 *
99
+	 */
100
+	public static function get_transaction_details()
101
+	{
102
+		EED_Single_Page_Checkout::process_ajax_request('get_transaction_details_for_gateways');
103
+	}
104
+
105
+
106
+	/**
107
+	 * bypass_recaptcha_for_load_payment_method
108
+	 *
109
+	 * @access public
110
+	 * @return array
111
+	 * @throws InvalidArgumentException
112
+	 * @throws InvalidDataTypeException
113
+	 * @throws InvalidInterfaceException
114
+	 */
115
+	public static function bypass_recaptcha_for_load_payment_method()
116
+	{
117
+		return [
118
+			'EESID'  => EE_Registry::instance()->SSN->id(),
119
+			'step'   => 'payment_options',
120
+			'action' => 'spco_billing_form',
121
+		];
122
+	}
123
+
124
+
125
+	/**
126
+	 *    class constructor
127
+	 *
128
+	 * @access    public
129
+	 * @param EE_Checkout $checkout
130
+	 */
131
+	public function __construct(EE_Checkout $checkout)
132
+	{
133
+		$this->request   = EED_Single_Page_Checkout::getRequest();
134
+		$this->_slug     = 'payment_options';
135
+		$this->_name     = esc_html__('Payment Options', 'event_espresso');
136
+		$this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
137
+		$this->checkout  = $checkout;
138
+		$this->_reset_success_message();
139
+		$this->set_instructions(
140
+			esc_html__(
141
+				'Please select a method of payment and provide any necessary billing information before proceeding.',
142
+				'event_espresso'
143
+			)
144
+		);
145
+	}
146
+
147
+
148
+	/**
149
+	 * @return null
150
+	 */
151
+	public function line_item_display()
152
+	{
153
+		return $this->line_item_display;
154
+	}
155
+
156
+
157
+	/**
158
+	 * @param null $line_item_display
159
+	 */
160
+	public function set_line_item_display($line_item_display)
161
+	{
162
+		$this->line_item_display = $line_item_display;
163
+	}
164
+
165
+
166
+	/**
167
+	 * @return boolean
168
+	 */
169
+	public function handle_IPN_in_this_request()
170
+	{
171
+		return $this->handle_IPN_in_this_request;
172
+	}
173
+
174
+
175
+	/**
176
+	 * @param boolean $handle_IPN_in_this_request
177
+	 */
178
+	public function set_handle_IPN_in_this_request($handle_IPN_in_this_request)
179
+	{
180
+		$this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
181
+	}
182
+
183
+
184
+	/**
185
+	 * translate_js_strings
186
+	 *
187
+	 * @return void
188
+	 */
189
+	public function translate_js_strings()
190
+	{
191
+		EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
192
+			'Please select a method of payment in order to continue.',
193
+			'event_espresso'
194
+		);
195
+		EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
196
+			'A valid method of payment could not be determined. Please refresh the page and try again.',
197
+			'event_espresso'
198
+		);
199
+		EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
200
+			'Forwarding to Secure Payment Provider.',
201
+			'event_espresso'
202
+		);
203
+	}
204
+
205
+
206
+	/**
207
+	 * enqueue_styles_and_scripts
208
+	 *
209
+	 * @return void
210
+	 * @throws EE_Error
211
+	 * @throws InvalidArgumentException
212
+	 * @throws InvalidDataTypeException
213
+	 * @throws InvalidInterfaceException
214
+	 * @throws ReflectionException
215
+	 */
216
+	public function enqueue_styles_and_scripts()
217
+	{
218
+		$transaction = $this->checkout->transaction;
219
+		// if the transaction isn't set or nothing is owed on it, don't enqueue any JS
220
+		if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
221
+			return;
222
+		}
223
+		foreach (
224
+			EEM_Payment_Method::instance()->get_all_for_transaction(
225
+				$transaction,
226
+				EEM_Payment_Method::scope_cart
227
+			) as $payment_method
228
+		) {
229
+			$type_obj = $payment_method->type_obj();
230
+			if ($type_obj instanceof EE_PMT_Base) {
231
+				$billing_form = $type_obj->generate_new_billing_form($transaction);
232
+				if ($billing_form instanceof EE_Form_Section_Proper) {
233
+					$billing_form->enqueue_js();
234
+				}
235
+			}
236
+		}
237
+	}
238
+
239
+
240
+	/**
241
+	 * initialize_reg_step
242
+	 *
243
+	 * @return bool
244
+	 * @throws EE_Error
245
+	 * @throws InvalidArgumentException
246
+	 * @throws ReflectionException
247
+	 * @throws InvalidDataTypeException
248
+	 * @throws InvalidInterfaceException
249
+	 */
250
+	public function initialize_reg_step()
251
+	{
252
+		// TODO: if /when we implement donations, then this will need overriding
253
+		if (
254
+			// don't need payment options for:
255
+			// registrations made via the admin
256
+			// completed transactions
257
+			// overpaid transactions
258
+			// $ 0.00 transactions(no payment required)
259
+			! $this->checkout->payment_required()
260
+			// but do NOT remove if current action being called belongs to this reg step
261
+			&& ! is_callable([$this, $this->checkout->action])
262
+			&& ! $this->completed()
263
+		) {
264
+			// and if so, then we no longer need the Payment Options step
265
+			if ($this->is_current_step()) {
266
+				$this->checkout->generate_reg_form = false;
267
+			}
268
+			$this->checkout->remove_reg_step($this->_slug);
269
+			// DEBUG LOG
270
+			// $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
271
+			return false;
272
+		}
273
+		// load EEM_Payment_Method
274
+		EE_Registry::instance()->load_model('Payment_Method');
275
+		// get all active payment methods
276
+		$this->checkout->available_payment_methods = EEM_Payment_Method::instance()->get_all_for_transaction(
277
+			$this->checkout->transaction,
278
+			EEM_Payment_Method::scope_cart
279
+		);
280
+		return true;
281
+	}
282
+
283
+
284
+	/**
285
+	 * @return EE_Form_Section_Proper
286
+	 * @throws EE_Error
287
+	 * @throws InvalidArgumentException
288
+	 * @throws ReflectionException
289
+	 * @throws EntityNotFoundException
290
+	 * @throws InvalidDataTypeException
291
+	 * @throws InvalidInterfaceException
292
+	 * @throws InvalidStatusException
293
+	 */
294
+	public function generate_reg_form()
295
+	{
296
+		// reset in case someone changes their mind
297
+		$this->_reset_selected_method_of_payment();
298
+		// set some defaults
299
+		$this->checkout->selected_method_of_payment = 'payments_closed';
300
+		$registrations_requiring_payment            = [];
301
+		$registrations_for_free_events              = [];
302
+		$registrations_requiring_pre_approval       = [];
303
+		$sold_out_events                            = [];
304
+		$insufficient_spaces_available              = [];
305
+		$no_payment_required                        = true;
306
+		// loop thru registrations to gather info
307
+		$registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
308
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
309
+			$registrations,
310
+			$this->checkout->revisit
311
+		);
312
+		$extra_txn_fees_handler = new ExtraTxnFeesForRegistrantsHandler($registrations);
313
+		$extra_txn_fees_handler->applyExtraFeesToRegistrants();
314
+		foreach ($registrations as $REG_ID => $registration) {
315
+			/** @var $registration EE_Registration */
316
+			// has this registration lost it's space ?
317
+			if (isset($ejected_registrations[ $REG_ID ])) {
318
+				if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
319
+					$sold_out_events[ $registration->event()->ID() ] = $registration->event();
320
+				} else {
321
+					$insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
322
+				}
323
+				continue;
324
+			}
325
+			// event requires admin approval
326
+			if ($registration->status_ID() === EEM_Registration::status_id_not_approved) {
327
+				// add event to list of events with pre-approval reg status
328
+				$registrations_requiring_pre_approval[ $REG_ID ] = $registration;
329
+				do_action(
330
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
331
+					$registration->event(),
332
+					$this
333
+				);
334
+				continue;
335
+			}
336
+			if (
337
+				$this->checkout->revisit
338
+				&& $registration->status_ID() !== EEM_Registration::status_id_approved
339
+				&& (
340
+					$registration->event()->is_sold_out()
341
+					|| $registration->event()->is_sold_out(true)
342
+				)
343
+			) {
344
+				// add event to list of events that are sold out
345
+				$sold_out_events[ $registration->event()->ID() ] = $registration->event();
346
+				do_action(
347
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
348
+					$registration->event(),
349
+					$this
350
+				);
351
+				continue;
352
+			}
353
+			// are they allowed to pay now and is there monies owing?
354
+			if ($registration->owes_monies_and_can_pay()) {
355
+				$registrations_requiring_payment[ $REG_ID ] = $registration;
356
+				do_action(
357
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
358
+					$registration->event(),
359
+					$this
360
+				);
361
+			} elseif (
362
+				! $this->checkout->revisit
363
+					  && $registration->status_ID() !== EEM_Registration::status_id_not_approved
364
+					  && $registration->ticket()->is_free()
365
+			) {
366
+				$registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
367
+			}
368
+		}
369
+		$subsections = [];
370
+		// now decide which template to load
371
+		if (! empty($sold_out_events)) {
372
+			$subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
373
+		}
374
+		if (! empty($insufficient_spaces_available)) {
375
+			$subsections['insufficient_space'] = $this->_insufficient_spaces_available(
376
+				$insufficient_spaces_available
377
+			);
378
+		}
379
+		if (! empty($registrations_requiring_pre_approval)) {
380
+			$subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
381
+				$registrations_requiring_pre_approval
382
+			);
383
+		}
384
+		if (! empty($registrations_for_free_events)) {
385
+			$subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
386
+		}
387
+		if (! empty($registrations_requiring_payment)) {
388
+			if ($this->checkout->amount_owing > 0) {
389
+				// autoload Line_Item_Display classes
390
+				EEH_Autoloader::register_line_item_filter_autoloaders();
391
+				$line_item_filter_processor = new EE_Line_Item_Filter_Processor(
392
+					apply_filters(
393
+						'FHEE__SPCO__EE_Line_Item_Filter_Collection',
394
+						new EE_Line_Item_Filter_Collection()
395
+					),
396
+					$this->checkout->cart->get_grand_total()
397
+				);
398
+				/** @var EE_Line_Item $filtered_line_item_tree */
399
+				$filtered_line_item_tree = $line_item_filter_processor->process();
400
+				EEH_Autoloader::register_line_item_display_autoloaders();
401
+				$this->set_line_item_display(new EE_Line_Item_Display('spco'));
402
+				$subsections['payment_options'] = $this->_display_payment_options(
403
+					$this->line_item_display->display_line_item(
404
+						$filtered_line_item_tree,
405
+						['registrations' => $registrations]
406
+					)
407
+				);
408
+				$this->checkout->amount_owing   = $filtered_line_item_tree->total();
409
+				$this->_apply_registration_payments_to_amount_owing($registrations);
410
+			}
411
+			$no_payment_required = false;
412
+		} else {
413
+			$this->_hide_reg_step_submit_button_if_revisit();
414
+		}
415
+		$this->_save_selected_method_of_payment();
416
+
417
+		$subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
418
+		$subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
419
+
420
+		return new EE_Form_Section_Proper(
421
+			[
422
+				'name'            => $this->reg_form_name(),
423
+				'html_id'         => $this->reg_form_name(),
424
+				'subsections'     => $subsections,
425
+				'layout_strategy' => new EE_No_Layout(),
426
+			]
427
+		);
428
+	}
429
+
430
+
431
+	/**
432
+	 * add line item filters required for this reg step
433
+	 * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
434
+	 *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
435
+	 *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
436
+	 *        payment options reg step, can apply these filters via the following: apply_filters(
437
+	 *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
438
+	 *        filter collection by passing that instead of instantiating a new collection
439
+	 *
440
+	 * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
441
+	 * @return EE_Line_Item_Filter_Collection
442
+	 * @throws EE_Error
443
+	 * @throws InvalidArgumentException
444
+	 * @throws ReflectionException
445
+	 * @throws EntityNotFoundException
446
+	 * @throws InvalidDataTypeException
447
+	 * @throws InvalidInterfaceException
448
+	 * @throws InvalidStatusException
449
+	 */
450
+	public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
451
+	{
452
+		if (! EE_Registry::instance()->SSN instanceof EE_Session
453
+			|| ! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout
454
+			|| ! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction
455
+		) {
456
+			return $line_item_filter_collection;
457
+		}
458
+		$line_item_filter_collection->add(
459
+			new EE_Billable_Line_Item_Filter(
460
+				EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
461
+					EE_Registry::instance()->SSN->checkout()->transaction->registrations(
462
+						EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
463
+					)
464
+				)
465
+			)
466
+		);
467
+		$line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
468
+		return $line_item_filter_collection;
469
+	}
470
+
471
+
472
+	/**
473
+	 * remove_ejected_registrations
474
+	 * if a registrant has lost their potential space at an event due to lack of payment,
475
+	 * then this method removes them from the list of registrations being paid for during this request
476
+	 *
477
+	 * @param EE_Registration[] $registrations
478
+	 * @return EE_Registration[]
479
+	 * @throws EE_Error
480
+	 * @throws InvalidArgumentException
481
+	 * @throws ReflectionException
482
+	 * @throws EntityNotFoundException
483
+	 * @throws InvalidDataTypeException
484
+	 * @throws InvalidInterfaceException
485
+	 * @throws InvalidStatusException
486
+	 */
487
+	public static function remove_ejected_registrations(array $registrations)
488
+	{
489
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
490
+			$registrations,
491
+			EE_Registry::instance()->SSN->checkout()->revisit
492
+		);
493
+		foreach ($registrations as $REG_ID => $registration) {
494
+			// has this registration lost it's space ?
495
+			if (isset($ejected_registrations[ $REG_ID ])) {
496
+				unset($registrations[ $REG_ID ]);
497
+			}
498
+		}
499
+		return $registrations;
500
+	}
501
+
502
+
503
+	/**
504
+	 * find_registrations_that_lost_their_space
505
+	 * If a registrant chooses an offline payment method like Invoice,
506
+	 * then no space is reserved for them at the event until they fully pay fo that site
507
+	 * (unless the event's default reg status is set to APPROVED)
508
+	 * if a registrant then later returns to pay, but the number of spaces available has been reduced due to sales,
509
+	 * then this method will determine which registrations have lost the ability to complete the reg process.
510
+	 *
511
+	 * @param EE_Registration[] $registrations
512
+	 * @param bool              $revisit
513
+	 * @return array
514
+	 * @throws EE_Error
515
+	 * @throws InvalidArgumentException
516
+	 * @throws ReflectionException
517
+	 * @throws EntityNotFoundException
518
+	 * @throws InvalidDataTypeException
519
+	 * @throws InvalidInterfaceException
520
+	 * @throws InvalidStatusException
521
+	 */
522
+	public static function find_registrations_that_lost_their_space(array $registrations, $revisit = false)
523
+	{
524
+		// registrations per event
525
+		$event_reg_count = [];
526
+		// spaces left per event
527
+		$event_spaces_remaining = [];
528
+		// tickets left sorted by ID
529
+		$tickets_remaining = [];
530
+		// registrations that have lost their space
531
+		$ejected_registrations = [];
532
+		foreach ($registrations as $REG_ID => $registration) {
533
+			if (
534
+				$registration->status_ID() === EEM_Registration::status_id_approved
535
+				|| apply_filters(
536
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__find_registrations_that_lost_their_space__allow_reg_payment',
537
+					false,
538
+					$registration,
539
+					$revisit
540
+				)
541
+			) {
542
+				continue;
543
+			}
544
+			$EVT_ID = $registration->event_ID();
545
+			$ticket = $registration->ticket();
546
+			if (! isset($tickets_remaining[ $ticket->ID() ])) {
547
+				$tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
548
+			}
549
+			if ($tickets_remaining[ $ticket->ID() ] > 0) {
550
+				if (! isset($event_reg_count[ $EVT_ID ])) {
551
+					$event_reg_count[ $EVT_ID ] = 0;
552
+				}
553
+				$event_reg_count[ $EVT_ID ]++;
554
+				if (! isset($event_spaces_remaining[ $EVT_ID ])) {
555
+					$event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
556
+				}
557
+			}
558
+			if (
559
+				$revisit
560
+				&& ($tickets_remaining[ $ticket->ID() ] === 0
561
+					|| $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
562
+				)
563
+			) {
564
+				$ejected_registrations[ $REG_ID ] = $registration->event();
565
+				if ($registration->status_ID() !== EEM_Registration::status_id_wait_list) {
566
+					/** @type EE_Registration_Processor $registration_processor */
567
+					$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
568
+					// at this point, we should have enough details about the registrant to consider the registration
569
+					// NOT incomplete
570
+					$registration_processor->manually_update_registration_status(
571
+						$registration,
572
+						EEM_Registration::status_id_wait_list
573
+					);
574
+				}
575
+			}
576
+		}
577
+		return $ejected_registrations;
578
+	}
579
+
580
+
581
+	/**
582
+	 * _hide_reg_step_submit_button
583
+	 * removes the html for the reg step submit button
584
+	 * by replacing it with an empty string via filter callback
585
+	 *
586
+	 * @return void
587
+	 */
588
+	protected function _adjust_registration_status_if_event_old_sold()
589
+	{
590
+	}
591
+
592
+
593
+	/**
594
+	 * _hide_reg_step_submit_button
595
+	 * removes the html for the reg step submit button
596
+	 * by replacing it with an empty string via filter callback
597
+	 *
598
+	 * @return void
599
+	 */
600
+	protected function _hide_reg_step_submit_button_if_revisit()
601
+	{
602
+		if ($this->checkout->revisit) {
603
+			add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
604
+		}
605
+	}
606
+
607
+
608
+	/**
609
+	 * sold_out_events
610
+	 * displays notices regarding events that have sold out since hte registrant first signed up
611
+	 *
612
+	 * @param EE_Event[] $sold_out_events_array
613
+	 * @return EE_Form_Section_Proper
614
+	 * @throws EE_Error
615
+	 */
616
+	private function _sold_out_events($sold_out_events_array = [])
617
+	{
618
+		// set some defaults
619
+		$this->checkout->selected_method_of_payment = 'events_sold_out';
620
+		$sold_out_events                            = '';
621
+		foreach ($sold_out_events_array as $sold_out_event) {
622
+			$sold_out_events .= EEH_HTML::li(
623
+				EEH_HTML::span(
624
+					'  ' . $sold_out_event->name(),
625
+					'',
626
+					'dashicons dashicons-marker ee-icon-size-16 pink-text'
627
+				)
628
+			);
629
+		}
630
+		return new EE_Form_Section_Proper(
631
+			[
632
+				'layout_strategy' => new EE_Template_Layout(
633
+					[
634
+						'layout_template_file' => SPCO_REG_STEPS_PATH
635
+												  . $this->_slug
636
+												  . '/sold_out_events.template.php',
637
+						'template_args'        => apply_filters(
638
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
639
+							[
640
+								'sold_out_events'     => $sold_out_events,
641
+								'sold_out_events_msg' => apply_filters(
642
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
643
+									sprintf(
644
+										esc_html__(
645
+											'It appears that the event you were about to make a payment for has sold out since you first registered. If you have already made a partial payment towards this event, please contact the event administrator for a refund.%3$s%3$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%2$s',
646
+											'event_espresso'
647
+										),
648
+										'<strong>',
649
+										'</strong>',
650
+										'<br />'
651
+									)
652
+								),
653
+							]
654
+						),
655
+					]
656
+				),
657
+			]
658
+		);
659
+	}
660
+
661
+
662
+	/**
663
+	 * _insufficient_spaces_available
664
+	 * displays notices regarding events that do not have enough remaining spaces
665
+	 * to satisfy the current number of registrations looking to pay
666
+	 *
667
+	 * @param EE_Event[] $insufficient_spaces_events_array
668
+	 * @return EE_Form_Section_Proper
669
+	 * @throws EE_Error
670
+	 * @throws ReflectionException
671
+	 */
672
+	private function _insufficient_spaces_available($insufficient_spaces_events_array = [])
673
+	{
674
+		// set some defaults
675
+		$this->checkout->selected_method_of_payment = 'invoice';
676
+		$insufficient_space_events                  = '';
677
+		foreach ($insufficient_spaces_events_array as $event) {
678
+			if ($event instanceof EE_Event) {
679
+				$insufficient_space_events .= EEH_HTML::li(
680
+					EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
681
+				);
682
+			}
683
+		}
684
+		return new EE_Form_Section_Proper(
685
+			[
686
+				'subsections'     => [
687
+					'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
688
+					'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
689
+				],
690
+				'layout_strategy' => new EE_Template_Layout(
691
+					[
692
+						'layout_template_file' => SPCO_REG_STEPS_PATH
693
+												  . $this->_slug
694
+												  . '/sold_out_events.template.php',
695
+						'template_args'        => apply_filters(
696
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
697
+							[
698
+								'sold_out_events'     => $insufficient_space_events,
699
+								'sold_out_events_msg' => apply_filters(
700
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
701
+									esc_html__(
702
+										'It appears that the event you were about to make a payment for has sold additional tickets since you first registered, and there are no longer enough spaces left to accommodate your selections. You may continue to pay and secure the available space(s) remaining, or simply cancel if you no longer wish to purchase. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
703
+										'event_espresso'
704
+									)
705
+								),
706
+							]
707
+						),
708
+					]
709
+				),
710
+			]
711
+		);
712
+	}
713
+
714
+
715
+	/**
716
+	 * registrations_requiring_pre_approval
717
+	 *
718
+	 * @param array $registrations_requiring_pre_approval
719
+	 * @return EE_Form_Section_Proper
720
+	 * @throws EE_Error
721
+	 * @throws EntityNotFoundException
722
+	 * @throws ReflectionException
723
+	 */
724
+	private function _registrations_requiring_pre_approval($registrations_requiring_pre_approval = [])
725
+	{
726
+		$events_requiring_pre_approval = [];
727
+		foreach ($registrations_requiring_pre_approval as $registration) {
728
+			if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
729
+				$events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
730
+					EEH_HTML::span(
731
+						'',
732
+						'',
733
+						'dashicons dashicons-marker ee-icon-size-16 orange-text'
734
+					)
735
+					. EEH_HTML::span($registration->event()->name(), '', 'orange-text')
736
+				);
737
+			}
738
+		}
739
+		return new EE_Form_Section_Proper(
740
+			[
741
+				'layout_strategy' => new EE_Template_Layout(
742
+					[
743
+						'layout_template_file' => SPCO_REG_STEPS_PATH
744
+												  . $this->_slug
745
+												  . '/events_requiring_pre_approval.template.php', // layout_template
746
+						'template_args'        => apply_filters(
747
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
748
+							[
749
+								'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
750
+								'events_requiring_pre_approval_msg' => apply_filters(
751
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
752
+									esc_html__(
753
+										'The following events do not require payment at this time and will not be billed during this transaction. Billing will only occur after the attendee has been approved by the event organizer. You will be notified when your registration has been processed. If this is a free event, then no billing will occur.',
754
+										'event_espresso'
755
+									)
756
+								),
757
+							]
758
+						),
759
+					]
760
+				),
761
+			]
762
+		);
763
+	}
764
+
765
+
766
+	/**
767
+	 * _no_payment_required
768
+	 *
769
+	 * @param EE_Event[] $registrations_for_free_events
770
+	 * @return EE_Form_Section_Proper
771
+	 * @throws EE_Error
772
+	 */
773
+	private function _no_payment_required($registrations_for_free_events = [])
774
+	{
775
+		// set some defaults
776
+		$this->checkout->selected_method_of_payment = 'no_payment_required';
777
+		// generate no_payment_required form
778
+		return new EE_Form_Section_Proper(
779
+			[
780
+				'layout_strategy' => new EE_Template_Layout(
781
+					[
782
+						'layout_template_file' => SPCO_REG_STEPS_PATH
783
+												  . $this->_slug
784
+												  . '/no_payment_required.template.php', // layout_template
785
+						'template_args'        => apply_filters(
786
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
787
+							[
788
+								'revisit'                       => $this->checkout->revisit,
789
+								'registrations'                 => [],
790
+								'ticket_count'                  => [],
791
+								'registrations_for_free_events' => $registrations_for_free_events,
792
+								'no_payment_required_msg'       => EEH_HTML::p(
793
+									esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
794
+								),
795
+							]
796
+						),
797
+					]
798
+				),
799
+			]
800
+		);
801
+	}
802
+
803
+
804
+	/**
805
+	 * _display_payment_options
806
+	 *
807
+	 * @param string $transaction_details
808
+	 * @return EE_Form_Section_Proper
809
+	 * @throws EE_Error
810
+	 * @throws InvalidArgumentException
811
+	 * @throws InvalidDataTypeException
812
+	 * @throws InvalidInterfaceException
813
+	 */
814
+	private function _display_payment_options($transaction_details = '')
815
+	{
816
+		// has method_of_payment been set by no-js user?
817
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
818
+		// build payment options form
819
+		return apply_filters(
820
+			'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
821
+			new EE_Form_Section_Proper(
822
+				[
823
+					'subsections'     => [
824
+						'before_payment_options' => apply_filters(
825
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
826
+							new EE_Form_Section_Proper(
827
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
828
+							)
829
+						),
830
+						'payment_options'        => $this->_setup_payment_options(),
831
+						'after_payment_options'  => apply_filters(
832
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
833
+							new EE_Form_Section_Proper(
834
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
835
+							)
836
+						),
837
+					],
838
+					'layout_strategy' => new EE_Template_Layout(
839
+						[
840
+							'layout_template_file' => $this->_template,
841
+							'template_args'        => apply_filters(
842
+								'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
843
+								[
844
+									'reg_count'                 => $this->line_item_display->total_items(),
845
+									'transaction_details'       => $transaction_details,
846
+									'available_payment_methods' => [],
847
+								]
848
+							),
849
+						]
850
+					),
851
+				]
852
+			)
853
+		);
854
+	}
855
+
856
+
857
+	/**
858
+	 * _extra_hidden_inputs
859
+	 *
860
+	 * @param bool $no_payment_required
861
+	 * @return EE_Form_Section_Proper
862
+	 * @throws EE_Error
863
+	 * @throws ReflectionException
864
+	 */
865
+	private function _extra_hidden_inputs($no_payment_required = true)
866
+	{
867
+		return new EE_Form_Section_Proper(
868
+			[
869
+				'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
870
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
871
+				'subsections'     => [
872
+					'spco_no_payment_required' => new EE_Hidden_Input(
873
+						[
874
+							'normalization_strategy' => new EE_Boolean_Normalization(),
875
+							'html_name'              => 'spco_no_payment_required',
876
+							'html_id'                => 'spco-no-payment-required-payment_options',
877
+							'default'                => $no_payment_required,
878
+						]
879
+					),
880
+					'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
881
+						[
882
+							'normalization_strategy' => new EE_Int_Normalization(),
883
+							'html_name'              => 'spco_transaction_id',
884
+							'html_id'                => 'spco-transaction-id',
885
+							'default'                => $this->checkout->transaction->ID(),
886
+						]
887
+					),
888
+				],
889
+			]
890
+		);
891
+	}
892
+
893
+
894
+	/**
895
+	 *    _apply_registration_payments_to_amount_owing
896
+	 *
897
+	 * @param array $registrations
898
+	 * @throws EE_Error|ReflectionException
899
+	 */
900
+	protected function _apply_registration_payments_to_amount_owing(array $registrations)
901
+	{
902
+		$payments = [];
903
+		foreach ($registrations as $registration) {
904
+			if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
905
+				$payments += $registration->registration_payments();
906
+			}
907
+		}
908
+		if (! empty($payments)) {
909
+			foreach ($payments as $payment) {
910
+				if ($payment instanceof EE_Registration_Payment) {
911
+					$this->checkout->amount_owing -= $payment->amount();
912
+				}
913
+			}
914
+		}
915
+	}
916
+
917
+
918
+	/**
919
+	 *    _reset_selected_method_of_payment
920
+	 *
921
+	 * @access    private
922
+	 * @param bool $force_reset
923
+	 * @return void
924
+	 * @throws InvalidArgumentException
925
+	 * @throws InvalidDataTypeException
926
+	 * @throws InvalidInterfaceException
927
+	 */
928
+	private function _reset_selected_method_of_payment($force_reset = false)
929
+	{
930
+		/** @var RequestInterface $request */
931
+		$request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
932
+		$reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
933
+		if ($reset_payment_method) {
934
+			$this->checkout->selected_method_of_payment = null;
935
+			$this->checkout->payment_method             = null;
936
+			$this->checkout->billing_form               = null;
937
+			$this->_save_selected_method_of_payment();
938
+		}
939
+	}
940
+
941
+
942
+	/**
943
+	 * _save_selected_method_of_payment
944
+	 * stores the selected_method_of_payment in the session
945
+	 * so that it's available for all subsequent requests including AJAX
946
+	 *
947
+	 * @access        private
948
+	 * @param string $selected_method_of_payment
949
+	 * @return void
950
+	 * @throws InvalidArgumentException
951
+	 * @throws InvalidDataTypeException
952
+	 * @throws InvalidInterfaceException
953
+	 */
954
+	private function _save_selected_method_of_payment($selected_method_of_payment = '')
955
+	{
956
+		$selected_method_of_payment = ! empty($selected_method_of_payment)
957
+			? $selected_method_of_payment
958
+			: $this->checkout->selected_method_of_payment;
959
+		EE_Registry::instance()->SSN->set_session_data(
960
+			['selected_method_of_payment' => $selected_method_of_payment]
961
+		);
962
+	}
963
+
964
+
965
+	/**
966
+	 * _setup_payment_options
967
+	 *
968
+	 * @return EE_Form_Section_Proper
969
+	 * @throws EE_Error
970
+	 * @throws InvalidArgumentException
971
+	 * @throws InvalidDataTypeException
972
+	 * @throws InvalidInterfaceException
973
+	 */
974
+	public function _setup_payment_options()
975
+	{
976
+		// load payment method classes
977
+		$this->checkout->available_payment_methods = $this->_get_available_payment_methods();
978
+		if (empty($this->checkout->available_payment_methods)) {
979
+			EE_Error::add_error(
980
+				apply_filters(
981
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
982
+					sprintf(
983
+						esc_html__(
984
+							'Sorry, you cannot complete your purchase because a payment method is not active.%1$s Please contact %2$s for assistance and provide a description of the problem.',
985
+							'event_espresso'
986
+						),
987
+						'<br>',
988
+						EE_Registry::instance()->CFG->organization->get_pretty('email')
989
+					)
990
+				),
991
+				__FILE__,
992
+				__FUNCTION__,
993
+				__LINE__
994
+			);
995
+		}
996
+		// switch up header depending on number of available payment methods
997
+		$payment_method_header     = count($this->checkout->available_payment_methods) > 1
998
+			? apply_filters(
999
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
1000
+				esc_html__('Please Select Your Method of Payment', 'event_espresso')
1001
+			)
1002
+			: apply_filters(
1003
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
1004
+				esc_html__('Method of Payment', 'event_espresso')
1005
+			);
1006
+		$available_payment_methods = [
1007
+			// display the "Payment Method" header
1008
+			'payment_method_header' => new EE_Form_Section_HTML(
1009
+				apply_filters(
1010
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
1011
+					EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
1012
+					$payment_method_header
1013
+				)
1014
+			),
1015
+		];
1016
+		// the list of actual payment methods ( invoice, paypal, etc ) in a  ( slug => HTML )  format
1017
+		$available_payment_method_options = [];
1018
+		$default_payment_method_option    = [];
1019
+		// additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
1020
+		$payment_methods_billing_info = [
1021
+			new EE_Form_Section_HTML(
1022
+				EEH_HTML::div('<br />', '', '', 'clear:both;')
1023
+			),
1024
+		];
1025
+		// loop through payment methods
1026
+		foreach ($this->checkout->available_payment_methods as $payment_method) {
1027
+			if ($payment_method instanceof EE_Payment_Method) {
1028
+				$payment_method_button = EEH_HTML::img(
1029
+					$payment_method->button_url(),
1030
+					$payment_method->name(),
1031
+					'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1032
+					'spco-payment-method-btn-img'
1033
+				);
1034
+				// check if any payment methods are set as default
1035
+				// if payment method is already selected OR nothing is selected and this payment method should be
1036
+				// open_by_default
1037
+				if (
1038
+					($this->checkout->selected_method_of_payment === $payment_method->slug())
1039
+					|| (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1040
+				) {
1041
+					$this->checkout->selected_method_of_payment = $payment_method->slug();
1042
+					$this->_save_selected_method_of_payment();
1043
+					$default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1044
+				} else {
1045
+					$available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1046
+				}
1047
+				$payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1048
+					$this->_payment_method_billing_info(
1049
+						$payment_method
1050
+					);
1051
+			}
1052
+		}
1053
+		// prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1054
+		// of PMs
1055
+		$available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1056
+		// now generate the actual form  inputs
1057
+		$available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1058
+			$available_payment_method_options
1059
+		);
1060
+		$available_payment_methods                              += $payment_methods_billing_info;
1061
+		// build the available payment methods form
1062
+		return new EE_Form_Section_Proper(
1063
+			[
1064
+				'html_id'         => 'spco-available-methods-of-payment-dv',
1065
+				'subsections'     => $available_payment_methods,
1066
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1067
+			]
1068
+		);
1069
+	}
1070
+
1071
+
1072
+	/**
1073
+	 * _get_available_payment_methods
1074
+	 *
1075
+	 * @return EE_Payment_Method[]
1076
+	 * @throws EE_Error
1077
+	 * @throws InvalidArgumentException
1078
+	 * @throws InvalidDataTypeException
1079
+	 * @throws InvalidInterfaceException
1080
+	 */
1081
+	protected function _get_available_payment_methods()
1082
+	{
1083
+		if (! empty($this->checkout->available_payment_methods)) {
1084
+			return $this->checkout->available_payment_methods;
1085
+		}
1086
+		$available_payment_methods = [];
1087
+		$EEM_Payment_Method        = EEM_Payment_Method::instance();
1088
+		// get all active payment methods
1089
+		$payment_methods = $EEM_Payment_Method->get_all_for_transaction(
1090
+			$this->checkout->transaction,
1091
+			EEM_Payment_Method::scope_cart
1092
+		);
1093
+		foreach ($payment_methods as $payment_method) {
1094
+			if ($payment_method instanceof EE_Payment_Method) {
1095
+				$available_payment_methods[ $payment_method->slug() ] = $payment_method;
1096
+			}
1097
+		}
1098
+		return $available_payment_methods;
1099
+	}
1100
+
1101
+
1102
+	/**
1103
+	 *    _available_payment_method_inputs
1104
+	 *
1105
+	 * @access    private
1106
+	 * @param array $available_payment_method_options
1107
+	 * @return    EE_Form_Section_Proper
1108
+	 * @throws EE_Error
1109
+	 * @throws EE_Error
1110
+	 */
1111
+	private function _available_payment_method_inputs($available_payment_method_options = [])
1112
+	{
1113
+		// generate inputs
1114
+		return new EE_Form_Section_Proper(
1115
+			[
1116
+				'html_id'         => 'ee-available-payment-method-inputs',
1117
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1118
+				'subsections'     => [
1119
+					'' => new EE_Radio_Button_Input(
1120
+						$available_payment_method_options,
1121
+						[
1122
+							'html_name'          => 'selected_method_of_payment',
1123
+							'html_class'         => 'spco-payment-method',
1124
+							'default'            => $this->checkout->selected_method_of_payment,
1125
+							'label_size'         => 11,
1126
+							'enforce_label_size' => true,
1127
+						]
1128
+					),
1129
+				],
1130
+			]
1131
+		);
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 *    _payment_method_billing_info
1137
+	 *
1138
+	 * @access    private
1139
+	 * @param EE_Payment_Method $payment_method
1140
+	 * @return EE_Form_Section_Proper
1141
+	 * @throws EE_Error
1142
+	 * @throws InvalidArgumentException
1143
+	 * @throws InvalidDataTypeException
1144
+	 * @throws InvalidInterfaceException
1145
+	 */
1146
+	private function _payment_method_billing_info(EE_Payment_Method $payment_method)
1147
+	{
1148
+		$currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1149
+		// generate the billing form for payment method
1150
+		$billing_form                 = $currently_selected
1151
+			? $this->_get_billing_form_for_payment_method($payment_method)
1152
+			: new EE_Form_Section_HTML();
1153
+		$this->checkout->billing_form = $currently_selected
1154
+			? $billing_form
1155
+			: $this->checkout->billing_form;
1156
+		// it's all in the details
1157
+		$info_html = EEH_HTML::h3(
1158
+			esc_html__('Important information regarding your payment', 'event_espresso'),
1159
+			'',
1160
+			'spco-payment-method-hdr'
1161
+		);
1162
+		// add some info regarding the step, either from what's saved in the admin,
1163
+		// or a default string depending on whether the PM has a billing form or not
1164
+		if ($payment_method->description()) {
1165
+			$payment_method_info = $payment_method->description();
1166
+		} elseif ($billing_form instanceof EE_Billing_Info_Form) {
1167
+			$payment_method_info = sprintf(
1168
+				esc_html__(
1169
+					'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1170
+					'event_espresso'
1171
+				),
1172
+				$this->submit_button_text()
1173
+			);
1174
+		} else {
1175
+			$payment_method_info = sprintf(
1176
+				esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1177
+				$this->submit_button_text()
1178
+			);
1179
+		}
1180
+		$info_html .= EEH_HTML::div(
1181
+			apply_filters(
1182
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1183
+				$payment_method_info
1184
+			),
1185
+			'',
1186
+			'spco-payment-method-desc ee-attention'
1187
+		);
1188
+		return new EE_Form_Section_Proper(
1189
+			[
1190
+				'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1191
+				'html_class'      => 'spco-payment-method-info-dv',
1192
+				// only display the selected or default PM
1193
+				'html_style'      => $currently_selected ? '' : 'display:none;',
1194
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1195
+				'subsections'     => [
1196
+					'info'         => new EE_Form_Section_HTML($info_html),
1197
+					'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1198
+				],
1199
+			]
1200
+		);
1201
+	}
1202
+
1203
+
1204
+	/**
1205
+	 * get_billing_form_html_for_payment_method
1206
+	 *
1207
+	 * @return bool
1208
+	 * @throws EE_Error
1209
+	 * @throws InvalidArgumentException
1210
+	 * @throws ReflectionException
1211
+	 * @throws InvalidDataTypeException
1212
+	 * @throws InvalidInterfaceException
1213
+	 */
1214
+	public function get_billing_form_html_for_payment_method()
1215
+	{
1216
+		// how have they chosen to pay?
1217
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1218
+		$this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1219
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1220
+			return false;
1221
+		}
1222
+		if (
1223
+			apply_filters(
1224
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1225
+				false
1226
+			)
1227
+		) {
1228
+			EE_Error::add_success(
1229
+				apply_filters(
1230
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1231
+					sprintf(
1232
+						esc_html__(
1233
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1234
+							'event_espresso'
1235
+						),
1236
+						$this->checkout->payment_method->name()
1237
+					)
1238
+				)
1239
+			);
1240
+		}
1241
+		// now generate billing form for selected method of payment
1242
+		$payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1243
+		// fill form with attendee info if applicable
1244
+		if (
1245
+			$payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1246
+			&& $this->checkout->transaction_has_primary_registrant()
1247
+		) {
1248
+			$payment_method_billing_form->populate_from_attendee(
1249
+				$this->checkout->transaction->primary_registration()->attendee()
1250
+			);
1251
+		}
1252
+		// and debug content
1253
+		if (
1254
+			$payment_method_billing_form instanceof EE_Billing_Info_Form
1255
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1256
+		) {
1257
+			$payment_method_billing_form =
1258
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1259
+					$payment_method_billing_form
1260
+				);
1261
+		}
1262
+		$billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1263
+			? $payment_method_billing_form->get_html()
1264
+			: '';
1265
+		$this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1266
+		// localize validation rules for main form
1267
+		$this->checkout->current_step->reg_form->localize_validation_rules();
1268
+		$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1269
+		return true;
1270
+	}
1271
+
1272
+
1273
+	/**
1274
+	 * _get_billing_form_for_payment_method
1275
+	 *
1276
+	 * @param EE_Payment_Method $payment_method
1277
+	 * @return EE_Billing_Info_Form|EE_Form_Section_HTML
1278
+	 * @throws EE_Error
1279
+	 * @throws InvalidArgumentException
1280
+	 * @throws InvalidDataTypeException
1281
+	 * @throws InvalidInterfaceException
1282
+	 */
1283
+	private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1284
+	{
1285
+		$billing_form = $payment_method->type_obj()->billing_form(
1286
+			$this->checkout->transaction,
1287
+			['amount_owing' => $this->checkout->amount_owing]
1288
+		);
1289
+		if ($billing_form instanceof EE_Billing_Info_Form) {
1290
+			if (
1291
+				apply_filters(
1292
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1293
+					false
1294
+				)
1295
+				&& $this->request->requestParamIsSet('payment_method')
1296
+			) {
1297
+				EE_Error::add_success(
1298
+					apply_filters(
1299
+						'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1300
+						sprintf(
1301
+							esc_html__(
1302
+								'You have selected "%s" as your method of payment. Please note the important payment information below.',
1303
+								'event_espresso'
1304
+							),
1305
+							$payment_method->name()
1306
+						)
1307
+					)
1308
+				);
1309
+			}
1310
+			return apply_filters(
1311
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1312
+				$billing_form,
1313
+				$payment_method
1314
+			);
1315
+		}
1316
+		// no actual billing form, so return empty HTML form section
1317
+		return new EE_Form_Section_HTML();
1318
+	}
1319
+
1320
+
1321
+	/**
1322
+	 * _get_selected_method_of_payment
1323
+	 *
1324
+	 * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1325
+	 *                          is not found in the incoming request
1326
+	 * @param string  $request_param
1327
+	 * @return NULL|string
1328
+	 * @throws EE_Error
1329
+	 * @throws InvalidArgumentException
1330
+	 * @throws InvalidDataTypeException
1331
+	 * @throws InvalidInterfaceException
1332
+	 */
1333
+	private function _get_selected_method_of_payment(
1334
+		$required = false,
1335
+		$request_param = 'selected_method_of_payment'
1336
+	) {
1337
+		// is selected_method_of_payment set in the request ?
1338
+		$selected_method_of_payment = $this->request->getRequestParam($request_param);
1339
+		if ($selected_method_of_payment) {
1340
+			// sanitize it
1341
+			$selected_method_of_payment = is_array($selected_method_of_payment)
1342
+				? array_shift($selected_method_of_payment)
1343
+				: $selected_method_of_payment;
1344
+			$selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1345
+			// store it in the session so that it's available for all subsequent requests including AJAX
1346
+			$this->_save_selected_method_of_payment($selected_method_of_payment);
1347
+		} else {
1348
+			// or is is set in the session ?
1349
+			$selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1350
+				'selected_method_of_payment'
1351
+			);
1352
+		}
1353
+		// do ya really really gotta have it?
1354
+		if (empty($selected_method_of_payment) && $required) {
1355
+			EE_Error::add_error(
1356
+				sprintf(
1357
+					esc_html__(
1358
+						'The selected method of payment could not be determined.%sPlease ensure that you have selected one before proceeding.%sIf you continue to experience difficulties, then refresh your browser and try again, or contact %s for assistance.',
1359
+						'event_espresso'
1360
+					),
1361
+					'<br/>',
1362
+					'<br/>',
1363
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
1364
+				),
1365
+				__FILE__,
1366
+				__FUNCTION__,
1367
+				__LINE__
1368
+			);
1369
+			return null;
1370
+		}
1371
+		return $selected_method_of_payment;
1372
+	}
1373
+
1374
+
1375
+
1376
+
1377
+
1378
+
1379
+	/********************************************************************************************************/
1380
+	/***********************************  SWITCH PAYMENT METHOD  ************************************/
1381
+	/********************************************************************************************************/
1382
+	/**
1383
+	 * switch_payment_method
1384
+	 *
1385
+	 * @return bool
1386
+	 * @throws EE_Error
1387
+	 * @throws InvalidArgumentException
1388
+	 * @throws InvalidDataTypeException
1389
+	 * @throws InvalidInterfaceException
1390
+	 * @throws ReflectionException
1391
+	 */
1392
+	public function switch_payment_method()
1393
+	{
1394
+		if (! $this->_verify_payment_method_is_set()) {
1395
+			return false;
1396
+		}
1397
+		if (
1398
+			apply_filters(
1399
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1400
+				false
1401
+			)
1402
+		) {
1403
+			EE_Error::add_success(
1404
+				apply_filters(
1405
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1406
+					sprintf(
1407
+						esc_html__(
1408
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1409
+							'event_espresso'
1410
+						),
1411
+						$this->checkout->payment_method->name()
1412
+					)
1413
+				)
1414
+			);
1415
+		}
1416
+		// generate billing form for selected method of payment if it hasn't been done already
1417
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1418
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1419
+				$this->checkout->payment_method
1420
+			);
1421
+		}
1422
+		// fill form with attendee info if applicable
1423
+		if (
1424
+			apply_filters(
1425
+				'FHEE__populate_billing_form_fields_from_attendee',
1426
+				(
1427
+				$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1428
+				&& $this->checkout->transaction_has_primary_registrant()
1429
+				),
1430
+				$this->checkout->billing_form,
1431
+				$this->checkout->transaction
1432
+			)
1433
+		) {
1434
+			$this->checkout->billing_form->populate_from_attendee(
1435
+				$this->checkout->transaction->primary_registration()->attendee()
1436
+			);
1437
+		}
1438
+		// and debug content
1439
+		if (
1440
+			$this->checkout->billing_form instanceof EE_Billing_Info_Form
1441
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1442
+		) {
1443
+			$this->checkout->billing_form =
1444
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1445
+					$this->checkout->billing_form
1446
+				);
1447
+		}
1448
+		// get html and validation rules for form
1449
+		if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1450
+			$this->checkout->json_response->set_return_data(
1451
+				['payment_method_info' => $this->checkout->billing_form->get_html()]
1452
+			);
1453
+			// localize validation rules for main form
1454
+			$this->checkout->billing_form->localize_validation_rules(true);
1455
+			$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1456
+		} else {
1457
+			$this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1458
+		}
1459
+		// prevents advancement to next step
1460
+		$this->checkout->continue_reg = false;
1461
+		return true;
1462
+	}
1463
+
1464
+
1465
+	/**
1466
+	 * _verify_payment_method_is_set
1467
+	 *
1468
+	 * @return bool
1469
+	 * @throws EE_Error
1470
+	 * @throws InvalidArgumentException
1471
+	 * @throws ReflectionException
1472
+	 * @throws InvalidDataTypeException
1473
+	 * @throws InvalidInterfaceException
1474
+	 */
1475
+	protected function _verify_payment_method_is_set()
1476
+	{
1477
+		// generate billing form for selected method of payment if it hasn't been done already
1478
+		if (empty($this->checkout->selected_method_of_payment)) {
1479
+			// how have they chosen to pay?
1480
+			$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1481
+		} else {
1482
+			// choose your own adventure based on method_of_payment
1483
+			switch ($this->checkout->selected_method_of_payment) {
1484
+				case 'events_sold_out':
1485
+					EE_Error::add_attention(
1486
+						apply_filters(
1487
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1488
+							esc_html__(
1489
+								'It appears that the event you were about to make a payment for has sold out since this form first loaded. Please contact the event administrator if you believe this is an error.',
1490
+								'event_espresso'
1491
+							)
1492
+						),
1493
+						__FILE__,
1494
+						__FUNCTION__,
1495
+						__LINE__
1496
+					);
1497
+					return false;
1498
+				case 'payments_closed':
1499
+					EE_Error::add_attention(
1500
+						apply_filters(
1501
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1502
+							esc_html__(
1503
+								'It appears that the event you were about to make a payment for is not accepting payments at this time. Please contact the event administrator if you believe this is an error.',
1504
+								'event_espresso'
1505
+							)
1506
+						),
1507
+						__FILE__,
1508
+						__FUNCTION__,
1509
+						__LINE__
1510
+					);
1511
+					return false;
1512
+				case 'no_payment_required':
1513
+					EE_Error::add_attention(
1514
+						apply_filters(
1515
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1516
+							esc_html__(
1517
+								'It appears that the event you were about to make a payment for does not require payment. Please contact the event administrator if you believe this is an error.',
1518
+								'event_espresso'
1519
+							)
1520
+						),
1521
+						__FILE__,
1522
+						__FUNCTION__,
1523
+						__LINE__
1524
+					);
1525
+					return false;
1526
+				default:
1527
+			}
1528
+		}
1529
+		// verify payment method
1530
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1531
+			// get payment method for selected method of payment
1532
+			$this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1533
+		}
1534
+		return $this->checkout->payment_method instanceof EE_Payment_Method;
1535
+	}
1536
+
1537
+
1538
+
1539
+	/********************************************************************************************************/
1540
+	/***************************************  SAVE PAYER DETAILS  ****************************************/
1541
+	/********************************************************************************************************/
1542
+	/**
1543
+	 * save_payer_details_via_ajax
1544
+	 *
1545
+	 * @return void
1546
+	 * @throws EE_Error
1547
+	 * @throws InvalidArgumentException
1548
+	 * @throws ReflectionException
1549
+	 * @throws RuntimeException
1550
+	 * @throws InvalidDataTypeException
1551
+	 * @throws InvalidInterfaceException
1552
+	 */
1553
+	public function save_payer_details_via_ajax()
1554
+	{
1555
+		if (! $this->_verify_payment_method_is_set()) {
1556
+			return;
1557
+		}
1558
+		// generate billing form for selected method of payment if it hasn't been done already
1559
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1560
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1561
+				$this->checkout->payment_method
1562
+			);
1563
+		}
1564
+		// generate primary attendee from payer info if applicable
1565
+		if (! $this->checkout->transaction_has_primary_registrant()) {
1566
+			$attendee = $this->_create_attendee_from_request_data();
1567
+			if ($attendee instanceof EE_Attendee) {
1568
+				foreach ($this->checkout->transaction->registrations() as $registration) {
1569
+					if ($registration->is_primary_registrant()) {
1570
+						$this->checkout->primary_attendee_obj = $attendee;
1571
+						$registration->_add_relation_to($attendee, 'Attendee');
1572
+						$registration->set_attendee_id($attendee->ID());
1573
+						$registration->update_cache_after_object_save('Attendee', $attendee);
1574
+					}
1575
+				}
1576
+			}
1577
+		}
1578
+	}
1579
+
1580
+
1581
+	/**
1582
+	 * create_attendee_from_request_data
1583
+	 * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1584
+	 *
1585
+	 * @return EE_Attendee
1586
+	 * @throws EE_Error
1587
+	 * @throws InvalidArgumentException
1588
+	 * @throws ReflectionException
1589
+	 * @throws InvalidDataTypeException
1590
+	 * @throws InvalidInterfaceException
1591
+	 */
1592
+	protected function _create_attendee_from_request_data()
1593
+	{
1594
+		// get State ID
1595
+		$STA_ID = $this->request->getRequestParam('state');
1596
+		if (! empty($STA_ID)) {
1597
+			// can we get state object from name ?
1598
+			EE_Registry::instance()->load_model('State');
1599
+			$state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1600
+			$STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1601
+		}
1602
+		// get Country ISO
1603
+		$CNT_ISO = $this->request->getRequestParam('country');
1604
+		if (! empty($CNT_ISO)) {
1605
+			// can we get country object from name ?
1606
+			EE_Registry::instance()->load_model('Country');
1607
+			$country = EEM_Country::instance()->get_col(
1608
+				[['CNT_name' => $CNT_ISO], 'limit' => 1],
1609
+				'CNT_ISO'
1610
+			);
1611
+			$CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1612
+		}
1613
+		// grab attendee data
1614
+		$attendee_data = [
1615
+			'ATT_fname'    => $this->request->getRequestParam('first_name'),
1616
+			'ATT_lname'    => $this->request->getRequestParam('last_name'),
1617
+			'ATT_email'    => $this->request->getRequestParam('email'),
1618
+			'ATT_address'  => $this->request->getRequestParam('address'),
1619
+			'ATT_address2' => $this->request->getRequestParam('address2'),
1620
+			'ATT_city'     => $this->request->getRequestParam('city'),
1621
+			'STA_ID'       => $STA_ID,
1622
+			'CNT_ISO'      => $CNT_ISO,
1623
+			'ATT_zip'      => $this->request->getRequestParam('zip'),
1624
+			'ATT_phone'    => $this->request->getRequestParam('phone'),
1625
+		];
1626
+		// validate the email address since it is the most important piece of info
1627
+		if (empty($attendee_data['ATT_email'])) {
1628
+			EE_Error::add_error(
1629
+				esc_html__('An invalid email address was submitted.', 'event_espresso'),
1630
+				__FILE__,
1631
+				__FUNCTION__,
1632
+				__LINE__
1633
+			);
1634
+		}
1635
+		// does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1636
+		// AND email address
1637
+		if (
1638
+			! empty($attendee_data['ATT_fname'])
1639
+			&& ! empty($attendee_data['ATT_lname'])
1640
+			&& ! empty($attendee_data['ATT_email'])
1641
+		) {
1642
+			$existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1643
+				[
1644
+					'ATT_fname' => $attendee_data['ATT_fname'],
1645
+					'ATT_lname' => $attendee_data['ATT_lname'],
1646
+					'ATT_email' => $attendee_data['ATT_email'],
1647
+				]
1648
+			);
1649
+			if ($existing_attendee instanceof EE_Attendee) {
1650
+				return $existing_attendee;
1651
+			}
1652
+		}
1653
+		// no existing attendee? kk let's create a new one
1654
+		// kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1655
+		// don't exist
1656
+		$attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1657
+			? $attendee_data['ATT_fname']
1658
+			: $attendee_data['ATT_email'];
1659
+		$attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1660
+			? $attendee_data['ATT_lname']
1661
+			: $attendee_data['ATT_email'];
1662
+		return EE_Attendee::new_instance($attendee_data);
1663
+	}
1664
+
1665
+
1666
+
1667
+	/********************************************************************************************************/
1668
+	/****************************************  PROCESS REG STEP  *****************************************/
1669
+	/********************************************************************************************************/
1670
+	/**
1671
+	 * process_reg_step
1672
+	 *
1673
+	 * @return bool
1674
+	 * @throws EE_Error
1675
+	 * @throws InvalidArgumentException
1676
+	 * @throws ReflectionException
1677
+	 * @throws EntityNotFoundException
1678
+	 * @throws InvalidDataTypeException
1679
+	 * @throws InvalidInterfaceException
1680
+	 * @throws InvalidStatusException
1681
+	 */
1682
+	public function process_reg_step()
1683
+	{
1684
+		// how have they chosen to pay?
1685
+		$this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1686
+			? 'no_payment_required'
1687
+			: $this->_get_selected_method_of_payment(true);
1688
+		// choose your own adventure based on method_of_payment
1689
+		switch ($this->checkout->selected_method_of_payment) {
1690
+			case 'events_sold_out':
1691
+				$this->checkout->redirect     = true;
1692
+				$this->checkout->redirect_url = $this->checkout->cancel_page_url;
1693
+				$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1694
+				// mark this reg step as completed
1695
+				$this->set_completed();
1696
+				return false;
1697
+
1698
+			case 'payments_closed':
1699
+				if (
1700
+					apply_filters(
1701
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1702
+						false
1703
+					)
1704
+				) {
1705
+					EE_Error::add_success(
1706
+						esc_html__('no payment required at this time.', 'event_espresso'),
1707
+						__FILE__,
1708
+						__FUNCTION__,
1709
+						__LINE__
1710
+					);
1711
+				}
1712
+				// mark this reg step as completed
1713
+				$this->set_completed();
1714
+				return true;
1715
+
1716
+			case 'no_payment_required':
1717
+				if (
1718
+					apply_filters(
1719
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1720
+						false
1721
+					)
1722
+				) {
1723
+					EE_Error::add_success(
1724
+						esc_html__('no payment required.', 'event_espresso'),
1725
+						__FILE__,
1726
+						__FUNCTION__,
1727
+						__LINE__
1728
+					);
1729
+				}
1730
+				// mark this reg step as completed
1731
+				$this->set_completed();
1732
+				return true;
1733
+
1734
+			default:
1735
+				$registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1736
+					EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1737
+				);
1738
+				$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1739
+					$registrations,
1740
+					EE_Registry::instance()->SSN->checkout()->revisit
1741
+				);
1742
+				// calculate difference between the two arrays
1743
+				$registrations = array_diff($registrations, $ejected_registrations);
1744
+				if (empty($registrations)) {
1745
+					$this->_redirect_because_event_sold_out();
1746
+					return false;
1747
+				}
1748
+				$payment = $this->_process_payment();
1749
+				if ($payment instanceof EE_Payment) {
1750
+					$this->checkout->continue_reg = true;
1751
+					$this->_maybe_set_completed($payment);
1752
+				} else {
1753
+					$this->checkout->continue_reg = false;
1754
+				}
1755
+				return $payment instanceof EE_Payment;
1756
+		}
1757
+	}
1758
+
1759
+
1760
+	/**
1761
+	 * _redirect_because_event_sold_out
1762
+	 *
1763
+	 * @return void
1764
+	 */
1765
+	protected function _redirect_because_event_sold_out()
1766
+	{
1767
+		$this->checkout->continue_reg = false;
1768
+		// set redirect URL
1769
+		$this->checkout->redirect_url = add_query_arg(
1770
+			['e_reg_url_link' => $this->checkout->reg_url_link],
1771
+			$this->checkout->current_step->reg_step_url()
1772
+		);
1773
+		$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1774
+	}
1775
+
1776
+
1777
+	/**
1778
+	 * @param EE_Payment $payment
1779
+	 * @return void
1780
+	 * @throws EE_Error
1781
+	 */
1782
+	protected function _maybe_set_completed(EE_Payment $payment)
1783
+	{
1784
+		// Do we need to redirect them? If so, there's more work to be done.
1785
+		if (! $payment->redirect_url()) {
1786
+			$this->set_completed();
1787
+		}
1788
+	}
1789
+
1790
+
1791
+	/**
1792
+	 *    update_reg_step
1793
+	 *    this is the final step after a user  revisits the site to retry a payment
1794
+	 *
1795
+	 * @return bool
1796
+	 * @throws EE_Error
1797
+	 * @throws InvalidArgumentException
1798
+	 * @throws ReflectionException
1799
+	 * @throws EntityNotFoundException
1800
+	 * @throws InvalidDataTypeException
1801
+	 * @throws InvalidInterfaceException
1802
+	 * @throws InvalidStatusException
1803
+	 */
1804
+	public function update_reg_step()
1805
+	{
1806
+		$success = true;
1807
+		// if payment required
1808
+		if ($this->checkout->transaction->total() > 0) {
1809
+			do_action(
1810
+				'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1811
+				$this->checkout->transaction
1812
+			);
1813
+			// attempt payment via payment method
1814
+			$success = $this->process_reg_step();
1815
+		}
1816
+		if ($success && ! $this->checkout->redirect) {
1817
+			$this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1818
+				$this->checkout->transaction->ID()
1819
+			);
1820
+			// set return URL
1821
+			$this->checkout->redirect_url = add_query_arg(
1822
+				['e_reg_url_link' => $this->checkout->reg_url_link],
1823
+				$this->checkout->thank_you_page_url
1824
+			);
1825
+		}
1826
+		return $success;
1827
+	}
1828
+
1829
+
1830
+	/**
1831
+	 * @return EE_Payment|null
1832
+	 * @throws EE_Error
1833
+	 * @throws InvalidArgumentException
1834
+	 * @throws ReflectionException
1835
+	 * @throws RuntimeException
1836
+	 * @throws InvalidDataTypeException
1837
+	 * @throws InvalidInterfaceException
1838
+	 */
1839
+	private function _process_payment()
1840
+	{
1841
+		// basically confirm that the event hasn't sold out since they hit the page
1842
+		if (! $this->_last_second_ticket_verifications()) {
1843
+			return null;
1844
+		}
1845
+		// ya gotta make a choice man
1846
+		if (empty($this->checkout->selected_method_of_payment)) {
1847
+			$this->checkout->json_response->set_plz_select_method_of_payment(
1848
+				esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1849
+			);
1850
+			return null;
1851
+		}
1852
+		// get EE_Payment_Method object
1853
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1854
+			return null;
1855
+		}
1856
+		// setup billing form
1857
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1858
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1859
+				$this->checkout->payment_method
1860
+			);
1861
+			// bad billing form ?
1862
+			if (! $this->_billing_form_is_valid()) {
1863
+				return null;
1864
+			}
1865
+		}
1866
+		// ensure primary registrant has been fully processed
1867
+		if (! $this->_setup_primary_registrant_prior_to_payment()) {
1868
+			return null;
1869
+		}
1870
+		// if session is close to expiring (under 10 minutes by default)
1871
+		if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1872
+			// add some time to session expiration so that payment can be completed
1873
+			EE_Registry::instance()->SSN->extend_expiration();
1874
+		}
1875
+		/** @type EE_Transaction_Processor $transaction_processor */
1876
+		// $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1877
+		// in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1878
+		// for events with a default reg status of Approved
1879
+		// $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1880
+		//      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1881
+		// );
1882
+		// attempt payment
1883
+		$payment = $this->_attempt_payment($this->checkout->payment_method);
1884
+		// process results
1885
+		$payment = $this->_validate_payment($payment);
1886
+		$payment = $this->_post_payment_processing($payment);
1887
+		// verify payment
1888
+		if ($payment instanceof EE_Payment) {
1889
+			// store that for later
1890
+			$this->checkout->payment = $payment;
1891
+			// we can also consider the TXN to not have been failed, so temporarily upgrade it's status to abandoned
1892
+			$this->checkout->transaction->toggle_failed_transaction_status();
1893
+			$payment_status = $payment->status();
1894
+			if (
1895
+				$payment_status === EEM_Payment::status_id_approved
1896
+				|| $payment_status === EEM_Payment::status_id_pending
1897
+			) {
1898
+				return $payment;
1899
+			}
1900
+			return null;
1901
+		}
1902
+		if ($payment === true) {
1903
+			// please note that offline payment methods will NOT make a payment,
1904
+			// but instead just mark themselves as the PMD_ID on the transaction, and return true
1905
+			$this->checkout->payment = $payment;
1906
+			return $payment;
1907
+		}
1908
+		// where's my money?
1909
+		return null;
1910
+	}
1911
+
1912
+
1913
+	/**
1914
+	 * _last_second_ticket_verifications
1915
+	 *
1916
+	 * @return bool
1917
+	 * @throws EE_Error
1918
+	 * @throws ReflectionException
1919
+	 */
1920
+	protected function _last_second_ticket_verifications()
1921
+	{
1922
+		// don't bother re-validating if not a return visit
1923
+		if (! $this->checkout->revisit) {
1924
+			return true;
1925
+		}
1926
+		$registrations = $this->checkout->transaction->registrations();
1927
+		if (empty($registrations)) {
1928
+			return false;
1929
+		}
1930
+		foreach ($registrations as $registration) {
1931
+			if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1932
+				$event = $registration->event_obj();
1933
+				if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1934
+					EE_Error::add_error(
1935
+						apply_filters(
1936
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1937
+							sprintf(
1938
+								esc_html__(
1939
+									'It appears that the %1$s event that you were about to make a payment for has sold out since you first registered and/or arrived at this page. Please refresh the page and try again. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
1940
+									'event_espresso'
1941
+								),
1942
+								$event->name()
1943
+							)
1944
+						),
1945
+						__FILE__,
1946
+						__FUNCTION__,
1947
+						__LINE__
1948
+					);
1949
+					return false;
1950
+				}
1951
+			}
1952
+		}
1953
+		return true;
1954
+	}
1955
+
1956
+
1957
+	/**
1958
+	 * redirect_form
1959
+	 *
1960
+	 * @return bool
1961
+	 * @throws EE_Error
1962
+	 * @throws InvalidArgumentException
1963
+	 * @throws ReflectionException
1964
+	 * @throws InvalidDataTypeException
1965
+	 * @throws InvalidInterfaceException
1966
+	 */
1967
+	public function redirect_form()
1968
+	{
1969
+		$payment_method_billing_info = $this->_payment_method_billing_info(
1970
+			$this->_get_payment_method_for_selected_method_of_payment()
1971
+		);
1972
+		$html                        = $payment_method_billing_info->get_html();
1973
+		$html                        .= $this->checkout->redirect_form;
1974
+		/** @var ResponseInterface $response */
1975
+		$response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1976
+		$response->addOutput($html);
1977
+		return true;
1978
+	}
1979
+
1980
+
1981
+	/**
1982
+	 * _billing_form_is_valid
1983
+	 *
1984
+	 * @return bool
1985
+	 * @throws EE_Error
1986
+	 */
1987
+	private function _billing_form_is_valid()
1988
+	{
1989
+		if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1990
+			return true;
1991
+		}
1992
+		if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1993
+			if ($this->checkout->billing_form->was_submitted()) {
1994
+				$this->checkout->billing_form->receive_form_submission();
1995
+				if ($this->checkout->billing_form->is_valid()) {
1996
+					return true;
1997
+				}
1998
+				$validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
1999
+				$error_strings     = [];
2000
+				foreach ($validation_errors as $validation_error) {
2001
+					if ($validation_error instanceof EE_Validation_Error) {
2002
+						$form_section = $validation_error->get_form_section();
2003
+						if ($form_section instanceof EE_Form_Input_Base) {
2004
+							$label = $form_section->html_label_text();
2005
+						} elseif ($form_section instanceof EE_Form_Section_Base) {
2006
+							$label = $form_section->name();
2007
+						} else {
2008
+							$label = esc_html__('Validation Error', 'event_espresso');
2009
+						}
2010
+						$error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
2011
+					}
2012
+				}
2013
+				EE_Error::add_error(
2014
+					sprintf(
2015
+						esc_html__(
2016
+							'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
2017
+							'event_espresso'
2018
+						),
2019
+						'<br/>',
2020
+						implode('<br/>', $error_strings)
2021
+					),
2022
+					__FILE__,
2023
+					__FUNCTION__,
2024
+					__LINE__
2025
+				);
2026
+			} else {
2027
+				EE_Error::add_error(
2028
+					esc_html__(
2029
+						'The billing form was not submitted or something prevented it\'s submission.',
2030
+						'event_espresso'
2031
+					),
2032
+					__FILE__,
2033
+					__FUNCTION__,
2034
+					__LINE__
2035
+				);
2036
+			}
2037
+		} else {
2038
+			EE_Error::add_error(
2039
+				esc_html__(
2040
+					'The submitted billing form is invalid possibly due to a technical reason.',
2041
+					'event_espresso'
2042
+				),
2043
+				__FILE__,
2044
+				__FUNCTION__,
2045
+				__LINE__
2046
+			);
2047
+		}
2048
+		return false;
2049
+	}
2050
+
2051
+
2052
+	/**
2053
+	 * _setup_primary_registrant_prior_to_payment
2054
+	 * ensures that the primary registrant has a valid attendee object created with the critical details populated
2055
+	 * (first & last name & email) and that both the transaction object and primary registration object have been saved
2056
+	 * plz note that any other registrations will NOT be saved at this point (because they may not have any details
2057
+	 * yet)
2058
+	 *
2059
+	 * @return bool
2060
+	 * @throws EE_Error
2061
+	 * @throws InvalidArgumentException
2062
+	 * @throws ReflectionException
2063
+	 * @throws RuntimeException
2064
+	 * @throws InvalidDataTypeException
2065
+	 * @throws InvalidInterfaceException
2066
+	 */
2067
+	private function _setup_primary_registrant_prior_to_payment()
2068
+	{
2069
+		// check if transaction has a primary registrant and that it has a related Attendee object
2070
+		// if not, then we need to at least gather some primary registrant data before attempting payment
2071
+		if (
2072
+			$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2073
+			&& ! $this->checkout->transaction_has_primary_registrant()
2074
+			&& ! $this->_capture_primary_registration_data_from_billing_form()
2075
+		) {
2076
+			return false;
2077
+		}
2078
+		// because saving an object clears it's cache, we need to do the chevy shuffle
2079
+		// grab the primary_registration object
2080
+		$primary_registration = $this->checkout->transaction->primary_registration();
2081
+		// at this point we'll consider a TXN to not have been failed
2082
+		$this->checkout->transaction->toggle_failed_transaction_status();
2083
+		// save the TXN ( which clears cached copy of primary_registration)
2084
+		$this->checkout->transaction->save();
2085
+		// grab TXN ID and save it to the primary_registration
2086
+		$primary_registration->set_transaction_id($this->checkout->transaction->ID());
2087
+		// save what we have so far
2088
+		$primary_registration->save();
2089
+		return true;
2090
+	}
2091
+
2092
+
2093
+	/**
2094
+	 * _capture_primary_registration_data_from_billing_form
2095
+	 *
2096
+	 * @return bool
2097
+	 * @throws EE_Error
2098
+	 * @throws InvalidArgumentException
2099
+	 * @throws ReflectionException
2100
+	 * @throws InvalidDataTypeException
2101
+	 * @throws InvalidInterfaceException
2102
+	 */
2103
+	private function _capture_primary_registration_data_from_billing_form()
2104
+	{
2105
+		// convert billing form data into an attendee
2106
+		$this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2107
+		if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2108
+			EE_Error::add_error(
2109
+				sprintf(
2110
+					esc_html__(
2111
+						'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2112
+						'event_espresso'
2113
+					),
2114
+					'<br/>',
2115
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2116
+				),
2117
+				__FILE__,
2118
+				__FUNCTION__,
2119
+				__LINE__
2120
+			);
2121
+			return false;
2122
+		}
2123
+		$primary_registration = $this->checkout->transaction->primary_registration();
2124
+		if (! $primary_registration instanceof EE_Registration) {
2125
+			EE_Error::add_error(
2126
+				sprintf(
2127
+					esc_html__(
2128
+						'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2129
+						'event_espresso'
2130
+					),
2131
+					'<br/>',
2132
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2133
+				),
2134
+				__FILE__,
2135
+				__FUNCTION__,
2136
+				__LINE__
2137
+			);
2138
+			return false;
2139
+		}
2140
+		if (
2141
+			! $primary_registration->_add_relation_to($this->checkout->primary_attendee_obj, 'Attendee')
2142
+			  instanceof
2143
+			  EE_Attendee
2144
+		) {
2145
+			EE_Error::add_error(
2146
+				sprintf(
2147
+					esc_html__(
2148
+						'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2149
+						'event_espresso'
2150
+					),
2151
+					'<br/>',
2152
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2153
+				),
2154
+				__FILE__,
2155
+				__FUNCTION__,
2156
+				__LINE__
2157
+			);
2158
+			return false;
2159
+		}
2160
+		/** @type EE_Registration_Processor $registration_processor */
2161
+		$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2162
+		// at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2163
+		$registration_processor->toggle_incomplete_registration_status_to_default($primary_registration);
2164
+		return true;
2165
+	}
2166
+
2167
+
2168
+	/**
2169
+	 * _get_payment_method_for_selected_method_of_payment
2170
+	 * retrieves a valid payment method
2171
+	 *
2172
+	 * @return EE_Payment_Method
2173
+	 * @throws EE_Error
2174
+	 * @throws InvalidArgumentException
2175
+	 * @throws ReflectionException
2176
+	 * @throws InvalidDataTypeException
2177
+	 * @throws InvalidInterfaceException
2178
+	 */
2179
+	private function _get_payment_method_for_selected_method_of_payment()
2180
+	{
2181
+		if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2182
+			$this->_redirect_because_event_sold_out();
2183
+			return null;
2184
+		}
2185
+		// get EE_Payment_Method object
2186
+		if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2187
+			$payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2188
+		} else {
2189
+			// load EEM_Payment_Method
2190
+			EE_Registry::instance()->load_model('Payment_Method');
2191
+			$EEM_Payment_Method = EEM_Payment_Method::instance();
2192
+			$payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2193
+		}
2194
+		// verify $payment_method
2195
+		if (! $payment_method instanceof EE_Payment_Method) {
2196
+			// not a payment
2197
+			EE_Error::add_error(
2198
+				sprintf(
2199
+					esc_html__(
2200
+						'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2201
+						'event_espresso'
2202
+					),
2203
+					'<br/>',
2204
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2205
+				),
2206
+				__FILE__,
2207
+				__FUNCTION__,
2208
+				__LINE__
2209
+			);
2210
+			return null;
2211
+		}
2212
+		// and verify it has a valid Payment_Method Type object
2213
+		if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2214
+			// not a payment
2215
+			EE_Error::add_error(
2216
+				sprintf(
2217
+					esc_html__(
2218
+						'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2219
+						'event_espresso'
2220
+					),
2221
+					'<br/>',
2222
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2223
+				),
2224
+				__FILE__,
2225
+				__FUNCTION__,
2226
+				__LINE__
2227
+			);
2228
+			return null;
2229
+		}
2230
+		return $payment_method;
2231
+	}
2232
+
2233
+
2234
+	/**
2235
+	 *    _attempt_payment
2236
+	 *
2237
+	 * @access    private
2238
+	 * @type    EE_Payment_Method $payment_method
2239
+	 * @return mixed EE_Payment | boolean
2240
+	 * @throws EE_Error
2241
+	 * @throws InvalidArgumentException
2242
+	 * @throws ReflectionException
2243
+	 * @throws InvalidDataTypeException
2244
+	 * @throws InvalidInterfaceException
2245
+	 */
2246
+	private function _attempt_payment(EE_Payment_Method $payment_method)
2247
+	{
2248
+		$payment = null;
2249
+		$this->checkout->transaction->save();
2250
+		$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2251
+		if (! $payment_processor instanceof EE_Payment_Processor) {
2252
+			return false;
2253
+		}
2254
+		try {
2255
+			$payment_processor->set_revisit($this->checkout->revisit);
2256
+			// generate payment object
2257
+			$payment = $payment_processor->process_payment(
2258
+				$payment_method,
2259
+				$this->checkout->transaction,
2260
+				$this->checkout->amount_owing,
2261
+				$this->checkout->billing_form,
2262
+				$this->_get_return_url($payment_method),
2263
+				'CART',
2264
+				$this->checkout->admin_request,
2265
+				true,
2266
+				$this->reg_step_url()
2267
+			);
2268
+		} catch (Exception $e) {
2269
+			$this->_handle_payment_processor_exception($e);
2270
+		}
2271
+		return $payment;
2272
+	}
2273
+
2274
+
2275
+	/**
2276
+	 * _handle_payment_processor_exception
2277
+	 *
2278
+	 * @param Exception $e
2279
+	 * @return void
2280
+	 * @throws EE_Error
2281
+	 * @throws InvalidArgumentException
2282
+	 * @throws InvalidDataTypeException
2283
+	 * @throws InvalidInterfaceException
2284
+	 */
2285
+	protected function _handle_payment_processor_exception(Exception $e)
2286
+	{
2287
+		EE_Error::add_error(
2288
+			sprintf(
2289
+				esc_html__(
2290
+					'The payment could not br processed due to a technical issue.%1$sPlease try again or contact %2$s for assistance.||The following Exception was thrown in %4$s on line %5$s:%1$s%3$s',
2291
+					'event_espresso'
2292
+				),
2293
+				'<br/>',
2294
+				EE_Registry::instance()->CFG->organization->get_pretty('email'),
2295
+				$e->getMessage(),
2296
+				$e->getFile(),
2297
+				$e->getLine()
2298
+			),
2299
+			__FILE__,
2300
+			__FUNCTION__,
2301
+			__LINE__
2302
+		);
2303
+	}
2304
+
2305
+
2306
+	/**
2307
+	 * _get_return_url
2308
+	 *
2309
+	 * @param EE_Payment_Method $payment_method
2310
+	 * @return string
2311
+	 * @throws EE_Error
2312
+	 * @throws ReflectionException
2313
+	 */
2314
+	protected function _get_return_url(EE_Payment_Method $payment_method)
2315
+	{
2316
+		$return_url = '';
2317
+		switch ($payment_method->type_obj()->payment_occurs()) {
2318
+			case EE_PMT_Base::offsite:
2319
+				$return_url = add_query_arg(
2320
+					[
2321
+						'action'                     => 'process_gateway_response',
2322
+						'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2323
+						'spco_txn'                   => $this->checkout->transaction->ID(),
2324
+					],
2325
+					$this->reg_step_url()
2326
+				);
2327
+				break;
2328
+			case EE_PMT_Base::onsite:
2329
+			case EE_PMT_Base::offline:
2330
+				$return_url = $this->checkout->next_step->reg_step_url();
2331
+				break;
2332
+		}
2333
+		return $return_url;
2334
+	}
2335
+
2336
+
2337
+	/**
2338
+	 * _validate_payment
2339
+	 *
2340
+	 * @param EE_Payment $payment
2341
+	 * @return EE_Payment|FALSE
2342
+	 * @throws EE_Error
2343
+	 * @throws InvalidArgumentException
2344
+	 * @throws InvalidDataTypeException
2345
+	 * @throws InvalidInterfaceException
2346
+	 */
2347
+	private function _validate_payment($payment = null)
2348
+	{
2349
+		if ($this->checkout->payment_method->is_off_line()) {
2350
+			return true;
2351
+		}
2352
+		// verify payment object
2353
+		if (! $payment instanceof EE_Payment) {
2354
+			// not a payment
2355
+			EE_Error::add_error(
2356
+				sprintf(
2357
+					esc_html__(
2358
+						'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2359
+						'event_espresso'
2360
+					),
2361
+					'<br/>',
2362
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2363
+				),
2364
+				__FILE__,
2365
+				__FUNCTION__,
2366
+				__LINE__
2367
+			);
2368
+			return false;
2369
+		}
2370
+		return $payment;
2371
+	}
2372
+
2373
+
2374
+	/**
2375
+	 * _post_payment_processing
2376
+	 *
2377
+	 * @param EE_Payment|bool $payment
2378
+	 * @return bool|EE_Payment
2379
+	 * @throws EE_Error
2380
+	 * @throws InvalidArgumentException
2381
+	 * @throws InvalidDataTypeException
2382
+	 * @throws InvalidInterfaceException
2383
+	 * @throws ReflectionException
2384
+	 */
2385
+	private function _post_payment_processing($payment = null)
2386
+	{
2387
+		// Off-Line payment?
2388
+		if ($payment === true) {
2389
+			return true;
2390
+		}
2391
+		if ($payment instanceof EE_Payment) {
2392
+			// Should the user be redirected?
2393
+			if ($payment->redirect_url()) {
2394
+				do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2395
+				$this->checkout->redirect      = true;
2396
+				$this->checkout->redirect_form = $payment->redirect_form();
2397
+				$this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2398
+				// set JSON response
2399
+				$this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2400
+				// and lastly, let's bump the payment status to pending
2401
+				$payment->set_status(EEM_Payment::status_id_pending);
2402
+				$payment->save();
2403
+			} elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2404
+				// User shouldn't be redirected. So let's process it here.
2405
+				// $this->_setup_redirect_for_next_step();
2406
+				$this->checkout->continue_reg = false;
2407
+			}
2408
+			return $payment;
2409
+		}
2410
+		// ummm ya... not Off-Line, not On-Site, not off-Site ????
2411
+		$this->checkout->continue_reg = false;
2412
+		return false;
2413
+	}
2414
+
2415
+
2416
+	/**
2417
+	 *    _process_payment_status
2418
+	 *
2419
+	 * @type    EE_Payment $payment
2420
+	 * @param string       $payment_occurs
2421
+	 * @return bool
2422
+	 * @throws EE_Error
2423
+	 * @throws InvalidArgumentException
2424
+	 * @throws InvalidDataTypeException
2425
+	 * @throws InvalidInterfaceException
2426
+	 */
2427
+	private function _process_payment_status($payment, $payment_occurs = EE_PMT_Base::offline)
2428
+	{
2429
+		// off-line payment? carry on
2430
+		if ($payment_occurs === EE_PMT_Base::offline) {
2431
+			return true;
2432
+		}
2433
+		// verify payment validity
2434
+		if ($payment instanceof EE_Payment) {
2435
+			do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2436
+			$msg = $payment->gateway_response();
2437
+			// check results
2438
+			switch ($payment->status()) {
2439
+				// good payment
2440
+				case EEM_Payment::status_id_approved:
2441
+					EE_Error::add_success(
2442
+						esc_html__('Your payment was processed successfully.', 'event_espresso'),
2443
+						__FILE__,
2444
+						__FUNCTION__,
2445
+						__LINE__
2446
+					);
2447
+					return true;
2448
+				// slow payment
2449
+				case EEM_Payment::status_id_pending:
2450
+					if (empty($msg)) {
2451
+						$msg = esc_html__(
2452
+							'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2453
+							'event_espresso'
2454
+						);
2455
+					}
2456
+					EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2457
+					return true;
2458
+				// don't wanna payment
2459
+				case EEM_Payment::status_id_cancelled:
2460
+					if (empty($msg)) {
2461
+						$msg = _n(
2462
+							'Payment cancelled. Please try again.',
2463
+							'Payment cancelled. Please try again or select another method of payment.',
2464
+							count($this->checkout->available_payment_methods),
2465
+							'event_espresso'
2466
+						);
2467
+					}
2468
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2469
+					return false;
2470
+				// not enough payment
2471
+				case EEM_Payment::status_id_declined:
2472
+					if (empty($msg)) {
2473
+						$msg = _n(
2474
+							'We\'re sorry but your payment was declined. Please try again.',
2475
+							'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2476
+							count($this->checkout->available_payment_methods),
2477
+							'event_espresso'
2478
+						);
2479
+					}
2480
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2481
+					return false;
2482
+				// bad payment
2483
+				case EEM_Payment::status_id_failed:
2484
+					if (! empty($msg)) {
2485
+						EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2486
+						return false;
2487
+					}
2488
+					// default to error below
2489
+					break;
2490
+			}
2491
+		}
2492
+		// off-site payment gateway responses are too unreliable, so let's just assume that
2493
+		// the payment processing is just running slower than the registrant's request
2494
+		if ($payment_occurs === EE_PMT_Base::offsite) {
2495
+			return true;
2496
+		}
2497
+		EE_Error::add_error(
2498
+			sprintf(
2499
+				esc_html__(
2500
+					'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2501
+					'event_espresso'
2502
+				),
2503
+				'<br/>',
2504
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
2505
+			),
2506
+			__FILE__,
2507
+			__FUNCTION__,
2508
+			__LINE__
2509
+		);
2510
+		return false;
2511
+	}
2512
+
2513
+
2514
+
2515
+
2516
+
2517
+
2518
+	/********************************************************************************************************/
2519
+	/**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2520
+	/********************************************************************************************************/
2521
+	/**
2522
+	 * process_gateway_response
2523
+	 * this is the return point for Off-Site Payment Methods
2524
+	 * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2525
+	 * otherwise, it will load up the last payment made for the TXN.
2526
+	 * If the payment retrieved looks good, it will then either:
2527
+	 *    complete the current step and allow advancement to the next reg step
2528
+	 *        or present the payment options again
2529
+	 *
2530
+	 * @return bool
2531
+	 * @throws EE_Error
2532
+	 * @throws InvalidArgumentException
2533
+	 * @throws ReflectionException
2534
+	 * @throws InvalidDataTypeException
2535
+	 * @throws InvalidInterfaceException
2536
+	 */
2537
+	public function process_gateway_response()
2538
+	{
2539
+		// how have they chosen to pay?
2540
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2541
+		// get EE_Payment_Method object
2542
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2543
+			$this->checkout->continue_reg = false;
2544
+			return false;
2545
+		}
2546
+		if (! $this->checkout->payment_method->is_off_site()) {
2547
+			return false;
2548
+		}
2549
+		$this->_validate_offsite_return();
2550
+		// verify TXN
2551
+		if ($this->checkout->transaction instanceof EE_Transaction) {
2552
+			$gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2553
+			if (! $gateway instanceof EE_Offsite_Gateway) {
2554
+				$this->checkout->continue_reg = false;
2555
+				return false;
2556
+			}
2557
+			$payment = $this->_process_off_site_payment($gateway);
2558
+			$payment = $this->_process_cancelled_payments($payment);
2559
+			$payment = $this->_validate_payment($payment);
2560
+			// if payment was not declined by the payment gateway or cancelled by the registrant
2561
+			if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2562
+				// $this->_setup_redirect_for_next_step();
2563
+				// store that for later
2564
+				$this->checkout->payment = $payment;
2565
+				// mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2566
+				// because we will complete this step during the IPN processing then
2567
+				if (! $this->handle_IPN_in_this_request()) {
2568
+					$this->set_completed();
2569
+				}
2570
+				return true;
2571
+			}
2572
+		}
2573
+		// DEBUG LOG
2574
+		// $this->checkout->log(
2575
+		//     __CLASS__,
2576
+		//     __FUNCTION__,
2577
+		//     __LINE__,
2578
+		//     array('payment' => $payment)
2579
+		// );
2580
+		$this->checkout->continue_reg = false;
2581
+		return false;
2582
+	}
2583
+
2584
+
2585
+	/**
2586
+	 * _validate_return
2587
+	 *
2588
+	 * @return void
2589
+	 * @throws EE_Error
2590
+	 * @throws InvalidArgumentException
2591
+	 * @throws InvalidDataTypeException
2592
+	 * @throws InvalidInterfaceException
2593
+	 * @throws ReflectionException
2594
+	 */
2595
+	private function _validate_offsite_return()
2596
+	{
2597
+		$TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2598
+		if ($TXN_ID !== $this->checkout->transaction->ID()) {
2599
+			// Houston... we might have a problem
2600
+			$invalid_TXN = false;
2601
+			// first gather some info
2602
+			$valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2603
+			$primary_registrant = $valid_TXN instanceof EE_Transaction
2604
+				? $valid_TXN->primary_registration()
2605
+				: null;
2606
+			// let's start by retrieving the cart for this TXN
2607
+			$cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2608
+			if ($cart instanceof EE_Cart) {
2609
+				// verify that the current cart has tickets
2610
+				$tickets = $cart->get_tickets();
2611
+				if (empty($tickets)) {
2612
+					$invalid_TXN = true;
2613
+				}
2614
+			} else {
2615
+				$invalid_TXN = true;
2616
+			}
2617
+			$valid_TXN_SID = $primary_registrant instanceof EE_Registration
2618
+				? $primary_registrant->session_ID()
2619
+				: null;
2620
+			// validate current Session ID and compare against valid TXN session ID
2621
+			if (
2622
+				$invalid_TXN // if this is already true, then skip other checks
2623
+				|| EE_Session::instance()->id() === null
2624
+				|| (
2625
+					// WARNING !!!
2626
+					// this could be PayPal sending back duplicate requests (ya they do that)
2627
+					// or it **could** mean someone is simply registering AGAIN after having just done so
2628
+					// so now we need to determine if this current TXN looks valid or not
2629
+					// and whether this reg step has even been started ?
2630
+					EE_Session::instance()->id() === $valid_TXN_SID
2631
+					// really? you're half way through this reg step, but you never started it ?
2632
+					&& $this->checkout->transaction->reg_step_completed($this->slug()) === false
2633
+				)
2634
+			) {
2635
+				$invalid_TXN = true;
2636
+			}
2637
+			if ($invalid_TXN) {
2638
+				// is the valid TXN completed ?
2639
+				if ($valid_TXN instanceof EE_Transaction) {
2640
+					// has this step even been started ?
2641
+					$reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2642
+					if ($reg_step_completed !== false && $reg_step_completed !== true) {
2643
+						// so it **looks** like this is a double request from PayPal
2644
+						// so let's try to pick up where we left off
2645
+						$this->checkout->transaction = $valid_TXN;
2646
+						$this->checkout->refresh_all_entities(true);
2647
+						return;
2648
+					}
2649
+				}
2650
+				// you appear to be lost?
2651
+				$this->_redirect_wayward_request($primary_registrant);
2652
+			}
2653
+		}
2654
+	}
2655
+
2656
+
2657
+	/**
2658
+	 * _redirect_wayward_request
2659
+	 *
2660
+	 * @param EE_Registration|null $primary_registrant
2661
+	 * @return void
2662
+	 * @throws EE_Error
2663
+	 * @throws InvalidArgumentException
2664
+	 * @throws InvalidDataTypeException
2665
+	 * @throws InvalidInterfaceException
2666
+	 * @throws ReflectionException
2667
+	 */
2668
+	private function _redirect_wayward_request(EE_Registration $primary_registrant)
2669
+	{
2670
+		if (! $primary_registrant instanceof EE_Registration) {
2671
+			// try redirecting based on the current TXN
2672
+			$primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2673
+				? $this->checkout->transaction->primary_registration()
2674
+				: null;
2675
+		}
2676
+		if (! $primary_registrant instanceof EE_Registration) {
2677
+			EE_Error::add_error(
2678
+				sprintf(
2679
+					esc_html__(
2680
+						'Invalid information was received from the Off-Site Payment Processor and your Transaction details could not be retrieved from the database.%1$sPlease try again or contact %2$s for assistance.',
2681
+						'event_espresso'
2682
+					),
2683
+					'<br/>',
2684
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2685
+				),
2686
+				__FILE__,
2687
+				__FUNCTION__,
2688
+				__LINE__
2689
+			);
2690
+			return;
2691
+		}
2692
+		// make sure transaction is not locked
2693
+		$this->checkout->transaction->unlock();
2694
+		wp_safe_redirect(
2695
+			add_query_arg(
2696
+				[
2697
+					'e_reg_url_link' => $primary_registrant->reg_url_link(),
2698
+				],
2699
+				$this->checkout->thank_you_page_url
2700
+			)
2701
+		);
2702
+		exit();
2703
+	}
2704
+
2705
+
2706
+	/**
2707
+	 * _process_off_site_payment
2708
+	 *
2709
+	 * @param EE_Offsite_Gateway $gateway
2710
+	 * @return EE_Payment
2711
+	 * @throws EE_Error
2712
+	 * @throws InvalidArgumentException
2713
+	 * @throws InvalidDataTypeException
2714
+	 * @throws InvalidInterfaceException
2715
+	 * @throws ReflectionException
2716
+	 */
2717
+	private function _process_off_site_payment(EE_Offsite_Gateway $gateway)
2718
+	{
2719
+		try {
2720
+			$request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2721
+			$request_data = $request->requestParams();
2722
+			// if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2723
+			$this->set_handle_IPN_in_this_request(
2724
+				$gateway->handle_IPN_in_this_request($request_data, false)
2725
+			);
2726
+			if ($this->handle_IPN_in_this_request()) {
2727
+				// get payment details and process results
2728
+				/** @type EE_Payment_Processor $payment_processor */
2729
+				$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2730
+				$payment           = $payment_processor->process_ipn(
2731
+					$request_data,
2732
+					$this->checkout->transaction,
2733
+					$this->checkout->payment_method,
2734
+					true,
2735
+					false
2736
+				);
2737
+				// $payment_source = 'process_ipn';
2738
+			} else {
2739
+				$payment = $this->checkout->transaction->last_payment();
2740
+				// $payment_source = 'last_payment';
2741
+			}
2742
+		} catch (Exception $e) {
2743
+			// let's just eat the exception and try to move on using any previously set payment info
2744
+			$payment = $this->checkout->transaction->last_payment();
2745
+			// $payment_source = 'last_payment after Exception';
2746
+			// but if we STILL don't have a payment object
2747
+			if (! $payment instanceof EE_Payment) {
2748
+				// then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2749
+				$this->_handle_payment_processor_exception($e);
2750
+			}
2751
+		}
2752
+		return $payment;
2753
+	}
2754
+
2755
+
2756
+	/**
2757
+	 * _process_cancelled_payments
2758
+	 * just makes sure that the payment status gets updated correctly
2759
+	 * so tha tan error isn't generated during payment validation
2760
+	 *
2761
+	 * @param EE_Payment $payment
2762
+	 * @return EE_Payment|null
2763
+	 * @throws EE_Error
2764
+	 */
2765
+	private function _process_cancelled_payments($payment = null)
2766
+	{
2767
+		if (
2768
+			$payment instanceof EE_Payment
2769
+			&& $this->request->requestParamIsSet('ee_cancel_payment')
2770
+			&& $payment->status() === EEM_Payment::status_id_failed
2771
+		) {
2772
+			$payment->set_status(EEM_Payment::status_id_cancelled);
2773
+		}
2774
+		return $payment;
2775
+	}
2776
+
2777
+
2778
+	/**
2779
+	 *    get_transaction_details_for_gateways
2780
+	 *
2781
+	 * @access    public
2782
+	 * @return void
2783
+	 * @throws EE_Error
2784
+	 * @throws InvalidArgumentException
2785
+	 * @throws ReflectionException
2786
+	 * @throws InvalidDataTypeException
2787
+	 * @throws InvalidInterfaceException
2788
+	 */
2789
+	public function get_transaction_details_for_gateways()
2790
+	{
2791
+		$txn_details = [];
2792
+		// ya gotta make a choice man
2793
+		if (empty($this->checkout->selected_method_of_payment)) {
2794
+			$txn_details = [
2795
+				'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2796
+			];
2797
+		}
2798
+		// get EE_Payment_Method object
2799
+		if (
2800
+			empty($txn_details)
2801
+			&& ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2802
+		) {
2803
+			$txn_details = [
2804
+				'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2805
+				'error'                      => esc_html__(
2806
+					'A valid Payment Method could not be determined.',
2807
+					'event_espresso'
2808
+				),
2809
+			];
2810
+		}
2811
+		if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2812
+			$return_url  = $this->_get_return_url($this->checkout->payment_method);
2813
+			$txn_details = [
2814
+				'TXN_ID'         => $this->checkout->transaction->ID(),
2815
+				'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2816
+				'TXN_total'      => $this->checkout->transaction->total(),
2817
+				'TXN_paid'       => $this->checkout->transaction->paid(),
2818
+				'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2819
+				'STS_ID'         => $this->checkout->transaction->status_ID(),
2820
+				'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2821
+				'payment_amount' => $this->checkout->amount_owing,
2822
+				'return_url'     => $return_url,
2823
+				'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2824
+				'notify_url'     => EE_Config::instance()->core->txn_page_url(
2825
+					[
2826
+						'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2827
+						'ee_payment_method' => $this->checkout->payment_method->slug(),
2828
+					]
2829
+				),
2830
+			];
2831
+		}
2832
+		echo wp_json_encode($txn_details);
2833
+		exit();
2834
+	}
2835
+
2836
+
2837
+	/**
2838
+	 *    __sleep
2839
+	 * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2840
+	 * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2841
+	 * reg form, because if needed, it will be regenerated anyways
2842
+	 *
2843
+	 * @return array
2844
+	 */
2845
+	public function __sleep()
2846
+	{
2847
+		// remove the reg form and the checkout
2848
+		return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2849
+	}
2850 2850
 }
Please login to merge, or discard this patch.
Spacing   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -133,7 +133,7 @@  discard block
 block discarded – undo
133 133
         $this->request   = EED_Single_Page_Checkout::getRequest();
134 134
         $this->_slug     = 'payment_options';
135 135
         $this->_name     = esc_html__('Payment Options', 'event_espresso');
136
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
136
+        $this->_template = SPCO_REG_STEPS_PATH.$this->_slug.'/payment_options_main.template.php';
137 137
         $this->checkout  = $checkout;
138 138
         $this->_reset_success_message();
139 139
         $this->set_instructions(
@@ -188,7 +188,7 @@  discard block
 block discarded – undo
188 188
      */
189 189
     public function translate_js_strings()
190 190
     {
191
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
191
+        EE_Registry::$i18n_js_strings['no_payment_method'] = esc_html__(
192 192
             'Please select a method of payment in order to continue.',
193 193
             'event_espresso'
194 194
         );
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
             'A valid method of payment could not be determined. Please refresh the page and try again.',
197 197
             'event_espresso'
198 198
         );
199
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
199
+        EE_Registry::$i18n_js_strings['forwarding_to_offsite'] = esc_html__(
200 200
             'Forwarding to Secure Payment Provider.',
201 201
             'event_espresso'
202 202
         );
@@ -217,7 +217,7 @@  discard block
 block discarded – undo
217 217
     {
218 218
         $transaction = $this->checkout->transaction;
219 219
         // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
220
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
220
+        if ( ! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
221 221
             return;
222 222
         }
223 223
         foreach (
@@ -314,18 +314,18 @@  discard block
 block discarded – undo
314 314
         foreach ($registrations as $REG_ID => $registration) {
315 315
             /** @var $registration EE_Registration */
316 316
             // has this registration lost it's space ?
317
-            if (isset($ejected_registrations[ $REG_ID ])) {
317
+            if (isset($ejected_registrations[$REG_ID])) {
318 318
                 if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
319
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
319
+                    $sold_out_events[$registration->event()->ID()] = $registration->event();
320 320
                 } else {
321
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
321
+                    $insufficient_spaces_available[$registration->event()->ID()] = $registration->event();
322 322
                 }
323 323
                 continue;
324 324
             }
325 325
             // event requires admin approval
326 326
             if ($registration->status_ID() === EEM_Registration::status_id_not_approved) {
327 327
                 // add event to list of events with pre-approval reg status
328
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
328
+                $registrations_requiring_pre_approval[$REG_ID] = $registration;
329 329
                 do_action(
330 330
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
331 331
                     $registration->event(),
@@ -342,7 +342,7 @@  discard block
 block discarded – undo
342 342
                 )
343 343
             ) {
344 344
                 // add event to list of events that are sold out
345
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
345
+                $sold_out_events[$registration->event()->ID()] = $registration->event();
346 346
                 do_action(
347 347
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
348 348
                     $registration->event(),
@@ -352,7 +352,7 @@  discard block
 block discarded – undo
352 352
             }
353 353
             // are they allowed to pay now and is there monies owing?
354 354
             if ($registration->owes_monies_and_can_pay()) {
355
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
355
+                $registrations_requiring_payment[$REG_ID] = $registration;
356 356
                 do_action(
357 357
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
358 358
                     $registration->event(),
@@ -363,28 +363,28 @@  discard block
 block discarded – undo
363 363
                       && $registration->status_ID() !== EEM_Registration::status_id_not_approved
364 364
                       && $registration->ticket()->is_free()
365 365
             ) {
366
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
366
+                $registrations_for_free_events[$registration->ticket()->ID()] = $registration;
367 367
             }
368 368
         }
369 369
         $subsections = [];
370 370
         // now decide which template to load
371
-        if (! empty($sold_out_events)) {
371
+        if ( ! empty($sold_out_events)) {
372 372
             $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
373 373
         }
374
-        if (! empty($insufficient_spaces_available)) {
374
+        if ( ! empty($insufficient_spaces_available)) {
375 375
             $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
376 376
                 $insufficient_spaces_available
377 377
             );
378 378
         }
379
-        if (! empty($registrations_requiring_pre_approval)) {
379
+        if ( ! empty($registrations_requiring_pre_approval)) {
380 380
             $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
381 381
                 $registrations_requiring_pre_approval
382 382
             );
383 383
         }
384
-        if (! empty($registrations_for_free_events)) {
384
+        if ( ! empty($registrations_for_free_events)) {
385 385
             $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
386 386
         }
387
-        if (! empty($registrations_requiring_payment)) {
387
+        if ( ! empty($registrations_requiring_payment)) {
388 388
             if ($this->checkout->amount_owing > 0) {
389 389
                 // autoload Line_Item_Display classes
390 390
                 EEH_Autoloader::register_line_item_filter_autoloaders();
@@ -405,7 +405,7 @@  discard block
 block discarded – undo
405 405
                         ['registrations' => $registrations]
406 406
                     )
407 407
                 );
408
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
408
+                $this->checkout->amount_owing = $filtered_line_item_tree->total();
409 409
                 $this->_apply_registration_payments_to_amount_owing($registrations);
410 410
             }
411 411
             $no_payment_required = false;
@@ -449,7 +449,7 @@  discard block
 block discarded – undo
449 449
      */
450 450
     public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
451 451
     {
452
-        if (! EE_Registry::instance()->SSN instanceof EE_Session
452
+        if ( ! EE_Registry::instance()->SSN instanceof EE_Session
453 453
             || ! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout
454 454
             || ! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction
455 455
         ) {
@@ -492,8 +492,8 @@  discard block
 block discarded – undo
492 492
         );
493 493
         foreach ($registrations as $REG_ID => $registration) {
494 494
             // has this registration lost it's space ?
495
-            if (isset($ejected_registrations[ $REG_ID ])) {
496
-                unset($registrations[ $REG_ID ]);
495
+            if (isset($ejected_registrations[$REG_ID])) {
496
+                unset($registrations[$REG_ID]);
497 497
             }
498 498
         }
499 499
         return $registrations;
@@ -543,25 +543,25 @@  discard block
 block discarded – undo
543 543
             }
544 544
             $EVT_ID = $registration->event_ID();
545 545
             $ticket = $registration->ticket();
546
-            if (! isset($tickets_remaining[ $ticket->ID() ])) {
547
-                $tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
546
+            if ( ! isset($tickets_remaining[$ticket->ID()])) {
547
+                $tickets_remaining[$ticket->ID()] = $ticket->remaining();
548 548
             }
549
-            if ($tickets_remaining[ $ticket->ID() ] > 0) {
550
-                if (! isset($event_reg_count[ $EVT_ID ])) {
551
-                    $event_reg_count[ $EVT_ID ] = 0;
549
+            if ($tickets_remaining[$ticket->ID()] > 0) {
550
+                if ( ! isset($event_reg_count[$EVT_ID])) {
551
+                    $event_reg_count[$EVT_ID] = 0;
552 552
                 }
553
-                $event_reg_count[ $EVT_ID ]++;
554
-                if (! isset($event_spaces_remaining[ $EVT_ID ])) {
555
-                    $event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
553
+                $event_reg_count[$EVT_ID]++;
554
+                if ( ! isset($event_spaces_remaining[$EVT_ID])) {
555
+                    $event_spaces_remaining[$EVT_ID] = $registration->event()->spaces_remaining_for_sale();
556 556
                 }
557 557
             }
558 558
             if (
559 559
                 $revisit
560
-                && ($tickets_remaining[ $ticket->ID() ] === 0
561
-                    || $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
560
+                && ($tickets_remaining[$ticket->ID()] === 0
561
+                    || $event_reg_count[$EVT_ID] > $event_spaces_remaining[$EVT_ID]
562 562
                 )
563 563
             ) {
564
-                $ejected_registrations[ $REG_ID ] = $registration->event();
564
+                $ejected_registrations[$REG_ID] = $registration->event();
565 565
                 if ($registration->status_ID() !== EEM_Registration::status_id_wait_list) {
566 566
                     /** @type EE_Registration_Processor $registration_processor */
567 567
                     $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
@@ -621,7 +621,7 @@  discard block
 block discarded – undo
621 621
         foreach ($sold_out_events_array as $sold_out_event) {
622 622
             $sold_out_events .= EEH_HTML::li(
623 623
                 EEH_HTML::span(
624
-                    '  ' . $sold_out_event->name(),
624
+                    '  '.$sold_out_event->name(),
625 625
                     '',
626 626
                     'dashicons dashicons-marker ee-icon-size-16 pink-text'
627 627
                 )
@@ -677,7 +677,7 @@  discard block
 block discarded – undo
677 677
         foreach ($insufficient_spaces_events_array as $event) {
678 678
             if ($event instanceof EE_Event) {
679 679
                 $insufficient_space_events .= EEH_HTML::li(
680
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
680
+                    EEH_HTML::span(' '.$event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
681 681
                 );
682 682
             }
683 683
         }
@@ -726,7 +726,7 @@  discard block
 block discarded – undo
726 726
         $events_requiring_pre_approval = [];
727 727
         foreach ($registrations_requiring_pre_approval as $registration) {
728 728
             if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
729
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
729
+                $events_requiring_pre_approval[$registration->event()->ID()] = EEH_HTML::li(
730 730
                     EEH_HTML::span(
731 731
                         '',
732 732
                         '',
@@ -866,7 +866,7 @@  discard block
 block discarded – undo
866 866
     {
867 867
         return new EE_Form_Section_Proper(
868 868
             [
869
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
869
+                'html_id'         => 'ee-'.$this->slug().'-extra-hidden-inputs',
870 870
                 'layout_strategy' => new EE_Div_Per_Section_Layout(),
871 871
                 'subsections'     => [
872 872
                     'spco_no_payment_required' => new EE_Hidden_Input(
@@ -905,7 +905,7 @@  discard block
 block discarded – undo
905 905
                 $payments += $registration->registration_payments();
906 906
             }
907 907
         }
908
-        if (! empty($payments)) {
908
+        if ( ! empty($payments)) {
909 909
             foreach ($payments as $payment) {
910 910
                 if ($payment instanceof EE_Registration_Payment) {
911 911
                     $this->checkout->amount_owing -= $payment->amount();
@@ -994,7 +994,7 @@  discard block
 block discarded – undo
994 994
             );
995 995
         }
996 996
         // switch up header depending on number of available payment methods
997
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
997
+        $payment_method_header = count($this->checkout->available_payment_methods) > 1
998 998
             ? apply_filters(
999 999
                 'FHEE__registration_page_payment_options__method_of_payment_hdr',
1000 1000
                 esc_html__('Please Select Your Method of Payment', 'event_espresso')
@@ -1028,7 +1028,7 @@  discard block
 block discarded – undo
1028 1028
                 $payment_method_button = EEH_HTML::img(
1029 1029
                     $payment_method->button_url(),
1030 1030
                     $payment_method->name(),
1031
-                    'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1031
+                    'spco-payment-method-'.$payment_method->slug().'-btn-img',
1032 1032
                     'spco-payment-method-btn-img'
1033 1033
                 );
1034 1034
                 // check if any payment methods are set as default
@@ -1036,15 +1036,15 @@  discard block
 block discarded – undo
1036 1036
                 // open_by_default
1037 1037
                 if (
1038 1038
                     ($this->checkout->selected_method_of_payment === $payment_method->slug())
1039
-                    || (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1039
+                    || ( ! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1040 1040
                 ) {
1041 1041
                     $this->checkout->selected_method_of_payment = $payment_method->slug();
1042 1042
                     $this->_save_selected_method_of_payment();
1043
-                    $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1043
+                    $default_payment_method_option[$payment_method->slug()] = $payment_method_button;
1044 1044
                 } else {
1045
-                    $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1045
+                    $available_payment_method_options[$payment_method->slug()] = $payment_method_button;
1046 1046
                 }
1047
-                $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1047
+                $payment_methods_billing_info[$payment_method->slug().'-info'] =
1048 1048
                     $this->_payment_method_billing_info(
1049 1049
                         $payment_method
1050 1050
                     );
@@ -1057,7 +1057,7 @@  discard block
 block discarded – undo
1057 1057
         $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1058 1058
             $available_payment_method_options
1059 1059
         );
1060
-        $available_payment_methods                              += $payment_methods_billing_info;
1060
+        $available_payment_methods += $payment_methods_billing_info;
1061 1061
         // build the available payment methods form
1062 1062
         return new EE_Form_Section_Proper(
1063 1063
             [
@@ -1080,7 +1080,7 @@  discard block
 block discarded – undo
1080 1080
      */
1081 1081
     protected function _get_available_payment_methods()
1082 1082
     {
1083
-        if (! empty($this->checkout->available_payment_methods)) {
1083
+        if ( ! empty($this->checkout->available_payment_methods)) {
1084 1084
             return $this->checkout->available_payment_methods;
1085 1085
         }
1086 1086
         $available_payment_methods = [];
@@ -1092,7 +1092,7 @@  discard block
 block discarded – undo
1092 1092
         );
1093 1093
         foreach ($payment_methods as $payment_method) {
1094 1094
             if ($payment_method instanceof EE_Payment_Method) {
1095
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1095
+                $available_payment_methods[$payment_method->slug()] = $payment_method;
1096 1096
             }
1097 1097
         }
1098 1098
         return $available_payment_methods;
@@ -1187,7 +1187,7 @@  discard block
 block discarded – undo
1187 1187
         );
1188 1188
         return new EE_Form_Section_Proper(
1189 1189
             [
1190
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1190
+                'html_id'         => 'spco-payment-method-info-'.$payment_method->slug(),
1191 1191
                 'html_class'      => 'spco-payment-method-info-dv',
1192 1192
                 // only display the selected or default PM
1193 1193
                 'html_style'      => $currently_selected ? '' : 'display:none;',
@@ -1216,7 +1216,7 @@  discard block
 block discarded – undo
1216 1216
         // how have they chosen to pay?
1217 1217
         $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1218 1218
         $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1219
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1219
+        if ( ! $this->checkout->payment_method instanceof EE_Payment_Method) {
1220 1220
             return false;
1221 1221
         }
1222 1222
         if (
@@ -1391,7 +1391,7 @@  discard block
 block discarded – undo
1391 1391
      */
1392 1392
     public function switch_payment_method()
1393 1393
     {
1394
-        if (! $this->_verify_payment_method_is_set()) {
1394
+        if ( ! $this->_verify_payment_method_is_set()) {
1395 1395
             return false;
1396 1396
         }
1397 1397
         if (
@@ -1527,7 +1527,7 @@  discard block
 block discarded – undo
1527 1527
             }
1528 1528
         }
1529 1529
         // verify payment method
1530
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1530
+        if ( ! $this->checkout->payment_method instanceof EE_Payment_Method) {
1531 1531
             // get payment method for selected method of payment
1532 1532
             $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1533 1533
         }
@@ -1552,7 +1552,7 @@  discard block
 block discarded – undo
1552 1552
      */
1553 1553
     public function save_payer_details_via_ajax()
1554 1554
     {
1555
-        if (! $this->_verify_payment_method_is_set()) {
1555
+        if ( ! $this->_verify_payment_method_is_set()) {
1556 1556
             return;
1557 1557
         }
1558 1558
         // generate billing form for selected method of payment if it hasn't been done already
@@ -1562,7 +1562,7 @@  discard block
 block discarded – undo
1562 1562
             );
1563 1563
         }
1564 1564
         // generate primary attendee from payer info if applicable
1565
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1565
+        if ( ! $this->checkout->transaction_has_primary_registrant()) {
1566 1566
             $attendee = $this->_create_attendee_from_request_data();
1567 1567
             if ($attendee instanceof EE_Attendee) {
1568 1568
                 foreach ($this->checkout->transaction->registrations() as $registration) {
@@ -1593,7 +1593,7 @@  discard block
 block discarded – undo
1593 1593
     {
1594 1594
         // get State ID
1595 1595
         $STA_ID = $this->request->getRequestParam('state');
1596
-        if (! empty($STA_ID)) {
1596
+        if ( ! empty($STA_ID)) {
1597 1597
             // can we get state object from name ?
1598 1598
             EE_Registry::instance()->load_model('State');
1599 1599
             $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
@@ -1601,7 +1601,7 @@  discard block
 block discarded – undo
1601 1601
         }
1602 1602
         // get Country ISO
1603 1603
         $CNT_ISO = $this->request->getRequestParam('country');
1604
-        if (! empty($CNT_ISO)) {
1604
+        if ( ! empty($CNT_ISO)) {
1605 1605
             // can we get country object from name ?
1606 1606
             EE_Registry::instance()->load_model('Country');
1607 1607
             $country = EEM_Country::instance()->get_col(
@@ -1782,7 +1782,7 @@  discard block
 block discarded – undo
1782 1782
     protected function _maybe_set_completed(EE_Payment $payment)
1783 1783
     {
1784 1784
         // Do we need to redirect them? If so, there's more work to be done.
1785
-        if (! $payment->redirect_url()) {
1785
+        if ( ! $payment->redirect_url()) {
1786 1786
             $this->set_completed();
1787 1787
         }
1788 1788
     }
@@ -1839,7 +1839,7 @@  discard block
 block discarded – undo
1839 1839
     private function _process_payment()
1840 1840
     {
1841 1841
         // basically confirm that the event hasn't sold out since they hit the page
1842
-        if (! $this->_last_second_ticket_verifications()) {
1842
+        if ( ! $this->_last_second_ticket_verifications()) {
1843 1843
             return null;
1844 1844
         }
1845 1845
         // ya gotta make a choice man
@@ -1850,7 +1850,7 @@  discard block
 block discarded – undo
1850 1850
             return null;
1851 1851
         }
1852 1852
         // get EE_Payment_Method object
1853
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1853
+        if ( ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1854 1854
             return null;
1855 1855
         }
1856 1856
         // setup billing form
@@ -1859,12 +1859,12 @@  discard block
 block discarded – undo
1859 1859
                 $this->checkout->payment_method
1860 1860
             );
1861 1861
             // bad billing form ?
1862
-            if (! $this->_billing_form_is_valid()) {
1862
+            if ( ! $this->_billing_form_is_valid()) {
1863 1863
                 return null;
1864 1864
             }
1865 1865
         }
1866 1866
         // ensure primary registrant has been fully processed
1867
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1867
+        if ( ! $this->_setup_primary_registrant_prior_to_payment()) {
1868 1868
             return null;
1869 1869
         }
1870 1870
         // if session is close to expiring (under 10 minutes by default)
@@ -1920,7 +1920,7 @@  discard block
 block discarded – undo
1920 1920
     protected function _last_second_ticket_verifications()
1921 1921
     {
1922 1922
         // don't bother re-validating if not a return visit
1923
-        if (! $this->checkout->revisit) {
1923
+        if ( ! $this->checkout->revisit) {
1924 1924
             return true;
1925 1925
         }
1926 1926
         $registrations = $this->checkout->transaction->registrations();
@@ -1970,7 +1970,7 @@  discard block
 block discarded – undo
1970 1970
             $this->_get_payment_method_for_selected_method_of_payment()
1971 1971
         );
1972 1972
         $html                        = $payment_method_billing_info->get_html();
1973
-        $html                        .= $this->checkout->redirect_form;
1973
+        $html .= $this->checkout->redirect_form;
1974 1974
         /** @var ResponseInterface $response */
1975 1975
         $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1976 1976
         $response->addOutput($html);
@@ -1986,7 +1986,7 @@  discard block
 block discarded – undo
1986 1986
      */
1987 1987
     private function _billing_form_is_valid()
1988 1988
     {
1989
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1989
+        if ( ! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1990 1990
             return true;
1991 1991
         }
1992 1992
         if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
@@ -2104,7 +2104,7 @@  discard block
 block discarded – undo
2104 2104
     {
2105 2105
         // convert billing form data into an attendee
2106 2106
         $this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2107
-        if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2107
+        if ( ! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2108 2108
             EE_Error::add_error(
2109 2109
                 sprintf(
2110 2110
                     esc_html__(
@@ -2121,7 +2121,7 @@  discard block
 block discarded – undo
2121 2121
             return false;
2122 2122
         }
2123 2123
         $primary_registration = $this->checkout->transaction->primary_registration();
2124
-        if (! $primary_registration instanceof EE_Registration) {
2124
+        if ( ! $primary_registration instanceof EE_Registration) {
2125 2125
             EE_Error::add_error(
2126 2126
                 sprintf(
2127 2127
                     esc_html__(
@@ -2183,8 +2183,8 @@  discard block
 block discarded – undo
2183 2183
             return null;
2184 2184
         }
2185 2185
         // get EE_Payment_Method object
2186
-        if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2187
-            $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2186
+        if (isset($this->checkout->available_payment_methods[$this->checkout->selected_method_of_payment])) {
2187
+            $payment_method = $this->checkout->available_payment_methods[$this->checkout->selected_method_of_payment];
2188 2188
         } else {
2189 2189
             // load EEM_Payment_Method
2190 2190
             EE_Registry::instance()->load_model('Payment_Method');
@@ -2192,7 +2192,7 @@  discard block
 block discarded – undo
2192 2192
             $payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2193 2193
         }
2194 2194
         // verify $payment_method
2195
-        if (! $payment_method instanceof EE_Payment_Method) {
2195
+        if ( ! $payment_method instanceof EE_Payment_Method) {
2196 2196
             // not a payment
2197 2197
             EE_Error::add_error(
2198 2198
                 sprintf(
@@ -2210,7 +2210,7 @@  discard block
 block discarded – undo
2210 2210
             return null;
2211 2211
         }
2212 2212
         // and verify it has a valid Payment_Method Type object
2213
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2213
+        if ( ! $payment_method->type_obj() instanceof EE_PMT_Base) {
2214 2214
             // not a payment
2215 2215
             EE_Error::add_error(
2216 2216
                 sprintf(
@@ -2248,7 +2248,7 @@  discard block
 block discarded – undo
2248 2248
         $payment = null;
2249 2249
         $this->checkout->transaction->save();
2250 2250
         $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2251
-        if (! $payment_processor instanceof EE_Payment_Processor) {
2251
+        if ( ! $payment_processor instanceof EE_Payment_Processor) {
2252 2252
             return false;
2253 2253
         }
2254 2254
         try {
@@ -2350,7 +2350,7 @@  discard block
 block discarded – undo
2350 2350
             return true;
2351 2351
         }
2352 2352
         // verify payment object
2353
-        if (! $payment instanceof EE_Payment) {
2353
+        if ( ! $payment instanceof EE_Payment) {
2354 2354
             // not a payment
2355 2355
             EE_Error::add_error(
2356 2356
                 sprintf(
@@ -2400,7 +2400,7 @@  discard block
 block discarded – undo
2400 2400
                 // and lastly, let's bump the payment status to pending
2401 2401
                 $payment->set_status(EEM_Payment::status_id_pending);
2402 2402
                 $payment->save();
2403
-            } elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2403
+            } elseif ( ! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2404 2404
                 // User shouldn't be redirected. So let's process it here.
2405 2405
                 // $this->_setup_redirect_for_next_step();
2406 2406
                 $this->checkout->continue_reg = false;
@@ -2481,7 +2481,7 @@  discard block
 block discarded – undo
2481 2481
                     return false;
2482 2482
                 // bad payment
2483 2483
                 case EEM_Payment::status_id_failed:
2484
-                    if (! empty($msg)) {
2484
+                    if ( ! empty($msg)) {
2485 2485
                         EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2486 2486
                         return false;
2487 2487
                     }
@@ -2539,18 +2539,18 @@  discard block
 block discarded – undo
2539 2539
         // how have they chosen to pay?
2540 2540
         $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2541 2541
         // get EE_Payment_Method object
2542
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2542
+        if ( ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2543 2543
             $this->checkout->continue_reg = false;
2544 2544
             return false;
2545 2545
         }
2546
-        if (! $this->checkout->payment_method->is_off_site()) {
2546
+        if ( ! $this->checkout->payment_method->is_off_site()) {
2547 2547
             return false;
2548 2548
         }
2549 2549
         $this->_validate_offsite_return();
2550 2550
         // verify TXN
2551 2551
         if ($this->checkout->transaction instanceof EE_Transaction) {
2552 2552
             $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2553
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2553
+            if ( ! $gateway instanceof EE_Offsite_Gateway) {
2554 2554
                 $this->checkout->continue_reg = false;
2555 2555
                 return false;
2556 2556
             }
@@ -2564,7 +2564,7 @@  discard block
 block discarded – undo
2564 2564
                 $this->checkout->payment = $payment;
2565 2565
                 // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2566 2566
                 // because we will complete this step during the IPN processing then
2567
-                if (! $this->handle_IPN_in_this_request()) {
2567
+                if ( ! $this->handle_IPN_in_this_request()) {
2568 2568
                     $this->set_completed();
2569 2569
                 }
2570 2570
                 return true;
@@ -2667,13 +2667,13 @@  discard block
 block discarded – undo
2667 2667
      */
2668 2668
     private function _redirect_wayward_request(EE_Registration $primary_registrant)
2669 2669
     {
2670
-        if (! $primary_registrant instanceof EE_Registration) {
2670
+        if ( ! $primary_registrant instanceof EE_Registration) {
2671 2671
             // try redirecting based on the current TXN
2672 2672
             $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2673 2673
                 ? $this->checkout->transaction->primary_registration()
2674 2674
                 : null;
2675 2675
         }
2676
-        if (! $primary_registrant instanceof EE_Registration) {
2676
+        if ( ! $primary_registrant instanceof EE_Registration) {
2677 2677
             EE_Error::add_error(
2678 2678
                 sprintf(
2679 2679
                     esc_html__(
@@ -2744,7 +2744,7 @@  discard block
 block discarded – undo
2744 2744
             $payment = $this->checkout->transaction->last_payment();
2745 2745
             // $payment_source = 'last_payment after Exception';
2746 2746
             // but if we STILL don't have a payment object
2747
-            if (! $payment instanceof EE_Payment) {
2747
+            if ( ! $payment instanceof EE_Payment) {
2748 2748
                 // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2749 2749
                 $this->_handle_payment_processor_exception($e);
2750 2750
             }
Please login to merge, or discard this patch.