217 lines
5.3 KiB
PHP
217 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\Traits\HasRestaurant;
|
||
|
|
use Modules\RestaurantDelivery\Traits\HasUuid;
|
||
|
|
|
||
|
|
class DeliveryAssignment extends Model
|
||
|
|
{
|
||
|
|
use HasFactory, HasRestaurant, HasUuid;
|
||
|
|
|
||
|
|
public const TABLE_NAME = 'restaurant_delivery_assignments';
|
||
|
|
|
||
|
|
protected $table = self::TABLE_NAME;
|
||
|
|
|
||
|
|
protected $fillable = [
|
||
|
|
'delivery_id',
|
||
|
|
'rider_id',
|
||
|
|
'restaurant_id',
|
||
|
|
'status',
|
||
|
|
'attempt_number',
|
||
|
|
'assignment_type',
|
||
|
|
'assigned_by',
|
||
|
|
'score',
|
||
|
|
'score_breakdown',
|
||
|
|
'distance_to_restaurant',
|
||
|
|
'estimated_arrival_time',
|
||
|
|
'notified_at',
|
||
|
|
'responded_at',
|
||
|
|
'expires_at',
|
||
|
|
'rejection_reason',
|
||
|
|
'rejection_notes',
|
||
|
|
'rider_latitude',
|
||
|
|
'rider_longitude',
|
||
|
|
];
|
||
|
|
|
||
|
|
protected $casts = [
|
||
|
|
'score' => 'decimal:2',
|
||
|
|
'score_breakdown' => 'array',
|
||
|
|
'distance_to_restaurant' => 'decimal:2',
|
||
|
|
'notified_at' => 'datetime',
|
||
|
|
'responded_at' => 'datetime',
|
||
|
|
'expires_at' => 'datetime',
|
||
|
|
'rider_latitude' => 'decimal:7',
|
||
|
|
'rider_longitude' => 'decimal:7',
|
||
|
|
];
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| Relationships
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function delivery(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(Delivery::class, 'delivery_id');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function rider(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(Rider::class, 'rider_id');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function assignedBy(): BelongsTo
|
||
|
|
{
|
||
|
|
return $this->belongsTo(config('auth.providers.users.model'), 'assigned_by');
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| Scopes
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function scopePending($query)
|
||
|
|
{
|
||
|
|
return $query->where('status', 'pending');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function scopeAccepted($query)
|
||
|
|
{
|
||
|
|
return $query->where('status', 'accepted');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function scopeRejected($query)
|
||
|
|
{
|
||
|
|
return $query->where('status', 'rejected');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function scopeExpired($query)
|
||
|
|
{
|
||
|
|
return $query->where('status', 'expired');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function scopeActive($query)
|
||
|
|
{
|
||
|
|
return $query->whereIn('status', ['pending', 'accepted']);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function scopeNotExpired($query)
|
||
|
|
{
|
||
|
|
return $query->where(function ($q) {
|
||
|
|
$q->whereNull('expires_at')
|
||
|
|
->orWhere('expires_at', '>', now());
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| Methods
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
public function accept(): void
|
||
|
|
{
|
||
|
|
$this->update([
|
||
|
|
'status' => 'accepted',
|
||
|
|
'responded_at' => now(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Reject all other pending assignments for this delivery
|
||
|
|
DeliveryAssignment::where('delivery_id', $this->delivery_id)
|
||
|
|
->where('id', '!=', $this->id)
|
||
|
|
->where('status', 'pending')
|
||
|
|
->update([
|
||
|
|
'status' => 'cancelled',
|
||
|
|
'responded_at' => now(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function reject(string $reason, ?string $notes = null): void
|
||
|
|
{
|
||
|
|
$this->update([
|
||
|
|
'status' => 'rejected',
|
||
|
|
'responded_at' => now(),
|
||
|
|
'rejection_reason' => $reason,
|
||
|
|
'rejection_notes' => $notes,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function expire(): void
|
||
|
|
{
|
||
|
|
$this->update([
|
||
|
|
'status' => 'expired',
|
||
|
|
'responded_at' => now(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function cancel(): void
|
||
|
|
{
|
||
|
|
$this->update([
|
||
|
|
'status' => 'cancelled',
|
||
|
|
'responded_at' => now(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function isPending(): bool
|
||
|
|
{
|
||
|
|
return $this->status === 'pending';
|
||
|
|
}
|
||
|
|
|
||
|
|
public function isAccepted(): bool
|
||
|
|
{
|
||
|
|
return $this->status === 'accepted';
|
||
|
|
}
|
||
|
|
|
||
|
|
public function isRejected(): bool
|
||
|
|
{
|
||
|
|
return $this->status === 'rejected';
|
||
|
|
}
|
||
|
|
|
||
|
|
public function isExpired(): bool
|
||
|
|
{
|
||
|
|
if ($this->status === 'expired') {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->expires_at && $this->expires_at->isPast()) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function hasExpired(): bool
|
||
|
|
{
|
||
|
|
return $this->expires_at && $this->expires_at->isPast();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getResponseTime(): ?int
|
||
|
|
{
|
||
|
|
if (! $this->responded_at || ! $this->notified_at) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->notified_at->diffInSeconds($this->responded_at);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function getRejectionReasons(): array
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'too_far' => 'Restaurant is too far',
|
||
|
|
'busy' => 'Currently busy',
|
||
|
|
'ending_shift' => 'Ending my shift',
|
||
|
|
'vehicle_issue' => 'Vehicle issue',
|
||
|
|
'low_battery' => 'Phone/GPS low battery',
|
||
|
|
'personal' => 'Personal reason',
|
||
|
|
'other' => 'Other',
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|