✅Haz que tus Productos sean Reservables con este Código para Woocommerce

Este sistema permite añadir una funcionalidad de reservas personalizadas a productos en WooCommerce. Con esta integración, se pueden configurar horarios específicos de disponibilidad, días festivos no reservables y bloques de tiempo para gestionar las reservas de forma eficiente. Además, los clientes pueden seleccionar fecha y hora directamente desde la página del producto, y los administradores pueden gestionar todas las reservas desde el panel de control de WordPress.

El objetivo principal de este código es proporcionar una solución práctica y flexible para negocios que ofrecen servicios basados en reservas, como consultorías, alquileres o actividades programadas, optimizando la interacción entre clientes y administradores.

Explicación de las Funciones

1. Agregar Opciones de Configuración para Productos Reservables

  • Se introduce una sección adicional en la página de configuración del producto en WooCommerce.
  • Permite marcar un producto como reservable y configurar opciones como horarios disponibles, días festivos y duración de los bloques de reserva.

2. Visualización de Horarios Disponibles

  • Se genera dinámicamente una tabla interactiva para que los administradores ingresen los horarios disponibles por día.
  • Los horarios se almacenan en un formato JSON para facilitar su manipulación y uso.

3. Gestión de Días Festivos

  • Se habilita un campo para ingresar fechas específicas (en formato YYYY-MM-DD) como días no disponibles para reservas.
  • Estas fechas se excluyen automáticamente del selector de fechas para los clientes.
reservas wordpress woocommerce

4. Configuración de Duración de Bloques de Reserva

  • Se permite seleccionar la duración de los bloques de reserva (15, 30, 60 o 120 minutos).
  • Esto determina los intervalos de tiempo que estarán disponibles para los clientes.

5. Selector de Fecha y Hora en la Página del Producto

  • Los clientes pueden elegir una fecha y hora para su reserva mediante un formulario en la página del producto.
  • La selección se limita a las fechas y horarios disponibles según la configuración del administrador.
productos reservables woocommerce wordpress

6. Validación Antes de Añadir al Carrito

  • Se verifica que la fecha y hora seleccionadas no estén reservadas antes de permitir que el cliente añada el producto al carrito.
  • En caso de conflicto, se muestra un mensaje de error.

7. Almacenamiento de Reservas en el Carrito y el Pedido

  • Las fechas y horas seleccionadas se guardan como metadatos del producto en el carrito y en el pedido.
  • Esto asegura que la información de la reserva se mantenga durante todo el proceso de compra.

8. Gestión de Reservas en el Panel de Administración

  • Se añade una nueva página en el panel de administración de WordPress para visualizar, cancelar y eliminar reservas.
  • La página utiliza DataTables para una experiencia más interactiva, con opciones de búsqueda y ordenamiento.
✅Haz que tus Productos sean Reservables con este Código para Woocommerce 1

9. Estilos Personalizados para Formularios de Reserva

  • Se aplican estilos modernos y responsivos a los formularios de selección de fecha y hora para mejorar la experiencia del usuario.

10. Reemplazo del Botón «Añadir al Carrito»

  • Para productos reservables, el botón estándar se reemplaza con un enlace que redirige a la página del producto para seleccionar una fecha y hora.

11. Gestión de Reservas Existentes

  • Una función verifica las reservas existentes para evitar conflictos de horarios.
  • También se proporciona la opción de cancelar o eliminar reservas desde la página de administración.

12. Funciones Auxiliares

  • Obtener Reservas Existentes: Recupera todas las reservas asociadas a un producto desde los pedidos.
  • Validar Fechas y Horarios: Se asegura de que las selecciones respeten las configuraciones de horarios y días festivos.
  • Conversión de Días al Español: Traduce los nombres de días al español para la presentación en el frontend.

El siguiente código tienes que agregarlo en el functions.php de tu tema hijo o utilizar un plugin como CODE SNIPPETS.

