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 / invoices_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
invoices_controller.php
542 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( 'OsInvoicesController' ) ) :
12
13
14 class OsInvoicesController extends OsController {
15
16 function __construct() {
17 parent::__construct();
18
19 $this->action_access['public'] = array_merge( $this->action_access['public'], [ 'view_by_key', 'payment_form', 'summary_before_payment' ] );
20
21 $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'invoices/';
22 }
23
24
25 public function new_form() {
26 $order_id = absint( sanitize_text_field( $this->params['order_id'] ) );
27
28 if ( ! is_numeric( $order_id ) ) {
29 echo __( 'Invalid Order ID', 'latepoint' );
30
31 return;
32 }
33
34 $order = new OsOrderModel( $order_id );
35 if ( empty( $order ) || $order->is_new_record() ) {
36 echo __( 'Invalid Order ID', 'latepoint' );
37
38 return;
39 }
40
41 $invoice = new OsInvoiceModel();
42 $invoice->order_id = $order->id;
43 $invoice->payment_portion = LATEPOINT_PAYMENT_PORTION_CUSTOM;
44
45 $this->vars['invoice'] = $invoice;
46
47 $this->format_render( __FUNCTION__ );
48 }
49
50 private function get_invoice_params() {
51 $invoice_params = $this->params['invoice'];
52
53 // input date is in WP format (or in viewer's format), we need to make it "end of the day" and also convert to UTC timezone
54 $due_at_wp_time = sanitize_text_field( $invoice_params['due_at'] ) . ' 23:59:59';
55 $invoice_params['due_at'] = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $due_at_wp_time )->setTimezone( new DateTimeZone( 'UTC' ) )->format( LATEPOINT_DATETIME_DB_FORMAT );
56
57 $invoice_params['order_id'] = absint( sanitize_text_field( $this->params['invoice']['order_id'] ) );
58 $invoice_params['payment_portion'] = sanitize_text_field( $this->params['invoice']['payment_portion'] );
59 $invoice_params['charge_amount'] = OsParamsHelper::sanitize_param( sanitize_text_field( $this->params['invoice']['charge_amount'] ), 'money' );
60
61 $errors = [];
62 if ( ! in_array( $invoice_params['payment_portion'], array_keys( OsPaymentsHelper::get_payment_portions_list() ) ) ) {
63 $errors[] = __( 'Invalid payment portion', 'latepoint' );
64 }
65 if ( ! is_numeric( $invoice_params['order_id'] ) ) {
66 $errors[] = __( 'Invalid Order ID', 'latepoint' );
67 }
68 if ( ! empty( $errors ) ) {
69 return new WP_Error( 'invalid_params', implode( ', ', $errors ) );
70 }
71
72 return $invoice_params;
73 }
74
75 public function process_data_update() {
76
77 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
78 echo 'Invalid invoice';
79
80 return;
81 }
82 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
83 $old_invoice = clone $invoice;
84
85 if ( empty( $invoice ) || $invoice->is_new_record() ) {
86 echo 'Invalid invoice';
87
88 return;
89 }
90
91 // Verify nonce.
92 $this->check_nonce( 'update_invoice_' . $this->params['invoice_id'] );
93
94 $invoice->charge_amount = OsParamsHelper::sanitize_param( sanitize_text_field( $this->params['invoice']['charge_amount'] ), 'money' );
95 $due_at_wp_time = sanitize_text_field( $this->params['invoice']['due_at'] ) . ' 23:59:59';
96 $due_at_utc_time = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $due_at_wp_time )->setTimezone( new DateTimeZone( 'UTC' ) )->format( LATEPOINT_DATETIME_DB_FORMAT );
97 $invoice->due_at = $due_at_utc_time;
98 $invoice->status = sanitize_text_field( $this->params['invoice']['status'] );
99
100 if ( $invoice->save() ) {
101
102 /**
103 * Invoice was updated
104 *
105 * @param {OsInvoiceModel} $invoice instance of invoice model after it was updated
106 * @param {OsInvoiceModel} $old_invoice instance of invoice model before it was updated
107 *
108 * @since 5.1.0
109 * @hook latepoint_invoice_updated
110 *
111 */
112 do_action( 'latepoint_invoice_updated', $invoice, $old_invoice );
113 $status = LATEPOINT_STATUS_SUCCESS;
114 ob_start();
115 OsInvoicesHelper::invoice_document_html( $invoice, true );
116 $message = ob_get_clean();
117
118 } else {
119 $status = LATEPOINT_STATUS_ERROR;
120 $message = $invoice->get_error_messages();
121 }
122
123 $this->send_json(
124 [
125 'status' => $status,
126 'message' => $message,
127 ]
128 );
129 }
130
131 public function edit_data() {
132 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
133 echo __( 'Invalid Invoice ID', 'latepoint' );
134
135 return;
136 }
137 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
138 if ( empty( $invoice ) || $invoice->is_new_record() ) {
139 echo __( 'Invoice not found', 'latepoint' );
140
141 return;
142 }
143
144 $this->vars['invoice'] = $invoice;
145
146 $this->format_render( __FUNCTION__ );
147 }
148
149 public function reload_invoice_tile() {
150 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
151 echo 'Invalid invoice';
152
153 return;
154 }
155 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
156 if ( empty( $invoice ) || $invoice->is_new_record() ) {
157 echo 'Invalid invoice';
158
159 return;
160 }
161
162 $this->send_json(
163 [
164 'status' => LATEPOINT_STATUS_SUCCESS,
165 'message' => OsInvoicesHelper::generate_invoice_tile_on_order_edit_form( $invoice ),
166 ]
167 );
168 }
169
170 public function create() {
171 // Verify nonce.
172 $this->check_nonce( 'create_invoice' );
173
174 $invoice_params = $this->get_invoice_params();
175
176 if ( is_wp_error( $invoice_params ) ) {
177 $this->send_json(
178 [
179 'status' => LATEPOINT_STATUS_ERROR,
180 'message' => $invoice_params->get_error_message(),
181 ]
182 );
183
184 return;
185 }
186
187 $order = new OsOrderModel( $invoice_params['order_id'] );
188 if ( empty( $order ) || $order->is_new_record() ) {
189 echo __( 'Invalid Order ID', 'latepoint' );
190
191 return;
192 }
193
194 $invoice = new OsInvoiceModel();
195 $invoice->set_data( $invoice_params );
196
197 $invoice->data = json_encode( OsInvoicesHelper::generate_invoice_data_from_order( $order ) );
198 if ( $invoice->save() ) {
199 /**
200 * Invoice was created
201 *
202 * @param {OsInvoiceModel} $invoice instance of invoice model that was created
203 *
204 * @since 5.1.0
205 * @hook latepoint_invoice_created
206 *
207 */
208 do_action( 'latepoint_invoice_created', $invoice );
209 $response_html = OsInvoicesHelper::generate_invoice_tile_on_order_edit_form( $invoice );
210 $this->send_json(
211 [
212 'status' => LATEPOINT_STATUS_SUCCESS,
213 'message' => $response_html,
214 ]
215 );
216 } else {
217 $this->send_json(
218 [
219 'status' => LATEPOINT_STATUS_ERROR,
220 'message' => __( 'Error: ', 'latepoint' ) . $invoice->get_error_messages(),
221 ]
222 );
223 }
224 }
225
226 public function change_status() {
227 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
228 echo 'Invalid Invoice';
229
230 return;
231 }
232
233 // Condition for pro compatibility. Remove later.
234 if ( isset( $this->params['_wpnonce'] ) ) {
235 // Verify nonce.
236 $this->check_nonce( 'change_invoice_status_' . $this->params['invoice_id'] );
237 }
238
239 if ( ! in_array( $this->params['status'], array_keys( OsInvoicesHelper::list_of_statuses_for_select() ) ) ) {
240 echo 'Invalid Status';
241
242 return;
243 }
244
245 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
246 $invoice->change_status( $this->params['status'] );
247
248 $status = LATEPOINT_STATUS_SUCCESS;
249
250 ob_start();
251 OsInvoicesHelper::invoice_document_html( $invoice, true );
252 $response_html = ob_get_clean();
253
254 if ( $this->get_return_format() == 'json' ) {
255
256 $this->send_json(
257 [
258 'status' => $status,
259 'message' => $response_html,
260 ]
261 );
262 }
263 }
264
265 public function email_form() {
266 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
267 echo __( 'Invalid Invoice ID', 'latepoint' );
268
269 return;
270 }
271 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
272 if ( empty( $invoice ) || $invoice->is_new_record() ) {
273 echo __( 'Invoice not found', 'latepoint' );
274
275 return;
276 }
277 $errors = [];
278
279 $to = __( '{{customer_email}}', 'latepoint' );
280 $subject = OsInvoicesHelper::get_subject_for_invoice_email();
281 $content = OsInvoicesHelper::get_content_for_invoice_email();
282
283 if ( ! empty( $this->params['invoice_email'] ) ) {
284 // send email
285 $to = $this->params['invoice_email[to]'] ?? $to;
286 $order = new OsOrderModel( $invoice->order_id );
287 $customer = new OsCustomerModel( $order->customer_id );
288
289 $original_to = $to;
290 $to = OsReplacerHelper::replace_all_vars(
291 $to,
292 [
293 'order' => $order,
294 'customer' => $customer,
295 'invoice' => $invoice,
296 ]
297 );
298 $subject = OsReplacerHelper::replace_all_vars(
299 $subject,
300 [
301 'order' => $order,
302 'customer' => $customer,
303 'invoice' => $invoice,
304 ]
305 );
306 $content = OsReplacerHelper::replace_all_vars(
307 $content,
308 [
309 'order' => $order,
310 'customer' => $customer,
311 'invoice' => $invoice,
312 ]
313 );
314 if ( OsUtilHelper::is_valid_email( $to ) ) {
315 $mailer = new OsMailer();
316 wp_mail( $to, $subject, $content, $mailer->get_headers() );
317 // set back so it appears correctly on the front
318 $to = $original_to;
319 $this->vars['success'] = __( 'Invoice email sent', 'latepoint' );
320 } else {
321 $errors[] = __( 'Please enter a valid email address.', 'latepoint' );
322 }
323 }
324
325 $this->vars['errors'] = $errors;
326 $this->vars['to'] = $to;
327 $this->vars['subject'] = $subject;
328 $this->vars['content'] = $content;
329 $this->vars['invoice'] = $invoice;
330
331 $this->format_render( __FUNCTION__ );
332 }
333
334
335 public function payment_form() {
336 $invoice_access_key = sanitize_text_field( $this->params['key'] );
337 if ( empty( $invoice_access_key ) ) {
338 echo __( 'Invalid Invoice Key', 'latepoint' );
339 exit;
340 }
341
342 $invoice = OsInvoicesHelper::get_invoice_by_key( $invoice_access_key );
343 if ( $invoice->is_new_record() ) {
344 echo __( 'Invoice not found', 'latepoint' );
345 exit;
346 }
347
348 $errors = [];
349 $order = $invoice->get_order();
350
351 // find an existing transaction intent for this invoice
352
353 $transaction_intent = new OsTransactionIntentModel();
354 $transaction_intent = $transaction_intent->where(
355 [
356 'status' => [
357 LATEPOINT_TRANSACTION_INTENT_STATUS_NEW,
358 LATEPOINT_TRANSACTION_INTENT_STATUS_PROCESSING,
359 LATEPOINT_TRANSACTION_INTENT_STATUS_CONVERTED,
360 ],
361 'invoice_id' => $invoice->id,
362 ]
363 )->set_limit( 1 )->get_results_as_models();
364 if ( empty( $transaction_intent ) ) {
365 $transaction_intent = new OsTransactionIntentModel();
366 }
367
368 $transaction_intent->charge_amount = $invoice->charge_amount;
369 $transaction_intent->invoice_id = $invoice->id;
370 $transaction_intent->order_id = $order->id;
371 $transaction_intent->customer_id = $order->customer_id;
372 $transaction_intent->set_payment_data_value( 'time', LATEPOINT_PAYMENT_TIME_NOW, false );
373 $transaction_intent->set_payment_data_value( 'portion', $invoice->payment_portion, false );
374
375 $form_prev_button = esc_html__( 'Back', 'latepoint' );
376 $form_next_button = esc_html__( 'Next', 'latepoint' );
377 $invoice_link = '';
378 $receipt_link = '';
379
380 $selected_payment_method = $this->params['payment_method'] ?? '';
381 $selected_payment_processor = $this->params['payment_processor'] ?? '';
382 $payment_token = $this->params['payment_token'] ?? '';
383
384 $enabled_payment_methods = OsPaymentsHelper::get_enabled_payment_methods_for_payment_time( LATEPOINT_PAYMENT_TIME_NOW );
385 // if only one available, force select it
386 if ( count( $enabled_payment_methods ) == 1 ) {
387 $selected_payment_method = array_key_first( $enabled_payment_methods );
388 }
389
390 if ( $selected_payment_method ) {
391 $enabled_payment_processors = OsPaymentsHelper::get_enabled_payment_processors_for_payment_time_and_method( LATEPOINT_PAYMENT_TIME_NOW, $selected_payment_method );
392 if ( count( $enabled_payment_processors ) == 1 ) {
393 $selected_payment_processor = array_key_first( $enabled_payment_processors );
394 }
395 }
396
397 if ( ! $selected_payment_method ) {
398 $current_step = 'methods';
399 $form_heading = __( 'Payment Methods', 'latepoint' );
400 $form_prev_button = false;
401 $form_next_button = false;
402 } else {
403 $transaction_intent->set_payment_data_value( 'method', $selected_payment_method, false );
404 if ( ! $selected_payment_processor ) {
405 $current_step = 'processors';
406 $form_heading = __( 'Payment Processors', 'latepoint' );
407
408 // hide prev button if we don't need to pick a payment methods
409 if ( count( $enabled_payment_methods ) <= 1 ) {
410 $form_prev_button = false;
411 }
412 $form_next_button = false;
413 } else {
414 $transaction_intent->set_payment_data_value( 'processor', $selected_payment_processor, false );
415 $form_next_button = sprintf( esc_html__( 'Pay %s', 'latepoint' ), OsMoneyHelper::format_price( $transaction_intent->charge_amount, true, false ) );
416 $form_heading = __( 'Payment Form', 'latepoint' );
417 // hide prev button if we don't need to pick a payment method or processor
418 if ( count( $enabled_payment_methods ) <= 1 && count( $enabled_payment_processors ) <= 1 ) {
419 $form_prev_button = false;
420 }
421 if ( $payment_token ) {
422 $transaction_intent->set_payment_data_value( 'token', $payment_token, false );
423 }
424 if ( ! $payment_token || empty( $this->params['submitting_payment'] ) ) {
425 $current_step = 'pay';
426 $transaction_intent->calculate_specs_charge_amount();
427 $transaction_intent->save();
428 } else {
429 $transaction_id = $transaction_intent->convert_to_transaction();
430 if ( $transaction_id ) {
431 $transaction = new OsTransactionModel( $transaction_id );
432 $form_next_button = false;
433 $form_prev_button = false;
434 $invoice_link = apply_filters( 'latepoint_transaction_invoice_link', $invoice_link, $invoice );
435 $receipt_link = apply_filters( 'latepoint_transaction_receipt_link', $receipt_link, $invoice, $transaction );
436 $current_step = 'confirmation';
437 $this->vars['transaction'] = $transaction;
438 $form_heading = __( 'Confirmation', 'latepoint' );
439 } else {
440 $current_step = 'pay';
441 $errors[] = implode( ', ', $transaction_intent->get_error_messages() );
442 }
443 }
444 }
445 }
446
447
448 $this->vars['invoice'] = $invoice;
449 $this->vars['invoice_link'] = $invoice_link;
450 $this->vars['receipt_link'] = $receipt_link;
451 $this->vars['form_heading'] = $form_heading;
452 $this->vars['payment_token'] = $payment_token;
453 $this->vars['errors'] = $errors;
454 $this->vars['in_lightbox'] = $this->params['in_lightbox'] ?? 'yes';
455 $this->vars['transaction_intent'] = $transaction_intent;
456 $this->vars['current_step'] = $current_step;
457 $this->vars['selected_payment_method'] = $selected_payment_method;
458 $this->vars['selected_payment_processor'] = $selected_payment_processor;
459 $this->vars['enabled_payment_methods'] = $enabled_payment_methods;
460
461 $this->vars['form_next_button'] = $form_next_button;
462 $this->vars['form_prev_button'] = $form_prev_button;
463 $this->vars['invoice_access_key'] = $invoice_access_key;
464
465
466 $this->vars['order'] = $order;
467
468 $this->format_render( __FUNCTION__ );
469 }
470
471 public function summary_before_payment() {
472 $invoice_access_key = sanitize_text_field( $this->params['key'] );
473 if ( empty( $invoice_access_key ) ) {
474 echo __( 'Invalid Invoice Key', 'latepoint' );
475 exit;
476 }
477
478 $invoice = OsInvoicesHelper::get_invoice_by_key( $invoice_access_key );
479 if ( $invoice->is_new_record() ) {
480 echo __( 'Invoice not found', 'latepoint' );
481 exit;
482 }
483
484 $this->vars['invoice'] = $invoice;
485 $this->vars['order'] = $invoice->get_order();
486
487 if ( $this->get_return_format() == 'json' ) {
488 $this->vars['in_lightbox'] = true;
489 $this->set_layout( 'none' );
490 $response_html = $this->format_render_return( __FUNCTION__ );
491 $this->send_json(
492 [
493 'status' => LATEPOINT_STATUS_SUCCESS,
494 'message' => $response_html,
495 ]
496 );
497 } else {
498 $this->vars['in_lightbox'] = false;
499 $this->set_layout( 'clean' );
500 $this->format_render( __FUNCTION__ );
501 }
502 }
503
504
505 function view_by_key() {
506 $invoice_access_key = sanitize_text_field( $this->params['key'] );
507 $invoice = new OsInvoiceModel();
508 $invoice = $invoice->where( [ 'access_key' => $invoice_access_key ] )->set_limit( 1 )->get_results_as_models();
509 $this->vars['invoice'] = $invoice;
510
511 $this->set_layout( 'clean' );
512 $this->format_render( __FUNCTION__ );
513 }
514
515 function view() {
516 if ( ! filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
517 return;
518 }
519
520 $invoice = new OsInvoiceModel( $this->params['id'] );
521
522 $this->vars['invoice'] = $invoice;
523
524 $this->set_layout( 'none' );
525 $response_html = $this->format_render_return( __FUNCTION__ );
526
527 $status = LATEPOINT_STATUS_SUCCESS;
528
529 if ( $this->get_return_format() == 'json' ) {
530
531 $this->send_json(
532 [
533 'status' => $status,
534 'message' => $response_html,
535 ]
536 );
537 }
538 }
539 }
540
541 endif;
542