Skip to content

Commit

Permalink
Merge pull request #237 from HiEventsDev/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
daveearley authored Sep 23, 2024
2 parents d9e8c0d + 7db9dcd commit 1c2188f
Show file tree
Hide file tree
Showing 37 changed files with 1,092 additions and 495 deletions.
2 changes: 1 addition & 1 deletion backend/app/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ final class Constants
*
* @var int
*/
public const INFINITE = PHP_INT_MAX;
public const INFINITE = 999999999;
}
8 changes: 8 additions & 0 deletions backend/app/DomainObjects/TicketDomainObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace HiEvents\DomainObjects;

use Carbon\Carbon;
use HiEvents\Constants;
use HiEvents\DomainObjects\Enums\TicketType;
use HiEvents\DomainObjects\Interfaces\IsSortable;
use HiEvents\DomainObjects\SortingAndFiltering\AllowedSorts;
Expand Down Expand Up @@ -98,6 +99,13 @@ public function getQuantityAvailable(): int
return 0;
}

// This is to address a case where prices have an unlimited quantity available and the user has
// enabled show_quantity_remaining.
if ($this->getShowQuantityRemaining()
&& $this->getTicketPrices()->first(fn(TicketPriceDomainObject $price) => $price->getQuantityAvailable() === null)) {
return Constants::INFINITE;
}

return $availableCount;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use HiEvents\Events\OrderStatusChangedEvent;
use HiEvents\Jobs\Order\SendOrderDetailsEmailJob;

readonly class SendOrderDetailsEmailListener
class SendOrderDetailsEmailListener
{
public function handle(OrderStatusChangedEvent $changedEvent): void
{
Expand Down
3 changes: 2 additions & 1 deletion backend/app/Providers/RouteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class RouteServiceProvider extends ServiceProvider
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(120)->by($request->user()?->id ?: $request->ip());
return Limit::perMinute(config('app.api_rate_limit_per_minute'))
->by($request->user()?->id ?: $request->ip());
});

