Instituição: Pontifícia Universidade Católica do Paraná (PUCPR)
Disciplina: Programação Funcional
Professor: Frank Coelho de Alcantara
Atividade: RA2 - Sistema de Inventário em Haskell
| Nome do Aluno | GitHub Username | Responsabilidade |
|---|---|---|
| Ângelo Piovezan Jorgeto | @PJorgeto | Análise de Logs, Validação, Documentação e Testes |
| Fernando Alonso Piroga da Silva | @fernandooal | Lógica de Negócio Pura (módulo funcional) |
| Jafte Carneiro Fagundes da Silva | @cyberfika | Módulo de I/O, Persistência, Parser e Loop Principal |
| Renato Pestana Gouveia | @rpgouveia | Arquitetura de Dados e Tipos |
Sistema de gerenciamento de inventário desenvolvido em Haskell que implementa:
- ✅ Separação clara entre lógica pura e operações de I/O
- ✅ Persistência de dados em arquivo (
Inventario.dat) - ✅ Log de auditoria completo (
Auditoria.log) - ✅ Tratamento de erros com
Either - ✅ Validação de estoque e operações
- ✅ Interface interativa via terminal
O projeto pode ser executado diretamente no OnlineGDB:
👉 https://onlinegdb.com/74rIDCw5I
projeto/
├── InventarioTipos.hs # Tipos e estruturas do sistema
├── LogicaNegocio.hs # Funções puras (add, remove, update)
├── IOPersistencia.hs # Entrada/Saída e persistência
├── Analise.hs # Relatórios e análise de logs
├── Main.hs # Loop principal e parser CLI
├── TestesTipos.hs # Testes unitários dos tipos
├── TestesLogicaNegocio.hs # Testes puros da lógica de negócio
├── Inventario.dat # Persistência do inventário (runtime)
├── Auditoria.log # Auditoria de operações (runtime)
└── README.md # Este documento
# Compilar
ghc --make Main.hs -o inventario
# Executar
./inventarioNo OnlineGDB
- Acesse: https://onlinegdb.com/74rIDCw5I
- Selecione linguagem: Haskell
- Copie todos os arquivos
.hspara o editor (caso ainda não estejam todos lá) - Certifique-se de que
Main.hsestá como arquivo principal - Clique em Run
Link alternativo: https://onlinegdb.com/4xRBpLCc1
add <ID> <nome> <quantidade> <categoria>
Exemplo: add 001 Teclado 10 Perifericos
remove <ID> <quantidade>
Exemplo: remove 001 5
update <ID> <nova_quantidade>
Exemplo: update 001 20
list
report
help
exit
Objetivo: Verificar se o estado do inventário é persistido corretamente entre execuções.
Passos:
- Iniciar o programa (sem arquivos de dados)
- Executar os seguintes comandos:
add 001 Teclado 10 Perifericos add 002 Mouse 15 Perifericos add 003 Monitor 5 Monitores - Sair do programa com
exit - Verificar se os arquivos
Inventario.dateAuditoria.logforam criados - Reiniciar o programa
- Executar comando
list
Resultado Esperado:
- ✅ Arquivos
Inventario.dateAuditoria.logcriados - ✅ Ao reiniciar, o sistema carrega os 3 itens
- ✅ Comando
listexibe os 3 itens adicionados
Resultado Obtido:
==========================================
SISTEMA DE GERENCIAMENTO DE INVENTARIO
==========================================
[INICIALIZACAO] Carregando dados do sistema...
[INFO] Arquivo Inventario.dat nao encontrado.
[INFO] Iniciando com inventario vazio.
[INFO] Arquivo Auditoria.log nao encontrado.
[INFO] Iniciando com log de auditoria vazio.
[INFO] Inventario contem 0 item(ns)
[INFO] Log contem 0 entrada(s)
Digite 'help' para ver os comandos disponiveis.
> add 001 Teclado 10 Perifericos
[SUCESSO] Item adicionado: Teclado (ID: 001)
[PERSISTENCIA] Inventario salvo em Inventario.dat
[AUDITORIA] Log registrado em Auditoria.log
> add 002 Mouse 15 Perifericos
[SUCESSO] Item adicionado: Mouse (ID: 002)
[PERSISTENCIA] Inventario salvo em Inventario.dat
[AUDITORIA] Log registrado em Auditoria.log
> add 003 Monitor 5 Monitores
[SUCESSO] Item adicionado: Monitor (ID: 003)
[PERSISTENCIA] Inventario salvo em Inventario.dat
[AUDITORIA] Log registrado em Auditoria.log
> exit
==========================================
SISTEMA DE GERENCIAMENTO DE INVENTARIO
==========================================
[INICIALIZACAO] Carregando dados do sistema...
[INFO] Inventario carregado com sucesso!
[INFO] Log de auditoria carregado com sucesso!
[INFO] Inventario contem 3 item(ns)
[INFO] Log contem 3 entrada(s)
Digite 'help' para ver os comandos disponiveis.
> list
========== INVENTARIO ATUAL ==========
ID: 001
Nome: Teclado
Quantidade: 10
Categoria: Perifericos
---
ID: 002
Nome: Mouse
Quantidade: 15
Categoria: Perifericos
---
ID: 003
Nome: Monitor
Quantidade: 5
Categoria: Monitores
---
======================================
Status: [X] Passou | [ ] Falhou
Objetivo: Verificar se o sistema trata corretamente a tentativa de remover mais itens do que há em estoque.
Passos:
- Adicionar um item com 10 unidades:
add 004 Teclado_Gamer 10 Perifericos - Tentar remover 15 unidades:
remove 004 15 - Verificar mensagem de erro
- Executar
listpara confirmar que ainda há 10 unidades - Verificar o arquivo
Auditoria.log
Resultado Esperado:
- ✅ Sistema exibe mensagem: "Erro: Estoque insuficiente..."
- ✅ Comando
listmostra que o item ainda tem 10 unidades - ✅
Auditoria.logcontém entrada comStatusLog (Falha ...) - ✅
Inventario.datpermanece inalterado (10 unidades)
Resultado Obtido:
==========================================
SISTEMA DE GERENCIAMENTO DE INVENTARIO
==========================================
[INICIALIZACAO] Carregando dados do sistema...
[INFO] Inventario carregado com sucesso!
[INFO] Log de auditoria carregado com sucesso!
[INFO] Inventario contem 3 item(ns)
[INFO] Log contem 3 entrada(s)
Digite 'help' para ver os comandos disponiveis.
> add 004 Teclado_Gamer 10 Perifericos
[SUCESSO] Item adicionado: Teclado_Gamer (ID: 004)
[PERSISTENCIA] Inventario salvo em Inventario.dat
[AUDITORIA] Log registrado em Auditoria.log
> remove 004 15
[ERRO] Estoque insuficiente
[AUDITORIA] Log registrado em Auditoria.log
> list
========== INVENTARIO ATUAL ==========
ID: 001
Nome: Teclado
Quantidade: 10
Categoria: Perifericos
---
ID: 002
Nome: Mouse
Quantidade: 15
Categoria: Perifericos
---
ID: 003
Nome: Monitor
Quantidade: 5
Categoria: Monitores
---
ID: 004
Nome: Teclado_Gamer
Quantidade: 10
Categoria: Perifericos
---
======================================
Arquivo Auditoria.log:
LogEntry {timestamp = 2025-11-11 22:01:57.435906613 UTC, acao = Add, detalhes = "Adicionado item 'Teclado' (ID: 001) com quantidade 10 na categoria Perifericos", status = Sucesso}
LogEntry {timestamp = 2025-11-11 22:02:08.882197285 UTC, acao = Add, detalhes = "Adicionado item 'Mouse' (ID: 002) com quantidade 15 na categoria Perifericos", status = Sucesso}
LogEntry {timestamp = 2025-11-11 22:02:17.65382862 UTC, acao = Add, detalhes = "Adicionado item 'Monitor' (ID: 003) com quantidade 5 na categoria Monitores", status = Sucesso}
LogEntry {timestamp = 2025-11-11 22:05:45.181329383 UTC, acao = Add, detalhes = "Adicionado item 'Teclado_Gamer' (ID: 004) com quantidade 10 na categoria Perifericos", status = Sucesso}
LogEntry {timestamp = 2025-11-11 22:05:56.640684341 UTC, acao = Remove, detalhes = "Falha ao remover do item ID: 004", status = Falha "Estoque insuficiente"}
Status: [X] Passou | [ ] Falhou
Objetivo: Verificar se o relatório exibe corretamente as entradas de log de falhas.
Passos:
- Após executar o Cenário 2, executar:
report - Verificar se o relatório gerado pela função
logsDeErroexibe a entrada referente à falha do Cenário 2
Resultado Esperado:
- ✅ Relatório exibe a tentativa de remover estoque insuficiente
- ✅ Log mostra
StatusLog (Falha "Estoque insuficiente")
Resultado Obtido:
==========================================
SISTEMA DE GERENCIAMENTO DE INVENTARIO
==========================================
[INICIALIZACAO] Carregando dados do sistema...
[INFO] Inventario carregado com sucesso!
[INFO] Log de auditoria carregado com sucesso!
[INFO] Inventario contem 4 item(ns)
[INFO] Log contem 5 entrada(s)
Digite 'help' para ver os comandos disponiveis.
> report
[INFO] Carregando logs para gerar relatorio...
[INFO] Log de auditoria carregado com sucesso!
========== RELATORIO DE ANALISE DE LOGS ==========
** Sumario Geral **
Total de entradas de log: 5
Total de operacoes com falha: 1
Item mais movimentado (com sucesso): 001 (com 1 movimentacoes)
---
** Detalhe de Logs de Erro (1) **
[2025-11-14 00:30:16] [Remove] [FALHA: Estoque insuficiente] Falha ao remover do item ID: 004
====================================================
Digite um ID de item para ver seu historico (ou aperte o ENTER para pular):
> 004
--- Historico para Item ID: 004 ---
[2025-11-14 00:29:49] [Add] [SUCESSO] Adicionado item 'Teclado_Gamer' (ID: 004) com quantidade 10 na categoria Perifericos
[2025-11-14 00:30:16] [Remove] [FALHA: Estoque insuficiente] Falha ao remover do item ID: 004
---------------------------------------------
Status: [X] Passou | [ ] Falhou
Nota: A implementação detalhada das funções de relatório (logsDeErro, historicoPorItem, itemMaisMovimentado) será realizada pelo Aluno 4.
O sistema foi populado com 10 itens distintos para permitir testes adequados:
add 001 Teclado_Mecanico 15 Perifericos
add 002 Mouse_Gamer 20 Perifericos
add 003 Monitor_LED_24 8 Monitores
add 004 Headset_USB 12 Audio
add 005 Webcam_HD 10 Video
add 006 Mousepad_Grande 25 Acessorios
add 007 Hub_USB 18 Conectividade
add 008 Cabo_HDMI 30 Cabos
add 009 Adaptador_VGA 15 Adaptadores
add 010 SSD_500GB 6 ArmazenamentoDefine os tipos de dados fundamentais:
data Item = Item
{ itemID :: String
, nome :: String
, quantidade :: Int
, categoria :: String
} deriving (Show, Read, Eq)
type Inventario = Map String Item
data AcaoLog = Add | Remove | Update | QueryFail
deriving (Show, Read, Eq)
data StatusLog = Sucesso | Falha String
deriving (Show, Read, Eq)
data LogEntry = LogEntry
{ timestamp :: UTCTime
, acao :: AcaoLog
, detalhes :: String
, status :: StatusLog
} deriving (Show, Read, Eq)Implementa a lógica de negócio pura:
type ResultadoOperacao = (Inventario, LogEntry)
addItem :: UTCTime -> Item -> Inventario
-> Either String ResultadoOperacao
removeItem :: UTCTime -> String -> Int -> Inventario
-> Either String ResultadoOperacao
updateQty :: UTCTime -> String -> Int -> Inventario
-> Either String ResultadoOperacaoValidações implementadas:
- ✅ Quantidade deve ser >= 0
- ✅ Item ID não pode ser duplicado
- ✅ Item deve existir antes de remover/atualizar
- ✅ Estoque deve ser suficiente para remoção
Gerencia I/O e persistência:
Funções de Inicialização:
carregarInventario :: IO InventariocarregarLogs :: IO [LogEntry]
Funções de Persistência:
salvarInventario :: Inventario -> IO ()adicionarLogAuditoria :: LogEntry -> IO ()
Loop Principal:
main :: IO ()loopPrincipal :: Inventario -> IO ()processarComando :: Comando -> Inventario -> IO Inventario
Tratamento de Exceções:
- Usa
catchpara lidar comIOException - Inicia com estado vazio se arquivos não existirem
Funções de análise de logs:
historicoPorItem :: [LogEntry] -> String -> [LogEntry]logsDeErro :: [LogEntry] -> [LogEntry]itemMaisMovimentado :: [LogEntry] -> String
- Tipos de dados bem modelados
- Serialização (Show/Read) funciona
- Nomenclatura conforme especificação
- Separação clara entre funções puras e impuras
- Sistema carrega dados corretamente
- Sistema salva dados corretamente
- Log de auditoria funcional
- Relatórios implementados
- Código Haskell claro e comentado
- Uso adequado de
where,let,case - README.md completo
- Commits significativos no GitHub
- Tratamento de exceções de I/O (catch)
- Tratamento de erros de lógica (Either)
- Sistema não quebra com arquivos inexistentes
- Validações de entrada implementadas
O projeto segue rigorosamente o princípio de separação entre:
-
Funções Puras (LogicaNegocio.hs, Analise.hs):
- Não contêm nenhuma operação de I/O
- Retornam
Either String ResultadoOperacao(Logica) ou dados (Analise) - Totalmente testáveis e previsíveis
-
Funções Impuras (IOPersistencia.hs):
- Gerenciam toda a interação com arquivos
- Controlam o loop principal
- Fazem a ponte entre usuário e lógica pura
- Inventario.dat: Sobrescrito a cada operação bem-sucedida (writeFile)
- Auditoria.log: Modo append-only, nunca sobrescrito (appendFile)
- Ambos os arquivos usam
ShoweReadpara serialização
O sistema implementa dois níveis de tratamento de erros:
- Erros de Lógica: Tratados com
Either String - Erros de I/O: Tratados com
catcheIOException
Solução: É esperado na primeira execução. O sistema cria os arquivos automaticamente.
Solução: Deletar os arquivos .dat e .log e reiniciar o sistema.
Solução: Usar IDs únicos para cada item.
Para dúvidas sobre o projeto, entre em contato com os membros da equipe através do GitHub.
Última atualização: 14/11/2025
Versão: 1.4