финальная версия Finance Control

This commit is contained in:
2026-05-25 22:06:16 +03:00
parent 7dcb1a23a7
commit 5890e4ae64
22 changed files with 1441 additions and 927 deletions

View File

@@ -1,12 +1,8 @@
import sqlite3
from typing import Iterable
from app.config import DB_NAME
def get_connection() -> sqlite3.Connection:
connection = sqlite3.connect(DB_NAME)
connection.row_factory = sqlite3.Row
connection.execute("PRAGMA foreign_keys = ON")
return connection
DEFAULT_CATEGORIES: Iterable[tuple[str, str]] = [
("Продукты", "Расход"),
@@ -17,13 +13,26 @@ 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(
@@ -32,13 +41,15 @@ def init_database() -> None:
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
account_type TEXT NOT NULL,
balance REAL NOT NULL DEFAULT 0
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)
);
@@ -50,8 +61,10 @@ def init_database() -> None:
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
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 (
@@ -60,17 +73,38 @@ def init_database() -> None:
amount REAL NOT NULL,
billing_day INTEGER NOT NULL,
period TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'Активна'
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,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE RESTRICT
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)
@@ -79,3 +113,14 @@ def init_database() -> None:
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"
)