Skip to content

Layouts

Layouts are XML files that define how your models are displayed in the user interface. Netforce uses these declarative layouts to automatically generate forms, lists, menus, and dashboards without writing frontend code.

Layout Types

There are four main layout types:

  • Form - Detail view for editing records
  • List - Table view for browsing records
  • Menu - Navigation structure
  • Board - Dashboard with widgets

Form Layouts

Forms display individual records with fields organized in groups and sections.

Basic Form Structure

<form model="account.invoice">
    <head>
        <field name="state"/>
    </head>
    <group>
        <field name="number" span="3"/>
        <field name="date" span="3"/>
        <field name="customer_id" span="6"/>
    </group>
</form>

Form Sections

Element Purpose Example
<top> Action buttons toolbar <button string="Print" action="report"/>
<head> Status/state display <field name="state"/>
<group> Field grouping Groups related fields together
<field> Individual field display Field with various attributes
<newline> Force new row Break to next line
<template> Custom HTML content Rich content areas

Complete Form Example

<form model="account.invoice" attrs='{"readonly":[["state","=","posted"]]}'>
    <top>
        <button string="Confirm" method="confirm" states="draft"/>
        <button string="Print" icon="print" action="report_invoice"/>
        <button string="Options" dropdown="1">
            <item string="Copy" method="copy"/>
            <item string="Cancel" method="cancel" confirm="Cancel this invoice?"/>
        </button>
    </top>

    <head>
        <field name="state"/>
    </head>

    <group form_layout="stacked">
        <field name="type" invisible="1"/>
        <field name="number" span="3"/>
        <field name="date" span="3" onchange="update_due_date"/>
        <field name="customer_id" span="6" required="1"/>
        <newline/>
        <field name="due_date" span="3"/>
        <field name="payment_terms" span="3"/>
        <field name="currency_id" span="3"/>
        <field name="reference" span="3"/>
    </group>

    <!-- One-to-Many field with embedded list -->
    <field name="lines" nolabel="1" count="10">
        <list>
            <field name="product_id" onchange="product_changed"/>
            <field name="description"/>
            <field name="qty" onchange="calculate_amount"/>
            <field name="price" onchange="calculate_amount"/>
            <field name="amount"/>
        </list>
    </field>

    <group offset="8" span="4">
        <field name="subtotal" readonly="1"/>
        <field name="tax_amount" readonly="1"/>
        <field name="total_amount" readonly="1"/>
    </group>

    <template span="12">
        <![CDATA[
        <div class="alert alert-info">
            <strong>Note:</strong> This invoice is in {{currency_id.name}}
        </div>
        ]]>
    </template>
</form>

Form Attributes

Root Attributes

<form model="account.invoice" 
      attrs='{"readonly":[["state","=","posted"]]}'
      show_company="1"
      string="Invoice">
Attribute Description Example
model Target model name "account.invoice"
attrs Conditional attributes {"readonly":[["state","=","posted"]]}
show_company Show company selector "1"
string Form title override "Customer Invoice"

Field Attributes

<field name="customer_id" 
       span="6" 
       required="1" 
       readonly="1"
       invisible="1"
       nolabel="1"
       onchange="method_name"
       attrs='{"invisible":[["type","!=","out"]]}'
       condition='[["active","=",true]]'/>
Attribute Description Example
span Column width (1-12) span="6"
required Mandatory field required="1"
readonly Read-only field readonly="1"
invisible Hidden field invisible="1"
nolabel Hide field label nolabel="1"
onchange Change handler method onchange="update_total"
attrs Dynamic attributes Conditional visibility/readonly
condition Filter for relations Domain for Many2One fields

Group Layout

<group form_layout="stacked" span="6" offset="2" columns="2">
    <field name="field1"/>
    <field name="field2"/>
</group>
Attribute Description Default
form_layout Layout style "horizontal" or "stacked"
columns Number of columns 2
span Group width 12
offset Left margin 0

List Layouts

Lists display multiple records in a table format with columns, sorting, and filtering.

Basic List

<list model="account.invoice">
    <field name="number"/>
    <field name="date"/>
    <field name="customer_id"/>
    <field name="total_amount" show_total="1"/>
    <field name="state"/>
</list>

Advanced List Features

