PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.1.3
LatePoint – Calendar Booking Plugin for Appointments and Events v5.1.3
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 1 year ago auth_controller.php 1 year ago booking_form_settings_controller.php 1 year ago bookings_controller.php 1 year ago calendars_controller.php 1 year ago carts_controller.php 1 year ago controller.php 1 year ago customer_cabinet_controller.php 1 year ago customers_controller.php 1 year ago dashboard_controller.php 1 year ago default_agent_controller.php 1 year ago events_controller.php 1 year ago form_fields_controller.php 1 year ago integrations_controller.php 1 year 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 1 year ago pro_controller.php 1 year ago process_jobs_controller.php 1 year ago processes_controller.php 1 year ago search_controller.php 1 year ago services_controller.php 1 year ago settings_controller.php 1 year ago steps_controller.php 1 year ago stripe_connect_controller.php 1 year ago support_topics_controller.php 1 year ago todos_controller.php 1 year ago transactions_controller.php 1 year ago wizard_controller.php 1 year ago
invoices_controller.php
459 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 $invoice->charge_amount = OsParamsHelper::sanitize_param( sanitize_text_field( $this->params['invoice']['charge_amount'] ), 'money' );
92 $due_at_wp_time = sanitize_text_field( $this->params['invoice']['due_at'] ).' 23:59:59';
93 $due_at_utc_time = OsWpDateTime::os_createFromFormat(LATEPOINT_DATETIME_DB_FORMAT, $due_at_wp_time)->setTimezone(new DateTimeZone('UTC'))->format(LATEPOINT_DATETIME_DB_FORMAT);
94 $invoice->due_at = $due_at_utc_time;
95 $invoice->status = sanitize_text_field( $this->params['invoice']['status'] );
96
97 if ( $invoice->save() ) {
98
99 /**
100 * Invoice was updated
101 *
102 * @param {OsInvoiceModel} $invoice instance of invoice model after it was updated
103 * @param {OsInvoiceModel} $old_invoice instance of invoice model before it was updated
104 *
105 * @since 5.1.0
106 * @hook latepoint_invoice_updated
107 *
108 */
109 do_action( 'latepoint_invoice_updated', $invoice, $old_invoice );
110 $status = LATEPOINT_STATUS_SUCCESS;
111 ob_start();
112 OsInvoicesHelper::invoice_document_html( $invoice, true );
113 $message = ob_get_clean();
114
115 } else {
116 $status = LATEPOINT_STATUS_ERROR;
117 $message = $invoice->get_error_messages();
118 }
119
120 $this->send_json( [ 'status' => $status, 'message' => $message ] );
121 }
122
123 public function edit_data() {
124 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
125 echo __( 'Invalid Invoice ID', 'latepoint' );
126
127 return;
128 }
129 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
130 if ( empty( $invoice ) || $invoice->is_new_record() ) {
131 echo __( 'Invoice not found', 'latepoint' );
132
133 return;
134 }
135
136 $this->vars['invoice'] = $invoice;
137
138 $this->format_render( __FUNCTION__ );
139 }
140
141 public function reload_invoice_tile() {
142 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
143 echo 'Invalid invoice';
144
145 return;
146 }
147 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
148 if ( empty( $invoice ) || $invoice->is_new_record() ) {
149 echo 'Invalid invoice';
150
151 return;
152 }
153
154 $this->send_json( [ 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => OsInvoicesHelper::generate_invoice_tile_on_order_edit_form( $invoice ) ] );
155 }
156
157 public function create() {
158 $invoice_params = $this->get_invoice_params();
159 if ( is_wp_error( $invoice_params ) ) {
160 $this->send_json( [ 'status' => LATEPOINT_STATUS_ERROR, 'message' => $invoice_params->get_error_message() ] );
161
162 return;
163 }
164
165 $order = new OsOrderModel( $invoice_params['order_id'] );
166 if ( empty( $order ) || $order->is_new_record() ) {
167 echo __( 'Invalid Order ID', 'latepoint' );
168
169 return;
170 }
171
172 $invoice = new OsInvoiceModel();
173 $invoice->set_data( $invoice_params );
174
175 $invoice->data = json_encode( OsInvoicesHelper::generate_invoice_data_from_order( $order ) );
176 if ( $invoice->save() ) {
177 /**
178 * Invoice was created
179 *
180 * @param {OsInvoiceModel} $invoice instance of invoice model that was created
181 *
182 * @since 5.1.0
183 * @hook latepoint_invoice_created
184 *
185 */
186 do_action( 'latepoint_invoice_created', $invoice );
187 $response_html = OsInvoicesHelper::generate_invoice_tile_on_order_edit_form( $invoice );
188 $this->send_json( [ 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => $response_html ] );
189 } else {
190 $this->send_json( [ 'status' => LATEPOINT_STATUS_ERROR, 'message' => __( 'Error: ', 'latepoint' ) . $invoice->get_error_messages() ] );
191 }
192
193 }
194
195 public function change_status() {
196 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
197 echo 'Invalid Invoice';
198
199 return;
200 }
201
202 if ( ! in_array( $this->params['status'], array_keys( OsInvoicesHelper::list_of_statuses_for_select() ) ) ) {
203 echo 'Invalid Status';
204
205 return;
206 }
207
208 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
209 $invoice->change_status( $this->params['status'] );
210
211 $status = LATEPOINT_STATUS_SUCCESS;
212
213 ob_start();
214 OsInvoicesHelper::invoice_document_html( $invoice, true );
215 $response_html = ob_get_clean();
216
217 if ( $this->get_return_format() == 'json' ) {
218
219 $this->send_json( [ 'status' => $status, 'message' => $response_html ] );
220 }
221 }
222
223 public function email_form() {
224 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
225 echo __( 'Invalid Invoice ID', 'latepoint' );
226
227 return;
228 }
229 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
230 if ( empty( $invoice ) || $invoice->is_new_record() ) {
231 echo __( 'Invoice not found', 'latepoint' );
232
233 return;
234 }
235 $errors = [];
236
237 $to = __( '{{customer_email}}', 'latepoint' );
238 $subject = OsInvoicesHelper::get_subject_for_invoice_email();
239 $content = OsInvoicesHelper::get_content_for_invoice_email();
240
241 if ( ! empty( $this->params['invoice_email'] ) ) {
242 // send email
243 $to = $this->params['invoice_email[to]'] ?? $to;
244 $order = new OsOrderModel( $invoice->order_id );
245 $customer = new OsCustomerModel( $order->customer_id );
246
247 $original_to = $to;
248 $to = OsReplacerHelper::replace_all_vars( $to, [ 'order' => $order, 'customer' => $customer, 'invoice' => $invoice ] );
249 $subject = OsReplacerHelper::replace_all_vars( $subject, [ 'order' => $order, 'customer' => $customer, 'invoice' => $invoice ] );
250 $content = OsReplacerHelper::replace_all_vars( $content, [ 'order' => $order, 'customer' => $customer, 'invoice' => $invoice ] );
251 if ( OsUtilHelper::is_valid_email( $to ) ) {
252 $mailer = new OsMailer();
253 wp_mail( $to, $subject, $content, $mailer->get_headers() );
254 // set back so it appears correctly on the front
255 $to = $original_to;
256 $this->vars['success'] = __( 'Invoice email sent', 'latepoint' );
257 } else {
258 $errors[] = __( 'Please enter a valid email address.', 'latepoint' );
259 }
260
261 }
262
263 $this->vars['errors'] = $errors;
264 $this->vars['to'] = $to;
265 $this->vars['subject'] = $subject;
266 $this->vars['content'] = $content;
267 $this->vars['invoice'] = $invoice;
268
269 $this->format_render( __FUNCTION__ );
270 }
271
272
273 public function payment_form() {
274 $invoice_access_key = sanitize_text_field( $this->params['key'] );
275 if ( empty( $invoice_access_key ) ) {
276 echo __( 'Invalid Invoice Key', 'latepoint' );
277 exit;
278 }
279
280 $invoice = OsInvoicesHelper::get_invoice_by_key( $invoice_access_key );
281 if ( $invoice->is_new_record() ) {
282 echo __( 'Invoice not found', 'latepoint' );
283 exit;
284 }
285
286 $errors = [];
287 $order = $invoice->get_order();
288
289 // find an existing transaction intent for this invoice
290
291 $transaction_intent = new OsTransactionIntentModel();
292 $transaction_intent = $transaction_intent->where( [ 'status' => LATEPOINT_TRANSACTION_INTENT_STATUS_NEW, 'invoice_id' => $invoice->id ] )->set_limit( 1 )->get_results_as_models();
293 if ( empty( $transaction_intent ) ) {
294 $transaction_intent = new OsTransactionIntentModel();
295 }
296
297 $transaction_intent->charge_amount = $invoice->charge_amount;
298 $transaction_intent->invoice_id = $invoice->id;
299 $transaction_intent->order_id = $order->id;
300 $transaction_intent->customer_id = $order->customer_id;
301 $transaction_intent->set_payment_data_value( 'time', LATEPOINT_PAYMENT_TIME_NOW, false );
302 $transaction_intent->set_payment_data_value( 'portion', $invoice->payment_portion, false );
303
304 $form_prev_button = esc_html__( 'Back', 'latepoint' );
305 $form_next_button = esc_html__( 'Next', 'latepoint' );
306 $invoice_link = '';
307 $receipt_link = '';
308
309 $selected_payment_method = $this->params['payment_method'] ?? '';
310 $selected_payment_processor = $this->params['payment_processor'] ?? '';
311 $payment_token = $this->params['payment_token'] ?? '';
312
313 $enabled_payment_methods = OsPaymentsHelper::get_enabled_payment_methods_for_payment_time( LATEPOINT_PAYMENT_TIME_NOW );
314 // if only one available, force select it
315 if ( count( $enabled_payment_methods ) == 1 ) {
316 $selected_payment_method = array_key_first( $enabled_payment_methods );
317 }
318
319 if ( $selected_payment_method ) {
320 $enabled_payment_processors = OsPaymentsHelper::get_enabled_payment_processors_for_payment_time_and_method( LATEPOINT_PAYMENT_TIME_NOW, $selected_payment_method );
321 if ( count( $enabled_payment_processors ) == 1 ) {
322 $selected_payment_processor = array_key_first( $enabled_payment_processors );
323 }
324 }
325
326 if ( ! $selected_payment_method ) {
327 $current_step = 'methods';
328 $form_heading = __( 'Payment Methods', 'latepoint' );
329 $form_prev_button = false;
330 $form_next_button = false;
331 } else {
332 $transaction_intent->set_payment_data_value( 'method', $selected_payment_method, false );
333 if ( ! $selected_payment_processor ) {
334 $current_step = 'processors';
335 $form_heading = __( 'Payment Processors', 'latepoint' );
336
337 // hide prev button if we don't need to pick a payment methods
338 if ( count( $enabled_payment_methods ) <= 1 ) {
339 $form_prev_button = false;
340 }
341 $form_next_button = false;
342 } else {
343 $transaction_intent->set_payment_data_value( 'processor', $selected_payment_processor, false );
344 $form_next_button = sprintf( esc_html__( 'Pay %s', 'latepoint' ), OsMoneyHelper::format_price( $transaction_intent->charge_amount, true, false ) );
345 $form_heading = __( 'Payment Form', 'latepoint' );
346 // hide prev button if we don't need to pick a payment method or processor
347 if ( count( $enabled_payment_methods ) <= 1 && count( $enabled_payment_processors ) <= 1 ) {
348 $form_prev_button = false;
349 }
350 if ( $payment_token ) {
351 $transaction_intent->set_payment_data_value( 'token', $payment_token, false );
352 }
353 if ( ! $payment_token || empty( $this->params['submitting_payment'] ) ) {
354 $current_step = 'pay';
355 } else {
356 $transaction_id = $transaction_intent->convert_to_transaction();
357 if ( $transaction_id ) {
358 $transaction = new OsTransactionModel( $transaction_id );
359 $form_next_button = false;
360 $form_prev_button = false;
361 $invoice_link = apply_filters( 'latepoint_transaction_invoice_link', $invoice_link, $invoice );
362 $receipt_link = apply_filters( 'latepoint_transaction_receipt_link', $receipt_link, $invoice, $transaction );
363 $current_step = 'confirmation';
364 $this->vars['transaction'] = $transaction;
365 $form_heading = __( 'Confirmation', 'latepoint' );;
366 } else {
367 $current_step = 'pay';
368 $errors[] = implode( ', ', $transaction_intent->get_error_messages() );
369 }
370 }
371 }
372 }
373
374
375 $this->vars['invoice'] = $invoice;
376 $this->vars['invoice_link'] = $invoice_link;
377 $this->vars['receipt_link'] = $receipt_link;
378 $this->vars['form_heading'] = $form_heading;
379 $this->vars['payment_token'] = $payment_token;
380 $this->vars['errors'] = $errors;
381 $this->vars['in_lightbox'] = $this->params['in_lightbox'] ?? 'yes';
382 $this->vars['transaction_intent'] = $transaction_intent;
383 $this->vars['current_step'] = $current_step;
384 $this->vars['selected_payment_method'] = $selected_payment_method;
385 $this->vars['selected_payment_processor'] = $selected_payment_processor;
386 $this->vars['enabled_payment_methods'] = $enabled_payment_methods;
387
388 $this->vars['form_next_button'] = $form_next_button;
389 $this->vars['form_prev_button'] = $form_prev_button;
390 $this->vars['invoice_access_key'] = $invoice_access_key;
391
392
393 $this->vars['order'] = $order;
394
395 $this->format_render( __FUNCTION__ );
396 }
397
398 public function summary_before_payment() {
399 $invoice_access_key = sanitize_text_field( $this->params['key'] );
400 if ( empty( $invoice_access_key ) ) {
401 echo __( 'Invalid Invoice Key', 'latepoint' );
402 exit;
403 }
404
405 $invoice = OsInvoicesHelper::get_invoice_by_key( $invoice_access_key );
406 if ( $invoice->is_new_record() ) {
407 echo __( 'Invoice not found', 'latepoint' );
408 exit;
409 }
410
411 $this->vars['invoice'] = $invoice;
412 $this->vars['order'] = $invoice->get_order();
413
414 if ( $this->get_return_format() == 'json' ) {
415 $this->vars['in_lightbox'] = true;
416 $this->set_layout( 'none' );
417 $response_html = $this->format_render_return( __FUNCTION__ );
418 $this->send_json( [ 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => $response_html ] );
419 } else {
420 $this->vars['in_lightbox'] = false;
421 $this->set_layout( 'clean' );
422 $this->format_render( __FUNCTION__ );
423 }
424 }
425
426
427 function view_by_key() {
428 $invoice_access_key = sanitize_text_field( $this->params['key'] );
429 $invoice = new OsInvoiceModel();
430 $invoice = $invoice->where( [ 'access_key' => $invoice_access_key ] )->set_limit( 1 )->get_results_as_models();
431 $this->vars['invoice'] = $invoice;
432
433 $this->set_layout( 'clean' );
434 $this->format_render( __FUNCTION__ );
435 }
436
437 function view() {
438 if ( ! filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
439 return;
440 }
441
442 $invoice = new OsInvoiceModel( $this->params['id'] );
443
444 $this->vars['invoice'] = $invoice;
445
446 $this->set_layout( 'none' );
447 $response_html = $this->format_render_return( __FUNCTION__ );
448
449 $status = LATEPOINT_STATUS_SUCCESS;
450
451 if ( $this->get_return_format() == 'json' ) {
452
453 $this->send_json( [ 'status' => $status, 'message' => $response_html ] );
454 }
455 }
456 }
457
458 endif;
459