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

In the older versions of Odoo, before 16, there is “Edit” button, on click of which the view is opened in edit mode. Once the user is finished with the changes on click of “Save” the changes are stored in the database. That makes clear when the user is in Edit mode and when the changes are saved.

In newer versions of Odoo the “Edit” button does not exist anymore. The edit can be done by simply clicking on the field’s input area. This approach has positive sides, such as eliminating a few clicks for the user and doing the edits on the fly. However, it is a bit more challenging for the users to understand how it is working, especially for new users.

Here are a few general rules that can help in understanding how the Edit works in the newer versions of Odoo:

  • Any editable field on hover has the bottom border line of the input area highlighted
  • The field is getting in edit mode by clicking on it’s input area
  • When the field is edited it is not automatically saved.
  • There are three ways how the updated values are saved:
    • The user can manually save them by clicking on the button “Save”. The button is represented with an icon and text shown on hover
    • The user clicks on any workflow button in the view, for example “Confirm” button in the sales order
    • The user exits the view. Even when the user closes the window or the browser if there is content that is not saved will be stored in the database. One exception from this rule is when the changes are done in a private window which is closed without saving the changes with any of the previous two steps.

Removing the edit button makes it easier to update changes on the fly, yet it can be confusing sometimes if the changes are saved or not because the user does not have any indication for that, except for very small icons, Save / Discard, at the top of the screen. So in case if a user works in a multi-window setup, they may be surprised why they don’t see the changes in all windows.
However, even though this way of edit may increase the learning curve for the new users, in the long run makes the interaction with the system more convenient.

Functional Odoo 17 Odoo 18
Posts

In Odoo can be found multiple different methods for filtering and searching records. Each of them is designed with their own uniqueness which makes it more useful in certain cases than others, even though sometimes it may be confusing which one is the right to use. In this post we will go through some of the most used lookup methods and focus on when each of them should be used.

We will cover the following methods:

  • browse
  • search
  • filtered
  • filtered_domain

Browse

The method browse​returns a recordset for the provided IDs​ in the current environment. In case the method is called without any ID​ it will return an empty recordset. This method creates recordset for the IDs without fetching the actual data immediately from the database. It is useful to use when list of IDs need to be converted into recordset.

Search

The search​ method can accept multiple arguments:

  • domain​: A list of domains to be searched by. An empty list will return all records
  • offset​: Number of results to ignore. Usually used during page pagination
  • limit​: Maximum number of results to return
  • order​: Specified order of records

This method based on the provided arguments creates a search query that is executed in the database and the result from it are stored in memory.

The return from this method is recordset as well. The difference between this recordset and the one from the browse​ method is that this method is doing immediate query to the database and retrieves the data for the selected records, which means when a field is accessed, the value is taken from the memory instead of doing read in the database. The search method has a few additional optimization methods such as search_count​, search_read​ and search_fetch​.

Filtered

The filtered​ ​method as an argument accepts a function or a dot-separated sequence of field names. This method filters records on an already existing recordset, which is one of the differences compared to the previous two methods. The filtering in this method is done on python level and does not use database queries. 

Filtered Domain

Similarly to the filtered​ method, the filtering is done on the existing recordset. The difference is that in this case the argument is a domain, the same format as the one for search​, instead of a function. Depending on the complexity of the domain, the method may execute an actual search to do the proper filtering. However in most of the cases it filters the records in python by evaluating the domain. This is good in case the recordset is already fetched from the database and it is needed some additional filtering which is easier to express in a domain format.

Conclusion

To sum up, the browse​ method is used when there is list of IDs​ but we need a recordset for more convenient handling in the rest of the codebase. 
In case we have some parameters on base of which we need to query the database and get recordset then search​ method is the one that should be used. 
However, if we already have the recordset and need to do some additional filtering on it the filtered​ and filtered_domain​ methods are the right choice. Which one of these methods needs to be used mostly depends on the conditions that need to apply. In case of a simpler condition filtered would be more convenient to use but in case the conditions are more complex can be easier to translate them in a domain format and use filtered_domain​.

These suggestions are based on the assumption that the amount of data handling is moderate. In case of complex domains and bigger amounts of data may need to be done additional evaluation which method would perform the best for such case.


Odoo 17 Odoo 18 Technical
Posts

Sale Financial Risk module extends the Account Financial Risk by providing control of the customer’s credit limit in the sales order. This allows the system to include the financial risk check at the initial phase of the sales process and prevent a quote from being confirmed if the credit limit for the customer is exceeded.

Once the module is installed in the partner’s "Financial Risk" tab can be found two additional fields: “Include Sales Orders” in the General Limits and “Limit Sales Orders” in the Specific Limits.

Financial Risk Tab

When the “Include Sales Orders” is selected, the Sales Orders risk amount will be included in the customer’s Total Risk. The sales order risk amount is a sum of the sales orders’ total amount which is not invoiced. On quote confirmation, the system will check if the total risk exceeds the total credit limit specified for that customer. In case it is exceeded a warning message will be shown.

