Files
Finance-Control/app/database.py

127 lines
4.8 KiB
Python

import sqlite3
from typing import Iterable
from app.config import DB_NAME
DEFAULT_CATEGORIES: Iterable[tuple[str, str]] = [
("Продукты", "Расход"),
("Транспорт", "Расход"),
("Кафе", "Расход"),
("Жильё", "Расход"),
("Здоровье", "Расход"),
("Развлечения", "Расход"),
("Одежда", "Расход"),
("Образование", "Расход"),
("Коммунальные услуги", "Расход"),
("Связь и интернет", "Расход"),
("Зарплата", "Доход"),
("Фриланс", "Доход"),
("Подарки", "Доход"),
("Возврат", "Доход"),
("Проценты по вкладу", "Доход"),
]
def get_connection() -> sqlite3.Connection:
"""Open SQLite connection with row access by column name."""
connection = sqlite3.connect(DB_NAME)
connection.row_factory = sqlite3.Row
connection.execute("PRAGMA foreign_keys = ON")
return connection
def init_database() -> None:
"""Create all tables, indexes and default records."""
with get_connection() as connection:
cursor = connection.cursor()
cursor.executescript(
"""
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
account_type TEXT NOT NULL,
balance REAL NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
category_type TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(name, category_type)
);
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
amount REAL NOT NULL,
transaction_type TEXT NOT NULL,
account_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id)
REFERENCES accounts(id) ON DELETE RESTRICT,
FOREIGN KEY (category_id)
REFERENCES categories(id) ON DELETE RESTRICT
);
CREATE TABLE IF NOT EXISTS subscriptions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
amount REAL NOT NULL,
billing_day INTEGER NOT NULL,
period TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'Активна',
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS budgets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_id INTEGER NOT NULL UNIQUE,
limit_amount REAL NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (category_id)
REFERENCES categories(id) ON DELETE RESTRICT
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_transactions_created_at
ON transactions(created_at);
CREATE INDEX IF NOT EXISTS idx_transactions_type
ON transactions(transaction_type);
CREATE INDEX IF NOT EXISTS idx_transactions_account
ON transactions(account_id);
CREATE INDEX IF NOT EXISTS idx_transactions_category
ON transactions(category_id);
"""
)
_ensure_column(cursor, "accounts", "created_at", "TEXT")
_ensure_column(cursor, "categories", "created_at", "TEXT")
_ensure_column(cursor, "subscriptions", "created_at", "TEXT")
_ensure_column(cursor, "budgets", "created_at", "TEXT")
cursor.executemany(
"""
INSERT OR IGNORE INTO categories (name, category_type)
VALUES (?, ?)
""",
DEFAULT_CATEGORIES,
)
connection.commit()
def _ensure_column(cursor: sqlite3.Cursor, table: str, column: str, column_type: str) -> None:
"""Add a column when an old alpha database is opened."""
columns = [row[1] for row in cursor.execute(f"PRAGMA table_info({table})")]
if column not in columns:
cursor.execute(f"ALTER TABLE {table} ADD COLUMN {column} {column_type}")
cursor.execute(
f"UPDATE {table} SET {column} = CURRENT_TIMESTAMP "
f"WHERE {column} IS NULL"
)