Source of file Reservation.php
Size: 14,335 Bytes - Last Modified: 2021-12-24T06:33:52+00:00
/var/www/docs.ssmods.com/process/src/src/Model/Reservation.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 | <?php namespace Broarm\EventTickets\Model; use Broarm\EventTickets\Extensions\TicketExtension; use Exception; use Mpdf\Mpdf; use Mpdf\Output\Destination; use SilverStripe\Assets\FileNameFilter; use SilverStripe\Assets\Folder; use SilverStripe\CMS\Model\SiteTree; use SilverStripe\Control\Email\Email; use SilverStripe\Core\Config\Config; use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\GridField\GridFieldConfig_RecordViewer; use SilverStripe\Forms\ReadonlyField; use SilverStripe\Omnipay\Extensions\Payable; use SilverStripe\Omnipay\GatewayInfo; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\HasManyList; use SilverStripe\ORM\ManyManyList; use SilverStripe\ORM\ValidationException; use SilverStripe\SiteConfig\SiteConfig; use SilverStripe\View\SSViewer; /** * Class Reservation * * @package Broarm\EventTickets * * @property string Status * @property string Title * @property float Subtotal * @property float Total * @property string Comments * @property string ReservationCode * @property string Gateway * @property boolean SentTickets * @property boolean SentReservation * @property boolean SentNotification * * @property int TicketPageID * @property int MainContactID * * @method TicketExtension|SiteTree TicketPage() * @method Attendee MainContact() * @method HasManyList Payments() * @method HasManyList Attendees() * @method ManyManyList PriceModifiers() */ class Reservation extends DataObject { private static $table_name = 'EventTickets_Reservation'; const STATUS_CART = 'CART'; const STATUS_PENDING = 'PENDING'; const STATUS_PAID = 'PAID'; const STATUS_CANCELED = 'CANCELED'; /** * Time to wait before deleting the discarded cart * Give a string that is parsable by strtotime * * @var string */ private static $delete_after = '+1 hour'; /** * The address to whom the ticket notifications are sent * By default the admin email is used * * @config * @var string */ private static $mail_sender; /** * The address from where the ticket mails are sent * By default the admin email is used * * @config * @var string */ private static $mail_receiver; /** * Send the receipt mail * For organisations that only do free events you can configure * this to hold back the receipt and only send the tickets * * @config * @var bool */ private static $send_receipt_mail = true; /** * Send the admin notification * * @config * @var bool */ private static $send_admin_notification = true; private static $db = array( 'Status' => 'Enum("CART,PENDING,PAID,CANCELED","CART")', 'Title' => 'Varchar', 'Subtotal' => 'Currency', 'Total' => 'Currency', 'Gateway' => 'Varchar', 'Comments' => 'Text', 'AgreeToTermsAndConditions' => 'Boolean', 'ReservationCode' => 'Varchar', 'SentReservation' => 'Boolean', 'SentNotification' => 'Boolean', ); private static $default_sort = 'Created DESC'; private static $has_one = array( 'TicketPage' => SiteTree::class, 'MainContact' => Attendee::class ); private static $has_many = array( 'Attendees' => Attendee::class . '.Reservation' ); private static $extensions = [ Payable::class ]; private static $belongs_many_many = array( 'PriceModifiers' => PriceModifier::class ); private static $indexes = array( 'ReservationCode' => [ 'type' => 'unique', 'columns' => ['ReservationCode'] ] ); private static $summary_fields = array( 'ReservationCode' => 'Reservation', 'Title' => 'Customer', 'Total.Nice' => 'Total', 'State' => 'Status', 'GatewayNice' => 'Payment method', 'Created.Nice' => 'Date' ); public function getCMSFields() { $fields = parent::getCMSFields(); $fields->removeByName(['Attendees', 'Payments', 'PriceModifiers', 'Subtotal', 'Gateway', 'SentTickets', 'SentReservation', 'SentNotification', 'TicketPageID']); $gridFieldConfig = GridFieldConfig_RecordViewer::create(); $fields->addFieldsToTab('Root.Main', array( ReadonlyField::create('ReservationCode', _t(__CLASS__ . '.Code', 'Code')), ReadonlyField::create('Created', _t(__CLASS__ . '.Created', 'Date')), DropdownField::create('Status', _t(__CLASS__ . '.Status', 'Status'), $this->getStates()), ReadonlyField::create('Title', _t(__CLASS__ . '.MainContact', 'Main contact')), ReadonlyField::create('GateWayNice', _t(__CLASS__ . '.Gateway', 'Gateway')), ReadonlyField::create('Total', _t(__CLASS__ . '.Total', 'Total')), ReadonlyField::create('Comments', _t(__CLASS__ . '.Comments', 'Comments')), CheckboxField::create('AgreeToTermsAndConditions', _t(__CLASS__ . '.AgreeToTermsAndConditions', 'Agreed to terms and conditions'))->performReadonlyTransformation(), GridField::create('Attendees', 'Attendees', $this->Attendees(), $gridFieldConfig), GridField::create('Payments', 'Payments', $this->Payments(), $gridFieldConfig), GridField::create('PriceModifiers', 'PriceModifiers', $this->PriceModifiers(), $gridFieldConfig) )); return $fields; } /** * Generate a reservation code if it does not yet exists */ public function onBeforeWrite() { // Set the title to the name of the reservation holder $this->Title = $this->getName(); // Create a validation code to be used for confirmation and in the barcode if ($this->exists() && empty($this->ReservationCode)) { $this->ReservationCode = $this->createReservationCode(); } parent::onBeforeWrite(); } /** * After deleting a reservation, delete the attendees and files */ public function onBeforeDelete() { // If a reservation is deleted remove the names from the guest list foreach ($this->Attendees() as $attendee) { /** @var Attendee $attendee */ if ($attendee->exists()) { $attendee->delete(); } } // Remove the folder if (($folder = Folder::get()->find('Name', $this->ReservationCode)) && $folder->exists() && $folder->isEmpty()) { $folder->delete(); } parent::onBeforeDelete(); } /** * Gets a nice unnamespaced name * * @return string */ public function singular_name() { $name = explode('\\', parent::singular_name()); return trim(end($name)); } /** * Returns the nice gateway title * * @return string */ public function getGatewayNice() { return GatewayInfo::niceTitle($this->Gateway); } /** * Check if the cart is still in cart state and the delete_after time period has been exceeded * * @return bool */ public function isDiscarded() { $deleteAfter = strtotime(self::config()->get('delete_after'), strtotime($this->Created)); return ($this->Status === 'CART') && (time() > $deleteAfter); } /** * Get the full name * * @return string */ public function getName() { /** @var Attendee $attendee */ if (($mainContact = $this->MainContact()) && $mainContact->exists() && $name = $mainContact->getName()) { return $name; } else { return 'new reservation'; } } /** * Return the translated state * * @return string */ public function getState() { if ($this->exists()) { return _t(__CLASS__ . ".{$this->Status}", $this->Status); } return null; } /** * Get a the translated map of available states * * @return array */ private function getStates() { return array_map(function ($state) { return _t(__CLASS__ . ".$state", $state); }, $this->dbObject('Status')->enumValues()); } /** * Get the total by querying the sum of attendee ticket prices * * @return float */ public function calculateTotal() { $ticket = DataObject::getSchema()->tableName(Ticket::class); $attendee = DataObject::getSchema()->tableName(Attendee::class); $total = $this->Subtotal = $this->Attendees()->leftJoin( $ticket, "`$attendee`.`TicketID` = `$ticket`.`ID`" )->sum('Price'); // Calculate any price modifications if added if ($this->PriceModifiers()->exists()) { foreach ($this->PriceModifiers() as $priceModifier) { $priceModifier->updateTotal($total, $this); } } return $this->Total = $total; } /** * Safely change to a state * todo check if state direction matches * * @param $state * * @return boolean */ public function changeState($state) { $availableStates = $this->dbObject('Status')->enumValues(); if (in_array($state, $availableStates)) { $this->Status = $state; return true; } else { user_error(_t(__CLASS__ . '.STATE_CHANGE_ERROR', 'Selected state is not available')); return false; } } /** * Complete the reservation * * @throws ValidationException */ public function complete() { $this->changeState('PAID'); $this->send(); $this->write(); $this->extend('onAfterComplete'); } /** * Set the main contact id * @param $id * * @throws ValidationException */ public function setMainContact($id) { $this->MainContactID = $id; $this->write(); } /** * Create a reservation code * * @return string */ public function createReservationCode() { return uniqid($this->ID); } /** * Creates a printable ticket for the attendee * * @return Mpdf * @throws \Mpdf\MpdfException */ public function createTicketFile() { // Set the template and parse the data $html = SSViewer::execute_template('Broarm\\EventTickets\\ReservationPrintable', $this); $pdf = new Mpdf(); $pdf->WriteHTML($html); return $pdf; } /** * Send the reservation mail * * @return bool * @throws \Mpdf\MpdfException */ public function sendReservation() { if (!self::config()->get('send_receipt_mail')) { return true; } // Get the mail sender or fallback to the admin email if (!($from = self::config()->get('mail_sender')) || empty($from)) { $from = Email::config()->get('admin_email'); } $eventName = SiteConfig::current_site_config()->getTitle(); if (($event = $this->TicketPage()) && $event->hasMethod('getEventTitle')) { $eventName = $event->getEventTitle(); } // Create the email with given template and reservation data $email = new Email(); $email->setSubject(_t( __CLASS__ .'.ReservationSubject', 'Your tickets for {event}', null, array( 'event' => $eventName ) )); $email->setFrom($from); $email->setTo($this->MainContact()->getEmail()); $email->setHTMLTemplate('Broarm\\EventTickets\\ReservationEmail'); $pdf = $this->createTicketFile(); $fileName = FileNameFilter::create()->filter("Tickets {$eventName}.pdf"); $email->addAttachmentFromData($pdf->Output($fileName, Destination::STRING_RETURN), $fileName, 'application/pdf'); $email->setData($this); $this->extend('updateReservationMail', $email); return $email->send(); } /** * Send a booking notification to the ticket mail sender or the site admin * * @return bool * @throws Exception */ public function sendNotification() { if (!self::config()->get('send_admin_notification')) { return true; } if (!($from = self::config()->get('mail_sender')) || empty($from)) { $from = Email::config()->get('admin_email'); } if (!($to = self::config()->get('mail_receiver')) || empty($to)) { $to = Email::config()->get('admin_email'); } $eventName = SiteConfig::current_site_config()->getTitle(); if (($event = $this->TicketPage()) && $event->hasMethod('getEventTitle')) { $eventName = $event->getEventTitle(); } $email = new Email(); $email->setSubject(_t( __CLASS__ . '.NotificationSubject', 'New reservation for {event} by {name}', null, [ 'event' => $eventName, 'name' => $this->getName() ] )); $email->setFrom($from); $email->setTo($to); $email->setHTMLTemplate('Broarm\\EventTickets\\NotificationMail'); $email->setData($this); $this->extend('updateNotificationMail', $email); return $email->send(); } /** * Send the reservation and notification * @throws Exception */ public function send() { $this->extend('onBeforeSend'); $this->SentReservation = (boolean)$this->sendReservation(); $this->SentNotification = (boolean)$this->sendNotification(); } public function canView($member = null) { return $this->TicketPage()->canView($member); } public function canEdit($member = null) { return $this->TicketPage()->canEdit($member); } public function canDelete($member = null) { return $this->TicketPage()->canDelete($member); } public function canCreate($member = null, $context = []) { return $this->TicketPage()->canCreate($member, $context); } } |