// Mostrar opciones de configuración en el producto
add_action('woocommerce_product_options_general_product_data', function() {
    global $post;

    // Obtener los valores guardados
    $horarios_guardados = get_post_meta($post->ID, '_horario_disponible', true);
    $horarios_guardados = $horarios_guardados ? json_decode($horarios_guardados, true) : [];
    $festivos_guardados = get_post_meta($post->ID, '_dias_festivos', true);

    echo '<div class="options_group">';

    // Producto reservable
    woocommerce_wp_checkbox([
        'id'          => '_es_reservable',
        'label'       => __('Es Reservable', 'woocommerce'),
        'description' => __('Haz que este producto sea reservable en horarios específicos.', 'woocommerce')
    ]);

    // Tabla de horarios disponibles
    echo '<label for="_horario_disponible_table">' . __('Horarios Disponibles', 'woocommerce') . '</label>';
    echo '<div id="_horario_disponible_table" class="styled-horarios-table"></div>';
    echo '<textarea id="_horario_disponible" name="_horario_disponible" style="display:none;">' . esc_textarea(json_encode($horarios_guardados)) . '</textarea>';
    echo '<p class="description">' . __('Edita los horarios en la tabla. Ejemplo: Lunes 08:00-14:00,16:00-20:00.', 'woocommerce') . '</p>';

    // Campo para días festivos
    woocommerce_wp_text_input([
        'id'          => '_dias_festivos',
        'label'       => __('Días Festivos', 'woocommerce'),
        'description' => __('Ingresa las fechas de días festivos en formato YYYY-MM-DD separadas por comas.', 'woocommerce'),
        'desc_tip'    => true,
        'value'       => esc_attr($festivos_guardados)
    ]);

    // Duración de bloques
    woocommerce_wp_select([
        'id'          => '_duracion_bloque_reserva',
        'label'       => __('Duración de Bloques', 'woocommerce'),
        'description' => __('Selecciona la duración de cada bloque de reserva.', 'woocommerce'),
        'options'     => [
            '15' => __('15 Minutos', 'woocommerce'),
            '30' => __('30 Minutos', 'woocommerce'),
            '60' => __('1 Hora', 'woocommerce'),
            '120' => __('2 Horas', 'woocommerce')
        ]
    ]);

    echo '</div>';

    // Estilos CSS para la tabla
    echo '<style>
    .styled-horarios-table table {
        width: 100%;
        border-collapse: collapse;
        margin-top: 10px;
        margin-bottom: 10px;
    }

    .styled-horarios-table th {
        background-color: #f4f4f4;
        font-weight: bold;
        text-align: left;
        padding: 8px;
        border: 1px solid #ccc;
    }

    .styled-horarios-table td {
        padding: 8px;
        border: 1px solid #ccc;
        vertical-align: middle;
    }

    .styled-horarios-table td input {
        width: 100%;
        padding: 6px;
        font-size: 14px;
    }

    .styled-horarios-table tr:nth-child(even) {
        background-color: #f9f9f9;
    }

    .styled-horarios-table tr:hover {
        background-color: #f1f1f1;
    }
    </style>';

    // Script para generar la tabla
    echo '<script>
    document.addEventListener("DOMContentLoaded", function() {
        const days = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado", "domingo"];
        const horarios = document.querySelector("#_horario_disponible").value ? JSON.parse(document.querySelector("#_horario_disponible").value) : {};
        const tableContainer = document.querySelector("#_horario_disponible_table");

        const createTable = () => {
            const table = document.createElement("table");

            const headerRow = document.createElement("tr");
            const dayHeader = document.createElement("th");
            dayHeader.textContent = "Día";
            const horarioHeader = document.createElement("th");
            horarioHeader.textContent = "Horarios";
            headerRow.appendChild(dayHeader);
            headerRow.appendChild(horarioHeader);
            table.appendChild(headerRow);

            days.forEach(day => {
                const row = document.createElement("tr");
                const dayCell = document.createElement("td");
                dayCell.textContent = day.charAt(0).toUpperCase() + day.slice(1);
                const horarioCell = document.createElement("td");

                const input = document.createElement("input");
                input.type = "text";
                input.dataset.day = day;
                input.value = horarios[day] || "";
                horarioCell.appendChild(input);

                row.appendChild(dayCell);
                row.appendChild(horarioCell);
                table.appendChild(row);
            });

            tableContainer.appendChild(table);

            // Actualizar el campo oculto con el JSON
            table.addEventListener("input", () => {
                const updatedHorarios = {};
                table.querySelectorAll("input").forEach(input => {
                    updatedHorarios[input.dataset.day] = input.value.trim();
                });
                document.querySelector("#_horario_disponible").value = JSON.stringify(updatedHorarios);
            });
        };

        createTable();
    });
    </script>';
});

