Schneespur — Open-source winter service documentation software (PWA + Admin). GPS tracking via OwnTracks, weather data, photo evidence, and legally compliant service records for winter maintenance operators. License: AGPL-3.0-or-later
153 lines
3.5 KiB
PHP
153 lines
3.5 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Enums\JobType;
|
|
use Database\Factories\JobFactory;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
class Job extends Model
|
|
{
|
|
/** @use HasFactory<JobFactory> */
|
|
use HasFactory;
|
|
|
|
protected $table = 'service_jobs';
|
|
|
|
protected $fillable = [
|
|
'work_shift_id',
|
|
'customer_id',
|
|
'customer_object_id',
|
|
'user_id',
|
|
'vehicle_id',
|
|
'type',
|
|
'started_at',
|
|
'ended_at',
|
|
'notes',
|
|
'is_manual',
|
|
];
|
|
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'type' => JobType::class,
|
|
'started_at' => 'datetime',
|
|
'ended_at' => 'datetime',
|
|
'is_manual' => 'boolean',
|
|
];
|
|
}
|
|
|
|
public function workShift(): BelongsTo
|
|
{
|
|
return $this->belongsTo(WorkShift::class);
|
|
}
|
|
|
|
public function customerObject(): BelongsTo
|
|
{
|
|
return $this->belongsTo(CustomerObject::class);
|
|
}
|
|
|
|
public function customer(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Customer::class);
|
|
}
|
|
|
|
public function user(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class);
|
|
}
|
|
|
|
public function vehicle(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Vehicle::class);
|
|
}
|
|
|
|
public function gpsPoints(): HasMany
|
|
{
|
|
return $this->hasMany(GpsPoint::class);
|
|
}
|
|
|
|
public function weatherSnapshots(): HasMany
|
|
{
|
|
return $this->hasMany(WeatherSnapshot::class);
|
|
}
|
|
|
|
public function jobPhotos(): HasMany
|
|
{
|
|
return $this->hasMany(JobPhoto::class);
|
|
}
|
|
|
|
public function notificationLogs(): MorphMany
|
|
{
|
|
return $this->morphMany(NotificationLog::class, 'notifiable');
|
|
}
|
|
|
|
public function alertDismissals(): HasMany
|
|
{
|
|
return $this->hasMany(AlertDismissal::class);
|
|
}
|
|
|
|
public function audits(): HasMany
|
|
{
|
|
return $this->hasMany(JobAudit::class);
|
|
}
|
|
|
|
public function isCompleted(): bool
|
|
{
|
|
return $this->ended_at !== null;
|
|
}
|
|
|
|
public function isInGracePeriod(): bool
|
|
{
|
|
return $this->isCompleted() && $this->ended_at->addHours(24)->isFuture();
|
|
}
|
|
|
|
public function isLocked(): bool
|
|
{
|
|
return $this->isCompleted() && ! $this->isInGracePeriod();
|
|
}
|
|
|
|
public function isGpsLocked(): bool
|
|
{
|
|
return $this->isCompleted();
|
|
}
|
|
|
|
public function graceDeadline(): ?Carbon
|
|
{
|
|
return $this->ended_at?->addHours(24);
|
|
}
|
|
|
|
public function localStartedAt(): Carbon
|
|
{
|
|
return $this->started_at->copy()->setTimezone(config('app.display_timezone'));
|
|
}
|
|
|
|
public function localEndedAt(): ?Carbon
|
|
{
|
|
return $this->ended_at?->copy()->setTimezone(config('app.display_timezone'));
|
|
}
|
|
|
|
public function durationFormatted(): string
|
|
{
|
|
if (! $this->ended_at) {
|
|
return '';
|
|
}
|
|
|
|
$diff = $this->started_at->diff($this->ended_at);
|
|
$hours = $diff->h + ($diff->days * 24);
|
|
|
|
if ($hours > 0 && $diff->i > 0) {
|
|
return __('job.duration_hours_minutes', ['hours' => $hours, 'minutes' => $diff->i]);
|
|
}
|
|
|
|
if ($hours > 0) {
|
|
return trans_choice('job.duration_hours', $hours, ['hours' => $hours]);
|
|
}
|
|
|
|
return __('job.duration_minutes', ['minutes' => $diff->i ?: 1]);
|
|
}
|
|
}
|