package main import ( "fmt" "log" "math/rand" "os" "strconv" "time" "erp_system/internal/database" _ "modernc.org/sqlite" ) func main() { dbPath := "erp.db" if len(os.Args) > 1 { dbPath = os.Args[1] } log.Printf("Connecting to database at %s...", dbPath) // We can use the internal database package to initialize (runs migrations) db, err := database.Initialize(dbPath) if err != nil { log.Fatalf("Failed to initialize database: %v", err) } defer db.Close() // Configuration for generation const ( numCustomers = 500 maxOrdersPerCust = 15 maxLinesPerOrder = 8 numJournalEntries = 100 ) log.Println("Starting data generation...") start := time.Now() tx, err := db.Begin() if err != nil { log.Fatalf("Failed to begin transaction: %v", err) } // 1. Customers customerIDs := make([]int64, 0, numCustomers) for i := 0; i < numCustomers; i++ { name := fmt.Sprintf("Customer %s %s", randomString(5), randomString(7)) email := fmt.Sprintf("%s.%s@example.com", randomString(4), randomString(5)) phone := fmt.Sprintf("555-%04d", rand.Intn(10000)) address := fmt.Sprintf("%d %s St, City %s", rand.Intn(999), randomString(6), randomString(4)) res, err := tx.Exec("INSERT INTO customers (name, email, phone, address) VALUES (?, ?, ?, ?)", name, email, phone, address) if err != nil { tx.Rollback() log.Fatalf("Failed to insert customer: %v", err) } id, _ := res.LastInsertId() customerIDs = append(customerIDs, id) } log.Printf("Generated %d customers", numCustomers) // 2. Orders and Lines and Invoices var orderCount, lineCount, invoiceCount int for _, custID := range customerIDs { numOrders := rand.Intn(maxOrdersPerCust) for j := 0; j < numOrders; j++ { // Create Order status := "draft" r := rand.Float32() if r > 0.7 { status = "fulfilled" } else if r > 0.4 { status = "confirmed" } else if r > 0.3 { status = "cancelled" } // Random date in last year orderDate := time.Now().AddDate(0, 0, -rand.Intn(365)) res, err := tx.Exec("INSERT INTO orders (customer_id, status, order_date, notes) VALUES (?, ?, ?, ?)", custID, status, orderDate.Format("2006-01-02"), "Generated order") if err != nil { tx.Rollback() log.Fatalf("Failed to insert order: %v", err) } orderID, _ := res.LastInsertId() orderCount++ // Create Order Lines numLines := rand.Intn(maxLinesPerOrder) + 1 var totalAmount float64 for k := 0; k < numLines; k++ { qty := float64(rand.Intn(10) + 1) price := float64(rand.Intn(100) + 10) desc := fmt.Sprintf("Product %s-%d", randomString(3), rand.Intn(100)) lineTotal := qty * price totalAmount += lineTotal _, err := tx.Exec("INSERT INTO order_lines (order_id, description, quantity, unit_price, line_total) VALUES (?, ?, ?, ?, ?)", orderID, desc, qty, price, lineTotal) if err != nil { tx.Rollback() log.Fatalf("Failed to insert order line: %v", err) } lineCount++ } // Update Order Total _, err = tx.Exec("UPDATE orders SET total_amount = ? WHERE id = ?", totalAmount, orderID) if err != nil { tx.Rollback() log.Fatalf("Failed to update order total: %v", err) } // Create Invoice if order is confirmed or fulfilled if status == "confirmed" || status == "fulfilled" { invoiceStatus := "pending" if rand.Float32() > 0.5 { invoiceStatus = "paid" } invNum := fmt.Sprintf("INV-%d-%d", orderID, rand.Intn(10000)) dueDate := orderDate.AddDate(0, 1, 0) // Due in 30 days var paidDate interface{} = nil if invoiceStatus == "paid" { pd := dueDate.AddDate(0, 0, -rand.Intn(10)) paidDate = pd.Format("2006-01-02") } _, err := tx.Exec("INSERT INTO invoices (order_id, customer_id, invoice_number, status, amount, due_date, paid_date) VALUES (?, ?, ?, ?, ?, ?, ?)", orderID, custID, invNum, invoiceStatus, totalAmount, dueDate.Format("2006-01-02"), paidDate) if err != nil { tx.Rollback() log.Fatalf("Failed to insert invoice: %v", err) } invoiceCount++ } } } log.Printf("Generated %d orders, %d lines, %d invoices", orderCount, lineCount, invoiceCount) // 3. Journal Entries // Fetch account IDs first rows, err := tx.Query("SELECT id, code, type FROM gl_accounts") if err != nil { tx.Rollback() log.Fatalf("Failed to fetch accounts: %v", err) } defer rows.Close() type Account struct { ID int64 Code string Type string } var accounts []Account for rows.Next() { var a Account if err := rows.Scan(&a.ID, &a.Code, &a.Type); err != nil { log.Fatalf("Failed to scan account: %v", err) } accounts = append(accounts, a) } if len(accounts) >= 2 { for i := 0; i < numJournalEntries; i++ { date := time.Now().AddDate(0, 0, -rand.Intn(60)).Format("2006-01-02") desc := fmt.Sprintf("Journal Entry %d", i+1) res, err := tx.Exec("INSERT INTO journal_entries (entry_date, description, reference) VALUES (?, ?, ?)", date, desc, "GEN-"+strconv.Itoa(i)) if err != nil { tx.Rollback() log.Fatalf("Failed to insert journal entry: %v", err) } jeID, _ := res.LastInsertId() // Simple balanced entry: 2 lines amount := float64(rand.Intn(5000) + 100) // Pick 2 random accounts idx1 := rand.Intn(len(accounts)) idx2 := rand.Intn(len(accounts)) for idx1 == idx2 { idx2 = rand.Intn(len(accounts)) } // Debit line _, err = tx.Exec("INSERT INTO journal_lines (journal_entry_id, account_id, debit, credit) VALUES (?, ?, ?, ?)", jeID, accounts[idx1].ID, amount, 0) if err != nil { tx.Rollback() log.Fatalf("Failed to insert journal line 1: %v", err) } // Credit line _, err = tx.Exec("INSERT INTO journal_lines (journal_entry_id, account_id, debit, credit) VALUES (?, ?, ?, ?)", jeID, accounts[idx2].ID, 0, amount) if err != nil { tx.Rollback() log.Fatalf("Failed to insert journal line 2: %v", err) } } } log.Printf("Generated %d journal entries", numJournalEntries) if err := tx.Commit(); err != nil { log.Fatalf("Failed to commit transaction: %v", err) } log.Printf("Seeding completed in %v", time.Since(start)) } func randomString(n int) string { var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]rune, n) for i := range b { b[i] = letters[rand.Intn(len(letters))] } return string(b) }