package server import ( "encoding/json" "net/http" ) // handleLoadBreadcrumbs returns breadcrumb data for the current navigation path. // Mirrors: odoo/addons/web/controllers/action.py Action.load_breadcrumbs() func (s *Server) handleLoadBreadcrumbs(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req JSONRPCRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { s.writeJSONRPC(w, nil, nil, &RPCError{Code: -32700, Message: "Parse error"}) return } s.writeJSONRPC(w, req.ID, []interface{}{}, nil) } // handleActionLoad loads an action definition by ID. // Mirrors: odoo/addons/web/controllers/action.py Action.load() func (s *Server) handleActionLoad(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req JSONRPCRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { s.writeJSONRPC(w, nil, nil, &RPCError{Code: -32700, Message: "Parse error"}) return } var params struct { ActionID interface{} `json:"action_id"` Context interface{} `json:"context"` } json.Unmarshal(req.Params, ¶ms) // Parse action_id from params (can be float64 from JSON or string) actionID := 0 switch v := params.ActionID.(type) { case float64: actionID = int(v) case string: // Try to parse numeric string for _, c := range v { if c >= '0' && c <= '9' { actionID = actionID*10 + int(c-'0') } else { actionID = 0 break } } } // Action definitions by ID actions := map[int]map[string]interface{}{ 1: { "id": 1, "type": "ir.actions.act_window", "name": "Contacts", "res_model": "res.partner", "view_mode": "list,kanban,form", "views": [][]interface{}{{nil, "list"}, {nil, "kanban"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "contacts.action_contacts", }, 2: { "id": 2, "type": "ir.actions.act_window", "name": "Invoices", "res_model": "account.move", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": `[("move_type","in",["out_invoice","out_refund"])]`, "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "account.action_move_out_invoice_type", }, 3: { "id": 3, "type": "ir.actions.act_window", "name": "Sale Orders", "res_model": "sale.order", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "sale.action_quotations_with_onboarding", }, 4: { "id": 4, "type": "ir.actions.act_window", "name": "CRM Pipeline", "res_model": "crm.lead", "view_mode": "kanban,list,form", "views": [][]interface{}{{nil, "kanban"}, {nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "crm.crm_lead_all_pipeline", }, 5: { "id": 5, "type": "ir.actions.act_window", "name": "Transfers", "res_model": "stock.picking", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "stock.action_picking_tree_all", }, 6: { "id": 6, "type": "ir.actions.act_window", "name": "Products", "res_model": "product.template", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "stock.action_product_template", }, 7: { "id": 7, "type": "ir.actions.act_window", "name": "Purchase Orders", "res_model": "purchase.order", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "purchase.action_purchase_orders", }, 8: { "id": 8, "type": "ir.actions.act_window", "name": "Employees", "res_model": "hr.employee", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "hr.action_hr_employee", }, 9: { "id": 9, "type": "ir.actions.act_window", "name": "Departments", "res_model": "hr.department", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "hr.action_hr_department", }, 10: { "id": 10, "type": "ir.actions.act_window", "name": "Projects", "res_model": "project.project", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "project.action_project", }, 11: { "id": 11, "type": "ir.actions.act_window", "name": "Tasks", "res_model": "project.task", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "project.action_project_task", }, 12: { "id": 12, "type": "ir.actions.act_window", "name": "Vehicles", "res_model": "fleet.vehicle", "view_mode": "list,form", "views": [][]interface{}{{nil, "list"}, {nil, "form"}}, "search_view_id": false, "domain": "[]", "context": "{}", "target": "current", "limit": 80, "help": "", "xml_id": "fleet.action_fleet_vehicle", }, } action, ok := actions[actionID] if !ok { // Default to Contacts if unknown action ID action = actions[1] } s.writeJSONRPC(w, req.ID, action, nil) }