-- Схема БД проекта «Бюджет» (MariaDB/InnoDB). -- Модель согласована в .claude/memory/бюджет_текущий_план.md — сверяться туда при любых изменениях. SET NAMES utf8mb4; CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, login VARCHAR(64) NOT NULL, password VARCHAR(255) NOT NULL, name VARCHAR(128) NOT NULL, email VARCHAR(128) NULL, date_add DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, last_login DATETIME NULL, PRIMARY KEY (id), UNIQUE KEY uq_users_login (login) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; CREATE TABLE accounts ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_id INT UNSIGNED NOT NULL, type_acc ENUM ('cash', 'bank', 'savings') NOT NULL, title VARCHAR(128) NOT NULL, summa DECIMAL(15, 2) NOT NULL DEFAULT 0, currency CHAR(3) NOT NULL DEFAULT 'RUB', -- ISO 4217; курс — Services\CurrencyRate (ЦБ РФ), не хранится тут description VARCHAR(255) NULL, -- свободное поле, например "счёт жены" — без отдельной структуры include_in_total TINYINT(1) NOT NULL DEFAULT 1, -- чисто ручной переключатель пользователя, не системное правило по типу/валюте (дефолт 0 для savings — просто стартовое значение) is_delete TINYINT(1) NOT NULL DEFAULT 0, `order` INT NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY ix_accounts_user_id (user_id), CONSTRAINT fk_accounts_user FOREIGN KEY (user_id) REFERENCES users (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; -- Ставка savings-счёта хранится историей, а не полем в accounts — старые периоды не должны -- меняться задним числом при смене ставки; та же структура пригодится для кредитов позже. CREATE TABLE account_rates ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, account_id INT UNSIGNED NOT NULL, rate_percent DECIMAL(5, 2) NOT NULL, valid_from DATE NOT NULL, valid_to DATE NULL, PRIMARY KEY (id), KEY ix_account_rates_account_id (account_id), CONSTRAINT fk_account_rates_account FOREIGN KEY (account_id) REFERENCES accounts (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; -- Необязательная надстройка над savings-счётом: без записи здесь — просто копилка без цели. CREATE TABLE goals ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, account_id INT UNSIGNED NOT NULL, target_summa DECIMAL(15, 2) NOT NULL, deadline DATE NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_goals_account_id (account_id), CONSTRAINT fk_goals_account FOREIGN KEY (account_id) REFERENCES accounts (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; CREATE TABLE categories ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_id INT UNSIGNED NOT NULL, parent_id INT UNSIGNED NULL, title VARCHAR(128) NOT NULL, type ENUM ('income', 'expense') NOT NULL, is_delete TINYINT(1) NOT NULL DEFAULT 0, -- архивация вместо удаления, тот же приём что у accounts — сохраняет историю транзакций `order` INT NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY ix_categories_user_id (user_id), KEY ix_categories_parent_id (parent_id), CONSTRAINT fk_categories_user FOREIGN KEY (user_id) REFERENCES users (id), -- удаление родителя не должно тянуть за собой подкатегории — они просто станут верхнего уровня CONSTRAINT fk_categories_parent FOREIGN KEY (parent_id) REFERENCES categories (id) ON DELETE SET NULL ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; -- categorie_id NULL — обычный transfer (в т.ч. пополнение копилки/цели): деньги просто -- переехали между своими счетами, план/факт по категориям это не видит (см. бюджет_текущий_план.md). -- account_f_id/account_in_id: income заполняет только _in, expense — только _f, transfer — оба. CREATE TABLE transactions ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_id INT UNSIGNED NOT NULL, categorie_id INT UNSIGNED NULL, account_f_id INT UNSIGNED NULL, account_in_id INT UNSIGNED NULL, date DATE NOT NULL, summa DECIMAL(15, 2) NOT NULL, currency CHAR(3) NULL, -- валюта операции (обычно = валюте счёта списания); NULL = рубли rate_to_rub DECIMAL(10, 4) NULL, -- курс ЦБ РФ, зафиксированный на момент операции; NULL для рублёвых type ENUM ('income', 'expense', 'transfer') NOT NULL, PRIMARY KEY (id), KEY ix_transactions_user_date (user_id, date), KEY ix_transactions_categorie_id (categorie_id), KEY ix_transactions_account_f_id (account_f_id), KEY ix_transactions_account_in_id (account_in_id), CONSTRAINT fk_transactions_user FOREIGN KEY (user_id) REFERENCES users (id), CONSTRAINT fk_transactions_categorie FOREIGN KEY (categorie_id) REFERENCES categories (id), CONSTRAINT fk_transactions_account_f FOREIGN KEY (account_f_id) REFERENCES accounts (id), CONSTRAINT fk_transactions_account_in FOREIGN KEY (account_in_id) REFERENCES accounts (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; -- Факт не хранится — считается на лету суммой transactions по категории за месяц. CREATE TABLE budgets ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, categorie_id INT UNSIGNED NOT NULL, plan_summ DECIMAL(15, 2) NOT NULL, year SMALLINT UNSIGNED NOT NULL, month TINYINT UNSIGNED NOT NULL, type ENUM ('income', 'expense') NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_budgets_categorie_period (categorie_id, year, month), CONSTRAINT fk_budgets_categorie FOREIGN KEY (categorie_id) REFERENCES categories (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;