277 lines
13 KiB
PHP
277 lines
13 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Budget;
|
|
|
|
final class Schema
|
|
{
|
|
/** @return list<string> */
|
|
public static function statements(string $driver): array
|
|
{
|
|
return $driver === 'mysql' ? self::mysql() : self::sqlite();
|
|
}
|
|
|
|
/** @return list<string> */
|
|
private static function sqlite(): array
|
|
{
|
|
return [
|
|
'CREATE TABLE IF NOT EXISTS settings (
|
|
setting_key TEXT PRIMARY KEY,
|
|
setting_value TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)',
|
|
'CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
email TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
totp_secret TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)',
|
|
'CREATE TABLE IF NOT EXISTS monthly_plans (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
month TEXT NOT NULL UNIQUE,
|
|
starting_income NUMERIC NOT NULL DEFAULT 0,
|
|
rollover_in NUMERIC NOT NULL DEFAULT 0,
|
|
rolling_enabled INTEGER NOT NULL DEFAULT 1,
|
|
rollover_source_month TEXT,
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)',
|
|
'CREATE TABLE IF NOT EXISTS income_entries (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
month TEXT NOT NULL,
|
|
label TEXT NOT NULL,
|
|
amount NUMERIC NOT NULL,
|
|
received_on TEXT NOT NULL,
|
|
withholding_amount NUMERIC NOT NULL DEFAULT 0,
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)',
|
|
'CREATE INDEX IF NOT EXISTS idx_income_month ON income_entries(month)',
|
|
'CREATE TABLE IF NOT EXISTS categories (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
color TEXT NOT NULL DEFAULT "#7c5cff",
|
|
monthly_limit NUMERIC NOT NULL DEFAULT 0,
|
|
active INTEGER NOT NULL DEFAULT 1,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)',
|
|
'CREATE TABLE IF NOT EXISTS budget_items (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
month TEXT NOT NULL,
|
|
category_id INTEGER,
|
|
name TEXT NOT NULL,
|
|
planned_amount NUMERIC NOT NULL DEFAULT 0,
|
|
due_date TEXT,
|
|
recurrence TEXT NOT NULL DEFAULT "none",
|
|
series_key TEXT,
|
|
status TEXT NOT NULL DEFAULT "active",
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
|
|
)',
|
|
'CREATE INDEX IF NOT EXISTS idx_budget_items_month ON budget_items(month)',
|
|
'CREATE UNIQUE INDEX IF NOT EXISTS idx_budget_items_series_month ON budget_items(series_key, month) WHERE series_key IS NOT NULL',
|
|
'CREATE TABLE IF NOT EXISTS transactions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
month TEXT NOT NULL,
|
|
budget_item_id INTEGER,
|
|
category_id INTEGER,
|
|
merchant TEXT NOT NULL,
|
|
amount NUMERIC NOT NULL,
|
|
transacted_on TEXT NOT NULL,
|
|
dead_pull_amount NUMERIC NOT NULL DEFAULT 0,
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (budget_item_id) REFERENCES budget_items(id) ON DELETE SET NULL,
|
|
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
|
|
)',
|
|
'CREATE INDEX IF NOT EXISTS idx_transactions_month ON transactions(month)',
|
|
'CREATE TABLE IF NOT EXISTS dead_account_entries (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
month TEXT NOT NULL,
|
|
kind TEXT NOT NULL,
|
|
amount NUMERIC NOT NULL,
|
|
source_type TEXT,
|
|
source_id INTEGER,
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)',
|
|
'CREATE INDEX IF NOT EXISTS idx_dead_month ON dead_account_entries(month)',
|
|
'CREATE INDEX IF NOT EXISTS idx_dead_source ON dead_account_entries(source_type, source_id)',
|
|
'CREATE TABLE IF NOT EXISTS calendar_events (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
month TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
event_date TEXT NOT NULL,
|
|
event_type TEXT NOT NULL DEFAULT "note",
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)',
|
|
'CREATE TABLE IF NOT EXISTS alert_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
budget_item_id INTEGER NOT NULL,
|
|
alert_date TEXT NOT NULL,
|
|
window_days INTEGER NOT NULL,
|
|
recipient TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
response TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE (budget_item_id, alert_date, window_days, recipient),
|
|
FOREIGN KEY (budget_item_id) REFERENCES budget_items(id) ON DELETE CASCADE
|
|
)',
|
|
'CREATE TABLE IF NOT EXISTS visits (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
route TEXT NOT NULL,
|
|
visitor_hash TEXT NOT NULL,
|
|
visited_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)',
|
|
'CREATE INDEX IF NOT EXISTS idx_visits_date ON visits(visited_at)',
|
|
'CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER,
|
|
action TEXT NOT NULL,
|
|
entity_type TEXT,
|
|
entity_id INTEGER,
|
|
metadata TEXT,
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
|
)',
|
|
];
|
|
}
|
|
|
|
/** @return list<string> */
|
|
private static function mysql(): array
|
|
{
|
|
return [
|
|
'CREATE TABLE IF NOT EXISTS settings (
|
|
setting_key VARCHAR(191) PRIMARY KEY,
|
|
setting_value LONGTEXT NOT NULL,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS users (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
totp_secret VARCHAR(64) NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS monthly_plans (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
month CHAR(7) NOT NULL UNIQUE,
|
|
starting_income DECIMAL(14,2) NOT NULL DEFAULT 0,
|
|
rollover_in DECIMAL(14,2) NOT NULL DEFAULT 0,
|
|
rolling_enabled TINYINT(1) NOT NULL DEFAULT 1,
|
|
rollover_source_month CHAR(7) NULL,
|
|
notes TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS income_entries (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
month CHAR(7) NOT NULL,
|
|
label VARCHAR(255) NOT NULL,
|
|
amount DECIMAL(14,2) NOT NULL,
|
|
received_on DATE NOT NULL,
|
|
withholding_amount DECIMAL(14,2) NOT NULL DEFAULT 0,
|
|
notes TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_income_month (month)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS categories (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
name VARCHAR(191) NOT NULL UNIQUE,
|
|
color VARCHAR(16) NOT NULL DEFAULT "#7c5cff",
|
|
monthly_limit DECIMAL(14,2) NOT NULL DEFAULT 0,
|
|
active TINYINT(1) NOT NULL DEFAULT 1,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS budget_items (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
month CHAR(7) NOT NULL,
|
|
category_id BIGINT UNSIGNED NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
planned_amount DECIMAL(14,2) NOT NULL DEFAULT 0,
|
|
due_date DATE NULL,
|
|
recurrence VARCHAR(16) NOT NULL DEFAULT "none",
|
|
series_key VARCHAR(64) NULL,
|
|
status VARCHAR(24) NOT NULL DEFAULT "active",
|
|
notes TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_budget_items_month (month),
|
|
UNIQUE KEY idx_budget_items_series_month (series_key, month),
|
|
CONSTRAINT fk_item_category FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS transactions (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
month CHAR(7) NOT NULL,
|
|
budget_item_id BIGINT UNSIGNED NULL,
|
|
category_id BIGINT UNSIGNED NULL,
|
|
merchant VARCHAR(255) NOT NULL,
|
|
amount DECIMAL(14,2) NOT NULL,
|
|
transacted_on DATE NOT NULL,
|
|
dead_pull_amount DECIMAL(14,2) NOT NULL DEFAULT 0,
|
|
notes TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_transactions_month (month),
|
|
CONSTRAINT fk_transaction_item FOREIGN KEY (budget_item_id) REFERENCES budget_items(id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_transaction_category FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS dead_account_entries (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
month CHAR(7) NOT NULL,
|
|
kind VARCHAR(32) NOT NULL,
|
|
amount DECIMAL(14,2) NOT NULL,
|
|
source_type VARCHAR(32) NULL,
|
|
source_id BIGINT NULL,
|
|
notes TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_dead_month (month),
|
|
INDEX idx_dead_source (source_type, source_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS calendar_events (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
month CHAR(7) NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
event_date DATE NOT NULL,
|
|
event_type VARCHAR(24) NOT NULL DEFAULT "note",
|
|
notes TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS alert_logs (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
budget_item_id BIGINT UNSIGNED NOT NULL,
|
|
alert_date DATE NOT NULL,
|
|
window_days INT NOT NULL,
|
|
recipient VARCHAR(255) NOT NULL,
|
|
status VARCHAR(24) NOT NULL,
|
|
response TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE KEY alert_unique (budget_item_id, alert_date, window_days, recipient),
|
|
CONSTRAINT fk_alert_item FOREIGN KEY (budget_item_id) REFERENCES budget_items(id) ON DELETE CASCADE
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS visits (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
route VARCHAR(191) NOT NULL,
|
|
visitor_hash VARCHAR(64) NOT NULL,
|
|
visited_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_visits_date (visited_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
'CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
user_id BIGINT UNSIGNED NULL,
|
|
action VARCHAR(191) NOT NULL,
|
|
entity_type VARCHAR(64) NULL,
|
|
entity_id BIGINT NULL,
|
|
metadata LONGTEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_audit_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4',
|
|
];
|
|
}
|
|
}
|