get(); $orders = $this->filterOrders($request); $orders = $orders->paginate(10); return view('bo.orders.index', compact('orders')); } public function today(SortOrders $request) { //$orders = Order::orderBy('date', 'DESC')->get(); $orders = $this->filterOrders($request); //filter today $today = CarbonImmutable::now()->startOfDay(); $orders = $orders->whereHas('timeslot', function ($q) use ($today) { $q->where('day', '=', $today->format('d/m/Y')); }); $orders = $orders->get(); //paginate(20); return view('bo.orders.index', compact('orders', 'today')); } protected function filterOrders(Request $request) { $orders = Order::query(); if ($request->rst) { Session::remove('orders.sort_col'); Session::remove('orders.sort_way'); } if ($request->sort_col) { Session::put('orders.sort_col', $request->sort_col); Session::save(); } if ($request->sort_way) { Session::put('orders.sort_way', $request->sort_way); Session::save(); } if (Session::has('orders.sort_col') and Session::has('orders.sort_way')) { switch (Session::get('orders.sort_col')) { case 'date': $orders = $orders->orderBy('date', Session::get('orders.sort_way')); break; case 'status_id': $orders = $orders->orderBy('status_id', Session::get('orders.sort_way')); break; case 'magasin_id': $orders = $orders->select('orders.*'); $orders = $orders->leftJoin('timeslots', 'timeslots.order_id', '=', 'orders.id'); $orders = $orders->leftJoin('magasins', 'timeslots.magasin_id', '=', 'magasins.id'); $orders = $orders->orderBy('magasins.id', Session::get('orders.sort_way')); break; case 'date_retrait': $orders = $orders->select('orders.*'); $orders = $orders->leftJoin('timeslots', 'timeslots.order_id', '=', 'orders.id'); $orders = $orders->orderBy('timeslots.day', Session::get('orders.sort_way')); $orders = $orders->orderBy('timeslots.time', Session::get('orders.sort_way')); break; case 'customer_id': $orders = $orders->orderBy('customer_id', Session::get('orders.sort_way')); break; } } return $orders; } /** * action de changement de statut vers l'avant * envoi de mails transctionnels * @param String $reference reference de la commande */ public function changeStatus($reference) { $order = Order::where('reference', $reference)->first(); $order->status_id = $order->status_id + 1; $order->save(); //send confirm mail Mail::to($order->customer->email)->send(new OrderStatusMail($order)); //return redirect()->route('bo.orders')->with('success', "Changement de status enregistré pour la commande " . $order->reference . "."); return back()->with('success', "Changement de status enregistré pour la commande " . $order->reference . "."); } /** * mise au statut annulé d'une commande */ public function cancelOrder($reference) { $order = Order::where('reference', $reference)->first(); $order->status_id = 7; $order->save(); //return redirect()->route('bo.orders')->with('success', "La commande à été annulée."); return back()->with('success', "La commande " . $order->reference . " à été annulée."); } /** * @deprecated * ajout de produits à la commande * @todo : supprimer cette méthode qui repose sur un modèle de donnée osolète */ public function addProduct($orderReference, $productId, $quantity) { $product = Product::findOrFail($productId); $order = Order::where('reference', $orderReference)->first(); $order->total = $order->total + (($product->price - $product->price_buy) * $quantity); $orderProduct = new OrdersProducts(); $orderProduct->product_id = $product->id; $orderProduct->order_id = $order->id; $orderProduct->quantity = $quantity; $orderProduct->save(); return redirect()->route('bo.orders')->with('success', "Le produit à été ajouté."); } /** * front * récap panier du tunnel de commande * @todo : déplacer vers le controller idoine */ public function recapOrder() { $activestep = 2; Cart::instance('panier'); $cart = Cart::content(); $cartInfos = array( "totalTTC" => Cart::total(), 'totalTVA' => Cart::tax(), //total des taxes 'totalHT' => Cart::subtotal(), //total sans les taxes 'totalweight' => Cart::weight(), //poids total des items 'totalHTbefore' => Cart::priceTotal(), //total avant taxes et remises 'totaldiscount' => Cart::discount(), //total des remises 'countItems' => Cart::count(), //nb de produits dans le cart ); return view('sales.recap', compact('activestep', 'cart', 'cartInfos')); } /** * front * Storing order from sales tunnel */ public function store() { //save Cart::instance('panier'); $order = new Order(); //generate order ref $order->reference = $this->generateOrderRef(); $order->date = Carbon::now(); $order->status_id = 1; $order->total = str_replace(',', '.', str_replace(' ', '', Cart::total())); $order->customer_id = Auth::user()->id; $order->save(); //order_id Cart::instance('panier'); $cart = Cart::content(); foreach ($cart as $cartItem) { $order_line = new OrderLine(); $order_line->order_id = $order->id; $order_line->product_id = $cartItem->model->id; $order_line->quantity = $cartItem->qty; //gross unit price $order_line->unit_price = $cartItem->model->priceHt; //discount per unit $order_line->unit_discount = $cartItem->model->discountAmount; //tax rate for this line $order_line->tax_rate = $cartItem->taxRate; //tax amount par unit $order_line->unit_tax = round(( ($order_line->unit_price - $order_line->unit_discount) * ($order_line->tax_rate / 100) ) * 100) / 100; //discount amount for the line $order_line->total_discount = $order_line->unit_discount * $order_line->quantity; //gross amount for the line $order_line->total_ht = ($order_line->unit_price - $order_line->unit_discount) * $order_line->quantity; //tax amount for the line $order_line->total_tax = $order_line->unit_tax * $order_line->quantity; //total TTC avec remises de la ligne $order_line->total_ttc = $order_line->total_ht + $order_line->total_tax; $order_line->save(); } //empty cart Cart::destroy(); //then redirect return redirect()->route('sales.pick-slot', ['reference' => $order->reference]); } /** * generate order Reference * for a new order */ public function generateOrderRef() { //Avant //Str::uuid(); //maintenant //format : YYYYMMDDHHMM000 $now = Carbon::now(); //count todays orders $countorders = Order::whereDate('created_at', Carbon::today())->count(); //add one $countorders++; $ref = $now->isoFormat('YYYYMMDDHHmm') . str_pad($countorders, 3, "0", STR_PAD_LEFT); return $ref; } /** * front tunnel de commande étape 3 : choix de créneau */ public function timeSlotPick($reference) { $activestep = 3; if (config('theme.multi_relay')) { $magasin = Magasin::all(); } else { $magasin = Magasin::first(); } $availCreneaux = array(); //plus besoin car chargé par le composant vue. //$availCreneaux = $this->getAvailableCreneauxByShop($magasin->id); $order = Order::where('reference', '=', $reference)->firstOrFail(); return view('sales.pick-slot', compact('activestep', 'availCreneaux', 'order', 'magasin')); } /** * @return mixed */ public function getAvailableCreneauxByShop($id) { //setting à stocker et réutiliser $plage_days = Setting::where('key', 'order_withdrawal_period')->firstOrFail()->value; $magasin = Magasin::findOrFail($id); $timeslotSettings = $magasin->timeslotSettings; //$timeslotSettings = TimeslotSetting::all(); //fabriquons un tableau recap $plage = array(); foreach ($timeslotSettings as $day) { $plage[$day->day_index] = array( 'morning_start' => $day->morning_start, 'morning_end' => $day->morning_end, 'afternoon_start' => $day->afternoon_start, 'afternoon_end' => $day->afternoon_end, 'interval' => $day->interval, 'min_order_preparation' => $day->min_order_preparation, 'max_orders_per_interval' => $day->max_orders_per_interval ); } //génération des plages //start today $today = CarbonImmutable::now()->startOfDay(); $weekDay = $today->weekday(); //final boundary (permet de limiter la liste des jours d'exception) $finalday = $today->add($plage_days, 'day'); $now = CarbonImmutable::now(); //jours exceptions // $noCollectDays = CollecteException::where('magasin_id',$magasin->id) // ->whereDate('date', '>', $now) // ->whereDate('date', '<=', $finalday) // ->orderBy('date')->get(); $noCollectDays = $magasin->collecteExceptions() ->whereDate('date', '>', $now) ->whereDate('date', '<=', $finalday) ->orderBy('date')->get(); $delaiPrepa = CarbonInterval::createFromFormat('h:i', $plage[$weekDay]['min_order_preparation'] ?? '01:00'); $availCreneaux = array(); for ($i = 0; $i < $plage_days; $i++) { /** @var Carbon */ $current = $today->add($i, 'day'); $weekDay = $current->weekday(); $interval = CarbonInterval::createFromFormat('h:i', $plage[$weekDay]['interval'] ?? '00:15'); //si c'est un jour exception skip $skip = false; foreach ($noCollectDays as $not_today) { if ($current->isSameDay($not_today->date)) { $skip = true; } } if ($skip) { continue; } //matinée if (!empty($plage[$weekDay]['morning_start'])) { $currentTime = Carbon::createFromTimeString($plage[$weekDay]['morning_start']); $endTime = Carbon::createFromTimeString($plage[$weekDay]['morning_end']); do { $creneau = $currentTime->format('H:i'); $nbtakens = $magasin->timeslots() ->where('day', $current->format('d/m/Y')) ->where('time', $creneau)->count(); // $nbtakens = Timeslot::where('day', $current->format('d/m/Y')) // ->where('time', $creneau)->count(); if ($nbtakens >= $plage[$weekDay]['max_orders_per_interval']) { $isDispo = false; } else { $isDispo = true; } if ($current->addHours($currentTime->format('H'))->addMinutes($currentTime->format('i')) < $now->add($delaiPrepa)) { $isDispo = false; } //attention si la date est passée (today mais avant maintenant) : pas de créneau if ($current > $today || $currentTime >= $now) { $availCreneaux[$current->format('d/m/Y')][] = array($creneau,$isDispo); } $currentTime->add($interval); } while ($currentTime <= $endTime); } //aprem if (!empty($plage[$weekDay]['afternoon_start'])) { $currentTime = Carbon::createFromTimeString($plage[$weekDay]['afternoon_start']); $endTime = Carbon::createFromTimeString($plage[$weekDay]['afternoon_end']); do { $creneau = $currentTime->format('H:i'); $nbtakens = Timeslot::where('day', $current->format('d/m/Y'))->where('time', $creneau)->count(); if ($nbtakens >= $plage[$weekDay]['max_orders_per_interval']) { $isDispo = false; } else { $isDispo = true; } if ($current->addHours($currentTime->format('H'))->addMinutes($currentTime->format('i')) < $now->add($delaiPrepa)) { $isDispo = false; } //attention si la date est passée (today mais avant maintenant) : pas de créneau if ($current > $today || $currentTime >= $now) { $availCreneaux[$current->format('d/m/Y')][] = array($creneau,$isDispo); } $currentTime->add($interval); } while ($currentTime <= $endTime); } } return $availCreneaux; } //call ajax pour le composant vue de timeslots public function getAvailableCreneaux(Request $request) { return $this->getAvailableCreneauxByShop($request->id); } /** * front * enregistrement du créneau de collect * et validation de la commande */ public function setTimeSlot(Request $request) { $request->validate([ 'selectedmagasin' => 'required', 'selectedcreneau' => 'required', 'ok_cgv' => 'accepted', ]); $time_slot = Carbon::createFromFormat('d/m/Y-H:i', $request->selectedcreneau); $tsday = explode('-', $request->selectedcreneau)[0]; $tstime = explode('-', $request->selectedcreneau)[1]; $magasin_id = $request->selectedmagasin; $reference = $request->order_ref; $order = Order::where('reference', '=', $reference)->firstOrFail(); if ($order->status_id == 1) { // on ne traite l'enregistrement que pour les commandes en attente venant de l'étape de récap //save timeslot $timeslot = Timeslot::firstOrNew(['order_id' => $order->id]); $timeslot->order_id = $order->id; $timeslot->magasin_id = $magasin_id; $timeslot->day = $tsday; $timeslot->time = $tstime; $timeslot->save(); //save order status $order->status_id = 3; $order->save(); //send confirm mail Mail::to($order->customer->email)->queue(new OrderConfirmMail($order)); //envoi du mail de confirmation aux admins site $toMailSetting = Setting::where('key', 'send_order_confirm_mail_to')->first(); if (empty($toMailSetting->value)) { $toMail = config('mail.from.address'); } else { $toMail = $toMailSetting->value; } Mail::to($toMail)->queue(new OrderConfirmAdminMail($order)); //export commande if (config('bosettings.regate_orders')) { CsvOrderWrite::dispatch($order); } } //redirect to final confirmation return redirect()->route('sales.confirm', ['reference' => $order->reference]); } public function generateOrder($reference) { $order = Order::where('reference', '=', $reference)->with('lines')->firstOrFail(); $job = new CsvOrderWrite($order); $job->handle(); dd('end'); } /** * front * page de confirmation de commande */ public function confirmOrder($reference) { $activestep = 4; $idcommande = Session::get('idcommande'); $order = Order::where('reference', '=', $reference)->firstOrFail(); //1 seul magasin en base pour le moment $magasin = Magasin::find($order->timeslot->magasin->id); return view('sales.confirm', compact('activestep', 'order', 'magasin')); } /** * bo * ajx : affichage d'infos sur un client * @todo : déplacer dans le controller idoine */ public function getCustomerInfo($id, $mode = null) { $cust = Customers::findOrFail($id); if (!is_null($mode)) { if ($mode == "tippy") { return view('bo.customer.tippy-card', compact('cust')); } if ($mode == "json") { return $cust->apiData; } return view('bo.customer.info-card', compact('cust')); } return view('bo.customer.info-popup', compact('cust')); } /** * bo : page détatil commande */ public function orderDetail($reference) { $order = Order::where('reference', '=', $reference)->firstOrFail(); return view('bo.orders.detail', compact('order')); } }