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
Forms display individual records with fields organized in groups and sections.
<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>
| 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 |
<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>
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.
<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 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
<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]]'/>
- 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