refactor(event): move event configuration onto tenant
This commit is contained in:
@@ -7,7 +7,6 @@ use App\Domains\Catalog\Enums\CatalogItemType;
|
||||
use App\Domains\Catalog\Enums\EventProductType;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Services\CatalogInventoryService;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Models\Ticket;
|
||||
use App\Domains\Ticket\Models\ValidityTime;
|
||||
@@ -20,7 +19,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_code',
|
||||
'event_id',
|
||||
'event_product_type',
|
||||
'category_id',
|
||||
'brand_id',
|
||||
@@ -55,7 +53,6 @@ class CatalogItem extends Model
|
||||
'category_id' => 'integer',
|
||||
'brand_id' => 'integer',
|
||||
'inventory_id' => 'integer',
|
||||
'event_id' => 'integer',
|
||||
'event_product_type' => EventProductType::class,
|
||||
'type' => CatalogItemType::class,
|
||||
'precio' => 'decimal:2',
|
||||
@@ -72,12 +69,6 @@ class CatalogItem extends Model
|
||||
return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Event, $this> */
|
||||
public function event(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Event::class);
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Category, $this> */
|
||||
public function category(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -26,18 +26,9 @@ class StoreCatalogItemRequest extends FormRequest
|
||||
return [
|
||||
'tenant_code' => ['prohibited'],
|
||||
'type' => ['sometimes', Rule::enum(CatalogItemType::class)],
|
||||
'event_id' => [
|
||||
'sometimes',
|
||||
'nullable',
|
||||
'required_with:event_product_type',
|
||||
Rule::exists('events', 'id')->where(
|
||||
fn ($query) => $query->where('tenant_code', $tenantCode)
|
||||
),
|
||||
],
|
||||
'event_product_type' => [
|
||||
'sometimes',
|
||||
'nullable',
|
||||
'required_with:event_id',
|
||||
Rule::enum(EventProductType::class),
|
||||
],
|
||||
'category_id' => [
|
||||
@@ -93,7 +84,7 @@ class StoreCatalogItemRequest extends FormRequest
|
||||
'nullable',
|
||||
'integer',
|
||||
Rule::exists('event_dates', 'id')->where(
|
||||
fn ($query) => $query->where('event_id', $this->input('event_id'))
|
||||
fn ($query) => $query->where('tenant_code', $tenantCode)
|
||||
),
|
||||
],
|
||||
'variants.*.inventory_id' => ['prohibited'],
|
||||
|
||||
@@ -23,7 +23,6 @@ class CatalogItemDetailResource extends JsonResource
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'type' => $this->type->value,
|
||||
'event_id' => $this->event_id,
|
||||
'event_product_type' => $this->event_product_type?->value,
|
||||
'category_id' => $this->category_id,
|
||||
'brand_id' => $this->brand_id,
|
||||
@@ -38,9 +37,7 @@ class CatalogItemDetailResource extends JsonResource
|
||||
'has_tickets' => $this->has_tickets,
|
||||
'validity_time_id' => $this->validity_time_id,
|
||||
'validity_time' => ValidityTimeResource::make($this->validityTime),
|
||||
'attributes' => $this->itemAttributes
|
||||
->map(fn (ItemAttribute $itemAttribute): array => $this->attributeData($itemAttribute))
|
||||
->values(),
|
||||
'attributes' => $this->attributesData(),
|
||||
'stock_tecnico' => $this->when(
|
||||
$selectedVariant === null,
|
||||
fn () => $this->availableStock(),
|
||||
@@ -111,19 +108,70 @@ class CatalogItemDetailResource extends JsonResource
|
||||
];
|
||||
}
|
||||
|
||||
/** @return Collection<int, array<string, mixed>> */
|
||||
private function attributesData(): Collection
|
||||
{
|
||||
$attributes = $this->itemAttributes
|
||||
->map(fn (ItemAttribute $itemAttribute): array => $this->attributeData($itemAttribute));
|
||||
|
||||
if ($this->variants->isEmpty() || $this->variants->contains(
|
||||
fn (Variant $variant): bool => $variant->event_date_id === null
|
||||
)) {
|
||||
return $attributes->values();
|
||||
}
|
||||
|
||||
$eventDateAttribute = [
|
||||
'id' => -1,
|
||||
'codigo' => 'event_date',
|
||||
'nombre' => 'Fecha',
|
||||
'is_required' => true,
|
||||
'metadata_schema' => null,
|
||||
'type' => 'event_date',
|
||||
'options' => $this->variants
|
||||
->pluck('eventDate')
|
||||
->filter()
|
||||
->unique('id')
|
||||
->sortBy(fn ($eventDate): string => $eventDate->date->format('Y-m-d').' '.$eventDate->time_start)
|
||||
->values()
|
||||
->map(fn ($eventDate, int $index): array => [
|
||||
'id' => $eventDate->id,
|
||||
'value' => (string) $eventDate->id,
|
||||
'label' => $eventDate->date->format('d/m/Y').' · '
|
||||
.substr($eventDate->time_start, 0, 5).' a '
|
||||
.substr($eventDate->time_end, 0, 5),
|
||||
'sort_order' => $index,
|
||||
'validity_time_id' => null,
|
||||
'validity_time' => null,
|
||||
'metadata' => [
|
||||
'date' => $eventDate->date->format('Y-m-d'),
|
||||
'time_start' => $eventDate->time_start,
|
||||
'time_end' => $eventDate->time_end,
|
||||
],
|
||||
]),
|
||||
];
|
||||
|
||||
return collect([$eventDateAttribute])->concat($attributes)->values();
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
private function variantData(Variant $variant): array
|
||||
{
|
||||
$values = $variant->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
$definition->itemAttribute?->attribute?->codigo => $definition->value,
|
||||
])
|
||||
->filter(fn ($value, $key): bool => $key !== null);
|
||||
|
||||
if ($variant->event_date_id !== null) {
|
||||
$values->put('event_date', (string) $variant->event_date_id);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $variant->id,
|
||||
'event_date_id' => $variant->event_date_id,
|
||||
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
|
||||
'stock_tecnico' => $this->variantStock($variant),
|
||||
'values' => $variant->definitions
|
||||
->mapWithKeys(fn ($definition) => [
|
||||
$definition->itemAttribute?->attribute?->codigo => $definition->value,
|
||||
])
|
||||
->filter(fn ($value, $key): bool => $key !== null),
|
||||
'values' => $values,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ class CatalogItemResource extends JsonResource
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'type' => $this->type->value,
|
||||
'event_id' => $this->event_id,
|
||||
'event_product_type' => $this->event_product_type?->value,
|
||||
'category_id' => $this->category_id,
|
||||
'brand_id' => $this->brand_id,
|
||||
|
||||
@@ -12,7 +12,6 @@ use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\Inventory;
|
||||
use App\Domains\Catalog\Models\ItemAttribute;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -47,7 +46,14 @@ class CatalogService
|
||||
);
|
||||
$hasVariants = $attributeCodes !== [] || $hasEventDateVariants;
|
||||
|
||||
$this->validateEventData($data);
|
||||
if ($hasEventDateVariants && in_array('event_date', $attributeCodes, true)) {
|
||||
throw ValidationException::withMessages([
|
||||
'attribute_codes' => ['The event_date code is reserved for event dates.'],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->validateEventProductType($data);
|
||||
$this->validateUniqueVariantCombinations($variants, $attributeCodes);
|
||||
|
||||
if ($type === CatalogItemType::Bundle) {
|
||||
$this->validateBundleData($data, $components);
|
||||
@@ -127,7 +133,6 @@ class CatalogService
|
||||
'inventory',
|
||||
'category',
|
||||
'brand',
|
||||
'event',
|
||||
'validityTime',
|
||||
'itemAttributes.attribute',
|
||||
'variants.inventory',
|
||||
@@ -147,7 +152,6 @@ class CatalogService
|
||||
'inventory',
|
||||
'category',
|
||||
'brand',
|
||||
'event',
|
||||
'validityTime',
|
||||
'itemAttributes.attribute.options.validityTime',
|
||||
'variants' => fn ($query) => $query->orderBy('id'),
|
||||
@@ -492,16 +496,15 @@ class CatalogService
|
||||
if (
|
||||
$eventDateId !== null
|
||||
&& (
|
||||
$catalogItem->event_id === null
|
||||
|| ! EventDate::query()
|
||||
! EventDate::query()
|
||||
->whereKey($eventDateId)
|
||||
->where('event_id', $catalogItem->event_id)
|
||||
->where('tenant_code', $catalogItem->tenant_code)
|
||||
->exists()
|
||||
)
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
"variants.{$index}.event_date_id" => [
|
||||
'The event date must belong to the catalog item event.',
|
||||
'The event date must belong to the catalog item tenant.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -533,30 +536,14 @@ class CatalogService
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $data */
|
||||
private function validateEventData(array $data): void
|
||||
private function validateEventProductType(array $data): void
|
||||
{
|
||||
$eventId = $data['event_id'] ?? null;
|
||||
$eventProductType = $data['event_product_type'] ?? null;
|
||||
|
||||
if (($eventId === null) !== ($eventProductType === null)) {
|
||||
throw ValidationException::withMessages([
|
||||
'event_id' => ['Event and event product type must be provided together.'],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($eventId === null) {
|
||||
if ($eventProductType === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! Event::query()
|
||||
->whereKey($eventId)
|
||||
->where('tenant_code', $data['tenant_code'] ?? null)
|
||||
->exists()) {
|
||||
throw ValidationException::withMessages([
|
||||
'event_id' => ['The event must belong to the catalog item tenant.'],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array($eventProductType, EventProductType::values(), true)) {
|
||||
throw ValidationException::withMessages([
|
||||
'event_product_type' => ['The event product type is invalid.'],
|
||||
@@ -564,6 +551,35 @@ class CatalogService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $variants
|
||||
* @param array<int, string> $attributeCodes
|
||||
*/
|
||||
private function validateUniqueVariantCombinations(array $variants, array $attributeCodes): void
|
||||
{
|
||||
$seen = [];
|
||||
$attributeCodes = array_values(array_unique($attributeCodes));
|
||||
sort($attributeCodes);
|
||||
|
||||
foreach (array_values($variants) as $index => $variant) {
|
||||
$combination = [(string) ($variant['event_date_id'] ?? '')];
|
||||
|
||||
foreach ($attributeCodes as $attributeCode) {
|
||||
$value = trim((string) ($variant['values'][$attributeCode] ?? ''));
|
||||
$combination[] = Str::ascii(mb_strtolower($value));
|
||||
}
|
||||
|
||||
$key = implode('|', $combination);
|
||||
if (isset($seen[$key])) {
|
||||
throw ValidationException::withMessages([
|
||||
"variants.{$index}" => ['The variant combination must be unique.'],
|
||||
]);
|
||||
}
|
||||
|
||||
$seen[$key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @param array<int, array<string, mixed>> $variants
|
||||
|
||||
@@ -15,14 +15,14 @@ class EventController extends Controller
|
||||
public function show(Request $request): EventResource
|
||||
{
|
||||
return EventResource::make(
|
||||
$this->eventService->activeForTenant($request->user()->tenant()->firstOrFail())
|
||||
$this->eventService->forTenant($request->user()->tenant()->firstOrFail())
|
||||
);
|
||||
}
|
||||
|
||||
public function update(UpdateEventRequest $request): EventResource
|
||||
{
|
||||
return EventResource::make(
|
||||
$this->eventService->updateActiveForTenant(
|
||||
$this->eventService->updateForTenant(
|
||||
$request->user()->tenant()->firstOrFail(),
|
||||
$request->validated()
|
||||
)
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Event\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_code',
|
||||
'name',
|
||||
'address',
|
||||
])]
|
||||
class Event extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
/** @return BelongsTo<Tenant, $this> */
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return HasMany<EventDate, $this> */
|
||||
public function dates(): HasMany
|
||||
{
|
||||
return $this->hasMany(EventDate::class)->orderBy('date')->orderBy('time_start');
|
||||
}
|
||||
|
||||
/** @return HasMany<CatalogItem, $this> */
|
||||
public function catalogItems(): HasMany
|
||||
{
|
||||
return $this->hasMany(CatalogItem::class);
|
||||
}
|
||||
|
||||
/** @return HasMany<Purchase, $this> */
|
||||
public function purchases(): HasMany
|
||||
{
|
||||
return $this->hasMany(Purchase::class);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Domains\Event\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Carbon\CarbonInterface;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -12,7 +13,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
#[Fillable([
|
||||
'event_id',
|
||||
'tenant_code',
|
||||
'date',
|
||||
'time_start',
|
||||
'time_end',
|
||||
@@ -26,15 +27,14 @@ class EventDate extends Model
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'event_id' => 'integer',
|
||||
'date' => 'date:Y-m-d',
|
||||
];
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Event, $this> */
|
||||
public function event(): BelongsTo
|
||||
/** @return BelongsTo<Tenant, $this> */
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Event::class);
|
||||
return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return HasMany<Variant, $this> */
|
||||
|
||||
@@ -2,29 +2,29 @@
|
||||
|
||||
namespace App\Domains\Event\Resources;
|
||||
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin Event */
|
||||
/** @mixin Tenant */
|
||||
class EventResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
$socialMedia = $this->tenant->socialMedia->keyBy('code');
|
||||
$socialMedia = $this->socialMedia->keyBy('code');
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->name,
|
||||
'location' => $this->address,
|
||||
'dates' => $this->dates->map(fn ($eventDate): array => [
|
||||
'title' => $this->event_title,
|
||||
'location' => $this->event_location,
|
||||
'dates' => $this->eventDates->map(fn ($eventDate): array => [
|
||||
'id' => $eventDate->id,
|
||||
'date' => $eventDate->date->format('Y-m-d'),
|
||||
'start_time' => substr($eventDate->time_start, 0, 5),
|
||||
'end_time' => substr($eventDate->time_end, 0, 5),
|
||||
])->values(),
|
||||
'social_media' => $this->tenant->socialMedia->map(fn ($item): array => [
|
||||
'social_media' => $this->socialMedia->map(fn ($item): array => [
|
||||
'code' => $item->code,
|
||||
'url' => $item->pivot->url,
|
||||
'orden' => $item->pivot->orden,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Domains\Event\Services;
|
||||
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
@@ -14,53 +13,36 @@ class EventService
|
||||
'facebook_url' => 'facebook',
|
||||
];
|
||||
|
||||
public function activeForTenant(Tenant $tenant): Event
|
||||
public function forTenant(Tenant $tenant): Tenant
|
||||
{
|
||||
return $tenant->events()
|
||||
->whereKey($tenant->active_event_id)
|
||||
->with(['dates', 'tenant.socialMedia'])
|
||||
->firstOrFail();
|
||||
return $tenant->load(['eventDates', 'socialMedia']);
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $data */
|
||||
public function updateActiveForTenant(Tenant $tenant, array $data): Event
|
||||
public function updateForTenant(Tenant $tenant, array $data): Tenant
|
||||
{
|
||||
return DB::transaction(function () use ($tenant, $data): Event {
|
||||
return DB::transaction(function () use ($tenant, $data): Tenant {
|
||||
$tenant = Tenant::query()->whereKey($tenant->getKey())->lockForUpdate()->firstOrFail();
|
||||
$event = $tenant->active_event_id === null
|
||||
? $tenant->events()->create([
|
||||
'name' => $data['title'],
|
||||
'address' => $data['location'],
|
||||
])
|
||||
: $tenant->events()
|
||||
->whereKey($tenant->active_event_id)
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
|
||||
$event->update([
|
||||
'name' => $data['title'],
|
||||
'address' => $data['location'],
|
||||
$tenant->update([
|
||||
'event_title' => $data['title'],
|
||||
'event_location' => $data['location'],
|
||||
]);
|
||||
|
||||
if ($tenant->active_event_id === null) {
|
||||
$tenant->update(['active_event_id' => $event->id]);
|
||||
}
|
||||
|
||||
$this->syncDates($event, $data['dates']);
|
||||
$this->syncDates($tenant, $data['dates']);
|
||||
if (array_key_exists('social_media', $data)) {
|
||||
$this->syncSocialMedia($tenant, $data['social_media']);
|
||||
} else {
|
||||
$this->syncLegacyContact($tenant, $data['contact']);
|
||||
}
|
||||
|
||||
return $event->load(['dates', 'tenant.socialMedia']);
|
||||
return $tenant->load(['eventDates', 'socialMedia']);
|
||||
});
|
||||
}
|
||||
|
||||
/** @param array<int, array{date: string, start_time: string, end_time: string}> $dates */
|
||||
private function syncDates(Event $event, array $dates): void
|
||||
private function syncDates(Tenant $tenant, array $dates): void
|
||||
{
|
||||
$existingDates = $event->dates()->get()->values();
|
||||
$existingDates = $tenant->eventDates()->get()->values();
|
||||
|
||||
foreach (array_values($dates) as $index => $date) {
|
||||
$attributes = [
|
||||
@@ -74,12 +56,12 @@ class EventService
|
||||
if ($existingDate) {
|
||||
$existingDate->update($attributes);
|
||||
} else {
|
||||
$event->dates()->create($attributes);
|
||||
$tenant->eventDates()->create($attributes);
|
||||
}
|
||||
}
|
||||
|
||||
$existingDates->slice(count($dates))->each->delete();
|
||||
$event->unsetRelation('dates');
|
||||
$tenant->unsetRelation('eventDates');
|
||||
}
|
||||
|
||||
/** @param array<string, string|null> $contact */
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Domains\Purchase\Models;
|
||||
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Cart\Models\Cart;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Logging\Models\Concerns\LogsValueChanges;
|
||||
use App\Domains\Purchase\Events\PurchasePaid;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
@@ -20,7 +19,6 @@ use Illuminate\Support\Facades\DB;
|
||||
#[Fillable([
|
||||
'cart_id',
|
||||
'tenant_codigo',
|
||||
'event_id',
|
||||
'user_id',
|
||||
'status',
|
||||
'payment_method',
|
||||
@@ -72,7 +70,6 @@ class Purchase extends Model
|
||||
{
|
||||
return [
|
||||
'cart_id' => 'integer',
|
||||
'event_id' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
'expires_at' => 'datetime',
|
||||
'total' => 'decimal:2',
|
||||
@@ -87,12 +84,6 @@ class Purchase extends Model
|
||||
return $this->belongsTo(Tenant::class, 'tenant_codigo', 'codigo');
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Event, $this> */
|
||||
public function event(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Event::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
|
||||
@@ -42,7 +42,6 @@ class PurchaseResource extends JsonResource
|
||||
'id' => $this->id,
|
||||
'cart_id' => $this->cart_id,
|
||||
'tenant_codigo' => $this->tenant_codigo,
|
||||
'event_id' => $this->event_id,
|
||||
'user_id' => $this->user_id,
|
||||
'created_at' => $this->created_at,
|
||||
'status' => $this->status,
|
||||
|
||||
@@ -229,7 +229,6 @@ class StartCheckoutService
|
||||
...$purchaseData,
|
||||
'cart_id' => $cartId,
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
'event_id' => $tenant->active_event_id,
|
||||
'user_id' => $userId,
|
||||
'status' => Purchase::STATUS_CREATED,
|
||||
'payment_method' => null,
|
||||
|
||||
@@ -7,7 +7,7 @@ use App\Domains\Catalog\Enums\GroupLayout;
|
||||
use App\Domains\Catalog\Enums\ProductLayout;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Menu\Models\TenantMenu;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
@@ -33,7 +33,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
'search_product_layout',
|
||||
'search_group_layout',
|
||||
'search_items_per_page',
|
||||
'active_event_id',
|
||||
'event_title',
|
||||
'event_location',
|
||||
])]
|
||||
class Tenant extends Model
|
||||
{
|
||||
@@ -61,7 +62,6 @@ class Tenant extends Model
|
||||
'search_product_layout' => ProductLayout::class,
|
||||
'search_group_layout' => GroupLayout::class,
|
||||
'search_items_per_page' => 'integer',
|
||||
'active_event_id' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -94,16 +94,12 @@ class Tenant extends Model
|
||||
return $this->hasMany(CatalogItem::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return HasMany<Event, $this> */
|
||||
public function events(): HasMany
|
||||
/** @return HasMany<EventDate, $this> */
|
||||
public function eventDates(): HasMany
|
||||
{
|
||||
return $this->hasMany(Event::class, 'tenant_code', 'codigo');
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Event, $this> */
|
||||
public function activeEvent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Event::class, 'active_event_id');
|
||||
return $this->hasMany(EventDate::class, 'tenant_code', 'codigo')
|
||||
->orderBy('date')
|
||||
->orderBy('time_start');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,14 +32,12 @@ class TenantResource extends JsonResource
|
||||
'header_bg_color' => $this->header_bg_color,
|
||||
'footer_bg_color' => $this->footer_bg_color,
|
||||
'website_type_code' => $this->website_type_code,
|
||||
'active_event_id' => $this->active_event_id,
|
||||
'active_event' => $this->whenLoaded('activeEvent', fn () => $this->activeEvent === null
|
||||
'event' => $this->whenLoaded('eventDates', fn () => $this->event_title === null
|
||||
? null
|
||||
: [
|
||||
'id' => $this->activeEvent->id,
|
||||
'name' => $this->activeEvent->name,
|
||||
'address' => $this->activeEvent->address,
|
||||
'dates' => $this->activeEvent->dates->map(fn ($eventDate): array => [
|
||||
'title' => $this->event_title,
|
||||
'location' => $this->event_location,
|
||||
'dates' => $this->eventDates->map(fn ($eventDate): array => [
|
||||
'id' => $eventDate->id,
|
||||
'date' => $eventDate->date->format('Y-m-d'),
|
||||
'time_start' => $eventDate->time_start,
|
||||
|
||||
@@ -15,7 +15,7 @@ class TenantInformationService
|
||||
'footerLogo',
|
||||
'socialMedia',
|
||||
'websiteExtras.websiteTypeExtra',
|
||||
'activeEvent.dates',
|
||||
'eventDates',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
125
database/migrations/2026_08_07_000000_move_event_onto_tenant.php
Normal file
125
database/migrations/2026_08_07_000000_move_event_onto_tenant.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
$table->string('event_title')->nullable()->after('nombre');
|
||||
$table->string('event_location')->nullable()->after('event_title');
|
||||
});
|
||||
|
||||
Schema::table('event_dates', function (Blueprint $table): void {
|
||||
$table->string('tenant_code')->nullable()->after('id');
|
||||
});
|
||||
|
||||
DB::table('tenants')
|
||||
->whereNotNull('active_event_id')
|
||||
->orderBy('id')
|
||||
->each(function (object $tenant): void {
|
||||
$event = DB::table('events')->where('id', $tenant->active_event_id)->first();
|
||||
|
||||
if ($event === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::table('tenants')->where('id', $tenant->id)->update([
|
||||
'event_title' => $event->name,
|
||||
'event_location' => $event->address,
|
||||
]);
|
||||
});
|
||||
|
||||
DB::table('events')->orderBy('id')->each(function (object $event): void {
|
||||
DB::table('event_dates')
|
||||
->where('event_id', $event->id)
|
||||
->update(['tenant_code' => $event->tenant_code]);
|
||||
});
|
||||
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
$table->dropConstrainedForeignId('active_event_id');
|
||||
});
|
||||
Schema::table('catalog_items', function (Blueprint $table): void {
|
||||
$table->dropConstrainedForeignId('event_id');
|
||||
});
|
||||
Schema::table('compras', function (Blueprint $table): void {
|
||||
$table->dropConstrainedForeignId('event_id');
|
||||
});
|
||||
Schema::table('event_dates', function (Blueprint $table): void {
|
||||
$table->dropConstrainedForeignId('event_id');
|
||||
$table->string('tenant_code')->nullable(false)->change();
|
||||
$table->foreign('tenant_code')
|
||||
->references('codigo')
|
||||
->on('tenants')
|
||||
->cascadeOnUpdate()
|
||||
->cascadeOnDelete();
|
||||
});
|
||||
|
||||
Schema::dropIfExists('events');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::create('events', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('tenant_code');
|
||||
$table->string('name');
|
||||
$table->string('address');
|
||||
$table->foreign('tenant_code')
|
||||
->references('codigo')
|
||||
->on('tenants')
|
||||
->cascadeOnUpdate()
|
||||
->cascadeOnDelete();
|
||||
});
|
||||
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
$table->foreignId('active_event_id')->nullable();
|
||||
});
|
||||
Schema::table('catalog_items', function (Blueprint $table): void {
|
||||
$table->foreignId('event_id')->nullable();
|
||||
});
|
||||
Schema::table('compras', function (Blueprint $table): void {
|
||||
$table->foreignId('event_id')->nullable();
|
||||
});
|
||||
Schema::table('event_dates', function (Blueprint $table): void {
|
||||
$table->foreignId('event_id')->nullable();
|
||||
});
|
||||
|
||||
DB::table('tenants')->orderBy('id')->each(function (object $tenant): void {
|
||||
if ($tenant->event_title === null && $tenant->event_location === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$eventId = DB::table('events')->insertGetId([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'name' => $tenant->event_title ?? $tenant->nombre,
|
||||
'address' => $tenant->event_location ?? '',
|
||||
]);
|
||||
|
||||
DB::table('tenants')->where('id', $tenant->id)->update(['active_event_id' => $eventId]);
|
||||
DB::table('catalog_items')->where('tenant_code', $tenant->codigo)->update(['event_id' => $eventId]);
|
||||
DB::table('compras')->where('tenant_codigo', $tenant->codigo)->update(['event_id' => $eventId]);
|
||||
DB::table('event_dates')->where('tenant_code', $tenant->codigo)->update(['event_id' => $eventId]);
|
||||
});
|
||||
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
$table->foreign('active_event_id')->references('id')->on('events')->nullOnDelete();
|
||||
$table->dropColumn(['event_title', 'event_location']);
|
||||
});
|
||||
Schema::table('catalog_items', function (Blueprint $table): void {
|
||||
$table->foreign('event_id')->references('id')->on('events')->nullOnDelete();
|
||||
});
|
||||
Schema::table('compras', function (Blueprint $table): void {
|
||||
$table->foreign('event_id')->references('id')->on('events')->nullOnDelete();
|
||||
});
|
||||
Schema::table('event_dates', function (Blueprint $table): void {
|
||||
$table->dropForeign(['tenant_code']);
|
||||
$table->dropColumn('tenant_code');
|
||||
$table->foreign('event_id')->references('id')->on('events')->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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('variantes', function (Blueprint $table): void {
|
||||
$table->index(['catalog_item_id', 'event_date_id']);
|
||||
});
|
||||
|
||||
Schema::table('variantes', function (Blueprint $table): void {
|
||||
$table->dropUnique(['catalog_item_id', 'event_date_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('variantes', function (Blueprint $table): void {
|
||||
$table->unique(['catalog_item_id', 'event_date_id']);
|
||||
});
|
||||
|
||||
Schema::table('variantes', function (Blueprint $table): void {
|
||||
$table->dropIndex(['catalog_item_id', 'event_date_id']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -12,7 +12,6 @@ use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Services\CatalogService;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Enums\ValidityTimeType;
|
||||
use App\Domains\Ticket\Models\ValidityTime;
|
||||
@@ -38,18 +37,15 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
||||
->update(['categoria_id' => null]);
|
||||
Category::query()->where('tenant_code', $tenant->codigo)->delete();
|
||||
|
||||
$event = Event::query()->updateOrCreate(
|
||||
[
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'name' => 'Fiesta Nacional del Fútbol Infantil',
|
||||
],
|
||||
['address' => 'Sunchales, Santa Fe'],
|
||||
);
|
||||
$event->dates()->delete();
|
||||
$tenant->update([
|
||||
'event_title' => 'Fiesta Nacional del Fútbol Infantil',
|
||||
'event_location' => 'Sunchales, Santa Fe',
|
||||
]);
|
||||
$tenant->eventDates()->delete();
|
||||
|
||||
$eventDates = collect(['2026-10-09', '2026-10-10', '2026-10-11', '2026-10-12'])
|
||||
->mapWithKeys(function (string $date) use ($event): array {
|
||||
$eventDate = $event->dates()->create([
|
||||
->mapWithKeys(function (string $date) use ($tenant): array {
|
||||
$eventDate = $tenant->eventDates()->create([
|
||||
'date' => $date,
|
||||
'time_start' => '00:00:00',
|
||||
'time_end' => '23:59:59',
|
||||
@@ -58,9 +54,6 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
||||
return [$date => $eventDate];
|
||||
});
|
||||
|
||||
$tenant->active_event_id = $event->id;
|
||||
$tenant->save();
|
||||
|
||||
$ticketCategory = Category::query()->firstOrCreate([
|
||||
'nombre' => 'Entradas',
|
||||
'tenant_code' => $tenant->codigo,
|
||||
@@ -89,7 +82,6 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
||||
|
||||
$generalAdmission = $this->catalogService->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'event_id' => $event->id,
|
||||
'event_product_type' => EventProductType::Entry->value,
|
||||
'category_id' => $ticketCategory->id,
|
||||
'slug' => 'entrada-general',
|
||||
@@ -121,7 +113,6 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
||||
foreach ($items as $item) {
|
||||
$createdItems[$item['slug']] = $this->catalogService->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'event_id' => $event->id,
|
||||
'event_product_type' => EventProductType::Product->value,
|
||||
'descripcion' => $item['descripcion'] ?? $item['nombre'],
|
||||
'inventory_policy' => InventoryPolicy::Unlimited->value,
|
||||
@@ -133,7 +124,6 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
||||
|
||||
$this->catalogService->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'event_id' => $event->id,
|
||||
'event_product_type' => EventProductType::Entry->value,
|
||||
'type' => CatalogItemType::Bundle->value,
|
||||
'slug' => 'entrada-general-todos-los-dias',
|
||||
@@ -152,7 +142,6 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
||||
|
||||
$this->catalogService->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'event_id' => $event->id,
|
||||
'event_product_type' => EventProductType::Product->value,
|
||||
'type' => CatalogItemType::Bundle->value,
|
||||
'slug' => 'combo-2-panchos-2-hamburguesas',
|
||||
|
||||
@@ -170,6 +170,33 @@ class CatalogItemDetailControllerTest extends TestCase
|
||||
->assertJsonPath('data.variants.0.stock_tecnico', null);
|
||||
}
|
||||
|
||||
public function test_it_exposes_event_dates_as_a_dynamic_variant_attribute(): void
|
||||
{
|
||||
$tenant = $this->createTenant('detail-event-date');
|
||||
$tenant->update([
|
||||
'event_title' => 'Festival',
|
||||
'event_location' => 'Rosario',
|
||||
]);
|
||||
$eventDate = $tenant->eventDates()->create([
|
||||
'date' => '2026-10-09',
|
||||
'time_start' => '09:00',
|
||||
'time_end' => '18:00',
|
||||
]);
|
||||
$item = $this->createItem($tenant, 'Entry');
|
||||
$variant = $this->createVariant($item, 10, 0);
|
||||
$variant->update(['event_date_id' => $eventDate->id]);
|
||||
|
||||
$this->getJson("/api/tenants/{$tenant->codigo}/catalog-items/{$item->id}")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.attributes.0.id', -1)
|
||||
->assertJsonPath('data.attributes.0.codigo', 'event_date')
|
||||
->assertJsonPath('data.attributes.0.type', 'event_date')
|
||||
->assertJsonPath('data.attributes.0.options.0.id', $eventDate->id)
|
||||
->assertJsonPath('data.attributes.0.options.0.value', (string) $eventDate->id)
|
||||
->assertJsonPath('data.attributes.0.options.0.label', '09/10/2026 · 09:00 a 18:00')
|
||||
->assertJsonPath('data.variants.0.values.event_date', (string) $eventDate->id);
|
||||
}
|
||||
|
||||
private function createItem(
|
||||
Tenant $tenant,
|
||||
string $name,
|
||||
|
||||
@@ -32,7 +32,6 @@ class CatalogSchemaTest extends TestCase
|
||||
$this->assertEqualsCanonicalizing([
|
||||
'id',
|
||||
'tenant_code',
|
||||
'event_id',
|
||||
'event_product_type',
|
||||
'category_id',
|
||||
'brand_id',
|
||||
@@ -186,21 +185,21 @@ class CatalogSchemaTest extends TestCase
|
||||
]));
|
||||
}
|
||||
|
||||
public function test_events_and_event_dates_are_linked_to_the_catalog(): void
|
||||
public function test_event_configuration_and_dates_belong_to_the_tenant(): void
|
||||
{
|
||||
$this->assertFalse(Schema::hasTable('events'));
|
||||
$this->assertTrue(Schema::hasColumns('tenants', [
|
||||
'event_title',
|
||||
'event_location',
|
||||
]));
|
||||
$this->assertEqualsCanonicalizing([
|
||||
'id',
|
||||
'tenant_code',
|
||||
'name',
|
||||
'address',
|
||||
], Schema::getColumnListing('events'));
|
||||
$this->assertEqualsCanonicalizing([
|
||||
'id',
|
||||
'event_id',
|
||||
'date',
|
||||
'time_start',
|
||||
'time_end',
|
||||
], Schema::getColumnListing('event_dates'));
|
||||
$this->assertTrue(Schema::hasColumn('tenants', 'active_event_id'));
|
||||
$this->assertFalse(Schema::hasColumn('catalog_items', 'event_id'));
|
||||
$this->assertFalse(Schema::hasColumn('compras', 'event_id'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,68 @@ class CatalogServiceTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function test_it_allows_the_same_event_date_with_different_attribute_values(): void
|
||||
{
|
||||
$sector = $this->createAttribute('sector');
|
||||
$eventDate = $this->tenant->eventDates()->create([
|
||||
'date' => '2026-10-09',
|
||||
'time_start' => '09:00',
|
||||
'time_end' => '18:00',
|
||||
]);
|
||||
|
||||
$item = $this->service->create([
|
||||
'tenant_code' => $this->tenant->codigo,
|
||||
'slug' => 'entry-by-sector',
|
||||
'nombre' => 'Entry by sector',
|
||||
'precio' => 100,
|
||||
'attribute_codes' => [$sector->codigo],
|
||||
'variants' => [
|
||||
[
|
||||
'event_date_id' => $eventDate->id,
|
||||
'values' => ['sector' => 'General'],
|
||||
],
|
||||
[
|
||||
'event_date_id' => $eventDate->id,
|
||||
'values' => ['sector' => 'VIP'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertCount(2, $item->variants);
|
||||
$this->assertSame(
|
||||
[$eventDate->id, $eventDate->id],
|
||||
$item->variants->pluck('event_date_id')->all(),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_rejects_duplicate_variant_combinations(): void
|
||||
{
|
||||
$sector = $this->createAttribute('sector');
|
||||
$eventDate = $this->tenant->eventDates()->create([
|
||||
'date' => '2026-10-09',
|
||||
'time_start' => '09:00',
|
||||
'time_end' => '18:00',
|
||||
]);
|
||||
|
||||
try {
|
||||
$this->service->create([
|
||||
'tenant_code' => $this->tenant->codigo,
|
||||
'slug' => 'duplicate-entry',
|
||||
'nombre' => 'Duplicate entry',
|
||||
'precio' => 100,
|
||||
'attribute_codes' => [$sector->codigo],
|
||||
'variants' => [
|
||||
['event_date_id' => $eventDate->id, 'values' => ['sector' => 'VIP']],
|
||||
['event_date_id' => $eventDate->id, 'values' => ['sector' => ' vip ']],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->fail('A validation exception was not thrown.');
|
||||
} catch (ValidationException $exception) {
|
||||
$this->assertArrayHasKey('variants.1', $exception->errors());
|
||||
}
|
||||
}
|
||||
|
||||
public function test_it_rejects_direct_inventory_together_with_variants(): void
|
||||
{
|
||||
$attribute = $this->createAttribute('size');
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Tests\Feature\Event;
|
||||
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Authorization\Enums\RoleCode;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Tenant\Models\WebsiteType;
|
||||
use Database\Seeders\AuthorizationSeeder;
|
||||
@@ -50,17 +49,14 @@ class AdminAppEventControllerTest extends TestCase
|
||||
->assertJsonPath('data.contact.instagram_url', 'https://instagram.com/acme')
|
||||
->assertJsonPath('data.contact.facebook_url', null);
|
||||
|
||||
$eventId = $response->json('data.id');
|
||||
|
||||
$this->assertDatabaseHas('events', [
|
||||
'id' => $eventId,
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'name' => 'Festival Acme',
|
||||
'address' => 'Predio Ferial, Rosario',
|
||||
$this->assertSame($tenant->id, $response->json('data.id'));
|
||||
$this->assertDatabaseHas('tenants', [
|
||||
'id' => $tenant->id,
|
||||
'event_title' => 'Festival Acme',
|
||||
'event_location' => 'Predio Ferial, Rosario',
|
||||
]);
|
||||
$this->assertSame($eventId, $tenant->fresh()->active_event_id);
|
||||
$this->assertDatabaseHas('event_dates', [
|
||||
'event_id' => $eventId,
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'date' => '2026-10-09',
|
||||
'time_start' => '09:00:00',
|
||||
'time_end' => '18:30:00',
|
||||
@@ -86,24 +82,27 @@ class AdminAppEventControllerTest extends TestCase
|
||||
->assertJsonMissing(['title' => 'Other Event']);
|
||||
}
|
||||
|
||||
public function test_reading_a_tenant_without_an_active_event_returns_not_found(): void
|
||||
public function test_reading_a_tenant_without_event_configuration_returns_empty_values(): void
|
||||
{
|
||||
$tenant = $this->createTenant('acme');
|
||||
Sanctum::actingAs($this->createAdminAppUser($tenant));
|
||||
|
||||
$this->getJson('/api/v1/adminapp/tenant/event')->assertNotFound();
|
||||
$this->getJson('/api/v1/adminapp/tenant/event')
|
||||
->assertOk()
|
||||
->assertJsonPath('data.title', null)
|
||||
->assertJsonCount(0, 'data.dates');
|
||||
}
|
||||
|
||||
public function test_updating_reuses_the_active_event_and_synchronizes_dates_and_contact(): void
|
||||
{
|
||||
$tenant = $this->createTenant('acme');
|
||||
$event = $this->createActiveEvent($tenant, 'Old Event');
|
||||
$firstDate = $event->dates()->create([
|
||||
$eventTenant = $this->createActiveEvent($tenant, 'Old Event');
|
||||
$firstDate = $eventTenant->eventDates()->create([
|
||||
'date' => '2026-10-01',
|
||||
'time_start' => '08:00',
|
||||
'time_end' => '12:00',
|
||||
]);
|
||||
$removedDate = $event->dates()->create([
|
||||
$removedDate = $eventTenant->eventDates()->create([
|
||||
'date' => '2026-10-02',
|
||||
'time_start' => '08:00',
|
||||
'time_end' => '12:00',
|
||||
@@ -127,7 +126,7 @@ class AdminAppEventControllerTest extends TestCase
|
||||
|
||||
$this->putJson('/api/v1/adminapp/tenant/event', $payload)
|
||||
->assertOk()
|
||||
->assertJsonPath('data.id', $event->id)
|
||||
->assertJsonPath('data.id', $tenant->id)
|
||||
->assertJsonPath('data.dates.0.id', $firstDate->id)
|
||||
->assertJsonPath('data.contact.facebook_url', null);
|
||||
|
||||
@@ -175,7 +174,7 @@ class AdminAppEventControllerTest extends TestCase
|
||||
'contact.whatsapp_url',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseCount('events', 0);
|
||||
$this->assertNull($tenant->fresh()->event_title);
|
||||
}
|
||||
|
||||
public function test_social_media_accepts_any_registered_code_and_rejects_unknown_codes(): void
|
||||
@@ -257,14 +256,13 @@ class AdminAppEventControllerTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
private function createActiveEvent(Tenant $tenant, string $name): Event
|
||||
private function createActiveEvent(Tenant $tenant, string $name): Tenant
|
||||
{
|
||||
$event = $tenant->events()->create([
|
||||
'name' => $name,
|
||||
'address' => 'Rosario',
|
||||
$tenant->update([
|
||||
'event_title' => $name,
|
||||
'event_location' => 'Rosario',
|
||||
]);
|
||||
$tenant->update(['active_event_id' => $event->id]);
|
||||
|
||||
return $event;
|
||||
return $tenant->fresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\Inventory;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Purchase\Services\CheckoutService;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
@@ -34,12 +33,10 @@ class StorePurchaseTest extends TestCase
|
||||
public function test_it_creates_an_independent_purchase_snapshot_from_cart(): void
|
||||
{
|
||||
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
$event = Event::query()->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'name' => 'Sonder Fest',
|
||||
'address' => 'Test address',
|
||||
$tenant->update([
|
||||
'event_title' => 'Sonder Fest',
|
||||
'event_location' => 'Test address',
|
||||
]);
|
||||
$tenant->update(['active_event_id' => $event->id]);
|
||||
$user = User::factory()->create([
|
||||
'email' => 'buyer@example.com',
|
||||
]);
|
||||
@@ -92,7 +89,6 @@ class StorePurchaseTest extends TestCase
|
||||
$response->assertJsonPath('data.nombre_apellido', null);
|
||||
$response->assertJsonPath('data.email', null);
|
||||
$response->assertJsonPath('data.tenant_codigo', 'sonder');
|
||||
$response->assertJsonPath('data.event_id', $event->id);
|
||||
$response->assertJsonPath('data.status', Purchase::STATUS_CREATED);
|
||||
$response->assertJsonPath('data.items_source', 'purchase');
|
||||
$response->assertJsonCount(1, 'data.items');
|
||||
@@ -105,7 +101,6 @@ class StorePurchaseTest extends TestCase
|
||||
'id' => $purchaseId,
|
||||
'cart_id' => $cartId,
|
||||
'tenant_codigo' => 'sonder',
|
||||
'event_id' => $event->id,
|
||||
'user_id' => $user->id,
|
||||
'dni' => null,
|
||||
'telefono' => null,
|
||||
|
||||
@@ -14,7 +14,6 @@ use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
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;
|
||||
@@ -96,15 +95,11 @@ class FiestaFutbolInfantilProductSeederTest extends TestCase
|
||||
->all(),
|
||||
);
|
||||
|
||||
$event = Event::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->with('dates')
|
||||
->sole();
|
||||
|
||||
$this->assertSame($event->id, $tenant->fresh()->active_event_id);
|
||||
$this->assertCount(4, $event->dates);
|
||||
$this->assertSame(1, Event::query()->where('tenant_code', $tenant->codigo)->count());
|
||||
$this->assertSame(4, EventDate::query()->where('event_id', $event->id)->count());
|
||||
$tenant->refresh()->load('eventDates');
|
||||
$this->assertSame('Fiesta Nacional del Fútbol Infantil', $tenant->event_title);
|
||||
$this->assertSame('Sunchales, Santa Fe', $tenant->event_location);
|
||||
$this->assertCount(4, $tenant->eventDates);
|
||||
$this->assertSame(4, EventDate::query()->where('tenant_code', $tenant->codigo)->count());
|
||||
|
||||
$generalAdmission = CatalogItem::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
@@ -113,7 +108,6 @@ class FiestaFutbolInfantilProductSeederTest extends TestCase
|
||||
->sole();
|
||||
|
||||
$this->assertNull($generalAdmission->inventory_id);
|
||||
$this->assertTrue($generalAdmission->event->is($event));
|
||||
$this->assertSame(EventProductType::Entry, $generalAdmission->event_product_type);
|
||||
$this->assertCount(0, $generalAdmission->itemAttributes);
|
||||
$this->assertCount(4, $generalAdmission->variants);
|
||||
@@ -133,7 +127,6 @@ class FiestaFutbolInfantilProductSeederTest extends TestCase
|
||||
|
||||
$this->assertCount(7, $standardItems);
|
||||
foreach ($standardItems as $standardItem) {
|
||||
$this->assertSame($event->id, $standardItem->event_id);
|
||||
$this->assertSame(
|
||||
'2026-10-09 00:00:00',
|
||||
$standardItem->validityTime->fixed_starts_at->format('Y-m-d H:i:s'),
|
||||
@@ -154,7 +147,6 @@ class FiestaFutbolInfantilProductSeederTest extends TestCase
|
||||
$this->assertSame('40000.00', $allDaysItem->precio);
|
||||
$this->assertNull($allDaysItem->inventory_id);
|
||||
$this->assertFalse($allDaysItem->has_tickets);
|
||||
$this->assertSame($event->id, $allDaysItem->event_id);
|
||||
$this->assertSame(EventProductType::Entry, $allDaysItem->event_product_type);
|
||||
$this->assertCount(4, $allDaysItem->bundleComponents);
|
||||
$this->assertEqualsCanonicalizing(
|
||||
|
||||
@@ -21,7 +21,6 @@ use App\Domains\Catalog\Models\Inventory;
|
||||
use App\Domains\Catalog\Models\ItemAttribute;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Catalog\Models\VariantDefinition;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Shared\Enums\FieldType;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
@@ -65,7 +64,6 @@ class CatalogModelsTest extends TestCase
|
||||
'category_id' => '10',
|
||||
'brand_id' => '20',
|
||||
'inventory_id' => '30',
|
||||
'event_id' => '40',
|
||||
'event_product_type' => EventProductType::Entry->value,
|
||||
'type' => CatalogItemType::Standard->value,
|
||||
'precio' => '12.50',
|
||||
@@ -78,14 +76,12 @@ class CatalogModelsTest extends TestCase
|
||||
$this->assertSame(10, $item->category_id);
|
||||
$this->assertSame(20, $item->brand_id);
|
||||
$this->assertSame(30, $item->inventory_id);
|
||||
$this->assertSame(40, $item->event_id);
|
||||
$this->assertSame(EventProductType::Entry, $item->event_product_type);
|
||||
$this->assertSame(CatalogItemType::Standard, $item->type);
|
||||
$this->assertSame('12.50', $item->precio);
|
||||
$this->assertSame(InventoryPolicy::Tracked, $item->inventory_policy);
|
||||
$this->assertTrue($item->has_tickets);
|
||||
$this->assertInstanceOf(Tenant::class, $item->tenant()->getRelated());
|
||||
$this->assertInstanceOf(Event::class, $item->event()->getRelated());
|
||||
$this->assertInstanceOf(Category::class, $item->category()->getRelated());
|
||||
$this->assertInstanceOf(Brand::class, $item->brand()->getRelated());
|
||||
$this->assertInstanceOf(Inventory::class, $item->inventory()->getRelated());
|
||||
|
||||
@@ -2,48 +2,35 @@
|
||||
|
||||
namespace Tests\Unit\Event;
|
||||
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Event\Models\Event;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Tests\TestCase;
|
||||
|
||||
class EventModelsTest extends TestCase
|
||||
{
|
||||
public function test_event_maps_its_tenant_dates_and_catalog_items(): void
|
||||
{
|
||||
$event = new Event;
|
||||
|
||||
$this->assertFalse($event->usesTimestamps());
|
||||
$this->assertInstanceOf(Tenant::class, $event->tenant()->getRelated());
|
||||
$this->assertInstanceOf(EventDate::class, $event->dates()->getRelated());
|
||||
$this->assertInstanceOf(CatalogItem::class, $event->catalogItems()->getRelated());
|
||||
}
|
||||
|
||||
public function test_event_date_maps_schedule_and_variants(): void
|
||||
{
|
||||
$eventDate = new EventDate;
|
||||
$eventDate->setRawAttributes([
|
||||
'event_id' => '10',
|
||||
'tenant_code' => 'acme',
|
||||
'date' => '2026-10-09',
|
||||
'time_start' => '09:00:00',
|
||||
'time_end' => '18:30:00',
|
||||
]);
|
||||
|
||||
$this->assertFalse($eventDate->usesTimestamps());
|
||||
$this->assertSame(10, $eventDate->event_id);
|
||||
$this->assertSame('acme', $eventDate->tenant_code);
|
||||
$this->assertSame('2026-10-09 09:00:00', $eventDate->startsAt()->format('Y-m-d H:i:s'));
|
||||
$this->assertSame('2026-10-09 18:30:00', $eventDate->endsAt()->format('Y-m-d H:i:s'));
|
||||
$this->assertInstanceOf(Event::class, $eventDate->event()->getRelated());
|
||||
$this->assertInstanceOf(Tenant::class, $eventDate->tenant()->getRelated());
|
||||
$this->assertInstanceOf(Variant::class, $eventDate->variants()->getRelated());
|
||||
}
|
||||
|
||||
public function test_tenant_has_many_events_and_one_active_event(): void
|
||||
public function test_tenant_has_many_event_dates(): void
|
||||
{
|
||||
$tenant = new Tenant;
|
||||
|
||||
$this->assertInstanceOf(Event::class, $tenant->events()->getRelated());
|
||||
$this->assertInstanceOf(Event::class, $tenant->activeEvent()->getRelated());
|
||||
$this->assertInstanceOf(EventDate::class, $tenant->eventDates()->getRelated());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user