criar uma análise financeira completa de fluxo de caixa a partir dos dados colados do relatório. Considerar Novembro como mês de fechamento e destacar os pontos principais como Receitas totais, Despesas totais, resultado operacional, lucro líquido, margem de contribuição, etc. Detalhar a composição das despesas e receitas (incluir a divisão das receitas, Receitas de Trafego, de Web, de Serviços, etc) e informar o PLR. Inclui também uma análise com principais pontos de atenção e diretrizes. Incluir uma projeção do fluxo da caixa. Seguem dados: FLUXO DE CAIXA Realizado (R$) Realizado (R$) Realizado (R$) Realizado (R$) Realizado (R$) Realizado (R$) Realizado (R$) Realizado (R$) Realizado (R$) Realizado (R$) Realizado (R$) Previsto (R$) Realizado (R$)
2f61e46 verified | // Financial Data and Analysis | |
| const financialData = { | |
| months: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov'], | |
| revenues: [32757, 36279, 37684, 34902, 34148, 42104, 33888, 44910, 46361, 49334, 51009], | |
| expenses: [38408, 38022, 41353, 38789, 38435, 46177, 43866, 44193, 50352, 51320, 52617], | |
| operationalProfit: [14289, 20353, 15290, 14714, 14704, 17381, 11422, 20951, 15252, 20792, 20249], | |
| plr: [19396, 19396, 19396, 19396, 19396, 21396, 21396, 21396, 20964, 22964, 22964], | |
| cashBalance: [57765, 52114, 50371, 46703, 42816, 42529, 44457, 36478, 25195, 27204, 27102, 27611], | |
| revenueBreakdown: { | |
| november: { | |
| traffic: 37828, | |
| webDesign: 9165, | |
| services: 2627, | |
| hosting: 222, | |
| financial: 0, | |
| others: 1168 | |
| } | |
| }, | |
| expensesBreakdown: { | |
| november: { | |
| plr: 22964, | |
| salaries: 11294, | |
| variableCosts: 11391, | |
| taxes: 4813, | |
| administrative: 5827, | |
| commercial: 893, | |
| property: 89, | |
| financial: 61, | |
| others: 0 | |
| } | |
| } | |
| }; | |
| // Utility Functions | |
| function formatCurrency(value) { | |
| return new Intl.NumberFormat('pt-BR', { | |
| style: 'currency', | |
| currency: 'BRL' | |
| }).format(value); | |
| } | |
| function formatPercentage(value) { | |
| return (value * 100).toFixed(1) + '%'; | |
| } | |
| // Calculate Financial Metrics | |
| function calculateMetrics() { | |
| const totalRevenues = financialData.revenues.reduce((a, b) => a + b, 0); | |
| const totalExpenses = financialData.expenses.reduce((a, b) => a + b, 0); | |
| const avgOperationalProfit = financialData.operationalProfit.reduce((a, b) => a + b, 0) / financialData.operationalProfit.length; | |
| // Calculate average contribution margin | |
| let totalContributionMargin = 0; | |
| for (let i = 0; i < financialData.months.length; i++) { | |
| const contributionMargin = financialData.revenues[i] * 0.754; // Using average 75.4% | |
| totalContributionMargin += contributionMargin; | |
| } | |
| const avgContributionMargin = totalContributionMargin / totalRevenues; | |
| return { | |
| totalRevenues, | |
| totalExpenses, | |
| avgOperationalProfit, | |
| avgContributionMargin | |
| }; | |
| } | |
| // Update KPI Cards | |
| function updateKPIs() { | |
| const metrics = calculateMetrics(); | |
| document.getElementById('totalRevenues').textContent = formatCurrency(metrics.totalRevenues); | |
| document.getElementById('totalExpenses').textContent = formatCurrency(metrics.totalExpenses); | |
| document.getElementById('operationalProfit').textContent = formatCurrency(metrics.avgOperationalProfit); | |
| document.getElementById('contributionMargin').textContent = formatPercentage(metrics.avgContributionMargin); | |
| } | |
| // Create Monthly Flow Chart | |
| function createMonthlyFlowChart() { | |
| const ctx = document.getElementById('monthlyFlowChart').getContext('2d'); | |
| new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov'], | |
| datasets: [{ | |
| label: 'Receitas', | |
| data: financialData.revenues, | |
| borderColor: '#10B981', | |
| backgroundColor: 'rgba(16, 185, 129, 0.1)', | |
| tension: 0.4, | |
| fill: true | |
| }, { | |
| label: 'Despesas', | |
| data: financialData.expenses, | |
| borderColor: '#EF4444', | |
| backgroundColor: 'rgba(239, 68, 68, 0.1)', | |
| tension: 0.4, | |
| fill: true | |
| }, { | |
| label: 'Saldo Caixa', | |
| data: financialData.cashBalance.slice(1), | |
| borderColor: '#3B82F6', | |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', | |
| tension: 0.4, | |
| fill: false, | |
| yAxisID: 'y1' | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'top', | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| ticks: { | |
| callback: function(value) { | |
| return formatCurrency(value); | |
| } | |
| } | |
| }, | |
| y1: { | |
| type: 'linear', | |
| display: true, | |
| position: 'right', | |
| grid: { | |
| drawOnChartArea: false, | |
| }, | |
| ticks: { | |
| callback: function(value) { | |
| return formatCurrency(value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Create Revenue Composition Chart | |
| function createRevenueCompositionChart() { | |
| const ctx = document.getElementById('revenueCompositionChart').getContext('2d'); | |
| const novemberData = financialData.revenueBreakdown.november; | |
| new Chart(ctx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['Tráfego', 'Web Design', 'Serviços', 'Hospedagem', 'Outras'], | |
| datasets: [{ | |
| data: [novemberData.traffic, novemberData.webDesign, novemberData.services, novemberData.hosting, novemberData.others], | |
| backgroundColor: [ | |
| '#3B82F6', | |
| '#10B981', | |
| '#F59E0B', | |
| '#8B5CF6', | |
| '#EF4444' | |
| ] | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'right', | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| const total = context.dataset.data.reduce((a, b) => a + b, 0); | |
| const percentage = ((context.raw / total) * 100).toFixed(1); | |
| return context.label + ': ' + formatCurrency(context.raw) + ' (' + percentage + '%)'; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Create Projection Chart | |
| function createProjectionChart() { | |
| const ctx = document.getElementById('projectionChart').getContext('2d'); | |
| // Project next 6 months based on trends | |
| const projectionMonths = ['Dez', 'Jan', 'Fev', 'Mar', 'Abr', 'Mai']; | |
| const lastRevenue = financialData.revenues[financialData.revenues.length - 1]; | |
| const lastExpense = financialData.expenses[financialData.expenses.length - 1]; | |
| // Simple linear projection with slight growth for revenues and controlled expenses | |
| const projectedRevenues = [ | |
| lastRevenue * 0.9, // December lower due to holidays | |
| lastRevenue * 1.02, | |
| lastRevenue * 1.04, | |
| lastRevenue * 1.06, | |
| lastRevenue * 1.08, | |
| lastRevenue * 1.10 | |
| ]; | |
| const projectedExpenses = [ | |
| lastExpense * 0.8, // December lower expenses | |
| lastExpense * 0.95, // Cost reduction measures | |
| lastExpense * 0.96, | |
| lastExpense * 0.97, | |
| lastExpense * 0.98, | |
| lastExpense * 0.99 | |
| ]; | |
| let projectedCash = financialData.cashBalance[financialData.cashBalance.length - 1]; | |
| const projectedCashFlow = [projectedCash]; | |
| for (let i = 0; i < projectedRevenues.length; i++) { | |
| projectedCash += (projectedRevenues[i] - projectedExpenses[i]); | |
| projectedCashFlow.push(projectedCash); | |
| } | |
| new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: ['Nov', ...projectionMonths], | |
| datasets: [{ | |
| label: 'Receitas Projetadas', | |
| data: [null, ...projectedRevenues], | |
| borderColor: '#10B981', | |
| borderDash: [5, 5], | |
| tension: 0.4 | |
| }, { | |
| label: 'Despesas Projetadas', | |
| data: [null, ...projectedExpenses], | |
| borderColor: '#EF4444', | |
| borderDash: [5, 5], | |
| tension: 0.4 | |
| }, { | |
| label: 'Saldo Caixa Projetado', | |
| data: projectedCashFlow, | |
| borderColor: '#3B82F6', | |
| backgroundColor: 'rgba(59, 130, 246, 0.2)', | |
| fill: true, | |
| tension: 0.4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'top', | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: false, | |
| ticks: { | |
| callback: function(value) { | |
| return formatCurrency(value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Populate Monthly Table | |
| function populateMonthlyTable() { | |
| const tableBody = document.getElementById('monthlyTable'); | |
| tableBody.innerHTML = ''; | |
| for (let i = 0; i < financialData.months.length; i++) { | |
| const revenue = financialData.revenues[i]; | |
| const expense = financialData.expenses[i]; | |
| const result = revenue - expense; | |
| const cashBalance = financialData.cashBalance[i + 1]; | |
| const row = document.createElement('tr'); | |
| row.className = 'border-b border-gray-100'; | |
| row.innerHTML = ` | |
| <td class="py-2 px-3 font-medium">${financialData.months[i].charAt(0).toUpperCase() + financialData.months[i].slice(1)}/25</td> | |
| <td class="py-2 px-3 text-right font-medium text-green-600">${formatCurrency(revenue)}</td> | |
| <td class="py-2 px-3 text-right font-medium text-red-600">${formatCurrency(expense)}</td> | |
| <td class="py-2 px-3 text-right font-medium ${result >= 0 ? 'text-green-600' : 'text-red-600'}">${formatCurrency(result)}</td> | |
| <td class="py-2 px-3 text-right font-medium">${formatCurrency(cashBalance)}</td> | |
| `; | |
| tableBody.appendChild(row); | |
| } | |
| // Add totals row | |
| const totalRevenue = financialData.revenues.reduce((a, b) => a + b, 0); | |
| const totalExpense = financialData.expenses.reduce((a, b) => a + b, 0); | |
| const totalResult = totalRevenue - totalExpense; | |
| const totalsRow = document.createElement('tr'); | |
| totalsRow.className = 'font-bold bg-gray-50'; | |
| totalsRow.innerHTML = ` | |
| <td class="py-2 px-3">TOTAL</td> | |
| <td class="py-2 px-3 text-right text-green-600">${formatCurrency(totalRevenue)}</td> | |
| <td class="py-2 px-3 text-right text-red-600">${formatCurrency(totalExpense)}</td> | |
| <td class="py-2 px-3 text-right ${totalResult >= 0 ? 'text-green-600' : 'text-red-600'}">${formatCurrency(totalResult)}</td> | |
| <td class="py-2 px-3 text-right">${formatCurrency(financialData.cashBalance[financialData.cashBalance.length - 1])}</td> | |
| `; | |
| tableBody.appendChild(totalsRow); | |
| } | |
| // Export to PDF (placeholder function) | |
| function exportToPDF() { | |
| alert('Função de exportação para PDF será implementada com integração de biblioteca PDF como jsPDF'); | |
| } | |
| // Refresh Data (placeholder function) | |
| function refreshData() { | |
| const cards = document.querySelectorAll('.bg-white'); | |
| cards.forEach(card => card.classList.add('loading')); | |
| setTimeout(() => { | |
| cards.forEach(card => card.classList.remove('loading')); | |
| updateKPIs(); | |
| populateMonthlyTable(); | |
| }, 1000); | |
| } | |
| // Initialize Dashboard | |
| document.addEventListener('DOMContentLoaded', function() { | |
| updateKPIs(); | |
| createMonthlyFlowChart(); | |
| createRevenueCompositionChart(); | |
| createProjectionChart(); | |
| populateMonthlyTable(); | |
| }); | |
| // Smooth scroll for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const target = document.querySelector(this.getAttribute('href')); | |
| if (target) { | |
| target.scrollIntoView({ | |
| behavior: 'smooth', | |
| block: 'start' | |
| }); | |
| } | |
| }); | |
| }); |