belongsToMany(Role::class, 'user_roles') ->withPivot(['branch_code', 'is_active', 'assigned_at', 'expires_at']) ->withTimestamps(); } /** * Get user's direct permissions */ public function permissions(): BelongsToMany { return $this->belongsToMany(Permission::class, 'user_permissions') ->withPivot(['granted', 'branch_code', 'assigned_at', 'expires_at']) ->withTimestamps(); } /** * Check if user has a specific role */ public function hasRole(string|array $roles, ?string $branchCode = null): bool { if (is_array($roles)) { return collect($roles)->some(fn($role) => $this->hasRole($role, $branchCode)); } $query = $this->roles()->where('roles.name', $roles); if ($branchCode) { $query->where('user_roles.branch_code', $branchCode); } return $query->where('user_roles.is_active', true) ->where(function ($q) { $q->whereNull('user_roles.expires_at') ->orWhere('user_roles.expires_at', '>', now()); }) ->exists(); } /** * Check if user has any of the given roles */ public function hasAnyRole(array $roles, ?string $branchCode = null): bool { return collect($roles)->some(fn($role) => $this->hasRole($role, $branchCode)); } /** * Check if user has all of the given roles */ public function hasAllRoles(array $roles, ?string $branchCode = null): bool { return collect($roles)->every(fn($role) => $this->hasRole($role, $branchCode)); } /** * Check if user has a specific permission */ public function hasPermission(string $permission, ?string $branchCode = null): bool { // Check direct permissions first $directPermission = $this->permissions() ->where('permissions.name', $permission) ->when($branchCode, fn($q) => $q->where('user_permissions.branch_code', $branchCode)) ->where('user_permissions.granted', true) ->where(function ($q) { $q->whereNull('user_permissions.expires_at') ->orWhere('user_permissions.expires_at', '>', now()); }) ->exists(); if ($directPermission) { return true; } // Check permissions through roles $rolePermissions = $this->roles() ->when($branchCode, fn($q) => $q->where('user_roles.branch_code', $branchCode)) ->where('user_roles.is_active', true) ->where(function ($q) { $q->whereNull('user_roles.expires_at') ->orWhere('user_roles.expires_at', '>', now()); }) ->whereHas('permissions', function ($q) use ($permission) { $q->where('permissions.name', $permission)->where('permissions.is_active', true); }) ->exists(); return $rolePermissions; } /** * Check if user has any of the given permissions */ public function hasAnyPermission(array $permissions, ?string $branchCode = null): bool { return collect($permissions)->some(fn($permission) => $this->hasPermission($permission, $branchCode)); } /** * Check if user has all of the given permissions */ public function hasAllPermissions(array $permissions, ?string $branchCode = null): bool { return collect($permissions)->every(fn($permission) => $this->hasPermission($permission, $branchCode)); } /** * Assign a role to user */ public function assignRole(string|Role $role, ?string $branchCode = null, ?\DateTime $expiresAt = null): self { if (is_string($role)) { $role = Role::where('name', $role)->first(); } if ($role && !$this->hasRole($role->name, $branchCode)) { $this->roles()->attach($role->id, [ 'branch_code' => $branchCode, 'is_active' => true, 'assigned_at' => now(), 'expires_at' => $expiresAt, ]); } return $this; } /** * Remove a role from user */ public function removeRole(string|Role $role, ?string $branchCode = null): self { if (is_string($role)) { $role = Role::where('name', $role)->first(); } if ($role) { $query = $this->roles()->where('role_id', $role->id); if ($branchCode) { $query->where('user_roles.branch_code', $branchCode); } $query->detach(); } return $this; } /** * Give permission directly to user */ public function givePermission(string|Permission $permission, ?string $branchCode = null, ?\DateTime $expiresAt = null): self { if (is_string($permission)) { $permission = Permission::where('name', $permission)->first(); } if ($permission) { $this->permissions()->syncWithoutDetaching([ $permission->id => [ 'granted' => true, 'branch_code' => $branchCode, 'assigned_at' => now(), 'expires_at' => $expiresAt, ] ]); } return $this; } /** * Revoke permission from user */ public function revokePermission(string|Permission $permission, ?string $branchCode = null): self { if (is_string($permission)) { $permission = Permission::where('name', $permission)->first(); } if ($permission) { $query = $this->permissions()->where('permission_id', $permission->id); if ($branchCode) { $query->where('user_permissions.branch_code', $branchCode); } $query->detach(); } return $this; } /** * Get all user permissions (from roles and direct) */ public function getAllPermissions(?string $branchCode = null): Collection { // Get direct permissions $directPermissions = $this->permissions() ->when($branchCode, fn($q) => $q->where('user_permissions.branch_code', $branchCode)) ->where('user_permissions.granted', true) ->where(function ($q) { $q->whereNull('user_permissions.expires_at') ->orWhere('user_permissions.expires_at', '>', now()); }) ->get(); // Get permissions through roles $rolePermissions = Permission::whereHas('roles.users', function ($q) use ($branchCode) { $q->where('user_id', $this->id) ->when($branchCode, fn($query) => $query->where('user_roles.branch_code', $branchCode)) ->where('user_roles.is_active', true) ->where(function ($query) { $query->whereNull('user_roles.expires_at') ->orWhere('user_roles.expires_at', '>', now()); }); })->where('is_active', true)->get(); return $directPermissions->merge($rolePermissions)->unique('id'); } /** * Check if user can access a specific module */ public function canAccessModule(string $module, ?string $branchCode = null): bool { return $this->getAllPermissions($branchCode) ->where('module', $module) ->isNotEmpty(); } }