In certain business scenarios, it is required to maintain audit logs on actions affecting the data in the system such as create​, read​, update​, and delete​ (CRUD).

Although the native version of Odoo does not support such functionality yet, there is a module maintained by OCA that covers these types of operations.

In this review, we will go through the options that the “Audit Log” module offers.

The latest available version of the module at the moment of writing is 17.0.1.0.0. Once the addon is installed in the system the related sub-menus can be found in the “Technical” menu.

There are five available sub-menus:

  • Rules
  • Logs
  • Log Lines
  • User Sessions
  • HTTPS Requests

Only the content in the first sub-menu, “Rules”, is customizable. While the rest of them are read-only list views that contain the different types of logs. The type of logs that each of them contains is closely related to their names.

For the module to start logging data, it is required to create a rule. Each rule is defined per model, which means that every model in the system will need a separate rule in order to log data. Except for the model name that needs to be selected, a rule has the following additional fields:

  • Name: The name of the rule, for example, if the logging is for sale.order​ model the name can be "Sales Order"
  • Type: Two available options “Full Log” which compares the data before and after the operation and logs both states, in this case, data from computed fields is logged as well, but because of the complexity of the operation, it can be a bit slower. On the other side the second option “Fast Log” logs only the new values for the changes made during the create​ and write​ operations, since this is much simpler it is also faster.
  • Capture Record: If selected, when the record is deleted the full info of the record will be added to the log, otherwise a simple log without any details for the record will be added. This option is possible only if the type “Full Log” is selected.
  • Action: This field is visible in debug mode, but even it is possible to edit it, on “Subscribe” action will be overridden by the “View Logs” action created for the rule.
  • Users to Exclude: Can be specified multiple users that need to be excluded from the logs
  • Fields to Exclude: Can be specified list of fields that need to be excluded from the logs
  • Log Reads: If selected, every time a user sees the model’s data a log will be created, by default is not selected
  • Log Writes: If selected, every time a user updates a record related to the model a log will be created
  • Log Deletes: If selected, when a user deletes a record related to the model a log will be created
  • Log Creates: If selected, when a user creates a new record related to the model a log will be created

The last three options are by default selected on the creation of a new rule.

After creating the rule, the next step is subscribing to it by using the "Subscribe" button. Once that is done, the system will start logging data according to the rule configuration.
The rule is editable only in the “Draft” state so in case if anything needs to be changed needs to be moved back to “Draft” using the “Unsubscribe” button.

Audit Log Rule Form View

From a security perspective, the module has two groups:

  • Auditlog User
  • Auditlog Manager

The first one allows users to see the logs from the record using the action “View Log”, which can be found in the record’s action menu. The users with this group have only read access to the logs.

The “Auditlog Manager” group adds full access rights to the related audit log models, however, a user can have access to the Audit log menus only if they have the “Administration Settings” group.

The logs are by default kept in the system for 6 months, but it is possible to change that in the "Auto-vacuum audit logs” scheduled action that can be found in Settings > Technical > Automation > Scheduled Actions​.

 Audit Log Auto-vacuum

The number 180​​ in the method model.autovacum(180)​​ is number of days that the logs need to be kept, by updating this number the period that the logs are kept in the system will be changed. 
Also, as a second parameter to this method, it is possible to add the number of logs that can be deleted per run, for each of the related models where the logs are stored. This parameter can be helpful in cases when there are too many logs that need to be removed at once. Because of this, Odoo can fail the task and not remove any of them. As a result of that the system can accumulate a large amount of logs, which can increase the size of the database and also slow down the system.
Therefore our recommendation is to ensure that the scheduled action is successfully running and it is set to remove a limited number of logs per run in shorter intervals. Finally should be tested manually if the action is working as expected once the settings are completed.
The "Auto-vacuum audit logs” is not enabled by default, therefore once the module is installed it needs to be enabled.

This module is a great addition to systems that require such functionality. Also, it is regularly maintained by the Odoo Community Association (OCA) which can assure that from a technical perspective is well build module with appropriate quality assurance checks. 

Functional
Modules Review


In some cases, there is a need to override the create​ method in Odoo and add additional logic to it. 

When overriding this method, the custom logic is triggered on each iteration of creating a new object.

 This means whenever a new object is created the extra code will be evaluated and applied.

In case when the additional logic is closely related to the object's data, this approach can be all right, since the codebase looks simpler and more clear.

However, in situations where some of the custom logic is repeating, especially when using database data, the logic will be executed n-times for n-objects, without any need for that.

In such cases should be used the decorator model_create_multi​. Adding this decorator to the create​ method allows the repeating code to be executed only once and applied to each object using a for​ loop.

