198 lines
5.3 KiB
PHP
198 lines
5.3 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Modules\RestaurantDelivery\Models;
|
||
|
|
|
||
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||
|
|
use Illuminate\Database\Eloquent\Model;
|
||
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||
|
|
use Modules\RestaurantDelivery\Enums\DeliveryStatus;
|
||
|
|
|
||
|
|
class DeliveryStatusHistory extends Model
|
||
|
|
{
|
||
|
|
use HasFactory;
|
||
|
|
|
||
|
|
public const TABLE_NAME = 'restaurant_delivery_status_history';
|
||
|
|
|
||
|
|
protected $table = self::TABLE_NAME;
|
||
|
|
|
||
|
|
protected $fillable = [
|
||
|
|
'delivery_id',
|
||
|
|
'rider_id',
|
||
|
|
'from_status',
|
||
|
|
'to_status',
|
||
|
|
'changed_by_type',
|
||
|
|
'changed_by_id',
|
||
|
|
'latitude',
|
||
|
|
'longitude',
|
||
|
|
'notes',
|
||
|
|
'meta',
|
||
|
|
'duration_from_previous',
|
||
|
|
'duration_from_start',
|
||
|
|
'changed_at',
|
||
|
|
];
|
||
|
|
|
||
|
|
protected $casts = [
|
||
|
|
'latitude' => 'decimal:7',
|
||
|
|
'longitude' => 'decimal:7',
|
||
|
|
'meta' => 'array',
|
||
|
|
'changed_at' => 'datetime',
|
||
|
|
];
|
||
|
|
|
||
|
|
protected static function boot()
|
||
|
|
{
|
||
|
|
parent::boot();
|
||
|
|
|
||
|
|
static::creating(function ($history) {
|
||
|
|
// Calculate durations
|
||
|
|
$delivery = Delivery::find($history->delivery_id);
|
||
|
|
|
||
|
|
if ($delivery) {
|
||
|
|
// Duration from delivery creation
|
||
|
|
$history->duration_from_start = $delivery->created_at->diffInSeconds(now());
|
||
|
|
|
||
|
|
// Duration from previous status
|
||
|
|
$previousHistory = static::where('delivery_id', $history->delivery_id)
|
||
|
|
->orderBy('changed_at', 'desc')
|
||
|
|
->first();
|
||
|
|
|
||
|
|
if ($previousHistory) {
|
||
|
|
$history->duration_from_previous = $previousHistory->changed_at->diffInSeconds(now());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| Relationships
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function delivery(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(Delivery::class, 'delivery_id');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function rider(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(Rider::class, 'rider_id');
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| Scopes
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function scopeForDelivery($query, int $deliveryId)
|
||
|
|
{
|
||
|
|
return $query->where('delivery_id', $deliveryId);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function scopeByStatus($query, string $status)
|
||
|
|
{
|
||
|
|
return $query->where('to_status', $status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function scopeRecent($query)
|
||
|
|
{
|
||
|
|
return $query->orderBy('changed_at', 'desc');
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| Accessors
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function getFromStatusEnumAttribute(): ?DeliveryStatus
|
||
|
|
{
|
||
|
|
return $this->from_status ? DeliveryStatus::tryFrom($this->from_status) : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getToStatusEnumAttribute(): DeliveryStatus
|
||
|
|
{
|
||
|
|
return DeliveryStatus::from($this->to_status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getFromStatusLabelAttribute(): ?string
|
||
|
|
{
|
||
|
|
return $this->from_status_enum?->label();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getToStatusLabelAttribute(): string
|
||
|
|
{
|
||
|
|
return $this->to_status_enum->label();
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| Methods
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function getDurationFormatted(): string
|
||
|
|
{
|
||
|
|
if (! $this->duration_from_previous) {
|
||
|
|
return '-';
|
||
|
|
}
|
||
|
|
|
||
|
|
$seconds = $this->duration_from_previous;
|
||
|
|
|
||
|
|
if ($seconds < 60) {
|
||
|
|
return "{$seconds}s";
|
||
|
|
}
|
||
|
|
|
||
|
|
$minutes = floor($seconds / 60);
|
||
|
|
$remainingSeconds = $seconds % 60;
|
||
|
|
|
||
|
|
if ($minutes < 60) {
|
||
|
|
return "{$minutes}m {$remainingSeconds}s";
|
||
|
|
}
|
||
|
|
|
||
|
|
$hours = floor($minutes / 60);
|
||
|
|
$remainingMinutes = $minutes % 60;
|
||
|
|
|
||
|
|
return "{$hours}h {$remainingMinutes}m";
|
||
|
|
}
|
||
|
|
|
||
|
|
public function hasLocation(): bool
|
||
|
|
{
|
||
|
|
return $this->latitude !== null && $this->longitude !== null;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getCoordinates(): ?array
|
||
|
|
{
|
||
|
|
if (! $this->hasLocation()) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return [
|
||
|
|
'lat' => $this->latitude,
|
||
|
|
'lng' => $this->longitude,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function getTimeline(int $deliveryId): array
|
||
|
|
{
|
||
|
|
return static::forDelivery($deliveryId)
|
||
|
|
->orderBy('changed_at')
|
||
|
|
->get()
|
||
|
|
->map(fn ($history) => [
|
||
|
|
'status' => $history->to_status,
|
||
|
|
'label' => $history->to_status_label,
|
||
|
|
'color' => $history->to_status_enum->color(),
|
||
|
|
'icon' => $history->to_status_enum->icon(),
|
||
|
|
'changed_at' => $history->changed_at->toIso8601String(),
|
||
|
|
'changed_at_human' => $history->changed_at->diffForHumans(),
|
||
|
|
'duration' => $history->getDurationFormatted(),
|
||
|
|
'notes' => $history->notes,
|
||
|
|
'location' => $history->getCoordinates(),
|
||
|
|
'changed_by' => $history->changed_by_type,
|
||
|
|
])
|
||
|
|
->toArray();
|
||
|
|
}
|
||
|
|
}
|