invoice_number ??= static::generateInvoiceNumber(); }); } public static function generateInvoiceNumber(): string { $prefix = date('y').'-'; do { $number = $prefix.str_pad(random_int(0, 99999), 5, '0', STR_PAD_LEFT); } while (static::where('invoice_number', $number)->exists()); return $number; } protected $fillable = [ 'invoice_number', 'client_id', 'status', 'invoice_date', 'sent_at', 'due_date', 'notes', 'internal_notes', ]; protected $casts = [ 'total' => MoneyCast::class, 'status' => InvoiceStatus::class, 'invoice_date' => 'date', 'due_date' => 'date', 'sent_at' => 'date', ]; public function client(): BelongsTo { return $this->belongsTo(Client::class); } public function lines(): HasMany { return $this->hasMany(InvoiceLine::class); } public function formattedTotal(): string { return '$'.number_format($this->total, 2); } public function recalculateTotal(): void { $this->total = $this->lines()->sum('amount'); $this->saveQuietly(); } public function isLocked(): bool { return in_array($this->status, [InvoiceStatus::POSTED, InvoiceStatus::PAID, InvoiceStatus::VOID]); } }