search) { $this->searchQuery( $orders, $request->search, null, 'label' ); } $this->sortQuery($orders); $searchFields = trans('Réference'); if ($this->indexModeTodayOnly) { $orders->whereHas('timeslot', function (Builder $q) { $q->whereDate('slot', \now()->today()); }); } $orders->orderBy('created_at', 'DESC'); $orders->with(['deliveryMode', 'trashedTimeSlot.shop']); $orders = $orders->paginate(10); return view('back::pages.orders.index', compact('orders', 'searchFields')); } /** * Display a listing of the resource. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory */ public function indexToday(Request $request): View { $this->indexModeTodayOnly = true; /** @var \Illuminate\View\View $response */ $response = $this->callAction('index', ['request' => $request]); return $response; } /** * Display the specified resource. * * @param \App\Models\Shop\Order $order * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory */ public function show(Order $order): View { $order->load('transactions'); return view('back::pages.orders.show', compact('order')); } /** * Mettre a jour le status de validation d'une commande * * @param \App\Http\Requests\Bo\Orders\ChangeOrderValidationStatusRequest $request * @param \App\Models\Shop\Order $order * @return \Illuminate\Http\RedirectResponse */ public function changeValidationStatus(ChangeOrderValidationStatusRequest $request, Order $order): RedirectResponse { $this->authorize('changeValidationStatus', $order); return DB::transaction(function () use ($request, $order) { $order->validation_status = $request->validation_status; switch ($request->validation_status) { case ValidationStatus::cancelled()->value: \session()->flash('bo.warning', trans('La commande est marquée comme annulée')); // Fall through. case ValidationStatus::validated()->value: $order->saveOrFail(); return \response()->redirectToRoute('bo.orders.show', $order)->with('bo.success', trans( ':model a été mis à jour', ['model' => Str::singular(trans('models.classes.orders'))] )); case ValidationStatus::pending()->value: return \back()->with('bo.errors', \trans('Il faut choisir un statut pour cette commande.')); default: throw new \RuntimeException("Unhandled status `{$request->validation_status}`"); }//end switch }); } /** * Mettre a jour le status de livraison d'une commande * * @param \App\Http\Requests\Bo\Orders\ChangeOrderDeliveryStatusRequest $request * @param \App\Models\Shop\Order $order * @return \Illuminate\Http\RedirectResponse */ public function changeDeliveryStatus(ChangeOrderDeliveryStatusRequest $request, Order $order): RedirectResponse { $this->authorize('changeDeliveryStatus', $order); return DB::transaction(function () use ($request, $order) { $order->delivery_status = $request->delivery_status; switch (true) { // * La commande va être préparé ou est déja en cours de préparation case $order->delivery_status->equals(DeliveryStatus::preparation()) or $order->delivery_status->equals(DeliveryStatus::preparation()): $order->delivery_status = DeliveryStatus::preparation(); $order->shipped_basket = ''; $order->saveOrFail(); return \redirect()->route('bo.orders.prepare', $order); // * La commande est récupérée. case $order->delivery_status->equals(DeliveryStatus::recovered()): // * Déclarer la commande comme réglée si récupération à la boutique. if ($order->deliveryMode->mode->equals(DeliveryModeEnum::inShop())) { // ! There should be one and only one inShop transaction ! /** @var \App\Models\Payment\Transaction|null */ $inShopTransaction = $order->transactions() ->where('type', TransactionType::debit()) ->where('payment_mode', PaymentModeEnum::inShop()) ->latest()->first(); $paidTransactions = $order->transactions()->where('type', TransactionType::debit()) ->where('status', TransactionStatus::validated())->count(); // * Check if there are not paid transactions then // * we should have one pending inShop transaction. if (!$paidTransactions and !$inShopTransaction) { throw new \RuntimeException( "There should be one inshop transaction for order '{$order->id}'" ); } if ($inShopTransaction) { $inShopTransaction->status = TransactionStatus::validated(); $inShopTransaction->saveOrFail(); } } else { throw new \RuntimeException("Unhandled delivery mode other than in shop"); } //end if // Fall through. // * La commande est prête. case $order->delivery_status->equals(DeliveryStatus::ready()): $order->saveOrFail(); return \back()->with('bo.success', trans( ':model a été mis à jour', ['model' => Str::singular(trans('models.classes.orders'))] )); default: throw new \RuntimeException("Unhandled status `{$request->delivery_status}`"); }//end switch }); } /** * Show the order prepare form * * @param \App\Models\Shop\Order $order * @return \Illuminate\View\View * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass */ public function prepare(Order $order): View { // phpcs:enable $this->authorize('prepare', $order); return \view('back::pages.orders.prepare', \compact('order')); } /** * Validate order preparation * * @param \App\Http\Requests\Bo\PrepareOrderRequest $request * @param \App\Models\Shop\Order $order * @return \Illuminate\Http\RedirectResponse */ public function prepareSubmit(PrepareOrderRequest $request, Order $order): RedirectResponse { $this->authorize('prepare', $order); return DB::transaction(function () use ($request, $order) { // * Stockage de la préparation de la commande. // * FIXME: Same code here as in Models/Shop/Order::getPartialRefundedBasket. // ! Ici on veux réutiliser le cart calculator. $cart = ToolboxHelper::createVirtualCart(\collect($order->basket) ->map(function (object $basketItem) { $cartItem = ToolboxHelper::basketItemObjectToCartItem($basketItem); $cartItem->associate(Product::query()->where('id', $cartItem->id)->first()); return $cartItem; }) // * Enlever les produits qui ne seront pas livrés. ->filter(function (CartItem $basketItem) { return $basketItem->qty; })); // * Update virtual basket with actual content \collect($order->basket)->each(function (object $basketItem) use ($request, $cart) { /** @var \Gloudemans\Shoppingcart\CartItem $cartItem */ $cartItem = $cart->search(function (CartItem $cartItem) use ($basketItem) { return $cartItem->id === $basketItem->id; })->first(); $newQty = \intval($request->validated()['items'][$basketItem->id]); // * Update Qty or remove item from the cart. if (!$newQty) { $cart->remove($cartItem->rowId); return; } $cartItem->setQuantity($newQty); // ! https://github.com/bumbummen99/LaravelShoppingcart/pull/181 // * Note Lib is is actually refactoring for laravel 9 php8 only // * https://github.com/bumbummen99/LaravelShoppingcart/pull/145 // ! These lines are required to prevent amount bug. $cartItem->setTaxRate($basketItem->options->tax_rate / 100); $cartItem->setDiscountRate($basketItem->options->discount_rate); }); $virtualOrder = new Order(); $virtualOrder->assignBasket($cart); // * Mettre a jour la commande. $order->shipped_basket = $virtualOrder->basket; $order->delivery_status = DeliveryStatus::ready(); $order->saveOrFail(); if ($request->willNeedCompleteRefund) { $transactionUsedForPayment = $order->getTransactionUsedForPayment(); $this->refundTransaction( $order, $transactionUsedForPayment, intval($transactionUsedForPayment->amount->getAmount()) ); // * Cancel Order completely. /** @var \Illuminate\Http\RedirectResponse */ return $this->callAction('changeValidationStatus', [ ChangeOrderValidationStatusRequest::create( \route('bo.orders.validation-status', $order), 'POST', [ 'validation_status' => ValidationStatus::cancelled()->value ] ), $order ]); } if ($request->willNeedRefund) { $transactionUsedForPayment = $order->getTransactionUsedForPayment(); $this->refundTransaction( $order, $transactionUsedForPayment, $order->total_ttc - $virtualOrder->total_ttc ); } return \redirect()->route('bo.orders.show', $order)->with('bo.success', trans( ':model a été mis à jour', ['model' => Str::singular(trans('models.classes.orders'))] )); }); } /** * Complete order refund and cancel * * @param \App\Models\Shop\Order $order * @return \Illuminate\Http\RedirectResponse */ public function cancelRefundSubmit(Order $order): RedirectResponse { $this->authorize('refund', $order); return DB::transaction(function () use ($order) { $transactionUsedForPayment = $order->getTransactionUsedForPayment(); $this->refundTransaction( $order, $transactionUsedForPayment, $order->total_ttc - $order->total_refund ); // * Cancel Order completely. /** @var \Illuminate\Http\RedirectResponse */ return $this->callAction('changeValidationStatus', [ ChangeOrderValidationStatusRequest::create( \route('bo.orders.validation-status', $order), 'POST', [ 'validation_status' => ValidationStatus::cancelled()->value ] ), $order ]); }); } /** * Complete order refund * * @param \App\Models\Shop\Order $order * @return \Illuminate\Http\RedirectResponse */ public function refundSubmit(Order $order): RedirectResponse { $this->authorize('refund', $order); return DB::transaction(function () use ($order) { $transactionUsedForPayment = $order->getTransactionUsedForPayment(); $this->refundTransaction( $order, $transactionUsedForPayment, $order->total_ttc - $order->total_refund ); return \back()->with('bo.success', trans( ':model a été mis à jour', ['model' => Str::singular(trans('models.classes.orders'))] )); }); } /** * Refund a transaction * * @param \App\Models\Shop\Order $order * @param \App\Models\Payment\Transaction $transaction * @param integer $amount * @return void */ private function refundTransaction(Order $order, Transaction $transaction, int $amount): void { // ? Le paiment se fait en boutique ? if ($transaction->payment_mode->equals(PaymentModeEnum::inShop())) { // ? La validation est annulée et le montant a remboursé equivaut a montant de la commande ? if ( $order->validation_status->equals(ValidationStatus::cancelled()) and $order->total_ttc === $amount ) { // * Mettre la transaction inShop comme ok et la commande comme remboursée DB::transaction(function () use ($order, $transaction, $amount) { $order->total_refund = $amount; $order->payment_status = PaymentStatus::refunded(); $order->saveQuietly(); $transaction->status = TransactionStatus::validated(); $transaction->saveQuietly(); $previousMessage = session()->get('bo.warning', ''); session()->flash('bo.warning', \trans( 'La commande est marquée comme remboursée' ) . " {$previousMessage}"); }); } else { $previousMessage = session()->get('bo.error', ''); session()->flash('bo.error', \trans( 'Vous devrez rembourser vous-même le client au magasin' ) . " {$previousMessage}"); } } else { RefundOrder::dispatch( $order, $transaction, $amount )->onQueue('database'); $previousMessage = session()->get('bo.info', ''); session()->flash('bo.info', \trans( 'Un remboursement va être émit au client' ) . " \n{$previousMessage}"); }//end if } }