Simulador de Investimentos: Como Saber se o Seu Faturamento Pode Pagar um Novo Ativo?
- #Tomada de Decisão
- #Matemática financeira
- #Modularização
- #Python
- #Modelagem de Negócios
⚙️ Comprar uma Máquina ou Aplicar o Dinheiro?
Às vezes nos deparamos diante da necessidade de decidirmos o melhor caminho a tomarmos. No mundo dinâmico dos negócios, a tomada de decisões financeiras é como um caminho a ser tomado, uma das atividades mais críticas para o sucesso empresarial.
Imagine que você é um analista financeiro em uma fábrica de artefatos metálicos e recebe um pedido dos administradores: analisar se é mais viável investir em uma nova máquina industrial ou manter os recursos aplicados em um investimento de risco moderado. Agora, pense em como seria interessante ter um simulador que processasse todos os dados financeiros e retornasse, de forma objetiva, qual é a melhor decisão. Com essa ferramenta, os administradores poderiam visualizar claramente os impactos financeiros de cada escolha, tornando a tomada de decisão muito mais embasada e estratégica.
Diante desse cenário, este artigo tem como objetivo apresentar um script em Python que simulará diferentes situações de investimentos considerando as receitas projetadas com base nos faturamentos passados.
A ideia é bem simples. O simulador comparará a receita projetada com a parcela mensal fixa do investimento para indicar se o fluxo de caixa é positivo, facilitando a tomada de decisão.
📊 A ideia e o Modelo Matemático
O desenvolvimento do modelo para simular esse cenário partiu inicialmente da necessidade de prever os valores futuros dos faturamentos, dado a imprevisibilidade do mercado e o impacto no fluxo de caixa: empresas frequentemente enfrentam um dilema estratégico que é manter seus recursos aplicados em investimentos financeiros de risco moderado ou investir na expansão do negócio adquirindo novos ativos, como máquinas e contratações. Essa decisão é desafiadora, pois envolve riscos financeiros e pode impactar diretamente o crescimento da empresa.
Para auxiliar nessa análise, o modelo matemático do simulador segue uma abordagem estruturada. O primeiro passo é avaliar o histórico financeiro da empresa, utilizando a média dos faturamentos dos últimos 12 meses como base para projeções futuras. A partir disso, os administradores podem definir um percentual de sensibilidade — um ajuste baseado em expectativas de mercado, podendo ser um crescimento de 10%, uma redução de 5%, ou mesmo a manutenção do faturamento atual.
Com essa projeção em mãos, o simulador realiza a comparação entre a receita esperada e o valor das parcelas do investimento, resultando abaixo, os três indicadores financeiros essenciais para a análise de viabilidade ou não do negócio.
✅ o ROI (Retorno sobre o Investimento): Mede o percentual de retorno obtido em relação ao valor investido.
✅ a TIR (Taxa Interna de Retorno): Representa a taxa de retorno do fluxo de caixa projetado, indicando se o investimento se paga ao longo do tempo.
✅ e o VPL (Valor Presente Líquido): Determina se o investimento gera valor, comparando os fluxos de caixa futuros descontados pela taxa de atratividade.
Com base nesses cálculos, os resultados serão apresentados pelo simulador que determinará a viabilidade ou inviabilidade do investimento, apresentando um parecer final embasado e gerando um relatório profissional em PDF para que os administradores possam fundamentar a tomada de decisão. Essa abordagem permite que as empresas tomem decisões estratégicas com mais segurança, equilibrando riscos e oportunidades para garantir um crescimento sustentável.
📈 Funcionalidades Principais do Script em Python
Os valores históricos dos faturamentos dos últimos 12 meses; a projeção da média futura do faturamento ajustada pelo percentual de sensibilidade de mercado informado pelo analista como reflexo de um cenário futuro do mercado, de crescimento, redução e manutenção da evolução do faturamento da empresa.
🔍 Como Funciona o Simulador
- Entrada de Dados:
- O analista insere o faturamento dos últimos 12 meses.
- Informa os parâmetros do investimento: valor, taxa de juros, prazo, taxa de atratividade e sensibilidade de previsão.
- Processamento dos Dados:
- O módulo de simulação calcula a média dos faturamentos e ajusta essa média conforme a sensibilidade informada para projetar o faturamento futuro.
- Calcula a parcela mensal do investimento usando juros compostos e gera os fluxos de caixa.
- Cálculo dos Indicadores Financeiros:
- ROI (Retorno Sobre Investimento): Mede o retorno percentual em relação ao investimento inicial.
- VPL (Valor Presente Líquido): Calcula o valor atual dos fluxos de caixa futuros descontados pela taxa de atratividade.
- TIR (Taxa Interna de Retorno): Representa a taxa de desconto que faz com que o VPL seja zero.
- Observação importante: Se a TIR for negativa ou “N/D”, significa que os fluxos de caixa nunca se tornaram positivos – ou seja, não houve entradas de dinheiro suficientes para compensar o investimento.
🗂️ Relação Entre os Arquivos
Os arquivos Python se relacionam da seguinte forma:
- O usuário insere os dados na interface_grafica.py.
- Esses dados são processados pela calculadora_financeira.py, que retorna os indicadores financeiros.
- Os resultados são enviados para a relatorio_pdf.py, que gera o relatório final.
- Tudo isso é coordenado pelo main.py, que atua como o controlador central.
📚 Conceitos Financeiros Explicados
- Faturamento Médio Projetado vs. Parcela do Investimento:
- Se o faturamento médio projetado for igual ou superior à parcela mensal, o investimento é considerado viável.
- Se for inferior, os fluxos de caixa permanecem negativos, resultando em uma TIR não calculada (ou negativa) e indicando que o investimento é inviável.
- TIR (Taxa Interna de Retorno):
- Indica a taxa de retorno que iguala o valor presente dos fluxos de caixa futuros ao investimento inicial.
- Quando a TIR é negativa ou "N/D": Isso ocorre porque os fluxos de caixa não apresentam alternância entre valores negativos e positivos – ou seja, não há entradas suficientes para compensar o investimento.
- ROI e VPL:
- ROI: Mostra a porcentagem de retorno em relação ao valor investido.
- VPL: Permite entender o valor atual dos benefícios futuros esperados, descontados por uma taxa de atratividade.
🐍 Por Que Python?
- Eficiência e Facilidade de Uso:
- Python é uma linguagem poderosa para cálculos financeiros, graças a bibliotecas como NumPy.
- Interface Gráfica Simples:
- Tkinter permite a criação de interfaces de forma rápida e intuitiva, ideal para protótipos e aplicações internas.
- Geração de Relatórios:
- Com ReportLab, é possível gerar relatórios em PDF de alta qualidade, facilitando a apresentação dos resultados.
- Versatilidade:
- Python é amplamente utilizado em diversas áreas, e esse simulador pode servir de base para outros projetos de análise financeira.
📊 Resultados e Interpretação
- Exemplo de Cenário:
- Investimento: R$ 1.000.000,00
- Prazo: 12 meses
- Taxa de Juros: 2% ao mês
- Taxa de Atratividade: 1% ao mês
- Parcela Mensal Calculada: Aproximadamente R$ 94.559,60
- Análise:
- Se a receita projetada for inferior a R$ 94.559,60, os fluxos de caixa serão todos negativos.
- Isso resulta em uma TIR “N/D” (não definida) e o investimento será considerado inviável.
- Conclusão:
- O simulador ajuda o administrador a visualizar se o investimento se paga ou não com o faturamento esperado, permitindo uma decisão embasada.
🤝 Conclusão
1. Impacto Prático
O Simulador de Investimentos é uma ferramenta poderosa para administradores financeiros. Ele transforma dados históricos e projeções em insights acionáveis, permitindo decisões informadas sobre onde alocar recursos.
2. Benefícios
- Decisão Informada: Fornece dados precisos para avaliar a viabilidade de investimentos.
- Flexibilidade: Adapta-se a diferentes tipos de investimentos, desde aquisição de ativos até contratação de pessoal.
- Interface Amigável: Facilita a entrada de dados e visualização dos resultados.
- Relatórios Profissionais: Gera documentos que podem ser compartilhados com stakeholders.
3. Parecer Conclusivo
Com base nos resultados obtidos durante os testes, concluímos que o simulador é uma ferramenta indispensável para empresas que desejam otimizar suas decisões financeiras. Ele combina a precisão dos cálculos financeiros com a praticidade de uma interface gráfica intuitiva, tornando-se acessível até mesmo para usuários sem formação técnica avançada.
4. Próximos Passos
Futuramente, planejamos adicionar suporte para exportação em Excel e criar uma interface web para ampliar o alcance do simulador.
ANEXO – Códigos
a) Simulação.py
# simulacao.py
import numpy as np
def calcular_media_faturamento(faturamentos):
"""Calcula a média dos faturamentos informados."""
if len(faturamentos) == 0:
return 0
return sum(faturamentos) / len(faturamentos)
def calcular_parcela(investimento, taxa, prazo):
"""
Calcula a parcela mensal de um financiamento usando a fórmula de juros compostos.
investimento: valor do investimento;
taxa: taxa de juros (decimal, ex.: 0.02 para 2%);
prazo: número de meses.
"""
if taxa == 0:
return investimento / prazo
return investimento * (taxa * (1 + taxa) ** prazo) / ((1 + taxa) ** prazo - 1)
def calcular_npv(cash_flows, discount_rate):
"""
Calcula o Valor Presente Líquido (NPV ou VPL).
cash_flows: lista de fluxos de caixa (com CF0 negativo);
discount_rate: taxa de desconto (decimal).
"""
npv = 0
for t, cf in enumerate(cash_flows):
npv += cf / ((1 + discount_rate) ** t)
return npv
def calcular_roi(cash_flows, investimento):
"""
Calcula o ROI (Return on Investment) com base nos fluxos de caixa.
Considera que cash_flows[0] é o investimento (valor negativo)
e os demais os retornos mensais.
"""
net_profit = sum(cash_flows[1:])
return (net_profit / investimento) * 100
def calcular_tir(cash_flows):
"""
Calcula a Taxa Interna de Retorno (TIR) usando a função np.irr.
Retorna a TIR em percentual.
"""
try:
tir = np.irr(cash_flows)
return tir * 100 # converte para %
except Exception:
return None
def simular_investimento(faturamentos, sensibilidade, investimento, taxa_juros, prazo, taxa_atratividade):
"""
Executa a simulação do investimento com base nos seguintes parâmetros:
- faturamentos: lista com os faturamentos dos últimos 12 meses;
- sensibilidade: percentual de ajuste da previsão de mercado (pode ser aumento ou redução);
- investimento: valor do investimento;
- taxa_juros: taxa de juros compostos (em %, ex.: 2 para 2%);
- prazo: prazo em meses do financiamento;
- taxa_atratividade: taxa de desconto para o NPV (em %).
O modelo considera que a receita projetada para os meses futuros é dada por:
proj_receita = média * (1 + sensibilidade/100)
E o fluxo de caixa mensal é calculado como:
fluxo = proj_receita - parcela_mensal
Caso a receita projetada seja inferior à parcela mensal, calcula-se o valor mínimo de faturamento
que, quando ajustado pela sensibilidade, permita cobrir a parcela:
valor_mínimo = parcela / (1 + sensibilidade/100)
Retorna um dicionário com os resultados da simulação.
"""
# Cálculo da média e projeção da receita
media = calcular_media_faturamento(faturamentos)
proj_receita = media * (1 + sensibilidade / 100.0)
# Cálculo da parcela mensal (taxa convertida para decimal)
parcela = calcular_parcela(investimento, taxa_juros / 100.0, prazo)
# Fluxos de caixa: CF0 negativo (investimento) e, para cada mês, o benefício líquido
cash_flows = [-investimento] + [(proj_receita - parcela) for _ in range(prazo)]
# Cálculo dos indicadores financeiros
npv = calcular_npv(cash_flows, taxa_atratividade / 100.0)
roi = calcular_roi(cash_flows, investimento)
tir = calcular_tir(cash_flows)
# Verifica se a receita projetada é suficiente para cobrir a parcela mensal.
# Se não for, calcula o valor mínimo de faturamento (média) necessário.
if proj_receita < parcela:
valor_minimo_faturamento = parcela / (1 + sensibilidade / 100.0)
else:
valor_minimo_faturamento = None
# Para a análise, consideramos viável se a receita projetada cobre a parcela.
viavel = proj_receita >= parcela
observacao = "Investimento viável" if viavel else "Investimento inviável"
resultado = {
"media_faturamento": media,
"projecao_receita": proj_receita,
"parcela": parcela,
"cash_flows": cash_flows,
"npv": npv,
"roi": roi,
"tir": tir,
"observacao": observacao,
"valor_minimo_faturamento": valor_minimo_faturamento
}
return resultado
b) interface_grafica.py
# src/interface_grafica.py
import tkinter as tk
from tkinter import ttk, messagebox
from datetime import datetime
import locale
from .simulacao import simular_investimento
from .gerar_relatorio import gerar_relatorio
# Configura o locale para formatação de moeda (pt_BR, se disponível)
try:
locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
except:
locale.setlocale(locale.LC_ALL, '')
class InterfaceGrafica:
def __init__(self, master):
self.master = master
master.title("Simulador de Investimento")
# --- Informações do Analista e Empresa (ocupa duas colunas) ---
info_frame = ttk.LabelFrame(master, text="Informações do Analista e Empresa")
info_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky="ew")
ttk.Label(info_frame, text="Empresa:").grid(row=0, column=0, sticky="w", padx=5, pady=2)
self.empresa_entry = ttk.Entry(info_frame, width=30)
self.empresa_entry.grid(row=0, column=1, padx=5, pady=2)
ttk.Label(info_frame, text="Analista:").grid(row=0, column=2, sticky="w", padx=5, pady=2)
self.analista_entry = ttk.Entry(info_frame, width=30)
self.analista_entry.grid(row=0, column=3, padx=5, pady=2)
ttk.Label(info_frame, text="DE:").grid(row=1, column=0, sticky="w", padx=5, pady=2)
self.de_entry = ttk.Entry(info_frame, width=30)
self.de_entry.grid(row=1, column=1, padx=5, pady=2)
ttk.Label(info_frame, text="PARA:").grid(row=1, column=2, sticky="w", padx=5, pady=2)
self.para_entry = ttk.Entry(info_frame, width=30)
self.para_entry.grid(row=1, column=3, padx=5, pady=2)
ttk.Label(info_frame, text="Data da Análise:").grid(row=2, column=0, sticky="w", padx=5, pady=2)
self.data_analise = tk.StringVar()
self.data_analise.set(datetime.now().strftime("%d/%m/%Y"))
self.data_label = ttk.Label(info_frame, textvariable=self.data_analise)
self.data_label.grid(row=2, column=1, padx=5, pady=2, sticky="w")
# NOVOS CAMPOS: Cidade e Estado
ttk.Label(info_frame, text="Cidade:").grid(row=3, column=0, sticky="w", padx=5, pady=2)
self.cidade_entry = ttk.Entry(info_frame, width=30)
self.cidade_entry.grid(row=3, column=1, padx=5, pady=2)
ttk.Label(info_frame, text="Estado:").grid(row=3, column=2, sticky="w", padx=5, pady=2)
self.estado_entry = ttk.Entry(info_frame, width=30)
self.estado_entry.grid(row=3, column=3, padx=5, pady=2)
# --- Frame dos Faturamentos (coluna 0) ---
faturamento_frame = ttk.LabelFrame(master, text="Faturamentos dos Últimos 12 Meses")
faturamento_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
self.faturamento_entries = []
ttk.Label(faturamento_frame, text="Mês/Ano").grid(row=0, column=0, padx=5, pady=2)
ttk.Label(faturamento_frame, text="Faturamento").grid(row=0, column=1, padx=5, pady=2)
for i in range(12):
mes_entry = ttk.Entry(faturamento_frame, width=15)
mes_entry.grid(row=i+1, column=0, padx=5, pady=2)
faturamento_entry = ttk.Entry(faturamento_frame, width=15)
faturamento_entry.grid(row=i+1, column=1, padx=5, pady=2)
self.faturamento_entries.append((mes_entry, faturamento_entry))
# --- Container para Parâmetros e Resultados (coluna 1) ---
container_frame = ttk.Frame(master)
container_frame.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")
container_frame.columnconfigure(0, weight=1)
# Parâmetros do Investimento (dentro do container, na parte superior)
parametros_frame = ttk.LabelFrame(container_frame, text="Parâmetros do Investimento")
parametros_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")
ttk.Label(parametros_frame, text="Sensibilidade de Previsão (%) :").grid(row=0, column=0, sticky="w", padx=5, pady=2)
self.sensibilidade_entry = ttk.Entry(parametros_frame, width=15)
self.sensibilidade_entry.grid(row=0, column=1, padx=5, pady=2)
ttk.Label(parametros_frame, text="Valor do Investimento:").grid(row=1, column=0, sticky="w", padx=5, pady=2)
self.investimento_entry = ttk.Entry(parametros_frame, width=15)
self.investimento_entry.grid(row=1, column=1, padx=5, pady=2)
ttk.Label(parametros_frame, text="Taxa de Juros Compostos (%) :").grid(row=2, column=0, sticky="w", padx=5, pady=2)
self.taxa_juros_entry = ttk.Entry(parametros_frame, width=15)
self.taxa_juros_entry.grid(row=2, column=1, padx=5, pady=2)
ttk.Label(parametros_frame, text="Prazo (meses):").grid(row=3, column=0, sticky="w", padx=5, pady=2)
self.prazo_entry = ttk.Entry(parametros_frame, width=15)
self.prazo_entry.grid(row=3, column=1, padx=5, pady=2)
ttk.Label(parametros_frame, text="Taxa de Atratividade (%) :").grid(row=4, column=0, sticky="w", padx=5, pady=2)
self.taxa_atratividade_entry = ttk.Entry(parametros_frame, width=15)
self.taxa_atratividade_entry.grid(row=4, column=1, padx=5, pady=2)
# Resultados (dentro do mesmo container, abaixo dos Parâmetros)
resultados_frame = ttk.LabelFrame(container_frame, text="Resultados")
resultados_frame.grid(row=1, column=0, padx=5, pady=5, sticky="nsew")
ttk.Label(resultados_frame, text="Valor da Parcela Investimento:").grid(row=0, column=0, sticky="w", padx=5, pady=2)
self.label_parcela = ttk.Label(resultados_frame, text="R$ 0,00", foreground="blue")
self.label_parcela.grid(row=0, column=1, sticky="w", padx=5, pady=2)
ttk.Label(resultados_frame, text="ROI:").grid(row=1, column=0, sticky="w", padx=5, pady=2)
self.label_roi = ttk.Label(resultados_frame, text="0%", foreground="blue")
self.label_roi.grid(row=1, column=1, sticky="w", padx=5, pady=2)
ttk.Label(resultados_frame, text="TIR:").grid(row=2, column=0, sticky="w", padx=5, pady=2)
self.label_tir = ttk.Label(resultados_frame, text="0%", foreground="blue")
self.label_tir.grid(row=2, column=1, sticky="w", padx=5, pady=2)
ttk.Label(resultados_frame, text="VLP:").grid(row=3, column=0, sticky="w", padx=5, pady=2)
self.label_vlp = ttk.Label(resultados_frame, text="R$ 0,00", foreground="blue")
self.label_vlp.grid(row=3, column=1, sticky="w", padx=5, pady=2)
# Alterado: "Faturamento Médio PRÓXIMOS 12 meses" (projeção com sensibilidade)
ttk.Label(resultados_frame, text="Faturamento Médio PRÓXIMOS 12 meses:").grid(row=4, column=0, sticky="w", padx=5, pady=2)
self.label_media_proj = ttk.Label(resultados_frame, text="R$ 0,00", foreground="blue")
self.label_media_proj.grid(row=4, column=1, sticky="w", padx=5, pady=2)
# Novo: "Faturamento Médio ÚLTIMOS 12 Meses"
ttk.Label(resultados_frame, text="Faturamento Médio ÚLTIMOS 12 Meses:").grid(row=5, column=0, sticky="w", padx=5, pady=2)
self.label_media_ultimos = ttk.Label(resultados_frame, text="R$ 0,00", foreground="blue")
self.label_media_ultimos.grid(row=5, column=1, sticky="w", padx=5, pady=2)
# Novo: "Faturamento Médio FALTANTE" (diferença entre parcela e faturamento médio próximos)
ttk.Label(resultados_frame, text="Faturamento Médio FALTANTE:").grid(row=6, column=0, sticky="w", padx=5, pady=2)
self.label_media_faltante = ttk.Label(resultados_frame, text="R$ 0,00", foreground="blue")
self.label_media_faltante.grid(row=6, column=1, sticky="w", padx=5, pady=2)
# --- Frame "Parecer do Analista" ---
parecer_frame = ttk.LabelFrame(master, text="Parecer do Analista")
parecer_frame.grid(row=2, column=0, columnspan=2, padx=10, pady=10, sticky="ew")
self.parecer_label = ttk.Label(parecer_frame, text="Aguardando simulação...", foreground="green", wraplength=600, justify="left")
self.parecer_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
# --- Quadro de Botões ---
button_frame = ttk.Frame(master)
button_frame.grid(row=3, column=0, columnspan=2, padx=10, pady=10)
self.salvar_button = ttk.Button(button_frame, text="Salvar Dados", command=self.salvar_dados)
self.salvar_button.grid(row=0, column=0, padx=5)
self.recuperar_button = ttk.Button(button_frame, text="Recuperar Dados Gravados", command=self.recuperar_dados)
self.recuperar_button.grid(row=0, column=1, padx=5)
self.calcular_button = ttk.Button(button_frame, text="Calcular", command=self.simular)
self.calcular_button.grid(row=0, column=2, padx=5)
self.imprimir_button = ttk.Button(button_frame, text="Imprimir", command=self.gerar_relatorio)
self.imprimir_button.grid(row=0, column=3, padx=5)
self.limpar_button = ttk.Button(button_frame, text="Limpar", command=self.limpar)
self.limpar_button.grid(row=0, column=4, padx=5)
self.nova_simulacao_button = ttk.Button(button_frame, text="Nova Simulação", command=self.nova_simulacao)
self.nova_simulacao_button.grid(row=0, column=5, padx=5)
self.sair_button = ttk.Button(button_frame, text="Sair", command=self.master.destroy)
self.sair_button.grid(row=0, column=6, padx=5)
# Variável para armazenar os dados da simulação
self.simulation_data = None
# simulação
def simular(self):
"""Coleta os dados informados, executa a simulação e atualiza a interface."""
try:
# Coleta dos faturamentos e demais parâmetros (mesmo código já implementado)
faturamentos = []
faturamento_tabela = [] # lista de tuplas (mês/ano, faturamento)
for mes_entry, fat_entry in self.faturamento_entries:
mes = mes_entry.get().strip()
fat_str = fat_entry.get().strip()
if fat_str == "":
continue
try:
fat_val = float(fat_str)
except ValueError:
messagebox.showerror("Erro", "O faturamento deve ser numérico.")
return
faturamentos.append(fat_val)
faturamento_tabela.append((mes, fat_val))
if len(faturamentos) == 0:
messagebox.showerror("Erro", "Informe pelo menos um faturamento.")
return
sensibilidade = float(self.sensibilidade_entry.get())
investimento = float(self.investimento_entry.get())
taxa_juros = float(self.taxa_juros_entry.get())
prazo = int(self.prazo_entry.get())
taxa_atratividade = float(self.taxa_atratividade_entry.get())
# Executa a simulação (função definida em simulacao.py)
resultado = simular_investimento(faturamentos, sensibilidade, investimento, taxa_juros, prazo, taxa_atratividade)
# Atualiza os labels do frame "Resultados"
self.label_parcela.config(text=locale.currency(resultado["parcela"], grouping=True))
self.label_roi.config(text=f"{resultado['roi']:.2f}%")
tir_value = resultado.get("tir")
if tir_value is None:
tir_text = "N/D"
else:
tir_text = f"{tir_value:.2f}%"
self.label_tir.config(text=tir_text)
self.label_vlp.config(text=locale.currency(resultado["npv"], grouping=True))
self.label_media_proj.config(text=locale.currency(resultado["projecao_receita"], grouping=True))
self.label_media_ultimos.config(text=locale.currency(resultado["media_faturamento"], grouping=True))
faltante = max(resultado["parcela"] - resultado["projecao_receita"], 0)
self.label_media_faltante.config(text=locale.currency(faltante, grouping=True))
# Define a viabilidade com base na comparação direta entre receita projetada e parcela
if resultado["projecao_receita"] >= resultado["parcela"]:
viabilidade_text = "VIÁVEL"
else:
viabilidade_text = "INVIÁVEL"
# Determina se o faturamento projetado é SUFICIENTE ou INSUFICIENTE
if resultado["projecao_receita"] >= resultado["parcela"]:
faturamento_status = "SUFICIENTE"
else:
faturamento_status = "INSUFICIENTE"
# Compor o parecer técnico em um único parágrafo
parecer_text = (
f"Conforme os resultados apresentados, informo que o investimento é {viabilidade_text}, "
f"considerando que a taxa interna de retorno - TIR foi de {tir_text} e a Taxa de Retorno sobre os Investimentos foi de {resultado['roi']:.2f}%, "
f"frente a uma parcela mensal fixa de {locale.currency(resultado['parcela'], grouping=True)} pelo prazo de {prazo} meses. "
f"O faturamento médio para os próximos 12 meses é de {locale.currency(resultado['projecao_receita'], grouping=True)} "
f"e o faturamento médio dos últimos 12 meses foi de {locale.currency(resultado['media_faturamento'], grouping=True)}. "
f"Portanto, o faturamento projetado é considerado "
f"{'SUFICIENTE' if resultado['projecao_receita'] >= resultado['parcela'] else 'INSUFICIENTE'} para arcar com o investimento."
)
self.parecer_label.config(text=parecer_text)
# Armazena os dados para uso posterior (ex.: relatório)
self.simulation_data = {
"empresa": self.empresa_entry.get(),
"analista": self.analista_entry.get(),
"de": self.de_entry.get(),
"para": self.para_entry.get(),
"data_analise": self.data_analise.get(),
"cidade": self.cidade_entry.get(),
"estado": self.estado_entry.get(),
"faturamento_tabela": faturamento_tabela,
"sensibilidade": sensibilidade,
"investimento": investimento,
"taxa_juros": taxa_juros,
"prazo": prazo,
"taxa_atratividade": taxa_atratividade,
"resultado_simulacao": resultado,
"parecer": parecer_text
}
except Exception as e:
messagebox.showerror("Erro", f"Ocorreu um erro na simulação: {e}")
def gerar_relatorio(self):
"""Gera o relatório em PDF a partir dos dados informados e simulados."""
if not self.simulation_data:
messagebox.showwarning("Aviso", "Realize a simulação antes de gerar o relatório.")
return
try:
relatorio_path = gerar_relatorio(self.simulation_data)
messagebox.showinfo("Relatório Gerado", f"Relatório gerado com sucesso em:\n{relatorio_path}")
except Exception as e:
messagebox.showerror("Erro", f"Erro ao gerar relatório: {e}")
def salvar_dados(self):
"""Salva os dados informados em um arquivo de texto."""
try:
dados = {
"empresa": self.empresa_entry.get(),
"analista": self.analista_entry.get(),
"de": self.de_entry.get(),
"para": self.para_entry.get(),
"data_analise": self.data_analise.get(),
"cidade": self.cidade_entry.get(),
"estado": self.estado_entry.get(),
"faturamentos": [(mes.get().strip(), fat.get().strip()) for mes, fat in self.faturamento_entries],
"sensibilidade": self.sensibilidade_entry.get(),
"investimento": self.investimento_entry.get(),
"taxa_juros": self.taxa_juros_entry.get(),
"prazo": self.prazo_entry.get(),
"taxa_atratividade": self.taxa_atratividade_entry.get()
}
with open("dados_informados.txt", "w", encoding="utf-8") as f:
for key, value in dados.items():
f.write(f"{key}: {value}\n")
messagebox.showinfo("Salvar Dados", "Dados informados foram salvos com sucesso em dados_informados.txt.")
except Exception as e:
messagebox.showerror("Erro", f"Erro ao salvar dados: {e}")
def recuperar_dados(self):
"""Recupera os dados gravados no arquivo 'dados_informados.txt' e repovoa os campos da interface."""
try:
with open("dados_informados.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
data = {}
for line in lines:
if ": " in line:
key, value = line.split(": ", 1)
data[key.strip()] = value.strip()
self.empresa_entry.delete(0, tk.END)
self.empresa_entry.insert(0, data.get("empresa", ""))
self.analista_entry.delete(0, tk.END)
self.analista_entry.insert(0, data.get("analista", ""))
self.de_entry.delete(0, tk.END)
self.de_entry.insert(0, data.get("de", ""))
self.para_entry.delete(0, tk.END)
self.para_entry.insert(0, data.get("para", ""))
self.data_analise.set(data.get("data_analise", datetime.now().strftime("%d/%m/%Y")))
self.cidade_entry.delete(0, tk.END)
self.cidade_entry.insert(0, data.get("cidade", ""))
self.estado_entry.delete(0, tk.END)
self.estado_entry.insert(0, data.get("estado", ""))
faturamentos_str = data.get("faturamentos", "")
if faturamentos_str:
try:
faturamentos_list = eval(faturamentos_str)
if isinstance(faturamentos_list, list):
for i, (mes, fat) in enumerate(faturamentos_list):
if i < len(self.faturamento_entries):
mes_entry, fat_entry = self.faturamento_entries[i]
mes_entry.delete(0, tk.END)
mes_entry.insert(0, mes)
fat_entry.delete(0, tk.END)
fat_entry.insert(0, fat)
except Exception as ex:
messagebox.showerror("Erro", f"Não foi possível recuperar faturamentos: {ex}")
self.sensibilidade_entry.delete(0, tk.END)
self.sensibilidade_entry.insert(0, data.get("sensibilidade", ""))
self.investimento_entry.delete(0, tk.END)
self.investimento_entry.insert(0, data.get("investimento", ""))
self.taxa_juros_entry.delete(0, tk.END)
self.taxa_juros_entry.insert(0, data.get("taxa_juros", ""))
self.prazo_entry.delete(0, tk.END)
self.prazo_entry.insert(0, data.get("prazo", ""))
self.taxa_atratividade_entry.delete(0, tk.END)
self.taxa_atratividade_entry.insert(0, data.get("taxa_atratividade", ""))
messagebox.showinfo("Recuperar Dados", "Dados recuperados com sucesso!")
except Exception as e:
messagebox.showerror("Erro", f"Erro ao recuperar dados: {e}")
def limpar(self):
"""Limpa os campos de resultado e a variável de simulação (mantém os dados informados)."""
self.label_parcela.config(text="R$ 0,00")
self.label_roi.config(text="0%")
self.label_tir.config(text="0%")
self.label_vlp.config(text="R$ 0,00")
self.label_media_proj.config(text="R$ 0,00")
self.label_media_ultimos.config(text="R$ 0,00")
self.label_media_faltante.config(text="R$ 0,00")
self.parecer_label.config(text="Aguardando simulação...", foreground="green")
self.simulation_data = None
def nova_simulacao(self):
"""Limpa todos os campos para iniciar uma nova simulação."""
self.empresa_entry.delete(0, tk.END)
self.analista_entry.delete(0, tk.END)
self.de_entry.delete(0, tk.END)
self.para_entry.delete(0, tk.END)
self.data_analise.set(datetime.now().strftime("%d/%m/%Y"))
self.cidade_entry.delete(0, tk.END)
self.estado_entry.delete(0, tk.END)
for mes_entry, fat_entry in self.faturamento_entries:
mes_entry.delete(0, tk.END)
fat_entry.delete(0, tk.END)
self.sensibilidade_entry.delete(0, tk.END)
self.investimento_entry.delete(0, tk.END)
self.taxa_juros_entry.delete(0, tk.END)
self.prazo_entry.delete(0, tk.END)
self.taxa_atratividade_entry.delete(0, tk.END)
self.limpar()
def iniciar_interface():
root = tk.Tk()
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
app = InterfaceGrafica(root)
root.mainloop()
if __name__ == "__main__":
iniciar_interface()
c) gerar_relatorio.py
# src/gerar_relatorio.py
import locale
from datetime import datetime
from tabulate import tabulate
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
from reportlab.lib import colors
def gerar_relatorio(simulation_data, filename="relatorio.pdf"):
"""
Gera um relatório em PDF com cabeçalho, corpo e rodapé utilizando os dados informados pelo analista.
"""
try:
locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
except:
locale.setlocale(locale.LC_ALL, '')
# Extração dos dados
empresa = simulation_data.get("empresa", "Não Informado")
analista = simulation_data.get("analista", "Não Informado")
de = simulation_data.get("de", "Não Informado")
para = simulation_data.get("para", "Não Informado")
data_analise = simulation_data.get("data_analise", datetime.now().strftime("%d/%m/%Y"))
cidade = simulation_data.get("cidade", "Não Informado")
estado = simulation_data.get("estado", "Não Informado")
faturamento_tabela = simulation_data.get("faturamento_tabela", [])
sensibilidade = simulation_data.get("sensibilidade", 0)
investimento = simulation_data.get("investimento", 0)
taxa_juros = simulation_data.get("taxa_juros", 0)
prazo = simulation_data.get("prazo", 0)
taxa_atratividade = simulation_data.get("taxa_atratividade", 0)
resultado_simulacao = simulation_data.get("resultado_simulacao", {})
parecer = simulation_data.get("parecer", "")
media_faturamento = resultado_simulacao.get("media_faturamento", 0)
proj_receita = resultado_simulacao.get("projecao_receita", 0)
parcela = resultado_simulacao.get("parcela", 0)
npv = resultado_simulacao.get("npv", 0)
roi = resultado_simulacao.get("roi", 0)
tir = resultado_simulacao.get("tir") # Pode ser None
observacao = resultado_simulacao.get("observacao", "")
# Tratamento da TIR para exibição
if tir is None:
tir_text = "N/D"
else:
tir_text = f"{tir:.2f}%"
doc = SimpleDocTemplate(filename, pagesize=A4)
styles = getSampleStyleSheet()
story = []
# --- Novo Cabeçalho com Formatação Adequada ---
# Converte a data para formato extenso (ex.: "12 de fevereiro de 2025")
try:
data_dt = datetime.strptime(data_analise, "%d/%m/%Y")
data_extenso = data_dt.strftime("%d de %B de %Y")
except Exception:
data_extenso = data_analise
# Linha superior à direita: Cidade - Estado, Data (em formato extenso)
header1 = Paragraph(f"<para align='right'>{cidade} - {estado}, {data_extenso}</para>", styles["Normal"])
# Duas linhas para baixo: DE: (analista) e PARA:
header2 = Paragraph(f"<para align='left'>DE: (analista) {analista}</para>", styles["Normal"])
header3 = Paragraph(f"<para align='left'>PARA: {para}</para>", styles["Normal"])
# Duas linhas para baixo: Prezado Senhor,
header4 = Paragraph(f"<para align='left'>Prezado Senhor,</para>", styles["Normal"])
# Duas linhas depois: Parágrafo inicial
header5 = Paragraph(f"<para align='left'>Informo abaixo os resultados da análise do investimento solicitado.</para>", styles["Normal"])
# Adiciona os elementos do cabeçalho com espaçamentos adequados
story.append(header1)
story.append(Spacer(1, 12))
story.append(header2)
story.append(header3)
story.append(Spacer(1, 12))
story.append(header4)
story.append(Spacer(1, 12))
story.append(header5)
story.append(Spacer(1, 24))
# --- Tabela de Faturamentos ---
faturamento_data = [["Mês/Ano", "Faturamento"]]
for mes, fat in faturamento_tabela:
try:
fat_float = float(fat)
fat_str = locale.currency(fat_float, grouping=True)
except:
fat_str = "N/D"
faturamento_data.append([mes, fat_str])
table = Table(faturamento_data, colWidths=[100, 150])
table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.gray),
('TEXTCOLOR',(0,0),(-1,0),colors.whitesmoke),
('ALIGN',(0,0),(-1,-1),'CENTER'),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('BOTTOMPADDING', (0,0), (-1,0), 12),
('BACKGROUND',(0,1),(-1,-1),colors.beige),
('GRID',(0,0),(-1,-1),1,colors.black),
]))
story.append(table)
story.append(Spacer(1, 12))
# --- Parâmetros do Investimento ---
parametros = (
f"<b>Parâmetros do Investimento:</b><br/>"
f"Sensibilidade de Previsão: {sensibilidade}%<br/>"
f"Valor do Investimento: {locale.currency(float(investimento), grouping=True)}<br/>"
f"Taxa de Juros Compostos: {taxa_juros}%<br/>"
f"Prazo (meses): {prazo}<br/>"
f"Taxa de Atratividade: {taxa_atratividade}%<br/>"
)
story.append(Paragraph(parametros, styles["Normal"]))
story.append(Spacer(1, 12))
# --- Resultados da Simulação ---
sim_results = (
f"<b>Resultados da Simulação:</b><br/>"
f"Média do Faturamento: {locale.currency(float(media_faturamento), grouping=True)}<br/>"
f"Receita Projetada: {locale.currency(float(proj_receita), grouping=True)}<br/>"
f"Parcela Mensal: {locale.currency(float(parcela), grouping=True)}<br/>"
f"VLP (NPV): {locale.currency(float(npv), grouping=True)}<br/>"
f"ROI: {roi:.2f}%<br/>"
f"TIR: {tir_text}<br/>"
)
story.append(Paragraph(sim_results, styles["Normal"]))
story.append(Spacer(1, 12))
# --- Parecer do Analista (já definido na interface, texto resumido) ---
story.append(Paragraph(f"<b>Parecer do Analista:</b><br/>{parecer}", styles["Normal"]))
story.append(Spacer(1, 12))
# --- Rodapé ---
footer_text = f"Relatório gerado em: {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}"
story.append(Paragraph(footer_text, styles["Normal"]))
doc.build(story)
return filename
if __name__ == "__main__":
# Bloco de teste rápido com dados fictícios.
# OBSERVAÇÃO: Este bloco é apenas para testes locais. Em produção, os dados passados para gerar_relatorio()
# deverão ser os informados pelo analista por meio da interface.
dummy_data = {
"empresa": "Empresa Exemplo",
"analista": "Izairton Vasconcelos",
"de": "Izairton Vasconcelos",
"para": "Sr. Osmar Telo",
"data_analise": "12/02/2025",
"cidade": "Ponta",
"estado": "PR",
"faturamento_tabela": [("Jan/2025", "1000000.0"), ("Fev/2025", "1000000.0")],
"sensibilidade": 10,
"investimento": 1000000,
"taxa_juros": 2,
"prazo": 12,
"taxa_atratividade": 1,
"resultado_simulacao": {
"media_faturamento": 1000000.0,
"projecao_receita": 900000.0, # Exemplo: insuficiente para cobrir a parcela
"parcela": 94559.60,
"npv": -50000.0,
"roi": 5.0,
"tir": None, # Nesse cenário, fluxo de caixa negativo em todos os períodos
"observacao": "Investimento inviável"
},
"parecer": "Conforme os resultados apresentados, informo que o investimento é INVIÁVEL, considerando que a taxa interna de retorno - TIR foi de N/D e a Taxa de Retorno sobre os Investimentos foi de 5.00%, frente a uma parcela mensal fixa de R$ 94.559,60 pelo prazo de 12 meses. O faturamento médio para os próximos 12 meses é de R$ 900.000,00 (INSUFICIENTE) para arcar com o investimento."
}
gerar_relatorio(dummy_data, "relatorio_teste.pdf")
d) main.py
# main.py
from src.interface_grafica import iniciar_interface
if __name__ == "__main__":
iniciar_interface()
Siga-me no LinkedIn: www.linkedin.com/comm/mynetwork/discovery-see-all?usecase=PEOPLE_FOLLOWS&followMember=izairton-oliveira-de-vasconcelos-a1916351
Minha Newsletter, o link para assinar: https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7287106727202742273
https://github.com/IOVASCON/simulador_minimo_faturamento.git