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