296 lines
10 KiB
PHP
296 lines
10 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Catalog;
|
|
|
|
use App\Domains\Attachable\Enums\AttachmentType;
|
|
use App\Domains\Attachable\Models\Attachment;
|
|
use App\Domains\Catalog\Enums\CatalogItemType;
|
|
use App\Domains\Catalog\Models\Attribute;
|
|
use App\Domains\Catalog\Models\CatalogItem;
|
|
use App\Domains\Catalog\Models\Inventory;
|
|
use App\Domains\Catalog\Models\Variant;
|
|
use App\Domains\Catalog\Services\CatalogService;
|
|
use App\Domains\Shared\Enums\FieldType;
|
|
use App\Domains\Tenant\Models\Tenant;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Tests\TestCase;
|
|
|
|
class CatalogServiceTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private CatalogService $service;
|
|
|
|
private Tenant $tenant;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->service = app(CatalogService::class);
|
|
$this->tenant = $this->createTenant();
|
|
}
|
|
|
|
public function test_it_creates_an_item_with_direct_inventory_when_it_has_no_variants(): void
|
|
{
|
|
$item = $this->service->create([
|
|
'tenant_code' => $this->tenant->codigo,
|
|
'slug' => 'simple-item',
|
|
'nombre' => 'Simple item',
|
|
'precio' => 100,
|
|
'real_stock' => 12,
|
|
]);
|
|
|
|
$this->assertNotNull($item->inventory_id);
|
|
$this->assertSame(CatalogItemType::Standard, $item->type);
|
|
$this->assertCount(0, $item->variants);
|
|
$this->assertSame(12, $item->inventory->real_stock);
|
|
$this->assertSame(0, $item->inventory->reserved_stock);
|
|
$this->assertSame(0, $item->inventory->sold_units);
|
|
}
|
|
|
|
public function test_it_creates_variant_inventory_without_direct_item_inventory(): void
|
|
{
|
|
$attribute = Attribute::query()->create([
|
|
'tenant_codigo' => $this->tenant->codigo,
|
|
'codigo' => 'size',
|
|
'nombre' => 'Size',
|
|
'type' => FieldType::String,
|
|
]);
|
|
$item = $this->service->create([
|
|
'tenant_code' => $this->tenant->codigo,
|
|
'slug' => 'variant-item',
|
|
'nombre' => 'Variant item',
|
|
'precio' => 100,
|
|
'attribute_codes' => [$attribute->codigo],
|
|
'variants' => [
|
|
[
|
|
'real_stock' => 5,
|
|
'values' => [$attribute->codigo => 'S'],
|
|
],
|
|
[
|
|
'real_stock' => 8,
|
|
'values' => [$attribute->codigo => 'M'],
|
|
],
|
|
],
|
|
]);
|
|
|
|
$this->assertNull($item->inventory_id);
|
|
$this->assertCount(2, $item->variants);
|
|
$this->assertCount(1, $item->itemAttributes);
|
|
$this->assertTrue($item->itemAttributes->first()->attribute->is($attribute));
|
|
$this->assertSame([5, 8], $item->variants->pluck('inventory.real_stock')->all());
|
|
$this->assertSame(
|
|
['S', 'M'],
|
|
$item->variants->pluck('definitions')->flatten()->pluck('value')->all(),
|
|
);
|
|
|
|
foreach ($item->variants as $variant) {
|
|
$this->assertNotNull($variant->inventory_id);
|
|
$this->assertSame(0, $variant->inventory->reserved_stock);
|
|
$this->assertSame(0, $variant->inventory->sold_units);
|
|
}
|
|
}
|
|
|
|
public function test_variant_use_dates_override_or_inherit_catalog_item_dates(): void
|
|
{
|
|
$attribute = $this->createAttribute('day');
|
|
$itemMinimum = Carbon::parse('2026-08-01 09:00:00');
|
|
$itemMaximum = Carbon::parse('2026-08-31 18:00:00');
|
|
$variantMaximum = Carbon::parse('2026-08-15 18:00:00');
|
|
|
|
$item = $this->service->create([
|
|
'tenant_code' => $this->tenant->codigo,
|
|
'slug' => 'dated-variants',
|
|
'nombre' => 'Dated variants',
|
|
'precio' => 100,
|
|
'minimum_use_date' => $itemMinimum,
|
|
'maximum_use_date' => $itemMaximum,
|
|
'attribute_codes' => [$attribute->codigo],
|
|
'variants' => [
|
|
[
|
|
'real_stock' => 5,
|
|
'maximum_use_date' => $variantMaximum,
|
|
'values' => [$attribute->codigo => 'Saturday'],
|
|
],
|
|
],
|
|
]);
|
|
|
|
$variant = $item->variants->firstOrFail();
|
|
|
|
$this->assertNull($variant->minimum_use_date);
|
|
$this->assertTrue($variant->maximum_use_date->equalTo($variantMaximum));
|
|
$this->assertTrue($variant->getMinimumUseDate()->equalTo($itemMinimum));
|
|
$this->assertTrue($variant->getMaximumUseDate()->equalTo($variantMaximum));
|
|
}
|
|
|
|
public function test_it_rejects_an_invalid_effective_variant_use_date_range(): void
|
|
{
|
|
$attribute = $this->createAttribute('day');
|
|
|
|
try {
|
|
$this->service->create([
|
|
'tenant_code' => $this->tenant->codigo,
|
|
'slug' => 'invalid-dated-variant',
|
|
'nombre' => 'Invalid dated variant',
|
|
'precio' => 100,
|
|
'minimum_use_date' => '2026-08-10 09:00:00',
|
|
'maximum_use_date' => '2026-08-31 18:00:00',
|
|
'attribute_codes' => [$attribute->codigo],
|
|
'variants' => [
|
|
[
|
|
'real_stock' => 5,
|
|
'maximum_use_date' => '2026-08-09 18:00:00',
|
|
'values' => [$attribute->codigo => 'Saturday'],
|
|
],
|
|
],
|
|
]);
|
|
|
|
$this->fail('A validation exception was not thrown.');
|
|
} catch (ValidationException $exception) {
|
|
$this->assertArrayHasKey(
|
|
'variants.0.maximum_use_date',
|
|
$exception->errors(),
|
|
);
|
|
}
|
|
|
|
$this->assertDatabaseMissing('catalog_items', [
|
|
'slug' => 'invalid-dated-variant',
|
|
]);
|
|
}
|
|
|
|
public function test_it_rejects_direct_inventory_together_with_variants(): void
|
|
{
|
|
$attribute = $this->createAttribute('size');
|
|
|
|
try {
|
|
$this->service->create([
|
|
'tenant_code' => $this->tenant->codigo,
|
|
'slug' => 'invalid-item',
|
|
'nombre' => 'Invalid item',
|
|
'precio' => 100,
|
|
'real_stock' => 10,
|
|
'attribute_codes' => [$attribute->codigo],
|
|
'variants' => [
|
|
['real_stock' => 5],
|
|
],
|
|
]);
|
|
|
|
$this->fail('A validation exception was not thrown.');
|
|
} catch (ValidationException $exception) {
|
|
$this->assertArrayHasKey('real_stock', $exception->errors());
|
|
}
|
|
|
|
$this->assertDatabaseMissing('catalog_items', ['slug' => 'invalid-item']);
|
|
$this->assertSame(0, CatalogItem::query()->count());
|
|
$this->assertSame(0, Variant::query()->count());
|
|
$this->assertSame(0, Inventory::query()->count());
|
|
}
|
|
|
|
public function test_it_rejects_variants_when_attribute_codes_are_empty(): void
|
|
{
|
|
$this->expectException(ValidationException::class);
|
|
|
|
$this->service->create([
|
|
'tenant_code' => $this->tenant->codigo,
|
|
'slug' => 'variants-without-attributes',
|
|
'nombre' => 'Variants without attributes',
|
|
'precio' => 100,
|
|
'variants' => [
|
|
['real_stock' => 5],
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function test_it_requires_variants_when_attribute_codes_are_present(): void
|
|
{
|
|
$attribute = $this->createAttribute('color');
|
|
|
|
$this->expectException(ValidationException::class);
|
|
|
|
$this->service->create([
|
|
'tenant_code' => $this->tenant->codigo,
|
|
'slug' => 'attributes-without-variants',
|
|
'nombre' => 'Attributes without variants',
|
|
'precio' => 100,
|
|
'attribute_codes' => [$attribute->codigo],
|
|
]);
|
|
}
|
|
|
|
public function test_it_only_resolves_attribute_codes_from_the_item_tenant(): void
|
|
{
|
|
$otherTenant = $this->createTenant('other-tenant');
|
|
Attribute::query()->create([
|
|
'tenant_codigo' => $otherTenant->codigo,
|
|
'codigo' => 'size',
|
|
'nombre' => 'Size',
|
|
'type' => FieldType::String,
|
|
]);
|
|
|
|
try {
|
|
$this->service->create([
|
|
'tenant_code' => $this->tenant->codigo,
|
|
'slug' => 'foreign-attribute',
|
|
'nombre' => 'Foreign attribute',
|
|
'precio' => 100,
|
|
'attribute_codes' => ['size'],
|
|
'variants' => [
|
|
[
|
|
'real_stock' => 5,
|
|
'values' => ['size' => 'M'],
|
|
],
|
|
],
|
|
]);
|
|
|
|
$this->fail('A validation exception was not thrown.');
|
|
} catch (ValidationException $exception) {
|
|
$this->assertArrayHasKey('attribute_codes', $exception->errors());
|
|
}
|
|
|
|
$this->assertDatabaseMissing('catalog_items', ['slug' => 'foreign-attribute']);
|
|
$this->assertSame(0, Inventory::query()->count());
|
|
}
|
|
|
|
private function createTenant(string $code = 'catalog-service'): Tenant
|
|
{
|
|
$headerLogo = $this->createAttachment("{$code}-header");
|
|
$footerLogo = $this->createAttachment("{$code}-footer");
|
|
|
|
return Tenant::query()->create([
|
|
'codigo' => $code,
|
|
'nombre' => ucfirst($code),
|
|
'dominio' => "{$code}.local",
|
|
'primary_color' => '#000000',
|
|
'secondary_color' => '#000000',
|
|
'danger_color' => '#000000',
|
|
'success_color' => '#000000',
|
|
'header_bg_color' => '#000000',
|
|
'footer_bg_color' => '#000000',
|
|
'header_logo_id' => $headerLogo->id,
|
|
'footer_logo_id' => $footerLogo->id,
|
|
]);
|
|
}
|
|
|
|
private function createAttachment(string $name): Attachment
|
|
{
|
|
return Attachment::query()->create([
|
|
'path' => "test/{$name}.png",
|
|
'filename' => "{$name}.png",
|
|
'type' => AttachmentType::Image,
|
|
'mime_type' => 'image/png',
|
|
]);
|
|
}
|
|
|
|
private function createAttribute(string $code): Attribute
|
|
{
|
|
return Attribute::query()->create([
|
|
'tenant_codigo' => $this->tenant->codigo,
|
|
'codigo' => $code,
|
|
'nombre' => ucfirst($code),
|
|
'type' => FieldType::String,
|
|
]);
|
|
}
|
|
}
|