✅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.

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.

Compartir Artículo