From b778622b2beb8534d0ddffac6101e751a5134622 Mon Sep 17 00:00:00 2001 From: Constantin Graf Date: Fri, 9 Oct 2026 10:37:44 +0200 Subject: [PATCH] Detect time entries with a task but no project in consistency check The comparison used != which evaluates to NULL when the time entry has no project, so entries that kept their task after the project was removed were not reported. --- .../SelfHost/SelfHostDatabaseConsistency.php | 2 +- ...SelfHostDatabaseConsistencyCommandTest.php | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/Console/Commands/SelfHost/SelfHostDatabaseConsistency.php b/app/Console/Commands/SelfHost/SelfHostDatabaseConsistency.php index 88a11184..4764bdd0 100644 --- a/app/Console/Commands/SelfHost/SelfHostDatabaseConsistency.php +++ b/app/Console/Commands/SelfHost/SelfHostDatabaseConsistency.php @@ -39,7 +39,7 @@ class SelfHostDatabaseConsistency extends Command $problems = DB::table('time_entries') ->select(['time_entries.id as id']) ->join('tasks', 'time_entries.task_id', '=', 'tasks.id') - ->where('tasks.project_id', '!=', DB::raw('time_entries.project_id')) + ->whereRaw('tasks.project_id is distinct from time_entries.project_id') ->get(); $this->logProblems($problems, 'Time entries have a task that does not belong to the project of the time entry', $hadAProblem); diff --git a/tests/Unit/Console/Commands/SelfHost/SelfHostDatabaseConsistencyCommandTest.php b/tests/Unit/Console/Commands/SelfHost/SelfHostDatabaseConsistencyCommandTest.php index 26491860..73f97f19 100644 --- a/tests/Unit/Console/Commands/SelfHost/SelfHostDatabaseConsistencyCommandTest.php +++ b/tests/Unit/Console/Commands/SelfHost/SelfHostDatabaseConsistencyCommandTest.php @@ -38,6 +38,26 @@ class SelfHostDatabaseConsistencyCommandTest extends TestCaseWithDatabase $this->assertSame("Consistency problem: Time entries have a task that does not belong to the project of the time entry\n - ".$timeEntry->getKey()."\n", $output); } + public function test_checks_that_task_need_to_be_part_of_project_in_time_entries_with_no_project_in_time_entry(): void + { + // Arrange + $user = $this->createUserWithRole(Role::Owner); + $project = Project::factory()->forOrganization($user->organization)->create(); + $task = Task::factory()->forOrganization($user->organization)->forProject($project)->create(); + $timeEntry = TimeEntry::factory()->forMember($user->member)->forTask($task)->create([ + 'project_id' => null, + 'client_id' => null, + ]); + + // Act + $exitCode = $this->withoutMockingConsoleOutput()->artisan('self-host:database-consistency'); + + // Assert + $this->assertSame(Command::FAILURE, $exitCode); + $output = Artisan::output(); + $this->assertSame("Consistency problem: Time entries have a task that does not belong to the project of the time entry\n - ".$timeEntry->getKey()."\n", $output); + } + public function test_checks_that_client_id_is_the_client_id_of_the_project(): void { // Arrange