feat(128): erli orders import
Phase 128 complete: - add Erli /inbox order import with safe mark-read ACK - add cron/manual import controls and sync state tracking - map Erli orders into orderPRO aggregates with mapper tests and docs
This commit is contained in:
152
tests/Unit/ErliOrderMapperTest.php
Normal file
152
tests/Unit/ErliOrderMapperTest.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Core\Constants\IntegrationSources;
|
||||
use App\Modules\Settings\ErliOrderMapper;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RuntimeException;
|
||||
|
||||
final class ErliOrderMapperTest extends TestCase
|
||||
{
|
||||
private ErliOrderMapper $mapper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->mapper = new ErliOrderMapper();
|
||||
}
|
||||
|
||||
public function testPurchasedOrderMapsToImportAggregate(): void
|
||||
{
|
||||
$aggregate = $this->mapper->mapInboxMessage(7, $this->message('purchased'));
|
||||
|
||||
self::assertIsArray($aggregate);
|
||||
self::assertSame(IntegrationSources::ERLI, $aggregate['order']['source']);
|
||||
self::assertSame('erli-123', $aggregate['order']['source_order_id']);
|
||||
self::assertSame('nowe', $aggregate['order']['status_code']);
|
||||
self::assertSame(2, $aggregate['order']['payment_status']);
|
||||
self::assertSame(129.99, $aggregate['order']['total_with_tax']);
|
||||
self::assertSame(14.99, $aggregate['order']['delivery_price']);
|
||||
self::assertCount(1, $aggregate['items']);
|
||||
self::assertSame('Produkt Erli', $aggregate['items'][0]['original_name']);
|
||||
self::assertCount(1, $aggregate['payments']);
|
||||
}
|
||||
|
||||
public function testPendingOrderMapsAsUnpaid(): void
|
||||
{
|
||||
$aggregate = $this->mapper->mapInboxMessage(7, $this->message('pending'));
|
||||
|
||||
self::assertIsArray($aggregate);
|
||||
self::assertSame('nieoplacone', $aggregate['order']['status_code']);
|
||||
self::assertSame(0, $aggregate['order']['payment_status']);
|
||||
}
|
||||
|
||||
public function testCancelledOrderMapsCancellationFlag(): void
|
||||
{
|
||||
$aggregate = $this->mapper->mapInboxMessage(7, $this->message('cancelled'));
|
||||
|
||||
self::assertIsArray($aggregate);
|
||||
self::assertSame('anulowane', $aggregate['order']['status_code']);
|
||||
self::assertTrue($aggregate['order']['is_canceled_by_buyer']);
|
||||
}
|
||||
|
||||
public function testCompanyInvoiceDataDetectsInvoiceRequest(): void
|
||||
{
|
||||
$message = $this->message('purchased');
|
||||
$message['payload']['user']['invoiceAddress'] = [
|
||||
'type' => 'company',
|
||||
'companyName' => 'Test Sp. z o.o.',
|
||||
'nip' => '1234567890',
|
||||
'address' => 'Testowa 1',
|
||||
'street' => 'Testowa',
|
||||
'buildingNumber' => '1',
|
||||
'zip' => '00-001',
|
||||
'city' => 'Warszawa',
|
||||
'country' => 'pl',
|
||||
];
|
||||
|
||||
$aggregate = $this->mapper->mapInboxMessage(7, $message);
|
||||
|
||||
self::assertIsArray($aggregate);
|
||||
self::assertTrue($aggregate['invoice_detected']);
|
||||
self::assertSame('1234567890', $aggregate['addresses'][2]['company_tax_number']);
|
||||
}
|
||||
|
||||
public function testUnsupportedInboxMessageIsSkipped(): void
|
||||
{
|
||||
$message = $this->message('purchased');
|
||||
$message['type'] = 'productsNeedSync';
|
||||
|
||||
self::assertNull($this->mapper->mapInboxMessage(7, $message));
|
||||
}
|
||||
|
||||
public function testMissingOrderIdThrowsControlledException(): void
|
||||
{
|
||||
$message = $this->message('purchased');
|
||||
unset($message['payload']['id']);
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('ID zamowienia');
|
||||
|
||||
$this->mapper->mapInboxMessage(7, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function message(string $status): array
|
||||
{
|
||||
return [
|
||||
'id' => '5f9e1b3b0f0b9b0001c3e0a0',
|
||||
'shopId' => 99,
|
||||
'created' => '2026-05-15T10:00:00+02:00',
|
||||
'read' => false,
|
||||
'type' => 'orderCreated',
|
||||
'payload' => [
|
||||
'id' => 'erli-123',
|
||||
'externalOrderId' => 'EXT-123',
|
||||
'status' => $status,
|
||||
'created' => '2026-05-15T09:00:00+02:00',
|
||||
'updated' => '2026-05-15T09:05:00+02:00',
|
||||
'purchasedAt' => '2026-05-15T09:01:00+02:00',
|
||||
'totalPrice' => 129.99,
|
||||
'user' => [
|
||||
'email' => 'jan@example.com',
|
||||
'deliveryAddress' => [
|
||||
'firstName' => 'Jan',
|
||||
'lastName' => 'Kowalski',
|
||||
'address' => 'Testowa 1',
|
||||
'street' => 'Testowa',
|
||||
'buildingNumber' => '1',
|
||||
'zip' => '00-001',
|
||||
'city' => 'Warszawa',
|
||||
'country' => 'pl',
|
||||
'phone' => '500100200',
|
||||
],
|
||||
],
|
||||
'delivery' => [
|
||||
'methodName' => 'Kurier',
|
||||
'price' => 14.99,
|
||||
],
|
||||
'payment' => [
|
||||
'id' => 'pay-123',
|
||||
'status' => $status === 'purchased' ? 'COMPLETED' : 'NEW',
|
||||
'methodCode' => 'PAYU.p',
|
||||
],
|
||||
'items' => [
|
||||
[
|
||||
'id' => 'item-1',
|
||||
'externalProductId' => 'sku-1',
|
||||
'name' => 'Produkt Erli',
|
||||
'quantity' => 2,
|
||||
'price' => 57.50,
|
||||
'sku' => 'SKU-1',
|
||||
],
|
||||
],
|
||||
'comment' => 'Prosze o szybka wysylke',
|
||||
'sellerStatus' => 'created',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user