$this->routes(function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace HiEvents\Services\Domain\Order;

use Exception;
use HiEvents\DomainObjects\CapacityAssignmentDomainObject;
use HiEvents\DomainObjects\Enums\TicketType;
use HiEvents\DomainObjects\EventDomainObject;
use HiEvents\DomainObjects\Generated\PromoCodeDomainObjectAbstract;
Expand All @@ -13,6 +14,7 @@
use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface;
use HiEvents\Repository\Interfaces\TicketRepositoryInterface;
use HiEvents\Services\Domain\Ticket\AvailableTicketQuantitiesFetchService;
use HiEvents\Services\Domain\Ticket\DTO\AvailableTicketQuantitiesDTO;
use HiEvents\Services\Domain\Ticket\DTO\AvailableTicketQuantitiesResponseDTO;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;
Expand Down Expand Up @@ -186,7 +188,23 @@ private function validateTicketQuantity(int $ticketIndex, array $ticketAndQuanti
{
$totalQuantity = collect($ticketAndQuantities['quantities'])->sum('quantity');
$maxPerOrder = (int)$ticket->getMaxPerOrder() ?: 100;
$minPerOrder = (int)$ticket->getMinPerOrder() ?: 1;

$capacityMaximum = $this->availableTicketQuantities
->ticketQuantities
->where('ticket_id', $ticket->getId())
->map(fn(AvailableTicketQuantitiesDTO $price) => $price->capacities)
->flatten()
->min(fn(CapacityAssignmentDomainObject $capacity) => $capacity->getCapacity());

$ticketAvailableQuantity = $this->availableTicketQuantities
->ticketQuantities
->first(fn(AvailableTicketQuantitiesDTO $price) => $price->ticket_id === $ticket->getId())
->quantity_available;

# if there are fewer tickets available than the configured minimum, we allow less than the minimum to be purchased
$minPerOrder = min((int)$ticket->getMinPerOrder() ?: 1,
$capacityMaximum ?: $maxPerOrder,
$ticketAvailableQuantity ?: $maxPerOrder);

$this->validateTicketPricesQuantity(
quantities: $ticketAndQuantities['quantities'],
Expand Down
38 changes: 29 additions & 9 deletions backend/app/Services/Handlers/Order/CompleteOrderHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
use HiEvents\DomainObjects\Status\AttendeeStatus;
use HiEvents\DomainObjects\Status\OrderPaymentStatus;
use HiEvents\DomainObjects\Status\OrderStatus;
use HiEvents\DomainObjects\TicketDomainObject;
use HiEvents\DomainObjects\TicketPriceDomainObject;
use HiEvents\Events\OrderStatusChangedEvent;
use HiEvents\Exceptions\ResourceConflictException;
use HiEvents\Helper\IdHelper;
use HiEvents\Repository\Eloquent\Value\Relationship;
use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface;
use HiEvents\Repository\Interfaces\OrderRepositoryInterface;
use HiEvents\Repository\Interfaces\QuestionAnswerRepositoryInterface;
Expand All @@ -37,14 +39,14 @@
/**
* @todo - Tidy this up
*/
readonly class CompleteOrderHandler
class CompleteOrderHandler
{
public function __construct(
private OrderRepositoryInterface $orderRepository,
private AttendeeRepositoryInterface $attendeeRepository,
private QuestionAnswerRepositoryInterface $questionAnswersRepository,
private TicketQuantityUpdateService $ticketQuantityUpdateService,
private TicketPriceRepositoryInterface $ticketPriceRepository,
private readonly OrderRepositoryInterface $orderRepository,
private readonly AttendeeRepositoryInterface $attendeeRepository,
private readonly QuestionAnswerRepositoryInterface $questionAnswersRepository,
private readonly TicketQuantityUpdateService $ticketQuantityUpdateService,
private readonly TicketPriceRepositoryInterface $ticketPriceRepository,
)
{
}
Expand Down Expand Up @@ -89,14 +91,14 @@ public function handle(string $orderShortId, CompleteOrderDTO $orderData): Order
private function createAttendees(Collection $attendees, OrderDomainObject $order): void
{
$inserts = [];
$publicIdIndex = 1;

$ticketsPrices = $this->ticketPriceRepository->findWhereIn(
field: TicketPriceDomainObjectAbstract::ID,
values: $attendees->pluck('ticket_price_id')->toArray(),
);

$this->validateTicketPriceIdsMatchOrder($order, $ticketsPrices);
$this->validateAttendees($order, $attendees);

foreach ($attendees as $attendee) {
$ticketId = $ticketsPrices->first(
Expand Down Expand Up @@ -192,7 +194,7 @@ private function createAttendeeQuestions(
private function validateOrder(OrderDomainObject $order): void
{
if ($order->getEmail() !== null) {
throw new ResourceConflictException(__('This order is has already been processed'));
throw new ResourceConflictException(__('This order has already been processed'));
}

if (Carbon::createFromTimeString($order->getReservedUntil())->isPast()) {
Expand All @@ -210,7 +212,11 @@ private function validateOrder(OrderDomainObject $order): void
private function getOrder(string $orderShortId): OrderDomainObject
{
$order = $this->orderRepository
->loadRelation(OrderItemDomainObject::class)
->loadRelation(
new Relationship(
domainObject: OrderItemDomainObject::class,
nested: [new Relationship(TicketDomainObject::class, name: 'ticket')]
))
->findByShortId($orderShortId);

if ($order === null) {
Expand Down Expand Up @@ -258,4 +264,18 @@ private function validateTicketPriceIdsMatchOrder(OrderDomainObject $order, Coll
throw new ResourceConflictException(__('There is an unexpected ticket price ID in the order'));
}
}

/**
* @throws ResourceConflictException
*/
private function validateAttendees(OrderDomainObject $order, Collection $attendees): void
{
$orderAttendeeCount = $order->getOrderItems()->sum(fn(OrderItemDomainObject $orderItem) => $orderItem->getQuantity());

if ($orderAttendeeCount !== $attendees->count()) {
throw new ResourceConflictException(
__('The number of attendees does not match the number of tickets in the order')
);
}
}
}
1 change: 1 addition & 0 deletions backend/config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'saas_mode_enabled' => env('APP_SAAS_MODE_ENABLED', false),
'saas_stripe_application_fee_percent' => env('APP_SAAS_STRIPE_APPLICATION_FEE_PERCENT', 1.5),
'disable_registration' => env('APP_DISABLE_REGISTRATION', false),
'api_rate_limit_per_minute' => env('APP_API_RATE_LIMIT_PER_MINUTE', 180),

/**
* The number of page views to batch before updating the database
Expand Down
2 changes: 1 addition & 1 deletion backend/phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="DB_DATABASE" value="testing"/>
<!-- <env name="DB_DATABASE" value="testing"/>-->
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
Expand Down
Loading

0 comments on commit 1c2188f

Please sign in to comment.