Añade estructura aditiva del flujo WhatsApp/llamada + workers (DB única)

Integra el esquema "reformix-full" del equipo de forma ADITIVA, sin tocar los
enums ni columnas existentes de la app (una sola DB, Drizzle es el dueño):
- Enums nuevos: estado_wa, canal_contacto, canal_origen, resultado_contacto,
  rol_mensaje, job_tipo, job_estado, nivel_calificacion, visita_estado.
- Tablas nuevas: conversacion_whatsapp, intentos_contacto, lead_calificacion
  (score 0-100 + nivel A/B/C/D), visitas, worker_jobs (cola async de los
  workers de fotos/render/presupuesto). Referencian nuestros leads/users/tenants.
- Columnas nuevas en leads (nullable, las rellena el bot/Luisa): estado_wa,
  canal_origen, espacio, rango_m2, estilo, presupuesto_declarado, viable,
  fotos_solicitadas_at.
- Migración 0010 + db-schema/schema.sql regenerado.

El bot/n8n escribe estas tablas en la DB única y usa nuestros leads (creados
solo desde el form web). Pendiente: alinear valores de lead_estado/pipeline_stage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Carlos Narro
2026-06-04 16:45:14 +02:00
parent cd38fe6233
commit f2b19ab719
5 changed files with 2813 additions and 1 deletions

View File

