Skip to content

Commit 1c2188f

Browse files
authored
Merge pull request #237 from HiEventsDev/develop
2 parents d9e8c0d + 7db9dcd commit 1c2188f

File tree

37 files changed

+1092
-495
lines changed

37 files changed

+1092
-495
lines changed

backend/app/Constants.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ final class Constants
1111
*
1212
* @var int
1313
*/
14-
public const INFINITE = PHP_INT_MAX;
14+
public const INFINITE = 999999999;
1515
}

backend/app/DomainObjects/TicketDomainObject.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace HiEvents\DomainObjects;
44

55
use Carbon\Carbon;
6+
use HiEvents\Constants;
67
use HiEvents\DomainObjects\Enums\TicketType;
78
use HiEvents\DomainObjects\Interfaces\IsSortable;
89
use HiEvents\DomainObjects\SortingAndFiltering\AllowedSorts;
@@ -98,6 +99,13 @@ public function getQuantityAvailable(): int
9899
return 0;
99100
}
100101

102+
// This is to address a case where prices have an unlimited quantity available and the user has
103+
// enabled show_quantity_remaining.
104+
if ($this->getShowQuantityRemaining()
105+
&& $this->getTicketPrices()->first(fn(TicketPriceDomainObject $price) => $price->getQuantityAvailable() === null)) {
106+
return Constants::INFINITE;
107+
}
108+
101109
return $availableCount;
102110
}
103111

backend/app/Listeners/Order/SendOrderDetailsEmailListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use HiEvents\Events\OrderStatusChangedEvent;
66
use HiEvents\Jobs\Order\SendOrderDetailsEmailJob;
77

