From e5c8956b641dcdc69942bd224f788eb4ef4a6d06 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 Jun 2026 23:11:46 -0400 Subject: [PATCH] Configuracion para guardar en base de datos --- mvp/Whatsapp-bot/src/claude/claude.service.ts | 21 ++++++- mvp/Whatsapp-bot/src/leads/leads.service.ts | 61 +++++++++++++++++-- .../src/whatsapp/whatsapp.service.ts | 27 ++++---- 3 files changed, 86 insertions(+), 23 deletions(-) diff --git a/mvp/Whatsapp-bot/src/claude/claude.service.ts b/mvp/Whatsapp-bot/src/claude/claude.service.ts index 9cfcd5c..6b03e07 100644 --- a/mvp/Whatsapp-bot/src/claude/claude.service.ts +++ b/mvp/Whatsapp-bot/src/claude/claude.service.ts @@ -332,14 +332,14 @@ Reglas para valor_extraido: } const valoresPermitidos = this.leadsService.getValoresPermitidos(estado); - const valor = clasificacion.valor_extraido?.trim().toLowerCase(); + const valor = this.normalizarTexto(clasificacion.valor_extraido ?? ''); if (!valor) { return { valido: false, valorNormalizado: null }; } const coincide = valoresPermitidos.some( - (v) => v === valor || valor.includes(v), + (v) => v === valor || valor.includes(v) || v.includes(valor), ); if (!coincide) { @@ -347,11 +347,21 @@ Reglas para valor_extraido: } const valorNormalizado = - valoresPermitidos.find((v) => v === valor || valor.includes(v)) ?? valor; + valoresPermitidos.find( + (v) => v === valor || valor.includes(v) || v.includes(valor), + ) ?? valor; return { valido: true, valorNormalizado }; } + private normalizarTexto(valor: string): string { + return valor + .trim() + .toLowerCase() + .normalize('NFD') + .replace(/\p{Diacritic}/gu, ''); + } + private claveReintento(leadId: number, estado: string): string { return `${leadId}:${estado}`; } @@ -603,6 +613,11 @@ Devuelve SOLO el mensaje final listo para enviar, sin explicaciones ni JSON. const campo = this.leadsService.getCampoParaEstado(estadoFlujo); if (campo) { entidad = { [campo]: validacion.valorNormalizado }; + } else if ( + estadoFlujo === 'apertura' && + clasificacion.valor_extraido?.trim() + ) { + entidad = { nombre: clasificacion.valor_extraido.trim() }; } } diff --git a/mvp/Whatsapp-bot/src/leads/leads.service.ts b/mvp/Whatsapp-bot/src/leads/leads.service.ts index 13baf9e..66b37df 100644 --- a/mvp/Whatsapp-bot/src/leads/leads.service.ts +++ b/mvp/Whatsapp-bot/src/leads/leads.service.ts @@ -116,8 +116,11 @@ export class LeadsService { } async updateEstado(lead: Lead, estado: EstadoLead | string): Promise { - lead.estado_actual = estado as EstadoLead; - return this.leadRepo.save(lead); + await this.leadRepo.update(lead.id, { + estado_actual: estado as EstadoLead, + }); + this.logger.log(`Lead id=${lead.id} estado_actual=${estado}`); + return this.leadRepo.findOne({ where: { id: lead.id } }); } /** @@ -125,14 +128,62 @@ export class LeadsService { * Solo actualiza los campos que se pasan en el partial. */ async updateDatos(leadId: number, datos: Partial): Promise { + const campos = Object.keys(datos).filter( + (k) => datos[k as keyof Lead] !== undefined, + ); + if (campos.length === 0) { + return this.leadRepo.findOne({ where: { id: leadId } }); + } + await this.leadRepo.update(leadId, datos); + this.logger.log( + `Lead id=${leadId} datos guardados: ${JSON.stringify(datos)}`, + ); return this.leadRepo.findOne({ where: { id: leadId } }); } async marcarViable(lead: Lead, viable: boolean): Promise { - lead.viable = viable; - lead.estado_actual = viable ? 'completado' : 'no_viable'; - return this.leadRepo.save(lead); + const estado = viable ? 'completado' : 'no_viable'; + await this.leadRepo.update(lead.id, { viable, estado_actual: estado }); + this.logger.log(`Lead id=${lead.id} viable=${viable}, estado=${estado}`); + return this.leadRepo.findOne({ where: { id: lead.id } }); + } + + /** + * Persiste datos del lead y cambio de estado en una sola operacion. + */ + async persistirTurno( + leadId: number, + datos: Partial, + options?: { nuevoEstado?: string; viable?: boolean }, + ): Promise { + const patch: Partial = { ...datos }; + + if (options?.nuevoEstado === 'fin_viable') { + patch.viable = true; + patch.estado_actual = 'completado'; + } else if (options?.nuevoEstado === 'fin_no_viable') { + patch.viable = false; + patch.estado_actual = 'no_viable'; + } else if (options?.nuevoEstado) { + patch.estado_actual = options.nuevoEstado as EstadoLead; + } else if (options?.viable !== undefined && options?.viable !== null) { + patch.viable = options.viable; + patch.estado_actual = options.viable ? 'completado' : 'no_viable'; + } + + const campos = Object.keys(patch).filter( + (k) => patch[k as keyof Lead] !== undefined, + ); + if (campos.length === 0) { + return this.leadRepo.findOne({ where: { id: leadId } }); + } + + await this.leadRepo.update(leadId, patch); + this.logger.log( + `Lead id=${leadId} persistido: ${JSON.stringify(patch)}`, + ); + return this.leadRepo.findOne({ where: { id: leadId } }); } /** diff --git a/mvp/Whatsapp-bot/src/whatsapp/whatsapp.service.ts b/mvp/Whatsapp-bot/src/whatsapp/whatsapp.service.ts index 04ba64e..d833cbe 100644 --- a/mvp/Whatsapp-bot/src/whatsapp/whatsapp.service.ts +++ b/mvp/Whatsapp-bot/src/whatsapp/whatsapp.service.ts @@ -190,7 +190,7 @@ export class WhatsappService implements OnModuleInit, OnModuleDestroy { const telefono = jid.split("@")[0]; try { - const lead = await this.leadsService.findOrCreate(telefono); + let lead = await this.leadsService.findOrCreate(telefono); if (ESTADOS_TERMINALES.includes(lead.estado_actual)) { this.logger.log( @@ -299,21 +299,18 @@ export class WhatsappService implements OnModuleInit, OnModuleDestroy { this.logger.log(`LUISA [${telefono}]: ${respuesta}`); - if (entidad && Object.keys(entidad).length > 0) { - await this.leadsService.updateDatos(lead.id, entidad); - } - - if (nuevoEstado === "fin_viable" || nuevoEstado === "fin_no_viable") { - await this.leadsService.marcarViable( - lead, - nuevoEstado === "fin_viable", + if ( + (entidad && Object.keys(entidad).length > 0) || + nuevoEstado || + (viable !== undefined && viable !== null) + ) { + lead = await this.leadsService.persistirTurno(lead.id, entidad ?? {}, { + nuevoEstado, + viable, + }); + this.logger.log( + `Lead id=${lead.id} en DB — estado=${lead.estado_actual}, espacio=${lead.espacio ?? "-"}, rango_m2=${lead.rango_m2 ?? "-"}, estilo=${lead.estilo ?? "-"}, urgencia=${lead.urgencia ?? "-"}, presupuesto=${lead.presupuesto_declarado ?? "-"}`, ); - this.logger.log(`Lead id=${lead.id} finalizado como ${nuevoEstado}`); - } else if (nuevoEstado) { - await this.leadsService.updateEstado(lead, nuevoEstado); - this.logger.log(`Lead id=${lead.id} avanzado a estado=${nuevoEstado}`); - } else if (viable !== undefined && viable !== null) { - await this.leadsService.marcarViable(lead, viable); } await this.conversacionService.guardarMensaje(