@@ -0,0 +1,96 @@
CREATE TYPE "public"."canal_contacto" AS ENUM('formulario', 'whatsapp', 'llamada');--> statement-breakpoint
CREATE TYPE "public"."canal_origen" AS ENUM('formulario_web', 'whatsapp', 'llamada', 'referido', 'anuncio');--> statement-breakpoint
CREATE TYPE "public"."estado_wa" AS ENUM('sin_enviar', 'enviado', 'entregado', 'leido', 'fallido');--> statement-breakpoint
CREATE TYPE "public"."job_estado" AS ENUM('pendiente', 'procesando', 'completado', 'error');--> statement-breakpoint
CREATE TYPE "public"."job_tipo" AS ENUM('analisis_fotos', 'render', 'presupuesto_ia');--> statement-breakpoint
CREATE TYPE "public"."nivel_calificacion" AS ENUM('A', 'B', 'C', 'D');--> statement-breakpoint
CREATE TYPE "public"."resultado_contacto" AS ENUM('exitoso', 'no_contesta', 'ocupado', 'rechaza', 'error_tecnico');--> statement-breakpoint
CREATE TYPE "public"."rol_mensaje" AS ENUM('user', 'assistant', 'system');--> statement-breakpoint
CREATE TYPE "public"."visita_estado" AS ENUM('propuesta', 'confirmada', 'realizada', 'cancelada', 'reprogramada');--> statement-breakpoint
CREATE TABLE "conversacion_whatsapp" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"lead_id" uuid NOT NULL,
"rol" "rol_mensaje" NOT NULL,
"mensaje" text NOT NULL,
"media_type" text,
"media_url" text,
"transcripcion_audio" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "intentos_contacto" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"lead_id" uuid NOT NULL,
"canal" "canal_contacto" NOT NULL,
"resultado" "resultado_contacto",
"completado" boolean DEFAULT false NOT NULL,
"numero_intento" integer NOT NULL,
"duracion_seg" integer,
"intentado_at" timestamp with time zone DEFAULT now() NOT NULL,
"notas" text,
"metadata" jsonb
);
--> statement-breakpoint
CREATE TABLE "lead_calificacion" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"lead_id" uuid NOT NULL,
"score" integer,
"nivel" "nivel_calificacion",
"criterios" jsonb,
"notas_agente" text,
"calificado_por" uuid,
"calificado_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "lead_calificacion_lead_id_unique" UNIQUE("lead_id"),
CONSTRAINT "lead_calificacion_score_check" CHECK ("lead_calificacion"."score" >= 0 AND "lead_calificacion"."score" <= 100)
);
--> statement-breakpoint
CREATE TABLE "visitas" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"lead_id" uuid NOT NULL,
"tenant_id" uuid NOT NULL,
"fecha_propuesta" timestamp with time zone,
"fecha_confirmada" timestamp with time zone,
"estado" "visita_estado" DEFAULT 'propuesta' NOT NULL,
"direccion" text,
"notas" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "worker_jobs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"lead_id" uuid NOT NULL,
"tipo" "job_tipo" NOT NULL,
"estado_job" "job_estado" DEFAULT 'pendiente' NOT NULL,
"payload" jsonb NOT NULL,
"webhook_url" text,
"resultado_url" text,
"intentos" integer DEFAULT 0 NOT NULL,
"error_msg" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"completed_at" timestamp with time zone
);
--> statement-breakpoint
ALTER TABLE "leads" ADD COLUMN "estado_wa" "estado_wa";--> statement-breakpoint
ALTER TABLE "leads" ADD COLUMN "canal_origen" "canal_origen";--> statement-breakpoint
ALTER TABLE "leads" ADD COLUMN "espacio" text;--> statement-breakpoint
ALTER TABLE "leads" ADD COLUMN "rango_m2" text;--> statement-breakpoint
ALTER TABLE "leads" ADD COLUMN "estilo" text;--> statement-breakpoint
ALTER TABLE "leads" ADD COLUMN "presupuesto_declarado" text;--> statement-breakpoint
ALTER TABLE "leads" ADD COLUMN "viable" boolean;--> statement-breakpoint
ALTER TABLE "leads" ADD COLUMN "fotos_solicitadas_at" timestamp with time zone;--> statement-breakpoint
ALTER TABLE "conversacion_whatsapp" ADD CONSTRAINT "conversacion_whatsapp_lead_id_leads_id_fk" FOREIGN KEY ("lead_id") REFERENCES "public"."leads"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "intentos_contacto" ADD CONSTRAINT "intentos_contacto_lead_id_leads_id_fk" FOREIGN KEY ("lead_id") REFERENCES "public"."leads"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "lead_calificacion" ADD CONSTRAINT "lead_calificacion_lead_id_leads_id_fk" FOREIGN KEY ("lead_id") REFERENCES "public"."leads"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "lead_calificacion" ADD CONSTRAINT "lead_calificacion_calificado_por_users_id_fk" FOREIGN KEY ("calificado_por") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "visitas" ADD CONSTRAINT "visitas_lead_id_leads_id_fk" FOREIGN KEY ("lead_id") REFERENCES "public"."leads"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "visitas" ADD CONSTRAINT "visitas_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "worker_jobs" ADD CONSTRAINT "worker_jobs_lead_id_leads_id_fk" FOREIGN KEY ("lead_id") REFERENCES "public"."leads"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_conversacion_whatsapp_lead_id" ON "conversacion_whatsapp" USING btree ("lead_id");--> statement-breakpoint
CREATE INDEX "idx_conversacion_whatsapp_created_at" ON "conversacion_whatsapp" USING btree ("created_at");--> statement-breakpoint
CREATE INDEX "idx_intentos_contacto_lead_id" ON "intentos_contacto" USING btree ("lead_id");--> statement-breakpoint
CREATE INDEX "idx_lead_calificacion_lead_id" ON "lead_calificacion" USING btree ("lead_id");--> statement-breakpoint
CREATE INDEX "idx_visitas_lead_id" ON "visitas" USING btree ("lead_id");--> statement-breakpoint
CREATE INDEX "idx_visitas_tenant_id" ON "visitas" USING btree ("tenant_id");--> statement-breakpoint
CREATE INDEX "idx_worker_jobs_lead_id" ON "worker_jobs" USING btree ("lead_id");--> statement-breakpoint
CREATE INDEX "idx_worker_jobs_estado" ON "worker_jobs" USING btree ("estado_job");--> statement-breakpoint
CREATE INDEX "idx_worker_jobs_tipo" ON "worker_jobs" USING btree ("tipo");