It is important to mention that when using the model_create_multi​ decorator, the method receives a list of values while returns a list of newly created objects.

A very simple case to understand the need for this decorator is shown below:


@api.model_create_multi
def create(self, vals_list):
    round_precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
    for vals in vals_list:
		    vals["quantity"] = float_round(vals["quantity"], precision_digits=round_precision)
    return super().create(vals_list)
        

Without using the decorator the logic related to getting the value for round_precision​ will be executed every time a new object is created, which means for a batch of n-objects will be executed n-times.

But in our example, it will be executed only once and applied to all object values using the for​ loop.

Technical
Tips

When using many2many_tags​ widget, it is possible to define a background color for each of the tag values. For that to work the following steps need to be done:

  • The related model needs to have an Integer type field where the color index will be stored, usually named color​.
  • The color​ field needs to be added in the view using the widget color_picker
  • In the definition of the relational field in the XML view, when using the widget many2many_tags​, it needs to be set the option color_field​ with value the name of the color field previously defined.​
  • The last step is optional, by default when a user selects a value, can edit the background color of the value's tag, however, it is possible to make the color not editable at this stage, by using the option no_edit_color

Let’s review the steps above using as an example the relational many2many​ field tag_ids in the sale.order​ model related to the relational model crm.tag​​.

The model crm.tag​ there is a field color​ defined in the model definition as the following:

color = fields.Integer("Color")

The definition of the field in the XML view, using the widget color_picker​ looks like the following:

<field name="color" required="True" widget="color_picker"/>

Having this definition of the field makes it possible for every sales tag value defined in the system to be selected a color which later will be used as a background color for the tag.

Once the field is set up in the relational model the next step is when defining the related field tag_ids​ in the XML views to use the widget many2many_tags​ and add as an option the color_field​.

<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>

This will add a background color for the selected tag values in the sales order. Also users will be able to change the color of the tags during the selection process. A possible issue with this change is that the color will be applied to all sales orders where the same value is used. In case the admin wants to keep the colors same as initially set, it is possible to disable the edit of the color at the moment of selection by using the option no_edit_color​ set to True​.

<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color', 'no_edit_color': true}"/>
Odoo 17 Odoo 18 Technical
Posts


In Odoo it is really convenient to change the price of multiple products using the pricelist option. In this post we will go through the steps that are needed to make that happen.

At first would be need to enable the option “Advanced Price Rules” in the Sales configuration page, which can be found in Sales >> Configuration >> Settings

Once that is done, open the pricelist page from Sales >> Products >> Pricelists​ and select an existing pricelist or create a new one.

The pricelist can be specific per company, country group or website. In our case we will assume that the pricelist is global for all products without any restriction.

In the “Price Rules” tab click on the button “Add a line” which will open a wizard as the one on the image:


Select the Computation​ method “DIscount” and in the Discount​ field set the value by which the price should be changed.

The important thing when setting this field is to take into consideration that if the price need to increase the value should be negative or in case of decrease it should be positive.

For example if the price need to increase for all products by 10% than the value in this field should be -10. In case the price for all products need to decrease by 10% the value should be 10.

For more complex pricing rules need to be used the option “Formula”, which still have the field Discount​ but additionally can be setup few more variables such as:

  • Base On​:  The price field based on which the calculation will be done (Sales Price or Cost Price)
  • Extra Fee​: Additional fixed fee added on the price calculated with the discount
  • Rounding Method​: Decimal number representing the minimum non-zero value at the desired precision.
  • Margins​: Minimum margin over the base price

The wizard with “Formula” options is shown in the image below:

 

The created pricelist can be assigned to a Customer, used in a Sales Order, or applied on the Website.

Functional Odoo 17 Odoo 18 Sales
Posts


When using a date field in the email templates it is important to use it with formatting function for the value to be represented in the format set in the language settings.

The function for formatting a date is format_date​ and by default is using the format specified in the language settings. However, it is possible to specify a custom one (using LDML format), if needed.

An example of adding a date in an email template is shown in the example below:

<p>Date Created: <t t-out="format_date(object.date_order) or ''"></t></p>

In case it is needed to display datetime​ or time​ values the functions available for them are: format_datetime​ and format_time​.


Functional Technical
Tips

In everyday business cases, there are a lot of situations where a particular field needs to have different attributes or attribute values based on different access groups.

This type of case is possible to handle in Odoo, however, there are two different solutions based on which version of Odoo is used.

In Odoo 15 and older versions, this can be achieved by adding a more generic definition of the field in the main view, while the one specific for certain groups is added in a new inherited view, which is limited only to the specific access group.

