`)
title := ""
if v, ok := rec["display_name"]; ok {
title = fmt.Sprintf("%v", v)
} else if v, ok := rec["name"]; ok {
title = fmt.Sprintf("%v", v)
}
if title != "" {
b.WriteString(fmt.Sprintf("
%s
", htmlEscape(title)))
}
b.WriteString("
")
b.WriteString("| Field | Value |
")
for key, val := range rec {
if key == "id" {
continue
}
valStr := fmt.Sprintf("%v", val)
if valStr == "" {
valStr = ""
}
b.WriteString(fmt.Sprintf("| %s | %s |
",
htmlEscape(key), htmlEscape(valStr)))
}
b.WriteString("
")
b.WriteString("
")
}
b.WriteString(`
`)
return b.String()
}
// resolveReportModel maps a report name to the ORM model it operates on.
// Mirrors: odoo ir.actions.report → model field.
func resolveReportModel(reportName string) string {
mapping := map[string]string{
"account.report_invoice": "account.move",
"sale.report_saleorder": "sale.order",
"stock.report_picking": "stock.picking",
"purchase.report_purchaseorder": "purchase.order",
"contacts.report_partner": "res.partner",
}
return mapping[reportName]
}
// renderHTMLReport generates a basic HTML report for the given records.
// This is a minimal implementation — Odoo uses QWeb templates for real reports.
func renderHTMLReport(reportName, modelName string, records []orm.Values) string {
var b strings.Builder
b.WriteString(`
`)
// Use "name" or "display_name" as section title if available
title := ""
if v, ok := rec["display_name"]; ok {
title = fmt.Sprintf("%v", v)
} else if v, ok := rec["name"]; ok {
title = fmt.Sprintf("%v", v)
}
if title != "" {
b.WriteString(fmt.Sprintf("
%s
", htmlEscape(title)))
}
b.WriteString("
")
b.WriteString("| Field | Value |
")
for key, val := range rec {
if key == "id" {
continue
}
valStr := fmt.Sprintf("%v", val)
if valStr == "" {
valStr = ""
}
b.WriteString(fmt.Sprintf("| %s | %s |
",
htmlEscape(key), htmlEscape(valStr)))
}
b.WriteString("
")
b.WriteString("
")
}
b.WriteString("")
return b.String()
}
// htmlEscape escapes HTML special characters.
func htmlEscape(s string) string {
return template.HTMLEscapeString(s)
}