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 +80 -6 2.2.0 → 2.5.0 View file →
@@ -44,19 +44,43 @@
44 44 $config = $order->config;
45 45 $bookingId = Arr::get($cart->checkout_data, 'fluent_booking_data.booking_id');
46 46 if ($bookingId) {
47 47 $config['fcal_booking_id'] = $bookingId;
48 + $config['fcal_target_variant_id'] = Arr::get($cart->checkout_data, 'fluent_booking_data.target_variant_id');
48 49 $order->config = $config;
49 50 $order->save();
50 51 }
51 52 });
52 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 +
53 57 // after order confirmation
54 58 add_action('fluent_booking/cart/booking_order_completed', [$this, 'maybeScheduleBooking'], 10, 1);
55 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 +
56 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');
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 +
59 83 public function maybeFillSplitNameFields($nameFields, $context)
60 84 {
61 85 $cart = Arr::get($context, 'cart');
62 86 if (!$cart || empty($cart->checkout_data['fluent_booking_data'])) {
@@ -127,12 +151,10 @@
127 151 return $response;
128 152 }
129 153
130 154 $quantity = $booking->getMeta('quantity', 1);
131 - $newItem = $product->toArray();
155 + $instantCart = \FluentCart\App\Helpers\CartHelper::generateCartFromVariation($product, $quantity);
132 156
133 - $instantCart = \FluentCart\App\Helpers\CartHelper::generateCartFromCustomVariation($newItem, $quantity);
134 -
135 157 $cartData = $instantCart->cart_data;
136 158 $cartData[0]['fcal_booking_id'] = $booking->id;
137 159 $instantCart->cart_data = $cartData;
138 160
@@ -176,9 +198,9 @@
176 198 'booking_id' => $booking->id,
177 199 'status' => 'closed',
178 200 'type' => 'info',
179 201 'title' => __('Redirect to FluentCart checkout page', 'fluent-booking'),
180 - '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')
181 203 ]);
182 204
183 205 return $response;
184 206 }, 10, 2);
@@ -217,10 +239,10 @@
217 239
218 240 public function maybeScheduleBooking($eventData)
219 241 {
220 242 $order = Arr::get($eventData, 'order');
221 - $bookingId = Arr::get($order->config, 'fcal_booking_id', '');
222 - if (empty($order) || empty($bookingId)) {
243 +
244 + if (empty($order)) {
223 245 return;
224 246 }
225 247
226 248 $cart = Arr::get($eventData, 'cart');
@@ -242,8 +264,42 @@
242 264 if (!$checkoutItem) {
243 265 return;
244 266 }
245 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 +
246 302 $booking = Booking::find($bookingId);
247 303 if (!$booking || $booking->source_id) {
248 304 return;
249 305 }
@@ -249,8 +305,26 @@
249 305 }
250 306
251 307 $calendarEvent = $booking->calendar_event;
252 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 + ]);
253 327 return;
254 328 }
255 329
256 330 if ($booking->status != 'pending') {