For example, a very simple case can be if the field phone​ in the Leads view by default needs to be readonly​, but only for group Sales Administrator is editable:

<record id="crm_lead_view_form" model="https://ir.ui.view">
<field name="name">https://crm.lead.form</field>
<field name="model">crm.lead</field>
<field name="arch" type="xml">
<form>
​​...
<field name='phone' readonly="True"/>
</form>
</field>
</record>

<record id="crm_lead_view_phone_form" model="https://ir.ui.view">
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_lead_view_form"/>
<field name="priority">99</field>
<field name="groups_id" eval="[(6, 0, [ref('sales_team.group_sale_manager')])]"/>
<field name="arch" type="xml">
<form>
<field name='phone' position="attributes">
<attribute name="readonly">False</attribute>
</field>
</form>
</field>
</record>

Starting from Odoo 16 this type of inherited view with an attached group to it is no longer supported, and will show a server error if used. The new approach now is by defining the same field twice in the view with different access group rules.

The same example with the phone​ field in Odoo 16 would look like the following:

<record id="crm_lead_view_form" model="ir.ui.view">
    <field name="name">crm.lead.form</field>
    <field name="model">crm.lead</field>
    <field name="arch" type="xml">
        <form>
            ...
            <field name='phone' groups="!sales_team.group_sale_manager" readonly="True"/>
            <field name='phone' groups="sales_team.group_sale_manager" readonly="False"/>
        </form>
    </field>
</record>

To avoid any confusion, this example does not intend to show the best solution on how to make a field editable only for certain access groups, but it is just used to demonstrate how to define the same field with different attributes for different groups.

Odoo 17 Odoo 18 Technical
Tips

The module Partner Stock Risk extends the Account Financial Risk module functionality by adding an extra financial risk check on validation of inventory transfers with "Customer" as a destination location.

Once the module is installed, the extra check is enabled without any additional settings. In order to disable the check the module needs to be uninstalled.

The stock financial risk validation is based only on the General Limits, which means at least one General Limit financial risk needs to be enabled for the check to work.

Specific Limits are not taken into consideration for this check, which is expected, knowing that there is no specific option for Stock in the financial risk Specific Limits section.

The module is a great addition to business processes where additional financial risk validation is needed before dispatching the products. For example, if the invoice is generated after the delivery this module will help in preventing products from being shipped in the first place for customers with financial risk issues.

The module is maintained by the Odoo Community Association (OCA).

Accounting Functional Inventory
Modules Review


A column in a list view can be hidden by using the attributes column_invisible​ or optional​.

The column_invisible​ attribute accepts a boolean value as True/False​ or 1/0​. Since this attribute is related to a column in a table that contains more than one object, it does not support condition-based value related to the tree view’s model. An example of using the attribute in a list view is shown below:


        <field name="name" column_invisible="True"/>

However, if the view is defined as an inline tree view in a form view, can be defined condition based on the value from the form view related object. 
For example, in the account journal entry view the “Tax Incl.” column is shown in the account move line list view only if the Rounding Method is not “Round Globally”.


    <record id="view_move_form" model="ir.ui.view">
        <field name="name">account.move.form</field>
        <field name="model">account.move</field>
        <field name="arch" type="xml">
            <form string="Account Entry" js_class="account_move_form">
            ...
            <field name="invoice_line_ids">
                <tree>
                    ...
                    <field name="price_total"
                    string="Tax incl."
                    column_invisible="parent.tax_calculation_rounding_method == 'round_globally'"
                    />              
                </tree>
            </field>
            </form>
        </field>
    </record>        

The optionl​ attribute supports two possible values show/hide​. In case of hide​ value the column is hidden from the list view by default, however, the user has the option to make it available by using the “Adjustments” option in the right upper corner of the table.

Additionally, It is possible both attributes to be used together:


    <record id="view_move_form" model="ir.ui.view">
        <field name="name">account.move.form</field>
        <field name="model">account.move</field>
        <field name="arch" type="xml">
            <form string="Account Entry" js_class="account_move_form">
            ...
            <field name="invoice_line_ids">
                <tree>
                    ...
                    <field name="price_total"
                    string="Tax incl."
                    column_invisible="parent.tax_calculation_rounding_method == 'round_globally'"
	                optional="hide"
                    />              
                </tree>
            </field>
            </form>
        </field>
    </record>
        

In such cases if the optional attribute is with value hide​ by default the column is not shown in the view. However, depending on the value that column_invisible​ attribute gets it can be available or not in the optional columns shown on click of “Adjustments” icon at the right upper corner of the table.

Odoo 17 Odoo 18 Technical
Tips