PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.1.8
LatePoint – Calendar Booking Plugin for Appointments and Events v5.1.8
5.6.7 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 1 year ago agent_helper.php 1 year ago auth_helper.php 1 year ago blocks_helper.php 1 year ago booking_helper.php 1 year ago bricks_helper.php 1 year ago bundles_helper.php 1 year ago calendar_helper.php 1 year ago carts_helper.php 1 year ago connector_helper.php 1 year ago csv_helper.php 1 year ago customer_helper.php 1 year ago database_helper.php 1 year ago debug_helper.php 1 year ago defaults_helper.php 1 year ago elementor_helper.php 1 year ago email_helper.php 1 year ago encrypt_helper.php 1 year ago events_helper.php 1 year ago form_helper.php 1 year ago icalendar_helper.php 1 year ago image_helper.php 1 year ago invoices_helper.php 1 year ago license_helper.php 1 year ago location_helper.php 1 year ago marketing_systems_helper.php 1 year ago meeting_systems_helper.php 1 year ago menu_helper.php 1 year ago meta_helper.php 1 year ago migrations_helper.php 1 year ago money_helper.php 1 year ago notifications_helper.php 1 year ago order_intent_helper.php 1 year ago orders_helper.php 1 year ago pages_helper.php 1 year ago params_helper.php 1 year ago payments_helper.php 1 year ago price_breakdown_helper.php 1 year ago process_jobs_helper.php 1 year ago processes_helper.php 1 year ago replacer_helper.php 1 year ago resource_helper.php 1 year ago roles_helper.php 1 year ago router_helper.php 1 year ago service_helper.php 1 year ago sessions_helper.php 1 year ago settings_helper.php 1 year ago shortcodes_helper.php 1 year ago sms_helper.php 1 year ago steps_helper.php 1 year ago stripe_connect_helper.php 1 year ago styles_helper.php 1 year ago support_topics_helper.php 1 year ago time_helper.php 1 year ago timeline_helper.php 1 year ago transaction_helper.php 1 year ago transaction_intent_helper.php 1 year ago util_helper.php 1 year ago version_specific_updates_helper.php 1 year ago whatsapp_helper.php 1 year ago work_periods_helper.php 1 year ago wp_datetime.php 1 year ago wp_user_helper.php 1 year ago
orders_helper.php
665 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' ];
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 [ 'order_id' => $order_id, 'for' => 'agent' ];
239 }
240
241 $order_id = OsMetaHelper::get_order_id_by_meta_value( "key_to_manage_for_customer", $key );
242 if ( $order_id ) {
243 return [ 'order_id' => $order_id, 'for' => 'customer' ];
244 }
245
246 return false;
247 }
248
249 public static function create_booking_object_from_booking_data_form( array $params ): OsBookingModel {
250 $booking = new OsBookingModel();
251
252 if ( ! empty( $params['item_data'] ) ) {
253 $item_data = json_decode( base64_decode( $params['item_data'] ), true );
254 $booking->set_data( $item_data );
255 } else {
256 $filtered_params = $params;
257 // input fields are formatted in customer preferred format, we need to convert that to database format Y-m-d
258 $filtered_params['start_date'] = OsTimeHelper::reformat_date_string( $params['start_date_formatted'], OsSettingsHelper::get_date_format(), 'Y-m-d' );
259
260
261 if ( isset( $params['start_time']['formatted_value'] ) ) {
262 $start_ampm = isset( $params['start_time']['ampm'] ) ? $params['start_time']['ampm'] : false;
263 $filtered_params['start_time'] = OsTimeHelper::convert_time_to_minutes( $params['start_time']['formatted_value'], $start_ampm );
264 }
265
266 // set custom end time/date if it was passed in params
267 if ( isset( $params['end_time']['formatted_value'] ) ) {
268 $end_ampm = isset( $params['end_time']['ampm'] ) ? $params['end_time']['ampm'] : false;
269 $filtered_params['end_time'] = OsTimeHelper::convert_time_to_minutes( $params['end_time']['formatted_value'], $end_ampm );
270 if ( $filtered_params['end_time'] <= $filtered_params['start_time'] ) {
271 // it's next day
272 $date_obj = new OsWpDateTime( $filtered_params['start_date'] );
273 $filtered_params['end_date'] = $date_obj->modify( '+1 day' )->format( 'Y-m-d' );
274 } else {
275 $filtered_params['end_date'] = $filtered_params['start_date'];
276 }
277 }
278 $booking->set_data( $filtered_params );
279 $booking->set_utc_datetimes();
280 }
281
282 return $booking;
283 }
284
285 public static function loading_tile_for_order_item( string $order_item_id ) {
286 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>';
287 }
288
289 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 {
290 $services = OsServiceHelper::get_allowed_active_services();
291 $agents = OsAgentHelper::get_allowed_active_agents();
292
293 $order_item_id = empty( $order_item_id ) ? OsUtilHelper::generate_form_id() : $order_item_id;
294
295 $extra_css_class = $is_folded ? 'is-folded' : 'is-unfolded';
296 $html = '';
297 $html = '<div class="order-item-booking-data-form-wrapper order-item-booking-data-variant-' . $order_item_variant . ' ' . $extra_css_class . '"
298 data-order-item-variant="' . $order_item_variant . '"
299 data-order-item-id="' . $order_item_id . '"
300 data-booking-id="' . $booking->get_form_id() . '">';
301 $html .= self::loading_tile_for_order_item( $order_item_id );
302 switch ( $order_item_variant ) {
303 case LATEPOINT_ITEM_VARIANT_BOOKING:
304 $html .= OsOrdersHelper::generate_order_item_pill_for_booking( $booking, $order_item_id );
305 break;
306 case LATEPOINT_ITEM_VARIANT_BUNDLE:
307 $html .= OsOrdersHelper::generate_order_item_pill_for_bundle_booking( $booking, $order_item_id );
308 break;
309 }
310 $html .= '<div class="order-booking-data-heading">
311 <div class="fold-order-item-wrapper fold-order-item-booking-data-form-btn">
312 <div class="fold-order-item-icon">
313 <i class="latepoint-icon latepoint-icon-chevron-up"></i>
314 </div>
315 <div class="ob-label">' . ( $booking->is_new_record() ? __( 'New Booking', 'latepoint' ) : __( 'Edit Booking', 'latepoint' ) ) . '</div>
316 </div>
317
318 <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' ) . '">
319 <i class="latepoint-icon latepoint-icon-trash1"></i>
320 </div>
321 </div>';
322 $html .= '<div class="order-item-booking-data-form-inner">';
323 $html .= OsFormHelper::hidden_field( 'order_items[' . $order_item_id . '][variant]', $order_item_variant );
324 ob_start();
325 include( LATEPOINT_VIEWS_ABSPATH . 'bookings/_booking_data.php' );
326 $html .= ob_get_clean();
327 $html .= '</div>';
328 $html .= '</div>';
329
330 return $html;
331 }
332
333
334 public static function get_orders_for_select() : array {
335 $order = new OsOrderModel();
336 $order = $order->order_by( 'id desc' )->set_limit( 100 )->get_results_as_models();
337 $order_options = [];
338 foreach ( $order as $order ) {
339 $name = $order->customer->full_name . ' [' . $order->confirmation_code . ' : ' . $order->id . ']';
340 $order_options[] = [ 'value' => $order->id, 'label' => esc_html( $name ) ];
341 }
342
343 return $order_options;
344 }
345
346 public static function generate_order_item_pill_for_bundle( OsBundleModel $bundle, $order_item_id = false, $preselected_booking_id = false ): string {
347 $html = '';
348 $order_item_form_id = $order_item_id ? $order_item_id : OsUtilHelper::generate_form_id();
349 $html .= '<div class="order-item-pill order-item-pill-variant-bundle" data-order-item-id="' . $order_item_form_id . '">';
350 $html .= '<input name="order_items[' . $order_item_form_id . '][id]" class="order_item_id" value="' . $order_item_form_id . '" type="hidden"/>';
351 $html .= '<input name="order_items[' . $order_item_form_id . '][variant]" value="' . LATEPOINT_ITEM_VARIANT_BUNDLE . '" type="hidden"/>';
352 $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"/>';
353 $html .= '<div class="order-item-pill-inner">';
354 $html .= '<div class="order-item-remove-btn remove-order-item-btn"
355 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>';
356 $html .= OsBundlesHelper::generate_order_summary_for_bundle( $bundle, $order_item_form_id, $preselected_booking_id );
357 $html .= '<div class="bundle-icon"><i class="latepoint-icon latepoint-icon-chevron-down"></i></div>';
358 $html .= '</div>';
359 $html .= '<div class="order-item-shadow"></div><div class="order-item-shadow"></div>';
360 $html .= '</div>';
361
362 return $html;
363 }
364
365 public static function generate_order_item_pill_for_booking( OsBookingModel $booking, $order_item_id = false ): string {
366 $html = '';
367 $order_item_form_id = $order_item_id ? $order_item_id : OsUtilHelper::generate_form_id();
368 $is_past = ( ! $booking->is_upcoming() ) ? 'is-past' : '';
369 $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 . '">';
370 $html .= '<input name="order_items[' . $order_item_form_id . '][id]" class="order_item_id" value="' . $order_item_form_id . '" type="hidden"/>';
371 $html .= '<input name="order_items[' . $order_item_form_id . '][variant]" value="' . LATEPOINT_ITEM_VARIANT_BOOKING . '" type="hidden"/>';
372 $html .= '<div class="order-item-pill-inner">';
373 if($booking->recurrence_id){
374 $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>';
375 }
376 $html .= '<div class="order-item-remove-btn remove-order-item-btn"
377 data-os-prompt="' . __( 'Are you sure you want to remove this item from the order?', 'latepoint' ) . '"></div>';
378 $html .= '<div class="booking-item-status-pill"></div>';
379 $html .= OsBookingHelper::generate_summary_for_booking( $booking, false, 'agent' );
380 $html .= '<div class="os-avatar-w" style="background-image: url(' . ( ( $booking->agent->avatar_image_id ) ? $booking->agent->get_avatar_url() : '' ) . ')">';
381 if ( ! $booking->agent->avatar_image_id ) {
382 $html .= '<div class="os-avatar"><span>' . $booking->agent->get_initials() . '</span></div>';
383 }
384 $html .= '</div>';
385 $html .= '</div>';
386 $html .= '</div>';
387
388 return $html;
389 }
390
391 public static function generate_order_item_pill_for_bundle_booking( OsBookingModel $booking, $order_item_id ): string {
392 $is_past = ( ! $booking->is_upcoming() ) ? 'is-past' : '';
393 $html = '<div class="bundle-booking-item-pill ' . $is_past . ' status-' . $booking->status . '">';
394 $html .= '<div class="bundle-booking-item-pill-inner">';
395 $html .= '<div class="booking-item-status-pill"></div>';
396 $html .= '<div class="bib-datetime">' . $booking->get_nice_start_datetime() . '</div>';
397 $html .= '</div>';
398 $html .= '</div>';
399
400 return $html;
401 }
402
403 public static function generate_booking_block_for_bundle_order_item( OsBookingModel $booking, $order_item_id, bool $is_booked = true, $is_preselected = false ): string {
404 $html = '';
405 $is_booked_css = $is_booked ? 'is-booked' : '';
406 $is_preselected_css = ( $is_preselected ? 'is-preselected' : '' );
407
408 $html .= '<div class="order-item-variant-bundle-booking ' . $is_booked_css . ' ' . $is_preselected_css . '"
409 data-order-item-variant="' . LATEPOINT_ITEM_VARIANT_BUNDLE . '"
410 data-order-item-id="' . $order_item_id . '"
411 data-booking-id="' . $booking->get_form_id() . '">';
412 $html .= '<div class="scheduled-bundle-booking">';
413 $html .= self::loading_tile_for_order_item( $order_item_id );
414 if ( $is_booked ) {
415 $html .= OsOrdersHelper::booking_data_form_for_order_item_id( $order_item_id, $booking, LATEPOINT_ITEM_VARIANT_BUNDLE, ! $is_preselected );
416 }
417 $html .= '</div>';
418 $html .= '<div class="unscheduled-bundle-booking">
419 <div class="booking-item-status-pill"></div>
420 <div class="bib-label">' . __( 'Schedule now', 'latepoint' ) . '</div>
421 <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"/>
422 </div>';
423 $html .= '</div>';
424
425 return $html;
426 }
427
428 public static function generate_price_breakdown_from_params( array $price_breakdown_params ): array {
429 $price_breakdown_rows = [];
430 $allowed_keys = [ 'before_subtotal', 'after_subtotal' ];
431 foreach ( $allowed_keys as $key ) {
432 if ( ! empty( $price_breakdown_params[ $key ] ) ) {
433 foreach ( $price_breakdown_params[ $key ] as $row ) {
434 if ( ! empty( $row['items'] ) ) {
435 $group = [ 'heading' => '', 'items' => [], 'sub_items' => [] ];
436 if ( ! empty( $row['heading'] ) ) {
437 $group['heading'] = $row['heading'];
438 }
439 foreach ( $row['items'] as $item ) {
440 if ( isset( $item['value'] ) ) {
441 $item['raw_value'] = OsMoneyHelper::convert_amount_from_money_input_to_db_format( $item['value'] );
442 }
443 $group['items'][] = $item;
444 }
445 if ( ! empty( $row['sub_items'] ) ) {
446 foreach ( $row['sub_items'] as $sub_item ) {
447 if ( isset( $sub_item['value'] ) ) {
448 $sub_item['raw_value'] = OsMoneyHelper::convert_amount_from_money_input_to_db_format( $sub_item['value'] );
449 }
450 $group['sub_items'][] = $sub_item;
451 }
452 }
453 $price_breakdown_rows[ $key ][] = $group;
454 } else {
455 if ( isset( $row['value'] ) ) {
456 $row['raw_value'] = OsMoneyHelper::convert_amount_from_money_input_to_db_format( $row['value'] );
457 }
458 $price_breakdown_rows[ $key ][] = $row;
459 }
460 }
461 }
462 }
463
464 return $price_breakdown_rows;
465 }
466
467 public static function create_order_item_object( array $order_items_param ): OsOrderItemModel {
468 $order_item_model = new OsOrderItemModel();
469 $order_item_model->variant = $order_items_param['variant'];
470 if ( $order_item_model->is_bundle() ) {
471 $order_item_model->item_data = base64_decode( $order_items_param['item_data'] );
472 } else {
473 // it's a booking variant - so there is only one booking in the array, but still loop for ease of use
474 foreach ( $order_items_param['bookings'] as $booking_params ) {
475 $booking = OsOrdersHelper::create_booking_object_from_booking_data_form( $booking_params );
476 $order_item_model->item_data = $booking->generate_item_data();
477 }
478 }
479
480 return $order_item_model;
481 }
482
483 public static function generate_transactions_breakdown_html( OsOrderModel $order ): string {
484 $html = '';
485 $transactions = $order->get_transactions();
486 if ( $transactions ) {
487 $html .= '<div style="margin: 20px 0;">';
488 foreach ( $transactions as $transaction ) {
489 $html .= '<div style="margin-bottom: 10px;">';
490 $html .= '<div>' . esc_html__( 'Payment Portion: ', 'latepoint' ) . '<strong>' . $transaction->get_payment_portion_nice_name() . '</strong></div>';
491 $html .= '<div>' . esc_html__( 'Payment Amount: ', 'latepoint' ) . '<strong>' . OsMoneyHelper::format_price( $transaction->amount, true, false ) . '</strong></div>';
492 $html .= '</div>';
493 }
494 $html .= '</div>';
495 } else {
496 $html .= esc_html__( 'No transactions found.', 'latepoint' );
497 }
498
499 return $html;
500 }
501
502 public static function generate_summary_breakdown_html( OsOrderModel $order ): string {
503 ob_start();
504 OsPriceBreakdownHelper::output_price_breakdown( $order->generate_price_breakdown_rows(), true );
505 $html = ob_get_clean();
506
507 return $html;
508 }
509
510 public static function generate_order_items_html( OsOrderModel $order ) {
511 $html = '';
512 $order_items = $order->get_items();
513 $html .= '<table style="width: 100%;">';
514 $total_items = count( $order_items );
515 $i = 0;
516 foreach ( $order_items as $order_item ) {
517 $i ++;
518 $html .= '<tr>';
519 $data_style = ( $i < $total_items ) ? 'border-bottom: 1px solid #eee;' : '';
520 $html .= '<td style="' . $data_style . ' padding: 10px 0;">';
521 if ( $order_item->is_bundle() ) {
522 $bundle = $order_item->build_original_object_from_item_data();
523 $html .= '<div>' . $bundle->name . '</div>';
524 $bundle_services = $bundle->get_services();
525 foreach ( $bundle_services as $service ) {
526 $qty = $service->join_attributes['quantity'];
527 $qty_html = $qty > 1 ? ' [' . $qty . ']' : '';
528 $html .= '<div style="color: #999; font-size: 14px;">' . esc_html( $service->name . $qty_html ) . '</div>';
529 }
530 } else {
531 $booking = $order_item->build_original_object_from_item_data();
532 $html .= '<div style="font-weight: bold;">' . $booking->service->name . '</div>';
533 $html .= '<div>' . $booking->get_nice_start_datetime() . '</div>';
534 $html .= '<div>' . $booking->agent->get_full_name() . '</div>';
535 }
536 $html .= '</td>';
537 $html .= '</tr>';
538 }
539 $html .= '</table>';
540
541 return $html;
542 }
543
544 public static function extract_agent_emails( OsOrderModel $order ): string {
545 $to_emails = [];
546 $order_items = $order->get_items();
547 foreach ( $order_items as $order_item ) {
548 if ( $order_item->is_bundle() ) {
549 $bundle_bookings = OsOrdersHelper::get_bookings_for_order_item( $order_item->id );
550 foreach ( $bundle_bookings as $booking ) {
551 $to_emails[] = $booking->agent->get_full_name() . ' <' . $booking->agent->email . '>';
552 }
553 } else {
554 // booking
555 $booking = $order_item->build_original_object_from_item_data();
556 $to_emails[] = $booking->agent->get_full_name() . ' <' . $booking->agent->email . '>';
557 }
558 }
559 $to_emails = array_unique( $to_emails );
560
561 return implode( ', ', $to_emails );
562 }
563
564 public static function extract_agent_full_names( OsOrderModel $order ): string {
565 $full_names = [];
566 $order_items = $order->get_items();
567 foreach ( $order_items as $order_item ) {
568 if ( $order_item->is_bundle() ) {
569 $bundle_bookings = OsOrdersHelper::get_bookings_for_order_item( $order_item->id );
570 foreach ( $bundle_bookings as $booking ) {
571 $full_names[] = $booking->agent->get_full_name();
572 }
573 } else {
574 // booking
575 $booking = $order_item->build_original_object_from_item_data();
576 $full_names[] = $booking->agent->get_full_name();
577 }
578 }
579 $full_names = array_unique( $full_names );
580
581 return implode( ', ', $full_names );
582 }
583
584
585
586 /**
587 * Extract property by name from order. For example, location_id, agent_id...
588 * @param OsOrderModel $order
589 * @param string $property
590 *
591 * @return string
592 */
593 public static function extract_property_by_name( OsOrderModel $order, string $property ): string {
594 $property_values = [];
595 $order_items = $order->get_items();
596
597 $property_map = [
598 'service_ids' => 'service_id',
599 'location_ids' => 'location_id',
600 'agent_ids' => 'agent_id',
601 'bundle_ids' => 'bundle_id'
602 ];
603
604 if(empty($property_map[$property])){
605 return '';
606 }
607 $mapped_property = $property_map[$property];
608
609 foreach ( $order_items as $order_item ) {
610 if ( $order_item->is_bundle() ) {
611 if ($mapped_property == 'bundle_id') {
612 $property_values[] = $order_item->get_item_data_value_by_key( 'bundle_id' );
613 } else {
614 $bundle_bookings = OsOrdersHelper::get_bookings_for_order_item( $order_item->id );
615 $property_values = array_merge( $property_values, array_column( $bundle_bookings, $mapped_property ) );
616 }
617 } else {
618 $booking = $order_item->build_original_object_from_item_data();
619 if ( ! empty( $booking->$mapped_property ) ) {
620 $property_values[] = $booking->$mapped_property;
621 }
622 }
623 }
624
625 return implode( ',', array_unique( $property_values ) );
626 }
627
628 public static function check_if_order_invoices_paid_full_balance( $order_id ) {
629 $order = new OsOrderModel( $order_id );
630 $invoices = new OsInvoiceModel();
631 $paid_invoices = $invoices->where(['status' => LATEPOINT_INVOICE_STATUS_PAID, 'order_id' => $order_id])->get_results_as_models();
632 $total_paid = 0;
633 $updated = false;
634 foreach ( $paid_invoices as $invoice ) {
635 $total_paid += $invoice->charge_amount;
636 }
637 if($total_paid > 0){
638 $old_order = clone $order;
639 if($total_paid < $order->get_total()){
640 if($order->payment_status != LATEPOINT_ORDER_PAYMENT_STATUS_PARTIALLY_PAID){
641 $updated = $order->update_attributes(['payment_status' => LATEPOINT_ORDER_PAYMENT_STATUS_PARTIALLY_PAID]);
642 }
643 }else{
644 if($order->get_total() > 0 && $order->payment_status != LATEPOINT_ORDER_PAYMENT_STATUS_FULLY_PAID){
645 $updated = $order->update_attributes(['payment_status' => LATEPOINT_ORDER_PAYMENT_STATUS_FULLY_PAID]);
646 }
647 }
648 if($updated){
649 /**
650 * Order was updated
651 *
652 * @param {OsOrderModel} $order instance of order model after it was updated
653 * @param {OsOrderModel} $old_order instance of order model before it was updated
654 *
655 * @since 5.0.0
656 * @hook latepoint_order_updated
657 *
658 */
659 do_action( 'latepoint_order_updated', $order, $old_order );
660 }
661 }
662 }
663
664 }
665