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