94938f24d7
Schema: - tire_sets (client/vehicle, season, size width/profile/diameter, brand/DOT, rims, tread JSON per position + tread_min cache, TPMS + sensor ids, photos) - tire_storage (location, season_label, stored/retrieved, check-in/out, fee) Models: - TireSet (HasMedia): sizeLabel, isStored, currentStorage, auto tread_min - TireStorage: durationDays, isActive Filament (new "Anvelope" nav group): - TireSetResource: specs form + per-position tread + TPMS + photo upload; table with size, season badge, min tread (red < 3mm), storage status - Check-in (location + period + fee → stored) / Check-out (→ retrieved) - StorageRelationManager (stay history); nav badge = sets currently stored Tests (6 new): - sizeLabel formatting; tread_min from positions; check-in active storage; check-out retrieved + duration; multiple stays per set; tenant isolation Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
99 lines
2.5 KiB
PHP
99 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Models\Tenant;
|
|
|
|
use App\Models\Concerns\BelongsToTenant;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Spatie\MediaLibrary\HasMedia;
|
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
|
|
|
class TireSet extends Model implements HasMedia
|
|
{
|
|
use BelongsToTenant, InteractsWithMedia, SoftDeletes;
|
|
|
|
public const SEASONS = [
|
|
'summer' => 'Vară',
|
|
'winter' => 'Iarnă',
|
|
'allseason' => 'All-season',
|
|
];
|
|
|
|
public const CONDITIONS = [
|
|
'nou' => 'Nou',
|
|
'bun' => 'Bun',
|
|
'uzat' => 'Uzat',
|
|
'critic' => 'Critic',
|
|
];
|
|
|
|
protected $fillable = [
|
|
'company_id', 'client_id', 'vehicle_id',
|
|
'label', 'season',
|
|
'width', 'profile', 'diameter', 'brand', 'model', 'dot_year',
|
|
'has_rims', 'rim_type',
|
|
'tread', 'tread_min', 'tpms', 'tpms_ids',
|
|
'condition', 'notes',
|
|
];
|
|
|
|
protected $casts = [
|
|
'tread' => 'array',
|
|
'tpms_ids' => 'array',
|
|
'tread_min' => 'decimal:1',
|
|
'has_rims' => 'boolean',
|
|
'tpms' => 'boolean',
|
|
];
|
|
|
|
public function registerMediaCollections(): void
|
|
{
|
|
$this->addMediaCollection('photos');
|
|
}
|
|
|
|
public function client(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Client::class);
|
|
}
|
|
|
|
public function vehicle(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Vehicle::class);
|
|
}
|
|
|
|
public function storage(): HasMany
|
|
{
|
|
return $this->hasMany(TireStorage::class);
|
|
}
|
|
|
|
public function currentStorage(): ?TireStorage
|
|
{
|
|
return $this->storage()->where('status', 'stored')->latest('checked_in_at')->first();
|
|
}
|
|
|
|
public function isStored(): bool
|
|
{
|
|
return $this->storage()->where('status', 'stored')->exists();
|
|
}
|
|
|
|
public function sizeLabel(): string
|
|
{
|
|
if (! $this->width || ! $this->profile || ! $this->diameter) {
|
|
return '—';
|
|
}
|
|
return "{$this->width}/{$this->profile} R{$this->diameter}";
|
|
}
|
|
|
|
/** Recompute tread_min from the per-position tread JSON. */
|
|
public function recomputeTreadMin(): void
|
|
{
|
|
$vals = array_filter(array_map('floatval', array_values((array) $this->tread)), fn ($v) => $v > 0);
|
|
$this->tread_min = $vals ? min($vals) : null;
|
|
}
|
|
|
|
protected static function booted(): void
|
|
{
|
|
static::saving(function (self $set) {
|
|
$set->recomputeTreadMin();
|
|
});
|
|
}
|
|
}
|