// Guardar datos del producto
add_action('woocommerce_process_product_meta', function($post_id) {
    $es_reservable = isset($_POST['_es_reservable']) ? 'yes' : 'no';
    update_post_meta($post_id, '_es_reservable', $es_reservable);

    if (isset($_POST['_horario_disponible'])) {
        update_post_meta($post_id, '_horario_disponible', sanitize_text_field($_POST['_horario_disponible']));
    }

    if (isset($_POST['_dias_festivos'])) {
        update_post_meta($post_id, '_dias_festivos', sanitize_text_field($_POST['_dias_festivos']));
    }

    if (isset($_POST['_duracion_bloque_reserva'])) {
        update_post_meta($post_id, '_duracion_bloque_reserva', sanitize_text_field($_POST['_duracion_bloque_reserva']));
    }
});



add_filter('woocommerce_before_add_to_cart_button', function() {
    global $product;

    if ($product->get_meta('_es_reservable') === 'yes') {
        $horarios = json_decode($product->get_meta('_horario_disponible'), true);
        $festivos = $product->get_meta('_dias_festivos');
        $reservas_existentes = obtener_reservas($product->get_id());
        $festivos_array = $festivos ? explode(',', $festivos) : [];
        $festivos_array = array_map(function($fecha) {
            $date = DateTime::createFromFormat('Y-m-d', trim($fecha));
            return $date ? $date->format('Y-m-d') : null;
        }, $festivos_array);
        $festivos_array = array_filter($festivos_array); // Eliminar nulos
        $duracion_bloque = $product->get_meta('_duracion_bloque_reserva') ?: 30; // Por defecto 30 minutos

        echo '<div class="modern-reservation-form">';
        echo '<label for="dia_reserva">Selecciona tu día:</label>';
        echo '<select name="dia_reserva" class="modern-select" required>';
        $fecha_actual = new DateTime();
        $fecha_actual->modify('+1 day'); // Solo a partir del día siguiente
        for ($i = 0; $i < 90; $i++) {
            $dia_semana = strtolower($fecha_actual->format('l'));
            $dia_semana_es = convertir_dia_a_espanol($dia_semana);
            $fecha_actual_str = $fecha_actual->format('Y-m-d');

            // Verificar si el día tiene horarios, no está en festivos y no tiene todas las horas reservadas
            if (
                isset($horarios[$dia_semana_es]) &&
                !empty($horarios[$dia_semana_es]) &&
                !in_array($fecha_actual_str, $festivos_array) &&
                !todas_horas_reservadas($fecha_actual_str, $horarios[$dia_semana_es], $reservas_existentes)
            ) {
                echo '<option value="' . esc_attr($fecha_actual_str) . '">' . esc_html($fecha_actual->format('d/m/Y')) . ' (' . ucfirst($dia_semana_es) . ')</option>';
            }
            $fecha_actual->modify('+1 day');
        }
        echo '</select>';

        echo '<label for="hora_reserva">Selecciona tu hora:</label>';
        echo '<select name="hora_reserva" class="modern-select" required></select>';
        echo '</div>';

        echo '<style>
        .modern-reservation-form {
            display: flex;
            flex-direction: column;
            gap: 1rem;
            margin: 1.5rem 0;
        }

        .modern-reservation-form label {
            font-weight: bold;
            font-size: 1rem;
            color: #333;
        }

        .modern-select {
            font-size: 1rem;
            padding: 0.75rem;
            border: 2px solid #ddd;
            border-radius: 5px;
            background-color: #fff;
            transition: all 0.3s ease;
            color: #333;
        }

        .modern-select:focus {
            border-color: #007bff;
            box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
            outline: none;
        }

        .modern-select:hover {
            border-color: #0056b3;
        }
        </style>';

        echo '<script>
        document.addEventListener("DOMContentLoaded", function() {
            const horarios = ' . json_encode($horarios) . ';
            const reservas = ' . json_encode($reservas_existentes) . ';
            const duracionBloque = ' . $duracion_bloque . ';
            const selectDia = document.querySelector("select[name=dia_reserva]");
            const selectHora = document.querySelector("select[name=hora_reserva]");

            selectDia.addEventListener("change", function() {
                const diaSeleccionado = this.value; // Valor en formato Y-m-d
                const diaSemana = new Date(diaSeleccionado).toLocaleDateString("es-ES", { weekday: "long" }).toLowerCase();
                selectHora.innerHTML = ""; // Limpiar las opciones previas

                if (horarios[diaSemana]) {
                    horarios[diaSemana].split(",").forEach(rango => {
                        const [inicio, fin] = rango.split("-");
                        let hora = new Date("1970-01-01T" + inicio + ":00");
                        const finHora = new Date("1970-01-01T" + fin + ":00");

                        while (hora < finHora) {
                            const horaTexto = hora.toTimeString().slice(0, 5);
                            const valor = diaSeleccionado + " " + horaTexto;

                            // Comprobar si la hora está reservada
                            if (!reservas.includes(valor)) {
                                const option = document.createElement("option");
                                option.value = horaTexto;
                                option.textContent = horaTexto;
                                selectHora.appendChild(option);
                            }

                            hora.setMinutes(hora.getMinutes() + parseInt(duracionBloque));
                        }
                    });
                }
            });

            // Disparar el evento de cambio para el primer día seleccionado
            if (selectDia.value) {
                selectDia.dispatchEvent(new Event("change"));
            }
        });
        </script>';
    }
});

