Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
/**
* Absicherung der Zeitauswertung: Die Auswertung muss auf derselben
* Datenbasis rechnen wie die Summe am Ticket – abgeschlossene
* Stoppuhr-Einträge UND die direkt an Antworten erfassten Zeiten.
*/
return static function (): void {
$src = file_get_contents(dirname(__DIR__) . '/src/Controllers/Admin/TicketTimeReportController.php');
test('Zeitquelle vereinigt Stoppuhr und Antwort-Erfassungen', function () use ($src) {
assertTrue(str_contains($src, 'ticket_time_entries'));
assertTrue(str_contains($src, 'ticket_replies'));
assertTrue(str_contains($src, 'time_spent_minutes'));
assertTrue(str_contains($src, 'UNION ALL'));
});
test('Antwort-Zweig nutzt die realen Spalten von ticket_replies', function () use ($src) {
// Verfasser-Spalte heißt author_id (author_type staff/customer) – nicht user_id
assertTrue(str_contains($src, 'tr.author_id'));
assertFalse(str_contains($src, 'tr.user_id'));
$schema = file_get_contents(dirname(__DIR__) . '/database/migrations/001_initial_schema.sql');
assertTrue(str_contains($schema, '`author_id`'));
});
test('Alle Abfragepfade nutzen die vereinigte Zeitquelle', function () use ($src) {
// fromClause, Mitarbeiter- und Einreichenden-Listen bauen auf TIME_SOURCE auf
assertSame(3, substr_count($src, 'self::TIME_SOURCE'));
// kein Abfragepfad hängt mehr direkt an der Einzeltabelle
assertFalse(str_contains($src, 'FROM ticket_time_entries te'));
assertFalse(str_contains($src, 'JOIN ticket_time_entries te'));
});
test('Override gewinnt weiterhin und Unterlauf zaehlt als 0', function () use ($src) {
assertTrue(str_contains($src, 'minutes_override'));
assertTrue(str_contains($src, 'GREATEST(0, CAST(TIMESTAMPDIFF(SECOND'));
assertTrue(str_contains($src, 'AS SIGNED'));
});
};