corregidos bugs de: ret, vs delivery, efectivo vs link, charsets, price query

This commit is contained in:
Lucas Tettamanti
2026-01-26 23:27:47 -03:00
parent 53293ce9b3
commit 493f26af17
13 changed files with 757 additions and 193 deletions

View File

@@ -333,13 +333,103 @@ async function handleRecommendIntent({ tenantId, text, nlu, currentOrder, audit
return null;
}
/**
* Detecta si el usuario pregunta por el total del carrito actual
*/
function isCartTotalQuery(nlu) {
const query = nlu?.entities?.product_query || "";
const q = query.trim().toLowerCase();
// Patrones que indican consulta sobre el carrito actual
const cartKeywords = [
"todo", "el total", "total", "mi pedido", "el pedido", "precio total",
"lo que tengo", "lo que llevo", "lo que estoy pidiendo", "lo que pedí",
"en el carrito", "del carrito", "mi carrito", "el carrito",
"lo que voy", "hasta ahora", "hasta el momento",
];
// Si la query contiene alguna de estas frases, es consulta del carrito
if (cartKeywords.some(kw => q.includes(kw))) {
return true;
}
// Patrones regex adicionales
const patterns = [
/^cu[aá]nto (es|sale|cuesta|est[aá])/i,
/^precio/i,
];
return patterns.some(p => p.test(q));
}
/**
* Maneja price_query
*/
async function handlePriceQuery({ tenantId, nlu, currentOrder, audit }) {
// Si pregunta por el total del carrito
if (isCartTotalQuery(nlu) || !nlu?.entities?.product_query) {
const cartItems = currentOrder?.cart || [];
if (cartItems.length === 0) {
return {
plan: {
reply: "Tu carrito está vacío. ¿Qué te gustaría agregar?",
next_state: ConversationState.CART,
intent: "price_query",
missing_fields: [],
order_action: "none",
},
decision: { actions: [], order: currentOrder, audit },
};
}
// Calcular y mostrar el total
let total = 0;
const lines = cartItems.map(item => {
const itemTotal = (item.price || 0) * (item.qty || 0);
total += itemTotal;
const unitStr = item.unit === "unit" ? "" : " kg";
return `${item.name}: ${item.qty}${unitStr} x $${item.price} = $${itemTotal.toLocaleString("es-AR")}`;
});
const reply = `Tu pedido actual:\n\n${lines.join("\n")}\n\n💰 Total: $${total.toLocaleString("es-AR")}\n\n¿Algo más?`;
return {
plan: {
reply,
next_state: ConversationState.CART,
intent: "price_query",
missing_fields: [],
order_action: "none",
},
decision: { actions: [], order: currentOrder, audit },
};
}
const productQueries = extractProductQueries(nlu);
if (productQueries.length === 0) {
// Si no hay query pero hay carrito, mostrar el carrito
const cartItems = currentOrder?.cart || [];
if (cartItems.length > 0) {
let total = 0;
const lines = cartItems.map(item => {
const itemTotal = (item.price || 0) * (item.qty || 0);
total += itemTotal;
const unitStr = item.unit === "unit" ? "" : " kg";
return `${item.name}: ${item.qty}${unitStr} x $${item.price} = $${itemTotal.toLocaleString("es-AR")}`;
});
const reply = `Tu pedido actual:\n\n${lines.join("\n")}\n\n💰 Total: $${total.toLocaleString("es-AR")}\n\n¿Querés saber el precio de algún producto específico?`;
return {
plan: {
reply,
next_state: ConversationState.CART,
intent: "price_query",
missing_fields: [],
order_action: "none",
},
decision: { actions: [], order: currentOrder, audit },
};
}
return {
plan: {
reply: "¿De qué producto querés saber el precio?",

View File

@@ -354,7 +354,7 @@ function processStrongMatch({ updatedOrder, pendingItem, best, audit }) {
const displayUnit = normalizeUnit(best.sell_unit) || inferDefaultUnit({ name: best.name, categories: best.categories });
const hasQty = pendingItem.requested_qty != null && pendingItem.requested_qty > 0;
const needsQuantity = displayUnit !== "unit" && !hasQty;
const autoSelectedOrder = updatePendingItem(updatedOrder, pendingItem.id, {
selected_woo_id: best.woo_product_id,
selected_name: best.name,

View File

@@ -25,25 +25,17 @@ function isDeliveryInfoQuestion(text) {
}
/**
* Formatea los días de entrega para mostrar
* Detecta si el usuario pregunta por horarios de retiro
*/
function formatDeliveryDays(daysStr) {
if (!daysStr) return null;
const dayMap = {
"lun": "Lunes", "mar": "Martes", "mie": "Miércoles", "mié": "Miércoles",
"jue": "Jueves", "vie": "Viernes", "sab": "Sábado", "sáb": "Sábado", "dom": "Domingo",
};
const days = daysStr.split(",").map(d => d.trim().toLowerCase());
const formatted = days.map(d => dayMap[d] || d).filter(Boolean);
if (formatted.length === 0) return null;
if (formatted.length === 1) return formatted[0];
// "Lunes, Martes, Miércoles y Jueves"
const last = formatted.pop();
return `${formatted.join(", ")} y ${last}`;
function isPickupInfoQuestion(text) {
const t = String(text || "").toLowerCase();
const patterns = [
/horario.*(retir|buscar|pasar)/i,
/cu[aá]ndo.*(retir|buscar|pasar)/i,
/a\s+qu[eé]\s+hora.*(retir|buscar)/i,
/d[ií]as?.*(retir|buscar)/i,
];
return patterns.some(p => p.test(t));
}
/**
@@ -71,19 +63,17 @@ export async function handleWaitingState({ tenantId, text, nlu, order, audit, st
// Preguntas sobre horarios/días de entrega
if (isDeliveryInfoQuestion(text)) {
const deliveryDays = formatDeliveryDays(storeConfig.delivery_days);
const startHour = storeConfig.delivery_hours_start?.slice(0, 5);
const endHour = storeConfig.delivery_hours_end?.slice(0, 5);
// Usar deliveryHours que ya viene formateado desde getStoreConfig
// (agrupa días con mismos horarios: "Lunes a Viernes de 9:00 a 14:00, Sábado de 9:00 a 13:00")
const deliveryHours = storeConfig.deliveryHours;
let reply = "";
if (deliveryDays) {
reply = `Hacemos entregas los días ${deliveryDays}`;
if (startHour && endHour) {
reply += ` de ${startHour} a ${endHour}`;
}
reply += ". ";
if (deliveryHours && deliveryHours !== "No disponible") {
reply = `Hacemos entregas: ${deliveryHours}. `;
} else if (storeConfig.deliveryEnabled === false) {
reply = "Por el momento no ofrecemos delivery. ";
} else {
reply = "Todavía no tengo configurado los días de entrega. ";
reply = "Todavía no tengo configurados los horarios de entrega. ";
}
reply += "Tu pedido ya está en proceso, avisame cualquier cosa.";
@@ -100,6 +90,33 @@ export async function handleWaitingState({ tenantId, text, nlu, order, audit, st
};
}
// Preguntas sobre horarios de retiro
if (isPickupInfoQuestion(text)) {
const pickupHours = storeConfig.pickupHours;
let reply = "";
if (pickupHours && pickupHours !== "No disponible") {
reply = `Podés retirar: ${pickupHours}. `;
} else if (storeConfig.pickupEnabled === false) {
reply = "Por el momento no ofrecemos retiro en tienda. ";
} else {
reply = "Todavía no tengo configurados los horarios de retiro. ";
}
reply += "Tu pedido ya está en proceso, avisame cualquier cosa.";
return {
plan: {
reply,
next_state: ConversationState.WAITING_WEBHOOKS,
intent: "pickup_info",
missing_fields: [],
order_action: "none",
},
decision: { actions: [], order: currentOrder, audit },
};
}
// Default
const reply = currentOrder.payment_type === "link"
? "Tu pedido está en proceso. Avisame si necesitás algo más o esperá la confirmación de pago."