Some checks are pending
CI / test (push) Waiting to run
Consistent brace style, spacing, and method expansion throughout domain, infrastructure, and test files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
182 lines
6.1 KiB
PHP
182 lines
6.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Infrastructure\Channel\Frappe;
|
|
|
|
use App\Domain\Article\Article;
|
|
use App\Domain\Article\ArticleCondition;
|
|
use App\Domain\Article\ArticleType;
|
|
use App\Domain\Channel\Platform;
|
|
use App\Domain\Order\Customer;
|
|
use App\Domain\Order\Order;
|
|
use App\Infrastructure\Channel\Frappe\FrappeErpAdapter;
|
|
use App\Infrastructure\Channel\Frappe\FrappeHttpClient;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class FrappeErpAdapterTest extends TestCase
|
|
{
|
|
private FrappeHttpClient&MockObject $frappe;
|
|
private FrappeErpAdapter $adapter;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->frappe = $this->createMock(FrappeHttpClient::class);
|
|
$this->adapter = new FrappeErpAdapter($this->frappe, 'REFURB-HW');
|
|
}
|
|
|
|
public function testCreateCustomerReturnsFrappeId(): void
|
|
{
|
|
$this->frappe
|
|
->method('post')
|
|
->with('/api/resource/Customer', $this->isType('array'))
|
|
->willReturn(['data' => ['name' => 'CUST-00001']]);
|
|
|
|
$customer = new Customer('Max Mustermann', 'max@test.de', ['street' => 'Str 1', 'city' => 'Berlin', 'zip' => '10115']);
|
|
$customer->addPlatformId('ebay', 'buyer123');
|
|
|
|
$result = $this->adapter->createCustomer($customer);
|
|
|
|
$this->assertSame('CUST-00001', $result);
|
|
}
|
|
|
|
public function testCreateSalesInvoiceSubmitsAndReturnsId(): void
|
|
{
|
|
$this->frappe
|
|
->expects($this->once())
|
|
->method('post')
|
|
->willReturn(['data' => ['name' => 'SINV-00001']]);
|
|
|
|
$this->frappe
|
|
->expects($this->once())
|
|
->method('put')
|
|
->with($this->stringContains('SINV-00001'), ['docstatus' => 1])
|
|
->willReturn(['data' => ['name' => 'SINV-00001', 'docstatus' => 1]]);
|
|
|
|
$customer = new Customer('Max Mustermann', 'max@test.de', ['street' => 'Str 1', 'city' => 'Berlin', 'zip' => '10115']);
|
|
$customer->setFrappeCustomerId('CUST-00001');
|
|
|
|
$order = new Order(
|
|
new Article(new ArticleType('Laptop'), 'LAP-001', 'INV-001', 1, ArticleCondition::Good),
|
|
$customer,
|
|
new Platform('ebay', 'eBay DE'),
|
|
'ORDER-001',
|
|
'299.99',
|
|
new \DateTimeImmutable('2026-05-13'),
|
|
);
|
|
|
|
$result = $this->adapter->createSalesInvoice($order);
|
|
|
|
$this->assertSame('SINV-00001', $result);
|
|
}
|
|
|
|
public function testFetchInvoicePdfReturnsBinary(): void
|
|
{
|
|
$this->frappe
|
|
->method('getContent')
|
|
->with($this->stringContains('SINV-00001'))
|
|
->willReturn('%PDF-binary-content');
|
|
|
|
$result = $this->adapter->fetchInvoicePdf('SINV-00001');
|
|
|
|
$this->assertStringStartsWith('%PDF', $result);
|
|
}
|
|
|
|
public function testFindExistingCustomerReturnsIdWhenAddressMatches(): void
|
|
{
|
|
$this->frappe
|
|
->expects($this->exactly(2))
|
|
->method('get')
|
|
->willReturnOnConsecutiveCalls(
|
|
['data' => [['name' => 'CUST-99999']]],
|
|
['data' => [['address_line1' => 'Kirchstr. 1', 'city' => 'Karbach', 'pincode' => '56281']]],
|
|
);
|
|
|
|
$result = $this->adapter->findExistingCustomer('Simon Kühn', 'Kirchstr. 1', 'Karbach', '56281');
|
|
|
|
$this->assertSame('CUST-99999', $result);
|
|
}
|
|
|
|
public function testFindExistingCustomerReturnsNullWhenAddressMismatch(): void
|
|
{
|
|
$this->frappe
|
|
->expects($this->exactly(2))
|
|
->method('get')
|
|
->willReturnOnConsecutiveCalls(
|
|
['data' => [['name' => 'CUST-99999']]],
|
|
['data' => [['address_line1' => 'Musterstr. 5', 'city' => 'Berlin', 'pincode' => '10115']]],
|
|
);
|
|
|
|
$result = $this->adapter->findExistingCustomer('Simon Kühn', 'Kirchstr. 1', 'Karbach', '56281');
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function testFindExistingCustomerReturnsNullWhenNotInErp(): void
|
|
{
|
|
$this->frappe
|
|
->expects($this->once())
|
|
->method('get')
|
|
->willReturn(['data' => []]);
|
|
|
|
$result = $this->adapter->findExistingCustomer('Nobody', 'Unknown St. 1', 'Nowhere', '00000');
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function testFindSimonAndCreateInvoiceFor1337(): void
|
|
{
|
|
$this->frappe
|
|
->expects($this->exactly(2))
|
|
->method('get')
|
|
->willReturnOnConsecutiveCalls(
|
|
['data' => [['name' => 'CUST-99999']]],
|
|
['data' => [['address_line1' => 'Kirchstr. 1', 'city' => 'Karbach', 'pincode' => '56281']]],
|
|
);
|
|
|
|
$this->frappe
|
|
->expects($this->once())
|
|
->method('post')
|
|
->willReturn(['data' => ['name' => 'SINV-13370']]);
|
|
|
|
$this->frappe
|
|
->expects($this->once())
|
|
->method('put')
|
|
->willReturn(['data' => ['name' => 'SINV-13370', 'docstatus' => 1]]);
|
|
|
|
$frappeId = $this->adapter->findExistingCustomer('Simon Kühn', 'Kirchstr. 1', 'Karbach', '56281');
|
|
$this->assertSame('CUST-99999', $frappeId);
|
|
|
|
$simon = new Customer('Simon Kühn', 'simon.kuehn83@gmail.com', [
|
|
'street' => 'Kirchstr. 1',
|
|
'city' => 'Karbach',
|
|
'zip' => '56281',
|
|
]);
|
|
$simon->setFrappeCustomerId($frappeId);
|
|
|
|
$article = new Article(
|
|
new ArticleType('Laptop'),
|
|
'LAP-THINK-001',
|
|
'INV-THINK-001',
|
|
1,
|
|
ArticleCondition::Good,
|
|
);
|
|
$article->setManufacturer('Lenovo');
|
|
$article->setModelName('ThinkBook 14 G6 IRL');
|
|
$article->setModelNumber('21KG00NQGE');
|
|
$article->setEbayTitle('Lenovo ThinkBook 14 G6 IRL — generalüberholt, top Zustand');
|
|
|
|
$order = new Order(
|
|
$article,
|
|
$simon,
|
|
new Platform('direct', 'Direktverkauf'),
|
|
'ORDER-SIMON-1337',
|
|
'1337.00',
|
|
new \DateTimeImmutable('2026-05-18'),
|
|
);
|
|
|
|
$invoiceId = $this->adapter->createSalesInvoice($order);
|
|
$this->assertSame('SINV-13370', $invoiceId);
|
|
}
|
|
}
|