// Función para obtener reservas existentes
function obtener_reservas($product_id) {
    $reservas = [];
    $orders = wc_get_orders(['limit' => -1, 'status' => ['wc-completed', 'wc-processing', 'wc-on-hold', 'wc-pending']]);

    foreach ($orders as $order) {
        foreach ($order->get_items() as $item) {
            if ($item->get_product_id() == $product_id) {
                $dia = $item->get_meta('Día Reservado');
                $hora = $item->get_meta('Hora Reservada');
                if ($dia && $hora) {
                    $reservas[] = "$dia $hora";
                }
            }
        }
    }
    return $reservas;
}

// Verificar si todas las horas de un día están reservadas
function todas_horas_reservadas($fecha, $horarios_dia, $reservas) {
    $horas_reservadas = array_filter($reservas, function($reserva) use ($fecha) {
        return strpos($reserva, $fecha) === 0;
    });

    $total_horas = 0;
    foreach (explode(',', $horarios_dia) as $rango) {
        [$inicio, $fin] = explode('-', $rango);
        $inicio_hora = new DateTime("1970-01-01T$inicio:00");
        $fin_hora = new DateTime("1970-01-01T$fin:00");

        while ($inicio_hora < $fin_hora) {
            $total_horas++;
            $inicio_hora->modify('+30 minutes');
        }
    }

    return count($horas_reservadas) >= $total_horas;
}

// Convertir día al español
function convertir_dia_a_espanol($dia) {
    $dias = [
        'monday' => 'lunes',
        'tuesday' => 'martes',
        'wednesday' => 'miércoles',
        'thursday' => 'jueves',
        'friday' => 'viernes',
        'saturday' => 'sábado',
        'sunday' => 'domingo'
    ];
    return $dias[$dia] ?? $dia;
}

// Validar antes de añadir al carrito
add_filter('woocommerce_add_to_cart_validation', function($passed, $product_id) {
    if (isset($_POST['dia_reserva'], $_POST['hora_reserva'])) {
        $reserva = sanitize_text_field($_POST['dia_reserva']) . ' ' . sanitize_text_field($_POST['hora_reserva']);
        if (in_array($reserva, obtener_reservas($product_id))) {
            wc_add_notice('La fecha y hora seleccionadas ya están reservadas.', 'error');
            return false;
        }
    }
    return $passed;
}, 10, 2);

// Guardar reserva en el carrito
add_filter('woocommerce_add_cart_item_data', function($cart_item_data, $product_id, $variation_id) {
    if (isset($_POST['dia_reserva'], $_POST['hora_reserva'])) {
        $cart_item_data['dia_reserva'] = sanitize_text_field($_POST['dia_reserva']);
        $cart_item_data['hora_reserva'] = sanitize_text_field($_POST['hora_reserva']);
    }
    return $cart_item_data;
}, 10, 3);

