Files
fotospiel-app/app/Models/TenantPackage.php
Codex Agent 78bd3c9267
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Allow superadmin to bypass onboarding billing
2026-01-25 00:05:34 +01:00

141 lines
3.6 KiB
PHP

<?php
namespace App\Models;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TenantPackage extends Model
{
use HasFactory;
protected $table = 'tenant_packages';
protected $fillable = [
'tenant_id',
'package_id',
'paddle_subscription_id',
'price',
'purchased_at',
'expires_at',
'used_events',
'active',
];
protected $casts = [
'price' => 'decimal:2',
'purchased_at' => 'datetime',
'expires_at' => 'datetime',
'used_events' => 'integer',
'active' => 'boolean',
'event_warning_sent_at' => 'datetime',
'event_warning_threshold' => 'float',
'event_limit_notified_at' => 'datetime',
'expiry_warning_sent_at' => 'datetime',
'expired_notified_at' => 'datetime',
];
public function tenant(): BelongsTo
{
return $this->belongsTo(Tenant::class);
}
public function package(): BelongsTo
{
return $this->belongsTo(Package::class)->withTrashed();
}
public function isActive(): bool
{
if ($this->package && $this->package->isEndcustomer()) {
return (bool) $this->active;
}
return $this->active && (! $this->expires_at || $this->expires_at->isFuture());
}
public function canCreateEvent(): bool
{
if (! $this->isActive()) {
return false;
}
if (! $this->package->isReseller()) {
return false;
}
$maxEvents = $this->package->max_events_per_year;
if ($maxEvents === null) {
return true;
}
$maxEvents = max(0, (int) $maxEvents);
return $this->used_events < $maxEvents;
}
public function getRemainingEventsAttribute(): ?int
{
if (! $this->package->isReseller()) {
return 0;
}
$max = $this->package->max_events_per_year;
if ($max === null) {
return null;
}
$max = max(0, (int) $max);
return max(0, $max - $this->used_events);
}
protected static function boot()
{
parent::boot();
static::creating(function (self $tenantPackage) {
if (! $tenantPackage->purchased_at) {
$tenantPackage->purchased_at = now();
}
if ($tenantPackage->price === null) {
$package = $tenantPackage->package ?? Package::query()->find($tenantPackage->package_id);
$tenantPackage->price = $package?->price ?? 0;
}
$package = $tenantPackage->package;
if ($package && $package->isReseller()) {
// Reseller packages represent prepaid Event-Kontingente and should not expire by default.
} elseif (! $tenantPackage->expires_at) {
$tenantPackage->expires_at = now()->addYear();
}
if ($tenantPackage->active === null) {
$tenantPackage->active = true;
}
});
static::updating(function (self $tenantPackage) {
$package = $tenantPackage->package;
if ($package && $package->isReseller()) {
if (
$tenantPackage->isDirty('expires_at')
&& $tenantPackage->expires_at instanceof CarbonInterface
&& $tenantPackage->expires_at->isPast()
) {
$tenantPackage->active = false;
}
return;
}
});
}
}