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');
}
}