feat: entry templates CRUD backend
This commit is contained in:
@@ -51,6 +51,7 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
|
||||
"ALTER TABLE projects ADD COLUMN budget_hours REAL DEFAULT NULL",
|
||||
"ALTER TABLE projects ADD COLUMN budget_amount REAL DEFAULT NULL",
|
||||
"ALTER TABLE projects ADD COLUMN rounding_override INTEGER DEFAULT NULL",
|
||||
"ALTER TABLE projects ADD COLUMN timeline_override TEXT DEFAULT NULL",
|
||||
];
|
||||
for sql in &project_migrations {
|
||||
match conn.execute(sql, []) {
|
||||
@@ -75,6 +76,22 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
|
||||
[],
|
||||
)?;
|
||||
|
||||
// Migrate tasks table - add estimated_hours column (safe to re-run)
|
||||
let task_migrations = [
|
||||
"ALTER TABLE tasks ADD COLUMN estimated_hours REAL DEFAULT NULL",
|
||||
];
|
||||
for sql in &task_migrations {
|
||||
match conn.execute(sql, []) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
let msg = e.to_string();
|
||||
if !msg.contains("duplicate column") {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS time_entries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -91,6 +108,22 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
|
||||
[],
|
||||
)?;
|
||||
|
||||
// Migrate time_entries table
|
||||
let time_entry_migrations = [
|
||||
"ALTER TABLE time_entries ADD COLUMN billable INTEGER DEFAULT 1",
|
||||
];
|
||||
for sql in &time_entry_migrations {
|
||||
match conn.execute(sql, []) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
let msg = e.to_string();
|
||||
if !msg.contains("duplicate column") {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS invoices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -187,6 +220,112 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS recurring_entries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL,
|
||||
task_id INTEGER,
|
||||
description TEXT,
|
||||
duration INTEGER DEFAULT 0,
|
||||
recurrence_rule TEXT NOT NULL,
|
||||
time_of_day TEXT DEFAULT '09:00',
|
||||
mode TEXT DEFAULT 'prompt',
|
||||
enabled INTEGER DEFAULT 1,
|
||||
last_triggered TEXT,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id),
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS expenses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL,
|
||||
client_id INTEGER,
|
||||
category TEXT DEFAULT 'other',
|
||||
description TEXT,
|
||||
amount REAL DEFAULT 0,
|
||||
date TEXT NOT NULL,
|
||||
receipt_path TEXT,
|
||||
invoiced INTEGER DEFAULT 0,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id),
|
||||
FOREIGN KEY (client_id) REFERENCES clients(id)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS timeline_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL,
|
||||
exe_name TEXT,
|
||||
exe_path TEXT,
|
||||
window_title TEXT,
|
||||
started_at TEXT NOT NULL,
|
||||
ended_at TEXT,
|
||||
duration INTEGER DEFAULT 0,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id)
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS calendar_sources (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
url TEXT,
|
||||
last_synced TEXT,
|
||||
sync_interval INTEGER DEFAULT 30,
|
||||
enabled INTEGER DEFAULT 1,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS calendar_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
source_id INTEGER NOT NULL,
|
||||
uid TEXT,
|
||||
summary TEXT,
|
||||
start_time TEXT,
|
||||
end_time TEXT,
|
||||
duration INTEGER DEFAULT 0,
|
||||
location TEXT,
|
||||
synced_at TEXT,
|
||||
FOREIGN KEY (source_id) REFERENCES calendar_sources(id) ON DELETE CASCADE
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS timesheet_locks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
week_start TEXT NOT NULL UNIQUE,
|
||||
status TEXT DEFAULT 'locked',
|
||||
locked_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS entry_templates (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
project_id INTEGER NOT NULL REFERENCES projects(id),
|
||||
task_id INTEGER REFERENCES tasks(id),
|
||||
description TEXT,
|
||||
duration INTEGER NOT NULL DEFAULT 0,
|
||||
billable INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
@@ -231,6 +370,7 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
|
||||
conn.execute("INSERT OR IGNORE INTO settings (key, value) VALUES ('shortcut_toggle_timer', 'CmdOrCtrl+Shift+T')", [])?;
|
||||
conn.execute("INSERT OR IGNORE INTO settings (key, value) VALUES ('shortcut_show_app', 'CmdOrCtrl+Shift+Z')", [])?;
|
||||
conn.execute("INSERT OR IGNORE INTO settings (key, value) VALUES ('mini_timer_opacity', '90')", [])?;
|
||||
conn.execute("INSERT OR IGNORE INTO settings (key, value) VALUES ('timeline_recording', 'off')", [])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user