mein-sterntours/app/Models/Offer.php

148 lines
4.8 KiB
PHP

<?php
namespace App\Models;
use App\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
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\SoftDeletes;
/**
* Angebots-Kopfdatensatz (Modul 6 — Offers).
*
* Trägt Angebotsnummer + Beziehungen zu Kontakt / Anfrage / Buchung und
* zeigt via `current_version_id` auf die aktuell gültige OfferVersion.
* Die eigentlichen Inhalte (Texte, Positionen, Preise, PDF) liegen in
* `offer_versions`. Jede Änderung nach dem ersten Versand erzeugt eine
* neue Version (siehe OfferVersion::isEditable()).
*
* @property int $id
* @property string $offer_number
* @property int $contact_id
* @property int|null $inquiry_id
* @property int|null $booking_id
* @property string $status draft|sent|accepted|declined|expired|withdrawn
* @property int|null $current_version_id
* @property int $created_by
* @property Carbon $created_at
* @property Carbon $updated_at
* @property Carbon|null $deleted_at
* @property-read Contact $contact
* @property-read Lead|null $inquiry
* @property-read Booking|null $booking
* @property-read Collection|OfferVersion[] $versions
* @property-read OfferVersion|null $currentVersion
* @property-read User $createdBy
*/
class Offer extends Model
{
use HasFactory, SoftDeletes;
public const STATUS_DRAFT = 'draft';
public const STATUS_SENT = 'sent';
public const STATUS_ACCEPTED = 'accepted';
public const STATUS_DECLINED = 'declined';
public const STATUS_EXPIRED = 'expired';
public const STATUS_WITHDRAWN = 'withdrawn';
/** Stati, die als „nicht abgeschlossen" gelten (Offer läuft noch). */
public const OPEN_STATUSES = [
self::STATUS_DRAFT,
self::STATUS_SENT,
];
protected $connection = 'mysql';
protected $table = 'offers';
protected $fillable = [
'offer_number',
'contact_id',
'inquiry_id',
'booking_id',
'status',
'current_version_id',
'created_by',
];
protected $casts = [
'contact_id' => 'int',
'inquiry_id' => 'int',
'booking_id' => 'int',
'current_version_id' => 'int',
'created_by' => 'int',
];
// ── Beziehungen ──────────────────────────────────────────────────────────
public function contact(): BelongsTo
{
return $this->belongsTo(Contact::class, 'contact_id');
}
/**
* Die Anfrage (Phase 2 heißt die Tabelle `inquiries`, das Eloquent-Model
* ist weiterhin `Lead` — Umbenennung des Models ist eigenes Ticket).
*/
public function inquiry(): BelongsTo
{
return $this->belongsTo(Lead::class, 'inquiry_id');
}
public function booking(): BelongsTo
{
return $this->belongsTo(Booking::class, 'booking_id');
}
public function versions(): HasMany
{
return $this->hasMany(OfferVersion::class, 'offer_id')->orderBy('version_no');
}
public function currentVersion(): BelongsTo
{
return $this->belongsTo(OfferVersion::class, 'current_version_id');
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
// ── Scopes ───────────────────────────────────────────────────────────────
/** Nur noch nicht abgeschlossene Angebote (draft + sent). */
public function scopeOpen(Builder $query): Builder
{
return $query->whereIn('status', self::OPEN_STATUSES);
}
public function scopeForContact(Builder $query, int $contactId): Builder
{
return $query->where('contact_id', $contactId);
}
/**
* @param string[] $stati
*/
public function scopeWithStatus(Builder $query, array $stati): Builder
{
return $query->whereIn('status', $stati);
}
// ── Hilfsmethoden ────────────────────────────────────────────────────────
public function isEditable(): bool
{
return $this->status === self::STATUS_DRAFT;
}
public function isOpen(): bool
{
return in_array($this->status, self::OPEN_STATUSES, true);
}
}