Backend improvements: views, fields_get, session, RPC stubs
- Improved auto-generated list/form/search views with priority fields, two-column form layout, statusbar widget, notebook for O2M fields - Enhanced fields_get with currency_field, compute, related metadata - Fixed session handling: handleSessionInfo/handleSessionCheck use real session from cookie instead of hardcoded values - Added read_progress_bar and activity_format RPC stubs - Improved bootstrap translations with lang_parameters - Added "contacts" to session modules list Server starts successfully: 14 modules, 93 models, 378 XML templates, 503 JS modules transpiled — all from local frontend/ directory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -108,54 +108,197 @@ func generateDefaultView(modelName, viewType string) string {
|
||||
}
|
||||
|
||||
func generateDefaultListView(m *orm.Model) string {
|
||||
// Prioritize important fields first
|
||||
priority := []string{"name", "display_name", "state", "partner_id", "date_order", "date",
|
||||
"amount_total", "amount_untaxed", "email", "phone", "company_id", "user_id",
|
||||
"product_id", "quantity", "price_unit", "price_subtotal"}
|
||||
|
||||
var fields []string
|
||||
count := 0
|
||||
added := make(map[string]bool)
|
||||
|
||||
// Add priority fields first
|
||||
for _, pf := range priority {
|
||||
f := m.GetField(pf)
|
||||
if f != nil && f.IsStored() && f.Type != orm.TypeBinary {
|
||||
fields = append(fields, fmt.Sprintf(`<field name="%s"/>`, pf))
|
||||
added[pf] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Fill remaining slots
|
||||
for _, f := range m.Fields() {
|
||||
if f.Name == "id" || !f.IsStored() || f.Name == "create_uid" || f.Name == "write_uid" ||
|
||||
f.Name == "create_date" || f.Name == "write_date" || f.Type == orm.TypeBinary {
|
||||
if len(fields) >= 10 {
|
||||
break
|
||||
}
|
||||
if added[f.Name] || f.Name == "id" || !f.IsStored() ||
|
||||
f.Name == "create_uid" || f.Name == "write_uid" ||
|
||||
f.Name == "create_date" || f.Name == "write_date" ||
|
||||
f.Type == orm.TypeBinary || f.Type == orm.TypeText || f.Type == orm.TypeHTML {
|
||||
continue
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf(`<field name="%s"/>`, f.Name))
|
||||
count++
|
||||
if count >= 8 {
|
||||
break
|
||||
}
|
||||
added[f.Name] = true
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<list>\n %s\n</list>", strings.Join(fields, "\n "))
|
||||
}
|
||||
|
||||
func generateDefaultFormView(m *orm.Model) string {
|
||||
var fields []string
|
||||
for _, f := range m.Fields() {
|
||||
if f.Name == "id" || f.Name == "create_uid" || f.Name == "write_uid" ||
|
||||
f.Name == "create_date" || f.Name == "write_date" || f.Type == orm.TypeBinary {
|
||||
skip := map[string]bool{
|
||||
"id": true, "create_uid": true, "write_uid": true,
|
||||
"create_date": true, "write_date": true,
|
||||
}
|
||||
|
||||
// Header with state widget if state field exists
|
||||
var header string
|
||||
if f := m.GetField("state"); f != nil && f.Type == orm.TypeSelection {
|
||||
header = ` <header>
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
`
|
||||
}
|
||||
|
||||
// Title field (name or display_name)
|
||||
var title string
|
||||
if f := m.GetField("name"); f != nil {
|
||||
title = ` <div class="oe_title">
|
||||
<h1><field name="name" placeholder="Name..."/></h1>
|
||||
</div>
|
||||
`
|
||||
skip["name"] = true
|
||||
}
|
||||
|
||||
// Split fields into left/right groups
|
||||
var leftFields, rightFields []string
|
||||
var o2mFields []string
|
||||
count := 0
|
||||
|
||||
// Prioritize important fields
|
||||
priority := []string{"partner_id", "date_order", "date", "company_id", "currency_id",
|
||||
"user_id", "journal_id", "product_id", "email", "phone"}
|
||||
|
||||
for _, pf := range priority {
|
||||
f := m.GetField(pf)
|
||||
if f == nil || skip[pf] || f.Type == orm.TypeBinary {
|
||||
continue
|
||||
}
|
||||
if f.Type == orm.TypeOne2many || f.Type == orm.TypeMany2many {
|
||||
continue // Skip relational fields in default form
|
||||
skip[pf] = true
|
||||
line := fmt.Sprintf(` <field name="%s"/>`, pf)
|
||||
if count%2 == 0 {
|
||||
leftFields = append(leftFields, line)
|
||||
} else {
|
||||
rightFields = append(rightFields, line)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf(` <field name="%s"/>`, f.Name))
|
||||
if len(fields) >= 20 {
|
||||
count++
|
||||
}
|
||||
|
||||
// Add remaining stored fields
|
||||
for _, f := range m.Fields() {
|
||||
if skip[f.Name] || !f.IsStored() || f.Type == orm.TypeBinary {
|
||||
continue
|
||||
}
|
||||
if f.Type == orm.TypeOne2many {
|
||||
o2mFields = append(o2mFields, fmt.Sprintf(` <field name="%s"/>`, f.Name))
|
||||
continue
|
||||
}
|
||||
if f.Type == orm.TypeMany2many {
|
||||
continue
|
||||
}
|
||||
line := fmt.Sprintf(` <field name="%s"/>`, f.Name)
|
||||
if len(leftFields) <= len(rightFields) {
|
||||
leftFields = append(leftFields, line)
|
||||
} else {
|
||||
rightFields = append(rightFields, line)
|
||||
}
|
||||
if len(leftFields)+len(rightFields) >= 20 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("<form>\n <sheet>\n <group>\n%s\n </group>\n </sheet>\n</form>",
|
||||
strings.Join(fields, "\n"))
|
||||
|
||||
// Build form
|
||||
var buf strings.Builder
|
||||
buf.WriteString("<form>\n")
|
||||
buf.WriteString(header)
|
||||
buf.WriteString(" <sheet>\n")
|
||||
buf.WriteString(title)
|
||||
buf.WriteString(" <group>\n")
|
||||
buf.WriteString(" <group>\n")
|
||||
buf.WriteString(strings.Join(leftFields, "\n"))
|
||||
buf.WriteString("\n </group>\n")
|
||||
buf.WriteString(" <group>\n")
|
||||
buf.WriteString(strings.Join(rightFields, "\n"))
|
||||
buf.WriteString("\n </group>\n")
|
||||
buf.WriteString(" </group>\n")
|
||||
|
||||
// O2M fields in notebook
|
||||
if len(o2mFields) > 0 {
|
||||
buf.WriteString(" <notebook>\n")
|
||||
buf.WriteString(" <page string=\"Lines\">\n")
|
||||
buf.WriteString(strings.Join(o2mFields, "\n"))
|
||||
buf.WriteString("\n </page>\n")
|
||||
buf.WriteString(" </notebook>\n")
|
||||
}
|
||||
|
||||
buf.WriteString(" </sheet>\n")
|
||||
buf.WriteString("</form>")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func generateDefaultSearchView(m *orm.Model) string {
|
||||
var fields []string
|
||||
// Add name field if it exists
|
||||
if f := m.GetField("name"); f != nil {
|
||||
fields = append(fields, `<field name="name"/>`)
|
||||
}
|
||||
if f := m.GetField("email"); f != nil {
|
||||
fields = append(fields, `<field name="email"/>`)
|
||||
var filters []string
|
||||
|
||||
// Search fields
|
||||
searchable := []string{"name", "display_name", "email", "phone", "ref",
|
||||
"partner_id", "company_id", "user_id", "state", "date_order", "date"}
|
||||
for _, sf := range searchable {
|
||||
if f := m.GetField(sf); f != nil {
|
||||
fields = append(fields, fmt.Sprintf(`<field name="%s"/>`, sf))
|
||||
}
|
||||
}
|
||||
if len(fields) == 0 {
|
||||
fields = append(fields, `<field name="id"/>`)
|
||||
}
|
||||
return fmt.Sprintf("<search>\n %s\n</search>", strings.Join(fields, "\n "))
|
||||
|
||||
// Auto-generate filter for state field
|
||||
if f := m.GetField("state"); f != nil && f.Type == orm.TypeSelection {
|
||||
for _, sel := range f.Selection {
|
||||
filters = append(filters, fmt.Sprintf(
|
||||
`<filter string="%s" name="filter_%s" domain="[('state','=','%s')]"/>`,
|
||||
sel.Label, sel.Value, sel.Value))
|
||||
}
|
||||
}
|
||||
|
||||
// Group-by for common fields
|
||||
var groupby []string
|
||||
groupable := []string{"partner_id", "state", "company_id", "user_id", "stage_id"}
|
||||
for _, gf := range groupable {
|
||||
if f := m.GetField(gf); f != nil {
|
||||
groupby = append(groupby, fmt.Sprintf(`<filter string="%s" name="groupby_%s" context="{'group_by': '%s'}"/>`,
|
||||
f.String, gf, gf))
|
||||
}
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
buf.WriteString("<search>\n")
|
||||
for _, f := range fields {
|
||||
buf.WriteString(" " + f + "\n")
|
||||
}
|
||||
if len(filters) > 0 {
|
||||
buf.WriteString(" <separator/>\n")
|
||||
for _, f := range filters {
|
||||
buf.WriteString(" " + f + "\n")
|
||||
}
|
||||
}
|
||||
if len(groupby) > 0 {
|
||||
buf.WriteString(" <group expand=\"0\" string=\"Group By\">\n")
|
||||
for _, g := range groupby {
|
||||
buf.WriteString(" " + g + "\n")
|
||||
}
|
||||
buf.WriteString(" </group>\n")
|
||||
}
|
||||
buf.WriteString("</search>")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func generateDefaultKanbanView(m *orm.Model) string {
|
||||
|
||||
Reference in New Issue
Block a user