← Code-Übersicht

InvoiceState.php

Pfad: src/Domain/Rechnung/InvoiceState.php

Ext: php

Größe: 3274 Bytes

Geändert: 2026-07-16 16:54:17+02

<?php
declare(strict_types=1);

namespace Demo\Domain\Rechnung;

use InvalidArgumentException;

/**
 * Versionierter Gesamtzustand der Rechnungsverarbeitung.
 */
final class InvoiceState
{
    public const SCHEMA_VERSION = '2.0';

    /** @param list<InvoiceRecord> $records Rechnungsstaende. */
    public function __construct(public readonly string $schemaVersion, public readonly string $generatedAt, public readonly array $records)
    {
    }

    /** Erzeugt einen leeren aktuellen Zustand. */
    public static function empty(): self { return new self(self::SCHEMA_VERSION, date(DATE_ATOM), []); }

    /** @param array<string,mixed> $data Persistierte Zustandsdaten. */
    public static function fromArray(array $data): self
    {
        $rawRecords = $data['records'] ?? null;
        if (!is_array($rawRecords)) {
            throw new InvalidArgumentException('State enthaelt keine Record-Liste');
        }
        $records = [];
        foreach ($rawRecords as $raw) {
            if (!is_array($raw)) {
                throw new InvalidArgumentException('Ungueltiger State-Record');
            }
            $records[] = InvoiceRecord::fromArray($raw);
        }
        return new self(self::SCHEMA_VERSION, (string) ($data['generatedAt'] ?? date(DATE_ATOM)), $records);
    }

    /** Ersetzt einen Record gleicher Quelle oder fuegt ihn hinzu. */
    public function withRecord(InvoiceRecord $record): self
    {
        $records = [];
        $replaced = false;
        foreach ($this->records as $current) {
            if ($current->quelleId === $record->quelleId) {
                $records[] = $record;
                $replaced = true;
            } else {
                $records[] = $current;
            }
        }
        if (!$replaced) {
            $records[] = $record;
        }
        return new self(self::SCHEMA_VERSION, date(DATE_ATOM), $records);
    }

    /** @return list<InvoiceRecord> Alle Records. */
    public function all(): array { return $this->records; }

    /** Sucht nach technischer Quell-ID. */
    public function findBySourceId(string $quelleId): ?InvoiceRecord { return $this->find(static fn (InvoiceRecord $r): bool => $r->quelleId === $quelleId); }

    /** Sucht nach kanonischem Rechnungsschluessel. */
    public function findByRechnungsschluessel(string $key): ?InvoiceRecord { return $this->find(static fn (InvoiceRecord $r): bool => $r->rechnungsschluessel === $key); }

    /** Sucht nach Payload-Hash. */
    public function findByPayloadSha256(string $hash): ?InvoiceRecord { return $this->find(static fn (InvoiceRecord $r): bool => $r->payloadSha256 === $hash); }

    /** @return array{schemaVersion:string,generatedAt:string,records:list<array<string,mixed>>} */
    public function toArray(): array { return ['schemaVersion' => self::SCHEMA_VERSION, 'generatedAt' => $this->generatedAt, 'records' => array_map(static fn (InvoiceRecord $r): array => $r->toArray(), $this->records)]; }

    /** @param callable(InvoiceRecord):bool $predicate Suchpraedikat. */
    private function find(callable $predicate): ?InvoiceRecord
    {
        foreach ($this->records as $record) {
            if ($predicate($record)) {
                return $record;
            }
        }
        return null;
    }
}

Frühere Versionen

Version Zeitpunkt Operation
20 2026-07-16 19:01:40.812929+02 UPDATE