Automated Actions (and their close cousin, Server Actions) are a powerful way to add functionality to Odoo without any programming (or with only a few lines of Python code).
Also, if you are using Odoo Online you cannot do development or install third-party add-ons but you can use Automated Actions (and Server Actions).
Automated Actions can be used to enhance and customize Odoo functionality in several ways:
- Simple workflow:
- automatically change the “stage” of a document based on rules
- Create activities for users to follow up
- Automatically set values for fields based on rules, to simplify data entry and reduce errors.
- Validation to prevent users making mistakes in data entry (e.g. Prevent duplicate products being created)
Automated Actions can be triggered on Creation, Deletion or Update of a database record in the specified Odoo Model (database table), and can also be based on Form Modification or a Timed Condition.
Automated Actions can be conditioned on domains and / or “watched fields”, which makes it possible to define “business rules” (an action for one group of customers, or one type of product, etc.) and more complex logic can be added with Python code.
Actions
- Update fields on the selected model (database table)
- Set a fixed value (can be based on rules)
- Simple example: Set customer leadtime
- Example (needs Python): Set Routes for new products
- Derive a new value:
- Example (needs Python): Set income and expense accounts for a new product (derived from Product Category)
- Example (needs Python): Set company on contacts (Odoo 13) – and products
- Clear a field
- Set a fixed value (can be based on rules)
- Create new records on another table
- Example: Create analytic accounts for sales orders
- Example: Add contacts to mailing lists
- Send email (a useful feature, but email templates can be tricky to setup)
- Add followers (example on Odoo Help Forum)
- Create activities (so a user can follow-up)
- Validation (needs Python)
- Example: Workflow validation in CRM
- Example: Prevent duplicate product references
- Example: Stop users archiving products
- Customize document numbering (needs Python)
- Other Python functions
- Copy Records
- Example: Create a BOM for a new product
- Sort data
- Copy Records
- It’s also possible to execute multiple server actions (for the same Model)
Getting started
Start by enabling developer mode and navigating to Settings / Technical / Automated Actions.

Note: Every Automated Action is also a Server Action, which enables it to be added as a “contextual action” (example).
Installation
If you cannot find this option, you may need to install the module “Automated Action Rules”.
First remove the “Apps” filter by clicking on the ‘X’:

Then search for “Automated”

If it’s not installed, click on ‘INSTALL’.
Now you might want to try one of the examples (links above). Or if you want to understand more about Automated Actions, read on:
Automated Action

Model
Select the Model (database table)
Action To Do
- Execute Python Code (see below)
- Create
- Create a new record in a specified model (db table). You specify the fields in the ‘Data to Write‘ tab.
- Update the (current) Record
- Update the fields specified in the ‘Data to Write‘ tab.
- Execute several actions
- Trigger several other server actions
- Send Email
- Automatically send an email (using a template)
- Add Followers
- Create Next Activity
Trigger Condition
This defines when the Automated Action can be triggered:
- On Creation [
on_create
] - On Update [
on_write
] - On Creation & Update [
on_create_or_write
] - On Deletion [
on_unlink
] - Based on Form Modification [
on_change
] - Based on Timed Condition
Watched Fields
This was added in Odoo 13

This is optional, but if you specify any fields here the Automated Action will only be triggered if the value of these field(s) have changed.
Domains
For the first four trigger conditions, you can define the records to be selected using domains. Odoo will show you how many records meet the criteria and you can display a list of records to check whether your criteria is correct.
There are two domains:

- Before Update Domain (used for Updates only)
- Apply on
So it is possible to have “before” and “after” domains to trigger an action when a user makes a specific change (e.g. adding a salesperson to a customer – the field was blank before and now has a value).
Note that there are some limitations with domains if you create Automated Actions through the ‘front-end’ menu.
- There is a pop-up dialog box which makes it easier to enter domains, but at the cost of limiting functionality.
- This won’t apply if you create Automated Actions through code, or you could change the widget in the XML.
- Or you can use Python code to select records, as per this example.
Data to Write
The simplest way to update or write data is to select the fields from a list. This applies when the action is “Create” or “Update the Record”

Simple Python expressions can also be used

