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 |