PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.24
Booktics – Appointment Booking Calendar for Service Businesses v1.0.24
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 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.2 1.0.3 All 31 releases
booktics / core / order / services / order-notifier.php

order-notifier.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.24, at core/order/services/order-notifier.php

182 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Booktics\Order\Services;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 use Booktics\Models\Guest_Model;
10 use Booktics\Models\Service_Model;
11 use Booktics\Models\Team_Member_Model;
12 use DateTime;
13
14 /**
15 * Emits the booking notification-flow events for an order.
16 *
17 * This used to live inside Order_Controller and was therefore only reachable
18 * from the REST layer. Payment gateways that confirm a booking outside a REST
19 * request — WooCommerce, whose paid transition happens in its own hooks — could
20 * not fire it, so those bookings sent no confirmation at all. Keeping it here
21 * lets both callers use the same code and the same filters.
22 *
23 * @package Booktics/Order
24 */
25 class Order_Notifier {
26
27 /**
28 * Fire the order-created and appointment-scheduled events for an order.
29 *
30 * @param object $db Database instance.
31 * @param object $order Order row or model.
32 * @param int $customer_id Customer (guest) id.
33 * @param array $appointments Appointment models belonging to the order.
34 *
35 * @return void
36 */
37 public static function order_created( $db, $order, $customer_id, $appointments ): void {
38 if ( empty( $appointments ) ) {
39 return;
40 }
41
42 $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $customer_id ) );
43
44 foreach ( $appointments as $appointment ) {
45 $formatted_appointment = self::format_appointment( $appointment );
46 $appointment_time = $formatted_appointment['date'] . ' ' . $formatted_appointment['start_time'];
47 $dt = new DateTime( $appointment_time, wp_timezone() );
48 $timestamp = $dt->getTimestamp();
49 $appointment_datetime = wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp, wp_timezone() );
50 $appointment_array = is_object( $appointment ) && method_exists( $appointment, 'to_array' ) ? $appointment->to_array() : (array) $appointment;
51 $team_member_email = self::resolve_team_member_email(
52 $formatted_appointment['team']['user_email'] ?? '',
53 $appointment_array['team_member_id'] ?? 0
54 );
55
56 $notification_flow_data_for_appointment = array(
57 'appointment_service' => $formatted_appointment['service']['title'] ?? '',
58 'appointment_team_member' => $formatted_appointment['team']['display_name'] ?? '',
59 'start_time' => $formatted_appointment['start_time'],
60 'appointment_date' => $formatted_appointment['date'],
61 'appointment_time' => $appointment_datetime,
62 'attendee_count' => $formatted_appointment['attendee_count'] ?? null,
63 'customer_name' => $customer->name ?? '',
64 'user_email' => $customer->email ?? '',
65 'appointment_team_member_email' => $team_member_email,
66 );
67
68 $notification_flow_data_for_appointment = apply_filters( 'booktics_appointment_scheduled_notification_flow_data', $notification_flow_data_for_appointment, $formatted_appointment );
69
70 do_action( 'booktics_gln_hook', 'appointment_scheduled', $notification_flow_data_for_appointment );
71 }
72
73 $appointment = $appointments[0];
74 $formatted_appointment = self::format_appointment( $appointment );
75
76 $notification_flow_data_for_order = array(
77 'order_no' => $order->order_no ?? '',
78 'order_date' => $order->created_at ?? '',
79 'order_total' => $order->total ?? 0,
80 'customer_name' => $customer->name ?? '',
81 'user_email' => $customer->email ?? '',
82 'order_status' => $order->status ?? '',
83 'service_name' => $formatted_appointment['service']['title'] ?? '',
84 'team_name' => $formatted_appointment['team']['display_name'] ?? '',
85 'start_time' => $formatted_appointment['start_time'] ?? '',
86 'end_time' => $formatted_appointment['end_time'] ?? '',
87 'appointment_date' => $formatted_appointment['date'] ?? '',
88 'location_address' => $formatted_appointment['location']['address'] ?? '',
89 'appointment_status' => $formatted_appointment['status'] ?? '',
90 'appointment_cancel_url' => method_exists( $appointment, 'get_cancel_link' ) ? $appointment->get_cancel_link() : '',
91 'appointment_reschedule_url' => method_exists( $appointment, 'get_reschedule_link' ) ? $appointment->get_reschedule_link() : '',
92 );
93
94 $notification_flow_data_for_order = apply_filters( 'booktics_order_created_notification_flow_data', $notification_flow_data_for_order, $formatted_appointment, $order );
95
96 do_action( 'booktics_gln_hook', 'order_created', $notification_flow_data_for_order );
97 }
98
99 /**
100 * Format a single appointment for output and notifications.
101 *
102 * @param object|array $appointment Appointment model or row.
103 *
104 * @return array
105 */
106 public static function format_appointment( $appointment ): array {
107 if ( is_object( $appointment ) && method_exists( $appointment, 'to_array' ) ) {
108 $appointment = $appointment->to_array();
109 }
110
111 $team_member = ( new Team_Member_Model() )->find( $appointment['team_member_id'] );
112 $service = ( new Service_Model() )->find( $appointment['service_id'] );
113 $service = $service ? $service->to_array() : array();
114 $additional_durations = $service['additional_durations'] ?? array();
115 $additional_durations = ! empty( $additional_durations )
116 ? array_filter(
117 $additional_durations, function ( $duration ) use ( $appointment ) {
118 return $duration['id'] === $appointment['additional_duration_id'];
119 }
120 ) : array();
121 $additional_duration = ! empty( $additional_durations ) ? reset( $additional_durations ) : array();
122
123 $formatted_appointment = array(
124 'id' => $appointment['id'] ?? 0,
125 'date' => $appointment['date'] ?? '',
126 'start_time' => $appointment['start_time'] ?? '',
127 'end_time' => $appointment['end_time'] ?? '',
128 'additional_duration_id' => $appointment['additional_duration_id'] ?? '',
129 'team' => array(
130 'id' => $team_member->id ?? null,
131 'display_name' => $team_member->display_name ?? '',
132 'image' => $team_member->image ?? '',
133 'user_email' => $team_member->user_email ?? '',
134 ),
135 'service' => array(
136 'id' => $appointment['service_id'] ?? '',
137 'title' => $service['title'] ?? '',
138 'additional_duration' => $additional_duration,
139 'enable_google_meet' => $service['enable_google_meet'] ?? false,
140 ),
141 'duration' => $appointment['duration'] ?? null,
142 'quantity' => $appointment['quantity'] ?? 1,
143 'price' => $appointment['price'] ?? 0,
144 'subtotal' => $appointment['subtotal'] ?? 0,
145 'total' => $appointment['total'] ?? 0,
146 'status' => $appointment['status'] ?? '',
147 'total_attendees' => $appointment['total_attendees'] ?? 1,
148 'appointment_token' => $appointment['security_token'] ?? '',
149 'cancel_note' => $appointment['cancel_note'] ?? '',
150 );
151
152 return apply_filters( 'booktics_appointment_pro_format', $formatted_appointment, $appointment );
153 }
154
155 /**
156 * Resolve team member email with a WP user fallback.
157 *
158 * Model hydration may miss user_email if the team-member WP user row is
159 * stale, and the notification SDK would then skip the team email node.
160 *
161 * @param string $primary Email already resolved, if any.
162 * @param mixed $team_member_id Team member id.
163 *
164 * @return string
165 */
166 public static function resolve_team_member_email( string $primary, $team_member_id ): string {
167 if ( ! empty( $primary ) ) {
168 return $primary;
169 }
170
171 $team_member_id = absint( $team_member_id );
172
173 if ( ! $team_member_id ) {
174 return '';
175 }
176
177 $user = get_userdata( $team_member_id );
178
179 return $user && ! empty( $user->user_email ) ? $user->user_email : '';
180 }
181 }
182