From c89fa87b0fe25b42a9221b14b22949cbbae1e34c Mon Sep 17 00:00:00 2001 From: Constantin Graf Date: Tue, 22 Sep 2026 11:07:47 +0200 Subject: [PATCH] Fix clockfiy importer, allow project imports without Billability --- .../Importers/ClockifyProjectsImporter.php | 3 +-- .../clockify_projects_import_test_5.csv | 4 ++++ .../Importers/ClockifyProjectsImporterTest.php | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 resources/testfiles/clockify_projects_import_test_5.csv diff --git a/app/Service/Import/Importers/ClockifyProjectsImporter.php b/app/Service/Import/Importers/ClockifyProjectsImporter.php index f60a385c..7a697c26 100644 --- a/app/Service/Import/Importers/ClockifyProjectsImporter.php +++ b/app/Service/Import/Importers/ClockifyProjectsImporter.php @@ -44,7 +44,7 @@ class ClockifyProjectsImporter extends DefaultImporter 'organization_id' => $this->organization->id, ], [ 'color' => $this->colorService->getRandomColor(), - 'is_billable' => $record['Billability'] === 'Yes', + 'is_billable' => ($record['Billability'] ?? '') === 'Yes', 'billable_rate' => $billableRateKey !== null && $record[$billableRateKey] !== '' ? (int) (((float) $record[$billableRateKey]) * 100) : null, 'estimated_time' => isset($record['Estimated (h)']) && is_numeric($record['Estimated (h)']) ? (int) ($record['Estimated (h)'] * 3600) : null, 'archived_at' => $record['Status'] === 'Archived' ? Carbon::now() : null, @@ -83,7 +83,6 @@ class ClockifyProjectsImporter extends DefaultImporter 'Project', 'Status', 'Visibility', - 'Billability', ]; foreach ($requiredFields as $requiredField) { if (! in_array($requiredField, $header, true)) { diff --git a/resources/testfiles/clockify_projects_import_test_5.csv b/resources/testfiles/clockify_projects_import_test_5.csv new file mode 100644 index 00000000..92ce706d --- /dev/null +++ b/resources/testfiles/clockify_projects_import_test_5.csv @@ -0,0 +1,4 @@ +"Project","Client","Status","Visibility","Tasks","Tracked (h)","Estimated (h)","Remaining (h)","Overage (h)","Progress(%)","Project members","Project manager","Note" +"Project for Big Company","Big Company","Active","Public","Task 1, Task 2, Task 3","1.38","","","","","Constantin Graf","","" +"Project without Client","","Active","Public","","0.00","","","","","Constantin Graf","","" +"TEST","Big Company","Active","Public","","0.00","","","","","Constantin Graf","","" diff --git a/tests/Unit/Service/Import/Importers/ClockifyProjectsImporterTest.php b/tests/Unit/Service/Import/Importers/ClockifyProjectsImporterTest.php index 2daef3a1..03cf3576 100644 --- a/tests/Unit/Service/Import/Importers/ClockifyProjectsImporterTest.php +++ b/tests/Unit/Service/Import/Importers/ClockifyProjectsImporterTest.php @@ -119,6 +119,24 @@ class ClockifyProjectsImporterTest extends ImporterTestAbstract ); } + public function test_import_of_test_file_without_billability_column_defaults_to_not_billable(): void + { + // Arrange + $organization = Organization::factory()->create(); + $timezone = 'Europe/Vienna'; + $importer = new ClockifyProjectsImporter; + $importer->init($organization); + // Some Clockify exports don't contain a "Billability" column. + $data = Storage::disk('testfiles')->get('clockify_projects_import_test_5.csv'); + + // Act + $importer->importData($data, $timezone); + + // Assert + $project = Project::query()->where('organization_id', $organization->id)->where('name', 'Project for Big Company')->firstOrFail(); + $this->assertFalse($project->is_billable); + } + public function test_import_supports_activities_column_alias_for_tasks(): void { // Arrange