'array', 'settings' => 'array', 'last_activity_at' => 'datetime', 'event_credits_balance' => 'integer', 'subscription_tier' => 'string', 'subscription_expires_at' => 'datetime', 'total_revenue' => 'decimal:2', 'settings_updated_at' => 'datetime', ]; public function events(): HasMany { return $this->hasMany(Event::class); } public function photos(): HasManyThrough { return $this->hasManyThrough( Photo::class, Event::class, 'tenant_id', 'event_id', 'id', 'id' ); } public function purchases(): HasMany { return $this->hasMany(PurchaseHistory::class); } public function eventPurchases(): HasMany { return $this->hasMany(EventPurchase::class); } public function creditsLedger(): HasMany { return $this->hasMany(EventCreditsLedger::class); } public function setSettingsAttribute($value): void { if (is_string($value)) { $this->attributes['settings'] = $value; return; } $this->attributes['settings'] = json_encode($value ?? []); } public function activeSubscription(): Attribute { return Attribute::make( get: fn () => $this->subscription_expires_at && $this->subscription_expires_at->isFuture(), ); } public function decrementCredits(int $amount, string $reason = 'event_create', ?string $note = null, ?int $relatedPurchaseId = null): bool { if ($amount <= 0) { return true; } $operation = function () use ($amount, $reason, $note, $relatedPurchaseId) { $locked = static::query() ->whereKey($this->getKey()) ->lockForUpdate() ->first(); if (! $locked || $locked->event_credits_balance < $amount) { return false; } EventCreditsLedger::create([ 'tenant_id' => $this->id, 'delta' => -$amount, 'reason' => $reason, 'related_purchase_id' => $relatedPurchaseId, 'note' => $note, ]); $locked->event_credits_balance -= $amount; $locked->save(); $this->event_credits_balance = $locked->event_credits_balance; return true; }; return $this->runCreditOperation($operation); } public function incrementCredits(int $amount, string $reason = 'manual_adjust', ?string $note = null, ?int $relatedPurchaseId = null): bool { if ($amount <= 0) { return true; } $operation = function () use ($amount, $reason, $note, $relatedPurchaseId) { $locked = static::query() ->whereKey($this->getKey()) ->lockForUpdate() ->first(); if (! $locked) { return false; } EventCreditsLedger::create([ 'tenant_id' => $this->id, 'delta' => $amount, 'reason' => $reason, 'related_purchase_id' => $relatedPurchaseId, 'note' => $note, ]); $locked->event_credits_balance += $amount; $locked->save(); $this->event_credits_balance = $locked->event_credits_balance; return true; }; return $this->runCreditOperation($operation); } private function runCreditOperation(callable $operation): bool { $connection = DB::connection(); if ($connection->transactionLevel() > 0) { return (bool) $operation(); } return (bool) $connection->transaction($operation); } }