package database import ( "database/sql" "fmt" "log" "golang.org/x/crypto/bcrypt" ) func runMigrations(db *sql.DB) error { migrations := []string{ // Users table `CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, // Customers table `CREATE TABLE IF NOT EXISTS customers ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT NOT NULL DEFAULT '', phone TEXT NOT NULL DEFAULT '', address TEXT NOT NULL DEFAULT '', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, // Orders table `CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id INTEGER NOT NULL, status TEXT NOT NULL DEFAULT 'draft', order_date DATE NOT NULL DEFAULT (date('now')), total_amount REAL NOT NULL DEFAULT 0, notes TEXT NOT NULL DEFAULT '', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (customer_id) REFERENCES customers(id) )`, // Order lines table `CREATE TABLE IF NOT EXISTS order_lines ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER NOT NULL, description TEXT NOT NULL, quantity REAL NOT NULL DEFAULT 1, unit_price REAL NOT NULL DEFAULT 0, line_total REAL NOT NULL DEFAULT 0, FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE )`, // Invoices table `CREATE TABLE IF NOT EXISTS invoices ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER, customer_id INTEGER NOT NULL, invoice_number TEXT NOT NULL UNIQUE, status TEXT NOT NULL DEFAULT 'pending', amount REAL NOT NULL DEFAULT 0, due_date DATE NOT NULL, paid_date DATE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (order_id) REFERENCES orders(id), FOREIGN KEY (customer_id) REFERENCES customers(id) )`, // GL Accounts table (Chart of Accounts) `CREATE TABLE IF NOT EXISTS gl_accounts ( id INTEGER PRIMARY KEY AUTOINCREMENT, code TEXT NOT NULL UNIQUE, name TEXT NOT NULL, type TEXT NOT NULL, balance REAL NOT NULL DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, // Journal Entries table `CREATE TABLE IF NOT EXISTS journal_entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, entry_date DATE NOT NULL DEFAULT (date('now')), description TEXT NOT NULL, reference TEXT NOT NULL DEFAULT '', created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, // Journal Lines table `CREATE TABLE IF NOT EXISTS journal_lines ( id INTEGER PRIMARY KEY AUTOINCREMENT, journal_entry_id INTEGER NOT NULL, account_id INTEGER NOT NULL, debit REAL NOT NULL DEFAULT 0, credit REAL NOT NULL DEFAULT 0, FOREIGN KEY (journal_entry_id) REFERENCES journal_entries(id) ON DELETE CASCADE, FOREIGN KEY (account_id) REFERENCES gl_accounts(id) )`, } for i, m := range migrations { if _, err := db.Exec(m); err != nil { return fmt.Errorf("migration %d failed: %w", i, err) } } log.Println("Migrations completed") return nil } func seedData(db *sql.DB) error { // Seed admin user if not exists var count int db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count) if count == 0 { hash, err := bcrypt.GenerateFromPassword([]byte("admin123"), bcrypt.DefaultCost) if err != nil { return fmt.Errorf("hashing password: %w", err) } _, err = db.Exec("INSERT INTO users (username, password_hash) VALUES (?, ?)", "admin", string(hash)) if err != nil { return fmt.Errorf("inserting admin user: %w", err) } log.Println("Seeded admin user (admin / admin123)") } // Seed Chart of Accounts if not exists db.QueryRow("SELECT COUNT(*) FROM gl_accounts").Scan(&count) if count == 0 { accounts := []struct { Code, Name, Type string }{ // Assets {"1000", "Cash", "asset"}, {"1100", "Accounts Receivable", "asset"}, {"1200", "Inventory", "asset"}, // Liabilities {"2000", "Accounts Payable", "liability"}, {"2100", "Sales Tax Payable", "liability"}, // Equity {"3000", "Owner's Equity", "equity"}, {"3100", "Retained Earnings", "equity"}, // Revenue {"4000", "Sales Revenue", "revenue"}, {"4100", "Service Revenue", "revenue"}, // Expenses {"5000", "Cost of Goods Sold", "expense"}, {"5100", "Salaries Expense", "expense"}, {"5200", "Rent Expense", "expense"}, {"5300", "Utilities Expense", "expense"}, } for _, a := range accounts { _, err := db.Exec("INSERT INTO gl_accounts (code, name, type) VALUES (?, ?, ?)", a.Code, a.Name, a.Type) if err != nil { return fmt.Errorf("inserting account %s: %w", a.Code, err) } } log.Println("Seeded chart of accounts") } return nil }