status = IOrderConstants::ReservedStatus; $this->currency = AllowedCurrencies::USD; $this->applied_taxes = new ArrayCollection(); $this->former_hashes = new ArrayCollection(); $this->raw_cost = 0.0; $this->discount = 0.0; $this->refunded_amount = 0.0; $this->is_active = true; } /** * @return string */ public function getHash(): string { return $this->hash; } /** * @return \DateTime */ public function getHashCreationDate(): \DateTime { return $this->hash_creation_date; } /** * @return string */ public function getNumber(): ?string { return $this->number; } /** * @param string $number */ public function setNumber(string $number): void { $this->number = $number; } /** * @return string */ public function getStatus(): string { return $this->status; } /** * @param string $status */ public function setStatus(string $status): void { $this->status = $status; } /** * @return float */ public function getRawCost(): ?float { return $this->raw_cost; } /** * @param float $raw_cost */ public function setRawCost(float $raw_cost): void { $this->raw_cost = $raw_cost; } /** * @return float */ public function getRefundedAmount(): ?float { return $this->refunded_amount; } /** * @param float $refunded_amount */ public function setRefundedAmount(float $refunded_amount): void { $this->refunded_amount = $refunded_amount; } /** * @return string */ public function getCurrency(): ?string { return $this->currency; } /** * @param string $currency */ public function setCurrency(string $currency): void { $this->currency = $currency; } /** * @return string */ public function getQRCode(): ?string { return $this->qr_code; } public function generateHash():void { $token = $this->number; if (!is_null($this->order)) { $token .= $this->order->getHash(); } $salt = random_bytes(16); if(!empty($this->hash)){ $former_hash = new SummitAttendeeTicketFormerHash($this->hash, $this); $this->former_hashes->add($former_hash); } $this->hash = hash('sha256', $token.$salt.time()); $this->hash_creation_date = new \DateTime('now', new \DateTimeZone('UTC')); } /** * @throws ValidationException */ public function sendPublicEditEmail(){ if (!$this->isPaid()) throw new ValidationException("ticket is not paid"); if (!$this->hasOwner()) throw new ValidationException("ticket must have an assigned owner"); $this->generateQRCode(); $this->generateHash(); SummitAttendeeTicketRegenerateHashEmail::dispatch($this); } /** * @return bool */ public function hasOrder() { return $this->getOrderId() > 0; } /** * @return int */ public function getOrderId() { try { return is_null($this->order) ? 0 : $this->order->getId(); } catch (\Exception $ex) { return 0; } } /** * @return SummitOrder */ public function getOrder(): SummitOrder { return $this->order; } /** * @param SummitOrder $order */ public function setOrder(SummitOrder $order): void { $this->order = $order; } /** * @return bool * @throws \Exception */ public function canPubliclyEdit(): bool { if (empty($this->hash) || is_null($this->hash_creation_date)) return false; $ttl_minutes = Config::get("registration.ticket_public_edit_ttl", 30); $eol = new \DateTime('now', new \DateTimeZone('UTC')); $eol->sub(new \DateInterval('PT' . $ttl_minutes . 'M')); if ($this->hash_creation_date <= $eol) return false; return true; } /** * @return string */ public function generateNumber(): string { $summit = $this->getOrder()->getSummit(); $this->number = strtoupper(str_replace(".", "", uniqid($summit->getTicketQRPrefix().'_', true))); return $this->number; } /** * @return mixed */ public function getChangedDate() { return $this->changed_date; } /** * @param mixed $changed_date */ public function setChangedDate($changed_date) { $this->changed_date = $changed_date; } /** * @return string */ public function getExternalOrderId():?string { return $this->external_order_id; } /** * @param string $external_order_id */ public function setExternalOrderId($external_order_id) { $this->external_order_id = $external_order_id; } /** * @return string */ public function getExternalAttendeeId():?string { return $this->external_attendee_id; } /** * @param string $external_attendee_id */ public function setExternalAttendeeId($external_attendee_id) { $this->external_attendee_id = $external_attendee_id; } /** * @return \DateTime */ public function getBoughtDate() { return $this->bought_date; } /** * @param \DateTime $bought_date */ public function setBoughtDate($bought_date) { $this->bought_date = $bought_date; } /** * @return SummitTicketType */ public function getTicketType() { return $this->ticket_type; } /** * @param $ticket_type * @return $this */ public function setTicketType($ticket_type) { $this->ticket_type = $ticket_type; $this->raw_cost = $this->ticket_type->getCost(); $this->currency = $this->ticket_type->getCurrency(); return $this->ticket_type->applyTo($this); } /** * @return bool */ public function hasTicketType() { return $this->getTicketTypeId() > 0; } /** * @return int */ public function getTicketTypeId() { try { return is_null($this->ticket_type) ? 0 : $this->ticket_type->getId(); } catch (\Exception $ex) { return 0; } } /** * @return SummitAttendee|null */ public function getOwner():?SummitAttendee { return $this->owner; } /** * @param SummitAttendee $owner */ public function setOwner($owner) { $this->owner = $owner; } /** * @return bool */ public function hasOwner() { return $this->getOwnerId() > 0; } /** * @return int */ public function getOwnerId() { try { return is_null($this->owner) ? 0 : $this->owner->getId(); } catch (\Exception $ex) { return 0; } } /** * @return bool */ public function isPaid():bool { return $this->status == IOrderConstants::PaidStatus; } /** * @return bool */ public function isCancelled():bool { return $this->status == IOrderConstants::CancelledStatus; } use QRGeneratorTrait; /** * @return string */ public function generateQRCode(): string { if(is_null($this->order)){ throw new ValidationException("ticket has not order set"); } $this->qr_code = $this->generateQRFromFields([ $this->order->getSummit()->getTicketQRPrefix(), $this->number ]); return $this->qr_code; } /** * @return SummitAttendeeBadge */ public function getBadge(): SummitAttendeeBadge { return $this->badge; } /** * @param SummitAttendeeBadge $badge */ public function setBadge(SummitAttendeeBadge $badge): void { $this->badge = $badge; $badge->setTicket($this); } /** * @return bool */ public function hasBadge() { return $this->getBadgeId() > 0 || !is_null($this->badge); } /** * @return int */ public function getBadgeId() { try { return is_null($this->badge) ? 0 : $this->badge->getId(); } catch (\Exception $ex) { return 0; } } public function setPaid($set_bought_date = true) { $this->status = IOrderConstants::PaidStatus; if($set_bought_date) $this->bought_date = new \DateTime('now', new \DateTimeZone('UTC')); } public function setCancelled() { if ($this->status == IOrderConstants::PaidStatus) return; $this->status = IOrderConstants::CancelledStatus; } function cancelRefundRequest():void { if(!$this->isRefundRequested()) throw new ValidationException(sprintf("You can not cancel any refund on this ticket")); $this->status = IOrderConstants::PaidStatus; } public function setRefunded() { $this->status = IOrderConstants::RefundedStatus; } public function setRefundRequests() { $this->status = IOrderConstants::RefundRequestedStatus; } /** * @return bool */ public function canRefund():bool{ $validStatuses = [IOrderConstants::RefundRequestedStatus, IOrderConstants::PaidStatus]; if(!in_array($this->status, $validStatuses)){ return false; } if($this->isFree()){ return false; } return true; } /** * @param float $amount * @throws ValidationException */ public function refund(float $amount) { if (!$this->canRefund()) throw new ValidationException ( sprintf ( "can not request a refund on a %s ticket", $this->status ) ); $this->status = IOrderConstants::RefundedStatus; $this->refunded_amount = $amount; $tickets_to_return = []; $promo_codes_to_return = []; if(!isset($tickets_to_return[$this->getTicketTypeId()])) $tickets_to_return[$this->getTicketTypeId()] = 0; $tickets_to_return[$this->getTicketTypeId()] += 1; if($this->hasPromoCode()){ if(!isset($promo_codes_to_return[$this->getPromoCode()->getCode()])) $promo_codes_to_return[$this->getPromoCode()->getCode()] = 0; $promo_codes_to_return[$this->getPromoCode()->getCode()] +=1; } Event::dispatch(new SummitAttendeeTicketRefundAccepted($this->getId(), $tickets_to_return, $promo_codes_to_return)); } /** * @return bool */ public function hasPromoCode(): bool { return $this->getPromoCodeId() > 0; } /** * @return int */ public function getPromoCodeId(): int { try { return is_null($this->promo_code) ? 0 : $this->promo_code->getId(); } catch(\Exception $ex){ return 0; } } /** * @return SummitRegistrationPromoCode */ public function getPromoCode(): ?SummitRegistrationPromoCode { return $this->promo_code; } /** * @param SummitRegistrationPromoCode $promo_code */ public function setPromoCode(SummitRegistrationPromoCode $promo_code): void { $this->promo_code = $promo_code; } /** * @return bool */ public function isRefundRequested():bool{ return $this->status == IOrderConstants::RefundRequestedStatus; } /** * @return bool */ public function isRefunded():bool{ return $this->status == IOrderConstants::RefundedStatus; } /** * @return bool */ public function isBadgePrinted():bool{ if($this->hasBadge()){ $badge = $this->getBadge(); return $badge->isPrinted(); } return false; } /** * @param bool $refund_entire_order * @throws ValidationException */ public function requestRefund($refund_entire_order = false): void { if ($this->status != IOrderConstants::PaidStatus) throw new ValidationException(sprintf( "you can not request a refund for this ticket %s ( invalid status %s)", $this->number, $this->status)); $summit = $this->getOrder()->getSummit(); $begin_date = $summit->getBeginDate(); if(is_null($begin_date)) return; if($this->isBadgePrinted()) throw new ValidationException(sprintf( "you can not request a refund for this ticket %s ( badge already printed)", $this->number)); $now = new \DateTime('now', new \DateTimeZone('UTC')); if($now > $begin_date){ Log::debug("SummitAttendeeTicket::requestRefund: now is greater than Summit.BeginDate"); throw new ValidationException("you can not request a refund after summit started"); } $interval = $begin_date->diff($now); $days_before_event_starts = intval($interval->format('%a')); Log::debug(sprintf("SummitAttendeeTicket::requestRefund: days_before_event_starts %s", $days_before_event_starts)); $this->status = IOrderConstants::RefundRequestedStatus; if (!$refund_entire_order) Event::dispatch(new RequestedSummitAttendeeTicketRefund($this->getId(), $days_before_event_starts)); } /** * @param array $taxes */ public function applyTaxes(array $taxes) { $amount = $this->raw_cost; $amount -= $this->discount; foreach ($taxes as $tax) { if (!$tax instanceof SummitTaxType) continue; if (!$tax->mustApplyTo($this->ticket_type)) continue; $ticketTax = new SummitAttendeeTicketTax(); $ticketTax->setTicket($this); $ticketTax->setTax($tax); $ticketTax->setAmount(($amount * $tax->getRate()) / 100.00); $this->applied_taxes->add($ticketTax); } } /** * @return float */ public function getFinalAmount(): float { $amount = $this->raw_cost; $amount -= $this->discount; foreach ($this->applied_taxes as $tax) { $amount += $tax->getAmount(); } return $amount; } /** * @return bool */ public function isFree():bool { return $this->getFinalAmount() == 0; } /** * @return float */ public function getTaxesAmount(): float { $amount = 0.0; foreach ($this->getAppliedTaxes() as $appliedTax) { $amount += $appliedTax->getAmount(); } return $amount; } /** * @return SummitAttendeeTicketTax[] */ public function getAppliedTaxes() { return $this->applied_taxes; } /** * @return float */ public function getDiscount(): float { return $this->discount; } /** * @param float $amount * @return $this */ public function setDiscount(float $amount) { $this->discount = $amount > $this->raw_cost ? $this->raw_cost: $amount; return $this; } public function clearOwner() { $this->owner = null; } /** * @return string */ public function getOwnerFullName():?string{ if(is_null($this->owner)) null; return $this->owner->getFullName(); } /** * @return string */ public function getOwnerFirstName():?string{ if(is_null($this->owner)) return null; return $this->owner->getFirstName(); } /** * @return string */ public function getOwnerSurname():?string{ if(is_null($this->owner)) return null; return $this->owner->getSurname(); } /** * @return string */ public function getOwnerCompany():?string{ if(is_null($this->owner)) return null; return $this->owner->getCompanyName(); } /** * @return string */ public function getOwnerEmail():?string{ try { return is_null($this->owner)? null : $this->owner->getEmail(); } catch (\Exception $ex){ return null; } } /** * @return int|null */ public function getBadgeTypeId():?int{ if(is_null($this->badge)) return 0; return $this->badge->getType()->getId(); } /** * @return null|string */ public function getBadgeTypeName():?string{ if(is_null($this->badge)) return null; return $this->badge->getType()->getName(); } /** * @return null|string */ public function getTicketTypeName():?string{ if(is_null($this->ticket_type)) return null; return $this->ticket_type->getName(); } /** * @return array */ public function getBadgeFeaturesNames():array { $res = []; if(is_null($this->badge)) return []; foreach ($this->badge->getFeatures() as $feature){ $res[] = $feature->getName(); } foreach ($this->badge->getType()->getBadgeFeatures() as $feature){ if(in_array($feature->getName(),$res)) continue; $res[] = $feature->getName(); } return $res; } public function getBadgeFeatures():array{ $res = []; if(is_null($this->badge)) return []; foreach ($this->badge->getFeatures() as $feature){ $res[$feature->getId()] = $feature; } foreach ($this->badge->getType()->getBadgeFeatures() as $feature){ if(key_exists($feature->getId(), $res)) continue; $res[$feature->getId()] = $feature; } return $res; } /** * @return null|string */ public function getPromoCodeValue():?string{ return $this->hasPromoCode() ? $this->promo_code->getCode() : null; } /** * @param Member $member * @return bool */ public function canEditTicket(Member $member):bool{ if($member->isAdmin()) return true; // i am ticket owner if($this->hasOwner() && $this->owner->getEmail() == $member->getEmail()) return true; // i am order owner if($this->order->getOwnerEmail() == $member->getEmail()) return true; } /** * @return bool */ public function isActive(): bool { return $this->is_active; } public function activate(): void { $this->is_active = true; } public function deActivate(): void { $this->is_active = false; } }