diff --git a/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php b/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php index f0fb94ab..ba051c71 100644 --- a/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php +++ b/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php @@ -116,10 +116,12 @@ class ClockifyTimeEntriesImporter extends DefaultImporter throw new ImportException('Time entry description is too long'); } $timeEntry->description = $record['Description']; - if (! in_array($record['Billable'], ['Yes', 'No'], true)) { - throw new ImportException('Invalid billable value'); + if (isset($record['Billable'])) { + if (! in_array($record['Billable'], ['Yes', 'No'], true)) { + throw new ImportException('Invalid billable value'); + } + $timeEntry->billable = $record['Billable'] === 'Yes'; } - $timeEntry->billable = $record['Billable'] === 'Yes'; $timeEntry->tags = $this->getTags($record['Tags']); $timeEntry->is_imported = true; @@ -219,7 +221,6 @@ class ClockifyTimeEntriesImporter extends DefaultImporter 'Group', 'Email', 'Tags', - 'Billable', 'Start Date', 'Start Time', 'End Date', diff --git a/resources/testfiles/clockify_time_entries_import_test_4.csv b/resources/testfiles/clockify_time_entries_import_test_4.csv new file mode 100644 index 00000000..f5456521 --- /dev/null +++ b/resources/testfiles/clockify_time_entries_import_test_4.csv @@ -0,0 +1,3 @@ +"Project","Client","Description","Task","User","Group","Email","Tags","Start Date","Start Time","End Date","End Time","Duration (h)","Duration (decimal)","Billable Rate (USD)","Billable Amount (USD)" +"Project without Client","","","","Peter Tester","","peter.test@email.test","Development, Backend","03/04/2024","10:23:52 AM","03/04/2024","10:23:52 AM","00:00:00","0.00","0.00","0.00" +"Project for Big Company","Big Company","Working hard","Task 1","Peter Tester","","peter.test@email.test","","03/04/2024","10:23 AM","03/04/2024","11:23:01 AM","01:00:01","0.00","0.00","0.00" diff --git a/tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php b/tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php index 4d0bcd4e..9712b909 100644 --- a/tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php +++ b/tests/Unit/Service/Import/Importers/ClockifyTimeEntriesImporterTest.php @@ -41,6 +41,30 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract $this->assertSame(1, $report->clientsCreated); } + public function test_import_of_test_file_without_billable_works_and_defaults_to_non_billable(): void + { + // Arrange + $organization = Organization::factory()->create(); + $timezone = 'Europe/Vienna'; + $importer = new ClockifyTimeEntriesImporter; + $importer->init($organization); + $data = Storage::disk('testfiles')->get('clockify_time_entries_import_test_4.csv'); + + // Act + $importer->importData($data, $timezone); + $report = $importer->getReport(); + + // Assert + $testScenario = $this->checkTestScenarioAfterImportExcludingTimeEntries(false, true); + $this->checkTimeEntries($testScenario, false, true); + $this->assertSame(2, $report->timeEntriesCreated); + $this->assertSame(2, $report->tagsCreated); + $this->assertSame(1, $report->tasksCreated); + $this->assertSame(1, $report->usersCreated); + $this->assertSame(2, $report->projectsCreated); + $this->assertSame(1, $report->clientsCreated); + } + public function test_import_of_test_with_special_characters_description_succeeds(): void { // Arrange diff --git a/tests/Unit/Service/Import/Importers/ImporterTestAbstract.php b/tests/Unit/Service/Import/Importers/ImporterTestAbstract.php index 294254b9..e03082f2 100644 --- a/tests/Unit/Service/Import/Importers/ImporterTestAbstract.php +++ b/tests/Unit/Service/Import/Importers/ImporterTestAbstract.php @@ -26,7 +26,7 @@ class ImporterTestAbstract extends TestCase /** * @return object{user1: User, project1: Project, project2: Project, tag1: Tag, tag2: Tag} */ - protected function checkTestScenarioAfterImportExcludingTimeEntries(bool $detailed = false): object + protected function checkTestScenarioAfterImportExcludingTimeEntries(bool $detailed = false, bool $billableDefault = false): object { $users = User::all(); $this->assertCount(2, $users); @@ -80,12 +80,12 @@ class ImporterTestAbstract extends TestCase $this->assertSame('#ef5350', $project1->color); $this->assertSame(null, $project1->billable_rate); // Project for Big Company - $this->assertSame(true, $project2->is_billable); + $this->assertSame(! $billableDefault, $project2->is_billable); $this->assertSame(false, $project2->is_public); $this->assertSame('#ec407a', $project2->color); $this->assertSame(10001, $project2->billable_rate); // Project (Archived) - $this->assertSame(true, $project3->is_billable); + $this->assertSame(! $billableDefault, $project3->is_billable); $this->assertSame(true, $project3->is_public); $this->assertSame('#6a407f', $project3->color); $this->assertSame(null, $project3->billable_rate); @@ -176,7 +176,7 @@ class ImporterTestAbstract extends TestCase /** * @param object{user1: User, project1: Project, project2: Project, tag1: Tag, tag2: Tag} $testScenario */ - protected function checkTimeEntries(object $testScenario, bool $secondRun = false): void + protected function checkTimeEntries(object $testScenario, bool $secondRun = false, bool $billableDefault = false): void { $timeEntries = TimeEntry::all(); if ($secondRun) { @@ -197,7 +197,7 @@ class ImporterTestAbstract extends TestCase $this->assertSame('Working hard', $timeEntry2->description); $this->assertSame('2024-03-04 09:23:00', $timeEntry2->start->toDateTimeString()); $this->assertSame('2024-03-04 10:23:01', $timeEntry2->end->toDateTimeString()); - $this->assertTrue($timeEntry2->billable); + $this->assertSame(! $billableDefault, $timeEntry2->billable); $this->assertTrue($timeEntry2->is_imported); $this->assertSame([], $timeEntry2->tags); }