Migrated endpoints from user to member; Renamed membership to member

This commit is contained in:
Constantin Graf
2024-05-13 19:50:50 +02:00
parent ba335b4f05
commit b2365e2778
64 changed files with 853 additions and 456 deletions

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Service;
use App\Models\Membership;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Project;
use App\Models\ProjectMember;
@@ -36,13 +36,13 @@ class BillableRateService
}
}
// Member rate
/** @var Membership|null $membership */
$membership = Membership::query()
/** @var Member|null $member */
$member = Member::query()
->where('user_id', '=', $timeEntry->user_id)
->where('organization_id', '=', $timeEntry->organization_id)
->first();
if ($membership !== null && $membership->billable_rate !== null) {
return $membership->billable_rate;
if ($member !== null && $member->billable_rate !== null) {
return $member->billable_rate;
}
// Organization rate

View File

@@ -335,20 +335,22 @@ class DashboardService
}
/**
* Rhe 4 most recently active members of your team with user_id, name, description of the latest time entry, time_entry_id, task_id and a boolean status if the team member is currently working
* Rhe 4 most recently active members of your team with member_id, name, description of the latest time entry, time_entry_id, task_id and a boolean status if the team member is currently working
*
* @return array<int, array{user_id: string, name: string, description: string|null, time_entry_id: string, task_id: string|null, status: bool }>
* @return array<int, array{member_id: string, name: string, description: string|null, time_entry_id: string, task_id: string|null, status: bool }>
*/
public function latestTeamActivity(Organization $organization): array
{
$timeEntries = TimeEntry::query()
->select(DB::raw('distinct on (user_id) user_id, description, id, task_id, start, "end"'))
->select(DB::raw('distinct on (member_id) member_id, description, id, task_id, start, "end"'))
->whereBelongsTo($organization, 'organization')
->orderBy('user_id')
->orderBy('member_id')
->orderBy('start', 'desc')
// Note: limit here does not work because of the distinct on
->with([
'user',
'member' => [
'user',
],
])
->get()
->sortByDesc('start')
@@ -358,8 +360,8 @@ class DashboardService
foreach ($timeEntries as $timeEntry) {
$response[] = [
'user_id' => $timeEntry->user_id,
'name' => $timeEntry->user->name,
'member_id' => $timeEntry->member_id,
'name' => $timeEntry->member->user->name,
'description' => $timeEntry->description,
'time_entry_id' => $timeEntry->id,
'task_id' => $timeEntry->task_id,

View File

@@ -56,6 +56,10 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
'timezone' => 'UTC',
'is_placeholder' => true,
]);
$memberId = $this->memberImportHelper->getKey([
'user_id' => $userId,
'organization_id' => $this->organization->getKey(),
]);
$clientId = null;
if ($record['Client'] !== '') {
$clientId = $this->clientImportHelper->getKey([
@@ -83,6 +87,7 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
}
$timeEntry = new TimeEntry();
$timeEntry->user_id = $userId;
$timeEntry->member_id = $memberId;
$timeEntry->task_id = $taskId;
$timeEntry->project_id = $projectId;
$timeEntry->organization_id = $this->organization->id;

View File

@@ -6,6 +6,7 @@ namespace App\Service\Import\Importers;
use App\Enums\Role;
use App\Models\Client;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Project;
use App\Models\ProjectMember;
@@ -26,6 +27,11 @@ abstract class DefaultImporter implements ImporterContract
*/
protected ImportDatabaseHelper $userImportHelper;
/**
* @var ImportDatabaseHelper<Member>
*/
protected ImportDatabaseHelper $memberImportHelper;
/**
* @var ImportDatabaseHelper<Project>
*/
@@ -77,6 +83,10 @@ abstract class DefaultImporter implements ImporterContract
'timezone:all',
],
]);
$this->memberImportHelper = new ImportDatabaseHelper(Member::class, ['user_id', 'organization_id'], true, function (Builder $builder) {
/** @var Builder<Member> $builder */
return $builder->whereBelongsTo($this->organization, 'organization');
});
$this->projectImportHelper = new ImportDatabaseHelper(Project::class, ['name', 'organization_id'], true, function (Builder $builder) {
/** @var Builder<Project> $builder */
return $builder->where('organization_id', $this->organization->id);
@@ -90,7 +100,7 @@ abstract class DefaultImporter implements ImporterContract
'integer',
],
]);
$this->projectMemberImportHelper = new ImportDatabaseHelper(ProjectMember::class, ['project_id', 'user_id'], true, function (Builder $builder) {
$this->projectMemberImportHelper = new ImportDatabaseHelper(ProjectMember::class, ['project_id', 'member_id'], true, function (Builder $builder) {
/** @var Builder<ProjectMember> $builder */
return $builder->whereBelongsToOrganization($this->organization);
}, validate: [

View File

@@ -74,13 +74,17 @@ class TogglDataImporter extends DefaultImporter
}
foreach ($workspaceUsers as $workspaceUser) {
$this->userImportHelper->getKey([
$userId = $this->userImportHelper->getKey([
'email' => $workspaceUser->email,
], [
'name' => $workspaceUser->name,
'timezone' => $workspaceUser->timezone ?? 'UTC',
'is_placeholder' => true,
], (string) $workspaceUser->uid);
$memberId = $this->memberImportHelper->getKey([
'user_id' => $userId,
'organization_id' => $this->organization->getKey(),
], [], $userId);
}
foreach ($projects as $project) {
@@ -114,10 +118,12 @@ class TogglDataImporter extends DefaultImporter
}
$projectMembers = json_decode($projectMembersFileContent);
foreach ($projectMembers as $projectMember) {
$userId = $this->userImportHelper->getKeyByExternalIdentifier((string) $projectMember->user_id);
$this->projectMemberImportHelper->getKey([
'project_id' => $projectId,
'user_id' => $this->userImportHelper->getKeyByExternalIdentifier((string) $projectMember->user_id),
'member_id' => $this->memberImportHelper->getKeyByExternalIdentifier($userId),
], [
'user_id' => $userId,
'billable_rate' => $projectMember->rate !== null ? (int) ($projectMember->rate * 100) : null,
]);
}

View File

@@ -56,6 +56,10 @@ class TogglTimeEntriesImporter extends DefaultImporter
'timezone' => 'UTC',
'is_placeholder' => true,
]);
$memberId = $this->memberImportHelper->getKey([
'user_id' => $userId,
'organization_id' => $this->organization->getKey(),
]);
$clientId = null;
if ($record['Client'] !== '') {
$clientId = $this->clientImportHelper->getKey([
@@ -83,6 +87,7 @@ class TogglTimeEntriesImporter extends DefaultImporter
}
$timeEntry = new TimeEntry();
$timeEntry->user_id = $userId;
$timeEntry->member_id = $memberId;
$timeEntry->task_id = $taskId;
$timeEntry->project_id = $projectId;
$timeEntry->organization_id = $this->organization->id;

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Service;
use App\Models\Member;
use App\Models\TimeEntry;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
@@ -59,12 +60,25 @@ class TimeEntryFilter
return $this;
}
public function addUserIdFilter(?string $userId): self
public function addMemberIdFilter(?Member $member): self
{
if ($userId === null) {
if ($member === null) {
return $this;
}
$this->builder->where('user_id', $userId);
$this->builder->where('member_id', $member->getKey());
return $this;
}
/**
* @param array<string>|null $memberIds
*/
public function addMemberIdsFilter(?array $memberIds): self
{
if ($memberIds === null) {
return $this;
}
$this->builder->whereIn('member_id', $memberIds);
return $this;
}

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace App\Service;
use App\Enums\Role;
use App\Models\Membership;
use App\Models\Member;
use App\Models\Organization;
use App\Models\ProjectMember;
use App\Models\TimeEntry;
@@ -19,12 +19,22 @@ class UserService
*/
public function assignOrganizationEntitiesToDifferentUser(Organization $organization, User $fromUser, User $toUser): void
{
/** @var Member|null $toMember */
$toMember = Member::query()
->whereBelongsTo($organization, 'organization')
->whereBelongsTo($toUser, 'user')
->first();
if ($toMember === null) {
throw new \InvalidArgumentException('User is not a member of the organization');
}
// Time entries
TimeEntry::query()
->whereBelongsTo($organization, 'organization')
->whereBelongsTo($fromUser, 'user')
->update([
'user_id' => $toUser->getKey(),
'member_id' => $toMember->getKey(),
]);
// Project members
@@ -33,6 +43,7 @@ class UserService
->whereBelongsTo($fromUser, 'user')
->update([
'user_id' => $toUser->getKey(),
'member_id' => $toMember->getKey(),
]);
}
@@ -45,13 +56,17 @@ class UserService
$organization->update([
'user_id' => $newOwner->getKey(),
]);
$userMembership = Membership::query()
/** @var Member|null $userMembership */
$userMembership = Member::query()
->whereBelongsTo($organization, 'organization')
->whereBelongsTo($newOwner, 'user')
->first();
if ($userMembership === null) {
throw new \InvalidArgumentException('User is not a member of the organization');
}
$userMembership->role = Role::Owner->value;
$userMembership->save();
$oldOwners = Membership::query()
$oldOwners = Member::query()
->whereBelongsTo($organization, 'organization')
->where('role', '=', Role::Owner->value)
->where('user_id', '!=', $newOwner->getKey())