modularizado de prompts
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
formatOptionsForDisplay,
|
||||
} from "./orderModel.js";
|
||||
import { handleRecommend } from "./recommendations.js";
|
||||
import { getProductQtyRules } from "../0-ui/db/repo.js";
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Utilidades
|
||||
@@ -356,6 +357,62 @@ export async function handleCartState({ tenantId, text, nlu, order, audit, fromI
|
||||
};
|
||||
}
|
||||
if (nextPending.status === PendingStatus.NEEDS_QUANTITY) {
|
||||
// Detectar "para X personas" en el texto original ANTES de preguntar cantidad
|
||||
const personasMatch = /(?:para\s+)?(\d+)\s*(personas?|comensales?|invitados?)/i.exec(text || "") ||
|
||||
/\bpara\s+(\d+)\b/i.exec(text || "") ||
|
||||
/\bcomo\s+para\s+(\d+)\b/i.exec(text || "");
|
||||
|
||||
if (personasMatch && nextPending.selected_woo_id) {
|
||||
const peopleCount = parseInt(personasMatch[1], 10);
|
||||
|
||||
if (peopleCount > 0 && peopleCount <= 100) {
|
||||
// Buscar reglas de cantidad por persona para este producto
|
||||
let qtyRules = [];
|
||||
try {
|
||||
qtyRules = await getProductQtyRules({ tenantId, wooProductId: nextPending.selected_woo_id });
|
||||
} catch (e) {
|
||||
audit.qty_rules_error = e?.message;
|
||||
}
|
||||
|
||||
// Calcular cantidad recomendada
|
||||
let calculatedQty;
|
||||
let calculatedUnit = nextPending.selected_unit || "kg";
|
||||
const rule = qtyRules[0];
|
||||
|
||||
if (rule && rule.qty_per_person > 0) {
|
||||
calculatedQty = rule.qty_per_person * peopleCount;
|
||||
calculatedUnit = rule.unit || calculatedUnit;
|
||||
audit.qty_from_rule = { rule_id: rule.id, qty_per_person: rule.qty_per_person, people: peopleCount };
|
||||
} else {
|
||||
// Fallback: 0.3 kg por persona para carnes
|
||||
calculatedQty = 0.3 * peopleCount;
|
||||
audit.qty_fallback = { default_per_person: 0.3, people: peopleCount };
|
||||
}
|
||||
|
||||
// Actualizar el pending item y mover al cart
|
||||
const updatedOrder = updatePendingItem(currentOrder, nextPending.id, {
|
||||
qty: calculatedQty,
|
||||
unit: calculatedUnit,
|
||||
status: PendingStatus.READY,
|
||||
});
|
||||
|
||||
const finalOrder = moveReadyToCart(updatedOrder);
|
||||
const qtyStr = calculatedUnit === "unit" ? calculatedQty : `${calculatedQty}${calculatedUnit}`;
|
||||
|
||||
return {
|
||||
plan: {
|
||||
reply: `Para ${peopleCount} personas, te recomiendo ${qtyStr} de ${nextPending.selected_name}. Ya lo anoté. ¿Algo más?`,
|
||||
next_state: ConversationState.CART,
|
||||
intent: "add_to_cart",
|
||||
missing_fields: [],
|
||||
order_action: "add_to_cart",
|
||||
},
|
||||
decision: { actions: [{ type: "add_to_cart" }], order: finalOrder, audit },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Si no hay "para X personas", preguntar cantidad normalmente
|
||||
const unitQuestion = unitAskFor(nextPending.selected_unit || "kg");
|
||||
return {
|
||||
plan: {
|
||||
@@ -840,6 +897,73 @@ async function processPendingClarification({ tenantId, text, nlu, order, pending
|
||||
};
|
||||
}
|
||||
|
||||
// Detectar "para X personas" y calcular cantidad automáticamente
|
||||
const personasMatch = /(?:para\s+)?(\d+)\s*(personas?|comensales?|invitados?)/i.exec(text || "") ||
|
||||
/\bpara\s+(\d+)\b/i.exec(text || "") ||
|
||||
/\bcomo\s+para\s+(\d+)\b/i.exec(text || "");
|
||||
|
||||
if (personasMatch && pendingItem.selected_woo_id) {
|
||||
const peopleCount = parseInt(personasMatch[1], 10);
|
||||
|
||||
if (peopleCount > 0 && peopleCount <= 100) {
|
||||
// Buscar reglas de cantidad por persona para este producto
|
||||
let qtyRules = [];
|
||||
try {
|
||||
qtyRules = await getProductQtyRules({ tenantId, wooProductId: pendingItem.selected_woo_id });
|
||||
} catch (e) {
|
||||
audit.qty_rules_error = e?.message;
|
||||
}
|
||||
|
||||
// Buscar regla para evento "asado" o genérica (null)
|
||||
const rule = qtyRules.find(r => r.event_type === "asado" && r.person_type === "adult") ||
|
||||
qtyRules.find(r => r.event_type === null && r.person_type === "adult") ||
|
||||
qtyRules.find(r => r.person_type === "adult") ||
|
||||
qtyRules[0];
|
||||
|
||||
let calculatedQty;
|
||||
let calculatedUnit = pendingItem.selected_unit || "kg";
|
||||
|
||||
if (rule && rule.qty_per_person > 0) {
|
||||
// Usar regla de BD
|
||||
calculatedQty = rule.qty_per_person * peopleCount;
|
||||
calculatedUnit = rule.unit || calculatedUnit;
|
||||
audit.qty_rule_used = { rule_id: rule.id, qty_per_person: rule.qty_per_person, unit: rule.unit };
|
||||
} else {
|
||||
// Fallback: 300g por persona para productos por peso
|
||||
const fallbackPerPerson = calculatedUnit === "unit" ? 1 : 0.3;
|
||||
calculatedQty = fallbackPerPerson * peopleCount;
|
||||
audit.qty_fallback_used = { per_person: fallbackPerPerson, unit: calculatedUnit };
|
||||
}
|
||||
|
||||
// Redondear a 1 decimal para kg, entero para unidades
|
||||
if (calculatedUnit === "unit") {
|
||||
calculatedQty = Math.ceil(calculatedQty);
|
||||
} else {
|
||||
calculatedQty = Math.round(calculatedQty * 10) / 10;
|
||||
}
|
||||
|
||||
const updatedOrder = updatePendingItem(order, pendingItem.id, {
|
||||
qty: calculatedQty,
|
||||
unit: calculatedUnit,
|
||||
status: PendingStatus.READY,
|
||||
});
|
||||
|
||||
const finalOrder = moveReadyToCart(updatedOrder);
|
||||
const qtyStr = calculatedUnit === "unit" ? calculatedQty : `${calculatedQty}${calculatedUnit}`;
|
||||
|
||||
return {
|
||||
plan: {
|
||||
reply: `Para ${peopleCount} personas, te recomiendo ${qtyStr} de ${pendingItem.selected_name}. Ya lo anoté. ¿Algo más?`,
|
||||
next_state: ConversationState.CART,
|
||||
intent: "add_to_cart",
|
||||
missing_fields: [],
|
||||
order_action: "add_to_cart",
|
||||
},
|
||||
decision: { actions: [{ type: "add_to_cart" }], order: finalOrder, audit },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// No entendió cantidad
|
||||
const unitQuestion = unitAskFor(pendingItem.selected_unit || "kg");
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user