'boolean', 'started_at' => 'datetime', 'expires_at' => 'datetime', 'suspended_at' => 'datetime', 'cancelled_at' => 'datetime', 'retry_requested_at' => 'datetime', ]; // ------------------------------------------------------------------------- // Relationships // ------------------------------------------------------------------------- public function customer() { return $this->belongsTo(Customer::class); } public function product() { return $this->belongsTo(Product::class, 'product_id'); } /** The order item that triggered provisioning. */ public function orderItem() { return $this->belongsTo(OrderItem::class, 'order_item_id'); } /** The most recent invoice covering this service (updated on renewal). */ public function invoice() { return $this->belongsTo(Invoice::class, 'invoice_id'); } /** Configurable resources / features attached to this service. */ public function addons() { return $this->hasMany(ServiceAddon::class, 'service_id'); } /** The VPS/VM associated with this service — FK: services.vm_id → module_vmware.id */ public function vmware() { return $this->belongsTo(ModuleVmware::class, 'vm_id'); } // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- public function isActive(): bool { return $this->status === 'active'; } public function isPending(): bool { return $this->status === 'pending'; } public function canRetryProvisioning(): bool { return $this->isPending() && $this->retry_requested_at === null; } public function retryRequested(): bool { return $this->retry_requested_at !== null; } public function isExpired(): bool { return $this->expires_at !== null && $this->expires_at->isPast(); } public function getStatusLabelAttribute(): string { return match ($this->status) { 'pending' => 'Chờ kích hoạt', 'active' => 'Đang hoạt động', 'suspended' => 'Tạm ngưng', 'expired' => 'Hết hạn', 'cancelled' => 'Đã hủy', 'terminated' => 'Đã xóa', default => ucfirst($this->status), }; } /** Convenience: get an addon value by type. */ public function getAddon(string $type): ?string { return $this->addons->firstWhere('addon_type', $type)?->addon_value; } }