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 / controllers / orders_controller.php
latepoint / lib / controllers Last commit date
activities_controller.php 3 months ago auth_controller.php 3 months ago booking_form_settings_controller.php 3 months ago bookings_controller.php 3 months ago calendars_controller.php 3 months ago carts_controller.php 3 months ago controller.php 3 months ago customer_cabinet_controller.php 3 months ago customers_controller.php 3 months ago dashboard_controller.php 3 months ago default_agent_controller.php 3 months ago events_controller.php 3 months ago form_fields_controller.php 3 months ago integrations_controller.php 3 months ago invoices_controller.php 3 months ago manage_booking_by_key_controller.php 3 months ago manage_order_by_key_controller.php 3 months ago notifications_controller.php 3 months ago orders_controller.php 3 months ago pro_controller.php 3 months ago process_jobs_controller.php 3 months ago processes_controller.php 3 months ago search_controller.php 3 months ago services_controller.php 3 months ago settings_controller.php 3 months ago steps_controller.php 3 months ago stripe_connect_controller.php 3 months ago support_topics_controller.php 3 months ago todos_controller.php 3 months ago transactions_controller.php 3 months ago wizard_controller.php 3 months ago
orders_controller.php
848 lines
1 <?php
2 /*
3 * Copyright (c) 2024 LatePoint LLC. All rights reserved.
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit; // Exit if accessed directly.
8 }
9
10
11 if ( ! class_exists( 'OsOrdersController' ) ) :
12
13
14 class OsOrdersController extends OsController {
15
16 function __construct() {
17 parent::__construct();
18
19 $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'orders/';
20 $this->vars['page_header'] = OsMenuHelper::get_menu_items_by_id( 'orders' );
21 $this->vars['breadcrumbs'][] = array(
22 'label' => __( 'Orders', 'latepoint' ),
23 'link' => OsRouterHelper::build_link( OsRouterHelper::build_route_name( 'orders', 'index' ) ),
24 );
25
26 $this->action_access['public'] = array_merge( $this->action_access['public'], [ 'continue_order_intent', 'continue_transaction_intent' ] );
27 }
28
29
30 public function view_order_log() {
31 $activities = new OsActivityModel();
32 $activities = $activities->where( [ 'order_id' => absint( $this->params['order_id'] ) ] )->order_by( 'id desc' )->get_results_as_models();
33
34 $order = new OsOrderModel( $this->params['order_id'] );
35
36 $this->vars['order'] = $order;
37 $this->vars['activities'] = $activities;
38
39 $this->format_render( __FUNCTION__ );
40 }
41
42
43 public function continue_order_intent() {
44 $order_intent_key = $this->params['order_intent_key'];
45 $order_intent = OsOrderIntentHelper::get_order_intent_by_intent_key( $order_intent_key );
46
47 if ( $order_intent->is_new_record() ) {
48 http_response_code( 400 );
49 OsDebugHelper::log( 'Order intent not found, id:' . $order_intent_key );
50 exit();
51 } else {
52
53 $order_intent->convert_to_order();
54
55 if ( $order_intent ) {
56 nocache_headers();
57 wp_redirect( $order_intent->get_page_url_with_intent(), 302 );
58 }
59 }
60 }
61
62
63 public function continue_transaction_intent() {
64 $intent_key = $this->params['transaction_intent_key'];
65 $transaction_intent = OsTransactionIntentHelper::get_transaction_intent_by_intent_key( $intent_key );
66
67 if ( $transaction_intent->is_new_record() ) {
68 http_response_code( 400 );
69 OsDebugHelper::log( 'Transaction intent not found, id:' . $intent_key );
70 exit();
71 } else {
72 $transaction_intent->convert_to_transaction();
73
74 if ( $transaction_intent ) {
75 nocache_headers();
76 wp_redirect( $transaction_intent->get_page_url_with_intent(), 302 );
77 }
78 }
79 }
80
81 /*
82 Update order (used in admin on quick side form save)
83 */
84
85 public function update() {
86 $this->create_or_update();
87 }
88
89
90 /*
91 Create order (used in admin on quick side form save)
92 */
93
94 public function create() {
95 $this->create_or_update();
96 }
97
98
99 // Create/Update order from quick form in admin
100 public function create_or_update() {
101 $validation_errors = [];
102
103 if ( ! empty( $this->params['order']['id'] ) ) {
104 $this->check_nonce( 'edit_order_' . $this->params['order']['id'] );
105 } else {
106 $this->check_nonce( 'new_order' );
107 }
108
109 $order_params = $this->params['order'];
110 $customer_params = $this->params['customer'];
111
112
113 $order_items_params = $this->params['order_items'] ?? [];
114
115
116 $order = new OsOrderModel( $order_params['id'] );
117
118 // if we are updating a order - save a copy by cloning old order
119 $old_order = ( $order->is_new_record() ) ? [] : clone $order;
120 $order->set_data( $order_params );
121
122
123 // first validate & create a customer the customer
124 if ( $order->customer_id ) {
125 $customer = new OsCustomerModel( $order->customer_id );
126 $old_customer_data = $customer->get_data_vars();
127 $is_new_customer = false;
128 } else {
129 $customer = new OsCustomerModel();
130 $is_new_customer = true;
131 }
132 // Security fix: Prevent mass assignment of wordpress_user_id by non-admin users.
133 // Use admin scope if user is authenticated as admin, otherwise restrict to public fields.
134 $customer->set_data( $customer_params, OsAuthHelper::is_admin_logged_in() ? LATEPOINT_PARAMS_SCOPE_ADMIN : LATEPOINT_PARAMS_SCOPE_PUBLIC );
135 if ( $customer->save() ) {
136 if ( $is_new_customer ) {
137 do_action( 'latepoint_customer_created', $customer );
138 $this->fields_to_update['order[customer_id]'] = $customer->id;
139 } else {
140 do_action( 'latepoint_customer_updated', $customer, $old_customer_data );
141 }
142
143 $order->customer_id = $customer->id;
144 } else {
145 $this->send_json(
146 [
147 'status' => LATEPOINT_STATUS_ERROR,
148 // translators: %s is the description of an error
149 'message' => sprintf( __( 'Error: %s', 'latepoint' ), implode( ', ', $customer->get_error_messages() ) ),
150 ]
151 );
152 }
153
154 // validate order items
155 foreach ( $order_items_params as $order_item_id => $order_item ) {
156 foreach ( $order_item['bookings'] as $booking_id => $booking_params ) {
157 $booking = OsOrdersHelper::create_booking_object_from_booking_data_form( $booking_params );
158 $booking->customer_id = $order->customer_id;
159 if ( ! $booking->validate( false, [ 'order_item_id' ] ) ) {
160 $validation_errors = array_merge( $validation_errors, $booking->get_error_messages() );
161 }
162 }
163 }
164
165 // check if there are errors saving bookings
166 if ( $validation_errors ) {
167 // translators: %s is the description of an error
168 $this->send_json(
169 array(
170 'status' => LATEPOINT_STATUS_ERROR,
171 'message' => sprintf( __( 'Error: %s', 'latepoint' ), implode( ', ', $validation_errors ) ),
172 )
173 );
174 }
175
176 if ( $old_order ) {
177 // make sure old order items are still there, if not - remove them
178 $order_items = $order->get_items();
179 foreach ( $order_items as $order_item ) {
180 if ( ! isset( $order_items_params[ $order_item->id ] ) ) {
181 $order_item_id_to_delete = $order_item->id;
182 /**
183 * Fires right before an order item is about to be deleted
184 *
185 * @param {integer} $order_item_id ID of the Order Item which will be deleted
186 *
187 * @since 5.0.0
188 * @hook latepoint_order_item_will_be_deleted
189 *
190 */
191 do_action( 'latepoint_order_item_will_be_deleted', $order_item_id_to_delete );
192
193 $order_item->delete();
194
195 /**
196 * Fires right after an order item has been deleted
197 *
198 * @param {integer} $order_item_id ID of the Order Item that was deleted
199 *
200 * @since 5.0.0
201 * @hook latepoint_order_item_deleted
202 *
203 */
204 do_action( 'latepoint_order_item_deleted', $order_item_id_to_delete );
205 OsActivitiesHelper::log_order_item_deleted( $order_item );
206 } else {
207 // it's a bundle order item - search for bookings that are attached to this bundle and remove them if not found in passed params list
208 if ( $order_item->is_bundle() ) {
209 $old_bundle_bookings = OsOrdersHelper::get_bookings_for_order_item( $order_item->id );
210 if ( $old_bundle_bookings ) {
211 foreach ( $old_bundle_bookings as $old_bundle_booking ) {
212 if ( empty( $order_items_params[ $order_item->id ]['bookings'][ $old_bundle_booking->id ] ) ) {
213
214 if ( OsRolesHelper::can_user_make_action_on_model_record( $old_bundle_booking, 'delete' ) ) {
215 $booking_id_to_delete = $old_bundle_booking->id;
216
217 /**
218 * Fires right before a booking is about to be deleted
219 *
220 * @param {integer} $booking_id ID of the booking that will be deleted
221 *
222 * @since 5.0.0
223 * @hook latepoint_booking_will_be_deleted
224 *
225 */
226 do_action( 'latepoint_booking_will_be_deleted', $booking_id_to_delete );
227
228 $old_bundle_booking->delete();
229 /**
230 * Fires right after a booking has been deleted
231 *
232 * @param {integer} $booking_id ID of the booking that was deleted
233 *
234 * @since 5.0.0
235 * @hook latepoint_booking_deleted
236 *
237 */
238 do_action( 'latepoint_booking_deleted', $booking_id_to_delete );
239 OsActivitiesHelper::log_booking_deleted( $old_bundle_booking );
240 } else {
241 OsDebugHelper::log( 'Not allowed: Deleting Booking', 'permissions_error' );
242 }
243 }
244 }
245 }
246 }
247 }
248 }
249 }
250
251 // Because price is not in allowed_params to bulk set, check if it's passed in params and set it, OTHERWISE CALCULATE IT
252 if ( isset( $order_params['total'] ) ) {
253 $order->total = OsParamsHelper::sanitize_param( $order_params['total'], 'money' );
254 }
255 if ( isset( $order_params['subtotal'] ) ) {
256 $order->subtotal = OsParamsHelper::sanitize_param( $order_params['subtotal'], 'money' );
257 }
258
259 // save price breakdown, we only need to save before and after subtotal, as total and subtotal values are stored on the Order record itself
260 if ( ! empty( $this->params['price_breakdown'] ) ) {
261 $order->price_breakdown = wp_json_encode( OsOrdersHelper::generate_price_breakdown_from_params( $this->params['price_breakdown'] ) );
262 }
263
264 // Check if we have to create a payment request
265 $create_payment_request = ( sanitize_text_field( $this->params['create_payment_request'] ?? '' ) == LATEPOINT_VALUE_ON );
266 if ( $create_payment_request ) {
267 $payment_request_data = $this->params['payment_request'];
268 $payment_request_data['portion'] = sanitize_text_field( $payment_request_data['portion'] );
269 $payment_request_data['charge_amount'] = sanitize_text_field( $payment_request_data[ 'charge_amount_' . $payment_request_data['portion'] ] );
270 $payment_request_data['due_at'] = OsTimeHelper::custom_datetime_utc_in_db_format( sanitize_text_field( $payment_request_data['due_at'] ) . ' 23:59:59' );
271 $order->set_initial_payment_data_value( 'time', LATEPOINT_PAYMENT_TIME_NOW, false );
272 $order->set_initial_payment_data_value( 'portion', $payment_request_data['portion'], false );
273 $order->set_initial_payment_data_value( 'charge_amount', OsMoneyHelper::convert_amount_from_money_input_to_db_format( $payment_request_data['charge_amount'] ) );
274
275 $payment_request = new OsPaymentRequestModel();
276
277 $payment_request = $payment_request->set_data( $payment_request_data );
278
279 } else {
280 $order->set_initial_payment_data_value( 'time', LATEPOINT_PAYMENT_TIME_LATER );
281 $payment_request = null;
282 }
283
284 if ( $order->save() ) {
285
286 // save transactions
287 if ( ! empty( $this->params['transactions'] ) ) {
288 foreach ( $this->params['transactions'] as $transaction_params ) {
289 if ( ! empty( $transaction_params['id'] ) && filter_var( $transaction_params['id'], FILTER_VALIDATE_INT ) ) {
290 // update existing transaction
291 $transaction = new OsTransactionModel( $transaction_params['id'] );
292 $is_new_transaction = false;
293 } else {
294 // new transaction
295 $transaction = new OsTransactionModel();
296 $is_new_transaction = true;
297 }
298 unset( $transaction_params['id'] );
299 $transaction_params['invoice_id'] = filter_var( $transaction_params['invoice_id'], FILTER_VALIDATE_INT ) ? $transaction_params['invoice_id'] : null;
300 $transaction->set_data( $transaction_params );
301 $transaction->order_id = $order->id;
302 $transaction->customer_id = $customer->id;
303 $transaction->status = LATEPOINT_TRANSACTION_STATUS_SUCCEEDED;
304 $transaction->save();
305 if ( $is_new_transaction ) {
306 /**
307 * Transaction for order was created
308 *
309 * @param {OsTransactionModel} $transaction instance of transaction model that was created
310 *
311 * @since 5.0.0
312 * @hook latepoint_transaction_created
313 *
314 */
315 do_action( 'latepoint_transaction_created', $transaction );
316 }
317 }
318 }
319 foreach ( $order_items_params as $order_item_id => $order_item ) {
320 $order_item_model = new OsOrderItemModel();
321 $order_item_model->variant = $order_item['variant'];
322 if ( strpos( $order_item_id, 'new_' ) === false ) {
323 $order_item_model->load_by_id( $order_item_id );
324 }
325 if ( $order_item_model->is_bundle() ) {
326 $order_item_model->item_data = base64_decode( $order_item['item_data'] );
327 $order_item_model->recalculate_prices();
328 }
329
330
331 $order_item_model->order_id = $order->id;
332 if ( $order_item_model->save() ) {
333 foreach ( $order_item['bookings'] as $booking_id => $booking_params ) {
334 $booking = new OsBookingModel();
335 $old_booking = false;
336 if ( strpos( $order_item_id, 'new_' ) === false ) {
337 $booking->load_by_id( $booking_id );
338 if ( ! $booking->is_new_record() ) {
339 $old_booking = clone $booking;
340 }
341 }
342
343 $booking = OsOrdersHelper::create_booking_object_from_booking_data_form( $booking_params );
344 $booking->customer_id = $order->customer_id;
345 $booking->order_item_id = $order_item_model->id;
346 $booking->form_id = $booking_id;
347 if ( $booking->save() ) {
348 if ( $order_item_model->is_booking() ) {
349 $order_item_model->item_data = $booking->generate_item_data();
350 $order_item_model->recalculate_prices();
351 $order_item_model->save();
352 }
353 if ( $old_booking ) {
354 do_action( 'latepoint_booking_updated', $booking, $old_booking );
355 if ( $old_booking->status != $booking->status ) {
356 do_action( 'latepoint_booking_change_status', $booking, $old_booking );
357 OsActivitiesHelper::log_booking_change_status( $booking, $old_booking );
358 }
359 } else {
360 do_action( 'latepoint_booking_created', $booking );
361 }
362 } else {
363 OsDebugHelper::log( 'Error saving booking (admin)', 'booking_save_error', $booking->get_error_messages() );
364 }
365 }
366 } else {
367 OsDebugHelper::log( 'Error saving order item (admin)', 'order_item_save_error', $order_item_model->get_error_messages() );
368 }
369 }
370
371
372
373 if ( $old_order ) {
374 /**
375 * Order was updated
376 *
377 * @param {OsOrderModel} $order instance of order model after it was updated
378 * @param {OsOrderModel} $old_order instance of order model before it was updated
379 *
380 * @since 5.0.0
381 * @hook latepoint_order_updated
382 *
383 */
384 do_action( 'latepoint_order_updated', $order, $old_order );
385 } else {
386 OsInvoicesHelper::create_invoices_for_new_order( $order, $payment_request );
387 /**
388 * Order was created
389 *
390 * @param {OsOrderModel} $order instance of order model that was created
391 *
392 * @since 5.0.0
393 * @hook latepoint_order_created
394 *
395 */
396 do_action( 'latepoint_order_created', $order );
397 }
398
399 $status = LATEPOINT_STATUS_SUCCESS;
400 // translators: %s is a link to the updated order
401 $response_html = sprintf( ( ( $old_order ) ? __( 'Order Updated ID: %s', 'latepoint' ) : __( 'Order Created ID: %s', 'latepoint' ) ), '<span class="os-notification-link" ' . OsOrdersHelper::quick_order_btn_html( $order->id ) . '>' . $order->id . '</span>' );
402 } else {
403 OsDebugHelper::log( 'Error saving order (admin)', 'order_save_error', $order->get_error_messages() );
404 $status = LATEPOINT_STATUS_ERROR;
405
406 // translators: %s is an error message
407 $response_html = sprintf( __( 'Error: %s', 'latepoint' ), implode( ', ', $order->get_error_messages() ) );
408 }
409
410 if ( $this->get_return_format() == 'json' ) {
411 $this->send_json(
412 array(
413 'status' => $status,
414 'message' => $response_html,
415 )
416 );
417 }
418 }
419
420
421 // reloads a section of a quick edit form that has a price breakdown
422 public function reload_price_breakdown() {
423 $order = new OsOrderModel();
424 $order->set_data( $this->params['order'] );
425
426 $order_items_params = $this->params['order_items'] ?? [];
427 foreach ( $order_items_params as $order_items_param ) {
428 $order->items[] = OsOrdersHelper::create_order_item_object( $order_items_param );
429 }
430
431 $order->subtotal = $order->recalculate_subtotal();
432 $order->total = $order->recalculate_total();
433
434 /**
435 * Reloads price breakdown rows
436 *
437 * @since 5.0.0
438 * @hook latepoint_register_role
439 *
440 * @param {OsOrderModel} $order Order for which to reload price breakdown
441 * @returns {OsOrderModel} Filtered order with updated price breakdown rows
442 */
443 $order = apply_filters( 'latepoint_order_reload_price_breakdown', $order );
444
445 // we don't need to generate balance and payments info as it is printed in a separate block
446 $this->vars['price_breakdown_rows'] = $order->generate_price_breakdown_rows( [ 'balance', 'payments' ], true );
447
448 $this->vars['order'] = $order;
449 $this->format_render( __FUNCTION__ );
450 }
451
452 function reload_balance_and_payments() {
453 $order_params = $this->params['order'];
454 $order_items_params = $this->params['order_items'] ?? [];
455
456 $order = new OsOrderModel();
457 $order->set_data( $order_params );
458
459 foreach ( $order_items_params as $order_items_param ) {
460 $order->items[] = OsOrdersHelper::create_order_item_object( $order_items_param );
461 }
462
463
464
465 // Because price is not in allowed_params to bulk set, check if it's passed in params and set it, OTHERWISE CALCULATE IT
466 if ( isset( $order_params['total'] ) ) {
467 $order->total = OsParamsHelper::sanitize_param( $order_params['total'], 'money' );
468 }
469 if ( isset( $order_params['subtotal'] ) ) {
470 $order->subtotal = OsParamsHelper::sanitize_param( $order_params['subtotal'], 'money' );
471 }
472
473
474 $this->vars['order'] = $order;
475 $this->format_render( __FUNCTION__ );
476 }
477
478 function generate_bundle_order_item_block() {
479 $bundle = new OsBundleModel( $this->params['bundle_id'] );
480
481 $order_item_id = OsUtilHelper::generate_form_id();
482 $response_html = '<div class="order-item order-item-variant-bundle" data-order-item-id="' . $order_item_id . '">';
483 $response_html .= OsOrdersHelper::generate_order_item_pill_for_bundle( $bundle, $order_item_id );
484 $response_html .= '</div>';
485
486 if ( $this->get_return_format() == 'json' ) {
487 $this->send_json(
488 array(
489 'status' => LATEPOINT_STATUS_SUCCESS,
490 'message' => $response_html,
491 )
492 );
493 }
494 }
495
496 function generate_booking_order_item_block() {
497
498 if ( ! empty( $this->params['order_item_variant'] ) && ( $this->params['order_item_variant'] == LATEPOINT_ITEM_VARIANT_BUNDLE ) ) {
499 // booking for bundle, we don't need to wrap in order-item block because order item is a bundle
500 $booking = OsBookingHelper::build_booking_model_from_item_data( json_decode( base64_decode( $this->params['booking_item_data'] ), true ) );
501 $response_html = OsOrdersHelper::booking_data_form_for_order_item_id( $this->params['order_item_id'], $booking, LATEPOINT_ITEM_VARIANT_BUNDLE, false );
502 } else {
503 // regular booking
504 $booking = OsBookingHelper::prepare_new_from_params( $this->params );
505 $order_item_id = OsUtilHelper::generate_form_id();
506 $response_html = '<div class="order-item order-item-variant-booking" data-order-item-id="' . $order_item_id . '">';
507 $response_html .= OsOrdersHelper::booking_data_form_for_order_item_id( $order_item_id, $booking, LATEPOINT_ITEM_VARIANT_BOOKING, false );
508 $response_html .= '</div>';
509 }
510
511
512 if ( $this->get_return_format() == 'json' ) {
513 $this->send_json(
514 array(
515 'status' => LATEPOINT_STATUS_SUCCESS,
516 'message' => $response_html,
517 )
518 );
519 }
520 }
521
522 function fold_booking_data_form() {
523 // input fields are formatted in customer preferred format, we need to convert that to database format Y-m-d
524 $order_item_id = $this->params['order_item_id'];
525 $booking_id = $this->params['booking_id'];
526
527 $booking_params = $this->params['order_items'][ $order_item_id ]['bookings'][ $booking_id ];
528 $booking = OsOrdersHelper::create_booking_object_from_booking_data_form( $booking_params );
529
530 if ( $this->params['order_items'][ $order_item_id ]['variant'] == LATEPOINT_ITEM_VARIANT_BUNDLE ) {
531 $response_html = OsOrdersHelper::generate_order_item_pill_for_bundle_booking( $booking, $order_item_id );
532 } else {
533 $response_html = OsOrdersHelper::generate_order_item_pill_for_booking( $booking, $order_item_id );
534 }
535 $this->send_json(
536 array(
537 'status' => LATEPOINT_STATUS_SUCCESS,
538 'message' => $response_html,
539 )
540 );
541 }
542
543 function generate_order_item_booking_data_form() {
544 $order_item = new OsOrderItemModel();
545 $order_item->variant = $this->params['order_item_variant'] ?? LATEPOINT_ITEM_VARIANT_BOOKING;
546 if ( ! empty( $this->params['order_item_id'] ) ) {
547 // existing order item
548 if ( strpos( $this->params['order_item_id'], 'new_' ) !== false ) {
549 $order_item->form_id = $this->params['order_item_id'];
550 } else {
551 $order_item->id = $this->params['order_item_id'];
552 }
553 $order_item->item_data = ! empty( $this->params['order_item_item_data'] ) ? base64_decode( $this->params['order_item_item_data'] ) : '';
554 if ( $order_item->is_bundle() ) {
555 $booking_item_data = ! empty( $this->params['booking_item_data'] ) ? base64_decode( $this->params['booking_item_data'] ) : '';
556 $booking = OsBookingHelper::build_booking_model_from_item_data( json_decode( $booking_item_data, true ) );
557 } else {
558 $booking = $order_item->build_original_object_from_item_data();
559 }
560 } else {
561 // new order item
562 $booking = OsBookingHelper::prepare_new_from_params( $this->params );
563 }
564
565 $response_html = OsOrdersHelper::booking_data_form_for_order_item_id( $order_item->get_form_id(), $booking, $order_item->variant );
566 $this->send_json(
567 array(
568 'status' => LATEPOINT_STATUS_SUCCESS,
569 'message' => $response_html,
570 )
571 );
572 }
573
574 function quick_edit() {
575
576 $customers = new OsCustomerModel();
577 // only load customers that belong to logged in agent, if any
578 if ( ! OsRolesHelper::are_all_records_allowed( 'agent' ) ) {
579 $customers->select( LATEPOINT_TABLE_CUSTOMERS . '.*' )->join( LATEPOINT_TABLE_BOOKINGS, [ 'customer_id' => LATEPOINT_TABLE_CUSTOMERS . '.id' ] )->group_by( LATEPOINT_TABLE_CUSTOMERS . '.id' )->where( [ LATEPOINT_TABLE_BOOKINGS . '.agent_id' => OsRolesHelper::get_allowed_records( 'agent' ) ] );
580 }
581
582
583 $customers_arr = $customers->order_by( 'first_name asc, last_name asc' )->set_limit( 20 )->get_results_as_models();
584 $this->vars['customers'] = $customers_arr;
585
586 // CREATING NEW ORDER
587 $order = new OsOrderModel();
588 $order_id = $this->params['id'] ?? false;
589
590 if ( ! empty( $this->params['booking_id'] ) ) {
591 $preselected_booking = new OsBookingModel( $this->params['booking_id'] );
592 $preselected_order_item = new OsOrderItemModel( $preselected_booking->order_item_id );
593 $order_id = $preselected_order_item->order_id;
594 } else {
595 $preselected_booking = false;
596 $preselected_order_item = false;
597 }
598
599 if ( $order_id ) {
600 // EDITING EXISTING ORDER
601 $order = new OsOrderModel( $order_id );
602 // TODO add this check for order
603 // if(!OsRolesHelper::can_user_make_action_on_model_record($order, 'view')){
604 // $this->send_json(array('status' => LATEPOINT_STATUS_ERROR, 'message' => 'Not Allowed'));
605 // }
606
607 $transactions = $order->get_transactions();
608
609 } else {
610 // NEW ORDER
611
612 // LOAD FROM PASSED PARAMS
613 $order->status = OsOrdersHelper::get_default_order_status();
614 $order->fulfillment_status = $order->get_default_fulfillment_status();
615
616 if ( ! empty( $this->params['customer_id'] ) ) {
617 $order->customer_id = $this->params['customer_id'];
618 }
619
620 $new_booking = OsBookingHelper::prepare_new_from_params( $this->params );
621 $new_booking = apply_filters( 'latepoint_prepare_booking_for_quick_view', $new_booking );
622 $order_item_model = new OsOrderItemModel();
623 $order_item_model->variant = LATEPOINT_ITEM_VARIANT_BOOKING;
624 $order_item_model->item_data = $new_booking->generate_item_data();
625
626 $order->items[] = $order_item_model;
627
628 $order->total = $order->recalculate_total();
629 $order->subtotal = $order->recalculate_subtotal();
630
631 $transactions = [];
632 }
633
634 $bundles = new OsBundleModel();
635 $bundles = $bundles->should_be_active()->get_results_as_models();
636 $this->vars['bundles'] = $bundles;
637
638
639 $this->vars['price_breakdown_rows'] = $order->generate_price_breakdown_rows();
640
641 $order = apply_filters( 'latepoint_prepare_order_for_quick_view', $order );
642
643 $order_bookings = $order->get_bookings_from_order_items();
644 $order_bundles = $order->get_bundles_from_order_items();
645
646
647 $this->vars['selected_customer'] = new OsCustomerModel( $order->customer_id );
648 $this->vars['order'] = $order;
649 $this->vars['preselected_booking'] = $preselected_booking;
650 $this->vars['preselected_order_item'] = $preselected_order_item;
651 $this->vars['show_only_preselected_items'] = $preselected_booking && $preselected_order_item && ( ( count( $order_bookings ) > 1 ) || ( count( $order_bundles ) ) || ( $order_bundles && $order_bookings ) );
652
653 $this->vars['order_bookings'] = $order_bookings;
654 $this->vars['order_bundles'] = $order_bundles;
655 $this->vars['transactions'] = $transactions;
656 $this->vars['default_fields_for_customer'] = OsSettingsHelper::get_default_fields_for_customer();
657 $this->format_render( __FUNCTION__ );
658 }
659
660 public function edit_form() {
661 $order = ( empty( $this->params['id'] ) ) ? new OsOrderModel() : new OsOrderModel( $this->params['id'] );
662 // legacy fix for older orders that didn't have portion column, get it from connected order
663 if ( ! $order->is_new_record() && empty( $order->payment_portion ) && ! empty( $order->booking_id ) ) {
664 $booking = new OsBookingModel( $order->booking_id );
665 if ( ! empty( $booking->id ) ) {
666 $order->payment_portion = $booking->payment_portion;
667 }
668 }
669 $this->vars['real_or_rand_id'] = ( $order->is_new_record() ) ? 'new_order_' . OsUtilHelper::random_text( 'alnum', 5 ) : $order->id;
670 $this->vars['order'] = $order;
671
672 $this->format_render( __FUNCTION__ );
673 }
674
675 public function destroy() {
676 if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
677 $this->check_nonce( 'destroy_order_' . $this->params['id'] );
678 $order = new OsOrderModel( $this->params['id'] );
679 if ( $order->delete() ) {
680 $status = LATEPOINT_STATUS_SUCCESS;
681 $response_html = __( 'Order Removed', 'latepoint' );
682 } else {
683 $status = LATEPOINT_STATUS_ERROR;
684 $response_html = __( 'Error Removing Order', 'latepoint' );
685 }
686 } else {
687 $status = LATEPOINT_STATUS_ERROR;
688 $response_html = __( 'Error Removing Order', 'latepoint' );
689 }
690 if ( $this->get_return_format() == 'json' ) {
691 $this->send_json(
692 array(
693 'status' => $status,
694 'message' => $response_html,
695 )
696 );
697 }
698 }
699
700 /*
701 Index of orders
702 */
703
704 public function index() {
705
706 $per_page = OsSettingsHelper::get_number_of_records_per_page();
707 $page_number = isset( $this->params['page_number'] ) ? $this->params['page_number'] : 1;
708
709 $this->vars['page_header'] = false;
710
711 $orders = new OsOrderModel();
712
713
714 // TABLE SEARCH FILTERS
715 $filter = $this->params['filter'] ?? false;
716 $query_args = [];
717 if ( $filter ) {
718 if ( ! empty( $filter['id'] ) ) {
719 $query_args['id'] = $filter['id'];
720 }
721 if ( ! empty( $filter['total'] ) ) {
722 $query_args['total'] = $filter['total'];
723 }
724 if ( ! empty( $filter['status'] ) ) {
725 $query_args['status'] = $filter['status'];
726 }
727 if ( ! empty( $filter['payment_status'] ) ) {
728 $query_args['payment_status'] = $filter['payment_status'];
729 }
730 if ( ! empty( $filter['fulfillment_status'] ) ) {
731 $query_args['fulfillment_status'] = $filter['fulfillment_status'];
732 }
733 if ( ! empty( $filter['confirmation_code'] ) ) {
734 $query_args['confirmation_code LIKE'] = '%' . $filter['confirmation_code'] . '%';
735 }
736
737 if ( ! empty( $filter['customer']['full_name'] ) ) {
738 $orders->select( LATEPOINT_TABLE_ORDERS . '.*, ' . LATEPOINT_TABLE_CUSTOMERS . '.first_name, ' . LATEPOINT_TABLE_CUSTOMERS . '.last_name' );
739 $orders->join( LATEPOINT_TABLE_CUSTOMERS, [ 'id' => LATEPOINT_TABLE_ORDERS . '.customer_id' ] );
740
741 $query_args[ 'concat_ws(" ", ' . LATEPOINT_TABLE_CUSTOMERS . '.first_name,' . LATEPOINT_TABLE_CUSTOMERS . '.last_name) LIKE' ] = '%' . $filter['customer']['full_name'] . '%';
742 $this->vars['customer_name_query'] = $filter['customer']['full_name'];
743
744 }
745
746 if ( ! empty( $filter['created_at_from'] ) && ! empty( $filter['created_at_to'] ) ) {
747 $query_args['created_at >='] = $filter['created_at_from'] . ' 00:00:00';
748 $query_args['created_at <='] = $filter['created_at_to'] . ' 23:59:59';
749 }
750 }
751
752
753 // OUTPUT CSV IF REQUESTED
754 if ( isset( $this->params['download'] ) && $this->params['download'] == 'csv' ) {
755 $csv_filename = 'payments_' . OsUtilHelper::random_text() . '.csv';
756
757 header( 'Content-Type: text/csv' );
758 header( "Content-Disposition: attachment; filename={$csv_filename}" );
759
760 $labels_row = [
761 __( 'ID', 'latepoint' ),
762 __( 'Token', 'latepoint' ),
763 __( 'Booking ID', 'latepoint' ),
764 __( 'Customer', 'latepoint' ),
765 __( 'Processor', 'latepoint' ),
766 __( 'Method', 'latepoint' ),
767 __( 'Amount', 'latepoint' ),
768 __( 'Status', 'latepoint' ),
769 __( 'Type', 'latepoint' ),
770 __( 'Date', 'latepoint' ),
771 ];
772
773
774 $orders_data = [];
775 $orders_data[] = $labels_row;
776
777
778 $orders_arr = $orders->where( $query_args )->filter_allowed_records()->get_results_as_models();
779
780 if ( $orders_arr ) {
781 foreach ( $orders_arr as $order ) {
782 $values_row = [
783 $order->id,
784 $order->token,
785 $order->booking_id,
786 ( $order->customer_id ? $order->customer->full_name : 'n/a' ),
787 $order->processor,
788 $order->payment_method,
789 OsMoneyHelper::format_price( $order->amount, true, false ),
790 $order->status,
791 $order->kind,
792 $order->created_at,
793 ];
794 $values_row = apply_filters( 'latepoint_order_row_for_csv_export', $values_row, $order, $this->params );
795 $orders_data[] = $values_row;
796 }
797 }
798
799 $orders_data = apply_filters( 'latepoint_orders_data_for_csv_export', $orders_data, $this->params );
800 OsCSVHelper::array_to_csv( $orders_data );
801
802 return;
803 }
804
805 if ( $query_args ) {
806 $orders->where( $query_args );
807 }
808 $orders->filter_allowed_records();
809
810
811 $count_orders = clone $orders;
812 $total_orders = $count_orders->count();
813
814 $orders = $orders->order_by( LATEPOINT_TABLE_ORDERS . '.created_at desc' )->set_limit( $per_page );
815 if ( $page_number > 1 ) {
816 $orders = $orders->set_offset( ( $page_number - 1 ) * $per_page );
817 }
818
819 $this->vars['orders'] = $orders->get_results_as_models();
820
821 $this->vars['total_orders'] = $total_orders;
822 $this->vars['current_page_number'] = $page_number;
823 $this->vars['per_page'] = $per_page;
824 $total_pages = ceil( $total_orders / $per_page );
825 $this->vars['total_pages'] = $total_pages;
826
827 $this->vars['showing_from'] = ( ( $page_number - 1 ) * $per_page ) ? ( ( $page_number - 1 ) * $per_page ) : 1;
828 $this->vars['showing_to'] = min( $page_number * $per_page, $total_orders );
829
830 $this->format_render(
831 [
832 'json_view_name' => '_table_body',
833 'html_view_name' => __FUNCTION__,
834 ],
835 [],
836 [
837 'total_pages' => $total_pages,
838 'showing_from' => $this->vars['showing_from'],
839 'showing_to' => $this->vars['showing_to'],
840 'total_records' => $total_orders,
841 ]
842 );
843 }
844 }
845
846
847 endif;
848