Files
shopit-back/app/Domains/Ticket/Services/ResolvedTicketValidity.php
ncoronel 573d4fe5e6 Refactor ticket validity handling and improve tests
- Updated tests for Accommodation, Entry, Food, Merchandise, and Sale controllers to use soft deletes for variants and ensure proper inventory counts.
- Enhanced ticket generation logic to resolve validity from soft-deleted catalog sources.
- Introduced a new TicketValidityResolver service to manage ticket validity based on event dates and variant definitions.
- Removed unnecessary database assertions and improved the clarity of validity checks in tests.
- Added comprehensive tests for the new TicketValidityResolver service, ensuring correct handling of event dates and multi-select options.
- Cleaned up unused code and assertions in existing tests for better maintainability.
2026-08-14 09:00:51 -03:00

78 lines
2.6 KiB
PHP

<?php
namespace App\Domains\Ticket\Services;
use Carbon\CarbonInterface;
use Illuminate\Support\Collection;
/**
* Resultado completo de resolver la vigencia de un ticket.
*
* Cada ResolvedValidityGroup contiene condiciones AND. Entre los grupos se
* aplica OR, por lo que alcanza con que uno de ellos esté activo.
*/
final readonly class ResolvedTicketValidity
{
/** @param Collection<int, ResolvedValidityGroup> $groups */
public function __construct(
public Collection $groups,
public bool $isResolvable = true,
public bool $isUnrestricted = false,
) {}
/** No existe ninguna restricción temporal configurada. */
public static function unrestricted(): self
{
return new self(collect(), isUnrestricted: true);
}
/** La configuración fuente está incompleta o es inconsistente. */
public static function unresolvable(): self
{
return new self(collect(), isResolvable: false);
}
/** Es válido cuando no tiene restricciones o algún grupo OR está activo. */
public function isValid(?CarbonInterface $at = null): bool
{
if (! $this->isResolvable) {
return false;
}
return $this->isUnrestricted || $this->groups->contains(
fn (ResolvedValidityGroup $group): bool => $group->isValid($at)
);
}
/** Sólo está vencido cuando todos los grupos OR ya vencieron. */
public function isExpired(?CarbonInterface $at = null): bool
{
return $this->isResolvable
&& ! $this->isUnrestricted
&& $this->groups->isNotEmpty()
&& $this->groups->every(
fn (ResolvedValidityGroup $group): bool => $group->isExpired($at)
);
}
/** Inicio más temprano de todas las alternativas, usado como resumen. */
public function effectiveStartsAt(?CarbonInterface $at = null): ?CarbonInterface
{
return $this->groups
->map(fn (ResolvedValidityGroup $group): ?CarbonInterface => $group->effectiveStartsAt($at))
->filter()
->sortBy(fn (CarbonInterface $startsAt): int => $startsAt->getTimestamp())
->first();
}
/** Vencimiento más tardío de todas las alternativas, usado como resumen. */
public function effectiveExpiresAt(?CarbonInterface $at = null): ?CarbonInterface
{
return $this->groups
->map(fn (ResolvedValidityGroup $group): ?CarbonInterface => $group->effectiveExpiresAt($at))
->filter()
->sortByDesc(fn (CarbonInterface $expiresAt): int => $expiresAt->getTimestamp())
->first();
}
}