<list model="account.invoice" 
      sortable_fields="date,number,customer_id"
      default_sort="-date"
      expand_form_layout="invoice_form"
      colors='{"draft":"muted","posted":"success"}'>

    <field name="number" link="1"/>
    <field name="date" width="100"/>
    <field name="customer_id"/>
    <field name="total_amount" show_total="1" align="right"/>
    <field name="state"/>
</list>

List Attributes

Root Attributes

Attribute Description Example
sortable_fields Sortable columns "date,number,customer_id"
default_sort Default sort order "-date,number"
expand_form_layout Inline editing form "invoice_form"
colors Row color coding {"draft":"muted","posted":"success"}

Field Attributes

Attribute Description Example
link Make field clickable link="1"
width Column width in pixels width="100"
show_total Show column total show_total="1"
align Text alignment align="right"
sum Show sum in footer sum="1"

List with Custom Columns

<list model="sale.order">
    <field name="number" string="Order #"/>
    <field name="partner_id" string="Customer"/>
    <field name="date_order" string="Date"/>
    <field name="amount_total" string="Total" show_total="1"/>
    <field name="state" string="Status"/>

    <!-- Virtual/computed column -->
    <field name="days_overdue" function="get_days_overdue"/>
</list>

Menus define the navigation structure with hierarchical items, icons, and permissions.

Basic Menu

<menu string="Sales">
    <item string="Orders" action="sale_orders" icon="fa_shopping_cart"/>
    <item string="Customers" action="customers" icon="fa_users"/>
    <divider/>
    <item string="Reports" icon="fa_chart_bar">
        <item string="Sales Analysis" action="sales_analysis"/>
        <item string="Customer Report" action="customer_report"/>
    </item>
</menu>
<menu string="Accounting">
    <item string="Dashboard" icon="fa_chart_pie" action="account_board"/>

    <item string="Invoicing" icon="fa_file_invoice">
        <item string="Customer Invoices" action="customer_invoices"/>
        <item string="Supplier Invoices" action="supplier_invoices" perm="accounting.supplier"/>
    </item>

    <item string="Reports" icon="fa_chart_bar" perm="accounting.reports">
        <item string="Profit & Loss" action="profit_loss"/>
        <item string="Balance Sheet" action="balance_sheet"/>
    </item>

    <item string="Settings" icon="fa_cog" perm="accounting.admin">
        <item string="Chart of Accounts" action="chart_accounts"/>
        <item string="Tax Rates" action="tax_rates"/>
    </item>
</menu>
Attribute Description Example
string Display text "Customer Invoices"
action Action to execute "customer_invoices"
icon FontAwesome icon "fa_chart_pie"
perm Required permission "accounting.admin"

Available Icons

Common FontAwesome icons (prefix with fa_):

  • Navigation: fa_home, fa_dashboard, fa_menu
  • Business: fa_file_invoice, fa_shopping_cart, fa_users
  • Charts: fa_chart_pie, fa_chart_bar, fa_chart_line
  • Actions: fa_plus, fa_edit, fa_trash, fa_print
  • Settings: fa_cog, fa_wrench, fa_key

Board Layouts (Dashboards)

Boards create dashboards with widgets arranged in panels.

Basic Board

<board>
    <vpanel>
        <widget action="sales_chart" string="Sales Overview"/>
    </vpanel>
    <vpanel>
        <widget action="top_customers" string="Top Customers"/>
        <widget action="recent_orders" string="Recent Orders"/>
    </vpanel>
</board>

Complex Dashboard

<board>
    <!-- Full-width header -->
    <hpanel>
        <widget action="kpi_summary" string="KPI Summary" span="12"/>
    </hpanel>

    <!-- Two-column layout -->
    <hpanel>
        <vpanel span="8">
            <widget action="sales_trend" string="Sales Trend" height="400"/>
            <widget action="order_status" string="Order Status"/>
        </vpanel>

        <vpanel span="4">
            <widget action="top_products" string="Top Products"/>
            <widget action="alerts" string="System Alerts"/>
            <widget action="quick_stats" string="Quick Stats"/>
        </vpanel>
    </hpanel>
</board>

Board Elements

Element Description Usage
<board> Root dashboard container Top-level element
<hpanel> Horizontal panel (row) Groups widgets horizontally
<vpanel> Vertical panel (column) Groups widgets vertically
<widget> Individual widget Links to action/chart

