PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.6.6
LatePoint – Calendar Booking Plugin for Appointments and Events v5.6.6
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 / invoices_controller.php
latepoint / lib / controllers Last commit date
activities_controller.php 2 months ago auth_controller.php 3 months ago booking_form_settings_controller.php 3 months ago bookings_controller.php 4 days ago calendars_controller.php 3 months ago carts_controller.php 4 days ago controller.php 3 months ago customer_cabinet_controller.php 2 months ago customers_controller.php 4 days ago dashboard_controller.php 2 months ago default_agent_controller.php 3 months ago events_controller.php 3 months ago form_fields_controller.php 1 week ago integrations_controller.php 3 months ago invoices_controller.php 4 days 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 4 days ago pro_controller.php 3 weeks ago process_jobs_controller.php 3 months ago processes_controller.php 1 month ago razorpay_connect_controller.php 1 week ago search_controller.php 3 months ago services_controller.php 3 months ago settings_controller.php 2 months ago steps_controller.php 3 weeks ago stripe_connect_controller.php 1 week ago support_topics_controller.php 3 months ago todos_controller.php 3 months ago transactions_controller.php 4 days ago wizard_controller.php 1 week ago
invoices_controller.php
539 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 $this->check_nonce( 'change_invoice_status_' . $this->params['invoice_id'] );
234
235 if ( ! in_array( $this->params['status'], array_keys( OsInvoicesHelper::list_of_statuses_for_select() ) ) ) {
236 echo 'Invalid Status';
237
238 return;
239 }
240
241 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
242 $invoice->change_status( $this->params['status'] );
243
244 $status = LATEPOINT_STATUS_SUCCESS;
245
246 ob_start();
247 OsInvoicesHelper::invoice_document_html( $invoice, true );
248 $response_html = ob_get_clean();
249
250 if ( $this->get_return_format() == 'json' ) {
251
252 $this->send_json(
253 [
254 'status' => $status,
255 'message' => $response_html,
256 ]
257 );
258 }
259 }
260
261 public function email_form() {
262 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
263 echo __( 'Invalid Invoice ID', 'latepoint' );
264
265 return;
266 }
267 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
268 if ( empty( $invoice ) || $invoice->is_new_record() ) {
269 echo __( 'Invoice not found', 'latepoint' );
270
271 return;
272 }
273 $errors = [];
274
275 $to = __( '{{customer_email}}', 'latepoint' );
276 $subject = OsInvoicesHelper::get_subject_for_invoice_email();
277 $content = OsInvoicesHelper::get_content_for_invoice_email();
278
279 if ( ! empty( $this->params['invoice_email'] ) ) {
280 // Verify nonce before dispatching email.
281 $this->check_nonce( 'email_invoice_' . $this->params['invoice_id'] );
282 // send email
283 $to = $this->params['invoice_email[to]'] ?? $to;
284 $order = new OsOrderModel( $invoice->order_id );
285 $customer = new OsCustomerModel( $order->customer_id );
286
287 $original_to = $to;
288 $to = OsReplacerHelper::replace_all_vars(
289 $to,
290 [
291 'order' => $order,
292 'customer' => $customer,
293 'invoice' => $invoice,
294 ]
295 );
296 $subject = OsReplacerHelper::replace_all_vars(
297 $subject,
298 [
299 'order' => $order,
300 'customer' => $customer,
301 'invoice' => $invoice,
302 ]
303 );
304 $content = OsReplacerHelper::replace_all_vars(
305 $content,
306 [
307 'order' => $order,
308 'customer' => $customer,
309 'invoice' => $invoice,
310 ]
311 );
312 if ( OsUtilHelper::is_valid_email( $to ) ) {
313 $mailer = new OsMailer();
314 wp_mail( $to, $subject, $content, $mailer->get_headers() );
315 // set back so it appears correctly on the front
316 $to = $original_to;
317 $this->vars['success'] = __( 'Invoice email sent', 'latepoint' );
318 } else {
319 $errors[] = __( 'Please enter a valid email address.', 'latepoint' );
320 }
321 }
322
323 $this->vars['errors'] = $errors;
324 $this->vars['to'] = $to;
325 $this->vars['subject'] = $subject;
326 $this->vars['content'] = $content;
327 $this->vars['invoice'] = $invoice;
328
329 $this->format_render( __FUNCTION__ );
330 }
331
332
333 public function payment_form() {
334 $invoice_access_key = sanitize_text_field( $this->params['key'] );
335 if ( empty( $invoice_access_key ) ) {
336 echo __( 'Invalid Invoice Key', 'latepoint' );
337 exit;
338 }
339
340 $invoice = OsInvoicesHelper::get_invoice_by_key( $invoice_access_key );
341 if ( $invoice->is_new_record() ) {
342 echo __( 'Invoice not found', 'latepoint' );
343 exit;
344 }
345
346 $errors = [];
347 $order = $invoice->get_order();
348
349 // find an existing transaction intent for this invoice
350 $transaction_intent = new OsTransactionIntentModel();
351 $transaction_intent = $transaction_intent->where(
352 [
353 'status' => [
354 LATEPOINT_TRANSACTION_INTENT_STATUS_NEW,
355 LATEPOINT_TRANSACTION_INTENT_STATUS_PROCESSING,
356 LATEPOINT_TRANSACTION_INTENT_STATUS_CONVERTED,
357 ],
358 'invoice_id' => $invoice->id,
359 ]
360 )->set_limit( 1 )->get_results_as_models();
361 if ( empty( $transaction_intent ) ) {
362 $transaction_intent = new OsTransactionIntentModel();
363 }
364
365 $transaction_intent->charge_amount = $invoice->charge_amount;
366 $transaction_intent->invoice_id = $invoice->id;
367 $transaction_intent->order_id = $order->id;
368 $transaction_intent->customer_id = $order->customer_id;
369 $transaction_intent->set_payment_data_value( 'time', LATEPOINT_PAYMENT_TIME_NOW, false );
370 $transaction_intent->set_payment_data_value( 'portion', $invoice->payment_portion, false );
371
372 $form_prev_button = esc_html__( 'Back', 'latepoint' );
373 $form_next_button = esc_html__( 'Next', 'latepoint' );
374 $invoice_link = '';
375 $receipt_link = '';
376
377 $selected_payment_method = $this->params['payment_method'] ?? '';
378 $selected_payment_processor = $this->params['payment_processor'] ?? '';
379 $payment_token = $this->params['payment_token'] ?? '';
380
381 $enabled_payment_methods = OsPaymentsHelper::get_enabled_payment_methods_for_payment_time( LATEPOINT_PAYMENT_TIME_NOW );
382 // if only one available, force select it
383 if ( count( $enabled_payment_methods ) == 1 ) {
384 $selected_payment_method = array_key_first( $enabled_payment_methods );
385 }
386
387 if ( $selected_payment_method ) {
388 $enabled_payment_processors = OsPaymentsHelper::get_enabled_payment_processors_for_payment_time_and_method( LATEPOINT_PAYMENT_TIME_NOW, $selected_payment_method );
389 if ( count( $enabled_payment_processors ) == 1 ) {
390 $selected_payment_processor = array_key_first( $enabled_payment_processors );
391 }
392 }
393
394 if ( ! $selected_payment_method ) {
395 $current_step = 'methods';
396 $form_heading = __( 'Payment Methods', 'latepoint' );
397 $form_prev_button = false;
398 $form_next_button = false;
399 } else {
400 $transaction_intent->set_payment_data_value( 'method', $selected_payment_method, false );
401 if ( ! $selected_payment_processor ) {
402 $current_step = 'processors';
403 $form_heading = __( 'Payment Processors', 'latepoint' );
404
405 // hide prev button if we don't need to pick a payment methods
406 if ( count( $enabled_payment_methods ) <= 1 ) {
407 $form_prev_button = false;
408 }
409 $form_next_button = false;
410 } else {
411 $transaction_intent->set_payment_data_value( 'processor', $selected_payment_processor, false );
412 $form_next_button = sprintf( esc_html__( 'Pay %s', 'latepoint' ), OsMoneyHelper::format_price( $transaction_intent->charge_amount, true, false ) );
413 $form_heading = __( 'Payment Form', 'latepoint' );
414 // hide prev button if we don't need to pick a payment method or processor
415 if ( count( $enabled_payment_methods ) <= 1 && count( $enabled_payment_processors ) <= 1 ) {
416 $form_prev_button = false;
417 }
418 if ( $payment_token ) {
419 $transaction_intent->set_payment_data_value( 'token', $payment_token, false );
420 }
421 if ( ! $payment_token || empty( $this->params['submitting_payment'] ) ) {
422 $current_step = 'pay';
423 $transaction_intent->calculate_specs_charge_amount();
424 $transaction_intent->save();
425 } else {
426 $transaction_id = $transaction_intent->convert_to_transaction();
427 if ( $transaction_id ) {
428 $transaction = new OsTransactionModel( $transaction_id );
429 $form_next_button = false;
430 $form_prev_button = false;
431 $invoice_link = apply_filters( 'latepoint_transaction_invoice_link', $invoice_link, $invoice );
432 $receipt_link = apply_filters( 'latepoint_transaction_receipt_link', $receipt_link, $invoice, $transaction );
433 $current_step = 'confirmation';
434 $this->vars['transaction'] = $transaction;
435 $form_heading = __( 'Confirmation', 'latepoint' );
436 } else {
437 $current_step = 'pay';
438 $errors[] = implode( ', ', $transaction_intent->get_error_messages() );
439 }
440 }
441 }
442 }
443
444
445 $this->vars['invoice'] = $invoice;
446 $this->vars['invoice_link'] = $invoice_link;
447 $this->vars['receipt_link'] = $receipt_link;
448 $this->vars['form_heading'] = $form_heading;
449 $this->vars['payment_token'] = $payment_token;
450 $this->vars['errors'] = $errors;
451 $this->vars['in_lightbox'] = $this->params['in_lightbox'] ?? 'yes';
452 $this->vars['transaction_intent'] = $transaction_intent;
453 $this->vars['current_step'] = $current_step;
454 $this->vars['selected_payment_method'] = $selected_payment_method;
455 $this->vars['selected_payment_processor'] = $selected_payment_processor;
456 $this->vars['enabled_payment_methods'] = $enabled_payment_methods;
457
458 $this->vars['form_next_button'] = $form_next_button;
459 $this->vars['form_prev_button'] = $form_prev_button;
460 $this->vars['invoice_access_key'] = $invoice_access_key;
461
462
463 $this->vars['order'] = $order;
464
465 $this->format_render( __FUNCTION__ );
466 }
467
468 public function summary_before_payment() {
469 $invoice_access_key = sanitize_text_field( $this->params['key'] );
470 if ( empty( $invoice_access_key ) ) {
471 echo __( 'Invalid Invoice Key', 'latepoint' );
472 exit;
473 }
474
475 $invoice = OsInvoicesHelper::get_invoice_by_key( $invoice_access_key );
476 if ( $invoice->is_new_record() ) {
477 echo __( 'Invoice not found', 'latepoint' );
478 exit;
479 }
480
481 $this->vars['invoice'] = $invoice;
482 $this->vars['order'] = $invoice->get_order();
483
484 if ( $this->get_return_format() == 'json' ) {
485 $this->vars['in_lightbox'] = true;
486 $this->set_layout( 'none' );
487 $response_html = $this->format_render_return( __FUNCTION__ );
488 $this->send_json(
489 [
490 'status' => LATEPOINT_STATUS_SUCCESS,
491 'message' => $response_html,
492 ]
493 );
494 } else {
495 $this->vars['in_lightbox'] = false;
496 $this->set_layout( 'clean' );
497 $this->format_render( __FUNCTION__ );
498 }
499 }
500
501
502 function view_by_key() {
503 $invoice_access_key = sanitize_text_field( $this->params['key'] );
504 $invoice = new OsInvoiceModel();
505 $invoice = $invoice->where( [ 'access_key' => $invoice_access_key ] )->set_limit( 1 )->get_results_as_models();
506 $this->vars['invoice'] = $invoice;
507
508 $this->set_layout( 'clean' );
509 $this->format_render( __FUNCTION__ );
510 }
511
512 function view() {
513 if ( ! filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
514 return;
515 }
516
517 $invoice = new OsInvoiceModel( $this->params['id'] );
518
519 $this->vars['invoice'] = $invoice;
520
521 $this->set_layout( 'none' );
522 $response_html = $this->format_render_return( __FUNCTION__ );
523
524 $status = LATEPOINT_STATUS_SUCCESS;
525
526 if ( $this->get_return_format() == 'json' ) {
527
528 $this->send_json(
529 [
530 'status' => $status,
531 'message' => $response_html,
532 ]
533 );
534 }
535 }
536 }
537
538 endif;
539