// Mostrar reserva en el carrito y la página de pago
add_filter('woocommerce_get_item_data', function($item_data, $cart_item) {
    if (isset($cart_item['dia_reserva'], $cart_item['hora_reserva'])) {
        $item_data[] = [
            'key'   => __('Día Reservado', 'woocommerce'),
            'value' => $cart_item['dia_reserva']
        ];
        $item_data[] = [
            'key'   => __('Hora Reservada', 'woocommerce'),
            'value' => $cart_item['hora_reserva']
        ];
    }
    return $item_data;
}, 10, 2);




// Guardar reserva en el pedido
add_action('woocommerce_checkout_create_order_line_item', function($item, $cart_item_key, $values) {
    if (isset($values['dia_reserva'], $values['hora_reserva'])) {
        $item->add_meta_data('Día Reservado', $values['dia_reserva']);
        $item->add_meta_data('Hora Reservada', $values['hora_reserva']);
        $item->add_meta_data('Estado de la Reserva', 'Activa');
    }
}, 10, 3);




// Página de administración para gestionar reservas
add_action('admin_menu', function() {
    add_menu_page(
        __('Reservas', 'woocommerce'),
        __('Reservas', 'woocommerce'),
        'manage_woocommerce',
        'reservas',
        'mostrar_pagina_reservas',
        'dashicons-calendar-alt',
        56
    );
});

function mostrar_pagina_reservas() {
    echo '<div class="wrap">';
    echo '<h1>' . __('Gestión de Reservas', 'woocommerce') . '</h1>';

    if (isset($_POST['cancelar_reserva'])) {
        procesar_cancelacion_reserva($_POST['reserva_dia'], $_POST['reserva_hora'], $_POST['producto']);
        echo '<div class="updated"><p>' . __('Reserva cancelada correctamente.', 'woocommerce') . '</p></div>';
    }

    if (isset($_POST['eliminar_reserva'])) {
        eliminar_reserva($_POST['reserva_dia'], $_POST['reserva_hora'], $_POST['producto']);
        echo '<div class="updated"><p>' . __('Reserva eliminada correctamente.', 'woocommerce') . '</p></div>';
    }

    $reservas = obtener_reservas_admin();

    if (!empty($reservas)) {
        echo '<table id="reservas-table" class="wp-list-table widefat fixed striped">';
        echo '<thead><tr>';
        echo '<th>' . __('Cliente', 'woocommerce') . '</th>';
        echo '<th>' . __('Email', 'woocommerce') . '</th>';
        echo '<th>' . __('Teléfono', 'woocommerce') . '</th>';
        echo '<th>' . __('Producto', 'woocommerce') . '</th>';
        echo '<th>' . __('Fecha Reservada', 'woocommerce') . '</th>';
        echo '<th>' . __('Hora Reservada', 'woocommerce') . '</th>';
        echo '<th>' . __('Estado', 'woocommerce') . '</th>';
        echo '<th>' . __('Acciones', 'woocommerce') . '</th>';
        echo '</tr></thead><tbody>';

        foreach ($reservas as $reserva) {
            echo '<tr>';
            echo '<td><a href="mailto:' . esc_html($reserva['email']) . '">' . esc_html($reserva['cliente']) . '</a></td>';
            echo '<td>' . esc_html($reserva['email']) . '</td>';
            echo '<td>' . esc_html($reserva['telefono']) . '</td>';
            echo '<td>' . esc_html($reserva['producto']) . '</td>';
            echo '<td>' . esc_html($reserva['fecha']) . '</td>';
            echo '<td>' . esc_html($reserva['hora']) . '</td>';

            $estado_color = $reserva['estado'] === 'Cancelada' ? 'red' : 'green';
            echo '<td style="color:' . $estado_color . ';">' . esc_html($reserva['estado']) . '</td>';

            echo '<td>';
            if ($reserva['estado'] !== 'Cancelada') {
                echo '<form method="post" style="display:inline-block;">';
                echo '<input type="hidden" name="reserva_dia" value="' . esc_attr($reserva['fecha']) . '">';
                echo '<input type="hidden" name="reserva_hora" value="' . esc_attr($reserva['hora']) . '">';
                echo '<input type="hidden" name="producto" value="' . esc_attr($reserva['producto']) . '">';
                echo '<button type="submit" name="cancelar_reserva" class="button-link-delete">' . __('Cancelar', 'woocommerce') . '</button>';
                echo '</form>';
            }

            if ($reserva['estado'] === 'Cancelada') {
                echo '<form method="post" style="display:inline-block;">';
                echo '<input type="hidden" name="reserva_dia" value="' . esc_attr($reserva['fecha']) . '">';
                echo '<input type="hidden" name="reserva_hora" value="' . esc_attr($reserva['hora']) . '">';
                echo '<input type="hidden" name="producto" value="' . esc_attr($reserva['producto']) . '">';
                echo '<button type="submit" name="eliminar_reserva" class="button-link-delete">' . __('Eliminar', 'woocommerce') . '</button>';
                echo '</form>';
            }

            echo '</td>';
            echo '</tr>';
        }

        echo '</tbody></table>';
    } else {
        echo '<p>' . __('No hay reservas disponibles.', 'woocommerce') . '</p>';
    }

    echo '</div>';
}

