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