Bicycle/schema.sql
2026-08-11 22:35:26 +03:00

140 lines
9.6 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- Схема БД проекта «Бюджет» (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,
-- формульные статьи (см. бюджет_текущий_план.md): настраиваются на статье ДОХОДА, ссылаются на
-- привязанную статью РАСХОДА + дефолтный процент; у остальных категорий оба поля NULL
linked_expense_categorie_id INT UNSIGNED NULL,
formula_pct DECIMAL(5, 2) NULL,
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,
CONSTRAINT fk_categories_linked_expense FOREIGN KEY (linked_expense_categorie_id) REFERENCES categories (id)
) 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 — цепочка снимков (не old/new-пары), достаточно для отката:
-- откат edit — snapshot предыдущей записи этой же transaction_id; восстановление после delete —
-- snapshot записи, предшествующей записи delete (сама delete-запись snapshot не хранит); откат
-- create — просто удалить операцию, snapshot не нужен. Механизм самого отката — не реализован,
-- здесь только лог.
CREATE TABLE transaction_history (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
transaction_id INT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
changed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
action ENUM ('create', 'edit', 'delete') NOT NULL,
snapshot JSON NULL, -- полный слепок transactions на этот момент; NULL для action='delete'
PRIMARY KEY (id),
-- без FK на transactions.id намеренно: после физического удаления операции строка transactions
-- пропадает, а история по ней должна остаться читаемой
KEY ix_transaction_history_transaction_id (transaction_id),
CONSTRAINT fk_transaction_history_user FOREIGN KEY (user_id) REFERENCES users (id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- Факт не хранится — считается на лету суммой transactions по категории за месяц.
-- plan_summ хранится всегда (и у формульных строк тоже) — у формульных пересчитывается и
-- перезаписывается автоматически при правке суммы/процента статьи-источника (кэш, как accounts.summa).
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,
-- каким процентом посчитан plan_summ этого месяца — только для строк формульной статьи расхода,
-- NULL для обычных строк (см. категорию categories.linked_expense_categorie_id)
formula_pct_used DECIMAL(5, 2) 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;