function obtener_reservas_admin() {
    $reservas = [];
    $orders = wc_get_orders(['limit' => -1, 'status' => ['wc-completed', 'wc-processing', 'wc-on-hold', 'wc-pending']]);

    foreach ($orders as $order) {
        foreach ($order->get_items() as $item) {
            $fecha = $item->get_meta('Día Reservado');
            $hora = $item->get_meta('Hora Reservada');
            $estado = $item->get_meta('Estado de la Reserva') ?: 'Activa';
            if ($fecha && $hora) {
                $reservas[] = [
                    'cliente' => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
                    'email' => $order->get_billing_email(),
                    'telefono' => $order->get_billing_phone(),
                    'producto' => $item->get_name(),
                    'fecha' => $fecha,
                    'hora' => $hora,
                    'estado' => $estado
                ];
            }
        }
    }

    return $reservas;
}


// Funciones adicionales al código previo
function eliminar_reserva($dia, $hora, $producto) {
    $orders = wc_get_orders(['limit' => -1, 'status' => ['wc-completed', 'wc-processing', 'wc-on-hold', 'wc-pending']]);

    foreach ($orders as $order) {
        foreach ($order->get_items() as $item_id => $item) {
            if ($item->get_meta('Día Reservado') === $dia && $item->get_meta('Hora Reservada') === $hora && $item->get_name() === $producto) {
                wc_delete_order_item_meta($item_id, 'Día Reservado');
                wc_delete_order_item_meta($item_id, 'Hora Reservada');
                wc_delete_order_item_meta($item_id, 'Estado de la Reserva');
                break 2; // Salir de ambos bucles
            }
        }
    }
}

function procesar_cancelacion_reserva($dia, $hora, $producto) {
    $orders = wc_get_orders(['limit' => -1, 'status' => ['wc-completed', 'wc-processing', 'wc-on-hold', 'wc-pending']]);

    foreach ($orders as $order) {
        foreach ($order->get_items() as $item_id => $item) {
            if ($item->get_meta('Día Reservado') === $dia && $item->get_meta('Hora Reservada') === $hora && $item->get_name() === $producto) {
                wc_update_order_item_meta($item_id, 'Estado de la Reserva', 'Cancelada');
                break 2; // Salir de ambos bucles
            }
        }
    }
}




// Encolar DataTables solo en la página de reservas
add_action('admin_enqueue_scripts', function($hook) {
    if ($hook === 'toplevel_page_reservas') { // Cambia 'toplevel_page_reservas' según el slug de tu página
        // Incluir estilos y scripts de DataTables desde el CDN
        wp_enqueue_script('datatables-js', 'https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js', ['jquery'], null, true);
        wp_enqueue_style('datatables-css', 'https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css');

        // Inicializar DataTables en la tabla de reservas
        wp_add_inline_script('datatables-js', '
            jQuery(document).ready(function($) {
                $("#reservas-table").DataTable({
                    "language": {
                        "url": "//cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json"
                    },
                    "order": [[4, "asc"]], // Ordenar por la columna "Fecha Reservada" (índice 4)
                    "pageLength": 10, // Mostrar 10 filas por página
                    "responsive": true // Hacer la tabla responsiva
                });
            });
        ');
    }
});




// Reemplazar el botón de "Añadir al carrito" en la tienda para productos reservables
add_filter('woocommerce_loop_add_to_cart_link', function($link, $product) {
    if ($product->get_meta('_es_reservable') === 'yes') {
        $product_url = get_permalink($product->get_id());
        $button_text = __('Elegir Fecha y Hora', 'woocommerce');

        return '<a href="' . esc_url($product_url) . '" class="button choose-date-time">' . esc_html($button_text) . '</a>';
    }
    return $link;
}, 10, 2);


Lenguaje del código: PHP (php)

En conclusión, este sistema de reservas personalizadas para WooCommerce proporciona una solución robusta y flexible para gestionar productos o servicios que requieren selección de horarios específicos. Desde la configuración de horarios y días festivos hasta la gestión avanzada de reservas desde el panel administrativo, esta integración permite a los negocios ofrecer una experiencia fluida tanto para los clientes como para los administradores.

Gracias a funcionalidades como la validación de reservas existentes, la personalización de bloques de tiempo y un diseño intuitivo, este código mejora la interacción con los usuarios finales y optimiza los procesos de reserva. Es una herramienta ideal para negocios que buscan simplificar la administración de sus servicios y garantizar una experiencia de usuario profesional y personalizada.

Contáctame por WhatsApp para servicios de desarrollo personalizado, ajustes en tu tienda online o cualquier solución a medida que necesites.
➡️Haz clic aquí para contactarme por WhatsApp

Nos vemos en el siguiente tutorial.

Un saludo.

5/5 - (5 votos)
Compartir Artículo

5 comentarios

  1. Fran Salas Master WP 1 año atrás

    Deja tus opiniones o dudas en los comentarios.

  2. Noemí Principiante WP 1 año atrás

    Estoy hecha un lio. Hay tantas opciones...
    Bookly, Amelia ahora me encuentro este código. Me ayudas a elegir para una tienda de estética? Te dejé otro comentario en el post de Bookly. Muchas gracias.

    • Fran Salas Master WP 1 año atrás

      Buenas, como te dije en el otro comentario lo mejor que puedes hacer es probarlo todo, ver la interfaz, hacer pruebas y te quedas con el que se adapte más a lo que necesitas. Primero fíjate en la funcionalidad antes que el diseño. El diseño es más fácil cambiarlo.
      En cuanto a este código, es algo más simple. Pensado para una única persona que ofrece sus servicios online sin mucha complicación. Saludos.

  3. Usuario Anónimo No Premium Junior WP 1 año atrás

    # Hola! El código no me funciona correctamente.
    La previsualización de los días sale cortada y no permite elegir una hora... No sé si estoy haciendo algo mal, pero no he modificado nada del código, solo copiar y pegar. ¿Hay alguna solución?
    Muchas gracias!!

    • Fran Salas Master WP 1 año atrás

      Hola, podría ser tu tema y que tengas que retocar algo con CSS. El código funciona perfectamente en esta web de pruebas con el tema Blocksy.
      https://codigos.fransalas-proyectos.com/product/producto-reservable/

¿Tienes una duda o sugerencia sobre este artículo?

Puedes dejar un comentario de forma anónima, sin necesidad de registrarte ni proporcionar ningún dato personal. Tu mensaje se publicará como “Usuario Anónimo No Premium”, un perfil que agrupa a todos los visitantes no registrados.

Importante: para evitar el spam automático, todos los comentarios anónimos deben comenzar con el símbolo #. Ejemplo: # Tengo una duda sobre los shortcodes personalizados...

Este espacio está destinado exclusivamente a preguntas concretas, dudas o sugerencias relacionadas con el contenido del artículo. Por respeto a los demás lectores, no se publicarán temas sin relación.

Privacidad y normas:
  • No tienes que agregar tu nombre, correo ni ningún otro dato identificativo.
  • No se permiten enlaces en comentarios anónimos.
  • No se permiten símbolos especiales como < o > (menor y mayor que), ya que se consideran intentos de insertar código y serán bloqueados.
  • Todos los comentarios anónimos pasan por revisión antes de ser publicados y solo se aprobarán si aportan valor al contenido del artículo.
¿Quieres comentar con tu nombre y que tu mensaje se publique al instante? Inicia sesión o regístrate.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

0%