From cc6184a18bcaf2c2207bc3aa193e911632246c4a Mon Sep 17 00:00:00 2001 From: Marc Date: Sat, 4 Apr 2026 11:58:08 +0200 Subject: [PATCH] Fix action domain/context parsing for fresh DB usability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Action domain strings like [("move_type","in",["out_invoice"])] were returned as raw strings. The OWL client needs JSON arrays. - parseDomainOrDefault: converts Python-style domain to JSON array (replaces () with [], ' with ", True/False/None) - parseContextOrDefault: same for context dicts - Fixes empty Invoice/Vendor Bills list views on fresh DB Full flow verified on clean DB: Contact → Sale Order → Confirm → Invoice → Post → all working. All 9 module actions load correctly. Co-Authored-By: Claude Opus 4.6 (1M context) --- pkg/server/action.go | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/pkg/server/action.go b/pkg/server/action.go index fd20f37..5771b20 100644 --- a/pkg/server/action.go +++ b/pkg/server/action.go @@ -162,8 +162,8 @@ func (s *Server) handleActionLoad(w http.ResponseWriter, r *http.Request) { "view_mode": coalesce(viewMode, "list,form"), "views": views, "search_view_id": false, - "domain": coalesce(deref(domain), "[]"), - "context": coalesce(deref(actCtx), "{}"), + "domain": parseDomainOrDefault(deref(domain)), + "context": parseContextOrDefault(deref(actCtx)), "target": coalesce(deref(target), "current"), "limit": coalesceInt(limit, 80), "help": deref(help), @@ -225,6 +225,45 @@ func coalesceInt(p *int, fallback int) int { return fallback } +// parseDomainOrDefault tries to parse a Python-style domain string to JSON. +// Returns parsed []interface{} if valid JSON, otherwise returns "[]". +func parseDomainOrDefault(s string) interface{} { + s = strings.TrimSpace(s) + if s == "" || s == "[]" { + return []interface{}{} + } + // Try JSON parse directly (handles [["field","op","val"]] format) + var result []interface{} + // Convert Python-style to JSON: replace ( with [, ) with ], True/False/None + jsonStr := strings.ReplaceAll(s, "(", "[") + jsonStr = strings.ReplaceAll(jsonStr, ")", "]") + jsonStr = strings.ReplaceAll(jsonStr, "'", "\"") + jsonStr = strings.ReplaceAll(jsonStr, "True", "true") + jsonStr = strings.ReplaceAll(jsonStr, "False", "false") + jsonStr = strings.ReplaceAll(jsonStr, "None", "null") + if err := json.Unmarshal([]byte(jsonStr), &result); err == nil { + return result + } + // Fallback: return as-is string (client may handle it) + return s +} + +// parseContextOrDefault tries to parse context string to JSON object. +func parseContextOrDefault(s string) interface{} { + s = strings.TrimSpace(s) + if s == "" || s == "{}" { + return map[string]interface{}{} + } + var result map[string]interface{} + jsonStr := strings.ReplaceAll(s, "'", "\"") + jsonStr = strings.ReplaceAll(jsonStr, "True", "true") + jsonStr = strings.ReplaceAll(jsonStr, "False", "false") + if err := json.Unmarshal([]byte(jsonStr), &result); err == nil { + return result + } + return map[string]interface{}{} +} + // deref returns the value of a string pointer, or "" if nil. func deref(p *string) string { if p != nil {