PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.19
Booktics – Appointment Booking Calendar for Service Businesses v1.0.19
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 1.0.4 1.0.5 1.0.6 1.0.7 All 27 releases
booktics / base / models / appointment-model.php

appointment-model.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.19, at base/models/appointment-model.php

316 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Appoint Model
4 *
5 * @package Booktics/Models
6 */
7
8 namespace Booktics\Models;
9
10 use Booktics\Abstracts\Booktics_Database;
11 use Booktics\Abstracts\Post_Model;
12 use Booktics\Models\Service_Model;
13 use Exception;
14
15 /**
16 * AppointmentModel class
17 */
18 class Appointment_Model extends Post_Model {
19 /**
20 * Store appointment data
21 *
22 * @var array
23 */
24 protected $fillable = array(
25 'id' => '',
26 'title' => '',
27 'content' => '',
28 'start_time' => '',
29 'end_time' => '',
30 'date' => '',
31 'buffer_before' => '',
32 'buffer_after' => '',
33 'duration' => '',
34 'status' => '',
35 'payment_status' => '',
36 'customer_id' => '',
37 'customer_name' => '',
38 'team_member_id' => '',
39 'service_id' => '',
40 'additional_duration_id' => '',
41 'group_booking' => array(),
42 'order_id' => '',
43 'quantity' => 0,
44 'price' => 0,
45 'subtotal' => 0,
46 'total' => 0,
47 'note' => '',
48 'source' => '',
49 'location_id' => '',
50 'location' => array(),
51 'extra_services' => array(),
52 'security_token' => '',
53 'total_attendees' => 1,
54 'timezone' => '',
55 'cancel_note' => '',
56 );
57
58 /**
59 * Accepted post statuses for appointments
60 *
61 * @var array
62 */
63 public static $accepted_statuses = array(
64 'pending',
65 'active',
66 'cancelled',
67 'canceled',
68 'completed',
69 'approved',
70 'not_now',
71 );
72
73 /**
74 * Get post type
75 *
76 * @return string Post type name
77 */
78 public function get_post_type() {
79 return 'booktics-appointment';
80 }
81
82 /**
83 * Get accepted statuses
84 *
85 * @return array Accepted statuses
86 */
87 public static function get_accepted_statuses() {
88 return self::$accepted_statuses;
89 }
90
91 /**
92 * Specify data which should be serialized to JSON.
93 *
94 * @return array
95 */
96 public function jsonSerialize(): array {
97 $data = $this->to_array();
98
99 $service = Service_Model::find( $this->service_id );
100 $db = new Booktics_Database();
101 $customer = ( new Guest_Model( $db ) )->find( array( 'id' => $this->customer_id ) );
102 $team_member = ( new Team_Member_Model() )->find( $this->team_member_id );
103
104 $appointment_data = array(
105 'service' => $service,
106 'team_member' => $team_member,
107 'customer' => $customer,
108 );
109
110 return array_merge( $data, $appointment_data );
111 }
112
113 /**
114 * Get appointments for a specific date and service/team member
115 *
116 * @param string $date
117 * @param int $team_member_id
118 *
119 * @return array
120 * @throws Exception
121 */
122 public function get_appointments_for_slot( string $date, int $team_member_id ): array {
123 $args = array(
124 'post_type' => $this->get_post_type(),
125 'post_status' => booktics_get_option( 'appointment_statuses_for_blocked_slot', array( 'publish', 'active', 'pending' ) ),
126 'posts_per_page' => - 1,
127 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering appointments by meta fields
128 'meta_query' => array(
129 'relation' => 'AND',
130 array(
131 'key' => 'date',
132 'value' => $date,
133 'compare' => '=',
134 ),
135 array(
136 'key' => 'team_member_id',
137 'value' => $team_member_id,
138 'compare' => '=',
139 ),
140 ),
141 );
142
143 $query = new \WP_Query( $args );
144
145 return array_map(
146 function ( $post ) {
147 return new static( $post );
148 }, $query->posts
149 );
150 }
151
152 /**
153 * Override load_attributes to include payment status from orders
154 *
155 * @return void
156 */
157 protected function load_attributes() {
158 parent::load_attributes();
159
160 if ( $this->order_id ) {
161 $db = new Booktics_Database();
162 $order_model = ( new Order_Model( $db ) )->find( array( 'id' => $this->order_id ) );
163
164 $this->attributes['payment_status'] = $order_model->payment_status ?? 'pending';
165 } else {
166 $this->attributes['payment_status'] = 'pending';
167 }
168 }
169
170 /**
171 * Get appointments count by order ID
172 *
173 * @param int $order_id Order ID
174 *
175 * @return int Number of appointments
176 */
177 public function get_appointments_count_by_order_id( $order_id ) {
178 $args = array(
179 'post_type' => $this->get_post_type(),
180 'post_status' => array( 'publish', 'active', 'approved', 'completed', 'pending' ),
181 'posts_per_page' => - 1,
182 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering appointments by meta fields
183 'meta_query' => array(
184 array(
185 'key' => 'order_id',
186 'value' => $order_id,
187 'compare' => '=',
188 ),
189 ),
190 );
191
192 $query = new \WP_Query( $args );
193
194 return $query->found_posts;
195 }
196
197 /**
198 * Get appointments count by team member ID
199 *
200 * @param int $team_member_id Team member ID
201 *
202 * @return int Number of appointments
203 */
204 public function get_appointments_count_by_team_member_id( $team_member_id ) {
205 $args = array(
206 'post_type' => $this->get_post_type(),
207 'post_status' => array( 'publish', 'active', 'pending' ),
208 'posts_per_page' => - 1,
209 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering appointments by meta fields
210 'meta_query' => array(
211 array(
212 'key' => 'team_member_id',
213 'value' => $team_member_id,
214 'compare' => '=',
215 ),
216 ),
217 );
218
219 $query = new \WP_Query( $args );
220
221 return $query->found_posts;
222 }
223
224 /**
225 * Get reschedule link
226 *
227 * @return string Reschedule link
228 */
229 public function get_reschedule_link() {
230 if ( ! booktics_get_option( 'allow_customer_rescheduling', false ) ) {
231 return '';
232 }
233
234 $this->add_security_token_if_not_exists();
235
236 $service_permalink = $this->get_associated_service_permalink();
237
238 $reschedule_url = add_query_arg( [
239 'service_id' => $this->service_id,
240 'appointment_id' => $this->id,
241 'booking_action' => 'reschedule',
242 'appointment_token' => $this->security_token,
243 'order_id' => $this->order_id,
244 'team_member_id' => $this->team_member_id,
245 ], $service_permalink );
246
247 return esc_url( $reschedule_url );
248 }
249
250 /**
251 * Get cancel link
252 *
253 * @return string Cancel link
254 */
255 public function get_cancel_link() {
256 if ( ! booktics_get_option( 'allow_customer_cancellation', false ) ) {
257 return '';
258 }
259
260 $this->add_security_token_if_not_exists();
261
262 $service_permalink = $this->get_associated_service_permalink();
263
264 $cancel_url = add_query_arg( [
265 'service_id' => $this->service_id,
266 'appointment_id' => $this->id,
267 'booking_action' => 'cancel',
268 'appointment_token' => $this->security_token,
269 'order_id' => $this->order_id,
270 'team_member_id' => $this->team_member_id,
271 ], $service_permalink );
272
273 return esc_url( $cancel_url );
274 }
275
276 /**
277 * Get associated service permalink
278 *
279 * @return string Service permalink
280 */
281 private function get_associated_service_permalink() {
282 if ( ! $this->service_id ) {
283 return '';
284 }
285
286 $service = Service_Model::find( $this->service_id );
287
288 return $service->link ?? '';
289 }
290
291 /**
292 * Generate a security token for the appointment
293 * This generated token is used to verify access to the appointment rescheduling and cancellation
294 *
295 * @return string Security token
296 */
297 public function generate_security_token() {
298 $token = bin2hex( random_bytes(4) ); // 8 hex chars
299
300 return $token;
301 }
302
303 /**
304 * Check if the security token exists, if not generate and save it to the post meta.
305 * This token is used to verify access to the appointment rescheduling and cancellation.
306 *
307 * @return void
308 */
309 public function add_security_token_if_not_exists() {
310 if ( empty( $this->security_token ) ) {
311 $this->security_token = $this->generate_security_token();
312 $this->save();
313 }
314 }
315 }
316