🌗 ← Índice

🐍 Python P10 — Desafio Final Mês 1


Data: 09/07/2026

Nível: Intermediário (Integrador)




Chegou a hora de juntar tudo que tu aprendeu no Mês 1 em um projeto real. Nada de exercício isolado — é um sistema completo, com entrada de usuário, arquivos, tratamento de erros e código organizado.




🎯 O Projeto: **Gestor de Vendas**


Um programa de terminal que gerencia vendas de uma loja:

  • Cadastra vendas com produto, valor e data
  • Salva em JSON (persistência real)
  • Lista vendas com filtros
  • Gera relatório de faturamento
  • Trata erros com elegância
  • Código organizado em módulos



  • 📁 Estrutura do projeto


    
    gestor_vendas/
    ├── main.py              # Menu e entrada do programa
    ├── modelos/
    │   ├── __init__.py
    │   └── venda.py         # Classe Venda
    ├── servicos/
    │   ├── __init__.py
    │   └── arquivo.py       # Salvar/carregar JSON
    └── utils/
        ├── __init__.py
        └── validacao.py     # Validação de entrada
    



    📋 Requisitos


    1. Classe `Venda` (`modelos/venda.py`)


    
    class Venda:
        def __init__(self, produto, valor, data, categoria="Geral"):
            self.produto = produto
            self.valor = valor      # float
            self.data = data        # string "DD/MM/AAAA"
            self.categoria = categoria
        
        def to_dict(self):
            """Converte pra dicionário (pra salvar em JSON)."""
            pass  # Implementa
        
        @classmethod
        def from_dict(cls, d):
            """Cria uma Venda a partir de um dicionário."""
            pass  # Implementa
        
        def __str__(self):
            """Representação bonita pra print."""
            pass  # Implementa
    

    2. Serviço de Arquivo (`servicos/arquivo.py`)


    
    import json
    from modelos.venda import Venda
    
    ARQUIVO = "vendas.json"
    
    def salvar(vendas):
        """Salva lista de vendas em JSON."""
        pass  # Implementa — use json.dump com indent=2
    
    def carregar():
        """Carrega lista de vendas do JSON. Retorna [] se não existir."""
        pass  # Implementa — trate FileNotFoundError
    

    3. Utilitários (`utils/validacao.py`)


    
    from datetime import datetime
    
    def validar_data(data_str):
        """Valida formato DD/MM/AAAA. Lança ValueError se inválida."""
        pass  # Implementa — use datetime.strptime + try/except
    
    def validar_valor(valor_str):
        """Converte string pra float. Lança ValueError se inválido."""
        pass  # Implementa — tente float(), capture ValueError
    
    def validar_nao_vazio(texto, campo="campo"):
        """Se texto vazio, lança ValueError com mensagem amigável."""
        pass  # Implementa
    

    4. Menu Principal (`main.py`)


    
    ╔══════════════════════════════════╗
    ║     🏪 GESTOR DE VENDAS v1.0    ║
    ╠══════════════════════════════════╣
    ║  1. ➕ Nova venda                ║
    ║  2. 📋 Listar vendas             ║
    ║  3. 🔍 Filtrar por data          ║
    ║  4. 📊 Relatório de faturamento  ║
    ║  5. 🚪 Sair                      ║
    ╚══════════════════════════════════╝
    

    Opções:

    1. Nova venda: pede produto, valor, data, categoria. Valida tudo. Salva.

    2. Listar vendas: mostra todas com print(venda). Numeradas.

    3. Filtrar por data: pede data, mostra vendas daquele dia.

    4. Relatório: mostra total de vendas, faturamento total, média por venda, top 3 produtos.




    🧩 Regras de negócio


  • Valor deve ser > 0
  • Data deve ser válida (formato DD/MM/AAAA, data real)
  • Produto não pode ser vazio
  • Categoria é opcional (default "Geral")
  • Se `vendas.json` não existir, começa com lista vazia
  • Ao sair, salva automaticamente



  • 🧪 Como testar


    
    cd gestor_vendas
    python3 main.py
    

    1. Cadastre 3-4 vendas de exemplo

    2. Liste pra ver se aparecem

    3. Filtre por uma data específica

    4. Gere o relatório

    5. Saia e entre de novo — os dados devem persistir!




    🏆 Critérios de sucesso


    CritérioPeso

    |---|---|

    Programa roda sem erroEssencial
    Dados persistem entre sessões (JSON)Essencial
    Validação de entrada (não quebra com input ruim)Essencial
    Relatório com cálculos corretosEssencial
    Código organizado em módulos (não 1 arquivo só)Importante
    Classes usadas corretamente (`Venda`)Importante
    Tratamento de erros (`try/except`)Importante
    Menu bonito e usávelDesejável
    Filtro por data funcionalDesejável



    💡 Dicas antes de começar


    1. Comece pelo modelo — crie Venda primeiro, teste no terminal

    2. Depois o arquivo — implemente salvar() e carregar(), teste com 2-3 vendas fake

    3. Validação depois — as funções de validacao.py são independentes

    4. Menu por último — monte o main.py integrando tudo

    5. Teste cada parte isolada antes de juntar




    🔓 Gabarito (só depois de tentar!)


    modelos/venda.py


    
    class Venda:
        def __init__(self, produto, valor, data, categoria="Geral"):
            self.produto = produto
            self.valor = float(valor)
            self.data = data
            self.categoria = categoria
    
        def to_dict(self):
            return {
                "produto": self.produto,
                "valor": self.valor,
                "data": self.data,
                "categoria": self.categoria
            }
    
        @classmethod
        def from_dict(cls, d):
            return cls(d["produto"], d["valor"], d["data"], d.get("categoria", "Geral"))
    
        def __str__(self):
            return f"{self.data} | {self.produto:20s} | R$ {self.valor:8.2f} | {self.categoria}"
    


    servicos/arquivo.py


    
    import json
    from modelos.venda import Venda
    
    ARQUIVO = "vendas.json"
    
    def salvar(vendas):
        with open(ARQUIVO, "w") as f:
            json.dump([v.to_dict() for v in vendas], f, indent=2, ensure_ascii=False)
    
    def carregar():
        try:
            with open(ARQUIVO) as f:
                return [Venda.from_dict(d) for d in json.load(f)]
        except (FileNotFoundError, json.JSONDecodeError):
            return []
    


    utils/validacao.py


    
    from datetime import datetime
    
    def validar_data(data_str):
        try:
            datetime.strptime(data_str, "%d/%m/%Y")
            return data_str
        except ValueError:
            raise ValueError(f"Data inválida: '{data_str}'. Use DD/MM/AAAA.")
    
    def validar_valor(valor_str):
        try:
            valor = float(valor_str)
            if valor <= 0:
                raise ValueError("Valor deve ser maior que zero.")
            return valor
        except ValueError:
            raise ValueError(f"Valor inválido: '{valor_str}'. Digite um número.")
    
    def validar_nao_vazio(texto, campo="campo"):
        if not texto or not texto.strip():
            raise ValueError(f"O {campo} não pode estar vazio.")
        return texto.strip()
    


    main.py


    
    from modelos.venda import Venda
    from servicos.arquivo import salvar, carregar
    from utils.validacao import validar_data, validar_valor, validar_nao_vazio
    
    def menu():
        print("""
    ╔══════════════════════════════════╗
    ║     🏪 GESTOR DE VENDAS v1.0    ║
    ╠══════════════════════════════════╣
    ║  1. ➕ Nova venda                ║
    ║  2. 📋 Listar vendas             ║
    ║  3. 🔍 Filtrar por data          ║
    ║  4. 📊 Relatório de faturamento  ║
    ║  5. 🚪 Sair                      ║
    ╚══════════════════════════════════╝""")
        return input("Escolha: ")
    
    def nova_venda(vendas):
        try:
            produto = validar_nao_vazio(input("Produto: "), "produto")
            valor = validar_valor(input("Valor (R$): "))
            data = validar_data(input("Data (DD/MM/AAAA): "))
            categoria = input("Categoria [Geral]: ").strip() or "Geral"
            vendas.append(Venda(produto, valor, data, categoria))
            print("✅ Venda registrada!")
        except ValueError as e:
            print(f"❌ {e}")
    
    def listar_vendas(vendas):
        if not vendas:
            print("Nenhuma venda registrada.")
            return
        for i, v in enumerate(vendas, 1):
            print(f"{i:3d}. {v}")
    
    def filtrar_por_data(vendas):
        try:
            data = validar_data(input("Data (DD/MM/AAAA): "))
            filtradas = [v for v in vendas if v.data == data]
            if filtradas:
                print(f"\nVendas em {data}:")
                for v in filtradas:
                    print(f"  {v}")
                print(f"Total: R$ {sum(v.valor for v in filtradas):.2f}")
            else:
                print(f"Nenhuma venda em {data}.")
        except ValueError as e:
            print(f"❌ {e}")
    
    def relatorio(vendas):
        if not vendas:
            print("Nenhuma venda registrada.")
            return
        
        total = sum(v.valor for v in vendas)
        media = total / len(vendas)
        
        # Top 3 produtos por valor total
        por_produto = {}
        for v in vendas:
            por_produto[v.produto] = por_produto.get(v.produto, 0) + v.valor
        
        top3 = sorted(por_produto.items(), key=lambda x: x[1], reverse=True)[:3]
        
        print(f"""
    ╔══════════════════════════════════╗
    ║        📊 RELATÓRIO             ║
    ╠══════════════════════════════════╣
    ║  Total de vendas: {len(vendas):>13d} ║
    ║  Faturamento:     R$ {total:>10.2f} ║
    ║  Média/venda:     R$ {media:>10.2f} ║
    ╠══════════════════════════════════╣
    ║  🏆 TOP 3 PRODUTOS:             ║""")
        for i, (prod, val) in enumerate(top3, 1):
            print(f"║  {i}. {prod[:25]:25s} R$ {val:>8.2f} ║")
        print("╚══════════════════════════════════╝")
    
    def main():
        vendas = carregar()
        print(f"📂 {len(vendas)} vendas carregadas.")
        
        while True:
            opcao = menu()
            if opcao == "1":
                nova_venda(vendas)
            elif opcao == "2":
                listar_vendas(vendas)
            elif opcao == "3":
                filtrar_por_data(vendas)
            elif opcao == "4":
                relatorio(vendas)
            elif opcao == "5":
                salvar(vendas)
                print(f"💾 {len(vendas)} vendas salvas. Até mais!")
                break
            else:
                print("Opção inválida.")
    
    if __name__ == "__main__":
        main()
    




    🎓 O que tu praticou nesse projeto


    ConceitoOnde foi usado

    |---|---|

    **Variáveis e tipos**Atributos da Venda, cálculos
    **Condicionais**Menu, validações, filtros
    **Listas e loops**`for v in vendas`, list comprehension
    **Funções**Cada opção do menu é uma função
    **Dicionários**`to_dict()`, agrupamento por produto
    **Arquivos e JSON**`salvar()` e `carregar()`
    **Tratamento de erros**Validação de entrada (`try/except`)
    **Classes**`Venda` com `__init__`, `__str__`, `@classmethod`
    **Módulos e pacotes**Projeto dividido em `modelos/`, `servicos/`, `utils/`
    **Strings e regex**Formatação, validação de data



    🚀 Extras (se quiser ir além)


    1. Adicione opção de remover venda

    2. Relatório por categoria

    3. Exportar pra CSV (use o que aprendeu em P06)

    4. Gráfico ASCII de faturamento por mês




    **🏁 Parabéns! Mês 1 concluído.** Tu já consegue construir sistemas Python completos e organizados. No Mês 2: Programação Orientada a Objetos e APIs.