PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.6.0
Tutor LMS – eLearning and online course solution v3.6.0
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 / Earnings.php
tutor / classes Last commit date
Addons.php 1 year ago Admin.php 1 year ago Ajax.php 1 year ago Announcements.php 1 year ago Assets.php 1 year ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year ago Course.php 1 year ago Course_Embed.php 3 years ago Course_Filter.php 1 year ago Course_List.php 1 year ago Course_Settings_Tabs.php 1 year ago Course_Widget.php 1 year ago Custom_Validation.php 3 years ago Dashboard.php 1 year ago Earnings.php 1 year ago FormHandler.php 2 years ago Frontend.php 1 year ago Gutenberg.php 1 year ago Input.php 1 year ago Instructor.php 1 year ago Instructors_List.php 1 year ago Lesson.php 1 year ago Options_V2.php 1 year ago Permalink.php 2 years ago Post_types.php 1 year ago Private_Course_Access.php 1 year ago Q_And_A.php 1 year ago Question_Answers_List.php 3 years ago Quiz.php 1 year ago QuizBuilder.php 1 year ago Quiz_Attempts_List.php 1 year ago RestAPI.php 2 years ago Reviews.php 3 years ago Rewrite_Rules.php 2 years ago Shortcode.php 1 year ago Singleton.php 1 year ago Student.php 1 year ago Students_List.php 1 year ago Taxonomies.php 1 year ago Template.php 1 year ago Theme_Compatibility.php 3 years ago Tools.php 1 year ago Tools_V2.php 1 year ago Tutor.php 1 year ago TutorEDD.php 1 year ago Tutor_Base.php 2 years ago Tutor_Setup.php 1 year ago Upgrader.php 1 year ago User.php 1 year ago Utils.php 1 year ago Video_Stream.php 3 years ago WhatsNew.php 2 years ago Withdraw.php 1 year ago Withdraw_Requests_List.php 1 year ago WooCommerce.php 1 year ago
Earnings.php
390 lines
1 <?php
2 /**
3 * Manage earnings
4 *
5 * @package Tutor\Ecommerce
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 3.0.0
9 */
10
11 namespace TUTOR;
12
13 use TUTOR\Singleton;
14 use Tutor\Models\OrderModel;
15 use Tutor\Traits\EarningData;
16 use Tutor\Helpers\QueryHelper;
17
18 /**
19 * Manage earnings
20 */
21 class Earnings extends Singleton {
22
23 /**
24 * Error message for the invalid earning data
25 *
26 * @since 1.0.0
27 *
28 * @var string
29 */
30 const INVALID_DATA_MSG = 'Invalid earning data';
31
32 /**
33 * Earning table name
34 *
35 * @since 3.0.0
36 *
37 * @var string
38 */
39 private $earning_table;
40
41 /**
42 * Order id
43 *
44 * @since 3.0.0
45 *
46 * @var int
47 */
48 private $order_id;
49
50 /**
51 * Keep earning data here
52 *
53 * @since 3.0.0
54 *
55 * @var array
56 */
57 public $earning_data = array();
58
59 /**
60 * Set table name prop
61 *
62 * @since 3.0.0
63 */
64 protected function __construct() {
65 global $wpdb;
66 $this->earning_table = $wpdb->prefix . 'tutor_earnings';
67 }
68
69 /**
70 * Prepare earnings from this order to store it as
71 * earning & commission data.
72 *
73 * @since 3.0.0
74 *
75 * @param int $order_id Order id.
76 *
77 * @return mixed
78 */
79 public function prepare_order_earnings( int $order_id ) {
80 $this->order_id = $order_id;
81
82 $order_model = new OrderModel();
83 $order_details = $order_model->get_order_by_id( $order_id );
84 $items = is_object( $order_details ) && property_exists( $order_details, 'items' ) ? $order_details->items : array();
85
86 $deducted_amount = $order_details->refund_amount + $order_details->coupon_amount;
87 if ( $order_details->discount_amount ) {
88 $discount_amount = $order_model->calculate_discount_amount( $order_details->discount_type, $order_details->discount_amount, $order_details->subtotal_price );
89 $deducted_amount += $discount_amount;
90 }
91
92 if ( is_array( $items ) && count( $items ) ) {
93
94 foreach ( $items as $item ) {
95
96 $subtotal_price = $item->regular_price;
97 $item_sold_price = $order_model->get_item_sold_price( $item->id, false );
98
99 try {
100 $per_earning_refund = $order_details->subtotal_price
101 ? ( $deducted_amount * $subtotal_price ) / $order_details->subtotal_price
102 : 0;
103 } catch ( \Throwable $th ) {
104 tutor_log( $th );
105 $per_earning_refund = 0;
106 }
107
108 // Split deduct amount fro admin & instructor.
109 $split_deduction = tutor_split_amounts( $per_earning_refund );
110
111 // Split earnings.
112 $split_earnings = tutor_split_amounts( $subtotal_price );
113
114 // Deduct earnings.
115 $admin_amount = $split_earnings['admin'] - $split_deduction['admin'];
116 $instructor_amount = $split_earnings['instructor'] - $split_deduction['instructor'];
117
118 $course_id = $item->id;
119
120 if ( OrderModel::TYPE_SINGLE_ORDER !== $order_details->order_type ) {
121 $plan_info = apply_filters( 'tutor_get_plan_info', null, $course_id );
122 if ( $plan_info && isset( $plan_info->is_membership_plan ) && $plan_info->is_membership_plan ) {
123 $course_id = null;
124 } else {
125 $course_id = apply_filters( 'tutor_subscription_course_by_plan', $item->id, $order_details );
126 }
127 }
128
129 $this->earning_data[] = $this->prepare_earning_data( $item_sold_price, $course_id, $order_id, $order_details->order_status, $admin_amount, $instructor_amount );
130 }
131 }
132 }
133
134 /**
135 * Prepare earning data
136 *
137 * @since 3.0.0
138 *
139 * @param mixed $total_price Total price of an item.
140 * @param int $course_id Connected course id.
141 * @param int $order_id Order id.
142 * @param string $order_status Order status.
143 * @param string $admin_amount Admin amount.
144 * @param string $instructor_amount Instructor status.
145 *
146 * @return array
147 */
148 public function prepare_earning_data( $total_price, $course_id, $order_id, $order_status, $admin_amount, $instructor_amount ) {
149 $fees_deduct_data = array();
150 $tutor_earning_fees = tutor_utils()->get_option( 'fee_amount_type' );
151 $enable_fees_deducting = tutor_utils()->get_option( 'enable_fees_deducting' );
152
153 $course_price_grand_total = $total_price;
154
155 // Site maintenance fees.
156 $fees_amount = 0;
157
158 // Deduct predefined amount (percent or fixed).
159 if ( $enable_fees_deducting ) {
160 $fees_name = tutor_utils()->get_option( 'fees_name', '' );
161 $fees_amount = (float) tutor_utils()->avalue_dot( 'fees_amount', $tutor_earning_fees );
162 $fees_type = tutor_utils()->avalue_dot( 'fees_type', $tutor_earning_fees );
163
164 if ( $fees_amount > 0 ) {
165 if ( 'percent' === $fees_type ) {
166 $fees_amount = ( $total_price * $fees_amount ) / 100;
167 }
168
169 $course_price_grand_total = max( $total_price - $fees_amount, 0 );
170 }
171
172 $fees_deduct_data = array(
173 'deduct_fees_amount' => $fees_amount,
174 'deduct_fees_name' => $fees_name,
175 'deduct_fees_type' => $fees_type,
176 );
177 }
178
179 if ( $fees_amount ) {
180 list( $admin_fees, $instructor_fees ) = array_values( tutor_split_amounts( $fees_amount ) );
181
182 // Deduct fees.
183 $admin_amount -= $admin_fees;
184 $instructor_amount -= $instructor_fees;
185 }
186
187 // Distribute amount between admin and instructor.
188 $sharing_enabled = tutor_utils()->get_option( 'enable_revenue_sharing' );
189 $instructor_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_instructor_commission' ) : 0;
190 $admin_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_admin_commission' ) : 100;
191 $commission_type = 'percent';
192
193 // Course author id.
194 $user_id = get_post_field( 'post_author', $course_id );
195
196 // (Use Pro Filter - Start)
197 // The response must be same array structure.
198 // Do not change used variable names here, or change in both of here and pro plugin
199 $pro_arg = array(
200 'user_id' => $user_id,
201 'instructor_rate' => $instructor_rate,
202 'admin_rate' => $admin_rate,
203 'instructor_amount' => max( 0, $instructor_amount ),
204 'admin_amount' => max( 0, $admin_amount ),
205 'course_price_grand_total' => $course_price_grand_total,
206 'commission_type' => $commission_type,
207 );
208
209 $pro_calculation = apply_filters( 'tutor_pro_earning_calculator', $pro_arg );
210 extract( $pro_calculation ); //phpcs:ignore
211 // (Use Pro Filter - End).
212
213 // Prepare insertable earning data.
214 $earning_data = array(
215 'user_id' => $user_id,
216 'course_id' => $course_id,
217 'order_id' => $order_id,
218 'order_status' => $order_status,
219 'course_price_total' => $total_price,
220 'course_price_grand_total' => $course_price_grand_total,
221
222 'instructor_amount' => $instructor_amount,
223 'instructor_rate' => $instructor_rate,
224 'admin_amount' => $admin_amount,
225 'admin_rate' => $admin_rate,
226
227 'commission_type' => $commission_type,
228 'process_by' => 'Tutor',
229 'created_at' => current_time( 'mysql', true ),
230 );
231 $earning_data = apply_filters( 'tutor_new_earning_data', array_merge( $earning_data, $fees_deduct_data ) );
232
233 return $earning_data;
234 }
235
236 /**
237 * Get order earnings
238 *
239 * @since 3.0.0
240 *
241 * @param int $order_id Order id.
242 *
243 * @return mixed Array of objects on success
244 */
245 public function get_order_earnings( int $order_id ) {
246 return QueryHelper::get_all(
247 $this->earning_table,
248 array( 'order_id' => $order_id ),
249 'earning_id'
250 );
251 }
252
253 /**
254 * Store earnings
255 *
256 * @since 3.0.0
257 *
258 * @throws \Exception If earning_data is empty.
259 *
260 * @return int On success inserted id will be returned
261 */
262 public function store_earnings() {
263 if ( empty( $this->earning_data ) ) {
264 throw new \Exception( self::INVALID_DATA_MSG );
265 }
266
267 $inserted_id = 0;
268 try {
269 foreach ( $this->earning_data as $earning ) {
270 $inserted_id = QueryHelper::insert( $this->earning_table, $earning );
271 }
272 } catch ( \Throwable $th ) {
273 throw new \Exception( $th->getMessage() );
274 }
275
276 return $inserted_id;
277 }
278
279 /**
280 * Check if earning for a order already exists
281 *
282 * @since 3.0.0
283 *
284 * @param int $order_id Order id.
285 *
286 * @return mixed Earning row if exists, false|null otherwise.
287 */
288 public function is_exist_order_earning( $order_id ) {
289 $row = QueryHelper::get_row(
290 $this->earning_table,
291 array(
292 'order_id' => $order_id,
293 ),
294 'earning_id'
295 );
296
297 return $row;
298 }
299
300 /**
301 * Update earning data
302 *
303 * Use prepare_order_earnings before updating
304 *
305 * @since 3.0.0
306 *
307 * @param int $earning_id Earning id.
308 *
309 * @throws \Exception If earning_data is empty.
310 *
311 * @return bool true|false
312 */
313 public function update_earning( $earning_id ) {
314 if ( empty( $this->earning_data ) ) {
315 throw new \Exception( self::INVALID_DATA_MSG );
316 }
317
318 $update = QueryHelper::update(
319 $this->earning_table,
320 $this->earning_data,
321 array( 'earning_id' => $earning_id )
322 );
323
324 if ( $update ) {
325 $this->earning_data = null;
326 }
327
328 return $update;
329 }
330
331 /**
332 * Delete earning
333 *
334 * @since 3.0.0
335 *
336 * @param int $earning_id Earning id.
337 *
338 * @return bool true|false
339 */
340 public function delete_earning( $earning_id ) {
341 return QueryHelper::delete(
342 $this->earning_table,
343 array( 'earning_id' => $earning_id )
344 );
345 }
346
347 /**
348 * Delete earning by order id
349 *
350 * @since 3.0.0
351 *
352 * @param int $order_id Order id.
353 *
354 * @return bool true|false
355 */
356 public function delete_earning_by_order( $order_id ) {
357 return QueryHelper::delete(
358 $this->earning_table,
359 array( 'order_id' => $order_id )
360 );
361 }
362
363 /**
364 * Before storing earning this method will check if
365 * earning exist for the given order id. If found it will
366 * remove then store.
367 *
368 * @since 3.0.0
369 *
370 * @throws \Exception If earning_data is empty.
371 *
372 * @return int On success inserted id will be returned
373 */
374 public function remove_before_store_earnings() {
375 if ( empty( $this->earning_data ) ) {
376 throw new \Exception( self::INVALID_DATA_MSG );
377 }
378
379 if ( $this->is_exist_order_earning( $this->order_id ) ) {
380 $this->delete_earning_by_order( $this->order_id );
381 }
382
383 try {
384 return $this->store_earnings();
385 } catch ( \Throwable $th ) {
386 tutor_log( $th );
387 }
388 }
389 }
390