8-
readonly class SendOrderDetailsEmailListener
8+
class SendOrderDetailsEmailListener
99
{
1010
public function handle(OrderStatusChangedEvent $changedEvent): void
1111
{

backend/app/Providers/RouteServiceProvider.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ class RouteServiceProvider extends ServiceProvider
2525
public function boot(): void
2626
{
2727
RateLimiter::for('api', function (Request $request) {
28-
return Limit::perMinute(120)->by($request->user()?->id ?: $request->ip());
28+
return Limit::perMinute(config('app.api_rate_limit_per_minute'))
29+
->by($request->user()?->id ?: $request->ip());
2930
});
3031

3132
$this->routes(function () {

backend/app/Services/Domain/Order/OrderCreateRequestValidationService.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace HiEvents\Services\Domain\Order;
44

55
use Exception;
6+
use HiEvents\DomainObjects\CapacityAssignmentDomainObject;
67
use HiEvents\DomainObjects\Enums\TicketType;
78
use HiEvents\DomainObjects\EventDomainObject;
89
use HiEvents\DomainObjects\Generated\PromoCodeDomainObjectAbstract;
@@ -13,6 +14,7 @@
1314
use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface;
1415
use HiEvents\Repository\Interfaces\TicketRepositoryInterface;
1516
use HiEvents\Services\Domain\Ticket\AvailableTicketQuantitiesFetchService;
17+
use HiEvents\Services\Domain\Ticket\DTO\AvailableTicketQuantitiesDTO;
1618
use HiEvents\Services\Domain\Ticket\DTO\AvailableTicketQuantitiesResponseDTO;
1719
use Illuminate\Support\Collection;
1820
use Illuminate\Support\Facades\Validator;
@@ -186,7 +188,23 @@ private function validateTicketQuantity(int $ticketIndex, array $ticketAndQuanti
186188
{
187189
$totalQuantity = collect($ticketAndQuantities['quantities'])->sum('quantity');
188190
$maxPerOrder = (int)$ticket->getMaxPerOrder() ?: 100;
189-
$minPerOrder = (int)$ticket->getMinPerOrder() ?: 1;
191+
192+
$capacityMaximum = $this->availableTicketQuantities
193+
->ticketQuantities
194+
->where('ticket_id', $ticket->getId())
195+
->map(fn(AvailableTicketQuantitiesDTO $price) => $price->capacities)
196+
->flatten()
197+
->min(fn(CapacityAssignmentDomainObject $capacity) => $capacity->getCapacity());
198+
199+
$ticketAvailableQuantity = $this->availableTicketQuantities
200+
->ticketQuantities
201+
->first(fn(AvailableTicketQuantitiesDTO $price) => $price->ticket_id === $ticket->getId())
202+
->quantity_available;
203+
204+
# if there are fewer tickets available than the configured minimum, we allow less than the minimum to be purchased
205+
$minPerOrder = min((int)$ticket->getMinPerOrder() ?: 1,
206+
$capacityMaximum ?: $maxPerOrder,
207+
$ticketAvailableQuantity ?: $maxPerOrder);
190208

191209
$this->validateTicketPricesQuantity(
192210
quantities: $ticketAndQuantities['quantities'],

backend/app/Services/Handlers/Order/CompleteOrderHandler.php

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
use HiEvents\DomainObjects\Status\AttendeeStatus;
1616
use HiEvents\DomainObjects\Status\OrderPaymentStatus;
1717
use HiEvents\DomainObjects\Status\OrderStatus;
18+
use HiEvents\DomainObjects\TicketDomainObject;
1819
use HiEvents\DomainObjects\TicketPriceDomainObject;
1920
use HiEvents\Events\OrderStatusChangedEvent;
2021
use HiEvents\Exceptions\ResourceConflictException;
2122
use HiEvents\Helper\IdHelper;
23+
use HiEvents\Repository\Eloquent\Value\Relationship;
2224
use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface;
2325
use HiEvents\Repository\Interfaces\OrderRepositoryInterface;
2426
use HiEvents\Repository\Interfaces\QuestionAnswerRepositoryInterface;
@@ -37,14 +39,14 @@
3739
/**
3840
* @todo - Tidy this up
3941
*/
40-
readonly class CompleteOrderHandler
42+
class CompleteOrderHandler
4143
{
4244
public function __construct(
43-
private OrderRepositoryInterface $orderRepository,
44-
private AttendeeRepositoryInterface $attendeeRepository,
45-
private QuestionAnswerRepositoryInterface $questionAnswersRepository,
46-
private TicketQuantityUpdateService $ticketQuantityUpdateService,
47-
private TicketPriceRepositoryInterface $ticketPriceRepository,
45+
private readonly OrderRepositoryInterface $orderRepository,
46+
private readonly AttendeeRepositoryInterface $attendeeRepository,
47+
private readonly QuestionAnswerRepositoryInterface $questionAnswersRepository,
48+
private readonly TicketQuantityUpdateService $ticketQuantityUpdateService,
49+
private readonly TicketPriceRepositoryInterface $ticketPriceRepository,
4850
)
4951
{
5052
}
@@ -89,14 +91,14 @@ public function handle(string $orderShortId, CompleteOrderDTO $orderData): Order
8991
private function createAttendees(Collection $attendees, OrderDomainObject $order): void
9092
{
9193
$inserts = [];
92-
$publicIdIndex = 1;
9394

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

99100
$this->validateTicketPriceIdsMatchOrder($order, $ticketsPrices);
101+
$this->validateAttendees($order, $attendees);
100102

101103
foreach ($attendees as $attendee) {
102104
$ticketId = $ticketsPrices->first(
@@ -192,7 +194,7 @@ private function createAttendeeQuestions(
192194
private function validateOrder(OrderDomainObject $order): void
193195
{
194196
if ($order->getEmail() !== null) {
195-
throw new ResourceConflictException(__('This order is has already been processed'));
197+
throw new ResourceConflictException(__('This order has already been processed'));
196198
}
197199

198200
if (Carbon::createFromTimeString($order->getReservedUntil())->isPast()) {
@@ -210,7 +212,11 @@ private function validateOrder(OrderDomainObject $order): void
210212
private function getOrder(string $orderShortId): OrderDomainObject
211213
{
212214
$order = $this->orderRepository
213-
->loadRelation(OrderItemDomainObject::class)
215+
->loadRelation(
216+
new Relationship(
217+
domainObject: OrderItemDomainObject::class,
218+
nested: [new Relationship(TicketDomainObject::class, name: 'ticket')]
219+
))
214220
->findByShortId($orderShortId);
215221

216222
if ($order === null) {
@@ -258,4 +264,18 @@ private function validateTicketPriceIdsMatchOrder(OrderDomainObject $order, Coll
258264
throw new ResourceConflictException(__('There is an unexpected ticket price ID in the order'));
259265
}
260266
}
267+
268+
/**
269+
* @throws ResourceConflictException
270+
*/
271+
private function validateAttendees(OrderDomainObject $order, Collection $attendees): void
272+
{
273+
$orderAttendeeCount = $order->getOrderItems()->sum(fn(OrderItemDomainObject $orderItem) => $orderItem->getQuantity());
274+
275+
if ($orderAttendeeCount !== $attendees->count()) {
276+
throw new ResourceConflictException(
277+
__('The number of attendees does not match the number of tickets in the order')
278+
);
279+
}
280+
}
261281
}

backend/config/app.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
'saas_mode_enabled' => env('APP_SAAS_MODE_ENABLED', false),
1515
'saas_stripe_application_fee_percent' => env('APP_SAAS_STRIPE_APPLICATION_FEE_PERCENT', 1.5),
1616
'disable_registration' => env('APP_DISABLE_REGISTRATION', false),
17+
'api_rate_limit_per_minute' => env('APP_API_RATE_LIMIT_PER_MINUTE', 180),
1718

1819
/**
1920
* The number of page views to batch before updating the database

backend/phpunit.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<env name="APP_ENV" value="testing"/>
2222
<env name="BCRYPT_ROUNDS" value="4"/>
2323
<env name="CACHE_DRIVER" value="array"/>
24-
<env name="DB_DATABASE" value="testing"/>
24+
<!-- <env name="DB_DATABASE" value="testing"/>-->
2525
<env name="MAIL_MAILER" value="array"/>
2626
<env name="QUEUE_CONNECTION" value="sync"/>
2727
<env name="SESSION_DRIVER" value="array"/>

0 commit comments

Comments
 (0)