Additionally, it is possible to specify a specific limit for the sales orders in the field “Limit Sales Orders”. If the field is set, on quote confirmation it will be checked if the specific limit exceeds the total of the quote’s total amount and sales orders’ risk amount. If it does a warning message will be shown with info that the sales orders limit is exceeded.

Furthermore, in the Accounting / Invoicing Configuration Settings can be found the option “Include orders in done state”. This option extends the range of orders used in the financial calculations, not only to orders with the state “Sale Order” but it adds the orders with the state “Locked” as well. This option can be very useful for business cases where the "Lock Sales Order" option is used. 

The warning message that is shown on quote confirmation, when any of the specified credit limits exceeds, can be overpassed by the Financial Risk Manager user. However, for other users, the confirmation will be blocked while the credit limit issue is not resolved.

Similarly to other Financial Risk limits, this module provides a convenient way of overviewing the financial data that contributes to the sales order financial risk.
By clicking on the total sale order risk amount will open the “Financial Risk Information” view where are listed all commercial partners related to the customer, that have an impact on the total risk amount, with information about their risk amounts and quantities. On click of the risk amount on any of the lines will open a list view with all sales order lines which are part of the calculation.

This module is a great addition to the Account Financial Risk module making the financial risk management for sales orders more convenient to implement by covering a wider range of business cases.

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

Accounting Functional Sales
Modules Review

Many2many and One2many fields in Odoo require a specific syntax for manipulating related data. The instructions mainly consist of 3 elements added in a tuple, where each of the elements has a special meaning.

The first element is an integer number ranging from 0 to 6. each corresponding to a different action. The associated actions for each numerical value are:

  • 0: Create
  • 1: Update
  • 2: Delete
  • 3: Unlink
  • 4: Link
  • 5: Clear
  • 6: Set

The second element for Update​, Delete​ and Unlink​ is the ID​ of the record to which the action applies while for the rest of the actions: Create​, Clear​ and Set​ this element is with value 0​.

The third element for the first two actions (Create​ and Update​) is the values​ used to create or update the record. For the next four, this element can be left out. The last action Set​, requires a list of IDs​ as a third element. The IDs​ represent the related records. This command replaces the existing relations with the newly supplied list of records’ IDs​.

In addition, is the complete tuple for each action:

  • Create: (0, 0, values)
  • Update: (1, ID, values)
  • Delete: (2, ID)
  • Unlink: (3, ID)
  • Link: (4, ID)
  • Clear: (5)
  • Set: (6, 0, IDs)

Knowing the logic for the elements in the tuple makes it much easier to understand how to create any action without memorizing the list of tuples. However, from version 15, Odoo made this even easier. Now there is a class helper Command​ that is creating the whole tuple. 

The commands for each action are listed below:

  • Create:  Command.create(values)
  • Update: Command.update(ID, values)
  • Delete:  Command.delete(ID)
  • Unlink: Command.unlink(ID)
  • Link:  Command.link(ID)
  • Clear: Command.clear()
  • Set: Command.set(IDs)

Where values​ represent a dictionary of values, ID​ is an integer value of the record’s id in the database and IDs​ is a list of related records ids in the database.

As an example, we will try to modify the tags related to a sales order. Initially, the sales order has only one tag added to it. That tag is with ID1​.

Create:

If the requirement is to add another tag to the sales order which is not presented in the database needs to be used the create​ command:

