Zonas de delivery por polígono + horarios + location share por WhatsApp
Schema delivery_zones JSONB pasa a { zones: [{ id, name, polygon (GeoJSON),
delivery_cost, delivery_days, delivery_hours, min_order_amount, enabled }] }.
Tirado el modelo legacy de 48 barrios CABA hardcoded.
Backend:
- lib/geo.js: pointInPolygon + findZoneForPoint (ray casting, sin deps) + 9 tests.
- storeContext.checkAddressInZone ahora valida con lat/lng (necesita ubicación
del cliente; no geocodifica texto). buildZonesForLLM expone zonas resumidas
para el agente. summarizeDeliveryZones genera prosa con costo+días+horas.
- settingsRepo expone delivery_zones (bug pre-existente: nunca se devolvía).
- pipeline: inboundLocation ⇒ persistir order.pending_location; orderModel
acepta pending_location, matched_zone, delivery_window.
Intake:
- evolutionParser detecta locationMessage/liveLocationMessage (Baileys).
- evolution + sim handlers propagan inboundLocation al pipeline.
Agent (DeepSeek tool-calling):
- workingMemory inyecta store.delivery.zones[], store.pickup.schedule,
order.pending_location/matched_zone/delivery_window.
- setAddress: matchea zona con la ubicación pendiente; sin location devuelve
need_location y el LLM le pide el pin al cliente.
- setShipping: para delivery, indica requires_location si faltan coords.
- confirmOrder: valida día+hora contra zone.delivery_days/hours o pickup.schedule.
- nueva tool set_delivery_window(day, time?) para registrar el slot pedido.
- systemPrompt agrega instrucciones de envío/zonas + flujo location share.
Frontend:
- zone-map-editor: web component (light DOM) que carga Leaflet 1.9 +
leaflet-geoman lazy desde CDN y permite dibujar/editar polígonos sobre OSM.
API zones get/set, eventos change/select, paleta tomada de --chart-*.
- settings-crud: borrada lista CABA_BARRIOS, nueva UI con mapa al lado y
formulario por zona seleccionada (nombre, costo, días, horario start/end,
mínimo, habilitada). Save serializa al schema nuevo.
Smoke E2E manual:
- "1kg vacío + envío" → bot pide pin → location en Centro → matched_zone
$1.500, lun-sab 10-20h → "martes 12hs" → confirma orden con total + envío.
- Location en Palermo Test → mar/jue 11-19h respetado.
- Location fuera de zonas → "no llegamos a esa zona" + lista de zonas válidas.
- Domingo en Centro → rechazado con días disponibles.
157/157 tests verde.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,11 +3,18 @@ import { processMessage } from "../../2-identity/services/pipeline.js";
|
||||
import { getTenantId } from "../../shared/tenant.js";
|
||||
|
||||
export async function handleSimSend(body) {
|
||||
const { chat_id, from_phone, text } = body || {};
|
||||
if (!chat_id || !from_phone || !text) {
|
||||
return { status: 400, payload: { ok: false, error: "chat_id, from_phone, text are required" } };
|
||||
const { chat_id, from_phone, text, location } = body || {};
|
||||
if (!chat_id || !from_phone || (!text && !location)) {
|
||||
return { status: 400, payload: { ok: false, error: "chat_id, from_phone, and text or location are required" } };
|
||||
}
|
||||
|
||||
// Aceptar location share desde el simulator. Mismo formato que el parser de
|
||||
// Evolution: { lat, lng, label? }.
|
||||
const inboundLocation =
|
||||
location && typeof location.lat === "number" && typeof location.lng === "number"
|
||||
? { lat: location.lat, lng: location.lng, label: location.label || null }
|
||||
: null;
|
||||
|
||||
const provider = "sim";
|
||||
const message_id = crypto.randomUUID();
|
||||
const tenantId = getTenantId();
|
||||
@@ -16,7 +23,8 @@ export async function handleSimSend(body) {
|
||||
tenantId,
|
||||
chat_id,
|
||||
from: from_phone,
|
||||
text,
|
||||
text: text || "",
|
||||
inboundLocation,
|
||||
provider,
|
||||
message_id,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user