PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.1.6
Tutor LMS – eLearning and online course solution v2.1.6
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / WooCommerce.php
tutor / classes Last commit date
Addons.php 3 years ago Admin.php 3 years ago Ajax.php 3 years ago Announcements.php 3 years ago Assets.php 3 years ago Backend_Page_Trait.php 3 years ago Course.php 3 years ago Course_Embed.php 3 years ago Course_Filter.php 3 years ago Course_List.php 3 years ago Course_Settings_Tabs.php 3 years ago Course_Widget.php 3 years ago Custom_Validation.php 3 years ago Dashboard.php 3 years ago FormHandler.php 3 years ago Frontend.php 3 years ago Gutenberg.php 3 years ago Input.php 3 years ago Instructor.php 3 years ago Instructors_List.php 3 years ago Lesson.php 3 years ago Options_V2.php 3 years ago Post_types.php 3 years ago Private_Course_Access.php 3 years ago Q_and_A.php 3 years ago Question_Answers_List.php 3 years ago Quiz.php 3 years ago Quiz_Attempts_List.php 3 years ago RestAPI.php 3 years ago Reviews.php 3 years ago Rewrite_Rules.php 3 years ago Shortcode.php 3 years ago Student.php 3 years ago Students_List.php 3 years ago Taxonomies.php 3 years ago Template.php 3 years ago Theme_Compatibility.php 3 years ago Tools.php 3 years ago Tools_V2.php 3 years ago Tutor.php 3 years ago TutorEDD.php 3 years ago Tutor_Base.php 3 years ago Tutor_Setup.php 3 years ago Upgrader.php 3 years ago User.php 3 years ago Utils.php 3 years ago Video_Stream.php 3 years ago Withdraw.php 3 years ago Withdraw_Requests_List.php 3 years ago WooCommerce.php 3 years ago
WooCommerce.php
834 lines
1 <?php
2 /**
3 * Manage WooCommerce integration
4 *
5 * @package Tutor\WooCommerce
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 1.0.0
9 */
10
11 namespace TUTOR;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Handle woocommerce hooks
19 *
20 * @since 1.0.0
21 */
22 class WooCommerce extends Tutor_Base {
23
24 /**
25 * Register hooks
26 *
27 * @since 1.0.0
28 */
29 public function __construct() {
30 parent::__construct();
31
32 // Add option settings.
33 add_filter( 'tutor_monetization_options', array( $this, 'tutor_monetization_options' ) );
34
35 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
36 if ( 'wc' !== $monetize_by ) {
37 return;
38 }
39
40 add_filter( 'tutor/options/attr', array( $this, 'add_options' ) );
41
42 /**
43 * Is Course Purchasable
44 */
45 add_filter( 'is_course_purchasable', array( $this, 'is_course_purchasable' ), 10, 2 );
46 add_filter( 'get_tutor_course_price', array( $this, 'get_tutor_course_price' ), 10, 2 );
47 add_filter( 'tutor_course_sell_by', array( $this, 'tutor_course_sell_by' ) );
48
49 add_filter( 'product_type_options', array( $this, 'add_tutor_type_in_wc_product' ) );
50
51 add_action( 'add_meta_boxes', array( $this, 'register_meta_box' ) );
52 add_action( 'save_post_' . $this->course_post_type, array( $this, 'save_course_meta' ), 10, 2 );
53 add_action( 'save_post_product', array( $this, 'save_wc_product_meta' ) );
54
55 add_action( 'tutor_course/single/before/enroll', 'wc_print_notices' );
56
57 /**
58 * After place new order
59 */
60 add_action( 'woocommerce_new_order', array( $this, 'course_placing_order_from_admin' ), 10, 3 );
61 add_action( 'woocommerce_new_order_item', array( $this, 'course_placing_order_from_customer' ), 10, 3 );
62
63 /**
64 * Order Status Hook
65 *
66 * Remove course from active courses if an order is cancelled or refunded
67 */
68 add_action( 'woocommerce_order_status_changed', array( $this, 'enrolled_courses_status_change' ), 10, 3 );
69
70 /**
71 * Add Earning Data
72 */
73 add_action( 'woocommerce_new_order_item', array( $this, 'add_earning_data' ), 10, 3 );
74 add_action( 'woocommerce_order_status_changed', array( $this, 'add_earning_data_status_change' ), 10, 3 );
75
76 /**
77 * WC Print Notices After Enroll
78 *
79 * @since 1.3.5
80 */
81 if ( tutor_utils()->has_wc() ) {
82 add_action( 'tutor_course/single/before/inner-wrap', 'wc_print_notices', 10 );
83 add_action( 'tutor_course/single/enrolled/before/inner-wrap', 'wc_print_notices', 10 );
84 }
85
86 /**
87 * Manage WooCommerce plugin dependency
88 *
89 * @since 1.7.8
90 */
91 $woocommerce_path = dirname( dirname( __DIR__ ) ) . DIRECTORY_SEPARATOR . 'woocommerce' . DIRECTORY_SEPARATOR . 'woocommerce.php';
92 register_deactivation_hook( $woocommerce_path, array( $this, 'disable_tutor_monetization' ) );
93 /**
94 * Redirect student on enrolled courses after course
95 * Enrollment complete
96 *
97 * @since 1.9.0
98 */
99 add_action( 'woocommerce_thankyou', array( $this, 'redirect_to_enrolled_courses' ) );
100
101 /**
102 * Change woo commerce cart product link if it is tutor product
103 */
104 add_filter( 'woocommerce_cart_item_permalink', array( $this, 'tutor_update_product_url' ), 10, 2 );
105 add_filter( 'woocommerce_order_item_permalink', array( $this, 'filter_order_item_permalink_callback' ), 10, 3 );
106
107 /**
108 * On WC product delete clear course linked product
109 *
110 * @since 2.0.7
111 */
112 add_action( 'delete_post', array( $this, 'clear_course_linked_product' ) );
113 }
114
115 /**
116 * On WC product delete, clear course linked product
117 *
118 * @since 2.0.7
119 *
120 * @param int $post_id post id.
121 *
122 * @return void
123 */
124 public function clear_course_linked_product( $post_id ) {
125 if ( get_post_type( $post_id ) === 'product' ) {
126 global $wpdb;
127 $wpdb->query(
128 $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key=%s AND meta_value=%d", '_tutor_course_product_id', $post_id )
129 );
130 }
131 }
132
133 /**
134 * Order item callback handler
135 *
136 * @since 1.0.0
137 *
138 * @param string $product_permalink permalink.
139 * @param mixed $item order item.
140 * @param mixed $order orders.
141 *
142 * @return string permalink of course
143 */
144 public function filter_order_item_permalink_callback( $product_permalink, $item, $order ) {
145
146 // For product variations.
147 if ( $item->get_variation_id() > 0 ) {
148 $product = $item->get_product();
149
150 $is_visible = $product && $product->is_visible();
151
152 // Get the instance of the parent variable product Object.
153 $parent_product = wc_get_product( $item->get_product_id() );
154
155 // Return the parent product permalink (if product is visible).
156 return $is_visible ? $parent_product->get_permalink() : '';
157 }
158
159 $course_id = $this->get_post_id_by_meta_key_and_value( '_tutor_course_product_id', $item->get_product_id() );
160
161 return get_permalink( $course_id );
162 }
163
164 /**
165 * Get post id my meta key & value
166 *
167 * @since 1.0.0
168 *
169 * @param mixed $key meta key.
170 * @param mixed $value meta value.
171 *
172 * @return mixed post id on success, false on failure
173 */
174 public function get_post_id_by_meta_key_and_value( $key, $value ) {
175 global $wpdb;
176 $meta = $wpdb->get_results( 'SELECT * FROM `' . $wpdb->postmeta . "` WHERE meta_key='" . esc_sql( $key ) . "' AND meta_value='" . esc_sql( $value ) . "'" );
177 if ( is_array( $meta ) && ! empty( $meta ) && isset( $meta[0] ) ) {
178 $meta = $meta[0];
179 }
180 if ( is_object( $meta ) ) {
181 return $meta->post_id;
182 } else {
183 return false;
184 }
185 }
186
187 /**
188 * Check if course is purchase able
189 *
190 * @since 1.0.0
191 *
192 * @param bool $bool default value.
193 * @param int $course_id course id.
194 *
195 * @return boolean
196 */
197 public function is_course_purchasable( $bool, $course_id ) {
198 if ( ! tutor_utils()->has_wc() ) {
199 return false;
200 }
201
202 $course_id = tutor_utils()->get_post_id( $course_id );
203 $has_product_id = get_post_meta( $course_id, '_tutor_course_product_id', true );
204 if ( $has_product_id ) {
205 return true;
206 }
207 return false;
208 }
209
210 /**
211 * Get course price
212 *
213 * @since 1.0.0
214 *
215 * @param mixed $price course price.
216 * @param int $course_id course id.
217 *
218 * @return string
219 */
220 public function get_tutor_course_price( $price, $course_id ) {
221 $price = null;
222
223 if ( tutor_utils()->is_course_purchasable( $course_id ) ) {
224 if ( tutor_utils()->has_wc() ) {
225 $product_id = tutor_utils()->get_course_product_id( $course_id );
226 $product = wc_get_product( $product_id );
227
228 if ( $product ) {
229 ob_start();
230 ?>
231 <div class="price">
232 <?php echo $product->get_price_html(); //phpcs:ignore ?>
233 </div>
234 <?php
235 return ob_get_clean();
236 }
237 }
238 }
239
240 return $price;
241 }
242
243 /**
244 * Sell by filter handler
245 *
246 * @since 1.0.0
247 *
248 * @return string
249 */
250 public function tutor_course_sell_by() {
251 return 'woocommerce';
252 }
253
254 /**
255 * Add tutor type in WC product
256 *
257 * @since 1.0.0
258 *
259 * @param array $types types.
260 *
261 * @return array
262 */
263 public function add_tutor_type_in_wc_product( $types ) {
264 $types['tutor_product'] = array(
265 'id' => '_tutor_product',
266 'wrapper_class' => 'show_if_simple',
267 'label' => __( 'For Tutor', 'tutor' ),
268 'description' => __( 'This checkmark ensure that you will sell a specif course via this product.', 'tutor' ),
269 'default' => 'no',
270 );
271
272 return $types;
273 }
274
275 /**
276 * Save course meta for attaching WC product
277 *
278 * @since 1.0.0
279 *
280 * @param int $post_ID this is course ID.
281 * @param mixed $post course details.
282 *
283 * @return void
284 */
285 public function save_course_meta( $post_ID, $post ) {
286 do_action( 'save_tutor_course', $post_ID, $post );
287 }
288
289 /**
290 * Register meta box
291 *
292 * @since 1.0.0
293 *
294 * @return void
295 */
296 public function register_meta_box() {
297 tutor_meta_box_wrapper( 'tutor-attach-product', __( 'Add Product', 'tutor' ), array( $this, 'course_add_product_metabox' ), $this->course_post_type, 'advanced', 'high', 'tutor-admin-post-meta' );
298 }
299
300 /**
301 * Meta box view
302 *
303 * @since 1.0.0
304 *
305 * @return void
306 */
307 public function course_add_product_metabox() {
308 include tutor()->path . 'views/metabox/course-add-product-metabox.php';
309 }
310
311 /**
312 * Save WC product meta
313 *
314 * @since 1.0.0
315 *
316 * @param int $post_ID post id.
317 *
318 * @return void
319 */
320 public function save_wc_product_meta( $post_ID ) {
321 $is_tutor_product = Input::post( '_tutor_product', '' );
322 if ( 'on' === $is_tutor_product ) {
323 update_post_meta( $post_ID, '_tutor_product', 'yes' );
324 } else {
325 delete_post_meta( $post_ID, '_tutor_product' );
326 }
327 }
328
329 /**
330 * Take enrolled course action based on order status change
331 *
332 * Order auto complete
333 *
334 * @param int $order_id wc order id.
335 * @param string $status_from from status.
336 * @param string $status_to to status.
337 *
338 * @return void
339 */
340 public function enrolled_courses_status_change( $order_id, $status_from, $status_to ) {
341 if ( ! tutor_utils()->is_tutor_order( $order_id ) ) {
342 return;
343 }
344
345 $enrolled_ids_with_course = $this->get_course_enrolled_ids_by_order_id( $order_id );
346
347 if ( $enrolled_ids_with_course ) {
348 $enrolled_ids = wp_list_pluck( $enrolled_ids_with_course, 'enrolled_id' );
349
350 if ( is_array( $enrolled_ids ) && count( $enrolled_ids ) ) {
351 foreach ( $enrolled_ids as $enrolled_id ) {
352 /**
353 * If order status is processing and payment is not cash on
354 * delivery then mark enrollment as completed.
355 *
356 * Note: Order status processing simply mean customer have done
357 * payment.
358 *
359 * @since v2.0.5
360 */
361 if ( self::should_order_auto_complete( $order_id ) ) {
362 tutor_utils()->course_enrol_status_change( $enrolled_id, 'completed' );
363 // Mark complete only from client side.
364 $mark_completed = self::mark_order_complete( $order_id );
365 if ( $mark_completed ) {
366 $user_id = get_post_field( 'post_author', $enrolled_id );
367 $course_id = get_post_field( 'post_parent', $enrolled_id );
368 do_action( 'tutor_after_enrolled', $course_id, $user_id, $enrolled_id );
369 }
370 } else {
371 tutor_utils()->course_enrol_status_change( $enrolled_id, $status_to );
372 }
373 // Invoke enrolled hook.
374 if ( 'completed' === $status_to ) {
375 $user_id = get_post_field( 'post_author', $enrolled_id );
376 $course_id = get_post_field( 'post_parent', $enrolled_id );
377 do_action( 'tutor_after_enrolled', $course_id, $user_id, $enrolled_id );
378 }
379 }
380 }
381 }
382 }
383
384 /**
385 * Get enrollment ids by order id
386 *
387 * @since 1.0.0
388 *
389 * @param int $order_id WC order id.
390 *
391 * @return array|bool
392 */
393 public function get_course_enrolled_ids_by_order_id( $order_id ) {
394 global $wpdb;
395 // Getting all of courses ids within this order.
396
397 $courses_ids = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key LIKE '_tutor_order_for_course_id_%' ", $order_id ) );
398
399 if ( is_array( $courses_ids ) && count( $courses_ids ) ) {
400 $course_enrolled_by_order = array();
401 foreach ( $courses_ids as $courses_id ) {
402 $course_id = str_replace( '_tutor_order_for_course_id_', '', $courses_id->meta_key );
403
404 $course_enrolled_by_order[] = array(
405 'course_id' => $course_id,
406 'enrolled_id' => $courses_id->meta_value,
407 'order_id' => $courses_id->post_id,
408 );
409 }
410 return $course_enrolled_by_order;
411 }
412 return false;
413 }
414
415 /**
416 * Add option for WooCommerce settings
417 *
418 * @since 1.0.0
419 *
420 * @param array $attr option attrs.
421 *
422 * @return mixed
423 */
424 public function add_options( $attr ) {
425 $attr['monetization']['blocks']['block_options']['fields'][] = array(
426 'key' => 'enable_guest_course_cart',
427 'type' => 'toggle_switch',
428 'label' => __( 'Enable Guest Mode', 'tutor' ),
429 'label_title' => '',
430 'default' => 'off',
431 'desc' => __( 'Allow customers to place orders without an account.', 'tutor' ),
432 );
433
434 return $attr;
435 }
436
437 /**
438 * Returning monetization options
439 *
440 * @since v.1.3.5
441 *
442 * @param array $arr attrs.
443 *
444 * @return mixed
445 */
446 public function tutor_monetization_options( $arr ) {
447 $has_wc = tutor_utils()->has_wc();
448 if ( $has_wc ) {
449 $arr['wc'] = __( 'WooCommerce', 'tutor' );
450 }
451 return $arr;
452 }
453
454 /**
455 * Adding Earning Data processing WooCommerce
456 *
457 * @param int $item_id item id.
458 * @param mixed $item item.
459 * @param int $order_id order id.
460 *
461 * @since 1.1.2
462 */
463 public function add_earning_data( $item_id, $item, $order_id ) {
464
465 if ( tutor_utils()->get_option( 'monetize_by' ) != 'wc' ) {
466 return;
467 }
468
469 global $wpdb;
470 $item = new \WC_Order_Item_Product( $item );
471
472 $product_id = $item->get_product_id();
473 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
474
475 if ( $if_has_course ) {
476 $course_id = $if_has_course->post_id;
477 $user_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_author FROM {$wpdb->posts} WHERE ID = %d ", $course_id ) );
478 $order_status = $wpdb->get_var( $wpdb->prepare( "SELECT post_status from {$wpdb->posts} where ID = %d ", $order_id ) );
479
480 /**
481 * Return here if already added this product from this order
482 *
483 * @since v1.9.7
484 */
485 $exist_count = (int) $wpdb->get_var(
486 $wpdb->prepare(
487 "SELECT COUNT(earning_id)
488 FROM {$wpdb->prefix}tutor_earnings
489 WHERE course_id=%d
490 AND order_id=%d
491 AND user_id=%d",
492 $course_id,
493 $order_id,
494 $user_id
495 )
496 );
497
498 if ( $exist_count > 0 ) {
499 return;
500 }
501
502 $total_price = $item->get_total();
503
504 $fees_deduct_data = array();
505 $tutor_earning_fees = tutor_utils()->get_option( 'fee_amount_type' );
506 $enable_fees_deducting = tutor_utils()->get_option( 'enable_fees_deducting' );
507
508 $course_price_grand_total = $total_price;
509
510 // Deduct predefined amount (percent or fixed).
511 if ( $enable_fees_deducting ) {
512 $fees_name = tutor_utils()->get_option( 'fees_name', '' );
513 $fees_amount = (int) tutor_utils()->avalue_dot( 'fees_amount', $tutor_earning_fees );
514 $fees_type = tutor_utils()->avalue_dot( 'fees_type', $tutor_earning_fees );
515
516 if ( $fees_amount > 0 ) {
517 if ( $fees_type === 'percent' ) {
518 $fees_amount = ( $total_price * $fees_amount ) / 100;
519 }
520
521 $course_price_grand_total = $total_price - $fees_amount;
522 }
523
524 $fees_deduct_data = array(
525 'deduct_fees_amount' => $fees_amount,
526 'deduct_fees_name' => $fees_name,
527 'deduct_fees_type' => $fees_type,
528 );
529 }
530
531 // Distribute amount between admin and instructor.
532 $sharing_enabled = tutor_utils()->get_option( 'enable_revenue_sharing' );
533 $instructor_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_instructor_commission' ) : 0;
534 $admin_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_admin_commission' ) : 100;
535 $commission_type = 'percent';
536 $instructor_amount = $instructor_rate > 0 ? ( ( $course_price_grand_total * $instructor_rate ) / 100 ) : 0;
537 $admin_amount = $admin_rate > 0 ? ( ( $course_price_grand_total * $admin_rate ) / 100 ) : 0;
538
539 // (Use Pro Filter - Start)
540 // The response must be same array structure.
541 // Do not change used variable names here, or change in both of here and pro plugin
542 $pro_arg = array(
543 'user_id' => $user_id,
544 'instructor_rate' => $instructor_rate,
545 'admin_rate' => $admin_rate,
546 'instructor_amount' => $instructor_amount,
547 'admin_amount' => $admin_amount,
548 'course_price_grand_total' => $course_price_grand_total,
549 'commission_type' => $commission_type,
550 );
551 $pro_calculation = apply_filters( 'tutor_pro_earning_calculator', $pro_arg );
552 extract( $pro_calculation );
553 // (Use Pro Filter - End).
554
555 // Prepare insertable earning data.
556 $earning_data = array(
557 'user_id' => $user_id,
558 'course_id' => $course_id,
559 'order_id' => $order_id,
560 'order_status' => $order_status,
561 'course_price_total' => $total_price,
562 'course_price_grand_total' => $course_price_grand_total,
563
564 'instructor_amount' => $instructor_amount,
565 'instructor_rate' => $instructor_rate,
566 'admin_amount' => $admin_amount,
567 'admin_rate' => $admin_rate,
568
569 'commission_type' => $commission_type,
570 'process_by' => 'woocommerce',
571 'created_at' => gmdate( 'Y-m-d H:i:s', tutor_time() ),
572 );
573 $earning_data = apply_filters( 'tutor_new_earning_data', array_merge( $earning_data, $fees_deduct_data ) );
574
575 $wpdb->insert( $wpdb->prefix . 'tutor_earnings', $earning_data );
576 }
577 }
578
579 /**
580 * Change Earning data status
581 *
582 * @since 1.0.0
583 *
584 * @param int $order_id wc order id.
585 * @param string $status_from previous status.
586 * @param string $status_to current status.
587 *
588 * @return void
589 */
590 public function add_earning_data_status_change( $order_id, $status_from, $status_to ) {
591 if ( ! tutor_utils()->is_tutor_order( $order_id ) ) {
592 tutor_log( 'not tutor order' );
593 return;
594 }
595 global $wpdb;
596
597 /**
598 * If it is auto complete order then make earning status complete
599 * to reflect earning for admin & instructor
600 *
601 * @since 2.0.9
602 */
603 if ( self::should_order_auto_complete( $order_id ) ) {
604 $status_to = 'completed';
605 }
606
607 $is_earning_data = (int) $wpdb->get_var(
608 $wpdb->prepare(
609 "SELECT COUNT(earning_id)
610 FROM {$wpdb->prefix}tutor_earnings
611 WHERE order_id = %d ",
612 $order_id
613 )
614 );
615
616 if ( $is_earning_data ) {
617 $update_earning_status = $wpdb->update(
618 $wpdb->prefix . 'tutor_earnings',
619 array( 'order_status' => $status_to ),
620 array( 'order_id' => $order_id )
621 );
622 do_action( 'tutor_after_earning_status_change', $update_earning_status );
623 }
624 }
625
626 /**
627 * Course placing order from admin
628 *
629 * @since v.1.6.7
630 *
631 * @param int $order_id wc order id.
632 *
633 * @return void
634 */
635 public function course_placing_order_from_admin( $order_id ) {
636 if ( ! is_admin() ) {
637 return;
638 }
639
640 $order = wc_get_order( $order_id );
641 foreach ( $order->get_items() as $item ) {
642 $product_id = $item->get_product_id();
643 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
644 if ( $if_has_course ) {
645 $course_id = $if_has_course->post_id;
646 $customer_id = $order->get_customer_id();
647 tutor_utils()->do_enroll( $course_id, $order_id, $customer_id );
648 }
649 }
650 }
651
652 /**
653 * Course placing order from customer
654 *
655 * @since 1.6.7
656 *
657 * @param int $item_id item id.
658 * @param mixed $item order item.
659 * @param int $order_id wc order id.
660 *
661 * @return void
662 */
663 public function course_placing_order_from_customer( $item_id, $item, $order_id ) {
664 if ( is_admin() ) {
665 return;
666 }
667
668 $item = new \WC_Order_Item_Product( $item );
669 $product_id = $item->get_product_id();
670 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
671
672 if ( $if_has_course ) {
673 $course_id = $if_has_course->post_id;
674 tutor_utils()->do_enroll( $course_id, $order_id );
675 }
676 }
677
678 /**
679 * Disable course monetization on woocommerce deactivation
680 *
681 * @since 1.7.8
682 *
683 * @return void
684 */
685 public function disable_tutor_monetization() {
686 tutor_utils()->update_option( 'monetize_by', 'free' );
687 update_option( 'tutor_show_woocommerce_notice', true );
688 }
689
690 /**
691 * Redirect student on enrolled courses after course
692 * enrollment complete if course is purchasable
693 *
694 * @since 1.0.0
695 *
696 * @param int $order_id wc order id.
697 *
698 * @return void
699 */
700 public function redirect_to_enrolled_courses( $order_id ) {
701 if ( ! tutor_utils()->get_option( 'wc_automatic_order_complete_redirect_to_courses' ) ) {
702 // Since 1.9.1.
703 return;
704 }
705
706 // get woo order details.
707 $order = wc_get_order( $order_id );
708 $tutor_product = false;
709 $url = tutor_utils()->tutor_dashboard_url() . 'enrolled-courses/';
710
711 foreach ( $order->get_items() as $item ) {
712 $product_id = $item->get_product_id();
713 // check if product associated with tutor course.
714 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
715 if ( $if_has_course ) {
716 $tutor_product = true;
717 }
718 }
719
720 // if tutor product & order status completed.
721 if ( $order->has_status( 'completed' ) && $tutor_product ) {
722 wp_safe_redirect( $url );
723 exit;
724 }
725 }
726
727 /**
728 * Change product url on cart page if product is tutor course
729 *
730 * @since 1.9.8
731 *
732 * @param string $permalink permalink.
733 * @param mixed $cart_item cart item.
734 *
735 * @return mixed
736 */
737 public function tutor_update_product_url( $permalink, $cart_item ) {
738
739 $woo_product_id = $cart_item['product_id'];
740 $product_meta = get_post_meta( $woo_product_id );
741
742 if ( isset( $product_meta['_tutor_product'] ) && $product_meta['_tutor_product'][0] ) {
743
744 global $wpdb;
745 $table = $wpdb->base_prefix . 'postmeta';
746 $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM {$table} WHERE meta_key = '_tutor_course_product_id' AND meta_value = %d ", $woo_product_id ) );
747
748 if ( $post_id ) {
749 $data = get_post_permalink( $post_id );
750 return $data;
751 }
752 }
753 }
754
755 /**
756 * Mark woocommerce order as complete only from the
757 * client side.
758 *
759 * @since 2.0.5
760 *
761 * @param int $order_id wc order id.
762 *
763 * @return bool
764 */
765 public static function mark_order_complete( int $order_id ): bool {
766 if ( is_admin() ) {
767 return false;
768 }
769 global $wpdb;
770 $update = $wpdb->update(
771 $wpdb->posts,
772 array( 'post_status' => 'wc-completed' ),
773 array( 'ID' => $order_id )
774 );
775 return (bool) $update;
776 }
777
778 /**
779 * Check if order should auto complete
780 *
781 * @since 2.0.9
782 *
783 * Bank transfer & check payments will consider as manual
784 * payments. Hence, if user pay with bank & check will not
785 * be auto complete
786 *
787 * @since 2.1.4
788 *
789 * @param int $order_id wc order id.
790 *
791 * @return boolean return true if it should auto complete, otherwise false
792 */
793 public static function should_order_auto_complete( int $order_id ): bool {
794 $auto_complete = false;
795 $order = wc_get_order( $order_id );
796 $order_data = is_object( $order ) && method_exists( $order, 'get_data' ) ? $order->get_data() : array();
797
798 $payment_method = isset( $order_data['payment_method'] ) ? $order_data['payment_method'] : '';
799 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
800
801 $should_auto_complete = tutor_utils()->get_option( 'tutor_woocommerce_order_auto_complete' );
802 $is_enabled_auto_complete = 'wc' === $monetize_by && $should_auto_complete ? true : false;
803
804 $manual_payments = array( 'cod', 'cheque', 'bacs' );
805 $order_status = method_exists( $order, 'get_status' ) ? $order->get_status() : '';
806 $is_tutor_order = get_post_meta( $order->get_id(), '_is_tutor_order_for_course', true );
807
808 /**
809 * Is tutor order condition added with other conditions,
810 * to prevent order other than Tutor get completed
811 *
812 * @since 2.1.6
813 */
814 if ( ! is_admin() && $is_enabled_auto_complete && 'processing' === $order_status && ! in_array( $payment_method, $manual_payments ) && $is_tutor_order ) {
815 $auto_complete = true;
816 }
817 return $auto_complete;
818 }
819 }
820
821
822 add_action(
823 'admin_notices',
824 function() {
825
826 $show = get_option( 'tutor_show_woocommerce_notice' ) && tutor_utils()->get_option( 'monetize_by', 'free' ) == 'free';
827
828 if ( $show ) {
829 $message = __( 'Since WooCommerce is disabled, your monetized courses have been set to free. Please make sure to enable Tutor LMS monetization if you decide to re-enable WooCommerce.', 'tutor' );
830 echo '<div class="notice notice-error"><p>' . esc_html( $message ) . '</p></div>';
831 }
832 }
833 );
834