Full port of Odoo's ERP system from Python to Go, with the original Odoo JavaScript frontend (OWL framework) running against the Go server. Backend (10,691 LoC Go): - Custom ORM: CRUD, domains→SQL with JOINs, computed fields, sequences - 93 models across 14 modules (base, account, sale, stock, purchase, hr, project, crm, fleet, product, l10n_de, google_address/translate/calendar) - Auth with bcrypt + session cookies - Setup wizard (company, SKR03 chart, admin, demo data) - Double-entry bookkeeping constraint - Sale→Invoice workflow (confirm SO → generate invoice → post) - SKR03 chart of accounts (110 accounts) + German taxes (USt/VSt) - Record rules (multi-company filter) - Google integrations as opt-in modules (Maps, Translate, Calendar) Frontend: - Odoo's original OWL webclient (503 JS modules, 378 XML templates) - JS transpiled via Odoo's js_transpiler (ES modules → odoo.define) - SCSS compiled to CSS (675KB) via dart-sass - XML templates compiled to registerTemplate() JS calls - Static file serving from Odoo source addons - Login page, session management, menu navigation - Contacts list view renders with real data from PostgreSQL Infrastructure: - 14MB single binary (CGO_ENABLED=0) - Docker Compose (Go server + PostgreSQL 16) - Zero phone-home (no outbound calls to odoo.com) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
105 lines
2.6 KiB
Go
105 lines
2.6 KiB
Go
// Odoo Go Server — Main entrypoint
|
|
// Mirrors: odoo-bin
|
|
//
|
|
// Usage:
|
|
//
|
|
// go run ./cmd/odoo-server
|
|
// ODOO_DB_HOST=localhost ODOO_DB_PORT=5432 go run ./cmd/odoo-server
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
// Import all modules (register models via init())
|
|
_ "odoo-go/addons/base"
|
|
_ "odoo-go/addons/account"
|
|
_ "odoo-go/addons/product"
|
|
_ "odoo-go/addons/sale"
|
|
_ "odoo-go/addons/stock"
|
|
_ "odoo-go/addons/purchase"
|
|
_ "odoo-go/addons/hr"
|
|
_ "odoo-go/addons/project"
|
|
_ "odoo-go/addons/crm"
|
|
_ "odoo-go/addons/fleet"
|
|
_ "odoo-go/addons/l10n_de"
|
|
|
|
// Google integrations (opt-in, only active with API keys)
|
|
_ "odoo-go/addons/google_address"
|
|
_ "odoo-go/addons/google_translate"
|
|
_ "odoo-go/addons/google_calendar"
|
|
|
|
"odoo-go/pkg/modules"
|
|
"odoo-go/pkg/server"
|
|
"odoo-go/pkg/service"
|
|
"odoo-go/pkg/tools"
|
|
)
|
|
|
|
func main() {
|
|
log.SetFlags(log.Ltime | log.Lshortfile)
|
|
|
|
// Load configuration
|
|
cfg := tools.DefaultConfig()
|
|
cfg.LoadFromEnv()
|
|
|
|
log.Printf("odoo: Odoo Go Server 19.0")
|
|
log.Printf("odoo: database: %s@%s:%d/%s", cfg.DBUser, cfg.DBHost, cfg.DBPort, cfg.DBName)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Handle shutdown signals
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
|
go func() {
|
|
sig := <-sigCh
|
|
log.Printf("odoo: received signal %s, shutting down...", sig)
|
|
cancel()
|
|
}()
|
|
|
|
// Connect to database
|
|
pool, err := pgxpool.New(ctx, cfg.DSN())
|
|
if err != nil {
|
|
log.Fatalf("odoo: database connection failed: %v", err)
|
|
}
|
|
defer pool.Close()
|
|
|
|
if err := pool.Ping(ctx); err != nil {
|
|
log.Fatalf("odoo: database ping failed: %v", err)
|
|
}
|
|
log.Println("odoo: database connected")
|
|
|
|
// Load modules (base is auto-registered via init())
|
|
log.Println("odoo: loading modules...")
|
|
if err := modules.LoadModules(modules.All()); err != nil {
|
|
log.Fatalf("odoo: module loading failed: %v", err)
|
|
}
|
|
log.Printf("odoo: %d modules loaded", len(modules.All()))
|
|
|
|
// Initialize database schema
|
|
log.Println("odoo: initializing database schema...")
|
|
if err := service.InitDatabase(ctx, pool); err != nil {
|
|
log.Fatalf("odoo: schema init failed: %v", err)
|
|
}
|
|
|
|
// Check if setup is needed (first boot)
|
|
if service.NeedsSetup(ctx, pool) {
|
|
log.Println("odoo: database is empty — setup wizard will be shown at /web/setup")
|
|
} else {
|
|
log.Println("odoo: database already initialized")
|
|
}
|
|
|
|
// Start HTTP server
|
|
srv := server.New(cfg, pool)
|
|
log.Printf("odoo: starting HTTP service on %s:%d", cfg.HTTPInterface, cfg.HTTPPort)
|
|
|
|
if err := srv.Start(); err != nil {
|
|
log.Fatalf("odoo: server error: %v", err)
|
|
}
|
|
}
|