## Summary - implement Spec 177 inventory coverage truth across resolver, badges, KPIs, coverage page, and operation run detail surfaces - add repo-native spec artifacts for the feature under `specs/177-inventory-coverage-truth` - add unit, feature, and browser coverage for truth derivation, continuity, and inventory item filter/pagination smoke paths ## Testing - `vendor/bin/sail bin pint --dirty --format agent` - focused Spec 177 browser smoke file passed with 2 tests / 57 assertions - extended inventory-focused test pack passed with 52 tests / 434 assertions Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #208
137 lines
4.1 KiB
PHP
137 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\Inventory;
|
|
|
|
use App\Models\OperationRun;
|
|
use InvalidArgumentException;
|
|
|
|
final readonly class TenantCoverageTruth
|
|
{
|
|
/**
|
|
* @param list<TenantCoverageTypeTruth> $rows
|
|
*/
|
|
public function __construct(
|
|
public int $tenantId,
|
|
public ?OperationRun $basisRun,
|
|
public bool $hasCurrentCoverageResult,
|
|
public int $supportedTypeCount,
|
|
public int $succeededTypeCount,
|
|
public int $failedTypeCount,
|
|
public int $skippedTypeCount,
|
|
public int $unknownTypeCount,
|
|
public int $followUpTypeCount,
|
|
public int $observedItemTotal,
|
|
public array $rows,
|
|
) {
|
|
if ($this->tenantId <= 0) {
|
|
throw new InvalidArgumentException('Tenant coverage truth requires a positive tenant id.');
|
|
}
|
|
|
|
if ($this->supportedTypeCount < 0 || $this->observedItemTotal < 0) {
|
|
throw new InvalidArgumentException('Tenant coverage truth counts must be zero or greater.');
|
|
}
|
|
}
|
|
|
|
public function basisRunId(): ?int
|
|
{
|
|
return $this->basisRun instanceof OperationRun
|
|
? (int) $this->basisRun->getKey()
|
|
: null;
|
|
}
|
|
|
|
public function basisRunOutcome(): ?string
|
|
{
|
|
return $this->basisRun instanceof OperationRun
|
|
? (string) $this->basisRun->outcome
|
|
: null;
|
|
}
|
|
|
|
public function basisCompletedAtLabel(): ?string
|
|
{
|
|
if (! $this->basisRun instanceof OperationRun) {
|
|
return null;
|
|
}
|
|
|
|
$timestamp = $this->basisRun->completed_at ?? $this->basisRun->started_at ?? $this->basisRun->created_at;
|
|
|
|
return $timestamp?->diffForHumans(['short' => true]);
|
|
}
|
|
|
|
public function topPriorityFollowUpRow(): ?TenantCoverageTypeTruth
|
|
{
|
|
foreach ($this->rows as $row) {
|
|
if ($row->followUpRequired) {
|
|
return $row;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function observedTypeCount(): int
|
|
{
|
|
return count(array_filter(
|
|
$this->rows,
|
|
static fn (TenantCoverageTypeTruth $row): bool => $row->observedItemCount > 0,
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return list<TenantCoverageTypeTruth>
|
|
*/
|
|
public function followUpRows(): array
|
|
{
|
|
return array_values(array_filter(
|
|
$this->rows,
|
|
static fn (TenantCoverageTypeTruth $row): bool => $row->followUpRequired,
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* tenantId: int,
|
|
* basisRun: array{id: int, outcome: string, completedAt: string|null}|null,
|
|
* hasCurrentCoverageResult: bool,
|
|
* summary: array{
|
|
* supportedTypes: int,
|
|
* succeededTypes: int,
|
|
* failedTypes: int,
|
|
* skippedTypes: int,
|
|
* unknownTypes: int,
|
|
* followUpTypes: int,
|
|
* observedItems: int
|
|
* },
|
|
* rows: list<array<string, mixed>>
|
|
* }
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return [
|
|
'tenantId' => $this->tenantId,
|
|
'basisRun' => $this->basisRun instanceof OperationRun
|
|
? [
|
|
'id' => (int) $this->basisRun->getKey(),
|
|
'outcome' => (string) $this->basisRun->outcome,
|
|
'completedAt' => $this->basisRun->completed_at?->toIso8601String(),
|
|
]
|
|
: null,
|
|
'hasCurrentCoverageResult' => $this->hasCurrentCoverageResult,
|
|
'summary' => [
|
|
'supportedTypes' => $this->supportedTypeCount,
|
|
'succeededTypes' => $this->succeededTypeCount,
|
|
'failedTypes' => $this->failedTypeCount,
|
|
'skippedTypes' => $this->skippedTypeCount,
|
|
'unknownTypes' => $this->unknownTypeCount,
|
|
'followUpTypes' => $this->followUpTypeCount,
|
|
'observedItems' => $this->observedItemTotal,
|
|
],
|
|
'rows' => array_map(
|
|
static fn (TenantCoverageTypeTruth $row): array => $row->toArray(),
|
|
$this->rows,
|
|
),
|
|
];
|
|
}
|
|
}
|