basic online
This commit is contained in:
86
internal/database/migrate.go
Normal file
86
internal/database/migrate.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package database
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Migrate runs the schema bootstrap. SQLite doesn't need a migration tool —
|
||||
// CREATE TABLE IF NOT EXISTS is idempotent so this is safe to call on every start.
|
||||
func Migrate(db *DB) error {
|
||||
stmts := []string{
|
||||
`CREATE TABLE IF NOT EXISTS departments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
cost_center TEXT NOT NULL DEFAULT '',
|
||||
active INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now'))
|
||||
)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS gl_accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT NOT NULL UNIQUE,
|
||||
description TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK(type IN ('revenue','cogs','opex','capex','headcount')),
|
||||
favour_high INTEGER NOT NULL DEFAULT 0,
|
||||
active INTEGER NOT NULL DEFAULT 1
|
||||
)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS budgets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
fiscal_year INTEGER NOT NULL,
|
||||
fiscal_period INTEGER NOT NULL CHECK(fiscal_period BETWEEN 1 AND 12),
|
||||
version TEXT NOT NULL DEFAULT 'original'
|
||||
CHECK(version IN ('original','forecast_1','forecast_2','forecast_3')),
|
||||
department_id INTEGER NOT NULL REFERENCES departments(id),
|
||||
gl_account_id INTEGER NOT NULL REFERENCES gl_accounts(id),
|
||||
amount REAL NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'DKK',
|
||||
notes TEXT NOT NULL DEFAULT '',
|
||||
created_by TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now')),
|
||||
UNIQUE(fiscal_year, fiscal_period, version, department_id, gl_account_id)
|
||||
)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS actuals (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
fiscal_year INTEGER NOT NULL,
|
||||
fiscal_period INTEGER NOT NULL CHECK(fiscal_period BETWEEN 1 AND 12),
|
||||
department_id INTEGER NOT NULL REFERENCES departments(id),
|
||||
gl_account_id INTEGER NOT NULL REFERENCES gl_accounts(id),
|
||||
amount REAL NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'DKK',
|
||||
source TEXT NOT NULL DEFAULT '',
|
||||
ingested_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now')),
|
||||
UNIQUE(fiscal_year, fiscal_period, department_id, gl_account_id)
|
||||
)`,
|
||||
|
||||
// Seed reference data if not already present
|
||||
`INSERT OR IGNORE INTO departments (code, name, cost_center) VALUES
|
||||
('ENG', 'Engineering', 'CC-100'),
|
||||
('SALES', 'Sales', 'CC-200'),
|
||||
('MKTG', 'Marketing', 'CC-300'),
|
||||
('FINANCE', 'Finance & Operations', 'CC-400'),
|
||||
('PRODUCT', 'Product', 'CC-500')`,
|
||||
|
||||
`INSERT OR IGNORE INTO gl_accounts (code, description, type, favour_high) VALUES
|
||||
('4000', 'Subscription Revenue', 'revenue', 1),
|
||||
('4100', 'Professional Services', 'revenue', 1),
|
||||
('5000', 'Cloud Infrastructure', 'cogs', 0),
|
||||
('5100', 'Third-party Licenses', 'cogs', 0),
|
||||
('6100', 'Salaries & Wages', 'headcount', 0),
|
||||
('6110', 'Employer Payroll Tax', 'headcount', 0),
|
||||
('6120', 'Employee Benefits', 'headcount', 0),
|
||||
('6300', 'Software Subscriptions', 'opex', 0),
|
||||
('6310', 'Travel & Entertainment', 'opex', 0),
|
||||
('6400', 'Marketing & Advertising', 'opex', 0),
|
||||
('6500', 'Consulting & Contractors', 'opex', 0),
|
||||
('6600', 'Office & Facilities', 'opex', 0)`,
|
||||
}
|
||||
|
||||
for _, stmt := range stmts {
|
||||
if _, err := db.Exec(stmt); err != nil {
|
||||
return fmt.Errorf("migrate: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user