feat: complete jobcard management workflows and UI
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../app/Domain/Reporting/ReportFilters.php';
|
||||
require_once __DIR__ . '/../app/Domain/Reporting/ReportAudience.php';
|
||||
require_once __DIR__ . '/../app/Domain/Reporting/ReportDataMapper.php';
|
||||
require_once __DIR__ . '/../app/Domain/Reporting/ReportQuery.php';
|
||||
require_once __DIR__ . '/../app/Domain/Reporting/HoursPerClientReport.php';
|
||||
require_once __DIR__ . '/../app/Domain/Reporting/TechnicianWorkloadReport.php';
|
||||
require_once __DIR__ . '/../app/Domain/Reporting/SlaReport.php';
|
||||
require_once __DIR__ . '/../app/Domain/Reporting/PrintReportRenderer.php';
|
||||
|
||||
function report_services_assert_same(mixed $expected, mixed $actual, string $message): void
|
||||
{
|
||||
if ($expected !== $actual) {
|
||||
throw new RuntimeException($message . "\nExpected: " . var_export($expected, true) . "\nActual: " . var_export($actual, true));
|
||||
}
|
||||
}
|
||||
|
||||
$filters = ReportFilters::fromArray([
|
||||
'date_from' => '2026-09-01', 'date_to' => '2026-09-30', 'client_id' => 7,
|
||||
'technician_id' => 4, 'status' => 'open', 'priority' => 'high', 'sla' => 'warning',
|
||||
]);
|
||||
$entries = [
|
||||
['client_id' => 7, 'client_name' => 'Acme', 'technician_id' => 4, 'technician_name' => 'Tess', 'work_date' => '2026-09-10', 'status' => 'open', 'priority' => 'high', 'sla_status' => 'warning', 'hours' => 1.25],
|
||||
['client_id' => 7, 'client_name' => 'Acme', 'technician_id' => 4, 'technician_name' => 'Tess', 'work_date' => '2026-09-11', 'status' => 'open', 'priority' => 'high', 'sla_status' => 'warning', 'hours' => 1.75],
|
||||
['client_id' => 8, 'client_name' => 'Beta', 'technician_id' => 4, 'work_date' => '2026-09-12', 'status' => 'open', 'priority' => 'high', 'sla_status' => 'warning', 'hours' => 9],
|
||||
];
|
||||
report_services_assert_same(
|
||||
[['client_id' => 7, 'client_name' => 'Acme', 'hours' => 3.0]],
|
||||
(new HoursPerClientReport($filters))->build($entries, ReportAudience::CLIENT),
|
||||
'Hours-per-client should apply the common filters before aggregation and return a client-safe projection.'
|
||||
);
|
||||
|
||||
$workload = (new TechnicianWorkloadReport($filters))->build($entries, ReportAudience::INTERNAL);
|
||||
report_services_assert_same(
|
||||
[['technician_id' => 4, 'technician_name' => 'Tess', 'hours' => 3.0, 'sla_hours' => 0.0]],
|
||||
$workload,
|
||||
'Technician workload should aggregate filtered hours with deterministic internal fields.'
|
||||
);
|
||||
$clientWorkload = (new TechnicianWorkloadReport())->build($entries, ReportAudience::CLIENT);
|
||||
report_services_assert_same(
|
||||
[['client_id' => 7, 'client_name' => 'Acme', 'hours' => 3.0, 'sla_hours' => 0.0], ['client_id' => 8, 'client_name' => 'Beta', 'hours' => 9.0, 'sla_hours' => 0.0]],
|
||||
$clientWorkload,
|
||||
'Client workload rows should preserve client attribution without technician identity.'
|
||||
);
|
||||
|
||||
$filtered = (new SlaReport(ReportFilters::fromArray(['sla' => 'critical'])))->build([
|
||||
['client_id' => 1, 'client_name' => 'Acme', 'allocated_hours' => 10, 'hours' => [9]],
|
||||
['client_id' => 2, 'client_name' => 'Beta', 'allocated_hours' => 10, 'hours' => [2]],
|
||||
], ReportAudience::CLIENT);
|
||||
report_services_assert_same(1, count($filtered), 'SLA status filtering should use the computed usage status.');
|
||||
report_services_assert_same(1, $filtered[0]['client_id'], 'SLA status filtering should retain the critical agreement.');
|
||||
|
||||
$html = (new PrintReportRenderer())->renderRecords('Rows', [
|
||||
['name' => '<b>Acme</b>', 'hours' => 3.0],
|
||||
]);
|
||||
if (!str_contains($html, 'application/pdf') || !str_contains($html, '<b>Acme</b>') || !str_contains($html, '@page')) {
|
||||
throw new RuntimeException('Print renderer should emit PDF-ready metadata, print CSS, and escaped record cells.');
|
||||
}
|
||||
|
||||
printf("Focused report service tests: 5 passed\n");
|
||||
Reference in New Issue
Block a user