refactor(backend): reorganize domains into Core, Commerce, Ticketing and Shared
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Forms\Resources\EntryFormResource;
|
||||
use App\Domains\Forms\Services\EntryFormService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EntryFormController extends Controller
|
||||
{
|
||||
public function __construct(protected EntryFormService $entryFormService) {}
|
||||
|
||||
public function __invoke(Request $request): EntryFormResource
|
||||
{
|
||||
return EntryFormResource::make(
|
||||
$this->entryFormService->get(
|
||||
$request->user('sanctum')->tenant()->firstOrFail()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Forms\Resources\EventFormResource;
|
||||
use App\Domains\Forms\Services\EventFormService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EventFormController extends Controller
|
||||
{
|
||||
public function __construct(protected EventFormService $eventFormService) {}
|
||||
|
||||
public function __invoke(Request $request): EventFormResource
|
||||
{
|
||||
return EventFormResource::make(
|
||||
$this->eventFormService->get(
|
||||
$request->user('sanctum')->tenant()->firstOrFail()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
22
app/Shared/Forms/Controllers/AdminApp/FoodFormController.php
Normal file
22
app/Shared/Forms/Controllers/AdminApp/FoodFormController.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Forms\Resources\FoodFormResource;
|
||||
use App\Domains\Forms\Services\FoodFormService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FoodFormController extends Controller
|
||||
{
|
||||
public function __construct(protected FoodFormService $foodFormService) {}
|
||||
|
||||
public function __invoke(Request $request): FoodFormResource
|
||||
{
|
||||
return FoodFormResource::make(
|
||||
$this->foodFormService->get(
|
||||
$request->user('sanctum')->tenant()->firstOrFail()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Forms\Resources\MerchandiseFormResource;
|
||||
use App\Domains\Forms\Services\MerchandiseFormService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MerchandiseFormController extends Controller
|
||||
{
|
||||
public function __construct(protected MerchandiseFormService $merchandiseFormService) {}
|
||||
|
||||
public function __invoke(Request $request): MerchandiseFormResource
|
||||
{
|
||||
return MerchandiseFormResource::make(
|
||||
$this->merchandiseFormService->get(
|
||||
$request->user('sanctum')->tenant()->firstOrFail()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
17
app/Shared/Forms/Controllers/AdminApp/SaleFormController.php
Normal file
17
app/Shared/Forms/Controllers/AdminApp/SaleFormController.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Forms\Resources\SaleFormResource;
|
||||
use App\Domains\Forms\Services\SaleFormService;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class SaleFormController extends Controller
|
||||
{
|
||||
public function __construct(protected SaleFormService $saleFormService) {}
|
||||
|
||||
public function __invoke(): SaleFormResource
|
||||
{
|
||||
return SaleFormResource::make($this->saleFormService->get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Forms\Resources\StaffFormResource;
|
||||
use App\Domains\Forms\Services\StaffFormService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class StaffFormController extends Controller
|
||||
{
|
||||
public function __construct(protected StaffFormService $staffFormService) {}
|
||||
|
||||
public function __invoke(Request $request): StaffFormResource
|
||||
{
|
||||
return StaffFormResource::make(
|
||||
$this->staffFormService->get(
|
||||
$request->user('sanctum')->tenant()->firstOrFail()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Forms\Resources\TicketFilterFormResource;
|
||||
use App\Domains\Forms\Services\TicketFilterFormService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TicketFilterFormController extends Controller
|
||||
{
|
||||
public function __construct(private readonly TicketFilterFormService $formService) {}
|
||||
|
||||
public function __invoke(Request $request): TicketFilterFormResource
|
||||
{
|
||||
$tenant = $request->user('sanctum')->tenant()->firstOrFail();
|
||||
|
||||
return TicketFilterFormResource::make($this->formService->get($tenant));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Controllers\AdminApp;
|
||||
|
||||
use App\Domains\Forms\Resources\TicketFormResource;
|
||||
use App\Domains\Forms\Services\TicketFormService;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TicketFormController extends Controller
|
||||
{
|
||||
public function __construct(protected TicketFormService $ticketFormService) {}
|
||||
|
||||
public function __invoke(Request $request): TicketFormResource
|
||||
{
|
||||
return TicketFormResource::make(
|
||||
$this->ticketFormService->get(
|
||||
$request->user('sanctum')->tenant()->firstOrFail()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
26
app/Shared/Forms/Resources/EntryFormResource.php
Normal file
26
app/Shared/Forms/Resources/EntryFormResource.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Ticket\Resources\ValidityTimeResource;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class EntryFormResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'event_dates' => $this->resource['event_dates']->map(
|
||||
fn (EventDate $eventDate): array => [
|
||||
'id' => $eventDate->id,
|
||||
'validity_time_id' => $eventDate->validity_time_id,
|
||||
'validity_time' => ValidityTimeResource::make($eventDate->validityTime),
|
||||
'date' => $eventDate->date->format('Y-m-d'),
|
||||
]
|
||||
)->values(),
|
||||
];
|
||||
}
|
||||
}
|
||||
19
app/Shared/Forms/Resources/EventFormResource.php
Normal file
19
app/Shared/Forms/Resources/EventFormResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class EventFormResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'social_media' => SocialMediaOptionResource::collection(
|
||||
$this->resource['social_media']
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
42
app/Shared/Forms/Resources/FoodFormResource.php
Normal file
42
app/Shared/Forms/Resources/FoodFormResource.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use App\Domains\Catalog\Models\AttributeOption;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Ticket\Resources\ValidityTimeResource;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class FoodFormResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'event_dates' => $this->resource['event_dates']->map(
|
||||
fn (EventDate $eventDate): array => [
|
||||
'id' => $eventDate->id,
|
||||
'validity_time_id' => $eventDate->validity_time_id,
|
||||
'validity_time' => ValidityTimeResource::make($eventDate->validityTime),
|
||||
'date' => $eventDate->date->format('Y-m-d'),
|
||||
]
|
||||
)->values(),
|
||||
'schedules' => $this->options($this->resource['schedules']),
|
||||
'services' => $this->options($this->resource['services']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<int, AttributeOption> $options
|
||||
* @return Collection<int, array{value: string, label: string}>
|
||||
*/
|
||||
private function options(Collection $options): Collection
|
||||
{
|
||||
return $options->map(fn (AttributeOption $option): array => [
|
||||
'value' => $option->value,
|
||||
'label' => $option->label,
|
||||
])->values();
|
||||
}
|
||||
}
|
||||
32
app/Shared/Forms/Resources/MerchandiseFormResource.php
Normal file
32
app/Shared/Forms/Resources/MerchandiseFormResource.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use App\Domains\Catalog\Models\AttributeOption;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class MerchandiseFormResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'colors' => $this->options($this->resource['colors']),
|
||||
'sizes' => $this->options($this->resource['sizes']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<int, AttributeOption> $options
|
||||
* @return Collection<int, array{value: string, label: string}>
|
||||
*/
|
||||
private function options(Collection $options): Collection
|
||||
{
|
||||
return $options->map(fn (AttributeOption $option): array => [
|
||||
'value' => $option->value,
|
||||
'label' => $option->label,
|
||||
])->values();
|
||||
}
|
||||
}
|
||||
17
app/Shared/Forms/Resources/SaleFormResource.php
Normal file
17
app/Shared/Forms/Resources/SaleFormResource.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class SaleFormResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'statuses' => $this->resource['statuses'],
|
||||
];
|
||||
}
|
||||
}
|
||||
22
app/Shared/Forms/Resources/SocialMediaOptionResource.php
Normal file
22
app/Shared/Forms/Resources/SocialMediaOptionResource.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use App\Domains\Tenant\Models\SocialMedia;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin SocialMedia */
|
||||
class SocialMediaOptionResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->code,
|
||||
'name' => $this->name,
|
||||
'icon' => $this->icon,
|
||||
'url' => $this->url,
|
||||
];
|
||||
}
|
||||
}
|
||||
20
app/Shared/Forms/Resources/StaffFormResource.php
Normal file
20
app/Shared/Forms/Resources/StaffFormResource.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class StaffFormResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'categories' => $this->resource['categories']->map(fn ($category) => [
|
||||
'id' => $category->id,
|
||||
'nombre' => $category->nombre,
|
||||
])->values(),
|
||||
];
|
||||
}
|
||||
}
|
||||
21
app/Shared/Forms/Resources/TicketFilterFormResource.php
Normal file
21
app/Shared/Forms/Resources/TicketFilterFormResource.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class TicketFilterFormResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->resource['code'],
|
||||
'action' => $this->resource['action'],
|
||||
'method' => $this->resource['method'],
|
||||
'fields' => $this->resource['fields'],
|
||||
'columns' => $this->resource['columns'],
|
||||
];
|
||||
}
|
||||
}
|
||||
18
app/Shared/Forms/Resources/TicketFormResource.php
Normal file
18
app/Shared/Forms/Resources/TicketFormResource.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class TicketFormResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'statuses' => $this->resource['statuses'],
|
||||
'categories' => $this->resource['categories'],
|
||||
];
|
||||
}
|
||||
}
|
||||
22
app/Shared/Forms/Services/EntryFormService.php
Normal file
22
app/Shared/Forms/Services/EntryFormService.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Services;
|
||||
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class EntryFormService
|
||||
{
|
||||
/** @return array{event_dates: Collection<int, EventDate>} */
|
||||
public function get(Tenant $tenant): array
|
||||
{
|
||||
return [
|
||||
'event_dates' => $tenant->eventDates()
|
||||
->whereNull('rescheduled_to_event_date_id')
|
||||
->whereNull('suspended_at')
|
||||
->with('validityTime')
|
||||
->get(),
|
||||
];
|
||||
}
|
||||
}
|
||||
27
app/Shared/Forms/Services/EventFormService.php
Normal file
27
app/Shared/Forms/Services/EventFormService.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Services;
|
||||
|
||||
use App\Domains\Tenant\Models\SocialMedia;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class EventFormService
|
||||
{
|
||||
/** @return array{social_media: Collection<int, SocialMedia>} */
|
||||
public function get(Tenant $tenant): array
|
||||
{
|
||||
$urls = $tenant->socialMedia()
|
||||
->pluck('tenant_social_media.url', 'social_media.code');
|
||||
|
||||
return [
|
||||
'social_media' => SocialMedia::query()
|
||||
->orderBy('id')
|
||||
->get()
|
||||
->each(fn (SocialMedia $item) => $item->setAttribute(
|
||||
'url',
|
||||
$urls->get($item->code)
|
||||
)),
|
||||
];
|
||||
}
|
||||
}
|
||||
46
app/Shared/Forms/Services/FoodFormService.php
Normal file
46
app/Shared/Forms/Services/FoodFormService.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Services;
|
||||
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\AttributeOption;
|
||||
use App\Domains\Event\Enums\EventDateStatus;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class FoodFormService
|
||||
{
|
||||
/**
|
||||
* @return array{
|
||||
* event_dates: Collection<int, EventDate>,
|
||||
* schedules: Collection<int, AttributeOption>,
|
||||
* services: Collection<int, AttributeOption>
|
||||
* }
|
||||
*/
|
||||
public function get(Tenant $tenant): array
|
||||
{
|
||||
$attributes = Attribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->whereIn('codigo', ['horario', 'servicio'])
|
||||
->with('options')
|
||||
->get()
|
||||
->keyBy('codigo');
|
||||
|
||||
return [
|
||||
'event_dates' => $tenant->eventDates()
|
||||
->whereNull('rescheduled_to_event_date_id')
|
||||
->whereNull('suspended_at')
|
||||
->with('validityTime')
|
||||
->get()
|
||||
->filter(fn (EventDate $eventDate): bool => in_array(
|
||||
$eventDate->status,
|
||||
[EventDateStatus::Scheduled, EventDateStatus::InProgress],
|
||||
true,
|
||||
))
|
||||
->values(),
|
||||
'schedules' => $attributes->get('horario')?->options ?? new Collection,
|
||||
'services' => $attributes->get('servicio')?->options ?? new Collection,
|
||||
];
|
||||
}
|
||||
}
|
||||
32
app/Shared/Forms/Services/MerchandiseFormService.php
Normal file
32
app/Shared/Forms/Services/MerchandiseFormService.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Services;
|
||||
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\AttributeOption;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class MerchandiseFormService
|
||||
{
|
||||
/**
|
||||
* @return array{
|
||||
* colors: Collection<int, AttributeOption>,
|
||||
* sizes: Collection<int, AttributeOption>
|
||||
* }
|
||||
*/
|
||||
public function get(Tenant $tenant): array
|
||||
{
|
||||
$attributes = Attribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->whereIn('codigo', ['color', 'talle'])
|
||||
->with('options')
|
||||
->get()
|
||||
->keyBy('codigo');
|
||||
|
||||
return [
|
||||
'colors' => $attributes->get('color')?->options ?? new Collection,
|
||||
'sizes' => $attributes->get('talle')?->options ?? new Collection,
|
||||
];
|
||||
}
|
||||
}
|
||||
24
app/Shared/Forms/Services/SaleFormService.php
Normal file
24
app/Shared/Forms/Services/SaleFormService.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Services;
|
||||
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
|
||||
class SaleFormService
|
||||
{
|
||||
/** @return array{statuses: list<array{value: string, label: string, real_statuses: list<string>}>} */
|
||||
public function get(): array
|
||||
{
|
||||
return [
|
||||
'statuses' => array_map(
|
||||
fn (string $code, array $definition): array => [
|
||||
'value' => $code,
|
||||
'label' => $definition['name'],
|
||||
'real_statuses' => $definition['statuses'],
|
||||
],
|
||||
array_keys(Purchase::adminStatuses()),
|
||||
array_values(Purchase::adminStatuses()),
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
27
app/Shared/Forms/Services/StaffFormService.php
Normal file
27
app/Shared/Forms/Services/StaffFormService.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Services;
|
||||
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class StaffFormService
|
||||
{
|
||||
/** @return array{categories: Collection<int, Category>} */
|
||||
public function get(Tenant $tenant): array
|
||||
{
|
||||
return [
|
||||
'categories' => Category::query()
|
||||
->whereNull('categoria_id')
|
||||
->where(function (Builder $query) use ($tenant): void {
|
||||
$query->where('tenant_code', $tenant->codigo)
|
||||
->orWhereHas('catalogItems', fn (Builder $items) => $items
|
||||
->where('tenant_code', $tenant->codigo));
|
||||
})
|
||||
->orderBy('nombre')
|
||||
->get(),
|
||||
];
|
||||
}
|
||||
}
|
||||
151
app/Shared/Forms/Services/TicketFilterFormService.php
Normal file
151
app/Shared/Forms/Services/TicketFilterFormService.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Services;
|
||||
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Models\Ticket;
|
||||
use App\Domains\Ticket\Services\AdminAppTicketColumnService;
|
||||
|
||||
class TicketFilterFormService
|
||||
{
|
||||
private const FIESTA_FUTBOL_INFANTIL = 'fiesta_futbol_infantil';
|
||||
|
||||
public function __construct(
|
||||
private readonly TicketFormService $ticketFormService,
|
||||
private readonly AdminAppTicketColumnService $columnService,
|
||||
) {}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function get(Tenant $tenant): array
|
||||
{
|
||||
$fields = $this->commonFields();
|
||||
|
||||
if ($tenant->codigo === self::FIESTA_FUTBOL_INFANTIL) {
|
||||
$fields = [
|
||||
...$this->fiestaFutbolInfantilFields($tenant),
|
||||
...$this->commonFields(includeDate: false),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => 'tickets_filter',
|
||||
'action' => '/api/v1/adminapp/tenant/tickets',
|
||||
'method' => 'GET',
|
||||
'fields' => $fields,
|
||||
'columns' => $this->columnService->publicColumns($tenant),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return list<array<string, mixed>> */
|
||||
private function fiestaFutbolInfantilFields(Tenant $tenant): array
|
||||
{
|
||||
$form = $this->ticketFormService->getForFilters($tenant);
|
||||
|
||||
return [
|
||||
[
|
||||
'name' => 'category',
|
||||
'query_param' => 'category',
|
||||
'label' => 'Categoría',
|
||||
'type' => 'select',
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'placeholder' => 'Categoría',
|
||||
'options' => array_map(
|
||||
fn (array $category): array => [
|
||||
'value' => $category['value'],
|
||||
'label' => $category['label'],
|
||||
'children' => [
|
||||
[
|
||||
'field' => 'product',
|
||||
'disabled' => $category['products'] === [],
|
||||
'options' => array_map(
|
||||
fn (array $product): array => [
|
||||
'value' => $product['value'],
|
||||
'label' => $product['label'],
|
||||
'children' => [
|
||||
[
|
||||
'field' => 'type',
|
||||
'disabled' => $product['types'] === [],
|
||||
'options' => array_map(
|
||||
fn (array $type): array => [
|
||||
'value' => $type['value'],
|
||||
'label' => $type['label'],
|
||||
'children' => [[
|
||||
'field' => 'size',
|
||||
'disabled' => ($type['sizes'] ?? []) === [],
|
||||
'options' => $type['sizes'] ?? [],
|
||||
]],
|
||||
],
|
||||
$product['types'],
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
$category['products'],
|
||||
),
|
||||
],
|
||||
[
|
||||
'field' => 'date',
|
||||
'disabled' => ! in_array($category['value'], ['comidas', 'comida'], true),
|
||||
'options' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
$form['categories'],
|
||||
),
|
||||
],
|
||||
$this->dependentSelect('product', 'Producto', 'category'),
|
||||
$this->dependentSelect('type', 'Tipo', 'product'),
|
||||
$this->dependentSelect('size', 'Talle', 'type'),
|
||||
[
|
||||
...$this->dependentSelect('date', 'Fecha', 'category'),
|
||||
'type' => 'date',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return list<array<string, mixed>> */
|
||||
private function commonFields(bool $includeDate = true): array
|
||||
{
|
||||
return [
|
||||
...($includeDate ? [[
|
||||
'name' => 'date',
|
||||
'query_param' => 'date',
|
||||
'label' => 'Fecha',
|
||||
'type' => 'date',
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
]] : []),
|
||||
[
|
||||
'name' => 'status',
|
||||
'query_param' => 'status',
|
||||
'label' => 'Estado',
|
||||
'type' => 'select',
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'placeholder' => 'Estado',
|
||||
'options' => array_values(array_filter(
|
||||
Ticket::statusOptions(),
|
||||
fn (array $option): bool => $option['value'] !== Ticket::STATUS_CANCELLED,
|
||||
)),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
private function dependentSelect(string $name, string $label, string $dependency): array
|
||||
{
|
||||
return [
|
||||
'name' => $name,
|
||||
'query_param' => $name,
|
||||
'label' => $label,
|
||||
'type' => 'select',
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'placeholder' => $label,
|
||||
'depends_on' => $dependency,
|
||||
'disabled' => true,
|
||||
'options' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
396
app/Shared/Forms/Services/TicketFormService.php
Normal file
396
app/Shared/Forms/Services/TicketFormService.php
Normal file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Forms\Services;
|
||||
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Models\Ticket;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class TicketFormService
|
||||
{
|
||||
private const PRODUCT = 'product';
|
||||
|
||||
/**
|
||||
* @var array<string, array{label: string|null, product: string, type: string|null, order: int}>
|
||||
*/
|
||||
private const CATEGORY_PRESENTATIONS = [
|
||||
'entradas' => [
|
||||
'label' => null,
|
||||
'product' => self::PRODUCT,
|
||||
'type' => null,
|
||||
'order' => 1,
|
||||
],
|
||||
'alojamientos' => [
|
||||
'label' => 'Camping',
|
||||
'product' => 'tipo_alojamiento',
|
||||
'type' => null,
|
||||
'order' => 2,
|
||||
],
|
||||
'camping' => [
|
||||
'label' => null,
|
||||
'product' => 'tipo_alojamiento',
|
||||
'type' => null,
|
||||
'order' => 2,
|
||||
],
|
||||
'comidas' => [
|
||||
'label' => 'Comida',
|
||||
'product' => 'event_date',
|
||||
'type' => 'horario',
|
||||
'order' => 3,
|
||||
],
|
||||
'comida' => [
|
||||
'label' => null,
|
||||
'product' => 'event_date',
|
||||
'type' => 'horario',
|
||||
'order' => 3,
|
||||
],
|
||||
'merchandising' => [
|
||||
'label' => null,
|
||||
'product' => self::PRODUCT,
|
||||
'type' => 'color',
|
||||
'order' => 4,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, array{label: string|null, product: string, type: string|null, size: string|null, order: int}>
|
||||
*/
|
||||
private const FILTER_CATEGORY_PRESENTATIONS = [
|
||||
'entradas' => ['label' => null, 'product' => self::PRODUCT, 'type' => null, 'size' => null, 'order' => 1],
|
||||
'alojamientos' => ['label' => 'Camping', 'product' => 'tipo_alojamiento', 'type' => null, 'size' => null, 'order' => 2],
|
||||
'camping' => ['label' => null, 'product' => 'tipo_alojamiento', 'type' => null, 'size' => null, 'order' => 2],
|
||||
'comidas' => ['label' => 'Comida', 'product' => 'horario', 'type' => 'servicio', 'size' => null, 'order' => 3],
|
||||
'comida' => ['label' => null, 'product' => 'horario', 'type' => 'servicio', 'size' => null, 'order' => 3],
|
||||
'merchandising' => ['label' => null, 'product' => self::PRODUCT, 'type' => 'color', 'size' => 'talle', 'order' => 4],
|
||||
];
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* statuses: list<array{value: string, label: string}>,
|
||||
* categories: list<array{
|
||||
* value: string,
|
||||
* label: string,
|
||||
* products: list<array{
|
||||
* value: string,
|
||||
* label: string,
|
||||
* types: list<array{value: string, label: string}>
|
||||
* }>
|
||||
* }>
|
||||
* }
|
||||
*/
|
||||
public function get(Tenant $tenant): array
|
||||
{
|
||||
$items = CatalogItem::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->where('has_tickets', true)
|
||||
->whereHas('category')
|
||||
->with($this->relations())
|
||||
->orderBy('group_order')
|
||||
->orderBy('nombre')
|
||||
->get();
|
||||
|
||||
return $this->build($items, self::CATEGORY_PRESENTATIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return active catalog options plus soft-deleted sources still referenced by
|
||||
* tickets, so historical tickets never become impossible to filter.
|
||||
*
|
||||
* @return array{
|
||||
* statuses: list<array{value: string, label: string}>,
|
||||
* categories: list<array{
|
||||
* value: string,
|
||||
* label: string,
|
||||
* products: list<array{
|
||||
* value: string,
|
||||
* label: string,
|
||||
* types: list<array{value: string, label: string}>
|
||||
* }>
|
||||
* }>
|
||||
* }
|
||||
*/
|
||||
public function getForFilters(Tenant $tenant): array
|
||||
{
|
||||
$historicalVariantIds = Ticket::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->whereNotNull('source_variant_id')
|
||||
->distinct()
|
||||
->pluck('source_variant_id')
|
||||
->map(fn ($id): int => (int) $id)
|
||||
->all();
|
||||
$historicalCatalogItemIds = Ticket::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->whereNotNull('source_catalog_item_id')
|
||||
->distinct()
|
||||
->pluck('source_catalog_item_id')
|
||||
->map(fn ($id): int => (int) $id)
|
||||
->merge(
|
||||
Variant::withTrashed()
|
||||
->whereKey($historicalVariantIds)
|
||||
->pluck('catalog_item_id')
|
||||
->map(fn ($id): int => (int) $id),
|
||||
)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$items = CatalogItem::withTrashed()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->whereHas('category')
|
||||
->where(function ($query) use ($historicalCatalogItemIds): void {
|
||||
$query
|
||||
->where(function ($activeQuery): void {
|
||||
$activeQuery
|
||||
->whereNull('catalog_items.deleted_at')
|
||||
->where('has_tickets', true);
|
||||
})
|
||||
->orWhereIn('catalog_items.id', $historicalCatalogItemIds);
|
||||
})
|
||||
->with([
|
||||
'category',
|
||||
'itemAttributes.attribute.options',
|
||||
'variants' => fn ($query) => $query
|
||||
->withTrashed()
|
||||
->where(function ($variantQuery) use ($historicalVariantIds): void {
|
||||
$variantQuery
|
||||
->whereNull('variantes.deleted_at')
|
||||
->orWhereIn('variantes.id', $historicalVariantIds);
|
||||
}),
|
||||
'variants.definitions.itemAttribute.attribute.options',
|
||||
'variants.eventDates',
|
||||
'variants.eventDate',
|
||||
])
|
||||
->orderBy('group_order')
|
||||
->orderBy('nombre')
|
||||
->get();
|
||||
|
||||
return $this->build($items, self::FILTER_CATEGORY_PRESENTATIONS, includeSizes: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<int, CatalogItem> $items
|
||||
* @return array{
|
||||
* statuses: list<array{value: string, label: string}>,
|
||||
* categories: list<array{
|
||||
* value: string,
|
||||
* label: string,
|
||||
* products: list<array{
|
||||
* value: string,
|
||||
* label: string,
|
||||
* types: list<array{value: string, label: string}>
|
||||
* }>
|
||||
* }>
|
||||
* }
|
||||
*/
|
||||
private function build(Collection $items, array $presentations, bool $includeSizes = false): array
|
||||
{
|
||||
$categories = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
$sourceCategory = trim((string) $item->category?->nombre);
|
||||
$categoryValue = mb_strtolower($sourceCategory);
|
||||
$presentation = $presentations[$categoryValue] ?? [
|
||||
'label' => null,
|
||||
'product' => self::PRODUCT,
|
||||
'type' => null,
|
||||
'size' => null,
|
||||
'order' => PHP_INT_MAX,
|
||||
];
|
||||
|
||||
$categories[$categoryValue] ??= [
|
||||
'value' => $categoryValue,
|
||||
'label' => $presentation['label'] ?? $sourceCategory,
|
||||
'order' => $presentation['order'],
|
||||
'products' => [],
|
||||
];
|
||||
|
||||
foreach ($this->products(
|
||||
$item,
|
||||
$presentation['product'],
|
||||
$presentation['type'],
|
||||
$presentation['size'] ?? null,
|
||||
) as $product) {
|
||||
$productValue = $product['value'];
|
||||
$existingProduct = $categories[$categoryValue]['products'][$productValue] ?? [
|
||||
'value' => $productValue,
|
||||
'label' => $product['label'],
|
||||
'types' => [],
|
||||
'sizes' => [],
|
||||
];
|
||||
|
||||
foreach ($product['types'] as $type) {
|
||||
$existingProduct['types'][$type['value']] = $type;
|
||||
}
|
||||
|
||||
foreach ($product['sizes'] as $size) {
|
||||
$existingProduct['sizes'][$size['value']] = $size;
|
||||
}
|
||||
|
||||
$categories[$categoryValue]['products'][$productValue] = $existingProduct;
|
||||
}
|
||||
}
|
||||
|
||||
uasort($categories, fn (array $left, array $right): int => $left['order'] <=> $right['order']
|
||||
?: $left['label'] <=> $right['label']);
|
||||
|
||||
return [
|
||||
'statuses' => Ticket::statusOptions(),
|
||||
'categories' => array_values(array_map(
|
||||
fn (array $category): array => [
|
||||
'value' => $category['value'],
|
||||
'label' => $category['label'],
|
||||
'products' => array_values(array_map(
|
||||
fn (array $product): array => [
|
||||
'value' => $product['value'],
|
||||
'label' => $product['label'],
|
||||
'types' => array_values($product['types']),
|
||||
...($includeSizes ? ['sizes' => array_values($product['sizes'])] : []),
|
||||
],
|
||||
$category['products'],
|
||||
)),
|
||||
],
|
||||
$categories,
|
||||
)),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function relations(): array
|
||||
{
|
||||
return [
|
||||
'category',
|
||||
'itemAttributes.attribute.options',
|
||||
'variants.definitions.itemAttribute.attribute.options',
|
||||
'variants.eventDates',
|
||||
'variants.eventDate',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{
|
||||
* value: string,
|
||||
* label: string,
|
||||
* types: list<array{value: string, label: string}>,
|
||||
* sizes: list<array{value: string, label: string}>
|
||||
* }>
|
||||
*/
|
||||
private function products(CatalogItem $item, string $productCode, ?string $typeCode, ?string $sizeCode): array
|
||||
{
|
||||
if ($productCode === self::PRODUCT) {
|
||||
return [[
|
||||
'value' => $item->slug,
|
||||
'label' => $item->nombre,
|
||||
'types' => $this->types($item, $typeCode, $sizeCode),
|
||||
'sizes' => $this->types($item, $sizeCode),
|
||||
]];
|
||||
}
|
||||
|
||||
$products = [];
|
||||
|
||||
foreach ($item->variants as $variant) {
|
||||
foreach ($this->variantOptions($variant, $productCode) as $productOption) {
|
||||
$productValue = $productOption['value'];
|
||||
$products[$productValue] ??= [
|
||||
'value' => $productValue,
|
||||
'label' => $this->optionLabel($productOption['label'], $productCode),
|
||||
'types' => [],
|
||||
'sizes' => [],
|
||||
];
|
||||
|
||||
foreach ($this->variantOptions($variant, $typeCode) as $typeOption) {
|
||||
$this->mergeTypeOption(
|
||||
$products[$productValue]['types'],
|
||||
$typeOption,
|
||||
$variant,
|
||||
$sizeCode,
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->variantOptions($variant, $sizeCode) as $sizeOption) {
|
||||
$products[$productValue]['sizes'][$sizeOption['value']] = $sizeOption;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_map(
|
||||
fn (array $product): array => [
|
||||
'value' => $product['value'],
|
||||
'label' => $product['label'],
|
||||
'types' => array_values($product['types']),
|
||||
'sizes' => array_values($product['sizes']),
|
||||
],
|
||||
$products,
|
||||
));
|
||||
}
|
||||
|
||||
/** @return list<array<string, mixed>> */
|
||||
private function types(CatalogItem $item, ?string $typeCode, ?string $sizeCode = null): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
foreach ($item->variants as $variant) {
|
||||
foreach ($this->variantOptions($variant, $typeCode) as $typeOption) {
|
||||
$this->mergeTypeOption($types, $typeOption, $variant, $sizeCode);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_map(function (array $type) use ($sizeCode): array {
|
||||
if ($sizeCode !== null) {
|
||||
$type['sizes'] = array_values($type['sizes']);
|
||||
}
|
||||
|
||||
return $type;
|
||||
}, $types));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, mixed>> $types
|
||||
* @param array{value: string, label: string} $typeOption
|
||||
*/
|
||||
private function mergeTypeOption(
|
||||
array &$types,
|
||||
array $typeOption,
|
||||
Variant $variant,
|
||||
?string $sizeCode,
|
||||
): void {
|
||||
$typeValue = $typeOption['value'];
|
||||
$types[$typeValue] ??= [
|
||||
...$typeOption,
|
||||
...($sizeCode !== null ? ['sizes' => []] : []),
|
||||
];
|
||||
|
||||
foreach ($this->variantOptions($variant, $sizeCode) as $sizeOption) {
|
||||
$types[$typeValue]['sizes'][$sizeOption['value']] = $sizeOption;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return list<array{value: string, label: string}> */
|
||||
private function variantOptions(Variant $variant, ?string $attributeCode): array
|
||||
{
|
||||
if ($attributeCode === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$selection = $variant->selectionOptions($variant->catalogItem->itemAttributes)
|
||||
->get($attributeCode);
|
||||
|
||||
if ($selection === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_is_list($selection) ? $selection : [$selection];
|
||||
}
|
||||
|
||||
private function optionLabel(string $label, string $attributeCode): string
|
||||
{
|
||||
if ($attributeCode !== 'event_date') {
|
||||
return $label;
|
||||
}
|
||||
|
||||
[$day, $month] = array_pad(explode('/', $label), 2, null);
|
||||
|
||||
return $day !== null && $month !== null ? "{$day}/{$month}" : $label;
|
||||
}
|
||||
}
|
||||
28
app/Shared/Forms/documentacion/README.md
Normal file
28
app/Shared/Forms/documentacion/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Dominio Forms
|
||||
|
||||
## Propósito
|
||||
|
||||
Provee catálogos y opciones auxiliares para construir formularios del panel administrativo. Es un dominio de lectura que compone datos pertenecientes a otros dominios.
|
||||
|
||||
## Formularios disponibles
|
||||
|
||||
- `EventFormService`: devuelve redes sociales disponibles y las URL configuradas para el tenant.
|
||||
- `SaleFormService`: expone los estados admitidos para compras con sus etiquetas de presentación.
|
||||
- `StaffFormService`: lista categorías raíz que pueden asignarse al personal del tenant.
|
||||
- `TicketFormService`: expone estados y opciones anidadas de categoría, producto y tipo para los tickets de Fiesta Fútbol Infantil.
|
||||
|
||||
Cada servicio tiene un controlador invocable y un `JsonResource` específico. `SocialMediaOptionResource` representa las opciones de redes sociales.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Bajo `/v1/adminapp/forms`, con `auth:sanctum` y `adminapp.tenant`:
|
||||
|
||||
- `GET /event`.
|
||||
- `GET /sale`.
|
||||
- `GET /staff`.
|
||||
- `GET /fiesta-futbol-infantil/ticket`: estados y jerarquía categoría → producto → tipo para filtros de tickets.
|
||||
- `GET /fiesta-futbol-infantil/merchandise`: opciones de color y talle del tenant para merchandising.
|
||||
|
||||
## Dependencias
|
||||
|
||||
Compone datos de `Tenant`, `Purchase` y `Catalog`. No debe duplicar reglas de negocio: las listas y estados canónicos siguen perteneciendo a sus dominios de origen.
|
||||
38
app/Shared/Forms/routes/adminapp.php
Normal file
38
app/Shared/Forms/routes/adminapp.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use App\Domains\Forms\Controllers\AdminApp\EntryFormController;
|
||||
use App\Domains\Forms\Controllers\AdminApp\EventFormController;
|
||||
use App\Domains\Forms\Controllers\AdminApp\FoodFormController;
|
||||
use App\Domains\Forms\Controllers\AdminApp\MerchandiseFormController;
|
||||
use App\Domains\Forms\Controllers\AdminApp\SaleFormController;
|
||||
use App\Domains\Forms\Controllers\AdminApp\StaffFormController;
|
||||
use App\Domains\Forms\Controllers\AdminApp\TicketFilterFormController;
|
||||
use App\Domains\Forms\Controllers\AdminApp\TicketFormController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::prefix('v1/adminapp/forms')
|
||||
->middleware(['auth:sanctum', 'adminapp.tenant'])
|
||||
->group(function (): void {
|
||||
Route::get('event', EventFormController::class);
|
||||
Route::get('sale', SaleFormController::class);
|
||||
Route::get('staff', StaffFormController::class);
|
||||
Route::get('tickets-filter', TicketFilterFormController::class)
|
||||
->middleware('tenant.menu:adminapp.tickets')
|
||||
->name('adminapp.forms.tickets-filter');
|
||||
Route::get(
|
||||
'fiesta-futbol-infantil/ticket',
|
||||
TicketFormController::class
|
||||
);
|
||||
Route::get(
|
||||
'fiesta-futbol-infantil/entry',
|
||||
EntryFormController::class
|
||||
);
|
||||
Route::get(
|
||||
'fiesta-futbol-infantil/merchandise',
|
||||
MerchandiseFormController::class
|
||||
);
|
||||
Route::get(
|
||||
'fiesta-futbol-infantil/food',
|
||||
FoodFormController::class
|
||||
);
|
||||
});
|
||||
3
app/Shared/Forms/routes/api.php
Normal file
3
app/Shared/Forms/routes/api.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
require __DIR__.'/adminapp.php';
|
||||
Reference in New Issue
Block a user