diff --git a/app/Service/Import/Importers/TogglDataImporter.php b/app/Service/Import/Importers/TogglDataImporter.php index 3fa96789..8bcbb004 100644 --- a/app/Service/Import/Importers/TogglDataImporter.php +++ b/app/Service/Import/Importers/TogglDataImporter.php @@ -5,8 +5,11 @@ declare(strict_types=1); namespace App\Service\Import\Importers; use App\Enums\Role; +use App\Service\TimezoneService; use Exception; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Str; use Override; use Spatie\TemporaryDirectory\TemporaryDirectory; use ValueError; @@ -93,11 +96,22 @@ class TogglDataImporter extends DefaultImporter } foreach ($workspaceUsers as $workspaceUser) { + $timezone = Str::trim($workspaceUser->timezone); + if ($timezone === '') { + $timezone = 'UTC'; + } + if (! app(TimezoneService::class)->isValid($timezone)) { + Log::warning('TogglDateImporter: Invalid timezone', [ + 'timezone' => $timezone, + ]); + $timezone = 'UTC'; + } + $userId = $this->userImportHelper->getKey([ 'email' => $workspaceUser->email, ], [ 'name' => $workspaceUser->name, - 'timezone' => $workspaceUser->timezone ?? 'UTC', + 'timezone' => $timezone, 'is_placeholder' => true, ], (string) $workspaceUser->uid); $this->memberImportHelper->getKey([ diff --git a/resources/testfiles/toggl_data_import_test_2/clients.json b/resources/testfiles/toggl_data_import_test_2/clients.json new file mode 100644 index 00000000..1e1a8baa --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/clients.json @@ -0,0 +1,16 @@ +[ + { + "archived": false, + "creator_id": 201, + "id": 301, + "name": "Big Company", + "wid": 0 + }, + { + "archived": true, + "creator_id": 201, + "id": 302, + "name": "Other Company (Archived)", + "wid": 0 + } +] diff --git a/resources/testfiles/toggl_data_import_test_2/projects.json b/resources/testfiles/toggl_data_import_test_2/projects.json new file mode 100644 index 00000000..0b0b3f3c --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/projects.json @@ -0,0 +1,86 @@ +[ + { + "active": true, + "actual_hours": null, + "actual_seconds": null, + "auto_estimates": false, + "billable": false, + "cid": null, + "client_id": null, + "color": "#ef5350", + "currency": "EUR", + "estimated_hours": null, + "estimated_seconds": null, + "fixed_fee": null, + "guid": "", + "id": 401, + "is_private": true, + "name": "Project without Client", + "rate": null, + "rate_last_updated": null, + "recurring": false, + "recurring_parameters": null, + "start_date": "2020-01-01", + "status": "active", + "template": false, + "template_id": null, + "wid": 0, + "workspace_id": 0 + }, + { + "active": true, + "actual_hours": null, + "actual_seconds": null, + "auto_estimates": false, + "billable": true, + "cid": 301, + "client_id": 301, + "color": "#ec407a", + "currency": null, + "estimated_hours": null, + "estimated_seconds": null, + "fixed_fee": null, + "guid": "", + "id": 402, + "is_private": true, + "name": "Project for Big Company", + "rate": 100.01, + "rate_last_updated": null, + "recurring": false, + "recurring_parameters": null, + "start_date": "2020-01-01", + "status": "active", + "template": false, + "template_id": null, + "wid": 0, + "workspace_id": 0 + }, + { + "active": false, + "actual_hours": null, + "actual_seconds": null, + "auto_estimates": false, + "billable": true, + "cid": 302, + "client_id": 302, + "color": "#6a407f", + "currency": null, + "estimated_hours": null, + "estimated_seconds": null, + "fixed_fee": null, + "guid": "", + "id": 403, + "is_private": false, + "name": "Project (Archived)", + "rate": null, + "rate_last_updated": null, + "recurring": false, + "recurring_parameters": null, + "start_date": "2020-01-01", + "status": "active", + "template": false, + "template_id": null, + "wid": 0, + "workspace_id": 0 + } +] diff --git a/resources/testfiles/toggl_data_import_test_2/projects_users/401.json b/resources/testfiles/toggl_data_import_test_2/projects_users/401.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/projects_users/401.json @@ -0,0 +1 @@ +[] diff --git a/resources/testfiles/toggl_data_import_test_2/projects_users/402.json b/resources/testfiles/toggl_data_import_test_2/projects_users/402.json new file mode 100644 index 00000000..134ff705 --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/projects_users/402.json @@ -0,0 +1,14 @@ +[ + { + "gid": null, + "group_id": null, + "id": 801, + "labour_cost": null, + "manager": true, + "project_id": 402, + "rate": 100.02, + "rate_last_updated": null, + "user_id": 2001, + "workspace_id": 0 + } +] diff --git a/resources/testfiles/toggl_data_import_test_2/projects_users/403.json b/resources/testfiles/toggl_data_import_test_2/projects_users/403.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/projects_users/403.json @@ -0,0 +1 @@ +[] diff --git a/resources/testfiles/toggl_data_import_test_2/tags.json b/resources/testfiles/toggl_data_import_test_2/tags.json new file mode 100644 index 00000000..4bc90b57 --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/tags.json @@ -0,0 +1,14 @@ +[ + { + "creator_id": 0, + "id": 501, + "name": "Development", + "workspace_id": 0 + }, + { + "creator_id": 0, + "id": 502, + "name": "Backend", + "workspace_id": 0 + } +] diff --git a/resources/testfiles/toggl_data_import_test_2/tasks/401.json b/resources/testfiles/toggl_data_import_test_2/tasks/401.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/tasks/401.json @@ -0,0 +1 @@ +[] diff --git a/resources/testfiles/toggl_data_import_test_2/tasks/402.json b/resources/testfiles/toggl_data_import_test_2/tasks/402.json new file mode 100644 index 00000000..844a67b8 --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/tasks/402.json @@ -0,0 +1,24 @@ +[ + { + "active": true, + "estimated_seconds": 0, + "id": 601, + "name": "Task 1", + "project_id": 402, + "recurring": false, + "tracked_seconds": 0, + "user_id": null, + "workspace_id": 0 + }, + { + "active": false, + "estimated_seconds": 0, + "id": 602, + "name": "Task 2", + "project_id": 403, + "recurring": false, + "tracked_seconds": 0, + "user_id": null, + "workspace_id": 0 + } +] diff --git a/resources/testfiles/toggl_data_import_test_2/tasks/403.json b/resources/testfiles/toggl_data_import_test_2/tasks/403.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/tasks/403.json @@ -0,0 +1 @@ +[] diff --git a/resources/testfiles/toggl_data_import_test_2/workspace_users.json b/resources/testfiles/toggl_data_import_test_2/workspace_users.json new file mode 100644 index 00000000..b8456708 --- /dev/null +++ b/resources/testfiles/toggl_data_import_test_2/workspace_users.json @@ -0,0 +1,19 @@ +[ + { + "active": true, + "admin": true, + "email": "peter.test@email.test", + "group_ids": [], + "id": 201, + "inactive": false, + "labour_cost": null, + "name": "Peter Tester", + "rate": null, + "rate_last_updated": null, + "role": "admin", + "timezone": "Etc/UTC", + "uid": 2001, + "wid": 0, + "working_hours_in_minutes": null + } +] diff --git a/tests/Unit/Service/Import/Importers/TogglDataImporterTest.php b/tests/Unit/Service/Import/Importers/TogglDataImporterTest.php index ec36899c..fc5505e1 100644 --- a/tests/Unit/Service/Import/Importers/TogglDataImporterTest.php +++ b/tests/Unit/Service/Import/Importers/TogglDataImporterTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Tests\Unit\Service\Import\Importers; use App\Models\Organization; +use App\Models\User; use App\Service\Import\Importers\DefaultImporter; use App\Service\Import\Importers\ImportException; use App\Service\Import\Importers\TogglDataImporter; @@ -88,4 +89,30 @@ class TogglDataImporterTest extends ImporterTestAbstract $this->assertSame(0, $report->projectsCreated); $this->assertSame(0, $report->clientsCreated); } + + public function test_import_of_user_with_unknown_timezone_will_be_mapped_to_utc(): void + { + // Arrange + $zipPath = $this->createTestZip('toggl_data_import_test_2'); + $timezone = 'Europe/Vienna'; + $organization = Organization::factory()->create(); + $importer = new TogglDataImporter; + $importer->init($organization); + $data = file_get_contents($zipPath); + + // Act + $importer->importData($data, $timezone); + $report = $importer->getReport(); + + // Assert + $this->assertSame(0, $report->timeEntriesCreated); + $this->assertSame(2, $report->tagsCreated); + $this->assertSame(2, $report->tasksCreated); + $this->assertSame(1, $report->usersCreated); + $this->assertSame(3, $report->projectsCreated); + $this->assertSame(2, $report->clientsCreated); + $user = User::query()->where('email', '=', 'peter.test@email.test')->first(); + $this->assertSame('UTC', $user->timezone); + $this->assertTrue($user->is_placeholder); + } }