diff --git a/app/Http/Controllers/Fo/OrderController.php b/app/Http/Controllers/Fo/OrderController.php index 759bbd0..39b6488 100644 --- a/app/Http/Controllers/Fo/OrderController.php +++ b/app/Http/Controllers/Fo/OrderController.php @@ -2,10 +2,14 @@ namespace App\Http\Controllers\Fo; +use App\Enums\Order\DeliveryStatus; use App\Enums\Order\OrderProcess; +use App\Enums\Order\PaymentStatus; +use App\Enums\Order\ValidationStatus; use App\Http\Controllers\Controller; +use App\Http\Requests\Fo\Orders\ShippingModeFormRequest; +use App\Models\DeliveryMode; use App\Models\Order; -use App\Models\OrderLine; use Carbon\Carbon; use Gloudemans\Shoppingcart\Facades\Cart; use Illuminate\Support\Facades\Auth; @@ -28,8 +32,6 @@ class OrderController extends Controller $this->assertDoesNotHaveEmptyBasket(); $this->setOrderProcessStep(OrderProcess::reviewBasket()); - $activestep = 2; - /** @var \Gloudemans\Shoppingcart\Cart */ $basket = app('basket'); $messages = []; @@ -43,14 +45,13 @@ class OrderController extends Controller $basketItems = $this->basketToList($basket); return view('front.order-tunnel.basket-recap', compact( - 'activestep', 'basket', 'basketItems' )); } /** - * Select shipping Mode + * Select shipping Mode And create Session Order * * @return \Illuminate\Contracts\View\View */ @@ -64,10 +65,60 @@ class OrderController extends Controller // * CREATE ORDER - /** @var \Illuminate\Database\Eloquent\Collection<\App\Models\DeliveryMode> */ - $deliveyModes = app('DeliveryModesGlobal'); + /** @var \App\Models\Customer $customer */ + $customer = auth('frontend')->user(); - return view('front.order-tunnel.shipping-mode'); + /** @var \Gloudemans\Shoppingcart\Cart */ + $basket = app('basket'); + + /** @var \App\Models\Order|null $order */ + $order = Order::getCurrentOrder(); + + // * If there was a previous order that was not ended use the ID. + /** @var \App\Models\Order */ + $order = $order ?? new Order(); + $order->delivery_status = DeliveryStatus::pending(); + $order->payment_status = PaymentStatus::pending(); + $order->validation_status = ValidationStatus::pending(); + $order->assignBasket($basket); + $order->customer()->associate($customer); + + // * Save Session Order + Session::put('currentOrder', $order); + + /** @var \Illuminate\Database\Eloquent\Collection<\App\Models\DeliveryMode> */ + $deliveryModes = app('DeliveryModesGlobal'); + + return view('front.order-tunnel.shipping-mode', \compact('deliveryModes')); + } + + /** + * Save shipping Mode + * + * @param \App\Http\Requests\Fo\Orders\ShippingModeFormRequest $request + * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory + */ + public function shippingModeSubmit(ShippingModeFormRequest $request) + { + $this->assertDoesNotHaveEmptyBasket(); + $this->setOrderProcessStep(OrderProcess::shippingMethod()); + + /** @var \App\Models\DeliveryMode $mode */ + if (!($mode = DeliveryMode::availables()->whereId($request->mode)->first())) { + $e = new \RuntimeException('Mode de livraison non disponible', 1); + report($e); + Session::flash('error', trans('Mode de livraison non disponible')); + return \back()->withException($e); + } + + /** @var \App\Models\Order $order */ + $order = Order::getCurrentOrder(); + + $order->deliveryMode()->associate($mode); + + Session::put('currentOrder', $order); + + return \response()->redirectToRoute('orders.shipping'); } // * --- FIN TUNNEL DE COMMANDE --- diff --git a/app/Http/Requests/Fo/Orders/ShippingModeFormRequest.php b/app/Http/Requests/Fo/Orders/ShippingModeFormRequest.php new file mode 100644 index 0000000..1e233fa --- /dev/null +++ b/app/Http/Requests/Fo/Orders/ShippingModeFormRequest.php @@ -0,0 +1,47 @@ +check(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + $mode = null; + try { + $mode = DeliveryModeEnum::make(\intval($this->mode)); + } catch (\TypeError | \BadMethodCallException $e) { + $mode = null; + } + return [ + 'mode' => [ + 'required', new EnumIsValidRule(DeliveryModeEnum::class), + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + function ($attribute, $value, $fail) use ($mode) { + if (!DeliveryMode::availables()->whereId($mode)->first()) { + $fail(trans('Mode de livraison non disponible')); + } + } + ] + ]; + } +} diff --git a/app/Models/Order.php b/app/Models/Order.php index 0c7c3d0..bc52862 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -5,16 +5,30 @@ namespace App\Models; use App\Enums\Order\DeliveryStatus; use App\Enums\Order\PaymentStatus; use App\Enums\Order\ValidationStatus; +use Gloudemans\Shoppingcart\Cart; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Session; use Kwaadpepper\Enum\Traits\CastsEnums; /** * Order * - * @property boolean can_be_cancelled Can the order be cancelled ? - * @property integer total_ttc Total ttc in cents - * @property string formated_total_ttc Total ttc in euro + * @property-read string $reference The order unique reference string. + * @property array $basket The order serialized basket. + * @property string $email The customer email used for this order. + * @property boolean $mail_sent Has the confirmation mail been sent to the customer. + * @property array $shipping The shipping address info. + * @property array $billing The billing address info. + * @property integer $total_discounted Total discounted on this order in cents. + * @property integer $total_ttc Total payed fro mthe customer in cents. + * @property integer $total_ht Total without taxes in cents. + * @property integer $total_weight Total weight in gramms. + * @property \App\Enums\Order\ValidationStatus $validation_status The order validation status. + * @property \App\Enums\Order\PaymentStatus $payment_status The order payment status. + * @property \App\Enums\Order\DeliveryStatus $delivery_status The order delivery status. + * + * @property-read boolean $can_be_cancelled Can the order be cancelled ? * * @package App\Models */ @@ -34,6 +48,15 @@ class Order extends Model 'delivery_status' => DeliveryStatus::class ]; + /** + * The attributes that should be cast to native types. + * + * @var array + */ + protected $casts = [ + 'basket' => 'array', + ]; + /** * Perform any actions required after the model boots. * @@ -62,16 +85,6 @@ class Order extends Model return $this->belongsToMany(Product::class, 'order_lines', 'order_id', 'product_id'); } - /** - * hasMany lines relation - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function lines() - { - return $this->hasMany(OrderLine::class); - } - /** * BelongsTo customer relation * @@ -121,34 +134,6 @@ class Order extends Model $this->delivery_status->equals(DeliveryStatus::pending()); } - /** - * Getter formated_total_ttc - * - * @return string - */ - public function getFormattedTotalTtcAttribute(): string - { - return sprintf( - '%s €', - number_format($this->total_ttc / 100, 2, ',', ' ') - ); - } - - /** - * Getter total_ttc - * - * @return integer - */ - public function getTotalTtcAttribute(): int - { - /** @var \Illuminate\Database\Eloquent\Collection<\App\Models\OrderLine> */ - $lines = $this->lines; - return $lines->reduce(function ($previous, OrderLine $next) { - /** @var int|null $previous */ - return \intval($previous) + $next->getRawOriginal('total_ttc'); - }); - } - // * SETTER /** @@ -180,4 +165,37 @@ class Order extends Model throw new \RuntimeException("La commande '{$model->id}' ne peut être annulée"); } } + + /** + * Get updated current session order + * + * @return null|\App\Models\Order + */ + public static function getCurrentOrder(): ?self + { + /** @var \App\Models\Order|null $order */ + if ($order = Session::get('currentOrder')) { + // * Refresh order if exists in DB. + $order = Order::where('id', $order->id) + ->orWhere('reference', $order->ref) + ->first() ?? $order; + } + return $order; + } + + /** + * Inject the basket items into the order. + * + * @param \Gloudemans\Shoppingcart\Cart $basket + * @return void + * @throws \Illuminate\Validation\ValidationException If a product from the basket is not accessible anymore. + */ + public function assignBasket(Cart $basket): void + { + $this->basket = $basket->content(); + $this->total_discounted = \intval($basket->discount()); + $this->total_ttc = \intval($basket->total()); + $this->total_ht = \intval($this->total_ttc - $basket->tax()); + $this->total_weight = \intval($basket->weight()); + } } diff --git a/app/Models/OrderLine.php b/app/Models/OrderLine.php deleted file mode 100644 index 8c4d134..0000000 --- a/app/Models/OrderLine.php +++ /dev/null @@ -1,67 +0,0 @@ -belongsTo(Order::class); - } - - /** - * BelongsTo product relation - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function product() - { - return $this->belongsTo(Product::class); - } - - // * METHODS - - /** - * Getter formated_total_ht - * - * @return string - */ - public function getFormattedTotalHtAttribute(): string - { - return sprintf( - '%s €', - number_format($this->total_ht / 100, 2, ',', ' ') - ); - } - - /** - * Getter formated_total_ttc - * - * @return string - */ - public function getFormattedTotalTtcAttribute(): string - { - return sprintf( - '%s €', - number_format($this->total_ttc / 100, 2, ',', ' ') - ); - } -} diff --git a/database/migrations/2022_12_05_151923_add_move_order_lines_into_orders.php b/database/migrations/2022_12_05_151923_add_move_order_lines_into_orders.php new file mode 100644 index 0000000..637e61f --- /dev/null +++ b/database/migrations/2022_12_05_151923_add_move_order_lines_into_orders.php @@ -0,0 +1,63 @@ +truncate(); + DB::table('orders')->truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + // * Create json basket field on orders. + Schema::table('orders', function (Blueprint $table) { + $table->json('basket') + ->default('[]') + ->after('reference'); + }); + // * Unset orders default value. + Schema::table('orders', function (Blueprint $table) { + $table->json('basket') + ->change() + ->default(null) + ->comment('Le panier sérialisé des produits de la commande.'); + }); + + Schema::table('orders', function (Blueprint $table) { + $table->string('email')->after('basket') + ->comment('Adresse email de facturation (envoi du mail de confirmation)'); + $table->boolean('mail_sent')->after('email')->comment('Le mail de commande est envoyé au client.'); + $table->json('shipping')->after('mail_sent')->comment('Adresse de livraison'); + $table->json('billing')->after('shipping')->nullable()->comment('Adresse de facturation'); + $table->integer('total_discounted')->after('billing') + ->comment('Total des remises du panier en centimes, somme des lignes (discount * qty)'); + $table->integer('total_ttc')->after('total_discounted') + ->comment('Total TTC facturé en centimes, somme des lignes (prix - remise + TVA) * qty'); + $table->integer('total_ht')->after('total_ttc') + ->comment('Total HT facturé en centimes, somme des lignes (prix - remise )* qty'); + $table->integer('total_weight')->after('total_ht')->comment('Poids total en grammes'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + * @throws \Exception Wont do this. + */ + public function down() + { + throw new \Exception('Wont roll back this'); + } +}