sale.write({tag_ids: [Command.create({"name": "Tag 2"})]

this will create a new tag and will add it to the order.

Update:

In case the name of the newly added tag needs to be updated the update will look like this:

sale.write({tag_ids: [Command.update(2, {"name": "Updated Tag 2"})]      
Delete:

If the new tag is not needed anymore and needs to be removed from the relation with the sale order but at the same time from the database as well should be used the delete​ command.

sale.write({tag_ids: [Command.delete(2)]
Unlink:

If the requirement was only to remove the tag from the sales order but still to be available in the database then the command unlink​would be the right one.

sale.write({tag_ids: [Command.unlink(2)]        
Link:

If the tag with ID 3 that already exists in the database needs to be added to the sale order, then link​ is the correct command to use.

sale.write({tag_ids: [Command.link(3)]
Clear:

In case the tags are not needed in the order and need to be removed from it, instead of going over the whole list and using unlink​ it can be done at once with the clear​ command.

sale.clear({tag_ids: [Command.clear()]
Set:

The last case is when the current tags, with ID1​ and 3​, which are already added to the order need to be replaced with tags, with ID4​ and 5​, which are in the database but not related to the order.

sale.write({tag_ids: [Command.set([4, 5])]

The end result after this command will be sales order with tags with IDs4​ and 5​.

Odoo 17 Odoo 18 Technical
Posts

In the post The importance of correctly setting priority on inherited views we have already discussed the importance of the priority​ field in the inherited views and why it needs to have the right value. 

However, in some cases, it may be needed to update the priority of an existing view to meet certain requirements. The simplest case for that can be a request to change the primary view for a model that has multiple views from the same type.

The code sample below presents a way to modify the priority for an existing view from the codebase. In the particular example, the priority of the  view_order_tree​ view in the sale module is updated to value 5​. 


<function model="ir.ui.view" name="write" eval="[ref('sale.view_order_tree')], {'priority': 5}"/>

That would mean that in the system the list view with the biggest priority for the sale.order​ model will be view_quotation_tree​, which has a priority of 4​, and that one will be used always when the list view is not explicitly specified in the action.

Odoo 17 Odoo 18 Technical
Tips

In the native version of Odoo, it is possible to set a Credit Limit per customer, however, it is limited only to setting the Credit Limit amount and showing Informative messages on the Sales Order and Invoice in case the limit is exceeded for the selected customer. 

This functionality is not sufficient in many cases and a more comprehensive solution is needed. Functionality that will provide more control over the financial risk but at the same time flexibility in setting up and detailed overview of it. The module that allows all these is the Account Financial Risk module.

Security

From a security perspective, the module has two access groups User and Manager. Even though the User group is specified as read-only access to the Financial Risk data we couldn’t find integration of it in the codebase in version 16 at the time of reviewing the module.
The Manager group, on the other side, provides full access to the Financial Risk functionality. Additionally, the users with this group can overpass partner risk exceptions during Invoice confirmation. However, this group is not enough to see the tab Financial Risk in the Customer form view. That means to have full access to the features from this module it is required to have both Financial Risk Manager and Billing Administrator/Accountant groups. The accounting group is controlling the view of the Financial Risk tab.

Financial Risk Tab

Once the module is installed in the system the tab Financial Risk can be found in the Partner form view. The Tab contains a complete overview of the financial risk for the particular customer.

Financial Risk Tab

On the left side can be found the General Limits section which contains the type of credit limits that can be included in the Customer’s Financial Risk, together with the current amount for each of them. The available types of credit limits are:

  • Draft Invoices
  • Open Invoices/Principal Balance
  • Unpaid Invoices/Principal Balance
  • Other Account Open Amount
  • Other Account Unpaid Amount

Unpaid Invoices/Principal Balance type by default includes move lines with account same as the customer’s receivable account which are not reconciled and their due date is passed.
However, the module introduces an option of specifying additional margin before a move line is considered as Unpaid. This configuration is per company and can be found on the accounting configuration page under the name Maturity Margin. The value specified for this setting represents the number of additional days before a move line is considered unpaid.
"Other Account" limit types are move lines with account different than the customer’s receivable account which are with not past date or passed respectively. The Maturity Margin is taken into consideration for the Other Account Unpaid Amount as well.
At the bottom of the General Limits section, there is info about the Risk Remaining and the Total Risk for the customer, which is a sum of all included limits for that customer.

On the right side, can be found the Specific Limits section. In this section can be specified different limits for each type of credit limit.

Additionally, in the Info section it is possible to set a general credit limit per Customer. This limit is overpassed when the total amount that the customer owns from the selected General Limits is bigger than the set credit limit. Furthermore next to the Credit Limit is available the date when it was last updated.
Also in this section can be set the Credit Limit Currency. The available options for it are the following:

  • Company Currency
  • Receivable Currency
  • Pricelist Currency
  • Manual Credit Currency.

In case of selecting the Manual Credit Currency, an additional currency field is shown where the needed currency can be selected. Another available field in this section is Credit Policy, which is only informative and is not used anywhere in the system.

Invoice Confirmation

In case a customer exceeds a certain limit on confirmation of an Invoice “Partner risk exceeded” modal is shown. If the user has the Financial Risk Manager group there is an option in the modal to overpass the Risk Exception and continue with the Confirmation. Otherwise, the Confirmation will be blocked until the Financial Risk Exception for the customer is resolved.
Additionally for cases where this type of limitation is not needed there is a setting that allows invoices to be confirmed for customers with exceeded credit limits by all users that have access rights to confirm invoices. This feature can be enabled by selecting the option “Allow invoice validation over the risk” on the invoicing/accounting configuration page.

Financial Data Overview

The module allows a simple way of overviewing the financial data that is contributing to the current financial risk calculation for a customer. By clicking on the number next to each type of general limit will open the “Financial Risk Information” view, from which can be opened the list of Account Move Lines that are contributing to the Financial Risk for each account. This view can be opened by clicking on the number next to the name of the account.

This module is a great addition to the existing accounting features in Odoo. However, It is only related to the accounting part of the system and will not limit any action related to the Sales Order. In case such a feature is needed, a great addition to this module is the Sale Financial Risk module.

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

In addition is a short video with the module’s features.



Accounting Functional
Modules Review