feat(catalog): add max_units_per_user field to catalog items and implement purchase limit validation
This commit is contained in:
@@ -31,6 +31,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
'descripcion',
|
||||
'precio',
|
||||
'inventory_policy',
|
||||
'max_units_per_user',
|
||||
'has_tickets',
|
||||
'validity_time_id',
|
||||
])]
|
||||
@@ -59,6 +60,7 @@ class CatalogItem extends Model
|
||||
'type' => CatalogItemType::class,
|
||||
'precio' => 'decimal:2',
|
||||
'inventory_policy' => InventoryPolicy::class,
|
||||
'max_units_per_user' => 'integer',
|
||||
'has_tickets' => 'boolean',
|
||||
'validity_time_id' => 'integer',
|
||||
];
|
||||
|
||||
@@ -68,6 +68,7 @@ class StoreCatalogItemRequest extends FormRequest
|
||||
'descripcion' => ['sometimes', 'nullable', 'string'],
|
||||
'precio' => ['required', 'numeric', 'min:0'],
|
||||
'inventory_policy' => [Rule::prohibitedIf($isBundle), 'sometimes', Rule::enum(InventoryPolicy::class)],
|
||||
'max_units_per_user' => ['sometimes', 'nullable', 'integer', 'min:1'],
|
||||
'has_tickets' => [Rule::prohibitedIf($isBundle), 'sometimes', 'boolean'],
|
||||
'validity_time_id' => [Rule::prohibitedIf($isBundle), 'sometimes', 'nullable', 'integer', Rule::exists('validity_times', 'id')],
|
||||
'real_stock' => [Rule::prohibitedIf($isBundle), 'sometimes', 'integer', 'min:0'],
|
||||
|
||||
@@ -34,6 +34,7 @@ class CatalogItemDetailResource extends JsonResource
|
||||
'category' => $this->category?->nombre,
|
||||
'brand' => $this->brand?->nombre,
|
||||
'inventory_policy' => $this->inventory_policy?->value,
|
||||
'max_units_per_user' => $this->max_units_per_user,
|
||||
'has_tickets' => $this->has_tickets,
|
||||
'validity_time_id' => $this->validity_time_id,
|
||||
'validity_time' => ValidityTimeResource::make($this->validityTime),
|
||||
|
||||
@@ -25,6 +25,7 @@ class CatalogItemResource extends JsonResource
|
||||
'descripcion' => $this->descripcion,
|
||||
'precio' => $this->precio,
|
||||
'inventory_policy' => $this->inventory_policy?->value,
|
||||
'max_units_per_user' => $this->max_units_per_user,
|
||||
'has_tickets' => $this->has_tickets,
|
||||
'validity_time_id' => $this->validity_time_id,
|
||||
'validity_time' => $this->whenLoaded(
|
||||
|
||||
@@ -195,6 +195,19 @@ class CheckoutService
|
||||
|
||||
try {
|
||||
if ($difference > 0) {
|
||||
$otherItemQuantity = (int) $purchase->items()
|
||||
->where('source_catalog_item_id', $purchaseItem->source_catalog_item_id)
|
||||
->whereKeyNot($purchaseItem->getKey())
|
||||
->sum('cantidad');
|
||||
$catalogItem = $selection instanceof Variant
|
||||
? $selection->catalogItem
|
||||
: $selection;
|
||||
$this->assertUserPurchaseLimit(
|
||||
$catalogItem,
|
||||
(int) $purchase->user_id,
|
||||
$otherItemQuantity + $quantity,
|
||||
$purchase->getKey(),
|
||||
);
|
||||
$this->catalogInventoryService->reserve($selection, $difference);
|
||||
} else {
|
||||
$this->catalogInventoryService->release($selection, abs($difference));
|
||||
@@ -410,6 +423,15 @@ class CheckoutService
|
||||
$variantId = isset($directItem['variant_id']) ? (int) $directItem['variant_id'] : null;
|
||||
$quantity = (int) $directItem['cantidad'];
|
||||
$selection = $this->resolveSelection($tenant, $catalogItemId, $variantId);
|
||||
$catalogItem = $selection instanceof Variant
|
||||
? $selection->catalogItem
|
||||
: $selection;
|
||||
$this->assertUserPurchaseLimit(
|
||||
$catalogItem,
|
||||
$userId,
|
||||
$quantity,
|
||||
field: 'direct_item.cantidad',
|
||||
);
|
||||
$availableQuantity = $this->catalogInventoryService->availableQuantity($selection);
|
||||
|
||||
if ($availableQuantity !== null && $availableQuantity < $quantity) {
|
||||
@@ -461,6 +483,7 @@ class CheckoutService
|
||||
|
||||
$this->loadCartItems($cartItems);
|
||||
$this->verifyTenantItems($tenant, $cartItems);
|
||||
$this->assertCartUserPurchaseLimits($tenant, $userId, $cartItems);
|
||||
$cart->setRelation('items', $cartItems);
|
||||
|
||||
$purchase = $this->createPurchase(
|
||||
@@ -648,6 +671,74 @@ class CheckoutService
|
||||
}
|
||||
}
|
||||
|
||||
/** @param Collection<int, CartItem> $cartItems */
|
||||
private function assertCartUserPurchaseLimits(
|
||||
Tenant $tenant,
|
||||
int $userId,
|
||||
Collection $cartItems,
|
||||
): void {
|
||||
$quantities = $cartItems
|
||||
->groupBy('catalog_item_id')
|
||||
->map(fn (Collection $items): int => (int) $items->sum('cantidad'))
|
||||
->sortKeys();
|
||||
|
||||
$catalogItems = CatalogItem::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->whereKey($quantities->keys())
|
||||
->orderBy('id')
|
||||
->lockForUpdate()
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
foreach ($quantities as $catalogItemId => $quantity) {
|
||||
/** @var CatalogItem $catalogItem */
|
||||
$catalogItem = $catalogItems->get($catalogItemId);
|
||||
$this->assertUserPurchaseLimit(
|
||||
$catalogItem,
|
||||
$userId,
|
||||
$quantity,
|
||||
field: 'cart_id',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function assertUserPurchaseLimit(
|
||||
CatalogItem $catalogItem,
|
||||
int $userId,
|
||||
int $requestedQuantity,
|
||||
?int $excludedPurchaseId = null,
|
||||
string $field = 'quantity',
|
||||
): void {
|
||||
$limit = $catalogItem->max_units_per_user;
|
||||
|
||||
if ($limit === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$purchasedQuantity = (int) PurchaseItem::query()
|
||||
->where('source_catalog_item_id', $catalogItem->getKey())
|
||||
->whereHas('purchase', function ($query) use ($userId, $excludedPurchaseId): void {
|
||||
$query
|
||||
->where('user_id', $userId)
|
||||
->whereIn('status', [
|
||||
Purchase::STATUS_CREATED,
|
||||
Purchase::STATUS_PENDING_PAYMENT,
|
||||
Purchase::STATUS_PAID,
|
||||
])
|
||||
->when(
|
||||
$excludedPurchaseId !== null,
|
||||
fn ($query) => $query->whereKeyNot($excludedPurchaseId),
|
||||
);
|
||||
})
|
||||
->sum('cantidad');
|
||||
|
||||
if ($purchasedQuantity + $requestedQuantity > $limit) {
|
||||
throw ValidationException::withMessages([
|
||||
$field => __('api.purchase.max_units_per_user', ['max' => $limit]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function resolveCheckoutCart(Tenant $tenant, int $userId, int $cartId): Cart
|
||||
{
|
||||
/** @var Cart|null $cart */
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('catalog_items', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('max_units_per_user')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('catalog_items', function (Blueprint $table): void {
|
||||
$table->dropColumn('max_units_per_user');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -5,6 +5,8 @@ namespace Database\Seeders;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Shared\Enums\FieldType;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Enums\ValidityTimeType;
|
||||
use App\Domains\Ticket\Models\ValidityTime;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class AttributeSeeder extends Seeder
|
||||
@@ -17,12 +19,8 @@ class AttributeSeeder extends Seeder
|
||||
$tenants = Tenant::all();
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
// Event dates replace catalog attributes for Fiesta Futbol Infantil.
|
||||
if ($tenant->codigo === 'fiesta_futbol_infantil') {
|
||||
Attribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->whereIn('codigo', ['color', 'talle', 'talle_numerico', 'fecha'])
|
||||
->delete();
|
||||
$this->seedFiestaFutbolInfantilAttributes($tenant);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -108,6 +106,94 @@ class AttributeSeeder extends Seeder
|
||||
}
|
||||
}
|
||||
|
||||
private function seedFiestaFutbolInfantilAttributes(Tenant $tenant): void
|
||||
{
|
||||
Attribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->whereIn('codigo', ['talle_numerico', 'fecha'])
|
||||
->delete();
|
||||
|
||||
$breakfastValidityTime = $this->timeWindow('07:00:00', '12:00:00');
|
||||
$lunchValidityTime = $this->timeWindow('12:00:00', '15:00:00');
|
||||
$dinnerValidityTime = $this->timeWindow('20:00:00', '24:00:00');
|
||||
|
||||
$this->seedAttribute($tenant, [
|
||||
'codigo' => 'servicio',
|
||||
'nombre' => 'Servicio',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'options' => [
|
||||
['value' => 'Comedor', 'label' => 'Comedor', 'sort_order' => 1],
|
||||
['value' => 'Vianda', 'label' => 'Vianda', 'sort_order' => 2],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->seedAttribute($tenant, [
|
||||
'codigo' => 'color',
|
||||
'nombre' => 'Color',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'metadata_schema' => [
|
||||
'hex' => ['type' => 'string'],
|
||||
],
|
||||
'options' => [
|
||||
['value' => 'Verde', 'label' => 'Verde', 'sort_order' => 1, 'metadata' => ['hex' => '#00973F']],
|
||||
['value' => 'Blanco', 'label' => 'Blanco', 'sort_order' => 2, 'metadata' => ['hex' => '#FFFFFF']],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->seedAttribute($tenant, [
|
||||
'codigo' => 'horario',
|
||||
'nombre' => 'Horario',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'options' => [
|
||||
[
|
||||
'value' => 'Desayuno',
|
||||
'label' => 'Desayuno',
|
||||
'sort_order' => 1,
|
||||
'validity_time_id' => $breakfastValidityTime->id,
|
||||
],
|
||||
[
|
||||
'value' => 'Almuerzo',
|
||||
'label' => 'Almuerzo',
|
||||
'sort_order' => 2,
|
||||
'validity_time_id' => $lunchValidityTime->id,
|
||||
],
|
||||
[
|
||||
'value' => 'Cena',
|
||||
'label' => 'Cena',
|
||||
'sort_order' => 3,
|
||||
'validity_time_id' => $dinnerValidityTime->id,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->seedAttribute($tenant, [
|
||||
'codigo' => 'talle',
|
||||
'nombre' => 'Talle',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'options' => [
|
||||
['value' => '14', 'label' => '14', 'sort_order' => 1],
|
||||
['value' => 'S', 'label' => 'S', 'sort_order' => 2],
|
||||
['value' => 'M', 'label' => 'M', 'sort_order' => 3],
|
||||
['value' => 'L', 'label' => 'L', 'sort_order' => 4],
|
||||
['value' => 'XL', 'label' => 'XL', 'sort_order' => 5],
|
||||
['value' => 'XXL', 'label' => 'XXL', 'sort_order' => 6],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function timeWindow(string $startTime, string $endTime): ValidityTime
|
||||
{
|
||||
return ValidityTime::query()->firstOrCreate([
|
||||
'type' => ValidityTimeType::TimeWindow,
|
||||
'start_time' => $startTime,
|
||||
'end_time' => $endTime,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
|
||||
@@ -42,6 +42,7 @@ return [
|
||||
'inconsistent_reservation' => 'The purchase has an inconsistent stock reservation.',
|
||||
'paid_cannot_cancel' => 'A paid purchase cannot be cancelled.',
|
||||
'direct_item_max_stock' => 'There is not enough stock. Maximum available: :max.',
|
||||
'max_units_per_user' => 'You can purchase up to :max units of this product.',
|
||||
'empty_cart' => 'The selected cart does not contain items.',
|
||||
'catalog_item_missing' => 'One or more catalog items could not be loaded.',
|
||||
'catalog_item_wrong_tenant' => 'One or more catalog items do not belong to the tenant.',
|
||||
|
||||
@@ -42,6 +42,7 @@ return [
|
||||
'inconsistent_reservation' => 'La compra tiene una reserva de stock inconsistente.',
|
||||
'paid_cannot_cancel' => 'Una compra pagada no se puede cancelar.',
|
||||
'direct_item_max_stock' => 'Stock insuficiente. Máximo disponible: :max.',
|
||||
'max_units_per_user' => 'Podés comprar hasta :max unidades de este producto.',
|
||||
'empty_cart' => 'El carrito seleccionado no contiene productos.',
|
||||
'catalog_item_missing' => 'No se pudieron cargar uno o más productos del catálogo.',
|
||||
'catalog_item_wrong_tenant' => 'Uno o más productos no pertenecen al tenant.',
|
||||
|
||||
@@ -32,6 +32,7 @@ class CatalogItemControllerTest extends TestCase
|
||||
'slug' => 'shirt',
|
||||
'nombre' => 'Shirt',
|
||||
'precio' => 100,
|
||||
'max_units_per_user' => 4,
|
||||
'attribute_codes' => [$attribute->codigo],
|
||||
'images' => [$image, $image],
|
||||
'variants' => [
|
||||
@@ -46,6 +47,7 @@ class CatalogItemControllerTest extends TestCase
|
||||
$response
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.nombre', 'Shirt')
|
||||
->assertJsonPath('data.max_units_per_user', 4)
|
||||
->assertJsonCount(2, 'data.images')
|
||||
->assertJsonCount(1, 'data.variants')
|
||||
->assertJsonCount(1, 'data.variants.0.images');
|
||||
@@ -54,6 +56,7 @@ class CatalogItemControllerTest extends TestCase
|
||||
$variant = $item->variants()->firstOrFail();
|
||||
|
||||
$this->assertSame([0, 1], $item->attachments()->get()->pluck('pivot.orden')->all());
|
||||
$this->assertSame(4, $item->max_units_per_user);
|
||||
$this->assertSame([0], $variant->attachments()->get()->pluck('pivot.orden')->all());
|
||||
$this->assertDatabaseHas('catalog_items_attachments', [
|
||||
'catalog_item_id' => $item->id,
|
||||
@@ -77,6 +80,21 @@ class CatalogItemControllerTest extends TestCase
|
||||
$this->assertDatabaseMissing('catalog_items', ['slug' => 'invalid-image']);
|
||||
}
|
||||
|
||||
public function test_it_rejects_a_non_positive_user_purchase_limit(): void
|
||||
{
|
||||
$tenant = $this->createTenant('purchase-limit-validation');
|
||||
|
||||
$this->postJson("/api/tenants/{$tenant->codigo}/catalog-items", [
|
||||
'slug' => 'invalid-purchase-limit',
|
||||
'nombre' => 'Invalid purchase limit',
|
||||
'precio' => 100,
|
||||
'real_stock' => 10,
|
||||
'max_units_per_user' => 0,
|
||||
])->assertUnprocessable()->assertJsonValidationErrors('max_units_per_user');
|
||||
|
||||
$this->assertDatabaseMissing('catalog_items', ['slug' => 'invalid-purchase-limit']);
|
||||
}
|
||||
|
||||
private function createTenant(string $code = 'catalog-controller'): Tenant
|
||||
{
|
||||
$headerLogo = $this->createAttachment("{$code}-header");
|
||||
|
||||
@@ -43,6 +43,7 @@ class CatalogSchemaTest extends TestCase
|
||||
'descripcion',
|
||||
'precio',
|
||||
'inventory_policy',
|
||||
'max_units_per_user',
|
||||
'has_tickets',
|
||||
'validity_time_id',
|
||||
], Schema::getColumnListing('catalog_items'));
|
||||
|
||||
@@ -190,6 +190,98 @@ class StorePurchaseTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_enforces_the_user_purchase_limit_and_releases_it_after_cancellation(): void
|
||||
{
|
||||
$this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
$user = User::factory()->create();
|
||||
$otherUser = User::factory()->create();
|
||||
$variant = $this->createVariantForTenant('sonder', 20, '50.00');
|
||||
$variant->catalogItem->update(['max_units_per_user' => 3]);
|
||||
|
||||
$firstPurchaseId = $this->actingAs($user, 'sanctum')
|
||||
->postJson('/api/tenants/sonder/compras/start-checkout', [
|
||||
'direct_item' => [
|
||||
'catalog_item_id' => $variant->catalog_item_id,
|
||||
'variant_id' => $variant->id,
|
||||
'cantidad' => 2,
|
||||
],
|
||||
])
|
||||
->assertCreated()
|
||||
->json('data.id');
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->postJson('/api/tenants/sonder/compras/start-checkout', [
|
||||
'direct_item' => [
|
||||
'catalog_item_id' => $variant->catalog_item_id,
|
||||
'variant_id' => $variant->id,
|
||||
'cantidad' => 2,
|
||||
],
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('direct_item.cantidad');
|
||||
|
||||
$this->actingAs($otherUser, 'sanctum')
|
||||
->postJson('/api/tenants/sonder/compras/start-checkout', [
|
||||
'direct_item' => [
|
||||
'catalog_item_id' => $variant->catalog_item_id,
|
||||
'variant_id' => $variant->id,
|
||||
'cantidad' => 3,
|
||||
],
|
||||
])
|
||||
->assertCreated();
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->postJson("/api/tenants/sonder/compras/{$firstPurchaseId}/cancel")
|
||||
->assertOk();
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->postJson('/api/tenants/sonder/compras/start-checkout', [
|
||||
'direct_item' => [
|
||||
'catalog_item_id' => $variant->catalog_item_id,
|
||||
'variant_id' => $variant->id,
|
||||
'cantidad' => 3,
|
||||
],
|
||||
])
|
||||
->assertCreated();
|
||||
}
|
||||
|
||||
public function test_the_user_purchase_limit_is_shared_by_all_item_variants(): void
|
||||
{
|
||||
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
$user = User::factory()->create();
|
||||
$firstVariant = $this->createVariantForTenant('sonder', 20, '50.00');
|
||||
$firstVariant->catalogItem->update(['max_units_per_user' => 3]);
|
||||
$secondInventory = Inventory::query()->create(['real_stock' => 20]);
|
||||
$secondVariant = Variant::query()->create([
|
||||
'catalog_item_id' => $firstVariant->catalog_item_id,
|
||||
'inventory_id' => $secondInventory->id,
|
||||
]);
|
||||
$cart = Cart::query()->create([
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
'user_id' => $user->id,
|
||||
'status' => 'active',
|
||||
]);
|
||||
$cart->addItem($firstVariant->catalog_item_id, $firstVariant->id, 2);
|
||||
$cart->addItem($secondVariant->catalog_item_id, $secondVariant->id, 2);
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->postJson('/api/tenants/sonder/compras/start-checkout', [
|
||||
'cart_id' => $cart->id,
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('cart_id');
|
||||
|
||||
$this->assertDatabaseCount('compras', 0);
|
||||
$this->assertDatabaseHas('inventories', [
|
||||
'id' => $firstVariant->inventory_id,
|
||||
'reserved_stock' => 2,
|
||||
]);
|
||||
$this->assertDatabaseHas('inventories', [
|
||||
'id' => $secondInventory->id,
|
||||
'reserved_stock' => 2,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_restores_the_source_cart_when_checkout_is_cancelled(): void
|
||||
{
|
||||
$this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
@@ -382,6 +474,32 @@ class StorePurchaseTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_rejects_a_quantity_update_above_the_user_purchase_limit(): void
|
||||
{
|
||||
$this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
$user = User::factory()->create();
|
||||
$variant = $this->createVariantForTenant('sonder', 10, '50.00');
|
||||
$variant->catalogItem->update(['max_units_per_user' => 3]);
|
||||
$purchase = $this->createCheckoutPurchase($user, 'sonder', $variant, 2);
|
||||
$itemId = $purchase->items->firstOrFail()->id;
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->patchJson("/api/tenants/sonder/compras/{$purchase->id}/items/{$itemId}", [
|
||||
'quantity' => 4,
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('quantity');
|
||||
|
||||
$this->assertDatabaseHas('compra_items', [
|
||||
'id' => $itemId,
|
||||
'cantidad' => 2,
|
||||
]);
|
||||
$this->assertDatabaseHas('inventories', [
|
||||
'id' => $variant->inventory_id,
|
||||
'reserved_stock' => 2,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_reopens_a_pending_purchase_before_editing_items(): void
|
||||
{
|
||||
$this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
|
||||
@@ -17,6 +17,7 @@ use App\Domains\Catalog\Models\Inventory;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Enums\ValidityTimeType;
|
||||
use Database\Seeders\AttributeSeeder;
|
||||
use Database\Seeders\FiestaFutbolInfantilProductSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
@@ -64,9 +65,36 @@ class FiestaFutbolInfantilProductSeederTest extends TestCase
|
||||
FiestaFutbolInfantilProductSeeder::class,
|
||||
]);
|
||||
|
||||
$this->assertFalse(Attribute::query()
|
||||
$attributes = Attribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->exists());
|
||||
->with('options.validityTime')
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
$this->assertSame(
|
||||
['servicio', 'color', 'horario', 'talle'],
|
||||
$attributes->pluck('codigo')->all(),
|
||||
);
|
||||
$this->assertSame(['Comedor', 'Vianda'], $attributes[0]->options->pluck('value')->all());
|
||||
$this->assertSame(['Verde', 'Blanco'], $attributes[1]->options->pluck('value')->all());
|
||||
$this->assertSame(['Desayuno', 'Almuerzo', 'Cena'], $attributes[2]->options->pluck('value')->all());
|
||||
$this->assertSame(['14', 'S', 'M', 'L', 'XL', 'XXL'], $attributes[3]->options->pluck('value')->all());
|
||||
|
||||
$this->assertSame(
|
||||
[
|
||||
['Desayuno', ValidityTimeType::TimeWindow, '07:00:00', '12:00:00'],
|
||||
['Almuerzo', ValidityTimeType::TimeWindow, '12:00:00', '15:00:00'],
|
||||
['Cena', ValidityTimeType::TimeWindow, '20:00:00', '24:00:00'],
|
||||
],
|
||||
$attributes[2]->options
|
||||
->map(fn ($option): array => [
|
||||
$option->value,
|
||||
$option->validityTime->type,
|
||||
$option->validityTime->start_time,
|
||||
$option->validityTime->end_time,
|
||||
])
|
||||
->all(),
|
||||
);
|
||||
|
||||
$event = Event::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
|
||||
Reference in New Issue
Block a user