PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.64
Timetics – Appointment Booking Calendar & Scheduling v1.0.64
1.0.64 1.0.62 1.0.63 1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 All 65 releases
← All changes | core/integrations/woocommerce/hooks.php +115 -18 1.0.62 → 1.0.64 View file →
@@ -67,11 +67,14 @@
67 67 add_action( 'admin_init', [ $this, 'create_term_timetics_meeting' ] );
68 68
69 69 add_action( 'wp_head', [ $this, 'hide_regular_price_checkout_css' ] );
70 70
71 - // Display meeting details on checkout page
72 - add_action( 'woocommerce_review_order_before_payment', [ $this, 'display_meeting_details_on_checkout' ] );
71 + // Show meeting details under the product in the cart and on checkout
72 + add_filter( 'woocommerce_get_item_data', [ $this, 'add_meeting_details_to_cart_item' ], 10, 2 );
73 73
74 + // Store meeting details on the order line item ( thank you page, emails, my account, admin )
75 + add_action( 'woocommerce_checkout_create_order_line_item', [ $this, 'add_meeting_details_to_order_item' ], 10, 4 );
76 +
74 77 // Sync order status when booking status changes (bidirectional sync)
75 78 add_action( 'transition_post_status', [ $this, 'sync_order_status_from_booking' ], 10, 3 );
76 79 }
77 80
@@ -117,9 +120,8 @@
117 120 if ( ! $order->is_paid() ) {
118 121 // Mark booking as failed if payment was not completed
119 122 $session_data = WC()->session->get( 'timetics_data' );
120 123 if ( $session_data ) {
121 - $booking = new Booking( $session_data['booking_id'] );
122 124 Status_Mapper::set_order_id_for_booking( $session_data['booking_id'], $order_id );
123 125 }
124 126 WC()->session->set( 'timetics_data', null );
125 127 return;
@@ -622,32 +624,127 @@
622 624 ];
623 625 }
624 626
625 627 /**
626 - * Display meeting details on checkout page
628 + * Meeting details as label => value pairs, ready to display.
627 629 *
628 - * @return void
630 + * Shared by the cart / checkout item data and the order line item meta so the
631 + * customer sees the same details before and after paying.
632 + *
633 + * @param array $session_data
634 + *
635 + * @return array
629 636 */
630 - public function display_meeting_details_on_checkout() {
631 - if ( ! is_checkout() ) {
632 - return;
637 + private function get_meeting_detail_pairs( $session_data ) {
638 + $details = $this->prepare_meeting_details( $session_data );
639 +
640 + if ( ! $details ) {
641 + return [];
633 642 }
634 643
635 - $session_data = WC()->session->get( 'timetics_data' );
636 - $details = $this->prepare_meeting_details( $session_data );
644 + $time = $details['formatted_time'];
637 645
638 - if ( ! $details ) {
646 + if ( $time && $details['display_timezone'] ) {
647 + $time .= ' (' . $details['display_timezone'] . ')';
648 + }
649 +
650 + return array_filter( [
651 + __( 'Date', 'timetics' ) => $details['formatted_date'],
652 + __( 'Time', 'timetics' ) => $time,
653 + __( 'Duration', 'timetics' ) => $details['duration'],
654 + __( 'Location', 'timetics' ) => $details['location_label'],
655 + __( 'Timezone', 'timetics' ) => $details['timezone'],
656 + ] );
657 + }
658 +
659 + /**
660 + * Whether the given product is the meeting product this session booked.
661 + *
662 + * The cart can hold other products, so the details must only be attached to
663 + * the meeting line.
664 + *
665 + * @param array $session_data
666 + * @param integer $product_id
667 + *
668 + * @return bool
669 + */
670 + private function is_meeting_product( $session_data, $product_id ) {
671 + if ( empty( $session_data['meeting_id'] ) ) {
672 + return false;
673 + }
674 +
675 + $meeting = new Appointment( $session_data['meeting_id'] );
676 +
677 + return (int) $meeting->get_wc_product_id() === (int) $product_id;
678 + }
679 +
680 + /**
681 + * Show meeting details under the product in the cart and on checkout.
682 + *
683 + * WooCommerce runs this filter for both the classic templates and the Store API,
684 + * so one filter covers the classic and the block based cart / checkout. The block
685 + * checkout is the default since WooCommerce 8.3, and the old
686 + * woocommerce_review_order_before_payment hook this replaced never ran there.
687 + *
688 + * @param array $item_data
689 + * @param array $cart_item
690 + *
691 + * @return array
692 + */
693 + public function add_meeting_details_to_cart_item( $item_data, $cart_item ) {
694 + $session_data = WC()->session ? WC()->session->get( 'timetics_data' ) : null;
695 +
696 + if ( ! $session_data || ! $this->is_meeting_product( $session_data, $cart_item['product_id'] ) ) {
697 + return $item_data;
698 + }
699 +
700 + foreach ( $this->get_meeting_detail_pairs( $session_data ) as $label => $value ) {
701 + $item_data[] = [
702 + 'key' => $label,
703 + 'value' => $value,
704 + ];
705 + }
706 +
707 + return $item_data;
708 + }
709 +
710 + /**
711 + * Store meeting details as order line item meta.
712 + *
713 + * The booking details only live in the WooCommerce session, which is cleared
714 + * once the order is placed. Copying them onto the line item makes WooCommerce
715 + * render them everywhere an order is shown: the thank you page, the customer
716 + * and admin order emails, My Account -> View order, and the admin order screen.
717 + *
718 + * @param \WC_Order_Item_Product $item
719 + * @param string $cart_item_key
720 + * @param array $values
721 + * @param \WC_Order $order
722 + *
723 + * @return void
724 + */
725 + public function add_meeting_details_to_order_item( $item, $cart_item_key, $values, $order ) {
726 + $session_data = WC()->session ? WC()->session->get( 'timetics_data' ) : null;
727 +
728 + if ( ! $session_data || ! $this->is_meeting_product( $session_data, $item->get_product_id() ) ) {
639 729 return;
640 730 }
641 731
642 - $timetics_formatted_date = $details['formatted_date'];
643 - $timetics_formatted_time = $details['formatted_time'];
644 - $timetics_duration = $details['duration'];
645 - $timetics_location_label = $details['location_label'];
646 - $timetics_timezone = $details['timezone'];
647 - $timetics_display_timezone = $details['display_timezone'];
732 + foreach ( $this->get_meeting_detail_pairs( $session_data ) as $label => $value ) {
733 + $item->add_meta_data( $label, $value, true );
734 + }
648 735
649 - include TIMETICS_PLUGIN_DIR . '/templates/woocommerce/meeting-details-checkout.php';
736 + // Link the order back to the booking here rather than on woocommerce_thankyou.
737 + // The thank you page only runs for a customer who actually lands on it and, for
738 + // an unpaid order, only stored the booking -> order half of the link. Without the
739 + // order -> booking half, sync_booking_status_from_order() finds no booking and an
740 + // admin later marking the order paid or cancelled silently does nothing.
741 + //
742 + // The order is not saved here on purpose: in the classic checkout the order has no
743 + // ID yet at this point. WooCommerce saves it right after building the line items.
744 + if ( ! empty( $session_data['booking_id'] ) ) {
745 + $order->update_meta_data( '_tt_booking_id', $session_data['booking_id'] );
746 + }
650 747 }
651 748
652 749 /**
653 750 * Sync booking status when WooCommerce order status changes