false ]; /** * Map attribute names to enum classes. * * @var array */ protected $enumCasts = [ 'validation_status' => ValidationStatus::class, 'payment_status' => PaymentStatus::class, 'delivery_status' => DeliveryStatus::class ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'basket' => 'object', 'shipping' => 'object', 'billing' => 'object', 'mail_sent' => 'bool' ]; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'shipping', 'billing' ]; /** * Perform any actions required after the model boots. * * @return void */ protected static function booted() { static::creating(function (self $model) { // * ASSERTIONS static::assertValidateStatus($model); static::assertCancelStatus($model); $model->setUniqueOrderReference(); }); static::updating(function (self $model) { static::assertValidateStatus($model); static::assertCancelStatus($model); static::assertDeliveryStatusCanBeChanged($model); if ($model->validation_status->value !== $model->getOriginal('validation_status')) { // * Notifiy customer $model->customer->notify(new OrderValidationStatusChanged($model)); Session::flash('info', trans('Le client recevra une notification sur l\'état de validation.')); } if ( $model->delivery_status->equals(DeliveryStatus::ready()) and $model->delivery_status->value !== $model->getOriginal('delivery_status') ) { // * Notifiy customer $model->customer->notify(new OrderDeliveryStatusChanged($model)); Session::flash('info', trans('Le client recevra une notification sur \'état de livraison.')); } }); } // * RELATIONSHIPS /** * BelongsToMany products relation * * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ public function products() { return $this->belongsToMany(Product::class, 'order_lines', 'order_id', 'product_id'); } /** * BelongsTo customer relation * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function customer() { return $this->belongsTo(Customer::class); } /** * BelongsTo DeliveryMode relation * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function deliveryMode() { return $this->belongsTo(DeliveryMode::class); } /** * hasOne timeslot relation * * @return \Illuminate\Database\Eloquent\Relations\HasOne */ public function timeslot() { return $this->hasOne(Timeslot::class); } // * GETTER /** * Getter can_be_cancelled * * ! Logique modifiée pour refonte only here (sans paiement) * * On peut annuler la commande si le client * n'as pas payé et que la livraison n'a pas * encore été initié, et la commande n'est pas * déjà annulée. * * @return boolean */ public function getCanBeCancelledAttribute(): bool { // * Si le status de validation n'est pas annulé return $this->getOriginal('validation_status') !== ValidationStatus::cancelled()->value and // * Si le status de paiement n'est pas payé !($this->getOriginal('payment_status') === PaymentStatus::payed()->value) and // * Si le statut de livraison n'est pas récupéré !($this->getOriginal('delivery_status') === DeliveryStatus::recovered()->value); } // * SETTER /** * Getter reference * * @return void * @throws \RuntimeException This is readonly. */ public function setReferenceAttribute(): void { throw new \RuntimeException('Reference is readonly'); } // * METHODS /** * Setup reference * * @return void */ public function setUniqueOrderReference() { if (!$this->reference) { $this->setRawAttributes(\array_merge( $this->attributes, [ 'reference' => self::getUniqueOrderReference() ] )); } } /** * Generate an unique order reference * * @param integer $recursiveIncrement Used for interanl only. * @return string */ private static function getUniqueOrderReference(int $recursiveIncrement = 0): string { $counter = Order::whereDate('created_at', Carbon::today())->count() + 1 + $recursiveIncrement; $reference = Carbon::now()->isoFormat('YYYYMMDD') . str_pad($counter, 4, "0", STR_PAD_LEFT); if (self::query()->where('reference', $reference)->exists()) { // * Recursive counter to determinate reference unicity. return self::getUniqueOrderReference($counter + 1); } return $reference; } /** * Check if order can be cancelled * * @param self $model * @return void * @throws \Illuminate\Validation\ValidationException If the order is canceled while it cannot be. */ public static function assertCancelStatus(self $model) { if ( !$model->can_be_cancelled and $model->validation_status->equals(ValidationStatus::cancelled()) ) { throw ValidationException::withMessages([ 'messages' => "La commande '{$model->uuid}' ne peut être annulée" ]); } } /** * Check if order can be validated * * @param self $model * @return void * @throws \Illuminate\Validation\ValidationException If the order is canceled while it cannot be. */ public static function assertValidateStatus(self $model) { if ($model->getOriginal('validation_status') === ValidationStatus::cancelled()->value) { throw ValidationException::withMessages([ 'messages' => "La commande '{$model->uuid}' ne peut être validée après avoir été annulée" ]); } } /** * Check if delivery status can be changed. * * @param self $model * @return void * @throws \Illuminate\Validation\ValidationException If the order is canceled while it cannot be. */ public static function assertDeliveryStatusCanBeChanged(self $model) { $deliveryStatusHasChanged = $model->getOriginal('delivery_status') !== $model->delivery_status->value; if ($deliveryStatusHasChanged and !$model->validation_status->equals(ValidationStatus::validated())) { throw ValidationException::withMessages([ 'messages' => // phpcs:ignore Generic.Files.LineLength.TooLong "L'état de livraison de la commande ne peut être modifié car la commande '{$model->uuid}' n'est pas validée" ]); } if ($model->getOriginal('validation_status') === ValidationStatus::cancelled()->value) { throw ValidationException::withMessages([ 'messages' => "L'état de livraison de la commande ne peut être modifié car la commande '{$model->uuid}' est 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('uuid', $order->uuid) ->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()); } }