diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php index 0fc5ceedb..842dc4f15 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php @@ -685,7 +685,7 @@ public function addAttendeeTicket($summit_id, $attendee_id) if ($attendee->hasMember()) $payload['owner_id'] = $attendee->getMemberId(); - $ticket = $this->summit_order_service->createOrderSingleTicket($summit, $payload); + $ticket = $this->summit_order_service->createOfflineOrder($summit, $payload); return $this->created(SerializerRegistry::getInstance()->getSerializer($ticket)->serialize ( diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitOrdersApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitOrdersApiController.php index e7f8571f6..73ac353c7 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitOrdersApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitOrdersApiController.php @@ -764,23 +764,26 @@ public function updateTicket($summit_id, $order_id, $ticket_id){ */ public function addTicket($summit_id, $order_id){ try { + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->getResourceServerContext())->find($summit_id); if (is_null($summit)) return $this->error404(); $payload = $this->getJsonPayload([ 'ticket_type_id' => 'required|integer', + 'ticket_qty' => 'required|integer|min:1', + 'promo_code' => 'sometimes|string', 'badge_type_id' => 'nullable|integer', 'attendee_first_name' => 'nullable|string|max:255', 'attendee_last_name' => 'nullable|string|max:255', - 'attendee_email' => 'required|string|max:255|email', + 'attendee_email' => 'sometimes|string|max:255|email', 'attendee_company' => 'nullable|string|max:255', 'disclaimer_accepted' => 'nullable|boolean', 'extra_questions' => 'sometimes|extra_question_dto_array' ]); - $ticket = $this->service->addTicket($summit, intval($order_id), $payload); + $order = $this->service->addTickets($summit, intval($order_id), $payload); - return $this->created(SerializerRegistry::getInstance()->getSerializer($ticket)->serialize( Request::input('expand', ''))); + return $this->created(SerializerRegistry::getInstance()->getSerializer($order)->serialize(Request::input('expand', ''))); } catch(\InvalidArgumentException $ex){ @@ -1112,8 +1115,9 @@ function getAddValidationRules(array $payload): array 'owner_last_name' => 'required_without:owner_id|string|max:255', 'owner_email' => 'required_without:owner_id|string|max:255|email', 'owner_id' => 'required_without:owner_first_name,owner_last_name,owner_email|int', - 'ticket_type_id' => 'required|int', + 'ticket_type_id' => 'required|integer', 'promo_code' => 'sometimes|string', + 'ticket_qty' => 'required|integer|min:1', 'extra_questions' => 'sometimes|extra_question_dto_array', 'owner_company' => 'required|string|max:255', 'billing_address_1' => 'sometimes|string|max:255', @@ -1132,7 +1136,7 @@ function getAddValidationRules(array $payload): array */ protected function addChild(Summit $summit, array $payload): IEntity { - return $this->service->createOrderSingleTicket($summit, $payload); + return $this->service->createOfflineOrder($summit, $payload); } protected function getChildFromSummit(Summit $summit, $child_id): ?IEntity diff --git a/app/Jobs/Emails/Registration/RegisteredMemberOrderPaidMail.php b/app/Jobs/Emails/Registration/RegisteredMemberOrderPaidMail.php index e5ec3ad68..12b885349 100644 --- a/app/Jobs/Emails/Registration/RegisteredMemberOrderPaidMail.php +++ b/app/Jobs/Emails/Registration/RegisteredMemberOrderPaidMail.php @@ -81,7 +81,7 @@ public function __construct(SummitOrder $order) 'ticket_type_name' => $ticket->getTicketType()->getName(), 'has_owner' => false, 'price' => FormatUtils::getNiceFloat($ticket->getFinalAmount()), - 'currency' => $ticket->getCurrencySymbol(), + 'currency' => $ticket->getCurrency(), 'currency_symbol' => $ticket->getCurrencySymbol(), 'need_details' => false, ]; diff --git a/app/Models/Foundation/Summit/Registration/SummitOrder.php b/app/Models/Foundation/Summit/Registration/SummitOrder.php index 246395d6c..8cdb5286e 100644 --- a/app/Models/Foundation/Summit/Registration/SummitOrder.php +++ b/app/Models/Foundation/Summit/Registration/SummitOrder.php @@ -454,10 +454,13 @@ public function getQRCode(): ?string */ public function getOwnerFirstName(): ?string { + $res = ''; if($this->hasOwner()){ - return $this->owner->getFirstName(); + $res = $this->owner->getFirstName(); } - return $this->owner_first_name; + if(empty($res)) + $res = $this->owner_first_name; + return $res; } /** @@ -473,10 +476,13 @@ public function setOwnerFirstName(string $owner_first_name): void */ public function getOwnerSurname(): ?string { + $res = ''; if($this->hasOwner()){ - return $this->owner->getLastName(); + $res = $this->owner->getLastName(); } - return $this->owner_surname; + if(empty($res)) + $res = $this->owner_surname; + return $res; } /** diff --git a/app/Models/Foundation/Summit/Summit.php b/app/Models/Foundation/Summit/Summit.php index 9f0984167..c638e38c4 100644 --- a/app/Models/Foundation/Summit/Summit.php +++ b/app/Models/Foundation/Summit/Summit.php @@ -5065,13 +5065,12 @@ public function getSummitDaysWithEvents():array { $days = $this->getSummitDays(); $list = []; foreach ($days as $day){ - //Log::debug(sprintf("Summit::getSummitDaysWithEvents day %s", $day->format('Y-m-d H:i:s'))); + Log::debug(sprintf("Summit::getSummitDaysWithEvents day %s", $day->format('Y-m-d H:i:s'))); $begin = clone($day); $begin = $begin->setTime(0,0,0)->setTimezone(new \DateTimeZone('UTC')); $end = clone($day); $end = $end->setTime(23,59,59)->setTimezone(new \DateTimeZone('UTC')); - - //Log::debug(sprintf("Summit::getSummitDaysWithEvents UTC begin date %s UTC end date %s", $begin->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); + Log::debug(sprintf("Summit::getSummitDaysWithEvents UTC begin date %s UTC end date %s", $begin->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); $count = 0; try { $sql = <<fetchAll(\PDO::FETCH_COLUMN); $count = count($res) > 0 ? $res[0] : 0; + Log::debug(sprintf("Summit::getSummitDaysWithEvents summit %s begin %s end %s count %s", $this->id, $begin->format("Y-m-d H:i:s"), $end->format('Y-m-d H:i:s'), $count)); } catch (\Exception $ex) { + Log::debug($ex); $count = 0; } if($count > 0){ - //Log::debug(sprintf("Summit::getSummitDaysWithEvents UTC begin date %s UTC end date %s count %s", $begin->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'), $count)); $list[] = $day; } } diff --git a/app/Services/Model/ISummitOrderService.php b/app/Services/Model/ISummitOrderService.php index 7e30fc1eb..205ef9bec 100644 --- a/app/Services/Model/ISummitOrderService.php +++ b/app/Services/Model/ISummitOrderService.php @@ -100,7 +100,7 @@ public function ownerAssignTicket(Member $current_user, int $order_id, int $tick * @throws EntityNotFoundException * @throws ValidationException */ - public function addTicket(Summit $summit, int $order_id, array $payload):SummitAttendeeTicket; + public function addTickets(Summit $summit, int $order_id, array $payload):SummitOrder; /** * @param Summit $summit @@ -213,7 +213,7 @@ public function updateTicketsByOrderHash(string $order_hash, array $payload): Su * @throws EntityNotFoundException * @throws ValidationException */ - public function createOrderSingleTicket(Summit $summit, array $payload):SummitOrder; + public function createOfflineOrder(Summit $summit, array $payload):SummitOrder; /** * @param Summit $summit diff --git a/app/Services/Model/Imp/SummitOrderService.php b/app/Services/Model/Imp/SummitOrderService.php index f6126f6fa..91c4e2590 100644 --- a/app/Services/Model/Imp/SummitOrderService.php +++ b/app/Services/Model/Imp/SummitOrderService.php @@ -1772,31 +1772,31 @@ public function getTicketByHash(string $hash): SummitAttendeeTicket * @throws EntityNotFoundException * @throws ValidationException */ - public function createOrderSingleTicket(Summit $summit, array $payload): SummitOrder + public function createOfflineOrder(Summit $summit, array $payload): SummitOrder { $order = $this->tx_service->transaction(function () use ($summit, $payload) { - Log::debug(sprintf("SummitOrderService::createOrderSingleTicket summit %s payload %s", $summit->getId(), json_encode($payload))); + Log::debug(sprintf("SummitOrderService::createOfflineOrder summit %s payload %s", $summit->getId(), json_encode($payload))); // lock ticket type stock $owner = null; $ticket_type = $this->ticket_type_repository->getByIdExclusiveLock(intval($payload['ticket_type_id'])); if (is_null($ticket_type) || !$ticket_type instanceof SummitTicketType || $ticket_type->getSummitId() != $summit->getId()) { - Log::warning("SummitOrderService::createOrderSingleTicket ticket type not found"); + Log::warning("SummitOrderService::createOfflineOrder ticket type not found"); throw new EntityNotFoundException("ticket type not found"); } // check owner if (isset($payload['owner_id'])) { - Log::debug(sprintf("SummitOrderService::createOrderSingleTicket trying to get member by id %s", $payload['owner_id'])); + Log::debug(sprintf("SummitOrderService::createOfflineOrder trying to get member by id %s", $payload['owner_id'])); $owner = $this->member_repository->getById(intval($payload['owner_id'])); if (is_null($owner)) { - Log::warning("SummitOrderService::createOrderSingleTicket owner not found"); + Log::warning("SummitOrderService::createOfflineOrder owner not found"); throw new EntityNotFoundException("owner not found"); } } if (is_null($owner) && isset($payload['owner_email'])) { - Log::debug(sprintf("SummitOrderService::createOrderSingleTicket trying to get member by email %s", $payload['owner_email'])); + Log::debug(sprintf("SummitOrderService::createOfflineOrder trying to get member by email %s", $payload['owner_email'])); // if not try by email $owner = $this->member_repository->getByEmail(trim($payload['owner_email'])); } @@ -1805,7 +1805,7 @@ public function createOrderSingleTicket(Summit $summit, array $payload): SummitO $attendee = !is_null($owner) ? $summit->getAttendeeByMember($owner) : null; if (is_null($attendee) && isset($payload['owner_email'])) { - Log::debug(sprintf("SummitOrderService::createOrderSingleTicket trying to get attendee by email %s", $payload['owner_email'])); + Log::debug(sprintf("SummitOrderService::createOfflineOrder trying to get attendee by email %s", $payload['owner_email'])); $attendee = $this->attendee_repository->getBySummitAndEmail($summit, trim($payload['owner_email'])); } @@ -1815,19 +1815,19 @@ public function createOrderSingleTicket(Summit $summit, array $payload): SummitO if (is_null($attendee)) { // create it - Log::debug(sprintf("SummitOrderService::createOrderSingleTicket attendee is null")); + Log::debug(sprintf("SummitOrderService::createOfflineOrder attendee is null")); //first name $first_name = isset($payload['owner_first_name']) ? trim($payload['owner_first_name']) : null; if (empty($first_name) && !is_null($owner) && !is_null($owner->getFirstName())) $first_name = $owner->getFirstName(); if (empty($first_name)) { - Log::warning("SummitOrderService::createOrderSingleTicket owner firstname is null"); + Log::warning("SummitOrderService::createOfflineOrder owner firstname is null"); throw new ValidationException("you must provide an owner_first_name or a valid owner_id"); } // surname $surname = isset($payload['owner_last_name']) ? trim($payload['owner_last_name']) : null; if (empty($surname) && !is_null($owner) && !is_null($owner->getLastName())) $surname = $owner->getLastName(); if (empty($surname)) { - Log::warning("SummitOrderService::createOrderSingleTicket owner surname is null"); + Log::warning("SummitOrderService::createOfflineOrder owner surname is null"); throw new ValidationException("you must provide an owner_last_name or a valid owner_id"); } // mail @@ -1837,7 +1837,7 @@ public function createOrderSingleTicket(Summit $summit, array $payload): SummitO if (empty($email) && !is_null($owner)) $email = $owner->getEmail(); if (empty($email)) { - Log::warning("SummitOrderService::createOrderSingleTicket owner email is null"); + Log::warning("SummitOrderService::createOfflineOrder owner email is null"); throw new ValidationException("you must provide an owner_email or a valid owner_id"); } @@ -1859,53 +1859,21 @@ public function createOrderSingleTicket(Summit $summit, array $payload): SummitO $order->generateNumber(); } while (1); - Log::debug(sprintf("SummitOrderService::createOrderSingleTicket order number %s", $order->getNumber())); - $default_badge_type = $summit->getDefaultBadgeType(); - - if (is_null($default_badge_type)) { - Log::warning("SummitOrderService::createOrderSingleTicket default_badge_type is null"); - throw new ValidationException(sprintf("summit %s does not has a default badge type", $summit->getId())); - } + Log::debug(sprintf("SummitOrderService::createOfflineOrder order number %s", $order->getNumber())); $order->setPaymentMethodOffline(); - // create ticket + // create tickets + $ticket_qty = isset($payload["ticket_qty"]) ? intval($payload["ticket_qty"]) : 1; - $ticket = new SummitAttendeeTicket(); - $ticket->setOrder($order); - $ticket->setOwner($attendee); - $ticket->setTicketType($ticket_type); - $ticket->generateNumber(); - $ticket_type->sell(1); + Log::debug(sprintf("SummitOrderService::createOfflineOrder ticket_qty %s", $ticket_qty)); - do { - if (!$this->ticket_repository->existNumber($ticket->getNumber())) - break; - $ticket->generateNumber(); - } while (1); + $order = $this->createTicketsForOrder($order, $ticket_type, $ticket_qty , $payload['promo_code'] ?? null, $attendee); - Log::debug(sprintf("SummitOrderService::createOrderSingleTicket ticket number %s", $ticket->getNumber())); - - if (!$ticket->hasBadge()) { - $ticket->setBadge(SummitBadgeType::buildBadgeFromType($default_badge_type)); - } - - // promo code usage - $promo_code = isset($payload['promo_code']) ? $this->promo_code_repository->getByValueExclusiveLock($summit, trim($payload['promo_code'])) : null; - if (!is_null($promo_code)) { - $promo_code->addUsage(1); - $promo_code->applyTo($ticket); - } - - $ticket->applyTaxes($summit->getTaxTypes()->toArray()); - $order->addTicket($ticket); if (!is_null($owner)) { $owner->addSummitRegistrationOrder($order); } - $ticket->generateHash(); - $ticket->generateQRCode(); - $summit->addAttendee($attendee); $summit->addOrder($order); $order->generateHash(); @@ -1916,7 +1884,81 @@ public function createOrderSingleTicket(Summit $summit, array $payload): SummitO return $this->tx_service->transaction(function () use ($order) { $order->setPaid(); - Log::debug(sprintf("SummitOrderService::createOrderSingleTicket order number %s mark as paid", $order->getNumber())); + Log::debug(sprintf("SummitOrderService::createOfflineOrder order number %s mark as paid", $order->getNumber())); + return $order; + }); + } + + /** + * @param SummitOrder $order + * @param SummitTicketType $ticket_type + * @param int $ticket_qty + * @param string|null $promo_code + * @param SummitAttendee|null $attendee + * @return SummitOrder + * @throws \Exception + */ + private function createTicketsForOrder + ( + SummitOrder $order, + SummitTicketType $ticket_type, + int $ticket_qty = 1, + ?string $promo_code = null, + ?SummitAttendee $attendee = null + ):SummitOrder{ + + return $this->tx_service->transaction(function () use ($order, $ticket_type, $ticket_qty, $promo_code, $attendee) { + + $summit = $order->getSummit(); + + $default_badge_type = $summit->getDefaultBadgeType(); + + if (is_null($default_badge_type)) { + Log::warning("SummitOrderService::createTicketsForOrder default_badge_type is null"); + throw new ValidationException(sprintf("summit %s does not has a default badge type", $summit->getId())); + } + + for ($i = 0; $i < $ticket_qty; $i++) { + + $ticket = new SummitAttendeeTicket(); + $ticket->setOrder($order); + + if ($ticket_qty == 1 && !is_null($attendee)) + $ticket->setOwner($attendee); + + $ticket->setTicketType($ticket_type); + $ticket->generateNumber(); + $ticket_type->sell(1); + + do { + if (!$this->ticket_repository->existNumber($ticket->getNumber())) + break; + $ticket->generateNumber(); + } while (1); + + Log::debug(sprintf("SummitOrderService::createTicketsForOrder ticket number %s", $ticket->getNumber())); + + if (!$ticket->hasBadge()) { + $ticket->setBadge(SummitBadgeType::buildBadgeFromType($default_badge_type)); + } + + // promo code usage + if (!empty($promo_code)) { + $pc = $this->promo_code_repository->getByValueExclusiveLock($summit, trim($promo_code)); + if(is_null($pc)){ + throw new EntityNotFoundException(sprintf("Promo code %s not found.", $promo_code)); + } + Log::debug(sprintf("SummitOrderService::createTicketsForOrder applying promo code %s", $pc->getCode())); + $pc->addUsage(1); + $pc->applyTo($ticket); + } + + $ticket->applyTaxes($summit->getTaxTypes()->toArray()); + $order->addTicket($ticket); + $ticket->generateHash(); + $ticket->generateQRCode(); + } + return $order; }); } @@ -2416,14 +2458,32 @@ public function createBadge(Summit $summit, $ticket_id, array $payload): SummitA * @param Summit $summit * @param int $order_id * @param array $payload - * @return SummitAttendeeTicket + * @return SummitOrder * @throws EntityNotFoundException * @throws ValidationException */ - public function addTicket(Summit $summit, int $order_id, array $payload): SummitAttendeeTicket + public function addTickets(Summit $summit, int $order_id, array $payload): SummitOrder { return $this->tx_service->transaction(function () use ($summit, $order_id, $payload) { + $order = $this->order_repository->getByIdExclusiveLock($order_id); + if (is_null($order) || !$order instanceof SummitOrder) + throw new EntityNotFoundException("order not found"); + + if($summit->getId() != $order->getSummitId()) + throw new EntityNotFoundException("order not found"); + + $ticket_type = $this->ticket_type_repository->getByIdExclusiveLock(intval($payload['ticket_type_id'])); + + if (is_null($ticket_type) || !$ticket_type instanceof SummitTicketType || $ticket_type->getSummitId() != $summit->getId()) { + Log::warning("SummitOrderService::addTicket ticket type not found"); + throw new EntityNotFoundException("ticket type not found"); + } + + $ticket_qty = isset($payload["ticket_qty"]) ? intval($payload["ticket_qty"]) : 1; + $order = $this->createTicketsForOrder($order, $ticket_type, $ticket_qty, $payload['promo_code'] ?? null); + + return $order; }); } @@ -2897,7 +2957,7 @@ public function processTicketData(int $summit_id, string $filename) return; } - $order = $this->createOrderSingleTicket($summit, + $order = $this->createOfflineOrder($summit, [ 'ticket_type_id' => $ticket_type->getId(), 'attendee' => $attendee,