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