← Code-Übersicht

IngestInvoicesUseCase.php

Pfad: src/Application/Rechnung/IngestInvoicesUseCase.php

Ext: php

Größe: 3370 Bytes

Geändert: 2026-07-16 17:43:25+02

<?php
declare(strict_types=1);

namespace Demo\Application\Rechnung;

use Demo\Domain\Rechnung\ClockPort;
use Demo\Domain\Rechnung\InvoiceLoggerPort;
use Demo\Domain\Rechnung\InvoiceState;
use Demo\Domain\Rechnung\InvoiceStatePort;
use Demo\Domain\Rechnung\InvoiceStatus;
use Demo\Domain\Rechnung\SourceInvoice;

final class IngestInvoicesUseCase
{
    private readonly ProcessInvoiceUseCase $processor;

    public function __construct(
        private readonly InvoiceStatePort $statePort,
        private readonly DiscoverInvoicesUseCase $discover,
        DownloadInvoiceUseCase $download,
        ExtractInvoiceDataUseCase $extract,
        ResolveVendorUseCase $resolveVendor,
        PostInvoiceUseCase $post,
        private readonly InvoiceLoggerPort $logger,
        private readonly ClockPort $clock,
        int $maxRetries,
        float $amountTolerance,
    ) {
        $this->processor = new ProcessInvoiceUseCase($statePort, $download, $extract, $resolveVendor, $post, $logger, $clock, $maxRetries, $amountTolerance);
    }

    public function execute(string $runId, string $username, string $password): IngestResult
    {
        return $this->statePort->withExclusiveLock(
            fn (): IngestResult => $this->executeLocked($runId, $username, $password),
        );
    }

    private function executeLocked(string $runId, string $username, string $password): IngestResult
    {
        $loaded = $this->statePort->load();
        $state = new InvoiceState($loaded->schemaVersion, $this->clock->now(), $loaded->all());
        $sources = $this->discover->execute($runId, $username, $password);
        if ($sources === []) {
            $this->statePort->save($state);
            return new IngestResult($runId, InvoiceStatus::DONE_NO_WORK, 0, 0, 0, 0, ['summary' => 'leer']);
        }
        $processed = $skipped = $errors = 0;
        foreach ($sources as $source) {
            if (!$this->shouldProcess($source, $state)) {
                $skipped++;
                continue;
            }
            $outcome = $this->processor->execute($runId, $source, $state);
            $state = $outcome->state;
            match ($outcome->result) {
                InvoiceProcessOutcome::PROCESSED => $processed++,
                InvoiceProcessOutcome::SKIPPED => $skipped++,
                default => $errors++,
            };
        }
        $status = $this->runStatus(count($sources), $errors);
        $this->logger->logStep($runId, 'run', $status, compact('processed', 'skipped', 'errors'));
        return new IngestResult($runId, $status, count($sources), $processed, $skipped, $errors, compact('processed', 'skipped', 'errors'));
    }

    private function shouldProcess(SourceInvoice $source, InvoiceState $state): bool
    {
        $bySource = $state->findBySourceId($source->sourceId);
        $byKey = $state->findByRechnungsschluessel($source->rechnungsschluessel());
        return !($bySource !== null && InvoiceStatus::isSuccess($bySource->status))
            && !($byKey !== null && InvoiceStatus::isSuccess($byKey->status));
    }

    private function runStatus(int $discovered, int $errors): string
    {
        if ($errors === 0) { return InvoiceStatus::DONE; }
        return ($errors / $discovered) * 100 >= 20.0
            ? InvoiceStatus::DONE_PARTIAL
            : InvoiceStatus::DONE_WITH_WARNINGS;
    }
}

Frühere Versionen

Version Zeitpunkt Operation
34 2026-07-16 19:55:01.885759+02 UPDATE