The first two lines are taking data from the record, the third line writes multiple (fixed) values to a many2many field, the last line takes data from the parent record. This is from the Add contacts to mailing lists example.
Python Code
Automated Actions can use a subset of Python. This is more powerful but requires a deeper understanding of Python. These are the commands that are available:
env
: Odoo Environment- Information about current user and company, as per this example
model
: Odoo Model (database table)record
: gives access to field values, e.g.record['company_id']
records
: allows multiple records to be read / updated, as per this exampletime
,datetime
,dateutil
,timezone
: to retrieve date and timelog(message, level='info')
: to record debug information in their.logging
tableWarning
: Display a Message (as per this example)action = {...}
- This allows you to call another Server Action for the same Model. The syntax is:
action = { "type": "ir.actions.server", "id": 372, }
- 372 is the ID of the Server Action
- This allows you to call another Server Action for the same Model. The syntax is:
But there’s no need to understand everything that can be done using Python. In fact, you can create Automated Actions without any Python at all.
Set customer lead time

- Model = product.template
- Trigger Condition = On Creation
- Apply On (domain) = Can be Sold
- Note that Odoo tells you that 94 records meet the criteria (and you can click here to view a list of those records)
- Action To Do = Update the Record
- Data to Write
- Field = Customer Lead Time
- Evaluation Type = Value
- Value = 10
So we can see that using “Update the Record” is quite simple and requires only a basic knowledge of the Odoo database (but user-defined defaults can also be used to do something similar).
However, using simple Python expressions allows us to go beyond what can be done with user-defined defaults. Here we can use values from the current record, from a parent (e.g. update Sales Order Line with information from the Sales Order Header), from related Models (db tables) and environment variables.
Other Examples
Create records in another table and add a link
This is quite powerful. It’s possible to create analytic accounts for sales orders, which replicates the standard functionality in Odoo to automatically create an Analytic Account for each project.
- This example also contains some information about how to overcome a bug in Automated Actions.
There will be other business cases where this could be a good technique.
Advanced Python stuff
As explained in the comments, it is possible to use advanced Python commands to update “child” records. This Cybrosys blog seems to have a good explanation: How to pass values to the child lines of one2many fields
Can I set an automated action to automatically confirm a production order when I create it?
I tried this Python code (I don´t know any python) but it doesn’t seem to do anything:
model.action_confirm()
What I need to do is to import several production orders in Confirmed stage so that it will trigger inventory rules. Would this be the better way to solve it?
LikeLike
I’m not so familiar with Production Orders, but in theory this Python code should set orders to confirmed status:
for rec in records:
record[‘state’] = “confirmed”
LikeLike
Hi Chris!
I got an error message with that code, but I finally got it right (nearly by chance) with a server action and this code:
records.action_confirm()
“action_confirm” refers to the method associated to the confirmation button on the production order form.
Thanks a lot for all the information and help, it was very usefull!!
LikeLike
Hi, do you know if the “automated action rules” app is free? Thx
LikeLike
Yes it’s free for both Community and Enterprise and available for Odoo online.
LikeLike
Hi Chris,
Can I set an automated action to display a message which does not disrupt the process workflow on a delivery order? I tried using the raise a warning option but this stops the workflow. I just want to display a message after the delivery order is done.
Thank you in Advance!
LikeLike
I am not aware of any way to display a warning message “inline”, but there are some options:
1. Add a message to the “chatter”, which means it will be visible to everyone,
2. Send a message to the user (or to a group),
3. Send an email to any specified email address (it’s probably best to create a group email address for this).
I will try to add information on these options later.
LikeLike
How to create internal reference for all the product variants individually.
Thanks
LikeLike
Maybe this will give you some ideas about how to do it: Set sequence numbers for sales orders
Variants can be identified by checking on product_template_attribute_value_ids (TRUE if it is a variant, FALSE if it’s the product template).
LikeLike
Hi, I noticed that for calculated fields, the trigger does not work.
Eg. If a customer has a variable membership product and I want to use automated action to trigger a reminder email to be sent out 30 days before the membership expires.
As a variable membership, the expiry date is calculated from the start date PLUS the membership duration.
When I create an automated action using the “Based on Timed Condition” for Trigger Condition, it does not trigger.
Is this a known limitation or am I just doing it wrong ?
Thank you.
LikeLike
Hi – quick question.
How would I go about to change a field in the product template model? I have a custom field called “x_new_field_PD13904344933904755”. I want to do a calculation when the selling price or the cost price is updated.
It works when I edit the record like this:
record.write({
‘x_new_field_PD13904344933904755’: record.list_price – record.standard_price
})
But – when you click the “update cost” link next to the standard_price field (the cost one), it pops up with a form where you add your new cost. That does NOT trigger the action.
What would you advise the best way would be?
Thanks in advance…
LikeLike
Hello
How can I prevent POS creation if no customer is selected on pos in odoo ?
i update on automated Actions and select a Point of Sale Orders on , and select a execute python code on and select a Based on Form Modification on and select payments(pos.order) on then add this python code :
if not record.partner_id:
raise Warning(‘Please check customer’)
LikeLike
ahmed mesallam:
Make the automated action triggered on creation instead of on form modification ..
then the pos order can’t be validated if the customer not set
LikeLike
Hi I have created an automated action to detect a change to a field on a contact record.
on update
field – fiscal position
exacute python code – records.message_post(body=”Fiscal position updated.”)
I would like to make this display what the field was updated to!
I tried records.message_post(body=”Fiscal position updated.” + record.property_account_position_id) but this just creates an error?
this was based on another online tut. do you have any recommendations for displaying the updated fiscal position field on a contact record?
Any help would be much apprecited.
LikeLike
I haven’t used Python code to send messages so I can’t offer any assistance on this.
LikeLike
Hi Chris, I have a quick question.
My base uses multi-company and when I create an item, the cost and taxes do not change for all of them.
Is it possible to write a script that applies the change to all companies?
Thanks in advance.
LikeLike
I’m not a Python programmer, but I think it should be possible.
LikeLike
Hi Chris,
I am using Odoo 14 (online Version) and I am trying to customize the Sales Order Sequence so that it also includes the Customer Number: Year-CustomerNumber-InvoiceNumber.
I did it up to the customer number using Odoo Studio.
How can I add the customer number (I set it under Company/Sales & Purchase/Misc/Reference) to the quotation/sales number sequence?
Thank you in advance!
LikeLike
Hi Chris,
I would like to trigger the creation of a delivery order for rental orders.
Is it possible to create a delivery order using an automated action with ‘create new record’?
If yes, how?
Thank you and best regards,
Sajad
LikeLike
It might be possible. I’m not very familiar with the Rental app.
LikeLike
How to copy items from Sale.Order (order_lines) to Stock.Move (move_ids_without_package) model?
LikeLike
That would probably be beyond the scope of an Automated Action!
LikeLike
did we ever find out?
LikeLike
I need to send the Promo Code (Buy 2 Get one Free – B123) to the customer via email.
Is there a way to configure Odoo to send the email automatically
LikeLike
You can setup an automated action to send an email using a template that has the code in it (you have to setup the promo code ahead of time anyway so you already know the code). You would use the domain in auto action to determine when to send. As an example, you can use when a user creates an account, so in the domain it would be created by public user with an on creation trigger. In that scenario when an anonymous user chooses to create an account, they get an email with a promo code.
LikeLike
I got a doubt. I want to create a new task per month, every time a sales order it’s crated(it is crated one per month). For example, I wanna create a task for March on 1st March. And another one for April, on April 1st. How can i do it?
LikeLike
Thanks Chris for sharing all these tips; love your blog as reference. My question – do you have any experience with the ‘based on timed condition’ automatic action? I have created an automatic action to evaluate the end date of (custom module) contract. It should put the contract in a stage ‘terminated’ if the end date > today. It works whenever I create contracts with an end date after today (eg. within ending within 2 minutes or tomorrow). It doesn’t appear to work if I create a new contract with an end date in the past.
So the basis question is, what am I missing here?
LikeLike
I haven’t managed to get that working. Maybe someone else can help!
LikeLike
Hello Chris,
I find your blog really helpful! Thank you for sharing all these tips!
I have 2 separate questions, perhaps you could help me with them, I have reached the odoo forum already with no success 😦
1. I want to create an automated action that automatically deletes the project’s partners once the project is moved to the “completed” stage. Via tudio I managed to get the trigger, domain, and scope of the action correctly (I tested it already by setting the action to send an eamil), what I can’t find online is help to set the actual resulting action (delete project partners, as they don’t need to be involved anymore). I don’t know coding or phyton, so far I managed to set all my automatic actions by “create a new activity” but this one is more complex than that. Could you please help me with this?
2. I need to create an a automated action that automatically moves a project to the “completed” stage, once ALL the project’s tasks are moved to the “completed” stage inside the project.
Thank you for all your help!
LikeLike
To delete any records, we usually use python code to do an unlink, after identifying the appropriate records. I don’t recall there are any standard activities that allow you to delete records.
LikeLike
Thank you! I ended up using a python expression as you mentioned!
Domain: [(“stage_id.name”, “ilike”, “completed”)]
Action: Update record
field: message.partners. ids
Value: [(5, 0, False)]
LikeLike
Yes, that’s a good technique. I’ll update the main article
LikeLike
Luckily I was able to solve question 1, by using a Python expresion (I leave it here in case someone needs it in the future, v. 16):
Domain: [(“stage_id.name”, “ilike”, “completed”)]
Action: Update record
field: message.partners. ids
Value: [(5, 0, False)]
LikeLike