UNPKG

n8n-rdstation-crm

Version:

Nós personalizados do n8n para integração com a API do RD Station CRM (v1)

594 lines (548 loc) 15.9 kB
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, NodeOperationError, } from 'n8n-workflow'; import { OptionsWithUri } from 'request-promise-native'; /** * Classe para o nó de Negócios (Deals) do RD Station CRM * Este nó permite interações com a API de Negócios do RD Station CRM. */ export class RdStationCrmDeals implements INodeType { description: INodeTypeDescription = { // Informações básicas do nó displayName: 'RD Station CRM Negócios', name: 'rdStationCrmDeals', icon: 'file:rdstation.svg', // Ícone a ser exibido no n8n group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Operações com negócios no RD Station CRM', defaults: { name: 'RD Station CRM Negócios', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'rdStationCrmApi', required: true, }, ], properties: [ // Seleção do recurso (neste caso, só temos negócios/deals) { displayName: 'Recurso', name: 'resource', type: 'options', noDataExpression: true, options: [ { name: 'Negócio', value: 'deal', }, ], default: 'deal', required: true, }, // Seleção da operação a ser realizada { displayName: 'Operação', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: [ 'deal', ], }, }, options: [ { name: 'Criar', value: 'create', description: 'Criar um novo negócio', action: 'Criar um negócio', }, { name: 'Atualizar', value: 'update', description: 'Atualizar um negócio existente', action: 'Atualizar um negócio', }, { name: 'Listar', value: 'getAll', description: 'Listar negócios', action: 'Listar negócios', }, { name: 'Obter', value: 'get', description: 'Obter um negócio pelo ID', action: 'Obter um negócio', }, { name: 'Listar Contatos do Negócio', value: 'getContacts', description: 'Listar contatos associados a um negócio', action: 'Listar contatos de um negócio', }, ], default: 'getAll', }, // Campos para a operação LISTAR NEGÓCIOS { displayName: 'Parâmetros de Listagem', name: 'listParameters', type: 'collection', placeholder: 'Adicionar Parâmetros', default: {}, displayOptions: { show: { resource: [ 'deal', ], operation: [ 'getAll', ], }, }, options: [ { displayName: 'Limite', name: 'limit', type: 'number', default: 50, description: 'Número máximo de negócios a retornar', }, { displayName: 'Página', name: 'page', type: 'number', default: 1, description: 'Número da página para retornar', }, { displayName: 'ID do Funil', name: 'deal_pipeline_id', type: 'string', default: '', description: 'Filtrar negócios por funil específico', }, { displayName: 'ID da Etapa', name: 'deal_stage_id', type: 'string', default: '', description: 'Filtrar negócios por etapa específica do funil', }, { displayName: 'ID do Usuário Responsável', name: 'user_id', type: 'string', default: '', description: 'Filtrar negócios por usuário responsável', }, { displayName: 'Status', name: 'status', type: 'options', options: [ { name: 'Aberto', value: 'open', }, { name: 'Ganho', value: 'won', }, { name: 'Perdido', value: 'lost', }, { name: 'Todos', value: 'all', }, ], default: 'open', description: 'Status do negócio a ser filtrado', }, { displayName: 'Ordenação', name: 'order', type: 'options', options: [ { name: 'Título', value: 'title', }, { name: 'Data de Criação', value: 'created_at', }, { name: 'Valor', value: 'value', }, ], default: 'created_at', description: 'Campo pelo qual ordenar os resultados', }, { displayName: 'Direção da Ordenação', name: 'direction', type: 'options', options: [ { name: 'Ascendente', value: 'asc', }, { name: 'Descendente', value: 'desc', }, ], default: 'desc', description: 'Direção da ordenação (crescente ou decrescente)', }, ], }, // Campos para a operação OBTER NEGÓCIO { displayName: 'ID do Negócio', name: 'dealId', type: 'string', default: '', required: true, displayOptions: { show: { resource: [ 'deal', ], operation: [ 'get', 'update', 'getContacts', ], }, }, description: 'ID do negócio a ser consultado/atualizado', }, // Campos para CRIAR e ATUALIZAR negócios { displayName: 'Dados do Negócio', name: 'dealData', type: 'collection', placeholder: 'Adicionar Dados', default: {}, displayOptions: { show: { resource: [ 'deal', ], operation: [ 'create', 'update', ], }, }, options: [ { displayName: 'Título', name: 'title', type: 'string', default: '', description: 'Título ou nome do negócio (obrigatório para criação)', }, { displayName: 'ID da Empresa', name: 'organization_id', type: 'string', default: '', description: 'ID da empresa relacionada ao negócio', }, { displayName: 'ID do Funil', name: 'deal_pipeline_id', type: 'string', default: '', description: 'ID do funil de vendas no qual o negócio será criado', }, { displayName: 'ID da Etapa', name: 'deal_stage_id', type: 'string', default: '', description: 'ID da etapa do funil onde o negócio está atualmente', }, { displayName: 'ID do Usuário Responsável', name: 'user_id', type: 'string', default: '', description: 'ID do usuário responsável (owner) por esse negócio', }, { displayName: 'Valor', name: 'value', type: 'number', default: 0, description: 'Valor monetário do negócio', }, { displayName: 'Moeda', name: 'currency', type: 'options', options: [ { name: 'Real (BRL)', value: 'BRL', }, { name: 'Dólar (USD)', value: 'USD', }, { name: 'Euro (EUR)', value: 'EUR', }, ], default: 'BRL', description: 'Moeda do valor do negócio', }, ], }, // IDs de contatos para associar ao criar um negócio { displayName: 'IDs de Contatos', name: 'contactIds', placeholder: 'Adicionar ID de Contato', type: 'fixedCollection', typeOptions: { multipleValues: true, }, displayOptions: { show: { resource: [ 'deal', ], operation: [ 'create', 'update', ], }, }, default: {}, options: [ { name: 'contactIdValues', displayName: 'ID de Contato', values: [ { displayName: 'ID do Contato', name: 'id', type: 'string', default: '', description: 'ID do contato a ser associado ao negócio', required: true, }, ], }, ], }, ], }; /** * Método principal que executa o nó com base na operação selecionada */ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { const items = this.getInputData(); const returnData: INodeExecutionData[] = []; // URL base da API do RD Station CRM const baseUrl = 'https://crm.rdstation.com/api/v1'; const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; // Para cada item de entrada, processa a operação solicitada for (let i = 0; i < items.length; i++) { try { // Configuração comum para todas as requisições const options: OptionsWithUri = { uri: '', // Uri inicial vazio, será definido conforme a operação headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, json: true, resolveWithFullResponse: true, }; // Vamos determinar qual operação executar com base no recurso e operação selecionados if (resource === 'deal') { if (operation === 'getAll') { // Operação: LISTAR NEGÓCIOS // Preparando a requisição para listar negócios options.method = 'GET'; options.uri = `${baseUrl}/deals`; // Obtendo e aplicando parâmetros de filtro/paginação const listParameters = this.getNodeParameter('listParameters', i) as { limit?: number; page?: number; deal_pipeline_id?: string; deal_stage_id?: string; user_id?: string; status?: string; order?: string; direction?: string; }; const qs: Record<string, any> = {}; // Adicionando os parâmetros à query string if (listParameters.limit) qs.limit = listParameters.limit; if (listParameters.page) qs.page = listParameters.page; if (listParameters.deal_pipeline_id) qs.deal_pipeline_id = listParameters.deal_pipeline_id; if (listParameters.deal_stage_id) qs.deal_stage_id = listParameters.deal_stage_id; if (listParameters.user_id) qs.user_id = listParameters.user_id; if (listParameters.status) qs.status = listParameters.status; if (listParameters.order) qs.order = listParameters.order; if (listParameters.direction) qs.direction = listParameters.direction; options.qs = qs; } else if (operation === 'get') { // Operação: OBTER NEGÓCIO ESPECÍFICO // Obtém o ID do negócio a ser consultado const dealId = this.getNodeParameter('dealId', i) as string; // Verifica se o ID do negócio foi fornecido if (!dealId) { throw new NodeOperationError(this.getNode(), 'É necessário fornecer um ID de negócio válido!', { itemIndex: i }); } // Configurando a requisição para buscar um negócio específico options.method = 'GET'; options.uri = `${baseUrl}/deals/${dealId}`; } else if (operation === 'getContacts') { // Operação: LISTAR CONTATOS DO NEGÓCIO // Obtém o ID do negócio para listar seus contatos const dealId = this.getNodeParameter('dealId', i) as string; // Verifica se o ID do negócio foi fornecido if (!dealId) { throw new NodeOperationError(this.getNode(), 'É necessário fornecer um ID de negócio válido!', { itemIndex: i }); } // Configurando a requisição para buscar contatos de um negócio específico options.method = 'GET'; options.uri = `${baseUrl}/deals/${dealId}/contacts`; } else if (operation === 'create') { // Operação: CRIAR NEGÓCIO // Configurando requisição para criar negócio options.method = 'POST'; options.uri = `${baseUrl}/deals`; // Obtendo os dados do negócio const dealData = this.getNodeParameter('dealData', i) as { title?: string; organization_id?: string; deal_pipeline_id?: string; deal_stage_id?: string; user_id?: string; value?: number; currency?: string; }; // Verificando se o título foi fornecido (obrigatório para criação) if (!dealData.title) { throw new NodeOperationError( this.getNode(), 'O título do negócio é obrigatório para criação!', { itemIndex: i }, ); } // Preparando o corpo da requisição const body: any = { ...dealData }; // Adicionando IDs de contatos ao corpo const contactIdsCollection = this.getNodeParameter('contactIds', i) as { contactIdValues?: Array<{ id: string }>; }; if (contactIdsCollection.contactIdValues && contactIdsCollection.contactIdValues.length > 0) { body.contact_ids = contactIdsCollection.contactIdValues.map(item => item.id); } options.body = body; } else if (operation === 'update') { // Operação: ATUALIZAR NEGÓCIO // Obtém o ID do negócio a ser atualizado const dealId = this.getNodeParameter('dealId', i) as string; // Verifica se o ID do negócio foi fornecido if (!dealId) { throw new NodeOperationError( this.getNode(), 'É necessário fornecer um ID de negócio válido para atualização!', { itemIndex: i }, ); } // Configurando a requisição para atualizar o negócio options.method = 'PUT'; options.uri = `${baseUrl}/deals/${dealId}`; // Obtendo os dados a serem atualizados const dealData = this.getNodeParameter('dealData', i) as { title?: string; organization_id?: string; deal_pipeline_id?: string; deal_stage_id?: string; user_id?: string; value?: number; currency?: string; }; // Preparando o corpo da requisição const body: any = {}; // Adicionando apenas campos que não estão vazios if (dealData.title) body.title = dealData.title; if (dealData.organization_id) body.organization_id = dealData.organization_id; if (dealData.deal_pipeline_id) body.deal_pipeline_id = dealData.deal_pipeline_id; if (dealData.deal_stage_id) body.deal_stage_id = dealData.deal_stage_id; if (dealData.user_id) body.user_id = dealData.user_id; if (dealData.value !== undefined) body.value = dealData.value; if (dealData.currency) body.currency = dealData.currency; // Adicionando IDs de contatos ao corpo, se fornecidos const contactIdsCollection = this.getNodeParameter('contactIds', i) as { contactIdValues?: Array<{ id: string }>; }; if (contactIdsCollection.contactIdValues && contactIdsCollection.contactIdValues.length > 0) { body.contact_ids = contactIdsCollection.contactIdValues.map(item => item.id); } // Verifica se há campos para atualizar if (Object.keys(body).length === 0) { throw new NodeOperationError( this.getNode(), 'É necessário fornecer pelo menos um campo para atualizar o negócio!', { itemIndex: i }, ); } options.body = body; } } // Executa a requisição HTTP com as opções configuradas const response = await this.helpers.requestWithAuthentication.call( this, 'rdStationCrmApi', options, ); // Extrai os dados da resposta e adiciona ao retorno const responseData = response.body; returnData.push({ json: responseData, pairedItem: { item: i }, }); } catch (error) { // Trata e reporta erros ocorridos durante a execução if (this.continueOnFail()) { returnData.push({ json: { error: error instanceof Error ? error.message : String(error), }, pairedItem: { item: i }, }); continue; } throw error; } } return [returnData]; } }