Neustrukturierung Customer / Lead / Booking Phase 2
This commit is contained in:
parent
313f0dbf4e
commit
6df9c401af
69 changed files with 3809 additions and 374 deletions
|
|
@ -11,38 +11,39 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* Version eines Angebots (Modul 6).
|
||||
* Eine konkrete Version (Snapshot) eines Angebots.
|
||||
*
|
||||
* Jede versendete Fassung wird hier festgehalten — Texte, Positionen
|
||||
* und PDF bleiben damit unveränderlich, sobald ein Kunde sie per
|
||||
* Freigabe-Link einsehen kann. Neue Änderungen nach dem Versand
|
||||
* erzeugen eine neue Version (version_no = max+1, status = draft).
|
||||
* Alle inhaltlichen Felder (Texte, Preise, Gültigkeit, PDF-Pfad, Status,
|
||||
* Anhänge) hängen hier — nicht am Offer-Kopf. So bleiben bereits versendete
|
||||
* Versionen unveränderlich (`isEditable() === false`), während eine neue
|
||||
* Version ihre eigenen Texte/Positionen/Anhänge führt.
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $offer_id
|
||||
* @property int $version_no
|
||||
* @property string $status
|
||||
* @property Carbon|null $valid_until
|
||||
* @property float $total_price
|
||||
* @property string|null $headline
|
||||
* @property string|null $intro_text
|
||||
* @property string|null $itinerary_text
|
||||
* @property string|null $closing_text
|
||||
* @property int|null $template_id
|
||||
* @property string|null $pdf_path
|
||||
* @property bool $pdf_archived
|
||||
* @property Carbon|null $sent_at
|
||||
* @property Carbon|null $accepted_at
|
||||
* @property string|null $accepted_via
|
||||
* @property array|null $template_document_ids
|
||||
* @property int $created_by
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
* @property-read Offer $offer
|
||||
* @property-read OfferTemplate|null $template
|
||||
* @property-read Collection|OfferItem[] $items
|
||||
* @property-read Collection|OfferFile[] $files
|
||||
* @property-read User $creator
|
||||
* @property int $id
|
||||
* @property int $offer_id
|
||||
* @property int $version_no
|
||||
* @property string $status draft|sent|accepted|declined|expired|superseded
|
||||
* @property Carbon|null $valid_until
|
||||
* @property float $total_price
|
||||
* @property string|null $headline
|
||||
* @property string|null $intro_text
|
||||
* @property string|null $itinerary_text
|
||||
* @property string|null $closing_text
|
||||
* @property int|null $template_id
|
||||
* @property string|null $pdf_path
|
||||
* @property bool $pdf_archived
|
||||
* @property Carbon|null $sent_at
|
||||
* @property Carbon|null $accepted_at
|
||||
* @property string|null $accepted_via customer_link|admin|email
|
||||
* @property array|null $template_document_ids JSON-Array von zentral hinterlegten Dokument-IDs
|
||||
* @property int $created_by
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
* @property-read Offer $offer
|
||||
* @property-read Collection|OfferItem[] $items
|
||||
* @property-read Collection|OfferFile[] $files
|
||||
* @property-read Collection|OfferAccessToken[] $tokens
|
||||
* @property-read OfferTemplate|null $template
|
||||
* @property-read User $createdBy
|
||||
*/
|
||||
class OfferVersion extends Model
|
||||
{
|
||||
|
|
@ -57,8 +58,9 @@ class OfferVersion extends Model
|
|||
|
||||
public const ACCEPTED_VIA_LINK = 'customer_link';
|
||||
public const ACCEPTED_VIA_ADMIN = 'admin';
|
||||
public const ACCEPTED_VIA_MAIL = 'email';
|
||||
public const ACCEPTED_VIA_EMAIL = 'email';
|
||||
|
||||
protected $connection = 'mysql';
|
||||
protected $table = 'offer_versions';
|
||||
|
||||
protected $fillable = [
|
||||
|
|
@ -84,19 +86,36 @@ class OfferVersion extends Model
|
|||
protected $casts = [
|
||||
'offer_id' => 'int',
|
||||
'version_no' => 'int',
|
||||
'valid_until' => 'date',
|
||||
'total_price' => 'decimal:2',
|
||||
'template_id' => 'int',
|
||||
'pdf_archived' => 'bool',
|
||||
'created_by' => 'int',
|
||||
'total_price' => 'decimal:2',
|
||||
'valid_until' => 'date',
|
||||
'sent_at' => 'datetime',
|
||||
'accepted_at' => 'datetime',
|
||||
'pdf_archived' => 'bool',
|
||||
'template_document_ids' => 'array',
|
||||
'created_by' => 'int',
|
||||
];
|
||||
|
||||
// ── Beziehungen ──────────────────────────────────────────────────────────
|
||||
|
||||
public function offer(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Offer::class);
|
||||
return $this->belongsTo(Offer::class, 'offer_id');
|
||||
}
|
||||
|
||||
public function items(): HasMany
|
||||
{
|
||||
return $this->hasMany(OfferItem::class, 'offer_version_id')->orderBy('position');
|
||||
}
|
||||
|
||||
public function files(): HasMany
|
||||
{
|
||||
return $this->hasMany(OfferFile::class, 'offer_version_id');
|
||||
}
|
||||
|
||||
public function tokens(): HasMany
|
||||
{
|
||||
return $this->hasMany(OfferAccessToken::class, 'offer_version_id');
|
||||
}
|
||||
|
||||
public function template(): BelongsTo
|
||||
|
|
@ -104,23 +123,52 @@ class OfferVersion extends Model
|
|||
return $this->belongsTo(OfferTemplate::class, 'template_id');
|
||||
}
|
||||
|
||||
public function items(): HasMany
|
||||
{
|
||||
return $this->hasMany(OfferItem::class)->orderBy('position');
|
||||
}
|
||||
|
||||
public function files(): HasMany
|
||||
{
|
||||
return $this->hasMany(OfferFile::class);
|
||||
}
|
||||
|
||||
public function creator(): BelongsTo
|
||||
public function createdBy(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
// ── Hilfsmethoden ────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Darf diese Version noch bearbeitet werden?
|
||||
* Nur Draft-Versionen sind editierbar; alle anderen sind eingefroren.
|
||||
*/
|
||||
public function isEditable(): bool
|
||||
{
|
||||
return $this->status === self::STATUS_DRAFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert den zuletzt erzeugten, noch nicht widerrufenen Token
|
||||
* (falls vorhanden) — wird im Freigabe-Link verwendet.
|
||||
*/
|
||||
public function latestToken(): ?OfferAccessToken
|
||||
{
|
||||
return $this->tokens()
|
||||
->whereNull('revoked_at')
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatierter Preis („1.234,56 €"), konsistent mit der Darstellung
|
||||
* im Buchungsdetail.
|
||||
*/
|
||||
public function totalPriceFormatted(): string
|
||||
{
|
||||
return number_format((float) $this->total_price, 2, ',', '.') . ' €';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die URL auf das zuletzt erzeugte PDF dieser Version zurück,
|
||||
* oder null, wenn noch kein PDF existiert.
|
||||
*/
|
||||
public function getPdfUrl(bool $download = false): ?string
|
||||
{
|
||||
if (! $this->pdf_path) {
|
||||
return null;
|
||||
}
|
||||
return route('offer_pdf', ['versionId' => $this->id, 'do' => $download ? 'download' : null]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue