feat(#18): import de extratos OFX e CSV — parser, dedup, preview e confirmação
Parser OFX tolerante ao formato SGML de bancos brasileiros (sem closing tags, BOM, timezone suffix). Parser CSV com separador de campo/decimal configurável. Dedup por FITID (OFX) ou date+amount+desc normalizado. Fluxo: POST /preview → revisão na UI → POST /confirm salva e loga. Migration 002 adiciona external_id com unique index. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version BIGINT PRIMARY KEY,
|
||||
applied_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
color VARCHAR(7) NOT NULL DEFAULT '#6B7280',
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS transactions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
date DATE NOT NULL,
|
||||
amount NUMERIC(12, 2) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
type VARCHAR(10) NOT NULL CHECK (type IN ('income', 'expense')),
|
||||
source VARCHAR(10) NOT NULL DEFAULT 'manual' CHECK (source IN ('manual', 'import')),
|
||||
category_id INTEGER REFERENCES categories (id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS recurring_expenses (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
expected_amount NUMERIC(12, 2) NOT NULL,
|
||||
day_of_month INTEGER NOT NULL CHECK (day_of_month BETWEEN 1 AND 31),
|
||||
category_id INTEGER REFERENCES categories (id) ON DELETE SET NULL,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS import_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
format VARCHAR(10) NOT NULL CHECK (format IN ('ofx', 'csv')),
|
||||
imported_count INTEGER NOT NULL DEFAULT 0,
|
||||
duplicate_count INTEGER NOT NULL DEFAULT 0,
|
||||
error_count INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
INSERT INTO categories (name, color, is_default)
|
||||
VALUES
|
||||
('Saúde / Insulina', '#EF4444', TRUE),
|
||||
('Veleiro', '#3B82F6', TRUE),
|
||||
('Jogos de Tabuleiro', '#8B5CF6', TRUE),
|
||||
('Alimentação', '#F59E0B', TRUE),
|
||||
('Lazer', '#10B981', TRUE),
|
||||
('Freelance', '#6366F1', TRUE),
|
||||
('Outros', '#6B7280', TRUE)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES (1) ON CONFLICT DO NOTHING;
|
||||
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE transactions ADD COLUMN IF NOT EXISTS external_id TEXT;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS transactions_external_id_idx ON transactions (external_id) WHERE external_id IS NOT NULL;
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES (2) ON CONFLICT DO NOTHING;
|
||||
Reference in New Issue
Block a user