PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.1.9
Tutor LMS – eLearning and online course solution v2.1.9
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
842 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 $order = wc_get_order( $order_id );
674
675 /**
676 * Get customer ID from from order
677 *
678 * @since 2.1.7
679 */
680 $customer_id = $order->get_customer_id();
681 $course_id = $if_has_course->post_id;
682 tutor_utils()->do_enroll( $course_id, $order_id, $customer_id );
683 }
684 }
685
686 /**
687 * Disable course monetization on woocommerce deactivation
688 *
689 * @since 1.7.8
690 *
691 * @return void
692 */
693 public function disable_tutor_monetization() {
694 tutor_utils()->update_option( 'monetize_by', 'free' );
695 update_option( 'tutor_show_woocommerce_notice', true );
696 }
697
698 /**
699 * Redirect student on enrolled courses after course
700 * enrollment complete if course is purchasable
701 *
702 * @since 1.0.0
703 *
704 * @param int $order_id wc order id.
705 *
706 * @return void
707 */
708 public function redirect_to_enrolled_courses( $order_id ) {
709 if ( ! tutor_utils()->get_option( 'wc_automatic_order_complete_redirect_to_courses' ) ) {
710 // Since 1.9.1.
711 return;
712 }
713
714 // get woo order details.
715 $order = wc_get_order( $order_id );
716 $tutor_product = false;
717 $url = tutor_utils()->tutor_dashboard_url() . 'enrolled-courses/';
718
719 foreach ( $order->get_items() as $item ) {
720 $product_id = $item->get_product_id();
721 // check if product associated with tutor course.
722 $if_has_course = tutor_utils()->product_belongs_with_course( $product_id );
723 if ( $if_has_course ) {
724 $tutor_product = true;
725 }
726 }
727
728 // if tutor product & order status completed.
729 if ( $order->has_status( 'completed' ) && $tutor_product ) {
730 wp_safe_redirect( $url );
731 exit;
732 }
733 }
734
735 /**
736 * Change product url on cart page if product is tutor course
737 *
738 * @since 1.9.8
739 *
740 * @param string $permalink permalink.
741 * @param mixed $cart_item cart item.
742 *
743 * @return mixed
744 */
745 public function tutor_update_product_url( $permalink, $cart_item ) {
746
747 $woo_product_id = $cart_item['product_id'];
748 $product_meta = get_post_meta( $woo_product_id );
749
750 if ( isset( $product_meta['_tutor_product'] ) && $product_meta['_tutor_product'][0] ) {
751
752 global $wpdb;
753 $table = $wpdb->base_prefix . 'postmeta';
754 $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 ) );
755
756 if ( $post_id ) {
757 $data = get_post_permalink( $post_id );
758 return $data;
759 }
760 }
761 }
762
763 /**
764 * Mark woocommerce order as complete only from the
765 * client side.
766 *
767 * @since 2.0.5
768 *
769 * @param int $order_id wc order id.
770 *
771 * @return bool
772 */
773 public static function mark_order_complete( int $order_id ): bool {
774 if ( is_admin() ) {
775 return false;
776 }
777 global $wpdb;
778 $update = $wpdb->update(
779 $wpdb->posts,
780 array( 'post_status' => 'wc-completed' ),
781 array( 'ID' => $order_id )
782 );
783 return (bool) $update;
784 }
785
786 /**
787 * Check if order should auto complete
788 *
789 * @since 2.0.9
790 *
791 * Bank transfer & check payments will consider as manual
792 * payments. Hence, if user pay with bank & check will not
793 * be auto complete
794 *
795 * @since 2.1.4
796 *
797 * @param int $order_id wc order id.
798 *
799 * @return boolean return true if it should auto complete, otherwise false
800 */
801 public static function should_order_auto_complete( int $order_id ): bool {
802 $auto_complete = false;
803 $order = wc_get_order( $order_id );
804 $order_data = is_object( $order ) && method_exists( $order, 'get_data' ) ? $order->get_data() : array();
805
806 $payment_method = isset( $order_data['payment_method'] ) ? $order_data['payment_method'] : '';
807 $monetize_by = tutor_utils()->get_option( 'monetize_by' );
808
809 $should_auto_complete = tutor_utils()->get_option( 'tutor_woocommerce_order_auto_complete' );
810 $is_enabled_auto_complete = 'wc' === $monetize_by && $should_auto_complete ? true : false;
811
812 $manual_payments = array( 'cod', 'cheque', 'bacs' );
813 $order_status = method_exists( $order, 'get_status' ) ? $order->get_status() : '';
814 $is_tutor_order = get_post_meta( $order->get_id(), '_is_tutor_order_for_course', true );
815
816 /**
817 * Is tutor order condition added with other conditions,
818 * to prevent order other than Tutor get completed
819 *
820 * @since 2.1.6
821 */
822 if ( ! is_admin() && $is_enabled_auto_complete && 'processing' === $order_status && ! in_array( $payment_method, $manual_payments ) && $is_tutor_order ) {
823 $auto_complete = true;
824 }
825 return $auto_complete;
826 }
827 }
828
829
830 add_action(
831 'admin_notices',
832 function() {
833
834 $show = get_option( 'tutor_show_woocommerce_notice' ) && tutor_utils()->get_option( 'monetize_by', 'free' ) == 'free';
835
836 if ( $show ) {
837 $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' );
838 echo '<div class="notice notice-error"><p>' . esc_html( $message ) . '</p></div>';
839 }
840 }
841 );
842