diff --git a/00 - Fundamentos/desafio.py b/00 - Fundamentos/desafio.py index c2179f64d..9a0512f62 100644 --- a/00 - Fundamentos/desafio.py +++ b/00 - Fundamentos/desafio.py @@ -1,66 +1,283 @@ -menu = """ +#!/usr/bin/env python3 +import re +from typing import Dict, List, Tuple, Optional +menu = """ [d] Depositar [s] Sacar [e] Extrato +[nu] Novo usuário +[nc] Nova conta +[lc] Listar contas [q] Sair => """ -saldo = 0 -limite = 500 -extrato = "" -numero_saques = 0 -LIMITE_SAQUES = 3 +# Estruturas de dados +usuarios: List[Dict] = [] # cada usuário: {"nome","data_nascimento","cpf","endereco"} +contas: List[Dict] = [] # cada conta: {"agencia","numero","usuario","saldo","limite","extrato","numero_saques"} +PROXIMO_NUMERO_CONTA = 1 +AGENCIA_PADRAO = "0001" +LIMITE_PADRAO = 500.0 +LIMITE_SAQUES_PADRAO = 3 -while True: +# ------------------------- +# Funções pedidas pelo enunciado +# ------------------------- - opcao = input(menu) +# Saque -> apenas keyword-only +def sacar(*, saldo: float, valor: float, extrato: str, limite: float, numero_saques: int, limite_saques: int) -> Tuple[float, str, int, str]: + """ + Recebe somente por nome (keyword-only). + Retorna (novo_saldo, novo_extrato, novo_numero_saques, mensagem). + """ + mensagem = "" + if valor <= 0: + mensagem = "Operação falhou! O valor informado é inválido." + return saldo, extrato, numero_saques, mensagem - if opcao == "d": - valor = float(input("Informe o valor do depósito: ")) + if valor > saldo: + mensagem = "Operação falhou! Você não tem saldo suficiente." + return saldo, extrato, numero_saques, mensagem - if valor > 0: - saldo += valor - extrato += f"Depósito: R$ {valor:.2f}\n" + if valor > limite: + mensagem = "Operação falhou! O valor do saque excede o limite." + return saldo, extrato, numero_saques, mensagem - else: - print("Operação falhou! O valor informado é inválido.") + if numero_saques >= limite_saques: + mensagem = "Operação falhou! Número máximo de saques excedido." + return saldo, extrato, numero_saques, mensagem + + # se passou por todas as checagens: + saldo -= valor + extrato += f"Saque: R$ {valor:.2f}\n" + numero_saques += 1 + mensagem = f"Saque de R$ {valor:.2f} realizado com sucesso." + return saldo, extrato, numero_saques, mensagem + + +# Depósito -> positional-only +def depositar(saldo: float, valor: float, extrato: str, /) -> Tuple[float, str, str]: + """ + Recebe apenas por posição (positional-only). + Retorna (novo_saldo, novo_extrato, mensagem). + """ + mensagem = "" + if valor <= 0: + mensagem = "Operação falhou! O valor informado é inválido." + return saldo, extrato, mensagem + + saldo += valor + extrato += f"Depósito: R$ {valor:.2f}\n" + mensagem = f"Depósito de R$ {valor:.2f} realizado com sucesso." + return saldo, extrato, mensagem + + +# Extrato -> positional only + keyword only +def mostrar_extrato(saldo: float, /, *, extrato: str) -> None: + """ + Recebe saldo como positional-only e extrato como keyword-only. + Apenas imprime o extrato. + """ + print("\n================ EXTRATO ================") + print("Não foram realizadas movimentações." if not extrato else extrato, end="") + print(f"\nSaldo: R$ {saldo:.2f}") + print("==========================================\n") + + +# ------------------------- +# Novas funções: criar usuário e criar conta +# ------------------------- + +def limpar_cpf(cpf_raw: str) -> str: + """Remove tudo que não for dígito do CPF e retorna só dígitos.""" + return re.sub(r"\D", "", cpf_raw) + + +def existe_usuario_por_cpf(cpf: str) -> bool: + cpf = limpar_cpf(cpf) + for u in usuarios: + if u["cpf"] == cpf: + return True + return False + + +def criar_usuario(nome: str, data_nascimento: str, cpf: str, endereco: str) -> Tuple[bool, str]: + """ + Cria um usuário e o adiciona à lista 'usuarios'. + Retorna (sucesso, mensagem). + """ + cpf_clean = limpar_cpf(cpf) + if len(cpf_clean) != 11: + return False, "CPF inválido: deve conter 11 dígitos." + + if existe_usuario_por_cpf(cpf_clean): + return False, "Já existe usuário com esse CPF." + + usuario = { + "nome": nome.strip(), + "data_nascimento": data_nascimento.strip(), + "cpf": cpf_clean, + "endereco": endereco.strip(), + } + usuarios.append(usuario) + return True, "Usuário cadastrado com sucesso." + + +def buscar_usuario_por_cpf(cpf: str) -> Optional[Dict]: + cpf_clean = limpar_cpf(cpf) + for u in usuarios: + if u["cpf"] == cpf_clean: + return u + return None + + +def criar_conta(usuario_cpf: str) -> Tuple[bool, str]: + """ + Cria uma conta vinculada ao usuário cujo CPF for informado. + Retorna (sucesso, mensagem). + """ + global PROXIMO_NUMERO_CONTA + usuario = buscar_usuario_por_cpf(usuario_cpf) + if not usuario: + return False, "Usuário não encontrado para o CPF informado." + + conta = { + "agencia": AGENCIA_PADRAO, + "numero": PROXIMO_NUMERO_CONTA, + "usuario": usuario, + "saldo": 0.0, + "limite": LIMITE_PADRAO, + "extrato": "", + "numero_saques": 0, + "limite_saques": LIMITE_SAQUES_PADRAO, + } + contas.append(conta) + PROXIMO_NUMERO_CONTA += 1 + return True, f"Conta criada com sucesso. Agência: {conta['agencia']} Número: {conta['numero']}" + + +def listar_contas() -> None: + if not contas: + print("Nenhuma conta cadastrada.") + return + + print("\n=== Contas cadastradas ===") + for c in contas: + usuario = c["usuario"] + print(f"Agência: {c['agencia']} | Conta: {c['numero']} | Titular: {usuario['nome']} | CPF: {usuario['cpf']}") + print("=========================\n") + + +# ------------------------- +# Funções de seleção / interação +# ------------------------- + +def selecionar_conta() -> Optional[Dict]: + """Pede agência e número e retorna a conta correspondente (ou None).""" + if not contas: + print("Não há contas cadastradas.") + return None + + agencia = input("Informe a agência (ex: 0001): ").strip() + try: + numero = int(input("Informe o número da conta: ")) + except ValueError: + print("Número de conta inválido.") + return None + + for c in contas: + if c["agencia"] == agencia and c["numero"] == numero: + return c + + print("Conta não encontrada.") + return None + + +# ------------------------- +# Loop principal interativo +# ------------------------- + +def main_loop(): + while True: + opcao = input(menu).strip().lower() + + if opcao == "d": + conta = selecionar_conta() + if not conta: + continue + + try: + valor = float(input("Informe o valor do depósito: ")) + except ValueError: + print("Valor inválido.") + continue + + novo_saldo, novo_extrato, mensagem = depositar(conta["saldo"], valor, conta["extrato"]) + conta["saldo"] = novo_saldo + conta["extrato"] = novo_extrato + print(mensagem) + + elif opcao == "s": + conta = selecionar_conta() + if not conta: + continue + + try: + valor = float(input("Informe o valor do saque: ")) + except ValueError: + print("Valor inválido.") + continue - elif opcao == "s": - valor = float(input("Informe o valor do saque: ")) + saldo, extrato, numero_saques, mensagem = sacar( + saldo=conta["saldo"], + valor=valor, + extrato=conta["extrato"], + limite=conta["limite"], + numero_saques=conta["numero_saques"], + limite_saques=conta["limite_saques"] + ) + conta["saldo"] = saldo + conta["extrato"] = extrato + conta["numero_saques"] = numero_saques + print(mensagem) - excedeu_saldo = valor > saldo + elif opcao == "e": + conta = selecionar_conta() + if not conta: + continue - excedeu_limite = valor > limite + mostrar_extrato(conta["saldo"], extrato=conta["extrato"]) - excedeu_saques = numero_saques >= LIMITE_SAQUES + elif opcao == "nu": + nome = input("Nome completo: ") + data_nascimento = input("Data de nascimento (DD/MM/AAAA): ") + cpf = input("CPF (somente números ou com pontuação): ") + logradouro = input("Logradouro (ex: Rua ABC, 10): ") + bairro = input("Bairro: ") + cidade = input("Cidade: ") + estado = input("UF (sigla): ") + endereco = f"{logradouro} - {bairro} - {cidade}/{estado}" - if excedeu_saldo: - print("Operação falhou! Você não tem saldo suficiente.") + ok, msg = criar_usuario(nome, data_nascimento, cpf, endereco) + print(msg) - elif excedeu_limite: - print("Operação falhou! O valor do saque excede o limite.") + elif opcao == "nc": + cpf_para_vinculo = input("Informe o CPF do usuário para vincular a conta: ") + ok, msg = criar_conta(cpf_para_vinculo) + print(msg) - elif excedeu_saques: - print("Operação falhou! Número máximo de saques excedido.") + elif opcao == "lc": + listar_contas() - elif valor > 0: - saldo -= valor - extrato += f"Saque: R$ {valor:.2f}\n" - numero_saques += 1 + elif opcao == "q": + print("Encerrando. Até logo!") + break else: - print("Operação falhou! O valor informado é inválido.") + print("Operação inválida, por favor selecione novamente a operação desejada.") - elif opcao == "e": - print("\n================ EXTRATO ================") - print("Não foram realizadas movimentações." if not extrato else extrato) - print(f"\nSaldo: R$ {saldo:.2f}") - print("==========================================") - elif opcao == "q": - break +if __name__ == "__main__": + main_loop() - else: - print("Operação inválida, por favor selecione novamente a operação desejada.") diff --git a/README.md b/README.md index 0831f58c9..6d21f1c79 100644 --- a/README.md +++ b/README.md @@ -1 +1,83 @@ -# Trilha Python DIO +# 📌 Sistema Bancário em Python — Bootcamp Luizalabs Back-end com Python (DIO) + +Este projeto foi desenvolvido como parte do **primeiro desafio do Bootcamp Luizalabs Back-end com Python**, oferecido pela Digital Innovation One (DIO). +O objetivo principal foi evoluir o sistema bancário inicialmente proposto, tornando-o **modularizado**, mais organizado e capaz de realizar o **cadastro de usuários e contas bancárias**. + +--- + +## 🎯 Objetivos do Desafio + +### ✔ Modularização completa do código +As operações foram separadas em funções, respeitando regras específicas sobre como os argumentos devem ser recebidos: +- **Depósito** → argumentos *positional-only* +- **Saque** → argumentos *keyword-only* +- **Extrato** → combinação de *positional-only* e *keyword-only* + +### ✔ Novas funcionalidades implementadas +Além das operações de saque, depósito e extrato, o sistema agora inclui: + +- **Cadastro de Usuários** + - Nome + - Data de nascimento + - CPF (somente números, não pode repetir) + - Endereço completo + +- **Cadastro de Contas Correntes** + - Agência padrão: `0001` + - Número da conta sequencial + - Uma conta pertence a um usuário; um usuário pode ter várias contas + +- **Listagem de contas existentes** + +---- + +## 🧠 O que aprendi neste desafio + +- Organização do código usando funções +- Uso de *positional-only* (`/`) e *keyword-only* (`*`) no Python +- Estruturação de dados com listas e dicionários +- Boas práticas de modularização +- Criação de múltiplos fluxos no menu principal +- Operações bancárias simuladas (saque, depósito, extrato) +- Validação de CPF e controle de múltiplas contas +- Fluxo completo de Git: + - Fork → Clone → Branch → Commit → Push → Pull Request + +---- + +## 🏗 Estrutura do Projeto +trilha-python-dio/ +└── 00 - Fundamentos/ +└── desafio.py + + +O código final refatorado se encontra no arquivo **`desafio.py`** dentro da pasta *"00 - Fundamentos"*. + +--- + +## 🚀 Como executar o projeto + +1. Clone o repositório: + ```bash + git clone https://github.com/lexsantana/trilha-python-dio + + +## Entre na pasta +cd trilha-python-dio + +## Execute o script +python "00 - Fundamentos/desafio.py" + +## 📚 Tecnologias + +- Python 3 +- Git / GitHub +- VS Code + +## ✨ Sobre mim + +💼 Alexsander, graduado em Engenharia Aeronáutica e Mecânica, com pós-graduação em Engenharia Submarina pela Universidade Petrobras +💻 Estudante de Python e entusiasta de IA +📈 Construindo portfólio para área de tecnologia + +** Nov 18 2025