Prevent time entries with end before start

Partial updates of time entries only validated end against start if both
were part of the payload. Start and end are now also validated against the
persisted value if only one of them is sent (fixes #1189).

The importers now fail if the end of a time entry is before the start and
the database consistency check reports such time entries.
This commit is contained in:
Constantin Graf
2026-10-09 13:23:51 +02:00
parent c994345de9
commit 8d34513bde
14 changed files with 300 additions and 0 deletions

View File

@@ -13,6 +13,7 @@ use App\Models\Task;
use App\Models\TimeEntry;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Artisan;
use PHPUnit\Framework\Attributes\CoversClass;
use Tests\TestCaseWithDatabase;
@@ -158,4 +159,26 @@ class SelfHostDatabaseConsistencyCommandTest extends TestCaseWithDatabase
$output = Artisan::output();
$this->assertSame("Consistency problem: Users have a current organization that they are not a member of\n - ".$user1->user->getKey()."\n", $output);
}
public function test_checks_that_end_of_time_entries_is_not_before_start(): void
{
// Arrange
$user = $this->createUserWithRole(Role::Owner);
$timeEntry = TimeEntry::factory()->forMember($user->member)->create([
'start' => Carbon::parse('2026-08-01T03:00:00Z'),
'end' => Carbon::parse('2026-08-01T02:59:55Z'),
]);
TimeEntry::factory()->forMember($user->member)->create([
'start' => Carbon::parse('2026-08-01T04:00:00Z'),
'end' => Carbon::parse('2026-08-01T04:00:00Z'),
]);
// 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 an end that is before the start\n - ".$timeEntry->getKey()."\n", $output);
}
}