🎨 DRKDS Studio
A powerful no-code / low-code customisation suite for Odoo 19. Build views, automate workflows, manage models, configure security — all without leaving the browser.
All Features at a Glance
⚡ Quick Start Guide
Get from zero to a customised view in under 5 minutes.
Opening Studio
Open a form or list view of the model you want to customise (e.g. Sales Orders, Contacts, Employees).
The Studio panel slides in from the right. The model technical name (e.g. sale.order) is shown just below the header.
Click one of the toggle buttons: Form · List · Search · Kanban · Pivot · Graph · Cal · Gantt · Rpt
Most Common Tasks
All fields not yet in the view are listed. Click any to add instantly.
Opens the Field Wizard. Pick type → fill label → Save. The new field is added to both the model and the view.
Changes are written to the database view XML.
Lists all automation rules for this model.
Set: Name, Trigger (When), optional Filters.
E.g. "Send Email" → pick template → Save Rule.
Shows all Studio reports for this model.
Enter report name, tick the fields to include, click Create.
The new report appears in the Print (⎙) dropdown for every record of this model.
Opens the Custom Models manager.
Enter display name (e.g. "Project Phase"). Technical name auto-fills as x_project_phase.
Navigate to the model's list view, then use Studio to add custom fields and configure views.
📋 Form View Builder
Add, remove, reorder, and configure fields on any form view without writing XML.
Complete Workflow
Adding Fields
Add Tab — existing or new field
All fields on this model not yet in the view are shown with a type badge and technical name.
The field is inserted into the view. Switch to "Fields" tab to see and reorder it.
Opens the Field Wizard: choose type, label, and type-specific options. The field is created on the model and added to the view in one step.
Set: Label, Type (Object/Method or Action/XML-ID), Placement (Header or Sheet body), Style (Primary/Secondary/Warning/Danger).
Reorder & Remove Fields
All fields currently in the view are listed with drag handles.
"Unsaved changes" badge appears in footer.
Core model fields show a 🔒 and cannot be removed from the panel.
Field Properties (Props Tab)
| Property | Example value | Effect |
|---|---|---|
| Required | state == 'draft' | Field is mandatory when condition is true |
| Readonly | state != 'draft' | Field is read-only when condition is true |
| Invisible | type == 'service' | Field is hidden when condition is true |
| Widget | statusbar | Changes how the field renders in the form |
record. prefix needed).Other Panel Tabs
| Tab | What you can do |
|---|---|
| Struct | Manage tabs (notebook pages), smart buttons, field groups, separators, statusbar field, chatter toggle |
| XML | Edit raw view XML directly — invalid XML is rejected before saving |
| Models | Create or delete entire custom models from the panel header |
☰ List View Builder
Add, remove, and reorder columns. Manage list options, row decorations, optional columns, and sorting.
Workflow
Step 1 — Switch to List Mode
The Fields tab label changes to "Cols" reflecting column management.
Click "New / Manage List Views" below the toggle. Enter a name → Create List View, or click the open icon on an existing Studio list view to load it.
Step 2 — View & Reorder Columns (Cols tab)
All columns currently in the list are shown. Hint: "Drag to reorder columns. Click to edit column options."
"Unsaved changes" badge appears in panel footer.
Core model columns show a 🔒 and cannot be removed.
Opens inline options for that column (see Column Details below).
Step 3 — Add a Column (Add tab)
Lists all model fields not yet shown as columns.
The field appears at the end of the column list. Drag-reorder it under "Cols".
Opens the Field Wizard to create a new model field which is also added as a column.
Column Details (per-column options)
| Option | Description |
|---|---|
| Optional column | Mark as optional → users can show/hide it from the column header menu |
| col_invisible | Python expression — hides the column when true (e.g. context.get('no_qty')) |
| Aggregate | Aggregation function for numeric columns shown in list footer: sum, avg, min, max |
Step 4 — List Options (Opts tab)
The Opts tab is visible only in List mode. It provides list-level configuration.
Choose whether the list is editable inline: Top (new row at top), Bottom (new row at bottom), or Not editable.
Enable/disable: no_open (row click does not open form), create, delete, import, export_xlsx.
Set a default grouping field and a default sort field with direction (asc / desc).
Add conditional row highlight rules. Choose a decoration class and enter a Python condition. Click Add Decoration. Existing decorations can be deleted with ✕.
Add custom action buttons to the list header. Set: label, icon, style, and the server action name. Click Add Button.
This is a separate save from the main panel footer Save. Applies list-level config changes.
Row Decoration Classes
| Class | Visual | Example condition |
|---|---|---|
decoration-success | ■ Green | state == 'done' |
decoration-warning | ■ Amber | date_deadline < today |
decoration-danger | ■ Red | state == 'cancel' |
decoration-info | ■ Blue | priority == '2' |
decoration-muted | ■ Grey | active == False |
decoration-bf | Bold | amount_total > 50000 |
decoration-it | Italic | is_draft |
decoration-muted, NOT decoration-secondary — the latter is invalid in Odoo 19.state == 'cancel' with decoration-danger turns cancelled rows red.📌 Kanban View Builder
Configure which fields appear on kanban cards and control card appearance — status ribbon, color, groupby, quick create.
Step 1 — Switch to Kanban Mode
The Fields tab label changes to "Card".
Studio auto-creates a basic kanban view with the title/name field. The card field list then becomes available.
Step 2 — Manage Card Fields (Card tab)
Lists all fields on each kanban card. Hint: "Drag to reorder fields shown on each kanban card."
Updates the XPath //t[@t-name='card'] in the view arch.
Core fields (🔒) cannot be removed.
The field appears at the end of the card field list.
Step 3 — Configure Card Appearance (Cfg tab)
The Cfg tab appears next to "Add" when a kanban view exists.
Select a field for the ribbon indicator. priority shows star ratings; kanban_state shows a traffic-light dot. "None" disables the ribbon.
Select an Integer field (values 0–11) for card colour highlighting. Users can then pick a colour from the card's ⋮ menu. "None" disables colour. Requires Integer, not Many2one.
Select a many2one or selection field to group the kanban by default on load. "None" = no default grouping.
Enabled (default) = clicking an empty column header opens a quick-create input. Disabled = requires opening a full form to create records.
Saves the configuration to the kanban view XML. Button appears only when changes are pending (dirty state).
Tips
- The Props tab is disabled in Kanban mode — field attributes are not editable per-card from the panel.
- Use the XML tab to hand-edit the raw kanban view for advanced customisation.
- Color By requires an Integer field with values 0–11 (Odoo's built-in colour palette). Selection fields do not work for this.
🔍 Search View Builder
Control which fields appear in the search bar, add quick-filter buttons, and configure Group By options.
Searchable Field Types
Only these field types can be added to a search view:
Search View Elements
| Element | Purpose |
|---|---|
| <field> | Adds a field to the search bar for text/value matching |
| <filter> | Quick-filter button with a fixed domain (e.g. "Active" = [('active','=',True)]) |
| <group> | Group By option (groups records by a field) |
| <separator> | Visual separator between filter buttons |
📊 Pivot View Builder
Configure row groups, column groups, measures, date granularity, and view name for the pivot analysis view.
Step 1 — Switch to Pivot Mode
Hint: "Assign fields as Row groups, Column groups, or Measures."
Studio auto-creates a basic pivot view. The field configuration then becomes available.
Step 2 — Set View Name
The View Name input at the top of the Pivot tab sets the internal name saved with the view. Example: Monthly Revenue Pivot. Editing it marks the view as dirty.
Step 3 — Add Fields
Lists all suitable fields not yet in the pivot view.
Numeric fields default to Meas role; others default to Row role.
Step 4 — Set Field Roles
Each pivot field shows three role buttons: Row, Col, Meas.
| Role | Eligible Types | Purpose |
|---|---|---|
| Row | many2one, selection, date, datetime, char | Groups data into rows (vertical axis) |
| Col | many2one, selection, date, datetime | Groups data into columns (horizontal axis) |
| Meas | integer, float, monetary | Numeric value aggregated in pivot cells |
The active role is highlighted. Clicking an already-active role deactivates it (field has no role).
When a date/datetime field is assigned Row or Col role, a granularity dropdown appears: Day · Week · Month · Quarter · Year.
Multiple Measure fields can be added — each appears as a separate column in the pivot table.
Example: Sales Analysis
Result: A matrix with each salesperson on rows, months on columns, showing total amount per cell.
Step 5 — Save or Reset
Writes the view name and all field roles (including date granularity) to the pivot view XML using //pivot position="replace".
Reloads the pivot field list from the server.
📈 Graph View Builder
Configure bar, line, or pie chart views with field roles, view name, and chart type selector.
Step 1 — Switch to Graph Mode
Hint: "Select chart type and assign Row (category) and Measure (value) fields."
Studio auto-creates a basic graph view. The field configuration then becomes available.
Step 2 — Set View Name & Chart Type
The internal name saved with the view. Example: Sales by Month.
Click one to set the default chart type. The active button is highlighted. This sets the type attribute in the graph view XML.
| Type | Best for |
|---|---|
| 🏦 Bar | Comparing categories (e.g. sales by product) |
| 📉 Line | Trends over time (e.g. revenue per month) |
| 🥧 Pie | Proportional breakdown (e.g. orders by status) |
Step 3 — Add Fields & Set Roles
Numeric fields default to Meas; all others default to Row.
Active role is highlighted. Clicking active role deactivates it.
| Role | Purpose | Notes |
|---|---|---|
| Row | X-axis / category grouping / pie segments | Any non-numeric field |
| Col | Series grouping (stacked/grouped bar) | Disabled for integer, float, monetary fields |
| Meas | Y-axis numeric measure / pie segment size | Numeric fields only |
Step 4 — Save or Reset
Writes view name, chart type, and all field roles to the graph view XML using //graph position="replace".
Reloads the graph field list and chart type from the server.
📅 Calendar View Builder
Configure how records appear in the calendar view — date fields, labels, colour grouping, default mode, quick create.
Step 1 — Switch to Calendar Mode
Hint: "Configure date fields, color grouping, and default view mode."
Studio auto-creates a basic calendar view and auto-detects the best available date field for Start Date. The config panel then becomes available.
Step 2 — Configure Calendar Fields
Select a date or datetime field — marks where each calendar event begins. The "Save Calendar Config" button is disabled until a Start Date is selected.
Date/Datetime field for event end. If not set, each event appears as a single-day/single-time entry.
A float or integer field for event duration in hours. If both End Date and Duration Field are set, Duration Field takes precedence in the calendar view XML.
Select a field whose value is shown as the event title on the calendar. Date/datetime and many2one fields are listed.
Select a many2one field only — events are colour-coded by that field's value, with a colour legend in the calendar sidebar. Selection fields are not supported for calendar colouring in Odoo 19.
Sets the initial calendar display: Day · Week · Month (default) · Year.
When enabled (default), clicking an empty slot opens a quick-create dialog. When disabled, a full form opens instead.
Step 3 — Save Calendar Config
The button is disabled until a change is pending (dirty) AND a Start Date is selected. Saves the configuration to the calendar view XML.
🗓️ Gantt View Builder
Interactive timeline chart powered by Frappe Gantt (bundled locally — no CDN required). Drag bars to reschedule records.
Step 1 — Switch to Gantt Mode
Hint: "Configure the Gantt chart fields for this model."
The Gantt view is rendered as a standalone page. Configuration is stored in ir.config_parameter.
Step 2 — Configure the Gantt Chart
Free-text heading shown above the Gantt chart. Example: Project Task Timeline.
Select a date or datetime field — determines where each bar begins. "Save Config" and "Open Gantt" buttons are disabled until Start Date is set.
Date/datetime field for bar end. If left as "none (1-day bars)", each bar spans exactly one day.
Select a Char or Text field — its value is displayed as text on each Gantt bar.
Select a many2one or selection field — records are colour-coded by this field's value.
Select a float or integer field (values 0–100). Renders a progress bar inside each Gantt bar showing completion percentage.
Select a many2one or selection field to create swim-lane rows (grouped Gantt). "none (flat list)" shows all bars ungrouped.
Initial zoom level of the Gantt timeline.
| Scale option | Value stored |
|---|---|
| Week (default) | Week |
| Day | Day |
| Month | Month |
| Quarter Day | QuarterDay |
| Half Day | HalfDay |
Step 3 — Save & Open Gantt
Saves to ir.config_parameter key drkds_studio.gantt_config.{model}. Persists across server restarts. Disabled until Start Date is selected.
Opens the Gantt chart view in the main content area. Also disabled if no Start Date is selected.
static/lib/frappe-gantt/ — no npm install, no internet connection, no CDN needed. Each model stores its own separate Gantt config.🖨️ QWeb Report Designer
Create printable PDF or HTML reports for any model. Choose a layout template, pick fields, edit XML, and print directly from the model.
Step 1 — Open the Reports Tab
Shows all reports bound to the current model — both Studio-created (with "studio" badge) and native Odoo reports.
⎙ Print (preview), </> Edit XML (code editor), 🗑️ Delete (Studio reports only).
Step 2 — Create a New Report
Required. Becomes the report action name in the Print menu. "Create Report" button is disabled until a name is entered.
PDF = qweb-pdf (downloads a PDF file). HTML = qweb-html (renders inline in the browser).
Four template cards are available:
| Template | Description |
|---|---|
| Simple Table | Clean label/value rows for each selected field |
| Invoice Style | Coloured header block + info grid + bordered summary table — suitable for billing documents |
| Two Column | Fields displayed side by side in two columns |
| Delivery Slip | Compact 3-column card grid with green header — suitable for delivery/shipping documents |
Dropdown lists many2one fields pointing to res.partner or res.lang. Selecting one causes the report to auto-print in the recipient's language. Leave as "None (company language)" if not needed.
A scrollable checklist shows all suitable model fields. Excluded: Binary, HTML, One2many, Many2many. At least one field must be selected.
Generates the QWeb view XML (using lxml — no XML injection risk), creates an ir.actions.report bound to this model. The report immediately appears in the model's Print ⎙ action menu.
Step 3 — Edit Report XML
Opens the inline XML editor showing the full <t t-name="..."> QWeb template.
Common edits: add/remove <span t-field="o.field_name"/> elements, adjust styles, change layout. Note: t-field renders with Odoo's built-in formatting.
XML is validated before saving. Invalid XML is rejected with a clear error message.
Returns to the report list. Unsaved XML changes are discarded.
Step 4 — Print & Delete
Triggers the Odoo report action in the current record context. PDF reports download; HTML reports render inline.
Deletes both the ir.actions.report record and the underlying QWeb ir.ui.view. Only Studio-created reports (with "studio" badge) show the delete button. Native Odoo reports cannot be deleted.
web.external_layout — your company header and footer are included automatically. Click Refresh in the Rpt tab to reload the report list from the server.⚙️ Automation Builder
Create and manage base.automation rules with full inline form — triggers, filters, 7 action types.
Trigger Types
| Trigger | When it fires |
|---|---|
| on_create | When a new record is created |
| on_write | When an existing record is updated |
| on_create_or_write | On both create and update |
| on_unlink | When a record is deleted |
| on_change | When a specific field value changes |
| on_time | At a scheduled time (based on a date field) |
7 Action Types
| Action | What it does | Key input |
|---|---|---|
| Execute Python Code | Run arbitrary Python | Code block (record, env available) |
| Update the Record | Set a field value | Field name + Fixed Value or Python Expression |
| Create a New Record | Create a record on same or other model | Target model + population code |
| Send Email | Send via a mail template | Template selection |
| Add Followers | Subscribe partners to the thread | Partner checklist or dynamic field |
| Create Next Activity | Schedule a follow-up | Type, summary, due in, assigned to |
| Send SMS | Send an SMS (requires sms module) | SMS template selection |
Example: Auto-assign on Create
🛠️ Server Actions Builder
Create Python server actions and bind them to the model's Action (⚙️) menu.
Creating a Server Action
Lists all server actions for this model (both Studio-created and others).
Enter a name and write Python code. Variables: records, env, model.
When bound (green border), the action appears in the ⚙️ Action dropdown on list and form views.
binding_model_id=False and binding_view_types=False. Never set binding_type=False — that raises a validation error.🔗 Outgoing Webhook Builder
POST JSON payloads to external URLs whenever a model event (create / update / delete) occurs.
How Webhooks Work
import statements. Webhooks work by calling env['studio.webhook']._fire_outgoing() internally — never put imports in server action code.Payload
| Field | Value |
|---|---|
| model | Technical model name (e.g. sale.order) |
| trigger | on_create / on_create_or_write / on_unlink |
| record_id | ID of the triggering record |
| record_name | display_name of the triggering record |
| timestamp | ISO 8601 UTC timestamp |
⏰ Scheduled Actions Manager
Create cron jobs that run Python code on a time-based schedule.
Key Points
Identified by the cron_name prefix. All other crons are shown read-only.
Review the code before activating to avoid unintended side effects.
name AND cron_namename maps to the linked ir.actions.server.name (required). cron_name is the display label on ir.cron.
with_context(active_test=False) to see inactive crons — Odoo hides them by default in search queries.🔢 Sequence Builder
Define formatted sequences like INV/2025/0001 with custom prefix, suffix, padding, and increment.
Sequence Format Tokens
| Token | Replaced with | Example |
|---|---|---|
%(year)s | 4-digit year | 2025 |
%(month)s | 2-digit month | 03 |
%(day)s | 2-digit day | 09 |
| padding zeros | Auto-incremented counter | 00042 |
INV/%(year)s/ + Padding 4 → INV/2025/0001, INV/2025/0002, …Options
| Option | Description |
|---|---|
| Padding | Number of digits for the counter (e.g. 4 → 0001) |
| Increment | Step between sequence numbers (default 1) |
| Next Number | Override the next value to be issued |
| Prefix | Static prefix before counter (supports date tokens) |
| Suffix | Static suffix after counter (supports date tokens) |
🗃️ Custom Model Creator
Create brand-new data models without writing Python — complete with auto-generated views, menus, and access rules.
What Gets Auto-Created
| Artefact | Detail |
|---|---|
| ir.model | Model with state='manual' |
| x_name field | Auto-created Char field; Odoo sets it as _rec_name |
| Form view | Minimal form view named studio.custom.{model}.form |
| List view | Minimal list view named studio.custom.{model}.list |
| Window action | ir.actions.act_window pointing to the model |
| Menu item | Under top-level "Custom" menu (sequence 100) |
🏷️ Fields & Default Values Manager
Create custom fields on any model. Set global, user-specific, or company-specific default values.
Field Wizard — Supported Types
x_ (e.g. x_priority_score). They persist in the database until explicitly deleted.Default Value Scopes
| Scope | Applies to | Use case |
|---|---|---|
| Global | All users, all companies | System-wide default for a field |
| Company | Current company only | Company-specific default (e.g. tax rate) |
| User | Current logged-in user only | Personal preference default |
📝 Selection Field Values Editor
Add or remove values from Selection fields — for both standard and custom fields.
Two-Level UI
state != 'manual'). Only custom fields (state = 'manual') allow full CRUD on selection values.🔀 Model Relations Browser
Visually explore Many2one, One2many, and Many2many links for the current model. Read-only — no changes needed.
Relation Types
| Relation | Direction | Extra info shown |
|---|---|---|
| Many2one | This model → Related model | Field name, related model label |
| One2many | This model ← Child model | Field name, inverse field name, child model label |
| Many2many | This model ↔ Related model | Field name, relation table name, related model label |
🔐 Access Rights Manager
Grant or revoke CRUD permissions on any model per user group — with one-click toggle badges.
Permission Badges
| Badge | Permission | Colour when active |
|---|---|---|
| R | Read | ■ Green |
| W | Write (update) | ■ Green |
| C | Create | ■ Green |
| D | Delete (unlink) | ■ Green |
privilege_id != False in Odoo 19 (no category_id on res.groups).📜 Record Rules Manager
Row-level security — restrict which records a user can see or modify using domain filters.
Rule Options
| Option | Description |
|---|---|
| Domain | Python domain string like [('user_id','=',user.id)] |
| Global | Applies to all users (no group restriction) |
| Groups | Restricts the rule to specific user groups |
| Read / Write / Create / Delete | Which operations this rule applies to |
ir.rule.global is a Python keyword. Access via r['global'] (NOT r.global_).user and time available. Example: [('date','>=', (datetime.date.today() - datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]📧 Email Template Builder
Build reusable Jinja2 email templates. Use them in Automation rules or send manually.
Template Variables
| Variable | Refers to | Example |
|---|---|---|
object | The triggering record | {{ object.name }} |
user | The current user | {{ user.name }} |
company | The current company | {{ company.name }} |
ctx | Template context dict | {{ ctx.get('key') }} |
{{ user.email | safe }} is REJECTED (pipe filters not whitelisted). Use {{ user.email }} only.📨 Email Gateway Manager
Manage mail.alias entries so incoming emails create or update records on your model.
Alias Configuration
| Field | Description |
|---|---|
| Alias Name | Local part of the email (e.g. sales → sales@yourdomain.com) |
| Alias Domain | Your outgoing mail server domain |
| Alias Model | Model to create/update when email arrives |
| Alias Defaults | Python dict of default field values for new records |
| Thread Detection | Whether to detect existing records by email thread |
🔔 Activity Type Manager
Create custom activity types (phone call, upload, follow-up) with default due delays and scope control.
Activity Type Options
| Option | Values | Effect |
|---|---|---|
| Category | default / upload_file / phonecall | Changes icon and behaviour of the activity |
| Default Delay | N days / weeks / months | Pre-fills due date when creating the activity |
| Scope | model / global | Model-specific: only on this model. Global: available on all models |
🔖 Saved Filters Manager
Create reusable domain filters that appear as quick-access buttons in the search bar.
Filter Scope
| Scope | Who sees it | Odoo 19 implementation |
|---|---|---|
| Global | All users | user_ids=[(5,0,0)] (empty M2M) |
| Private | Current user only | user_ids=[(4, uid)] |
ir.filters.user_id was removed. Use user_ids (Many2many to res.users).safe_eval before saving. Invalid domain strings are rejected with a clear error message.⬇️ Data Export Templates Manager
Create named export templates that pre-select which fields to include when exporting model data.
How to Use
Shows all export templates for this model.
Enter a name and tick the fields to include. Binary, HTML, and relational fields (O2M) are excluded.
When users export data from a list view, the saved template appears as a preset in the export dialog (under Saved Exports).
⬆️ Import CSV / XLS
Upload a CSV or Excel file, map columns to model fields, and import data in 3 steps.
Steps
The wizard auto-detects column headers and attempts to auto-map them to model fields by name similarity.
For each CSV column, select the target model field from a dropdown. Unmapped columns are skipped.
Rows are validated and imported. A results panel shows the count of created/updated records and any row-level errors.
base_import Odoo module (included in standard Odoo 19).⚡ Batch Actions
Bind server actions to list and/or form views — so users can run them on selected records from the Action menu.
Binding & Unbinding
Lists all server actions that can be bound as batch actions for this model.
When ON (green border), the action appears in the ⚙️ Action dropdown on list views when records are selected.
Controls whether the action appears on the list view's multi-select menu, the form view's Action button, or both.
Studio.Batch: for identification. They are ir.actions.server records with binding_model_id set.📦 Module Export
Export all your Studio customisations as a standard Odoo .zip module — ready to deploy on any server.
What's Included in the Export
| File | Content |
|---|---|
__manifest__.py | Module metadata (name, version, depends) |
models/custom_models.py | Custom model definitions |
security/ir.model.access.csv | Access rules for custom models |
views/custom_model_views.xml | Form and list views for custom models |
views/view_customizations.xml | Studio view changes on standard models |
data/menu_data.xml | Custom menus and window actions |
zipfile and base64 only — no external dependencies. The browser downloads the zip automatically via Blob URL.ir.model.fields.selection_ids. External IDs are resolved safely for all records.🌐 API Info Panel
Browse JSON-RPC endpoints for the current model. Copy ready-to-use Python, JavaScript, or cURL snippets.
Available Snippets
| Snippet | Purpose |
|---|---|
| Python (xmlrpc) | Server-to-server integration using xmlrpc.client |
| JavaScript (fetch) | Browser/Node.js integration using fetch API |
| cURL | Command-line testing with curl |
search_read, create, write, unlink.JSON-RPC Endpoint
All Odoo JSON-RPC calls go to:
POST /web/dataset/call_kw
With body: {"model":"…","method":"search_read","args":[…],"kwargs":{…}}