Ayer tocaba entrega de paquete de Amazon en casa (para variar 🤪), pero no estábamos, y lo primero que pensé fue: «vaya putada para el repartidor».
Y con esto me acordé de mi época de instalador, donde teníamos que hacer una ruta por Andalucía instalando ordenadores, tabletas … organizando la ruta el día anterior, llamando a clientes …
Esto hoy en día se puede mejorar 💯, ahora es cuando alguien con dos dedos de luces haría research y se encontraría mil soluciones en el mercado, pero qué sería de la vida si no nos pusiésemos a picar código del tirón !!
Vamos a atacar el problema en principio desde 2 puntos:
- Mejora de la ruta física. Es decir, que si tengo que hacer 10 entregas, que se me ordenen de la forma más eficiente posible.
- Minimizar las posibles entregas fallidas, preavisando a los clientes, para que nos puedan confirmar o rechazar si van a estar en casa.
Optimizando la ruta
Empezamos con el paso de optimizar ruta, y como no, vamos a usar la aplicación por excelencia de mapas, Google Maps, y su API.
Las 2 APIs que vamos a tener que activar son:
- Maps JavaScript API
- Directions API
Desde Google Cloud Console podrás crear tu proyecto, y habilitarle ambas APIs
Teniendo nuestra herramienta de optimización de rutas preparadas, es hora de empezar a picar código.
Vamos a empezar a simplificar, y la forma más sencilla de poder gestionar ubicaciones o paradas, es usando un custom post type en WordPress, al que llamaremos ‘delivery_address’, con atributos como el título y la dirección en formato texto en principio tenemos para esta versión.
Vamos a atacar la llamada a la API https://maps.googleapis.com/maps/api/js, lo que nos permitirá obtener el mapa con las ubicaciones ya ordenadas y optimizadas (¿sencillo no?, porque recuerdo la que tuve que liar en una práctica de la facultad a few years ago 👴🏻 para optimizar rutas en un espacio 2D)
<div id="map" style="height: 500px; width: 100%;"></div>
<script>
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 6,
center: { lat: 40.4168, lng: -3.7038 } // Centro en Madrid por defecto
});
var directionsService = new google.maps.DirectionsService();
var directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
directionsService.route({
origin: "<?= urldecode($origin) ?>",
destination: "<?= urldecode($destination) ?>",
waypoints: [
<?php foreach ($addresses as $address) : ?>
{ location: "<?= $address ?>", stopover: true },
<?php endforeach; ?>
],
travelMode: google.maps.TravelMode.DRIVING,
optimizeWaypoints: true
}, function(response, status) {
if (status === "OK") {
directionsRenderer.setDirections(response);
// mostramos más información
var route = response.routes[0];
var summaryPanel = document.getElementById('directions-panel');
summaryPanel.innerHTML = '';
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += '<b>Ruta ' + routeSegment + '</b><br>';
summaryPanel.innerHTML += route.legs[i].start_address + ' a ';
summaryPanel.innerHTML += route.legs[i].end_address + '<br>';
summaryPanel.innerHTML += route.legs[i].distance.text + '<br><br>';
// tiempos
var time = route.legs[i].duration.text;
summaryPanel.innerHTML += 'Tiempo estimado: ' + time + '<br><br>';
}
} else {
alert("Error al calcular la ruta: " + status);
}
});
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=<?= $apiKey ?>&callback=initMap"></script>
<div id="directions-panel"></div>
Obtendremos un mapita como este, además de más info como distancias y tiempos de recorridos.
Parece que una primera versión de la parte de ruta la tenemos ya 🥳 🥳 🥳
Preavisando a los clientes
Entre la información que nos da la API de Google al crear la ruta, están los tiempos de trayectos, por lo que si consideramos que vamos a salir ya (o quizás podamos indicar una hora y día de salida, así los clientes tendrán más tiempo para confirmar su precencia), y teniendo en cuenta que el tiempo de parada va a ser de 15min por ejemplo, tendremos las distintas horas de llegada estimadas.
// ...
for (var i = 0; i < route.legs.length; i++) {
// tiempos
var time = route.legs[i].duration.text;
times.push(time);
}
var now = new Date();
var totalSeconds = now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds();
var arrivalTimes = [];
times.forEach((time, index) => {
// Procesar el formato "Xh Y min"
const timeStr = time.replace(' min', '').split('h ');
const hours = parseInt(timeStr[0]) || 0;
const minutes = parseInt(timeStr[1]) || 0;
// Añadir el tiempo de viaje más 15 minutos de parada
totalSeconds += (hours * 3600) + (minutes * 60) + (15 * 60);
// Calcular la hora de llegada
const arrivalHours = Math.floor(totalSeconds / 3600) % 24; // Usamos módulo 24 para formato 24h
const arrivalMinutes = Math.floor((totalSeconds % 3600) / 60);
// Formatear la hora de llegada
arrivalTimes.push(
`${arrivalHours.toString().padStart(2, '0')}:${arrivalMinutes.toString().padStart(2, '0')}`
);
console.log(`Parada ${index + 1}: ${time} -> Llegada: ${arrivalTimes[arrivalTimes.length - 1]}`);
});
// ...
Parece que ya tenemos la data necesaria para esta segunda funcionalidad, por lo que podemos empezar a notificar a los clientes.
Atacar a la API de Whatsapp directamente quedará para futuros episodios, ya que Meta se ha lucido con ella, no como mi querido Telegram que es una maravilla a nivel de potencial de desarrollo … pero no todo el mundo tiene Telegram 😓
Una alternativa que parece bastante potente es Twilio, ya que no sólo es una capa por encima de Whatsapp facilitando las integraciones, sino que tiene SMS y otros sistemas, además de tener integraciones con Zapier, lo cual siempre es un aliciente a usarlo.
Y ya puestos a hacernos los modernos, podríamos tirar de los agentes de IA de ElevenLabs, que podrían llamar a los usuarios y confirmar o no su asistencia., aunque en este caso quizás sea venirnos muy arriba.
Pero en esta primera fase en la que estamos en exploración de mercado y validación de la idea, y dado que la cuesta de Enero llega casi hasta Junio, es hora de reducir costes y empezar a escuchar a los usuarios lo antes posible, así que atacaremos a los clientes con el medio de comunicación más tradicional y efectivo … el email 📩
Para ellocrearemos un endpoint donde enviar la data ( /wp-json/deliveria/v1/notify ) y que el servidor pueda enviar los emails

Con esa notificación podremos jugar, por ejemplo haciendo que el botón de «No voy a estar en casa» notifique en un endpoint, y así se pueda reajustar la ruta, o que sea un formulario indicando cuando voy a estar y así reajustar ruta, o un simple tel: y que nos llame directamente (ya que a veces aunque no esté alguien hay opciones de entrega a vecinos y demás). Aquí la imaginación al poder 🚀
Y con esto tendríamos una primera versión MVP base sobre la que ir iterando, validando con clientes, tanto los repartidores, como los usuarios finales. Permitiéndonos ahorrar tiempo ⏱️, dinero 💰 y frustraciones 😓



