PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.4.0
Tutor LMS – eLearning and online course solution v3.4.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
389 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\Helpers\QueryHelper;
14 use Tutor\Models\OrderModel;
15 use TUTOR\Singleton;
16 use Tutor\Traits\EarningData;
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 = ( $deducted_amount * $subtotal_price ) / $order_details->subtotal_price;
101 } catch ( \Throwable $th ) {
102 tutor_log( $th );
103 $per_earning_refund = 0;
104 }
105
106 // Split deduct amount fro admin & instructor.
107 $split_deduction = tutor_split_amounts( $per_earning_refund );
108
109 // Split earnings.
110 $split_earnings = tutor_split_amounts( $subtotal_price );
111
112 // Deduct earnings.
113 $admin_amount = $split_earnings['admin'] - $split_deduction['admin'];
114 $instructor_amount = $split_earnings['instructor'] - $split_deduction['instructor'];
115
116 $course_id = $item->id;
117
118 if ( OrderModel::TYPE_SINGLE_ORDER !== $order_details->order_type ) {
119 $plan_info = apply_filters( 'tutor_get_plan_info', new \stdClass(), $course_id );
120 if ( $plan_info && isset( $plan_info->is_membership_plan ) && $plan_info->is_membership_plan ) {
121 $course_id = null;
122 } else {
123 $course_id = apply_filters( 'tutor_subscription_course_by_plan', $item->id, $order_details );
124 }
125 }
126
127 $this->earning_data[] = $this->prepare_earning_data( $item_sold_price, $course_id, $order_id, $order_details->order_status, $admin_amount, $instructor_amount );
128 }
129 }
130 }
131
132 /**
133 * Prepare earning data
134 *
135 * @since 3.0.0
136 *
137 * @param mixed $total_price Total price of an item.
138 * @param int $course_id Connected course id.
139 * @param int $order_id Order id.
140 * @param string $order_status Order status.
141 * @param string $admin_amount Admin amount.
142 * @param string $instructor_amount Instructor status.
143 *
144 * @return array
145 */
146 public function prepare_earning_data( $total_price, $course_id, $order_id, $order_status, $admin_amount, $instructor_amount ) {
147 $fees_deduct_data = array();
148 $tutor_earning_fees = tutor_utils()->get_option( 'fee_amount_type' );
149 $enable_fees_deducting = tutor_utils()->get_option( 'enable_fees_deducting' );
150
151 $course_price_grand_total = $total_price;
152
153 // Site maintenance fees.
154 $fees_amount = 0;
155
156 // Deduct predefined amount (percent or fixed).
157 if ( $enable_fees_deducting ) {
158 $fees_name = tutor_utils()->get_option( 'fees_name', '' );
159 $fees_amount = (float) tutor_utils()->avalue_dot( 'fees_amount', $tutor_earning_fees );
160 $fees_type = tutor_utils()->avalue_dot( 'fees_type', $tutor_earning_fees );
161
162 if ( $fees_amount > 0 ) {
163 if ( 'percent' === $fees_type ) {
164 $fees_amount = ( $total_price * $fees_amount ) / 100;
165 }
166
167 $course_price_grand_total = $total_price - $fees_amount;
168 }
169
170 $fees_deduct_data = array(
171 'deduct_fees_amount' => $fees_amount,
172 'deduct_fees_name' => $fees_name,
173 'deduct_fees_type' => $fees_type,
174 );
175 }
176
177 if ( $fees_amount ) {
178 list( $admin_fees, $instructor_fees ) = array_values( tutor_split_amounts( $fees_amount ) );
179
180 // Deduct fees.
181 $admin_amount -= $admin_fees;
182 $instructor_amount -= $instructor_fees;
183 }
184
185 // Distribute amount between admin and instructor.
186 $sharing_enabled = tutor_utils()->get_option( 'enable_revenue_sharing' );
187 $instructor_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_instructor_commission' ) : 0;
188 $admin_rate = $sharing_enabled ? tutor_utils()->get_option( 'earning_admin_commission' ) : 100;
189 $commission_type = 'percent';
190
191 // Course author id.
192 $user_id = get_post_field( 'post_author', $course_id );
193
194 // (Use Pro Filter - Start)
195 // The response must be same array structure.
196 // Do not change used variable names here, or change in both of here and pro plugin
197 $pro_arg = array(
198 'user_id' => $user_id,
199 'instructor_rate' => $instructor_rate,
200 'admin_rate' => $admin_rate,
201 'instructor_amount' => max( 0, $instructor_amount ),
202 'admin_amount' => max( 0, $admin_amount ),
203 'course_price_grand_total' => $course_price_grand_total,
204 'commission_type' => $commission_type,
205 );
206
207 $pro_calculation = apply_filters( 'tutor_pro_earning_calculator', $pro_arg );
208 extract( $pro_calculation ); //phpcs:ignore
209 // (Use Pro Filter - End).
210
211 // Prepare insertable earning data.
212 $earning_data = array(
213 'user_id' => $user_id,
214 'course_id' => $course_id,
215 'order_id' => $order_id,
216 'order_status' => $order_status,
217 'course_price_total' => $total_price,
218 'course_price_grand_total' => $course_price_grand_total,
219
220 'instructor_amount' => $instructor_amount,
221 'instructor_rate' => $instructor_rate,
222 'admin_amount' => $admin_amount,
223 'admin_rate' => $admin_rate,
224
225 'commission_type' => $commission_type,
226 'process_by' => 'Tutor',
227 'created_at' => current_time( 'mysql', true ),
228 );
229 $earning_data = apply_filters( 'tutor_new_earning_data', array_merge( $earning_data, $fees_deduct_data ) );
230
231 return $earning_data;
232 }
233
234 /**
235 * Get order earnings
236 *
237 * @since 3.0.0
238 *
239 * @param int $order_id Order id.
240 *
241 * @return mixed Array of objects on success
242 */
243 public function get_order_earnings( int $order_id ) {
244 return QueryHelper::get_all(
245 $this->earning_table,
246 array( 'order_id' => $order_id ),
247 'earning_id'
248 );
249 }
250
251 /**
252 * Store earnings
253 *
254 * @since 3.0.0
255 *
256 * @throws \Exception If earning_data is empty.
257 *
258 * @return int On success inserted id will be returned
259 */
260 public function store_earnings() {
261 if ( empty( $this->earning_data ) ) {
262 throw new \Exception( self::INVALID_DATA_MSG );
263 }
264
265 $inserted_id = 0;
266 try {
267 foreach ( $this->earning_data as $earning ) {
268 $inserted_id = QueryHelper::insert( $this->earning_table, $earning );
269 }
270 } catch ( \Throwable $th ) {
271 throw new \Exception( $th->getMessage() );
272 }
273
274 return $inserted_id;
275 }
276
277 /**
278 * Check if earning for a order already exists
279 *
280 * @since 3.0.0
281 *
282 * @param int $order_id Order id.
283 *
284 * @return mixed Earning row if exists, false|null otherwise.
285 */
286 public function is_exist_order_earning( $order_id ) {
287 $row = QueryHelper::get_row(
288 $this->earning_table,
289 array(
290 'order_id' => $order_id,
291 ),
292 'earning_id'
293 );
294
295 return $row;
296 }
297
298 /**
299 * Update earning data
300 *
301 * Use prepare_order_earnings before updating
302 *
303 * @since 3.0.0
304 *
305 * @param int $earning_id Earning id.
306 *
307 * @throws \Exception If earning_data is empty.
308 *
309 * @return bool true|false
310 */
311 public function update_earning( $earning_id ) {
312 if ( empty( $this->earning_data ) ) {
313 throw new \Exception( self::INVALID_DATA_MSG );
314 }
315
316 $update = QueryHelper::update(
317 $this->earning_table,
318 $this->earning_data,
319 array( 'earning_id' => $earning_id )
320 );
321
322 if ( $update ) {
323 $this->earning_data = null;
324 }
325
326 return $update;
327 }
328
329 /**
330 * Delete earning
331 *
332 * @since 3.0.0
333 *
334 * @param int $earning_id Earning id.
335 *
336 * @return bool true|false
337 */
338 public function delete_earning( $earning_id ) {
339 return QueryHelper::delete(
340 $this->earning_table,
341 array( 'earning_id' => $earning_id )
342 );
343 }
344
345 /**
346 * Delete earning by order id
347 *
348 * @since 3.0.0
349 *
350 * @param int $order_id Order id.
351 *
352 * @return bool true|false
353 */
354 public function delete_earning_by_order( $order_id ) {
355 return QueryHelper::delete(
356 $this->earning_table,
357 array( 'order_id' => $order_id )
358 );
359 }
360
361 /**
362 * Before storing earning this method will check if
363 * earning exist for the given order id. If found it will
364 * remove then store.
365 *
366 * @since 3.0.0
367 *
368 * @throws \Exception If earning_data is empty.
369 *
370 * @return int On success inserted id will be returned
371 */
372 public function remove_before_store_earnings() {
373 if ( empty( $this->earning_data ) ) {
374 throw new \Exception( self::INVALID_DATA_MSG );
375 }
376
377 if ( $this->is_exist_order_earning( $this->order_id ) ) {
378 $this->delete_earning_by_order( $this->order_id );
379 }
380
381 try {
382 return $this->store_earnings();
383 } catch ( \Throwable $th ) {
384 tutor_log( $th );
385 }
386 }
387
388 }
389