PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.2.11
LatePoint – Calendar Booking Plugin for Appointments and Events v5.2.11
5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / helpers / orders_helper.php
latepoint / lib / helpers Last commit date
activities_helper.php 3 months ago agent_helper.php 3 months ago analytics_helper.php 4 months ago auth_helper.php 3 months ago blocks_helper.php 3 months ago booking_helper.php 3 months ago bricks_helper.php 3 months ago bundles_helper.php 3 months ago calendar_helper.php 3 months ago carts_helper.php 3 months ago connector_helper.php 3 months ago csv_helper.php 3 months ago customer_helper.php 3 months ago customer_import_helper.php 3 months ago database_helper.php 3 months ago debug_helper.php 3 months ago defaults_helper.php 3 months ago elementor_helper.php 3 months ago email_helper.php 3 months ago encrypt_helper.php 3 months ago events_helper.php 3 months ago form_helper.php 3 months ago icalendar_helper.php 3 months ago image_helper.php 3 months ago invoices_helper.php 3 months ago license_helper.php 3 months ago location_helper.php 3 months ago marketing_systems_helper.php 3 months ago meeting_systems_helper.php 3 months ago menu_helper.php 3 months ago meta_helper.php 3 months ago migrations_helper.php 3 months ago money_helper.php 3 months ago notifications_helper.php 3 months ago nps_survey_helper.php 3 months ago order_intent_helper.php 3 months ago orders_helper.php 3 months ago otp_helper.php 3 months ago pages_helper.php 3 months ago params_helper.php 3 months ago payments_helper.php 3 months ago price_breakdown_helper.php 3 months ago process_jobs_helper.php 3 months ago processes_helper.php 3 months ago replacer_helper.php 3 months ago resource_helper.php 3 months ago roles_helper.php 3 months ago router_helper.php 3 months ago service_helper.php 3 months ago sessions_helper.php 3 months ago settings_helper.php 3 months ago short_links_systems_helper.php 3 months ago shortcodes_helper.php 3 months ago sms_helper.php 3 months ago steps_helper.php 3 months ago stripe_connect_helper.php 3 months ago styles_helper.php 3 months ago support_topics_helper.php 3 months ago time_helper.php 3 months ago timeline_helper.php 3 months ago transaction_helper.php 3 months ago transaction_intent_helper.php 3 months ago util_helper.php 3 months ago version_specific_updates_helper.php 3 months ago whatsapp_helper.php 3 months ago work_periods_helper.php 3 months ago wp_datetime.php 3 months ago wp_user_helper.php 3 months ago
orders_helper.php
682 lines
1 <?php
2 /*
3 * Copyright (c) 2023 LatePoint LLC. All rights reserved.
4 */
5
6 class OsOrdersHelper {
7
8 /**
9 * @param $order_item_id
10 * @param $service_id
11 *
12 * @return OsBookingModel[]
13 */
14 public static function get_bookings_for_order_item( $order_item_id, $service_id = false, array $only_statuses = [] ): array {
15 $bookings = new OsBookingModel();
16 if ( $service_id ) {
17 $bookings = $bookings->where( [ 'service_id' => $service_id ] );
18 }
19 if ( ! empty( $only_statuses ) ) {
20 $bookings = $bookings->where( [ 'status' => $only_statuses ] );
21 }
22 $bookings = $bookings->where( [ 'order_item_id' => $order_item_id ] )->order_by( 'start_datetime_utc asc' )->get_results_as_models();
23
24 return $bookings;
25 }
26
27 public static function quick_order_btn_html( $order_id = false, $params = array() ) {
28 $html = '';
29 if ( $order_id ) {
30 $params['id'] = $order_id;
31 }
32 $route = OsRouterHelper::build_route_name( 'orders', 'quick_edit' );
33
34 $params_str = http_build_query( $params );
35 $html = 'data-os-params="' . esc_attr( $params_str ) . '"
36 data-os-action="' . esc_attr( $route ) . '"
37 data-os-output-target="side-panel"
38 data-os-after-call="latepoint_init_quick_order_form"';
39
40 return $html;
41 }
42
43 public static function get_order_statuses_list() {
44 $statuses = [
45 LATEPOINT_ORDER_STATUS_OPEN => __( 'Open', 'latepoint' ),
46 LATEPOINT_ORDER_STATUS_CANCELLED => __( 'Cancelled', 'latepoint' ),
47 LATEPOINT_ORDER_STATUS_COMPLETED => __( 'Completed', 'latepoint' ),
48 ];
49
50 /**
51 * Get list of statuses for orders
52 *
53 * @param {array} $statuses array of order status codes
54 * @returns {array} The filtered array of status codes
55 *
56 * @since 5.0.0
57 * @hook latepoint_order_statuses
58 *
59 */
60 return apply_filters( 'latepoint_order_statuses', $statuses );
61 }
62
63 public static function bundle_services_and_booked_count_for_order_item_id( int $order_item_id ): array {
64 $order_item = new OsOrderItemModel( $order_item_id );
65 $bundle_services = [];
66 if ( $order_item->is_bundle() ) {
67 $bundle = $order_item->build_original_object_from_item_data();
68 $bundle_services = $bundle->get_services( $order_item_id );
69 }
70
71 return $bundle_services;
72 }
73
74
75 public static function get_items_for_order_id( $order_id ) {
76 $order_items = new OsOrderItemModel();
77
78 return $order_items->where( [ 'order_id' => $order_id ] )->get_results_as_models();
79 }
80
81 public static function create_order_item_from_cart_item( OsCartItemModel $cart_item ): OsOrderItemModel {
82 $order_item = new OsOrderItemModel();
83 $order_item->variant = $cart_item->variant;
84 $order_item->item_data = $cart_item->item_data;
85 $order_item->total = $cart_item->get_total();
86 $order_item->subtotal = $cart_item->get_subtotal();
87 $order_item->coupon_code = $cart_item->get_coupon_code();
88 $order_item->coupon_discount = $cart_item->get_coupon_discount();
89 $order_item->tax_total = $cart_item->get_tax_total();
90
91 return $order_item;
92 }
93
94
95 public static function get_fulfillment_statuses_list(): array {
96 $statuses = [
97 LATEPOINT_ORDER_FULFILLMENT_STATUS_NOT_FULFILLED => __( 'Not Fulfilled', 'latepoint' ),
98 LATEPOINT_ORDER_FULFILLMENT_STATUS_FULFILLED => __( 'Fulfilled', 'latepoint' ),
99 LATEPOINT_ORDER_FULFILLMENT_STATUS_PARTIALLY_FULFILLED => __( 'Partially Fulfilled', 'latepoint' ),
100 ];
101
102 /**
103 * Get list of fulfillment statuses for orders
104 *
105 * @param {array} $statuses array of order fulfillment status codes
106 * @returns {array} The filtered array of fulfillment status codes
107 *
108 * @since 5.0.0
109 * @hook latepoint_order_fulfillment_statuses
110 *
111 */
112 return apply_filters( 'latepoint_order_fulfillment_statuses', $statuses );
113 }
114
115 public static function get_order_payment_statuses_list(): array {
116 $statuses = [
117 LATEPOINT_ORDER_PAYMENT_STATUS_NOT_PAID => __( 'Not Paid', 'latepoint' ),
118 LATEPOINT_ORDER_PAYMENT_STATUS_PARTIALLY_PAID => __( 'Partially Paid', 'latepoint' ),
119 LATEPOINT_ORDER_PAYMENT_STATUS_FULLY_PAID => __( 'Fully Paid', 'latepoint' ),
120 LATEPOINT_ORDER_PAYMENT_STATUS_PROCESSING => __( 'Processing', 'latepoint' ),
121 ];
122
123 /**
124 * Get list of fulfillment statuses for orders
125 *
126 * @param {array} $statuses array of order payment status codes
127 * @returns {array} The filtered array of payment status codes
128 *
129 * @since 5.0.0
130 * @hook latepoint_order_payment_statuses
131 *
132 */
133 return apply_filters( 'latepoint_order_payment_statuses', $statuses );
134 }
135
136 public static function get_default_order_status(): string {
137 $status = LATEPOINT_ORDER_STATUS_OPEN;
138
139 /**
140 * Get default order status
141 *
142 * @param {string} $status a default order status
143 * @returns {string} filtered status
144 *
145 * @since 5.0.0
146 * @hook latepoint_get_default_order_status
147 *
148 */
149 return apply_filters( 'latepoint_get_default_order_status', $status );
150 }
151
152
153 public static function get_nice_order_payment_status_name( $status ) {
154 $statuses_list = OsOrdersHelper::get_order_payment_statuses_list();
155 if ( $status && isset( $statuses_list[ $status ] ) ) {
156 return $statuses_list[ $status ];
157 } else {
158 return __( 'Undefined Status', 'latepoint' );
159 }
160 }
161
162 public static function get_nice_order_status_name( $status ) {
163 $statuses_list = OsOrdersHelper::get_order_statuses_list();
164 if ( $status && isset( $statuses_list[ $status ] ) ) {
165 return $statuses_list[ $status ];
166 } else {
167 return __( 'Undefined Status', 'latepoint' );
168 }
169 }
170
171 public static function get_nice_order_fulfillment_status_name( $status ) {
172 $statuses_list = OsOrdersHelper::get_fulfillment_statuses_list();
173 if ( $status && isset( $statuses_list[ $status ] ) ) {
174 return $statuses_list[ $status ];
175 } else {
176 return __( 'Undefined Status', 'latepoint' );
177 }
178 }
179
180
181 public static function unfold_price_breakdown_row( $rows ) {
182 $readable_price_breakdown = [];
183 foreach ( $rows as $row ) {
184 if ( empty( $row ) ) {
185 continue;
186 }
187 if ( ! empty( $row['heading'] ) && ! empty( $row['items'] ) ) {
188 $readable_price_breakdown = array_merge( $readable_price_breakdown, self::unfold_price_breakdown_row( $row['items'] ) );
189 } elseif ( ! empty( $row['label'] ) ) {
190 $label = $row['label'];
191 if ( ! empty( $row['note'] ) ) {
192 $label .= ' ' . $row['note'];
193 }
194 if ( ! empty( $row['badge'] ) ) {
195 $label .= ' (' . $row['badge'] . ')';
196 }
197 $readable_price_breakdown[ $label ] = $row['raw_value'] * 1;
198 } else {
199 $readable_price_breakdown = array_merge( $readable_price_breakdown, self::unfold_price_breakdown_row( $row ) );
200 }
201 }
202
203 return $readable_price_breakdown;
204 }
205
206
207 public static function generate_confirmation_message( OsOrderModel $order ): string {
208 $html = '<div class="summary-status-wrapper summary-status-style-' . esc_attr( OsStepsHelper::get_step_setting_value( 'confirmation', 'order_confirmation_message_style', 'green' ) ) . '">
209 <div class="summary-status-inner">
210 <div class="ss-icon"></div>
211 <div class="ss-title">' . OsStepsHelper::get_step_setting_value( 'confirmation', 'order_confirmation_message_title', esc_html__( 'Appointment Confirmed', 'latepoint' ) ) . '</div>
212 <div class="ss-description">' . OsStepsHelper::get_step_setting_value( 'confirmation', 'order_confirmation_message_content', esc_html__( 'We look forward to seeing you.', 'latepoint' ) ) . '</div>
213 <div class="ss-confirmation-number"><span>' . esc_html__( 'Order #', 'latepoint' ) . '</span><strong>' . esc_html( $order->confirmation_code ) . '</strong></div>
214 </div>
215 </div>';
216
217 return $html;
218 }
219
220
221 public static function generate_direct_manage_order_url( OsOrderModel $order, string $for, string $action = 'show' ): string {
222 if ( ! in_array( $for, [ 'agent', 'customer' ] ) ) {
223 return '';
224 }
225 $actions = [ 'show', 'list_payments', 'print' ];
226 if ( ! in_array( $action, $actions ) ) {
227 $action = $actions[0];
228 }
229 $key = $order->get_key_to_manage_for( $for );
230 $url = OsRouterHelper::build_admin_post_link( [ 'manage_order_by_key', $action ], [ 'key' => $key ] );
231
232 return $url;
233 }
234
235 public static function get_order_id_and_manage_ability_by_key( string $key ) {
236 $order_id = OsMetaHelper::get_order_id_by_meta_value( 'key_to_manage_for_agent', $key );
237 if ( $order_id ) {
238 return [
239 'order_id' => $order_id,
240 'for' => 'agent',
241 ];
242 }
243
244 $order_id = OsMetaHelper::get_order_id_by_meta_value( 'key_to_manage_for_customer', $key );
245 if ( $order_id ) {
246 return [
247 'order_id' => $order_id,
248 'for' => 'customer',
249 ];
250 }
251
252 return false;
253 }
254
255 public static function create_booking_object_from_booking_data_form( array $params ): OsBookingModel {
256 $booking = new OsBookingModel();
257
258 if ( ! empty( $params['item_data'] ) ) {
259 $item_data = json_decode( base64_decode( $params['item_data'] ), true );
260 $booking->set_data( $item_data );
261 } else {
262 $filtered_params = $params;
263 // input fields are formatted in customer preferred format, we need to convert that to database format Y-m-d
264 $filtered_params['start_date'] = OsTimeHelper::reformat_date_string( $params['start_date_formatted'], OsSettingsHelper::get_date_format(), 'Y-m-d' );
265
266
267 if ( isset( $params['start_time']['formatted_value'] ) ) {
268 $start_ampm = isset( $params['start_time']['ampm'] ) ? $params['start_time']['ampm'] : false;
269 $filtered_params['start_time'] = OsTimeHelper::convert_time_to_minutes( $params['start_time']['formatted_value'], $start_ampm );
270 }
271
272 // set custom end time/date if it was passed in params
273 if ( isset( $params['end_time']['formatted_value'] ) ) {
274 $end_ampm = isset( $params['end_time']['ampm'] ) ? $params['end_time']['ampm'] : false;
275 $filtered_params['end_time'] = OsTimeHelper::convert_time_to_minutes( $params['end_time']['formatted_value'], $end_ampm );
276 if ( $filtered_params['end_time'] <= $filtered_params['start_time'] ) {
277 // it's next day
278 $date_obj = new OsWpDateTime( $filtered_params['start_date'] );
279 $filtered_params['end_date'] = $date_obj->modify( '+1 day' )->format( 'Y-m-d' );
280 } else {
281 $filtered_params['end_date'] = $filtered_params['start_date'];
282 }
283 }
284 $booking->set_data( $filtered_params );
285 $booking->set_utc_datetimes();
286 }
287
288 return $booking;
289 }
290
291 public static function loading_tile_for_order_item( string $order_item_id ) {
292 return '<div class="order-item-temp-holder" data-order-item-id="' . $order_item_id . '"><div class="oit-avatar"></div><div class="oit-main-info"><div class="oit-title"></div><div class="oit-sub-title"></div></div></div>';
293 }
294
295 public static function booking_data_form_for_order_item_id( string $order_item_id, OsBookingModel $booking, string $order_item_variant = LATEPOINT_ITEM_VARIANT_BOOKING, bool $is_folded = true ): string {
296 $services = OsServiceHelper::get_allowed_active_services();
297 $agents = OsAgentHelper::get_allowed_active_agents();
298
299 $order_item_id = empty( $order_item_id ) ? OsUtilHelper::generate_form_id() : $order_item_id;
300
301 $extra_css_class = $is_folded ? 'is-folded' : 'is-unfolded';
302 $html = '';
303 $html = '<div class="order-item-booking-data-form-wrapper order-item-booking-data-variant-' . $order_item_variant . ' ' . $extra_css_class . '"
304 data-order-item-variant="' . $order_item_variant . '"
305 data-order-item-id="' . $order_item_id . '"
306 data-booking-id="' . $booking->get_form_id() . '">';
307 $html .= self::loading_tile_for_order_item( $order_item_id );
308 switch ( $order_item_variant ) {
309 case LATEPOINT_ITEM_VARIANT_BOOKING:
310 $html .= OsOrdersHelper::generate_order_item_pill_for_booking( $booking, $order_item_id );
311 break;
312 case LATEPOINT_ITEM_VARIANT_BUNDLE:
313 $html .= OsOrdersHelper::generate_order_item_pill_for_bundle_booking( $booking, $order_item_id );
314 break;
315 }
316 $html .= '<div class="order-booking-data-heading">
317 <div class="fold-order-item-wrapper fold-order-item-booking-data-form-btn">
318 <div class="fold-order-item-icon">
319 <i class="latepoint-icon latepoint-icon-chevron-up"></i>
320 </div>
321 <div class="ob-label">' . ( $booking->is_new_record() ? __( 'New Booking', 'latepoint' ) : __( 'Edit Booking', 'latepoint' ) ) . '</div>
322 </div>
323
324 <div class="remove-order-item-new-booking-btn remove-order-item-btn" data-os-prompt="' . __( 'Are you sure you want to remove this booking from the order?', 'latepoint' ) . '">
325 <i class="latepoint-icon latepoint-icon-trash1"></i>
326 </div>
327 </div>';
328 $html .= '<div class="order-item-booking-data-form-inner">';
329 $html .= OsFormHelper::hidden_field( 'order_items[' . $order_item_id . '][variant]', $order_item_variant );
330 ob_start();
331 include LATEPOINT_VIEWS_ABSPATH . 'bookings/_booking_data.php';
332 $html .= ob_get_clean();
333 $html .= '</div>';
334 $html .= '</div>';
335
336 return $html;
337 }
338
339
340 public static function get_orders_for_select(): array {
341 $order = new OsOrderModel();
342 $order = $order->order_by( 'id desc' )->set_limit( 100 )->get_results_as_models();
343 $order_options = [];
344 foreach ( $order as $order ) {
345 $name = $order->customer->full_name . ' [' . $order->confirmation_code . ' : ' . $order->id . ']';
346 $order_options[] = [
347 'value' => $order->id,
348 'label' => esc_html( $name ),
349 ];
350 }
351
352 return $order_options;
353 }
354
355 public static function generate_order_item_pill_for_bundle( OsBundleModel $bundle, $order_item_id = false, $preselected_booking_id = false ): string {
356 $html = '';
357 $order_item_form_id = $order_item_id ? $order_item_id : OsUtilHelper::generate_form_id();
358 $html .= '<div class="order-item-pill order-item-pill-variant-bundle" data-order-item-id="' . $order_item_form_id . '">';
359 $html .= '<input name="order_items[' . $order_item_form_id . '][id]" class="order_item_id" value="' . $order_item_form_id . '" type="hidden"/>';
360 $html .= '<input name="order_items[' . $order_item_form_id . '][variant]" value="' . LATEPOINT_ITEM_VARIANT_BUNDLE . '" type="hidden"/>';
361 $html .= '<input name="order_items[' . $order_item_form_id . '][item_data]" class="order_item_item_data" value="' . base64_encode( wp_json_encode( $bundle->generate_params_for_booking_form() ) ) . '" type="hidden"/>';
362 $html .= '<div class="order-item-pill-inner">';
363 $html .= '<div class="order-item-remove-btn remove-order-item-btn"
364 data-os-prompt="' . __( 'Are you sure you want to remove this item from the order? All associated appointments will be removed as well.', 'latepoint' ) . '"></div>';
365 $html .= OsBundlesHelper::generate_order_summary_for_bundle( $bundle, $order_item_form_id, $preselected_booking_id );
366 $html .= '<div class="bundle-icon"><i class="latepoint-icon latepoint-icon-chevron-down"></i></div>';
367 $html .= '</div>';
368 $html .= '<div class="order-item-shadow"></div><div class="order-item-shadow"></div>';
369 $html .= '</div>';
370
371 return $html;
372 }
373
374 public static function generate_order_item_pill_for_booking( OsBookingModel $booking, $order_item_id = false ): string {
375 $html = '';
376 $order_item_form_id = $order_item_id ? $order_item_id : OsUtilHelper::generate_form_id();
377 $is_past = ( ! $booking->is_upcoming() ) ? 'is-past' : '';
378 $html .= '<div class="order-item-pill order-item-pill-variant-' . LATEPOINT_ITEM_VARIANT_BOOKING . ' ' . $is_past . ' status-' . $booking->status . '" data-order-item-id="' . $order_item_form_id . '">';
379 $html .= '<input name="order_items[' . $order_item_form_id . '][id]" class="order_item_id" value="' . $order_item_form_id . '" type="hidden"/>';
380 $html .= '<input name="order_items[' . $order_item_form_id . '][variant]" value="' . LATEPOINT_ITEM_VARIANT_BOOKING . '" type="hidden"/>';
381 $html .= '<div class="order-item-pill-inner">';
382 if ( $booking->recurrence_id ) {
383 $html .= '<div class="order-item-pill-recurring-mark"><div class="popover-message">' . esc_html__( 'Part of recurring sequence', 'latepoint' ) . '</div><i class="latepoint-icon latepoint-icon-refresh"></i></div>';
384 }
385 $html .= '<div class="order-item-remove-btn remove-order-item-btn"
386 data-os-prompt="' . __( 'Are you sure you want to remove this item from the order?', 'latepoint' ) . '"></div>';
387 $html .= '<div class="booking-item-status-pill"></div>';
388 $html .= OsBookingHelper::generate_summary_for_booking( $booking, false, 'agent' );
389 $html .= '<div class="os-avatar-w" style="background-image: url(' . ( ( $booking->agent->avatar_image_id ) ? $booking->agent->get_avatar_url() : '' ) . ')">';
390 if ( ! $booking->agent->avatar_image_id ) {
391 $html .= '<div class="os-avatar"><span>' . $booking->agent->get_initials() . '</span></div>';
392 }
393 $html .= '</div>';
394 $html .= '</div>';
395 $html .= '</div>';
396
397 return $html;
398 }
399
400 public static function generate_order_item_pill_for_bundle_booking( OsBookingModel $booking, $order_item_id ): string {
401 $is_past = ( ! $booking->is_upcoming() ) ? 'is-past' : '';
402 $html = '<div class="bundle-booking-item-pill ' . $is_past . ' status-' . $booking->status . '">';
403 $html .= '<div class="bundle-booking-item-pill-inner">';
404 $html .= '<div class="booking-item-status-pill"></div>';
405 $html .= '<div class="bib-datetime">' . $booking->get_nice_start_datetime() . '</div>';
406 $html .= '</div>';
407 $html .= '</div>';
408
409 return $html;
410 }
411
412 public static function generate_booking_block_for_bundle_order_item( OsBookingModel $booking, $order_item_id, bool $is_booked = true, $is_preselected = false ): string {
413 $html = '';
414 $is_booked_css = $is_booked ? 'is-booked' : '';
415 $is_preselected_css = ( $is_preselected ? 'is-preselected' : '' );
416
417 $html .= '<div class="order-item-variant-bundle-booking ' . $is_booked_css . ' ' . $is_preselected_css . '"
418 data-order-item-variant="' . LATEPOINT_ITEM_VARIANT_BUNDLE . '"
419 data-order-item-id="' . $order_item_id . '"
420 data-booking-id="' . $booking->get_form_id() . '">';
421 $html .= '<div class="scheduled-bundle-booking">';
422 $html .= self::loading_tile_for_order_item( $order_item_id );
423 if ( $is_booked ) {
424 $html .= OsOrdersHelper::booking_data_form_for_order_item_id( $order_item_id, $booking, LATEPOINT_ITEM_VARIANT_BUNDLE, ! $is_preselected );
425 }
426 $html .= '</div>';
427 $html .= '<div class="unscheduled-bundle-booking">
428 <div class="booking-item-status-pill"></div>
429 <div class="bib-label">' . __( 'Schedule now', 'latepoint' ) . '</div>
430 <input name="order_items[' . $order_item_id . '][unscheduled_bookings][' . $booking->get_form_id() . '][item_data]" class="unscheduled_booking_item_data" value="' . base64_encode( wp_json_encode( $booking->generate_params_for_booking_form() ) ) . '" type="hidden"/>
431 </div>';
432 $html .= '</div>';
433
434 return $html;
435 }
436
437 public static function generate_price_breakdown_from_params( array $price_breakdown_params ): array {
438 $price_breakdown_rows = [];
439 $allowed_keys = [ 'before_subtotal', 'after_subtotal' ];
440 foreach ( $allowed_keys as $key ) {
441 if ( ! empty( $price_breakdown_params[ $key ] ) ) {
442 foreach ( $price_breakdown_params[ $key ] as $row ) {
443 if ( ! empty( $row['items'] ) ) {
444 $group = [
445 'heading' => '',
446 'items' => [],
447 'sub_items' => [],
448 ];
449 if ( ! empty( $row['heading'] ) ) {
450 $group['heading'] = $row['heading'];
451 }
452 foreach ( $row['items'] as $item ) {
453 if ( isset( $item['value'] ) ) {
454 $item['raw_value'] = OsMoneyHelper::convert_amount_from_money_input_to_db_format( $item['value'] );
455 }
456 $group['items'][] = $item;
457 }
458 if ( ! empty( $row['sub_items'] ) ) {
459 foreach ( $row['sub_items'] as $sub_item ) {
460 if ( isset( $sub_item['value'] ) ) {
461 $sub_item['raw_value'] = OsMoneyHelper::convert_amount_from_money_input_to_db_format( $sub_item['value'] );
462 }
463 $group['sub_items'][] = $sub_item;
464 }
465 }
466 $price_breakdown_rows[ $key ][] = $group;
467 } else {
468 if ( isset( $row['value'] ) ) {
469 $row['raw_value'] = OsMoneyHelper::convert_amount_from_money_input_to_db_format( $row['value'] );
470 }
471 $price_breakdown_rows[ $key ][] = $row;
472 }
473 }
474 }
475 }
476
477 return $price_breakdown_rows;
478 }
479
480 public static function create_order_item_object( array $order_items_param ): OsOrderItemModel {
481 $order_item_model = new OsOrderItemModel();
482 $order_item_model->variant = $order_items_param['variant'];
483 if ( $order_item_model->is_bundle() ) {
484 $order_item_model->item_data = base64_decode( $order_items_param['item_data'] );
485 } else {
486 // it's a booking variant - so there is only one booking in the array, but still loop for ease of use
487 foreach ( $order_items_param['bookings'] as $booking_params ) {
488 $booking = OsOrdersHelper::create_booking_object_from_booking_data_form( $booking_params );
489 $order_item_model->item_data = $booking->generate_item_data();
490 }
491 }
492
493 return $order_item_model;
494 }
495
496 public static function generate_transactions_breakdown_html( OsOrderModel $order ): string {
497 $html = '';
498 $transactions = $order->get_transactions();
499 if ( $transactions ) {
500 $html .= '<div style="margin: 20px 0;">';
501 foreach ( $transactions as $transaction ) {
502 $html .= '<div style="margin-bottom: 10px;">';
503 $html .= '<div>' . esc_html__( 'Payment Portion: ', 'latepoint' ) . '<strong>' . $transaction->get_payment_portion_nice_name() . '</strong></div>';
504 $html .= '<div>' . esc_html__( 'Payment Amount: ', 'latepoint' ) . '<strong>' . OsMoneyHelper::format_price( $transaction->amount, true, false ) . '</strong></div>';
505 $html .= '</div>';
506 }
507 $html .= '</div>';
508 } else {
509 $html .= esc_html__( 'No transactions found.', 'latepoint' );
510 }
511
512 return $html;
513 }
514
515 public static function generate_summary_breakdown_html( OsOrderModel $order ): string {
516 ob_start();
517 OsPriceBreakdownHelper::output_price_breakdown( $order->generate_price_breakdown_rows(), true );
518 $html = ob_get_clean();
519
520 return $html;
521 }
522
523 public static function generate_order_items_html( OsOrderModel $order ) {
524 $html = '';
525 $order_items = $order->get_items();
526 $html .= '<table style="width: 100%;">';
527 $total_items = count( $order_items );
528 $i = 0;
529 foreach ( $order_items as $order_item ) {
530 $i++;
531 $html .= '<tr>';
532 $data_style = ( $i < $total_items ) ? 'border-bottom: 1px solid #eee;' : '';
533 $html .= '<td style="' . $data_style . ' padding: 10px 0;">';
534 if ( $order_item->is_bundle() ) {
535 $bundle = $order_item->build_original_object_from_item_data();
536 $html .= '<div>' . $bundle->name . '</div>';
537 $bundle_services = $bundle->get_services();
538 foreach ( $bundle_services as $service ) {
539 $qty = $service->join_attributes['quantity'];
540 $qty_html = $qty > 1 ? ' [' . $qty . ']' : '';
541 $html .= '<div style="color: #999; font-size: 14px;">' . esc_html( $service->name . $qty_html ) . '</div>';
542 }
543 } else {
544 $booking = $order_item->build_original_object_from_item_data();
545 $html .= '<div style="font-weight: bold;">' . $booking->service->name . '</div>';
546 $html .= '<div>' . $booking->get_nice_start_datetime() . '</div>';
547 $html .= '<div>' . $booking->agent->get_full_name() . '</div>';
548 }
549 $html .= '</td>';
550 $html .= '</tr>';
551 }
552 $html .= '</table>';
553
554 return $html;
555 }
556
557 public static function extract_agent_emails( OsOrderModel $order ): string {
558 $to_emails = [];
559 $order_items = $order->get_items();
560 foreach ( $order_items as $order_item ) {
561 if ( $order_item->is_bundle() ) {
562 $bundle_bookings = OsOrdersHelper::get_bookings_for_order_item( $order_item->id );
563 foreach ( $bundle_bookings as $booking ) {
564 $to_emails[] = $booking->agent->get_full_name() . ' <' . $booking->agent->email . '>';
565 }
566 } else {
567 // booking
568 $booking = $order_item->build_original_object_from_item_data();
569 $to_emails[] = $booking->agent->get_full_name() . ' <' . $booking->agent->email . '>';
570 }
571 }
572 $to_emails = array_unique( $to_emails );
573
574 return implode( ', ', $to_emails );
575 }
576
577 public static function extract_agent_full_names( OsOrderModel $order ): string {
578 $full_names = [];
579 $order_items = $order->get_items();
580 foreach ( $order_items as $order_item ) {
581 if ( $order_item->is_bundle() ) {
582 $bundle_bookings = OsOrdersHelper::get_bookings_for_order_item( $order_item->id );
583 foreach ( $bundle_bookings as $booking ) {
584 $full_names[] = $booking->agent->get_full_name();
585 }
586 } else {
587 // booking
588 $booking = $order_item->build_original_object_from_item_data();
589 $full_names[] = $booking->agent->get_full_name();
590 }
591 }
592 $full_names = array_unique( $full_names );
593
594 return implode( ', ', $full_names );
595 }
596
597
598
599 /**
600 * Extract property by name from order. For example, location_id, agent_id...
601 * @param OsOrderModel $order
602 * @param string $property
603 *
604 * @return string
605 */
606 public static function extract_property_by_name( OsOrderModel $order, string $property ): string {
607 $property_values = [];
608 $order_items = $order->get_items();
609
610 $property_map = [
611 'service_ids' => 'service_id',
612 'location_ids' => 'location_id',
613 'agent_ids' => 'agent_id',
614 'bundle_ids' => 'bundle_id',
615 ];
616
617 if ( empty( $property_map[ $property ] ) ) {
618 return '';
619 }
620 $mapped_property = $property_map[ $property ];
621
622 foreach ( $order_items as $order_item ) {
623 if ( $order_item->is_bundle() ) {
624 if ( $mapped_property == 'bundle_id' ) {
625 $property_values[] = $order_item->get_item_data_value_by_key( 'bundle_id' );
626 } else {
627 $bundle_bookings = OsOrdersHelper::get_bookings_for_order_item( $order_item->id );
628 $property_values = array_merge( $property_values, array_column( $bundle_bookings, $mapped_property ) );
629 }
630 } else {
631 $booking = $order_item->build_original_object_from_item_data();
632 if ( ! empty( $booking->$mapped_property ) ) {
633 $property_values[] = $booking->$mapped_property;
634 }
635 }
636 }
637
638 return implode( ',', array_unique( $property_values ) );
639 }
640
641 public static function check_if_order_invoices_paid_full_balance( $order_id ) {
642 $order = new OsOrderModel( $order_id );
643 $invoices = new OsInvoiceModel();
644 $paid_invoices = $invoices->where(
645 [
646 'status' => LATEPOINT_INVOICE_STATUS_PAID,
647 'order_id' => $order_id,
648 ]
649 )->get_results_as_models();
650 $total_paid = 0;
651 $updated = false;
652 foreach ( $paid_invoices as $invoice ) {
653 $total_paid += $invoice->charge_amount;
654 }
655 if ( $total_paid > 0 ) {
656 $old_order = clone $order;
657 if ( $total_paid < $order->get_total() ) {
658 if ( $order->payment_status != LATEPOINT_ORDER_PAYMENT_STATUS_PARTIALLY_PAID ) {
659 $updated = $order->update_attributes( [ 'payment_status' => LATEPOINT_ORDER_PAYMENT_STATUS_PARTIALLY_PAID ] );
660 }
661 } else {
662 if ( $order->get_total() > 0 && $order->payment_status != LATEPOINT_ORDER_PAYMENT_STATUS_FULLY_PAID ) {
663 $updated = $order->update_attributes( [ 'payment_status' => LATEPOINT_ORDER_PAYMENT_STATUS_FULLY_PAID ] );
664 }
665 }
666 if ( $updated ) {
667 /**
668 * Order was updated
669 *
670 * @param {OsOrderModel} $order instance of order model after it was updated
671 * @param {OsOrderModel} $old_order instance of order model before it was updated
672 *
673 * @since 5.0.0
674 * @hook latepoint_order_updated
675 *
676 */
677 do_action( 'latepoint_order_updated', $order, $old_order );
678 }
679 }
680 }
681 }
682