235 lines
10 KiB
JavaScript
235 lines
10 KiB
JavaScript
const axios = require('axios')
|
|
const flatten = require('flat')
|
|
const CustomError = require('../errors')
|
|
const CRM_Contact = require('../models/CRM_Contact')
|
|
const CRM = require('../models/CRM')
|
|
|
|
const { URL } = require('url')
|
|
const { getAccessToken } = require('./oauth2')
|
|
const findProperty = require('./findProperty')
|
|
const requestConfigHeader = require('./requestConfigHeader')
|
|
const sendMessageSocket = require('./sendMessageSocket')
|
|
const CRM_Ticket = require('../models/CRM_Ticket')
|
|
|
|
/**
|
|
* @description Realiza a busca de um contato no CRM externo pelo e-mail.
|
|
* Se o contato for encontrado, retorna suas informações.
|
|
* Também lida com a lógica de cache e de tratamento de duplicatas (ex: Hubspot).
|
|
*
|
|
* @param {object} rest - Configurações REST do CRM.
|
|
* @param {object} authentication - Detalhes de autenticação do CRM.
|
|
* @param {string} crmEmail - O endereço de e-mail do contato a ser procurado.
|
|
* @param {string} companyId - O ID da empresa.
|
|
* @param {object} [test={}] - Objeto para controle de testes/mensagens de socket.
|
|
* @param {boolean} [cacheContact=false] - Indica se deve verificar o cache local antes da API.
|
|
* @returns {Promise<object>} Um objeto com { exist: boolean, contactId: string, email: string, name: string, accountId: string } ou { exist: false }.
|
|
*/
|
|
async function lookupContactByEmail(rest, authentication, crmEmail, companyId, test = {}, cacheContact = false) {
|
|
// Acessa a configuração específica para busca de contato por e-mail
|
|
let { request, body, response } = findProperty(rest, 'lookupContactByEmail')
|
|
|
|
if (!request || !response) {
|
|
console.error('Configuração de "lookupContactByEmail" ausente ou incompleta no CRM REST properties.');
|
|
return { exist: false };
|
|
}
|
|
|
|
let { requestContentType, requestEncoding, requestType, url } = request
|
|
|
|
const { type, userName, passWord, token, crmClientId, crmAccountId } = authentication
|
|
|
|
// 1. Tenta buscar o contato no cache local (CRM_Contact) pelo e-mail
|
|
if (cacheContact) {
|
|
const crmInfo = await CRM_Contact.findOne({ companyId, crmBaseURL: new URL(url).hostname, email: crmEmail })
|
|
|
|
if (crmInfo) {
|
|
console.log(`[${new Date()}] Contato encontrado em cache por e-mail: ${crmEmail}`);
|
|
return { exist: true, contactId: crmInfo.contactId, email: crmEmail }
|
|
}
|
|
}
|
|
|
|
// 2. Prepara a configuração da requisição HTTP para a API do CRM
|
|
// A 'requestConfigHeader' precisa ser capaz de lidar com a busca por e-mail
|
|
const config = await requestConfigHeader(url, crmEmail, requestType, requestContentType, type, userName, passWord, token, crmClientId, '', '', companyId)
|
|
|
|
if (test?.testing) {
|
|
let msg = `Tentando checar se o contato de e-mail ${crmEmail} existe no crm`
|
|
sendMessageSocket({ companyId, status: 'processing', data: { request: config, msg } })
|
|
}
|
|
|
|
console.log("PAYLOAD CONFIG LOOKUP CONTACT BY EMAIL: ", JSON.stringify(config, null, 6))
|
|
|
|
let data
|
|
|
|
|
|
try {
|
|
let { data: _data } = await axios(config)
|
|
data = _data
|
|
} catch (error) {
|
|
if (error.response) {
|
|
console.error('==================> lookupContactByEmail Erro na resposta da API:', {
|
|
status: error.response.status,
|
|
data: error.response.data,
|
|
})
|
|
}
|
|
else if (error.request) {
|
|
console.error('==================> lookupContactByEmail Nenhuma resposta recebida da API:', error.request)
|
|
}
|
|
else {
|
|
console.error('==================> lookupContactByEmail Erro ao configurar a request:', error.message)
|
|
}
|
|
// throw error
|
|
}
|
|
|
|
console.log('CONTACT LOOKUP BY EMAIL DATA: ', JSON.stringify(data, null, 6))
|
|
|
|
// 3. Lógica específica para Hubspot (ou outros CRMs com tratamento especial de duplicatas)
|
|
// Se o CRM for Hubspot e retornar múltiplos contatos, ele tentará encontrar o mais relevante.
|
|
if (url.includes("hubapi") && data?.contacts && data.contacts.length > 1) {
|
|
// Assume que 'hs_full_name_or_email' pode ser usado para encontrar o contato "principal"
|
|
const auxContatWithName = data.contacts.find((c) => c.properties?.hs_full_name_or_email?.value?.trim().length > 2)
|
|
|
|
if (auxContatWithName) {
|
|
console.log(`[${new Date()}] ****** HUBSPOT CONTATO DUPLICADO POR EMAIL. CONTACTID ${auxContatWithName.vid} A SER CONSIDERADO ****** `)
|
|
|
|
data.contacts = [auxContatWithName] // Considera apenas este contato para o fluxo
|
|
|
|
// Lógica para limpar duplicatas no CRM_Contact local (se aplicável ao e-mail)
|
|
const contacts = await CRM_Contact.find({ companyId, crmBaseURL: new URL(url).hostname, email: crmEmail })
|
|
|
|
if (contacts && contacts.length > 1) {
|
|
for (const contact of contacts.slice(0, -1)) { // Deleta todos, exceto o último/mais relevante
|
|
await CRM_Ticket.deleteMany({ companyId, contact: contact })
|
|
await contact.deleteOne()
|
|
}
|
|
}
|
|
|
|
console.log(`[${new Date()}] dados do contato duplicado no crm (ajustado): `, data)
|
|
|
|
const updateResult = await CRM_Contact.updateOne(
|
|
{ companyId, crmBaseURL: new URL(url).hostname, email: crmEmail }, // Atualiza pelo e-mail
|
|
{
|
|
$set: { contactId: auxContatWithName.vid }
|
|
})
|
|
|
|
if (updateResult.modifiedCount > 0)
|
|
return { exist: true, contactId: auxContatWithName.vid }
|
|
}
|
|
}
|
|
|
|
// 4. Achata o objeto de resposta para facilitar a busca por propriedades
|
|
data = flatten(data)
|
|
|
|
let auxEmail // Variável para o e-mail encontrado na resposta
|
|
let auxContactId
|
|
let auxName
|
|
let auxAccountId
|
|
|
|
// 5. Itera sobre as propriedades do objeto 'data' para extrair e-mail e ID do contato
|
|
// Baseado nas configurações de 'response' do seu CRM REST (response.email e response.id)
|
|
for (const prop in data) {
|
|
const _prop = prop.replace(/^\d+\./, '').replace(/(?:^|\.)\d+\b/g, '')
|
|
|
|
if (_prop == response?.email?.trim()) { // Procura pelo campo de e-mail na resposta
|
|
auxEmail = data[prop]
|
|
}
|
|
|
|
if (_prop == response?.id?.trim()) {
|
|
auxContactId = data[prop]
|
|
}
|
|
|
|
if (auxEmail && auxContactId) break // Se encontrou ambos, pode sair do loop
|
|
}
|
|
|
|
|
|
// Caso não encontre nas primeiras iterações (alguns CRMs podem ter estruturas aninhadas ou diferentes)
|
|
// Esta parte é para casos onde o 'phone' e 'id' não são encontrados de primeira
|
|
// (mantendo a lógica do seu lookupContactByPhone, mas adaptada para email)
|
|
if (!auxEmail && !auxContactId) { // Se ainda não encontrou e-mail e ID
|
|
for (const prop in data) {
|
|
let _prop = prop.replace(/\.(\d+)(\.|$)/g, '[$1]$2')
|
|
|
|
// SALESFORCE GETTING THE NAME
|
|
if (_prop == response?.name?.trim()) {
|
|
auxName = data[prop]
|
|
}
|
|
|
|
// SALESFORCE GETTING THE ACCOUNT ID
|
|
if (_prop == response?.accountId?.trim()) {
|
|
auxAccountId = data[prop]
|
|
}
|
|
|
|
if (_prop == response?.email?.trim()) { // Procura novamente pelo campo de e-mail
|
|
auxEmail = data[prop]
|
|
}
|
|
|
|
if (_prop == response?.id?.trim()) {
|
|
auxContactId = data[prop]
|
|
}
|
|
|
|
// SALESFORCE CASE LOOOK UP ALL THE OBJECT PROPERTIES
|
|
// Se não for salesforce, pode sair do loop assim que encontrar email e ID
|
|
if (!url.includes('salesforce'))
|
|
if (auxEmail && auxContactId) break
|
|
}
|
|
}
|
|
|
|
// Tenta pegar o accountId no body da resposta se não conseguir, pega do template
|
|
// se a propriedade crmAccountId existir no template. Isso evita problemas com integrações
|
|
// já em funcionamento.
|
|
if (!auxAccountId && crmAccountId) {
|
|
console.log('---------> auxAccountId definido a partir do crmAccountId do template: ', crmAccountId)
|
|
auxAccountId = crmAccountId
|
|
}
|
|
|
|
console.log('---------> auxEmail: ', auxEmail, ' | auxContactId: ', auxContactId, ' | auxAccountId: ', auxAccountId)
|
|
|
|
// 6. Se um e-mail válido for encontrado na resposta da API
|
|
if (auxEmail) {
|
|
|
|
if (auxEmail && auxContactId) {
|
|
// Garante apenas 1 contato no seu banco local (CRM_Contact)
|
|
const contacts = await CRM_Contact.find({
|
|
companyId,
|
|
crmBaseURL: new URL(url).hostname,
|
|
email: crmEmail, // Busca pelo e-mail original para evitar duplicatas locais
|
|
contactId: { $ne: auxContactId } // O ID encontrado na API deve ser o único para este e-mail
|
|
})
|
|
|
|
if (contacts && contacts.length > 0) {
|
|
for (const contact of contacts) {
|
|
console.log("=====> DELETING DUPLICATE CONTACTS BY EMAIL: ", contact)
|
|
await CRM_Ticket.deleteMany({ companyId, contact: contact })
|
|
await contact.deleteOne()
|
|
}
|
|
}
|
|
|
|
// Atualiza ou cria o contato no seu banco local (CRM_Contact)
|
|
const contactInfo = await CRM_Contact.findOne({ companyId, crmBaseURL: new URL(url).hostname, contactId: auxContactId })
|
|
|
|
console.log('contactInfo (local): ', contactInfo, " | crmEmail: ", crmEmail)
|
|
|
|
if (!contactInfo) {
|
|
console.log('----------------> CREATE CONTACT MONGO (from email lookup)')
|
|
const crm = await CRM.findOne({ companyId, crmBaseURL: new URL(url).hostname })
|
|
// await CRM_Contact.create({ companyId, crm, crmBaseURL: new URL(url).hostname, contactId: auxContactId, email: auxEmail }) // Armazena o e-mail
|
|
} else if (contactInfo.email !== auxEmail) { // Se o contato existe mas o email mudou
|
|
console.log('----------------> UPDATING CONTACT EMAIL IN MONGO')
|
|
await CRM_Contact.updateOne(
|
|
{ companyId, crmBaseURL: new URL(url).hostname, contactId: auxContactId },
|
|
{ $set: { email: auxEmail } }
|
|
);
|
|
}
|
|
}
|
|
|
|
// Retorna que o contato existe e suas informações
|
|
return { exist: true, contactId: auxContactId, email: crmEmail, name: auxName, accountId: auxAccountId }
|
|
}
|
|
|
|
// Se nenhum e-mail válido foi encontrado na resposta da API, o contato não existe
|
|
return { exist: false }
|
|
}
|
|
|
|
module.exports = lookupContactByEmail
|
|
|
|
|