2 ]; public $appends = [ 'name', 'tagsList', 'tablesList', 'tablesSeatsList', 'isVip', 'isCustomerVip', 'shiftName', 'isPaid', 'isCardCaptured', 'isForCapturingCard', 'isPreauthConfirmed', 'paidAmountFormatted', 'customValues', 'cancelReason', 'emailPendingOpen', // para saber si se ha abierto o no 'emailConfirmedOpen', 'emailFinishedOpen', // Tracking 'trackingData' ]; protected $casts = [ 'id' => 'int', 'party' => 'int', 'children' => 'int', 'babies' => 'int', 'time' => 'int', 'duration' => 'int', 'shift' => 'int', 'custom_fields' => 'array', 'agree_receive_email' => 'int', 'agree_receive_sms' => 'int', 'is_recurring' => 'boolean', 'settings' => 'array' ]; public function restaurant() { return $this->belongsTo(Restaurant::class); } public function customer() { return $this->belongsTo(Customer::class); } public static function booted() { //ray('Calling Booking booted'); static::creating(function($booking){ //ray('Booted creating booking'); $booking->uuid = Str::uuid('bo'); $booking->token = Str::upper(Str::random(8)); }); static::saved(function($booking){ if ($booking->customer) { $booking->customer->calculateVisits(); } }); } public function tags() { return $this->belongsToMany(BTag::class, 'booking_btag', 'booking_id', 'btag_id', 'id', 'id', 'btags'); } public function tables() { //return $this->belongsToMany(Table::class, 'booking_table', 'booking_id', 'table_id', 'id', 'id', 'tables'); // ->pivot return $this->belongsToMany(Table::class, 'booking_table', 'booking_id', 'table_id', 'id', 'id', 'tables') ->withPivot(['seats']); } public function tablesWithSeats() { $tables_seats = []; foreach($this->tables as $table) { $tables_seats[$table->id] = ['seats' => $table->pivot_seats]; } return $tables_seats; } // Sincronizar las mesas pero mantener las mesas que no se hayan borrado con sus sillas public function syncTablesAndKeepCurrentSeats($tablesIds) { $current_tables_seats = $this->tablesWithSeats(); $new_sync = []; foreach($tablesIds as $tableId) { if (isset($current_tables_seats[$tableId])){ $new_sync[$tableId] = $current_tables_seats[$tableId]; } else { $new_sync[$tableId] = ['seats' => null]; } } $this->tables()->sync($new_sync); } public function notifications() { return $this->hasMany(BookingNotification::class); } public function payments() { return $this->hasMany(Payment::class); } public function review() { return $this->hasOne( BookingReview::class); } public function missedCallLog() { return $this->belongsTo(MissedCallLog::class); } public function getActionsAttribute() { $list = Action::where('restaurant_id', $this->restaurant_id) ->where('model_type', 'LIKE', '%Booking') //->where('model_type', Booking::class) // no funciona ->where('model_id', intval($this->id)) ->orderBy('date_created', 'ASC') ->get(); return $list; } public function getNameAttribute() { return $this->first_name . ' ' . $this->last_name; } public function getTagsListAttribute() { return $this->tags->map( function($tag) { return intval($tag->id); })->toArray(); } public function getTagsListNamesAttribute() { $names = $this->tagsListNamesArray; return implode(', ', $names); } public function getTagsListNamesArrayAttribute() { $names = $this->tags->map( function($tag) { return $tag->name; })->toArray(); return $names; } public function getTablesListAttribute() { return $this->tables->map( function($table) { return intval($table->id); })->toArray(); } // result: {'306': ["1", "2", "3"], '307': ["4", "5", "6"]} // En javascript llevan las keys como texto, aunque use numeros public function getTablesSeatsListAttribute() { $list = []; foreach($this->tables as $table) { $list[intval($table->id)] = $table->pivot_seats ? explode(',', $table->pivot_seats) : []; } return $list; //return $this->tables->map( function($table) { // return intval($table->id) . '[' . $table->pivot_seats . ']'; //})->toArray(); } public static function tablesNamesWithAreaArray($list) { return Table::whereIn('id', $list) ->get() ->map(function($table) { return $table->name.'('.$table->area->name.')'; }) ->toArray(); } public function getTablesListNamesAttribute() { $names = $this->tables->map( function($table) { return $table->name; })->toArray(); return implode(', ', $names); } public function getIsVipAttribute() { $list = $this->tags->filter(function($tag){ return $tag->group->is_vip; })->toArray(); return count($list) > 0; } public function getIsCustomerVipAttribute() { $customer = $this->customer; if (!$customer) return false; return $customer->isVip; } public function getShiftNameAttribute() { $id = $this->shift_event_id; if ($id > 0){ $shift = Shift::where('id', $id)->first(); if ($shift) return $shift->name; $event = Event::where('id', $id)->first(); if ($event) return $event->name; } return '-'; } /** * Configuration for the view booking page * depends on the widget where this booking was managed * @return false[] */ public function getViewLayoutAttribute() { $widget_id = $this->widget_id; if (!$widget_id) { return [ 'show_services_duration' => false ]; } $widget_form = WidgetForm::where('id', $widget_id)->first(); if (!$widget_form) { return [ 'show_services_duration' => false ]; } $show_services_duration = isset($widget_form->form_config['show_services_duration']) ? $widget_form->form_config['show_services_duration'] : 'yes'; $show_services_duration = $show_services_duration == 'no' ? false : true; return [ 'show_services_duration' => $show_services_duration ]; } // Esto puede ser muy lento si esta en la vista menusal public function getTrackingDataAttribute() { if (!self::$addTrackingDataToBooking) { return null; } $tracking = BookingTracking::where('booking_id', $this->id)->first(); if ($tracking) { return $tracking->toArray(); } return null; } public function getAreaSelectedNameAttribute() { if ($this->area_selected_id == null) return ''; $area = Area::find($this->area_selected_id); return $area != null ? $area->name : ''; } public function getTableSelectedNameAttribute() { if ($this->table_selected_id == null) return ''; $table = Table::find($this->table_selected_id); return $table != null ? $table->name : ''; } /** * Get custom fields formatted in array * Has the full field used and the value submitted * @return void */ public function getCustomValuesAttribute() { $fields = $this->custom_fields; if ($fields == null) { return null; } try { if (is_string($fields)){ $fields = json_decode($fields, true); } if (!is_array($fields)) { return null; } // Decode custom fields of type: options, select foreach($fields as $key => $field) { if ($field['field']['type'] == 'select' || $field['field']['type'] == 'options') { $value = $fields[$key]['value']; if (is_string($value)) { $value = str_replace('\"', '"', $value); $value = json_decode($value, true); $fields[$key]['value'] = $value; } } } } catch (\Exception $e) { $fields = null; } return $fields; } public function customFieldsEmailHtml() { $values = $this->customValues; if ($values == null) return ''; if (!is_array($values)) return ''; $html = ''; foreach($values as $key => $item) { if (!isset($item['field']) || !isset($item['value'])) { continue; } if ($item['field']['type'] == 'text'){ $html .= "
{$item['value']}
"; } else if ($item['field']['type'] == 'textarea'){ $html .= "
{$item['value']}
"; } else if ($item['field']['type'] == 'checkbox'){ $val = ($item['value'] === true || $item['value'] == 'true') ? '[X]' : '[-]'; $html .= "
{$val} {$item['field']['name']}
"; } else if ($item['field']['type'] == 'select') { $html .= "
{$item['value']['value']}
"; } else if ($item['field']['type'] == 'options') { foreach($item['value'] as $key => $val){ if ($val === true || $val == 'true'){ $html .= "
[X] {$key}
"; } } } } return $html; } public function customFieldsCsvFormat() { $html = $this->customFieldsEmailHtml(); $html = str_replace('
', '', $html); $html = str_replace('
', "\r\n", $html); return $html; } public function toCustomArray() { return self::toDataArray($this); } /** * Used for mybooking view * @param $booking * * @return array */ public static function toDataArray($booking) { $review = $booking->review; if ($review){ $review = $review->toArray(); } return [ 'restaurant' => $booking->restaurant->name, 'restaurant_id' => $booking->restaurant_id, //'date_format' => $booking->restaurant->date_format, //'time_format' => $booking->restaurant->time_format, 'timezone' => $booking->restaurant->timezone, 'uuid' => $booking->uuid, 'id' => $booking->id, 'name' => $booking->name, 'email' => $booking->email, 'status' => $booking->status, 'status_label' => \Alexr\Enums\BookingStatus::label($booking->status), 'date' => $booking->date, 'party' => $booking->party, 'time' => $booking->time, 'duration' => $booking->duration, 'language' => $booking->language, 'review' => $review, 'notes' => alexr_transform_new_lines_to_br($booking->notes), 'tags' => $booking->getTagsNames(), 'custom_fields' => $booking->customValues, 'gateway' => $booking->gateway, 'area_selected_id' => $booking->area_selected_id, 'table_selected_id' => $booking->table_selected_id, 'area_selected_name' => $booking->areaSelectedName, 'table_selected_name' => $booking->tableSelectedName ]; } /** * Data for CSV exporting * @return array */ public function toCsvArray($lang = 'en') { // Las claves tienen que coincidir con Alexr\Enums\BookingExportColumn $data = [ 'id' => $this->id, 'uuid' => $this->uuid, 'type' => $this->type, 'source' => $this->source, 'status' => alexr_translate_x( BookingStatus::label($this->status), $lang ), 'date' => $this->date, 'date_created' => $this->date_created, 'name' => $this->name, 'email' => $this->email, 'language' => $this->language, 'party' => $this->party, 'shift_event' => $this->shiftName, 'time_24h' => evavel_seconds_to_Hm($this->time), 'time_12h' => evavel_seconds_to_Hm12($this->time), 'duration' => evavel_seconds_to_duration($this->duration), 'phone' => '('.$this->dial_code.')'.$this->phone, 'country' => Countries::country($this->country_code), 'payment' => $this->paymentCsvFormat($lang), 'spend' => $this->spend, 'area_table_selected' => $this->areaTableSelectedCsvFormat(), 'tables' => $this->tablesListNames, 'tags' => $this->tagsListNames, 'notes' => $this->notes . "\n" . $this->private_notes, 'custom_fields' => $this->customFieldsCsvFormat() ]; // Compatibilidad: antes esta columna se llamaba 'shift' $data['shift'] = $data['shift_event']; return $data; } /** * Pago en formato texto para el CSV / impresion * * @param string $lang * * @return string */ protected function paymentCsvFormat($lang = 'en') { if (!$this->gateway) return ''; if ($this->isForCapturingCard) { return $this->isCardCaptured ? alexr_translate_x('Card Captured', $lang) : alexr_translate_x('Card Saved', $lang); } if ($this->isPaid) { return $this->paidAmountFormatted; } return alexr_translate_x('Pending Payment', $lang); } /** * Area y mesa seleccionadas por el cliente, en formato "Area (Mesa)" * * @return string */ protected function areaTableSelectedCsvFormat() { $area = $this->areaSelectedName; $table = $this->tableSelectedName; if ($area && $table) return $area . ' (' . $table . ')'; if ($table) return $table; if ($area) return $area; return ''; } // CANCEL REASON public function setCancelReasonAttribute($reason) { $this->set_setting('cancelReason', $reason); } public function getCancelReasonAttribute() { //return 'NADA'; return $this->get_setting('cancelReason'); } // EMAILS OPENED // yes => se ha abierto // no => se ha enviado pero no se ha abierto // not_sent => no se ha enviado protected function loadNotifications(){ if (!$this->listNotifications) { $this->listNotifications = BookingNotification::where('restaurant_id', $this->restaurant_id) ->where('booking_id', $this->id) ->where('type', 'email') ->get() ->toArrayEachModel(); } } public function getEmailPendingOpenAttribute() { if (!defined('ALEXR_PRO_VERSION')) return '-'; if (intval(ALEXR_PRO_RELEASE) < 102) return '-'; $this->loadNotifications(); // Buscar notificaciones con status 'pending' $pendingNotifications = array_filter($this->listNotifications, function($notification) { return isset($notification['payload']['status']) && $notification['payload']['status'] === BookingStatus::PENDING; }); // Si no hay notificaciones pending, no se ha enviado if (empty($pendingNotifications)) { return 'not_send'; } // Comprobar si alguna notificación se ha abierto foreach ($pendingNotifications as $notification) { if (isset($notification['payload']['opens']) && !empty($notification['payload']['opens'])) { return 'yes'; } } // Si hay notificaciones pero ninguna se ha abierto return 'no'; } public function getEmailConfirmedOpenAttribute() { if (!defined('ALEXR_PRO_VERSION')) return '-'; if (intval(ALEXR_PRO_RELEASE) < 102) return '-'; $this->loadNotifications(); // Buscar notificaciones con status 'booked' $confirmedNotifications = array_filter($this->listNotifications, function($notification) { return isset($notification['payload']['status']) && $notification['payload']['status'] === BookingStatus::BOOKED; }); // Si no hay notificaciones confirmed, no se ha enviado if (empty($confirmedNotifications)) { return 'not_send'; } // Comprobar si alguna notificación se ha abierto foreach ($confirmedNotifications as $notification) { if (isset($notification['payload']['opens']) && !empty($notification['payload']['opens'])) { return 'yes'; } } // Si hay notificaciones pero ninguna se ha abierto return 'no'; } public function getEmailFinishedOpenAttribute() { if (!defined('ALEXR_PRO_VERSION')) return '-'; if (intval(ALEXR_PRO_RELEASE) < 102) return '-'; $this->loadNotifications(); // Buscar notificaciones con status 'finished' $finishedNotifications = array_filter($this->listNotifications, function($notification) { return isset($notification['payload']['status']) && $notification['payload']['status'] === BookingStatus::FINISHED; }); // Si no hay notificaciones finished, no se ha enviado if (empty($finishedNotifications)) { return 'not_send'; } // Comprobar si alguna notificación se ha abierto foreach ($finishedNotifications as $notification) { if (isset($notification['payload']['opens']) && !empty($notification['payload']['opens'])) { return 'yes'; } } // Si hay notificaciones pero ninguna se ha abierto return 'no'; } // WOOCOMMERCE public function setCheckoutUrlAttribute($reason) { $this->set_setting('woo_checkout_url', $reason); } public function getCheckoutUrlAttribute() { //return 'NADA'; return $this->get_setting('woo_checkout_url'); } }