'integer', ]; public function __construct(array $attributes = []) { parent::__construct($attributes); $this->table = acadlix()->helper()->acadlix_table_prefix('orders'); } public function setExtraChargesAttribute($value) { $this->attributes['extra_charges'] = acadlix()->helper()->acadlix_format_price_for_storage($value); } public function getExtraChargesAttribute($value) { return acadlix()->helper()->acadlix_format_price_for_display($value); } public function setTotalAmountAttribute($value) { $this->attributes['total_amount'] = acadlix()->helper()->acadlix_format_price_for_storage($value); } public function getTotalAmountAttribute($value) { return acadlix()->helper()->acadlix_format_price_for_display($value); } public function scopeOfSuccess($query) { return $query->where('status', self::STATUS_SUCCESS); } public function order_items() { return $this->hasMany(acadlix()->model()->orderItem(), 'order_id', 'id'); } public function getCourseNames() { return $this ->order_items ->map(fn($item) => $item->item->post_title ?? $item->item_title ?? '') ->filter() ->implode(', '); } public function order_metas() { return $this->hasMany(acadlix()->model()->orderMeta(), 'order_id', 'id'); } public function activity_logs() { return $this ->hasMany(acadlix()->model()->userActivityMeta(), 'type_id', 'id') ->where('type', 'order') ->where('meta_key', 'activity_log') ->orderBy('id', 'desc'); } public function getMetaValue($key) { $meta = $this->order_metas()->where('meta_key', $key)->first(); // Check if the meta exists and return its value or null return $meta?->meta_value ?? null; } public function updateStatus($status = self::STATUS_PENDING) { return $this->update(['status' => $status]); } public function updateOrCreateMeta($metaKey, $metaValue) { return $this->order_metas()->updateOrCreate( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Its custom order meta table ['meta_key' => $metaKey], // Condition to check for existing meta_key // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Its custom order meta table ['meta_value' => $metaValue] // Values to update or create ); } public function createActivityLog($message = '') { return acadlix()->model()->userActivityMeta()->create([ 'user_id' => $this->user_id ?? 0, 'type' => 'order', 'type_id' => $this->id, 'meta_key' => 'activity_log', // phpcs:ignore 'meta_value' => $message // phpcs:ignore ]); } public function user() { return $this->belongsTo(acadlix()->model()->wpUsers(), 'user_id', 'ID'); } public function delete() { // Delete associated order items and their metas acadlix() ->model() ->enrollmentSource() ->revokeAccess('order', $this->id); acadlix() ->model() ->userActivityMeta() ->where('type', 'order') ->where('type_id', $this->id) ->delete(); // check if payment if offline to delete uploaded files $payment_method = $this->getMetaValue('payment_method'); if ($payment_method === 'offline') { // Add logic to delete uploaded files related to offline payment $uploaded_files = $this->getMetaValue('offline_upload_file'); if ($uploaded_files) { $file_path = $uploaded_files['file_path'] ?? ''; $delete_token = $uploaded_files['delete_token'] ?? null; $options = [ 'verify_token' => (bool) $delete_token, 'enforce_user' => (bool) $delete_token, 'allowed_subdir' => '/acadlix-offline-payments', ]; if (file_exists($file_path)) { $delete_result = acadlix()->helper()->acadlix_delete_file_from_wordpress( $file_path, $delete_token, $options ); if (is_wp_error($delete_result)) { // Handle the error if needed, e.g., log it // return $delete_result; } } } } return parent::delete(); } public function pending_order() { if ($this->status === self::STATUS_PENDING) { return $this; } $this->updateStatus(self::STATUS_PENDING); // unenroll user from purchased courses acadlix() ->model() ->enrollmentSource() ->revokeAccess('order', $this->id); return $this; } protected function enrollItem($item, $order, $via = 'user', $viaId = 0) { if ($item->type === 'course') { $course = acadlix()->model()->course()->find($item->item_id); if (!$course) { // Cast IDs to int before interpolation so the message can't carry unescaped/unsafe data throw new \Exception('Course not found for Order Item ID ' . (int) $item->id . ' with Course ID ' . (int) $item->item_id); } $courseSectionContentId = $course->getCurrentCourseContentId($order->user_id); acadlix() ->model() ->courseEnrollment() ->enroll( $order->user_id, $item->item_id, [ 'source_type' => 'order', 'source_id' => $item->order_id, 'type' => $item->type, 'item_id' => $item->item_id, 'via' => $via ?? 'user', 'via_id' => $viaId > 0 ? $viaId : $order->user_id, 'course_section_content_id' => $courseSectionContentId, ] ); } } public function success_order($via = 'user', $viaId = 0) { if ($this->status === self::STATUS_SUCCESS) { return $this; } $this->updateStatus(self::STATUS_SUCCESS); // enroll user to purchased courses $this->order_items->each(function ($item) use ($via, $viaId) { // 1. If it's a native course, handle it directly in Free $this->enrollItem($item, $this, $via, $viaId); /** * 2. Extensibility Hook for Anything Else (Pro Features) * If the item is NOT a course (e.g., 'bundle', 'membership', 'download'), * we pass it to this hook so Pro addons can process it. */ do_action('acadlix_order_success_order_item', $item, $this, $via, $viaId); }); // Universal hook after order processing finishes do_action('acadlix_order_success_order_complete', $this, $via, $viaId); // send mail on success acadlix()->email()->send( acadlix()->email()->blueprint()->coursePurchase() ->create() ->order($this) ); return $this; } public function failed_order() { if ($this->status === self::STATUS_FAILED) { return $this; } $this->updateStatus(self::STATUS_FAILED); // unenroll user from purchased courses acadlix() ->model() ->enrollmentSource() ->revokeAccess('order', $this->id); // send mail on failed acadlix()->email()->send( acadlix()->email()->blueprint()->failedTransaction() ->create() ->order($this) ); return $this; } public function getTodaySalesTotal() { return $this ->whereDate('created_at', Carbon::today()) ->ofSuccess() ->sum('total_amount'); } public function getTotalSales() { return $this ->ofSuccess() ->sum('total_amount'); } } }