74 lines
2.2 KiB
PHP
74 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Services\Billing\DunningSchedule;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
/**
|
|
* Eine Rückstands-Episode: von der ersten gescheiterten Abbuchung bis zur
|
|
* Begleichung oder zur Sperre.
|
|
*/
|
|
class DunningCase extends Model
|
|
{
|
|
protected $fillable = [
|
|
'subscription_id', 'stripe_invoice_id', 'level',
|
|
'opened_at', 'next_step_at', 'settled_at', 'fee_invoice_ids', 'notified_levels', 'note',
|
|
];
|
|
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'level' => 'integer',
|
|
'opened_at' => 'datetime',
|
|
'next_step_at' => 'datetime',
|
|
'settled_at' => 'datetime',
|
|
'fee_invoice_ids' => 'array',
|
|
'notified_levels' => 'array',
|
|
];
|
|
}
|
|
|
|
public function subscription(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Subscription::class);
|
|
}
|
|
|
|
/**
|
|
* Die Cloud, um die es in diesem Fall geht — und nur diese.
|
|
*
|
|
* EINE Stelle für die Frage, weil Sperre und Freigabe sie beide stellen und
|
|
* zwei Antworten darauf der Weg wären, auf dem sie auseinanderlaufen: eine
|
|
* Cloud, die abgeschaltet wird und nie zurückkommt.
|
|
*
|
|
* Über die BESTELLUNG, nicht über `subscriptions.instance_id`. Die Spalte
|
|
* gibt es, aber OpenSubscription füllt sie nicht — auf dieser Installation
|
|
* war sie am 31.7.2026 bei drei von fünf Verträgen leer. Eine Sperre, die
|
|
* daran hängt, wäre für die meisten Kunden ein stiller Nulleffekt gewesen:
|
|
* nie abgeschaltet, nie eine Meldung. `order_id` war bei keinem Vertrag und
|
|
* keiner Instanz leer.
|
|
*/
|
|
public function instance(): ?Instance
|
|
{
|
|
$subscription = $this->subscription;
|
|
|
|
if ($subscription === null) {
|
|
return null;
|
|
}
|
|
|
|
return $subscription->instance
|
|
?? Instance::query()->where('order_id', $subscription->order_id)->first();
|
|
}
|
|
|
|
/** Läuft noch — weder beglichen noch von Hand geschlossen. */
|
|
public function isOpen(): bool
|
|
{
|
|
return $this->settled_at === null;
|
|
}
|
|
|
|
public function isSuspended(): bool
|
|
{
|
|
return $this->level >= DunningSchedule::SUSPENDED;
|
|
}
|
|
}
|