mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-12 10:12:17 +01:00
Compare commits
2 Commits
feature/fr
...
pullreques
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7922af92e2 | ||
|
|
d1d2aedbae |
@@ -38,11 +38,17 @@ class ClientController extends Controller
|
|||||||
public function index(Organization $organization, ClientIndexRequest $request): ClientCollection
|
public function index(Organization $organization, ClientIndexRequest $request): ClientCollection
|
||||||
{
|
{
|
||||||
$this->checkPermission($organization, 'clients:view');
|
$this->checkPermission($organization, 'clients:view');
|
||||||
|
$canViewAllClients = $this->hasPermission($organization, 'clients:view:all');
|
||||||
|
$user = $this->user();
|
||||||
|
|
||||||
$clientsQuery = Client::query()
|
$clientsQuery = Client::query()
|
||||||
->whereBelongsTo($organization, 'organization')
|
->whereBelongsTo($organization, 'organization')
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('created_at', 'desc');
|
||||||
|
|
||||||
|
if (! $canViewAllClients) {
|
||||||
|
$clientsQuery->visibleByEmployee($user);
|
||||||
|
}
|
||||||
|
|
||||||
$filterArchived = $request->getFilterArchived();
|
$filterArchived = $request->getFilterArchived();
|
||||||
if ($filterArchived === 'true') {
|
if ($filterArchived === 'true') {
|
||||||
$clientsQuery->whereNotNull('archived_at');
|
$clientsQuery->whereNotNull('archived_at');
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace App\Models;
|
|||||||
use App\Models\Concerns\CustomAuditable;
|
use App\Models\Concerns\CustomAuditable;
|
||||||
use App\Models\Concerns\HasUuids;
|
use App\Models\Concerns\HasUuids;
|
||||||
use Database\Factories\ClientFactory;
|
use Database\Factories\ClientFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@@ -62,6 +63,18 @@ class Client extends Model implements AuditableContract
|
|||||||
return $this->hasMany(Project::class, 'client_id');
|
return $this->hasMany(Project::class, 'client_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Builder<Client> $builder
|
||||||
|
* @return Builder<Client>
|
||||||
|
*/
|
||||||
|
public function scopeVisibleByEmployee(Builder $builder, User $user): Builder
|
||||||
|
{
|
||||||
|
return $builder->whereHas('projects', function (Builder $builder) use ($user): Builder {
|
||||||
|
/** @var Builder<Project> $builder */
|
||||||
|
return $builder->visibleByEmployee($user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Attribute<bool, never>
|
* @return Attribute<bool, never>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ class JetstreamServiceProvider extends ServiceProvider
|
|||||||
'tags:update',
|
'tags:update',
|
||||||
'tags:delete',
|
'tags:delete',
|
||||||
'clients:view',
|
'clients:view',
|
||||||
|
'clients:view:all',
|
||||||
'clients:create',
|
'clients:create',
|
||||||
'clients:update',
|
'clients:update',
|
||||||
'clients:delete',
|
'clients:delete',
|
||||||
@@ -172,6 +173,7 @@ class JetstreamServiceProvider extends ServiceProvider
|
|||||||
'tags:update',
|
'tags:update',
|
||||||
'tags:delete',
|
'tags:delete',
|
||||||
'clients:view',
|
'clients:view',
|
||||||
|
'clients:view:all',
|
||||||
'clients:create',
|
'clients:create',
|
||||||
'clients:update',
|
'clients:update',
|
||||||
'clients:delete',
|
'clients:delete',
|
||||||
@@ -232,6 +234,7 @@ class JetstreamServiceProvider extends ServiceProvider
|
|||||||
'tags:update',
|
'tags:update',
|
||||||
'tags:delete',
|
'tags:delete',
|
||||||
'clients:view',
|
'clients:view',
|
||||||
|
'clients:view:all',
|
||||||
'clients:create',
|
'clients:create',
|
||||||
'clients:update',
|
'clients:update',
|
||||||
'clients:delete',
|
'clients:delete',
|
||||||
@@ -256,12 +259,13 @@ class JetstreamServiceProvider extends ServiceProvider
|
|||||||
'projects:view',
|
'projects:view',
|
||||||
'tags:view',
|
'tags:view',
|
||||||
'tasks:view',
|
'tasks:view',
|
||||||
|
'clients:view',
|
||||||
'time-entries:view:own',
|
'time-entries:view:own',
|
||||||
'time-entries:create:own',
|
'time-entries:create:own',
|
||||||
'time-entries:update:own',
|
'time-entries:update:own',
|
||||||
'time-entries:delete:own',
|
'time-entries:delete:own',
|
||||||
'organizations:view',
|
'organizations:view',
|
||||||
])->description('Employees have the ability to read, create, and update their own time entries and they can see the projects that they are members of.');
|
])->description('Employees have the ability to read, create, and update their own time entries, they can see the projects that they are members of and the clients they are assigned to.');
|
||||||
|
|
||||||
Jetstream::role(Role::Placeholder->value, 'Placeholder', [
|
Jetstream::role(Role::Placeholder->value, 'Placeholder', [
|
||||||
])->description('Placeholders are used for importing data. They cannot log in and have no permissions.');
|
])->description('Placeholders are used for importing data. They cannot log in and have no permissions.');
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class ClientEndpointTest extends ApiEndpointTestAbstract
|
|||||||
// Arrange
|
// Arrange
|
||||||
$data = $this->createUserWithPermission([
|
$data = $this->createUserWithPermission([
|
||||||
'clients:view',
|
'clients:view',
|
||||||
|
'clients:view:all',
|
||||||
]);
|
]);
|
||||||
$clients = Client::factory()->forOrganization($data->organization)->randomCreatedAt()->createMany(4);
|
$clients = Client::factory()->forOrganization($data->organization)->randomCreatedAt()->createMany(4);
|
||||||
Passport::actingAs($data->user);
|
Passport::actingAs($data->user);
|
||||||
@@ -57,11 +58,43 @@ class ClientEndpointTest extends ApiEndpointTestAbstract
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_index_endpoint_returns_list_of_clients_assigned_to_employee_user(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$data = $this->createUserWithPermission([
|
||||||
|
'clients:view',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$clients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
||||||
|
$projectWithMembership1 = Project::factory()->forOrganization($data->organization)->forClient($clients->get(0))->addMember($data->member)->isPrivate()->create();
|
||||||
|
$projectWithMembership2 = Project::factory()->forOrganization($data->organization)->forClient($clients->get(1))->addMember($data->member)->isPrivate()->create();
|
||||||
|
|
||||||
|
$otherClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
||||||
|
$projectWithoutMembership = Project::factory()->forOrganization($data->organization)->forClient($otherClients->get(0))->isPrivate()->create();
|
||||||
|
Passport::actingAs($data->user);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$response = $this->getJson(route('api.v1.clients.index', [$data->organization->getKey()]));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonCount(2, 'data');
|
||||||
|
$response->assertJson(fn (AssertableJson $json) => $json
|
||||||
|
->has('data')
|
||||||
|
->has('links')
|
||||||
|
->has('meta')
|
||||||
|
->count('data', 2)
|
||||||
|
->where('data.0.id', $clients->get(0)->getKey())
|
||||||
|
->where('data.1.id', $clients->get(1)->getKey())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_index_endpoint_without_filter_archived_returns_only_non_archived_clients(): void
|
public function test_index_endpoint_without_filter_archived_returns_only_non_archived_clients(): void
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
$data = $this->createUserWithPermission([
|
$data = $this->createUserWithPermission([
|
||||||
'clients:view',
|
'clients:view',
|
||||||
|
'clients:view:all',
|
||||||
]);
|
]);
|
||||||
$archivedClients = Client::factory()->forOrganization($data->organization)->archived()->createMany(2);
|
$archivedClients = Client::factory()->forOrganization($data->organization)->archived()->createMany(2);
|
||||||
$nonArchivedClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
$nonArchivedClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
||||||
@@ -81,6 +114,7 @@ class ClientEndpointTest extends ApiEndpointTestAbstract
|
|||||||
// Arrange
|
// Arrange
|
||||||
$data = $this->createUserWithPermission([
|
$data = $this->createUserWithPermission([
|
||||||
'clients:view',
|
'clients:view',
|
||||||
|
'clients:view:all',
|
||||||
]);
|
]);
|
||||||
$archivedClients = Client::factory()->forOrganization($data->organization)->archived()->createMany(2);
|
$archivedClients = Client::factory()->forOrganization($data->organization)->archived()->createMany(2);
|
||||||
$nonArchivedClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
$nonArchivedClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
||||||
@@ -103,6 +137,7 @@ class ClientEndpointTest extends ApiEndpointTestAbstract
|
|||||||
// Arrange
|
// Arrange
|
||||||
$data = $this->createUserWithPermission([
|
$data = $this->createUserWithPermission([
|
||||||
'clients:view',
|
'clients:view',
|
||||||
|
'clients:view:all',
|
||||||
]);
|
]);
|
||||||
$archivedClients = Client::factory()->forOrganization($data->organization)->archived()->createMany(2);
|
$archivedClients = Client::factory()->forOrganization($data->organization)->archived()->createMany(2);
|
||||||
$nonArchivedClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
$nonArchivedClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
||||||
@@ -125,6 +160,7 @@ class ClientEndpointTest extends ApiEndpointTestAbstract
|
|||||||
// Arrange
|
// Arrange
|
||||||
$data = $this->createUserWithPermission([
|
$data = $this->createUserWithPermission([
|
||||||
'clients:view',
|
'clients:view',
|
||||||
|
'clients:view:all',
|
||||||
]);
|
]);
|
||||||
$archivedClients = Client::factory()->forOrganization($data->organization)->archived()->createMany(2);
|
$archivedClients = Client::factory()->forOrganization($data->organization)->archived()->createMany(2);
|
||||||
$nonArchivedClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
$nonArchivedClients = Client::factory()->forOrganization($data->organization)->createMany(2);
|
||||||
|
|||||||
Reference in New Issue
Block a user