Files
autocrm/app/Models/Tenant/TireSet.php
T
Vasyka 94938f24d7 Stage 11 — Tire Service: tire hotel + wheel sets
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>
2026-05-28 06:33:00 +00:00

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();
});
}
}