Advertisement
lucasvinicius

Recorrência - Vendedores LP v2

Jul 8th, 2025
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 9.30 KB | Source Code | 0 0
  1. /**
  2.  * @OnlyCurrentDoc
  3.  */
  4.  
  5. // --- CONFIGURAÇÕES ---
  6. const ABA_CONSOLIDADA = "Base_Consolidada_Vendedores";
  7. const COLUNA_CICLO = 1;     // Coluna A
  8. const COLUNA_NOME = 2;      // Coluna B
  9. const COLUNA_ID_UNICO = 3;  // Coluna C (CPF)
  10. const COLUNA_EMAIL = 4;     // Coluna D
  11. // --------------------
  12.  
  13. /**
  14.  * Cria um menu personalizado na interface da planilha.
  15.  */
  16. function onOpen() {
  17.   SpreadsheetApp.getUi()
  18.     .createMenu('📈 Análise de Vendedores')
  19.     .addItem('Gerar Relatório de Recorrência', 'gerarRelatorioRecorrencia')
  20.     .addToUi();
  21. }
  22.  
  23. /**
  24.  * Função principal que gera o relatório de recorrência (VERSÃO CORRIGIDA)
  25.  */
  26. function gerarRelatorioRecorrencia() {
  27.   const ui = SpreadsheetApp.getUi();
  28.   const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  29.  
  30.   // 1. LER OS DADOS CONSOLIDADOS
  31.   const abaConsolidada = spreadsheet.getSheetByName(ABA_CONSOLIDADA);
  32.   if (!abaConsolidada) {
  33.     ui.alert(`Erro: A aba "${ABA_CONSOLIDADA}" não foi encontrada! Verifique o nome da aba.`);
  34.     return;
  35.   }
  36.  
  37.   const dadosRange = abaConsolidada.getRange(2, 1, abaConsolidada.getLastRow() - 1, abaConsolidada.getLastColumn());
  38.   // Usar getDisplayValues() para pegar as datas como texto (ex: "3/2024"), não como números de série
  39.   const dados = dadosRange.getDisplayValues();
  40.  
  41.   // 2. PROCESSAR E AGRUPAR OS DADOS
  42.   const vendedores = {};
  43.   let todosOsCiclos = new Set();
  44.  
  45.   dados.forEach(linha => {
  46.     const idLimpo = String(linha[COLUNA_ID_UNICO - 1]).replace(/\D/g, '');
  47.     if (!idLimpo) return;
  48.  
  49.     const nome = linha[COLUNA_NOME - 1];
  50.     const email = linha[COLUNA_EMAIL - 1];
  51.    
  52.     // Pega o valor da coluna ciclo (já como texto "3/2024" graças ao getDisplayValues())
  53.     const ciclo = linha[COLUNA_CICLO - 1];
  54.    
  55.     // Se o ciclo for um texto válido, adiciona ao nosso conjunto
  56.     if (ciclo && ciclo.includes('/')) {
  57.         todosOsCiclos.add(ciclo);
  58.     } else {
  59.         return; // Pula linhas onde a coluna de ciclo não está no formato esperado
  60.     }
  61.    
  62.     if (!vendedores[idLimpo]) {
  63.       vendedores[idLimpo] = {
  64.         nome: nome,
  65.         email: email,
  66.         ciclos: new Set(),
  67.       };
  68.     }
  69.     vendedores[idLimpo].ciclos.add(ciclo);
  70.   });
  71.  
  72.   // Ordena os ciclos de forma cronológica
  73.   const ciclosOrdenados = Array.from(todosOsCiclos).sort((a, b) => {
  74.       const [mesA, anoA] = a.split('/');
  75.       const [mesB, anoB] = b.split('/');
  76.       if (anoA !== anoB) return parseInt(anoA) - parseInt(anoB);
  77.       return parseInt(mesA) - parseInt(mesB);
  78.   });
  79.  
  80.   const totalDeCiclosPossiveis = ciclosOrdenados.length;
  81.  
  82.   if (totalDeCiclosPossiveis === 0) {
  83.       ui.alert("Atenção!", "Nenhum ciclo no formato 'mês/ano' foi encontrado na coluna A. Verifique sua base de dados.", ui.ButtonSet.OK);
  84.       return;
  85.   }
  86.  
  87.   // 3. PREPARAR OS DADOS PARA O RELATÓRIO (o resto do código permanece o mesmo)
  88.   const dadosDoRelatorio = [];
  89.   const cabecalhos = ["CPF (ID)", "Nome do Vendedor", "E-mail", "Nº de Participações", "Status de Recorrência"];
  90.   ciclosOrdenados.forEach(ciclo => cabecalhos.push(String(ciclo)));
  91.   dadosDoRelatorio.push(cabecalhos);
  92.  
  93.   for (const id in vendedores) {
  94.     const vendedor = vendedores[id];
  95.     const numParticipacoes = vendedor.ciclos.size;
  96.    
  97.     let status = `${numParticipacoes} de ${totalDeCiclosPossiveis} ciclos`;
  98.     if (numParticipacoes === totalDeCiclosPossiveis) {
  99.       status = "✅ Engajamento Máximo";
  100.     } else if (numParticipacoes === 1) {
  101.       status = "⚠️ Participação Única";
  102.     }
  103.  
  104.     const linhaRelatorio = [id, vendedor.nome, vendedor.email, numParticipacoes, status];
  105.    
  106.     ciclosOrdenados.forEach(ciclo => {
  107.       linhaRelatorio.push(vendedor.ciclos.has(ciclo) ? "Sim" : "");
  108.     });
  109.    
  110.     dadosDoRelatorio.push(linhaRelatorio);
  111.   }
  112.  
  113.   dadosDoRelatorio.sort((a, b) => {
  114.     if (typeof a[3] === 'string') return -1;
  115.     if (typeof b[3] === 'string') return 1;
  116.     return b[3] - a[3];
  117.   });
  118.  
  119.   // 4. ESCREVER O RELATÓRIO NA PLANILHA
  120.   let abaRelatorio = spreadsheet.getSheetByName("Relatorio_Recorrencia");
  121.   if (abaRelatorio) {
  122.     abaRelatorio.clear();
  123.   } else {
  124.     abaRelatorio = spreadsheet.insertSheet("Relatorio_Recorrencia");
  125.   }
  126.  
  127.   abaRelatorio.getRange(1, 1, dadosDoRelatorio.length, dadosDoRelatorio[0].length).setValues(dadosDoRelatorio);
  128.  
  129.   // 5. FORMATAR O RELATÓRIO
  130.   formatarRelatorio(abaRelatorio); // Reutilizando a função que já temos
  131.  
  132.   ui.alert("Sucesso!", `O Relatório de Recorrência foi gerado para ${totalDeCiclosPossiveis} ciclo(s) único(s) encontrado(s).`, ui.ButtonSet.OK);
  133. }
  134.  
  135. /**
  136.  * Função auxiliar para formatar a aba de relatório.
  137.  */
  138. function formatarRelatorio(sheet) {
  139.   sheet.autoResizeColumns(1, sheet.getLastColumn());
  140.   sheet.setFrozenRows(1);
  141.   sheet.getRange("1:1").setFontWeight("bold").setHorizontalAlignment("center");
  142.   sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).applyRowBanding();
  143.   sheet.getRange("D:Z").setHorizontalAlignment("center"); // Centraliza colunas de números e "Sim"
  144. }
  145.  
  146. /**
  147.  * ==================================================================
  148.  *  NOVO RELATÓRIO: ANÁLISE DE DESEMPENHO E INTENSIDADE
  149.  * ==================================================================
  150.  */
  151.  
  152. /**
  153.  * Adiciona o novo relatório ao menu.
  154.  * (Esta função deve ser mesclada com sua onOpen existente se você já tiver uma)
  155.  */
  156. function onOpen() {
  157.   SpreadsheetApp.getUi()
  158.     .createMenu('📈 Análise de Vendedores')
  159.     .addItem('Gerar Relatório de Recorrência (Sim/Não)', 'gerarRelatorioRecorrencia')
  160.     .addSeparator()
  161.     .addItem('Gerar Relatório de Desempenho (Contagem)', 'gerarRelatorioDesempenho')
  162.     .addToUi();
  163. }
  164.  
  165. /**
  166.  * Função principal que gera o Relatório de Desempenho.
  167.  */
  168. function gerarRelatorioDesempenho() {
  169.   const ui = SpreadsheetApp.getUi();
  170.   const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  171.  
  172.   const abaConsolidada = spreadsheet.getSheetByName(ABA_CONSOLIDADA);
  173.   if (!abaConsolidada) {
  174.     ui.alert(`Erro: A aba "${ABA_CONSOLIDADA}" não foi encontrada!`);
  175.     return;
  176.   }
  177.  
  178.   const dados = abaConsolidada.getRange(2, 1, abaConsolidada.getLastRow() - 1, abaConsolidada.getLastColumn()).getDisplayValues();
  179.  
  180.   // Objeto para armazenar dados detalhados dos vendedores
  181.   const vendedores = {};
  182.   let todosOsCiclos = new Set();
  183.  
  184.   dados.forEach(linha => {
  185.     const idLimpo = String(linha[COLUNA_ID_UNICO - 1]).replace(/\D/g, '');
  186.     if (!idLimpo) return;
  187.  
  188.     const nome = linha[COLUNA_NOME - 1];
  189.     const email = linha[COLUNA_EMAIL - 1];
  190.     const ciclo = linha[COLUNA_CICLO - 1];
  191.  
  192.     if (!ciclo || !ciclo.includes('/')) return;
  193.     todosOsCiclos.add(ciclo);
  194.    
  195.     // Se o vendedor é novo, inicializa sua estrutura de dados
  196.     if (!vendedores[idLimpo]) {
  197.       vendedores[idLimpo] = {
  198.         nome: nome,
  199.         email: email,
  200.         participacoesPorCiclo: {} // Objeto para contar participações por ciclo
  201.       };
  202.     }
  203.    
  204.     // Inicializa o contador para o ciclo se for a primeira vez
  205.     if (!vendedores[idLimpo].participacoesPorCiclo[ciclo]) {
  206.       vendedores[idLimpo].participacoesPorCiclo[ciclo] = 0;
  207.     }
  208.     // Incrementa a contagem de participações para aquele vendedor naquele ciclo
  209.     vendedores[idLimpo].participacoesPorCiclo[ciclo]++;
  210.   });
  211.  
  212.   const ciclosOrdenados = Array.from(todosOsCiclos).sort((a, b) => {
  213.       const [mesA, anoA] = a.split('/'); const [mesB, anoB] = b.split('/');
  214.       if (anoA !== anoB) return parseInt(anoA) - parseInt(anoB);
  215.       return parseInt(mesA) - parseInt(mesB);
  216.   });
  217.   const totalDeCiclosPossiveis = ciclosOrdenados.length;
  218.  
  219.   const dadosDoRelatorio = [];
  220.   const cabecalhos = [
  221.     "CPF (ID)", "Nome do Vendedor", "E-mail", "Total de Prêmios",
  222.     "Ciclos Distintos", "Status de Recorrência"
  223.   ];
  224.   ciclosOrdenados.forEach(ciclo => cabecalhos.push(`Prêmios em ${ciclo}`));
  225.   dadosDoRelatorio.push(cabecalhos);
  226.  
  227.   for (const id in vendedores) {
  228.     const vendedor = vendedores[id];
  229.    
  230.     const numCiclosDistintos = Object.keys(vendedor.participacoesPorCiclo).length;
  231.     const totalPremios = Object.values(vendedor.participacoesPorCiclo).reduce((sum, count) => sum + count, 0);
  232.  
  233.     let status = `${numCiclosDistintos} de ${totalDeCiclosPossiveis} ciclos`;
  234.     if (numCiclosDistintos === totalDeCiclosPossiveis) {
  235.       status = "✅ Recorrência Máxima";
  236.     }
  237.  
  238.     const linhaRelatorio = [id, vendedor.nome, vendedor.email, totalPremios, numCiclosDistintos, status];
  239.    
  240.     ciclosOrdenados.forEach(ciclo => {
  241.       linhaRelatorio.push(vendedor.participacoesPorCiclo[ciclo] || 0);
  242.     });
  243.    
  244.     dadosDoRelatorio.push(linhaRelatorio);
  245.   }
  246.  
  247.   dadosDoRelatorio.sort((a, b) => {
  248.     if (typeof a[3] === 'string') return -1;
  249.     if (typeof b[3] === 'string') return 1;
  250.     return b[3] - a[3]; // Ordena pelo Total de Prêmios
  251.   });
  252.  
  253.   let abaRelatorio = spreadsheet.getSheetByName("Relatorio_Desempenho");
  254.   if (abaRelatorio) {
  255.     abaRelatorio.clear();
  256.   } else {
  257.     abaRelatorio = spreadsheet.insertSheet("Relatorio_Desempenho");
  258.   }
  259.  
  260.   abaRelatorio.getRange(1, 1, dadosDoRelatorio.length, dadosDoRelatorio[0].length).setValues(dadosDoRelatorio);
  261.  
  262.   formatarRelatorio(abaRelatorio);
  263.  
  264.   ui.alert("Sucesso!", "O Relatório de Desempenho foi gerado na aba 'Relatorio_Desempenho'.", ui.ButtonSet.OK);
  265. }
Tags: lojaperfeita
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement