← Code-Übersicht

ResolveVendorUseCaseTest.php

Pfad: tests/Rechnung/ResolveVendorUseCaseTest.php

Ext: php

Größe: 3397 Bytes

Geändert: 2026-07-16 17:57:44+02

<?php
declare(strict_types=1);

namespace Demo\Tests\Rechnung;

use Demo\Application\Rechnung\InvoiceProcessException;
use Demo\Application\Rechnung\ResolveVendorUseCase;
use Demo\Domain\Rechnung\Amount;
use Demo\Domain\Rechnung\ExtractedInvoiceData;
use Demo\Domain\Rechnung\InvoiceLoggerPort;
use Demo\Domain\Rechnung\LexofficePort;
use Demo\Domain\Rechnung\PostingResult;
use PHPUnit\Framework\TestCase;

final class ResolveVendorUseCaseTest extends TestCase
{
    public function testReusesOnlyExactVendorAndBillingAddress(): void
    {
        $lexware = new VendorLexwareFake([$this->contact('contact-1', 'Street 1')]);
        $id = (new ResolveVendorUseCase($lexware, new VendorLoggerFake()))->execute('run', $this->invoice());

        self::assertSame('contact-1', $id);
        self::assertSame(0, $lexware->creates);
    }

    public function testCreatesContactWhenAddressDiffers(): void
    {
        $lexware = new VendorLexwareFake([$this->contact('other', 'Other 9')]);
        $id = (new ResolveVendorUseCase($lexware, new VendorLoggerFake()))->execute('run', $this->invoice());

        self::assertSame('created-1', $id);
        self::assertSame(1, $lexware->creates);
        self::assertSame('Street 1|79106 Freiburg|DE', $lexware->lastSignature);
    }

    public function testRejectsAmbiguousExactContacts(): void
    {
        $lexware = new VendorLexwareFake([$this->contact('one', 'Street 1'), $this->contact('two', 'Street 1')]);
        $this->expectException(InvoiceProcessException::class);
        $this->expectExceptionMessage('Mehrere identische');
        (new ResolveVendorUseCase($lexware, new VendorLoggerFake()))->execute('run', $this->invoice());
    }

    /** @return array<string,mixed> */
    private function contact(string $id, string $street): array
    {
        return [
            'id' => $id,
            'roles' => ['vendor' => []],
            'company' => ['name' => 'WEtell GmbH'],
            'addresses' => ['billing' => [[
                'street' => $street,
                'zip' => '79106',
                'city' => 'Freiburg',
                'countryCode' => 'DE',
            ]]],
        ];
    }

    private function invoice(): ExtractedInvoiceData
    {
        return new ExtractedInvoiceData(
            'WEtell GmbH',
            'Street 1|79106 Freiburg|DE',
            'RF123',
            '2026-07-04',
            new Amount('26.0504', '4.9496', '31.0000', 'EUR'),
            0.99,
        );
    }
}

final class VendorLexwareFake implements LexofficePort
{
    public int $creates = 0;
    public ?string $lastSignature = null;

    /** @param list<array<string,mixed>> $contacts */
    public function __construct(private readonly array $contacts) {}
    public function findContactByName(string $vendorName): array { return $this->contacts; }
    public function createContact(string $vendorName, string $vendorAddressSignature): string
    {
        $this->creates++;
        $this->lastSignature = $vendorAddressSignature;
        return 'created-1';
    }
    public function postInvoice(ExtractedInvoiceData $invoice, string $contactId, string $pdfPath): PostingResult
    {
        return new PostingResult('unused', 'OPEN', null);
    }
}

final class VendorLoggerFake implements InvoiceLoggerPort
{
    public function logStep(string $runId, string $step, string $status, array $context = []): void {}
}