Widget Attributes

<widget action="sales_chart" 
        string="Monthly Sales" 
        span="6" 
        height="300"
        refresh="60"/>
Attribute Description Example
action Widget action/data source "sales_chart"
string Widget title "Monthly Sales"
span Width (1-12) span="6"
height Height in pixels height="300"
refresh Auto-refresh seconds refresh="60"

Dynamic Attributes

Use the attrs attribute for conditional field behavior based on other field values.

Syntax

attrs='{"attribute_name":[["field_name","operator","value"]]}'

Examples

<!-- Hide field when state is draft -->
<field name="invoice_date" attrs='{"invisible":[["state","=","draft"]]}'/>

<!-- Make field required when type is invoice -->
<field name="due_date" attrs='{"required":[["type","=","invoice"]]}'/>

<!-- Make readonly when posted -->
<field name="amount" attrs='{"readonly":[["state","=","posted"]]}'/>

<!-- Multiple conditions (AND) -->
<field name="bank_account" 
       attrs='{"required":[["payment_method","=","bank"],["state","!=","draft"]]}'/>

<!-- Multiple conditions (OR) -->
<field name="notes" 
       attrs='{"invisible":["or",["type","=","credit"],["amount","=",0]]}'/>

Available Operators

Operator Description Example
= Equals ["state","=","draft"]
!= Not equals ["amount","!=",0]
>, < Greater/less than ["total",">",1000]
>=, <= Greater/less or equal ["qty",">=",1]
in In list ["state","in",["draft","confirmed"]]
not in Not in list ["type","not in",["view"]]

Logical Operators

<!-- AND (default) -->
attrs='{"readonly":[["state","=","posted"],["locked","=",true]]}'

<!-- OR -->
attrs='{"invisible":["or",["state","=","cancelled"],["amount","=",0]]}'

<!-- Complex logic -->
attrs='{"required":["or",["type","=","invoice"],["and",["state","=","draft"],["total",">",1000]]]}'

Layout Organization

File Naming Conventions

layouts/
├── model_name_form.xml      # Main form layout
├── model_name_list.xml      # List view
├── model_name_menu.xml      # Menu structure
├── model_name_board.xml     # Dashboard
├── model_name_form_simple.xml   # Alternative form
└── model_name_list_summary.xml  # Alternative list

Layout Registration

Layouts are automatically discovered by filename patterns or explicitly registered in actions:

<!-- actions/my_actions.xml -->
<action>
    <field name="view">form</field>
    <field name="model">my.model</field>
    <field name="layout">my_custom_form</field>
</action>

Best Practices

1. Form Organization

<form>
    <!-- Actions first -->
    <top>...</top>

    <!-- Status display -->
    <head>...</head>

    <!-- Main fields grouped logically -->
    <group string="Basic Information">...</group>
    <group string="Dates and References">...</group>

    <!-- One-to-Many relations -->
    <field name="lines">...</field>

    <!-- Totals and calculations -->
    <group offset="8" span="4">...</group>

    <!-- Additional info/help -->
    <template>...</template>
</form>

2. Responsive Design

<!-- Use span for responsive width -->
<field name="field1" span="6"/>  <!-- Half width -->
<field name="field2" span="3"/>  <!-- Quarter width -->
<field name="field3" span="12"/> <!-- Full width -->

<!-- Group related fields -->
<group span="6">
    <field name="start_date"/>
    <field name="end_date"/>
</group>

3. User Experience

<!-- Group related fields with labels -->
<group string="Contact Information" span="6">
    <field name="phone"/>
    <field name="email"/>
    <field name="website"/>
</group>

<!-- Use onchange for interactive behavior -->
<field name="product_id" onchange="product_changed"/>

<!-- Provide helpful conditions for relations -->
<field name="account_id" condition='[["type","!=","view"],["active","=",true]]'/>

4. Performance

  • Use condition attributes to filter large relations
  • Limit count on One2Many fields for better loading
  • Use appropriate field spans to avoid layout issues
  • Group related fields together

5. Accessibility

  • Always provide meaningful field labels
  • Use appropriate field types (Date for dates, Decimal for amounts)
  • Include help text in templates when needed
  • Use proper grouping and sections

Next Steps