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>
62 lines
1.8 KiB
Go
62 lines
1.8 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
)
|
|
|
|
// handleLoadMenus returns the menu tree for the webclient.
|
|
// Mirrors: odoo/addons/web/controllers/home.py Home.web_load_menus()
|
|
func (s *Server) handleLoadMenus(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "public, max-age=3600")
|
|
|
|
// Build menu tree from database or hardcoded defaults
|
|
menus := map[string]interface{}{
|
|
"root": map[string]interface{}{
|
|
"id": "root",
|
|
"name": "root",
|
|
"children": []int{1},
|
|
"appID": false,
|
|
"xmlid": "",
|
|
"actionID": false,
|
|
"actionModel": false,
|
|
"actionPath": false,
|
|
"webIcon": nil,
|
|
"webIconData": nil,
|
|
"webIconDataMimetype": nil,
|
|
"backgroundImage": nil,
|
|
},
|
|
"1": map[string]interface{}{
|
|
"id": 1,
|
|
"name": "Contacts",
|
|
"children": []int{10},
|
|
"appID": 1,
|
|
"xmlid": "contacts.menu_contacts",
|
|
"actionID": 1,
|
|
"actionModel": "ir.actions.act_window",
|
|
"actionPath": false,
|
|
"webIcon": "fa-address-book,#71639e,#FFFFFF",
|
|
"webIconData": nil,
|
|
"webIconDataMimetype": nil,
|
|
"backgroundImage": nil,
|
|
},
|
|
"10": map[string]interface{}{
|
|
"id": 10,
|
|
"name": "Contacts",
|
|
"children": []int{},
|
|
"appID": 1,
|
|
"xmlid": "contacts.menu_contacts_list",
|
|
"actionID": 1,
|
|
"actionModel": "ir.actions.act_window",
|
|
"actionPath": false,
|
|
"webIcon": nil,
|
|
"webIconData": nil,
|
|
"webIconDataMimetype": nil,
|
|
"backgroundImage": nil,
|
|
},
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(menus)
|
|
}
|