Edit File: OrderService.php
<?php namespace App\Services\Api; use App\Enums\{NotificationTypeEnum, OrderPayStatus, OrderPayType, OrderStatusEnum, ProductDeliveryTypesEnum}; use App\Models\{CartItem, Order, ProductAddon}; use App\Notifications\OrderNotification; use App\Services\{BaseService, CouponService}; use App\Services\Chat\ChatService; use App\Traits\{CartOrderTrait, LogException, PaymentServicesTrait, SettingsTrait}; use Carbon\Carbon; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; class OrderService extends BaseService { use CartOrderTrait, SettingsTrait, LogException, PaymentServicesTrait; public function __construct(private ChatService $chatService) { parent::__construct(Order::class); } // for User order ONLY public function store($request) { $authUser = auth()->user(); $userCartItems = CartItem::with('product', 'provider')->where('user_id', $authUser->id)->get(); $total = $this->calculateCartSummation($userCartItems); $expiryPeriod = $this->getKeyFromSetting('minutes_for_order_approval'); $valueAddedTax = $this->getKeyFromSetting('vat_ratio'); $commissionRatioFromProviders = $this->getKeyFromSetting('commission_from_providers'); $taxValue = ($valueAddedTax / 100) * $total; $commissionValueFromProviders = ($commissionRatioFromProviders / 100) * $total; $min_delivery_price = $this->getKeyFromSetting('min_delivery_price'); $max_delivery_price = $this->getKeyFromSetting('max_delivery_price'); $discount = 0; // if order contain coupons if (isset($request['coupon_num'])) { $couponApply = $this->applyCoupon($commissionValueFromProviders, $request['coupon_num']); if (isset($couponApply['key']) && $couponApply['key'] == 'fail') { return $couponApply; } $request['coupon_type'] = $couponApply['coupon']['type']; $request['coupon_value'] = $couponApply['coupon']['discount']; $request['coupon_id'] = $couponApply['coupon']['id']; $request['coupon_amount'] = $couponApply['disc_amount']; // if coupon greater than admin provider commission then max discount in admin provider commission $discount = min($couponApply['disc_amount'], $commissionValueFromProviders); $total -= $discount; $commissionValueFromProviders -= $discount; } $isOrderByDelivery = $request['receiving_method'] == ProductDeliveryTypesEnum::Home->value; $commissionRatioFromDelegates = $this->getKeyFromSetting('commission_from_delegates'); $shippingCostPrice = 0; if ($isOrderByDelivery) { $shippingCostPrice = getDeliveryPrice( $authUser->lat, $authUser->lng, $userCartItems->first()->provider->lat, $userCartItems->first()->provider->lng )['price']; $shippingCostPrice = min(max($shippingCostPrice, $min_delivery_price), $max_delivery_price); } $commissionValueFromDelegates = ($commissionRatioFromDelegates / 100) * $shippingCostPrice; try { DB::beginTransaction(); $order = Order::create($request + [ 'expiry_period' => $expiryPeriod, 'expired_at' => now()->addMinutes($expiryPeriod), 'user_id' => $authUser->id, 'provider_id' => $userCartItems->first()->provider_id, 'city_id' => $authUser->city_id, 'lat' => $authUser->lat, 'lng' => $authUser->lng, 'map_desc' => $authUser->map_desc ?? null, 'neighborhood_id' => $authUser->neighborhood_to_delivered_id, 'total_products' => $total, 'vat_per' => $valueAddedTax, 'vat_amount' => $taxValue, 'delivery_price' => $isOrderByDelivery ? $shippingCostPrice : 0, 'final_total' => $total + $taxValue + $shippingCostPrice, 'provider_due_amount' => $total + $discount + $taxValue - $commissionValueFromProviders, // I add the discount to the provider because the management bears the discount 'delegate_due_amount' => $isOrderByDelivery ? ($shippingCostPrice - $commissionValueFromDelegates) : 0, 'commission_ratio_from_provider' => $commissionRatioFromProviders, 'commission_value_from_provider' => $commissionValueFromProviders, 'commission_ratio_from_delegate' => $isOrderByDelivery ? $commissionRatioFromDelegates : 0, 'commission_value_from_delegate' => $isOrderByDelivery ? $commissionValueFromDelegates : 0, 'admin_commission_value' => $commissionValueFromProviders + $commissionValueFromDelegates, ]); foreach ($userCartItems as $cartItem) { $totalItem = $cartItem->product->price_after_discount ?? $cartItem->product->price * $cartItem->quantity; $addonsPrices = isset($cartItem->product_addons) ? ProductAddon::whereIn('id', $cartItem->product_addons)->sum('price') : 0; $productData = [ 'product_id' => $cartItem->product_id, 'quantity' => $cartItem->quantity, 'unit_price' => $cartItem->product->price_after_discount ? $cartItem->product->price_after_discount : $cartItem->product->price, 'prices' => [ 'after' => $cartItem->product->price_after_discount, 'before' => $cartItem->product->price ], 'total' => $totalItem, 'total_incl_addons' => $totalItem + $addonsPrices, ]; $orderItem = $order->orderItems()->create($productData); // if products has addons if (isset($cartItem->product_addons)) { $addons = ProductAddon::whereIn('id', $cartItem->product_addons)->get(); $productAddons = []; foreach ($addons as $addon) { $productAddons[] = [ 'order_item_id' => $orderItem->id, 'product_addon_id' => $addon->id, 'unit_price' => $addon->price, 'quantity' => $cartItem->quantity, 'total' => $addon->price * $cartItem->quantity, ]; } if (!empty($productAddons)) { $orderItem->orderItemAddons()->createMany($productAddons); } } } CartItem::where('user_id', auth()->id())->delete(); if (isset($request['coupon_num']) && $order->coupon) { $order->coupon?->increment('use_times'); } $this->chatService->createPrivateRoom(creator: $authUser, type: 'order', memberable: $order->provider, order: $order); Notification::send($order->provider, new OrderNotification(order: $order, type: NotificationTypeEnum::New->value)); DB::commit(); return [ 'key' => 'success', 'msg' => __('apis.user_order.order_placed'), 'order_id' => $order->id ]; } catch (\Exception $e) { DB::rollBack(); Log::channel('order_logs')->critical("File:" . $e->getFile() . "Line:" . $e->getLine() . "Message:" . $e->getMessage()); return [ 'key' => 'fail', 'msg' => __('apis.user_order.order_failed'), // 'msg' => $e->getMessage() ]; } } // for User order ONLY public function orderSummary($request) { $userCartItems = CartItem::with('product', 'provider')->where('user_id', auth()->id())->get(); $min_delivery_price = $this->getKeyFromSetting('min_delivery_price'); $max_delivery_price = $this->getKeyFromSetting('max_delivery_price'); if ($userCartItems->count() == 0) { return [ 'key' => 'fail', 'msg' => __('apis.user_cart.empty_cart'), ]; } $total = $this->calculateCartSummation($userCartItems); $authUser = auth()->user(); $valueAddedTax = $this->getKeyFromSetting('vat_ratio'); $taxValue = ($valueAddedTax / 100) * $total; // if order contain coupons if (isset($request['coupon_num'])) { $commissionRatioFromProviders = $this->getKeyFromSetting('commission_from_providers'); $commissionValueFromProviders = ($commissionRatioFromProviders / 100) * $total; $couponApply = $this->applyCoupon($commissionValueFromProviders, $request['coupon_num']); if (isset($couponApply['key']) && $couponApply['key'] == 'fail') { return $couponApply; } $request['coupon_type'] = $couponApply['coupon']['type']; $request['coupon_value'] = $couponApply['coupon']['discount']; $request['coupon_id'] = $couponApply['coupon']['id']; $request['coupon_amount'] = $couponApply['disc_amount']; // if coupon greater than admin provider commission then max discount in admin provider commission $discount = min($couponApply['disc_amount'], $commissionValueFromProviders); $total -= $discount; } $shippingCostPrice = 0; if ($request['receiving_method'] == ProductDeliveryTypesEnum::Home->value) { $shippingCostPrice = getDeliveryPrice( $authUser->lat, $authUser->lng, $userCartItems->first()->provider->lat, $userCartItems->first()->provider->lng )['price']; $shippingCostPrice = min(max($shippingCostPrice, $min_delivery_price), $max_delivery_price); } $coupon_discount_amount = isset($request['coupon_num']) && isset($couponApply['disc_amount']) ? $couponApply['disc_amount'] : 0; $final_total_before_discount = $total + $taxValue + $shippingCostPrice + $coupon_discount_amount; $orderSummary = [ 'delivery_method' => __('order.receiving_method.' . ProductDeliveryTypesEnum::from((int)$request['receiving_method'])->name), 'user_location' => $request['receiving_method'] == ProductDeliveryTypesEnum::Home->value ? auth()->user()->map_desc : null, 'total_products_before_discount' => isset($request['coupon_num']) && isset($couponApply['disc_amount']) ? number_format($total + $couponApply['disc_amount'], 2) : null, 'total_products' => number_format($total, 2), 'coupon_discount' => number_format($coupon_discount_amount, 2), 'vat_per' => number_format($valueAddedTax, 2), 'vat_amount' => number_format($taxValue, 2), 'delivery_price' => number_format($shippingCostPrice, 2), 'final_total' => number_format($total + $taxValue + $shippingCostPrice, 2), 'final_total_before_discount' => number_format($final_total_before_discount, 2) ]; return [ 'key' => 'success', 'msg' => __('apis.user_order.summary'), 'summary' => $orderSummary, ]; } // for User order ONLY public function payInvoice($order_id, $request): array { try { $order = $this->model::find(id: $order_id); if ($order->pay_status == OrderPayStatus::PAID->value) { return ['key' => 'fail', 'msg' => __('apis.user_order.paid_order_already')]; } $payResult = match ((int)$request->pay_type) { OrderPayType::WALLET->value => $this->payWithWallet(user: auth()->user(), order: $order), OrderPayType::CASH->value => $this->payWithCash(order: $order), OrderPayType::ONLINE->value => $this->payWithOnline(order: $order, request: $request), OrderPayType::APPLE_PAY->value => $this->payWithApplePay(order: $order, request: $request), }; if ($payResult['key'] == 'success' && $order->pay_type != OrderPayType::CASH->value) { $order->update(['pay_status' => OrderPayStatus::PAID->value]); } return ['key' => $payResult['key'], 'msg' => $payResult['msg'], 'data' => $payResult['data']]; } catch (\Exception $exception) { return ['key' => 'fail', 'msg' => __('apis.not_found'), 'data' => []]; } } // for User order ONLY private function applyCoupon($total, $couponNum) { $coupon = (new CouponService())->checkCoupon($couponNum, $total); return $coupon['key'] == 'fail' ? $coupon : $coupon['data']; } public function getIgnoredOrderForDelegate($paginateNum = 15) { $orders = auth()->user()->ignoredOrders()->paginate($paginateNum); return $orders; } /** * Get orders according to the given statuses * [ This function will used for provider, delegate and user ] * * @param array $statuses * * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ public function getOrderAccordingToStatus($user, array $statuses, array $with = [], $conditions = [], $paginateNum = 15) { return Order::with($with) ->where($user, auth()->id()) ->whereIn('status', $statuses) ->where($conditions) ->with('productImages') // To Display orders in current for delegate after accept // ->when( // $user === 'delegate_id' && !empty($statuses), // fn($q) => $q->orWhere([['delegate_id', auth()->id()], ['status', OrderStatusEnum::Prepared->value]]) // ) ->latest() ->paginate($paginateNum); } public function getOrderAccordingToStatusInDashboard(array $statuses, array $with = [], $conditions = []) { return Order::with($with) ->when(count($statuses), fn($q) => $q->whereIn('status', $statuses)) ->where($conditions) ->latest() ->paginate(15); } public function detailsForDashboard($id, array $with = []) { return Order::with($with)->where(['id' => $id])->first(); } public function getNearestOrders($user, array $statuses, array $with = [], $conditions = []): array { $user = auth()->user(); $authDelegatesNeighborhoods = $user->neighborhoods->pluck('id')->toArray(); $lat = $user?->lat ?? request('lat') ?? 31.0409; $lng = $user?->lng ?? request('lng') ?? 31.3786; $orders = Order::with($with) ->whereNotIn('id', $user->ignoredOrders->pluck('id')->toArray()) ->whereIn('status', $statuses) ->where($conditions) ->whereIn('neighborhood_id', $authDelegatesNeighborhoods) ->select( '*', DB::raw("6371 * acos(cos(radians(" . ($lat) . ")) * cos(radians(lat)) * cos(radians(lng) - radians(" . ($lng) . ")) + sin(radians(" . ($lat) . ")) * sin(radians(lat))) AS distance") ) ->orderByRaw("distance ASC") ->orderBy('created_at', 'desc') ->paginate(25); return ['key' => 'success', 'orders' => $orders, 'msg' => __('apis.success')]; } public function details($id, $user = null, array $with = [], $conditions = []) { return Order::with($with) ->when(isset($user), fn($q) => $q->where([$user => auth()->id()])) ->where('id', $id) ->when(!empty($conditions), fn($q) => $q->where($conditions))->firstOrFail(); } public function detailsForDelegates($id) { $order = Order::with([ 'orderItems.orderItemAddons.addon', 'user', 'provider', 'cancellationReason', 'rates', 'rates.ratedable', ])->where(function ($query) use ($id) { $query->where('id', $id) ->where(function ($query) { $query->where(function ($query) { $query->whereNull('delegate_id') // Orders not assigned to any delegate ->whereIn('neighborhood_id', auth()->user()->neighborhoods->pluck('id')->toArray()); // Orders in the delegate's neighborhoods })->orWhere(function ($query) { $query->where('delegate_id', auth('delegate')->id()); // Orders assigned to the delegate }); }); })->first(); if (!$order) { return ['key' => 'fail', 'msg' => __('apis.not_found'), 'data' => []]; } return ['key' => 'success', 'msg' => __('apis.success'), 'data' => $order]; } public function displayOrderByOrderNumber($order_num, $conditions = []) { return Order::where('order_num', $order_num) ->when(!empty($conditions), fn($q) => $q->where($conditions))->first(); } public function updateOrder($order, $updated_data): array { $order->update($updated_data); return [ 'key' => 'success', 'msg' => __('order.accept_order_success'), 'order' => $order->refresh() ]; } public function delegateIgnoreOrder($delegate_id, $order_id): array { $isCurrentOrder = Order::where('id', $order_id)->where('status', OrderStatusEnum::Prepared->value)->first(); if (!$isCurrentOrder || $isCurrentOrder->delegate_id == auth()->user()->id) { return ['key' => 'fail', 'msg' => __('order.can_not_ignore_order')]; } $order = auth()->user()->ignoredOrders()->where('order_id', $order_id)->exists(); if (!$order) { auth()->user()->ignoredOrders()->attach($order_id); } return ['key' => 'success', 'msg' => __('order.ignored_success')]; } /** * Change order status [ This function will used for provider, delegate and user ] * * @param \Illuminate\Http\Request $request * * @return array */ public function changeOrderStatus($request): array { $request->order->update([ 'status' => $request->status, 'cancellation_reason_id' => $request->cancellation_reason_id ?? null, ]); return ['key' => 'success', 'msg' => __('apis.status_changed'), 'row' => $request->order->refresh()]; } public function showUnAssignedDelegatesOrdersInDashboard($id) { return $this->model::with(['provider', 'user'])->where([ ['status', OrderStatusEnum::Prepared], [ 'delegate_id', null, ], ['receiving_method', ProductDeliveryTypesEnum::Home->value], ])->findOrFail($id); } // get completed orders statistics for Provider public function completedOrdersStatistics() { $completedOrdersStatuses = [ OrderStatusEnum::Provider_delivered_to_client->value, OrderStatusEnum::Client_delivered->value, ]; return Order::select([ DB::raw('COUNT(CASE WHEN status IN (' . implode(',', $completedOrdersStatuses) . ') THEN 1 END) AS completed_orders_today_count'), DB::raw('SUM(CASE WHEN status IN (' . implode(',', $completedOrdersStatuses) . ') THEN final_total ELSE 0 END) AS sum_final_total_today') ])->where('provider_id', auth()->id())->whereDate('created_at', Carbon::today())->first(); } }
Back to File Manager