@marcos_feitoza/personal-finance-frontend
v1.12.6
Published
Este repositório contém o frontend principal da aplicação de finanças pessoais. Ele é construído com Flutter e utiliza uma arquitetura modular para integrar funcionalidades de vários módulos de features.
Readme
Personal Finance - Aplicação Frontend (Flutter)
Este repositório contém o frontend principal da aplicação de finanças pessoais. Ele é construído com Flutter e utiliza uma arquitetura modular para integrar funcionalidades de vários módulos de features.
Visão Geral
O projeto utiliza uma arquitetura de microsserviços no backend e uma abordagem modular no frontend para separar as responsabilidades e permitir o desenvolvimento e a escalabilidade independentes de cada componente. O frontend, em Flutter, se comunica com o backend composto por vários serviços em Python.
Diagrama da Arquitetura
O diagrama abaixo ilustra a estrutura atual dos microsserviços e dos pacotes do frontend, e como eles se interconectam.

Diagrama futuro (visão de evolução)
Se a aplicação crescer (mais usuários, mais tráfego, integrações, jobs assíncronos), pode fazer sentido introduzir componentes como Redis (cache/rate limit) e mensageria/event streaming (RabbitMQ ou Pulsar).
Para gerar um PNG do diagrama futuro (via Python + Graphviz):
- Script:
./generate_future_diagram.py - Output:
./architecture_future_diagram.png
Projetos
A aplicação é dividida nos seguintes projetos:
Frontend
O frontend é uma aplicação Flutter dividida em uma arquitetura de pacotes para máxima separação e manutenibilidade:
personal-finance-frontend: A aplicação "shell" que hospeda e integra todos os outros módulos de frontend.personal-finance-frontend-core-ui: Um pacote de biblioteca contendo todos os widgets de UI reutilizáveis (formulários, diálogos, etc.).personal-finance-frontend-core-services: Um pacote de biblioteca que lida com a comunicação com o backend, contendo todos os serviços e modelos de dados.personal-finance-frontend-feature-dashboard: O módulo de feature que contém a tela principal do dashboard.personal-finance-frontend-feature-reports: O módulo de feature para todas as telas de relatórios.personal-finance-frontend-feature-charts: O módulo de feature para todas as telas de gráficos.personal-finance-frontend-feature-investments: O módulo de feature para as telas de visualização e gerenciamento de investimentos.personal-finance-frontend-feature-management: O módulo de feature para as telas de gerenciamento de transações e reconciliação.
Backend
personal-finance-backend-core: O microsserviço principal que lida com a lógica de negócios central, incluindo transações, contas e balanços.personal-finance-backend-market-data: Um microsserviço dedicado a buscar dados de mercado em tempo real, como cotações de ações.personal-finance-backend-trades-assets: Uma biblioteca de código para a lógica de criação e gerenciamento de trades (negociações) e ativos de investimento.personal-finance-backend-shared: Uma biblioteca de código Python compartilhada contendo os modelos de banco de dados (SQLAlchemy), schemas (Pydantic) e utilitários comuns a todos os serviços de backend.
Cada projeto contém seu próprio README.md com detalhes específicos de sua função e configuração.
Como Começar
Pré-requisitos
- Flutter SDK (instalação do Flutter)
- Um editor de código com suporte a Flutter (VS Code, Android Studio, IntelliJ IDEA).
Instalação
- Navegue até o diretório raiz do projeto (
personal-finance-frontend). - Obtenha as dependências do Flutter:
flutter pub get
Configuração
O aplicativo precisa se comunicar com o backend. A URL base da API do backend pode ser configurada via --dart-define durante o build ou run.
Auth: Após login, o app recebe um par
access_token+refresh_token.
- O
access_token(JWT) é enviado emAuthorization: Bearer <token>em chamadas protegidas.- O
refresh_tokené usado para renovar oaccess_tokensem exigir relogin.
Admin: Rotas
/api/admin/*exigem um usuário comrole=adminourole=support_admin. O app do usuário comum não expõe funções administrativas.
# Exemplo de uso para definir a URL da API
flutter run --dart-define=BASE_API_URL=http://localhost:8000Se BASE_API_URL não for definida, um valor padrão será usado (ver transaction_service.dart ou auth_service.dart em personal-finance-frontend-core-services).
Conceito atual de Admin Console
A experiência administrativa é centralizada em uma tela dedicada com abas:
Users: gestão de plano, role, status, segurança e créditos AIAudit Logs: trilha de ações administrativas (quem fez, quando, antes/depois, ip/request_id)AI Observability: visão operacional de AI por usuário (centralizada no admin)
Importante:
- a opção AI Observability foi removida do menu de usuário comum
- observability operacional agora é consumida no Admin Console
Padrões de Formatação (UI)
Para reduzir duplicação e evitar inconsistências entre telas, as formatações principais foram centralizadas no pacote core-ui.
Datas (padrão único)
Decisão do projeto: toda data exibida ao usuário e enviada para a API usa o formato:
yyyy-MM-dd
Helpers:
personal-finance-frontend-core-ui/lib/utils/dates.dartDates.formatUi(date)Dates.formatApi(date)- (quando precisar em listas)
Dates.formatCompactDate(date)
Dinheiro
Para exibir valores monetários com consistência (separador de milhar, 2 casas, etc.), use:
personal-finance-frontend-core-ui/lib/utils/money.dartMoney.format(value, currency: CurrencyCode.cad)Money.fixed(value)
Nota: a conversão CAD/USD está preparada no util, mas o fetch automático de cotação (API grátis) fica como próximo passo.
Como validar rapidamente na UI
Checklist de smoke-test (2–5 minutos):
- Forms (Expense/Salary/Move Money/RRSP/Dividend/Trade/Crypto Trade)
- Campo Date deve estar como
YYYY-MM-DD. - Selecionar uma data no calendário deve manter
YYYY-MM-DD. - Salvar deve funcionar (payload segue o mesmo formato).
- Reports (Expense Report / Salary Report)
- A coluna Date deve estar em
YYYY-MM-DD.
- Tabelas de investimentos (ex.: TFSA)
- Valores devem estar formatados (ex.:
$1,234.50).
Sessão, expiração e logout automático
Expiração de tokens (backend)
- Access token: expira em 30 minutos.
- Refresh token: expira por padrão em 30 dias (configurável no backend).
Renovação automática (frontend)
O frontend possui um ApiClient centralizado (no pacote personal-finance-frontend-core-services) que:
- injeta o header
Authorizationautomaticamente - ao receber 401, tenta
/api/auth/refreshe reexecuta a request uma vez
Idle timeout (segurança/UX)
O app aplica logout automático após 30 minutos sem interação (touch/mouse/scroll).
- Arquivo:
personal-finance-frontend/lib/widgets/idle_timeout_wrapper.dart - No Web, timers podem ser reduzidos/pausados em abas em background; por isso o app também reavalia o idle ao voltar para a aba.
Como Executar
Localmente para Desenvolvimento
A partir do diretório raiz do projeto (personal-finance-frontend), execute:
flutter runPara rodar para web (com a URL da API definida):
flutter run -d chrome --web-hostname localhost --web-port 5000 --dart-define=BASE_API_URL=http://localhost:8000/apiMetadata (Categories/Subcategories) — testes via curl
Alguns endpoints de metadata exigem autenticação. No ambiente personal-finance.casa, seu curl pode precisar do -k (certificado).
Decisão de produto (modo "clean"):
- Dropdowns do app (ex.: Add Expense) mostram somente categorias/subcategorias explicitamente salvas pelo usuário.
- O sistema não aprende/mergeia categorias a partir do histórico de transações (isso evitou poluição com itens como pagamentos de cartão gerados pela reconciliação).
Por isso, o endpoint
/api/metadata/categorieshoje retorna somente user-defined categories. A tela de Settings usa/api/metadata/user-categories(mesma lista, em endpoint separado).
1) Login (gera token)
curl -k -s -X POST https://personal-finance.casa/api/auth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=SEU_EMAIL" \
-d "password=SUA_SENHA" \
| jqDepois:
TOKEN="<access_token>"2) Lista para dropdown (somente user-defined)
curl -k -s -H "Authorization: Bearer $TOKEN" \
https://personal-finance.casa/api/metadata/categories | jq2.1) Template demo (read-only)
curl -k -s -H "Authorization: Bearer $TOKEN" \
https://personal-finance.casa/api/metadata/demo-categories | jq3) Lista do usuário (somente o que ele criou)
curl -k -s -H "Authorization: Bearer $TOKEN" \
https://personal-finance.casa/api/metadata/user-categories | jq4) CRUD: criar categoria
curl -k -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"house test"}' \
https://personal-finance.casa/api/metadata/categories | jq5) CRUD: criar subcategoria
curl -k -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Rent X"}' \
https://personal-finance.casa/api/metadata/categories/HOUSE%20TEST/subcategories | jq6) CRUD: deletar subcategoria
curl -k -s -X DELETE -H "Authorization: Bearer $TOKEN" \
https://personal-finance.casa/api/metadata/categories/HOUSE%20TEST/subcategories/Rent%20X7) CRUD: deletar categoria
curl -k -s -X DELETE -H "Authorization: Bearer $TOKEN" \
https://personal-finance.casa/api/metadata/categories/HOUSE%20TEST8) Importar categorias demo para o usuário
curl -k -s -X POST -H "Authorization: Bearer $TOKEN" \
https://personal-finance.casa/api/metadata/user-categories/import-defaults | jqObservação importante:
- Escolher uma categoria/subcategoria no Add Expense NÃO grava isso na sua lista custom automaticamente.
- Para que vire parte da sua lista custom (aparecer em
/user-categories), você precisa:
- importar o demo, ou
- criar manualmente no Settings, ou
- criar durante o Import Statement (CSV) (o botão Create item salva na sua lista).
Comportamento (decisão de produto):
- Para evitar poluição com itens que você nunca usa, o app não mostra a lista demo automaticamente nos dropdowns.
- O demo é apenas um template para importar quando fizer sentido.
Nota importante sobre cache e multi-user
Os resultados de metadata são cacheados no SharedPreferences.
- As chaves de cache para categories/user-categories/payment-methods são escopadas por usuário (ex.:
metadata.categories.v2.uid_123). - Isso evita que ao fazer logout/login com outro usuário, a UI reutilize a lista do usuário anterior.
9) Ver categorias usadas em transações (somente "aprendido" do histórico — diagnóstico)
curl -k -s -H "Authorization: Bearer $TOKEN" \
https://personal-finance.casa/api/metadata/transaction-categories | jq
> Nota: este endpoint é útil para diagnóstico/insights, mas **não** é usado para alimentar dropdowns.Build para Produção (Web)
Para gerar uma build otimizada para web:
flutter build web --release --dart-define=BASE_API_URL=http://seuservico.com/apiOs arquivos gerados estarão na pasta build/web.
Estrutura de Pacotes
Este aplicativo Flutter é construído sobre uma arquitetura de pacotes modulares, com a aplicação principal (personal-finance-frontend) orquestrando o carregamento de funcionalidades fornecidas por pacotes de features.
Arquitetura de Cálculos Financeiros
Este documento esclarece como os principais valores financeiros são calculados no sistema. Aderir a esta lógica é crucial para manter a consistência dos dados.
1. Balanço de Caixa (Cash Balance)
O balanço de caixa é a representação do dinheiro "em mãos" do usuário. O cálculo correto e completo é:
Balanço de Caixa = (Renda Total) - (Despesas Pagas com Caixa) + (Transferências para o Caixa) - (Transferências para Fora do Caixa)
- Renda Total: A soma de todas as transações com
type = 'credit'na tabelatransactions. - Despesas Pagas com Caixa: A soma de todas as transações com
type = 'debit'Epayment_method = 'CASH'na tabelatransactions. - Transferências para o Caixa: A soma de todos os registros na tabela
investmentsondedestination_account = 'CASH'. - Transferências para Fora do Caixa: A soma de todos os registros na tabela
investmentsondesource_account = 'CASH'.
2. Relatório de Despesas
O relatório de despesas deve refletir todos os gastos operacionais do usuário. Ele é calculado como:
Total de Despesas do Relatório = (Soma de todos os débitos) - (Soma dos débitos de investimento/capital)
- Lógica: Soma todas as transações com
type = 'debit'na tabelatransactions, excluindo aquelas cujas categorias sãoINVESTMENTS,PENT HAUS,Da Vince, eAcores, pois estas representam uma transferência de capital e não uma despesa operacional.
3. Relatório de Salário (Renda)
O relatório de salário deve refletir a renda principal do usuário.
Total de Salário do Relatório = Soma de todos os créditos das categorias de renda
- Lógica: Soma todas as transações com
type = 'credit'E cujacategorysejaSalary 1,Salary 2, ouExtra.
