PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.5.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.5.0
2.5.0 2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 All 34 releases
← All changes | app/Services/Integrations/FluentCart/Bootstrap.php +100 -38 1.10.01 → 2.5.0 View file →
@@ -26,9 +26,8 @@
26 26 public function registerHooks()
27 27 {
28 28 add_filter('fluent_booking/public_event_vars', [$this, 'maybePushPaymentVars'], 11, 2);
29 29 add_filter('fluent_booking/booking_data', [$this, 'maybePushPaymentData'], 10, 2);
30 - add_action('fluent_cart/cart/cart_data_items_updated', [$this, 'maybeSaveBookingID'], 10, 1);
31 30
32 31 add_action('fluent_cart/receipt/thank_you/after_order_items', [$this, 'maybeShowBookingDetailsInReceipt'], 10, 1);
33 32 add_action('fluent_booking/booking_meta_info_main_meta_cart', [$this, 'pushOrderDataToBookingView'], 10, 2);
34 33
@@ -45,19 +44,62 @@
45 44 $config = $order->config;
46 45 $bookingId = Arr::get($cart->checkout_data, 'fluent_booking_data.booking_id');
47 46 if ($bookingId) {
48 47 $config['fcal_booking_id'] = $bookingId;
48 + $config['fcal_target_variant_id'] = Arr::get($cart->checkout_data, 'fluent_booking_data.target_variant_id');
49 49 $order->config = $config;
50 50 $order->save();
51 51 }
52 52 });
53 53
54 + // The booking cart is locked to pin its booking item, which also hides order bumps
55 + add_filter('fluent_cart/cart/accepts_additional_items', [$this, 'maybeAcceptOrderBumps'], 10, 2);
56 +
54 57 // after order confirmation
55 58 add_action('fluent_booking/cart/booking_order_completed', [$this, 'maybeScheduleBooking'], 10, 1);
56 59
60 + // Offline and async payments settle after their cart is closed, so the
61 + // action above never reaches us. This one fires on the payment itself.
62 + add_action('fluent_cart/order_paid_done', [$this, 'reconcileBookingFromOrder'], 10, 1);
63 +
64 + add_filter('fluent_cart/checkout_page_name_fields_schema', [$this, 'maybeFillSplitNameFields'], 10, 2);
57 65 }
58 66
67 + public function maybeAcceptOrderBumps($accepts, $context)
68 + {
69 + $cart = Arr::get($context, 'cart');
59 70
71 + if ($accepts || !$cart || empty($cart->checkout_data['fluent_booking_data'])) {
72 + return $accepts;
73 + }
74 +
75 + // A draft order binds the cart, and upgrade carts keep their own restriction
76 + if ($cart->order_id || !empty($cart->checkout_data['upgrade_data'])) {
77 + return $accepts;
78 + }
79 +
80 + return true;
81 + }
82 +
83 + public function maybeFillSplitNameFields($nameFields, $context)
84 + {
85 + $cart = Arr::get($context, 'cart');
86 + if (!$cart || empty($cart->checkout_data['fluent_booking_data'])) {
87 + return $nameFields;
88 + }
89 +
90 + if (isset($nameFields['billing_first_name']) && empty($nameFields['billing_first_name']['value'])) {
91 + $nameFields['billing_first_name']['value'] = $cart->first_name;
92 + }
93 +
94 + if (isset($nameFields['billing_last_name']) && empty($nameFields['billing_last_name']['value'])) {
95 + $nameFields['billing_last_name']['value'] = $cart->last_name;
96 + }
97 +
98 + return $nameFields;
99 + }
100 +
101 +
60 102 public function maybePushPaymentVars($eventVars, CalendarSlot $calendarEvent)
61 103 {
62 104 if (!CartHelper::isEnabled($calendarEvent)) {
63 105 return $eventVars;
@@ -109,12 +151,10 @@
109 151 return $response;
110 152 }
111 153
112 154 $quantity = $booking->getMeta('quantity', 1);
113 - $newItem = $product->toArray();
155 + $instantCart = \FluentCart\App\Helpers\CartHelper::generateCartFromVariation($product, $quantity);
114 156
115 - $instantCart = \FluentCart\App\Helpers\CartHelper::generateCartFromCustomVariation($newItem, $quantity);
116 -
117 157 $cartData = $instantCart->cart_data;
118 158 $cartData[0]['fcal_booking_id'] = $booking->id;
119 159 $instantCart->cart_data = $cartData;
120 160
@@ -158,9 +198,9 @@
158 198 'booking_id' => $booking->id,
159 199 'status' => 'closed',
160 200 'type' => 'info',
161 201 'title' => __('Redirect to FluentCart checkout page', 'fluent-booking'),
162 - 'description' => __('User redirected to FluentCart checkout page to comeplete the order.', 'fluent-booking')
202 + 'description' => __('User redirected to FluentCart checkout page to complete the order.', 'fluent-booking')
163 203 ]);
164 204
165 205 return $response;
166 206 }, 10, 2);
@@ -167,38 +207,8 @@
167 207
168 208 return $bookingData;
169 209 }
170 210
171 - public function maybeSaveBookingID($cart)
172 - {
173 - $bookingHash = '';
174 - if (isset($_REQUEST['fcal_hash'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
175 - $bookingHash = sanitize_text_field(wp_unslash($_REQUEST['fcal_hash'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
176 - }
177 -
178 - if (!$bookingHash || empty($cart['cart'])) {
179 - return;
180 - }
181 -
182 - $cartData = $cart['cart']->cart_data;
183 - if (!empty(Arr::get($cartData[0], 'fcal_booking_id'))) {
184 - return;
185 - }
186 -
187 - $booking = Booking::where('hash', $bookingHash)->first();
188 - if (!$booking) {
189 - return;
190 - }
191 -
192 - if (!CartHelper::isEnabled($booking->calendar_event)) {
193 - return;
194 - }
195 -
196 - $cartData[0]['fcal_booking_id'] = $booking->id;
197 - $cart['cart']->cart_data = $cartData;
198 - $cart['cart']->save();
199 - }
200 -
201 211 public function maybeRenderBookingInfoOnCartItem($eventInfo)
202 212 {
203 213 $item = Arr::get($eventInfo, 'item', []);
204 214
@@ -207,9 +217,9 @@
207 217 }
208 218
209 219 $bookingId = $item['fcal_booking_id'];
210 220 $booking = Booking::find($bookingId);
211 - if (!$booking || $booking->status !== 'pending') {
221 + if (!$booking || !$booking->calendar_event || $booking->status !== 'pending') {
212 222 return;
213 223 }
214 224
215 225 $bookingTime = $booking->getFullBookingDateTimeText($booking->person_time_zone, true) . ' (' . $booking->person_time_zone . ')';
@@ -229,10 +239,10 @@
229 239
230 240 public function maybeScheduleBooking($eventData)
231 241 {
232 242 $order = Arr::get($eventData, 'order');
233 - $bookingId = Arr::get($order->config, 'fcal_booking_id', '');
234 - if (empty($order) || empty($bookingId)) {
243 +
244 + if (empty($order)) {
235 245 return;
236 246 }
237 247
238 248 $cart = Arr::get($eventData, 'cart');
@@ -254,8 +264,42 @@
254 264 if (!$checkoutItem) {
255 265 return;
256 266 }
257 267
268 + $this->scheduleBookingForOrder($order);
269 + }
270 +
271 + /**
272 + * The same work, reached without a cart.
273 + *
274 + * __on_success_actions__ only run while the cart is open, and offline and
275 + * async orders close theirs before the payment settles - leaving the
276 + * booking pending for good. The order keeps the booking id either way.
277 + *
278 + * @param array $eventData
279 + */
280 + public function reconcileBookingFromOrder($eventData)
281 + {
282 + $order = Arr::get($eventData, 'order');
283 +
284 + if (empty($order)) {
285 + return;
286 + }
287 +
288 + $this->scheduleBookingForOrder($order);
289 + }
290 +
291 + /**
292 + * @param \FluentCart\App\Models\Order $order
293 + */
294 + protected function scheduleBookingForOrder($order)
295 + {
296 + $bookingId = Arr::get($order->config, 'fcal_booking_id', '');
297 +
298 + if (empty($bookingId)) {
299 + return;
300 + }
301 +
258 302 $booking = Booking::find($bookingId);
259 303 if (!$booking || $booking->source_id) {
260 304 return;
261 305 }
@@ -261,8 +305,26 @@
261 305 }
262 306
263 307 $calendarEvent = $booking->calendar_event;
264 308 if (!CartHelper::isEnabled($calendarEvent)) {
309 + return;
310 + }
311 +
312 + // The cart takes order bumps, so a paid order is not proof the booking item was bought.
313 + // Orders drafted before the variant was recorded carry no id and skip the check.
314 + $targetVariantId = (int) Arr::get($order->config, 'fcal_target_variant_id', 0);
315 + if ($targetVariantId && !$order->order_items()->where('object_id', $targetVariantId)->exists()) {
316 + do_action('fluent_booking/log_booking_activity', [
317 + 'booking_id' => $booking->id,
318 + 'status' => 'closed',
319 + 'type' => 'error',
320 + 'title' => __('Cart: Booking status could not be changed', 'fluent-booking'),
321 + 'description' => sprintf(
322 + /* translators: %1$s and %2$s are the HTML link tags for "View Order" */
323 + __('The paid order does not contain the booking item. %1$sView Order%2$s', 'fluent-booking'),
324 + '<a target="_blank" href="' . $order->getViewUrl('admin') . '">',
325 + '</a>')
326 + ]);
265 327 return;
266 328 }
267 329
268 330 if ($booking->status != 'pending') {