paradigma-imperativo
Controle de Estoque e Vendas
Atividade Avaliativa AV1 da disciplina Paradigmas de Programação (UNAMA). Prof. Rodrigo Medeiros Costa.
Integrante: NOME COMPLETO (@johnpgr)
Linguagem: Crystal, com autorização do professor no lugar de C ou Python.
Vídeo de demonstração: LINK DO VÍDEO
Descrição do problema
Uma pequena loja precisa controlar o estoque de seus produtos e as vendas realizadas. O programa roda no terminal e mantém os dados em memória durante a execução.
Entradas
- Teclas de navegação no menu:
j(descer),k(subir),Enter(selecionar),q(sair). - Dados do produto: nome, preço unitário e quantidade inicial.
- Código do produto e quantidade, nas operações de entrada, venda e remoção.
Regras
- O código do produto é atribuído em sequência (1, 2, 3, ...).
- O nome não pode ser vazio, o preço deve ser maior que zero e a quantidade inicial não pode ser negativa.
- Uma entrada de estoque soma a quantidade recebida ao saldo. A quantidade deve ser maior que zero.
- Uma venda só é aceita se a quantidade for maior que zero e não passar do saldo. Se for aceita, o saldo diminui e a venda é registrada com o seu valor total.
- Um produto só pode ser removido quando o saldo é zero.
- Na listagem, produtos com saldo menor ou igual a 5 aparecem em vermelho (alerta de estoque baixo).
- Uma entrada numérica inválida (por exemplo,
abc) é pedida de novo.
Saídas
- Listagem de produtos com código, nome, preço e saldo.
- Mensagem de sucesso (verde) ou de erro (vermelho) após cada operação.
- Relatório de vendas com cada venda, o total de itens vendidos e o valor total.
Requisitos e execução
Requisito: Crystal 1.21.1 ou mais recente (instalação). O programa usa o modo raw do terminal, então deve ser executado em um terminal Linux ou macOS (no Windows, use o WSL).
git clone https://github.com/johnpgr/paradigma-imperativo
cd paradigma-imperativo
shards build
./bin/paradigma-imperativo
Para executar sem gerar o binário: crystal run src/main.cr.
Organização do código
| Arquivo | Conteúdo |
|---|---|
src/main.cr |
Estado do programa (variáveis) e laço principal com o menu |
src/inventory.cr |
Tipos Product e Sale e os procedimentos de estoque e vendas |
src/terminal.cr |
Leitura de teclas, leitura de valores e exibição de mensagens |
Explicação da solução
O programa é uma sequência de instruções dentro de um laço. A cada volta, ele desenha o menu com o estado atual, lê uma tecla, altera o estado conforme a tecla e repete. Todo o estado fica em quatro variáveis declaradas no início de src/main.cr (linhas 5 a 8):
menu_selected = 0
menu_items = ["Cadastrar produto", "Listar produtos", ...]
products = [] of Product
sales = [] of Sale
Os procedimentos recebem essas variáveis como parâmetros e as alteram. Nenhum dado fica escondido em objetos.
Sequência de instruções
O corpo do laço principal (src/main.cr:13-81) executa sempre na mesma ordem: (1) limpar a tela e desenhar o menu, (2) ler uma tecla, (3) executar a ação. Dentro de cada ação a ordem também é fixa. Por exemplo, ao registrar uma venda (src/main.cr:61-64), o programa lê o código, depois a quantidade, depois chama register_sale e por fim mostra o resultado.
Variáveis, atribuição e alteração de estado
menu_selectedmuda a cada teclajouk(src/main.cr:31e:33). O operador%faz a seleção voltar ao início quando passa do último item.product.quantity += quantitysoma ao saldo na entrada de estoque (src/inventory.cr:44).product.quantity -= quantitydiminui o saldo na venda (src/inventory.cr:54).products << ...esales << ...acrescentam registros às listas (src/inventory.cr:28e:55).products.delete(product)remove um registro (src/inventory.cr:76).
Product é uma classe (src/inventory.cr:4-12) porque product.quantity -= n precisa alterar o produto que está guardado na lista. Com uma struct, find_product retornaria uma cópia, e a alteração seria perdida.
Exemplo de alteração de estado:
| Operação | products |
sales |
|---|---|---|
| Início | [] |
[] |
| Cadastrar "Caneta", R$ 2,50, 10 un. | [{1, Caneta, 2.5, 10}] |
[] |
| Entrada de 5 un. do produto 1 | [{1, Caneta, 2.5, 15}] |
[] |
| Venda de 4 un. do produto 1 | [{1, Caneta, 2.5, 11}] |
[{1, Caneta, 4, 10.0}] |
| Venda de 20 un. do produto 1 | sem alteração ("Saldo insuficiente") | sem alteração |
Estruturas de decisão
case read_keyescolhe o que fazer com a tecla lida (src/main.cr:29-80), ecase menu_items[menu_selected]escolhe a ação do menu (src/main.cr:40-78).if index == menu_selecteddestaca o item selecionado (src/main.cr:21).if product.quantity <= LOW_STOCKmostra em vermelho os produtos com estoque baixo (src/main.cr:50).- As regras de negócio são cláusulas de guarda com
return ... if. Por exemplo, emregister_sale(src/inventory.cr:50-52), cada condição falsa encerra o procedimento com uma mensagem de erro antes de alterar o estado:
return "Produto não encontrado." unless product
return "A quantidade deve ser maior que zero." if quantity <= 0
return "Saldo insuficiente: #{product.quantity} em estoque." if quantity > product.quantity
Estruturas de repetição
loop do ... endé o laço principal do programa (src/main.cr:13). Ele termina combreakquando o usuário escolhe sair.each_with_indexpercorre os itens do menu para desenhá-los (src/main.cr:20).find_productfaz uma busca linear: percorre a lista e retorna o primeiro produto com o código pedido (src/inventory.cr:32-37).register_productpercorre a lista para achar o próximo código livre (src/inventory.cr:23-26).sales_summaryusa dois acumuladores,itemsetotal, que são atualizados a cada venda (src/inventory.cr:60-68).prompt_inteprompt_floatrepetem a pergunta até o usuário digitar um número válido (src/terminal.cr:17-31).
Funções e procedimentos
| Procedimento | Arquivo | O que faz |
|---|---|---|
register_product |
src/inventory.cr:18 |
Valida os dados e acrescenta um produto |
find_product |
src/inventory.cr:32 |
Busca um produto pelo código |
register_entry |
src/inventory.cr:39 |
Soma uma quantidade ao saldo |
register_sale |
src/inventory.cr:48 |
Diminui o saldo e registra a venda |
sales_summary |
src/inventory.cr:60 |
Calcula os itens vendidos e o valor total |
remove_product |
src/inventory.cr:71 |
Remove um produto com saldo zero |
hide_cursor, show_cursor, clear_screen |
src/terminal.cr:7-17 |
Controlam visibilidade do cursor e limpeza de tela |
read_key |
src/terminal.cr:20 |
Lê uma tecla sem esperar o Enter |
prompt, prompt_int, prompt_float |
src/terminal.cr:24-43 |
Leem um texto ou um número |
print_result |
src/terminal.cr:50 |
Mostra a mensagem de sucesso ou de erro |
Os procedimentos que alteram o estado retornam String?: uma mensagem de erro, ou nil quando a operação é realizada. Assim, main.cr trata o resultado de todas as operações da mesma forma, com print_result.
Reflexão
Decisões
- Usei Crystal porque a sintaxe é próxima de Python e o programa é compilado com tipos verificados, como em C.
- O modo raw do terminal é ativado só para ler uma tecla do menu. Os formulários usam a leitura de linha comum, então não foi necessário escrever um editor de linha.
- As regras ficam em procedimentos separados da interface (
inventory.cr). Cada procedimento recebe as listas como parâmetros, assim fica claro qual estado cada operação altera. - A venda guarda o seu valor total no momento em que é feita, para que o relatório não mude se o preço do produto mudar depois.
- Os dados ficam só em memória, para manter o foco nos conceitos do paradigma imperativo.
Dificuldades
- No modo raw,
Enterenvia\re não\n, e a quebra de linha não volta o cursor ao início. Por isso o modo raw ficou restrito à leitura de uma tecla. - Com
Productcomostruct, alterar o saldo de um produto encontrado na lista não tinha efeito, porque a alteração era feita em uma cópia. A solução foi usar uma classe. - Garantir que o cursor do terminal volte a aparecer quando o programa termina com
Ctrl+C(at_exiteSignal::INT.trapemsrc/main.cr:10-11).
Melhorias possíveis
- Salvar os dados em arquivo para não perdê-los ao fechar o programa.
- Buscar produtos pelo nome.
- Alterar o preço e o nome de um produto.
- Registrar a data de cada venda e mostrar o relatório por período.
paradigma-imperativo
- 0
- 0
- 0
- 0
- 0
- about 3 hours ago
- September 30, 2026
Fri, 02 Oct 2026 05:18:29 GMT