How to Use a Transient Model in Odoo

Transient model is a model in odoo where its data will be deleted periodically. Since the data will always be deleted, of course it is not recommended to use this model to store the real data. Usually, this model is used as a wizard or as a connector between several models, for example to show options when creating an invoice from a sale order. Or to provide certain messages to the user.

As an example, in this tutorial we will use the transient model to give a message to the user if the customer’s status is blacklisted when we confirm a sale order. Then, if the user have Approve sale orders of blacklisted customers access right, the sale order can be continued to the next process by clicking the Approve button. If we do not have the access right, of course the sale order cannot be confirmed and cannot be processed.

First, let’s create the access rights. Create a security.xml file with the following contents.

<?xml version="1.0" encoding="utf-8"?>
<odoo>
	<data>

	    <record id="group_edit_partner_trust_state" model="res.groups">
	        <field name="name">Edit partner trust state</field>
	    </record>

	    <record id="group_approve_sale_order_of_blacklisted_customer" model="res.groups">
	        <field name="name">Approve sale order of blacklisted customer</field>
	    </record>

	</data>
</odoo>

Don’t forget to import the file into the __manifest__.py file. If we succesfully install our module there will be 2 additional access rights as shown below.

Then make a selection field on the master partner to store the partner’s status.

# -*- coding: utf-8 -*-

from odoo import api, fields, models, _

class Partner(models.Model):
    _inherit = 'res.partner'

    trust_state = fields.Selection([
            ('trusted', 'Trusted'),
            ('blacklisted', 'Blacklisted')
        ], string='Trust Status', default='trusted')

Then add those field to the master partner form.

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>
        <record model="ir.ui.view" id="view_partner_form_inherit">
            <field name="name">view_partner_form_inherit</field>
            <field name="model">res.partner</field>
            <field name="inherit_id" ref="base.view_partner_form" />
            <field name="arch" type="xml">
                <field name="category_id" position="after">
                    <field name="trust_state" groups="partner_trust.group_edit_partner_trust_state" />
                </field>
            </field>
        </record>
    </data>
</odoo>

From the code above, we configure that the Trust Status field can only be seen and edited by users that have Edit partner trust state access right. Next, create a Transient Model as a wizard.

# -*- coding: utf-8 -*-

from odoo import api, fields, models, _

class ConfirmSaleTrust(models.TransientModel):
    _name = 'sale.trust.wizard'

    order_id = fields.Many2one('sale.order', string='Sale Order')
    partner_id = fields.Many2one('res.partner', string='Customer', related='order_id.partner_id')

    def approve_blacklisted_customer_order(self):
        # call the action_confirm method on the current sale order
        # include a context to prevent an infinite loop
        self.order_id.with_context({'approve_blacklisted_customer_order': 1}).action_confirm()

Then create its form.

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>
        <record model="ir.ui.view" id="sale_trust_wizard_form">
            <field name="name">sale_trust_wizard_form</field>
            <field name="model">sale.trust.wizard</field>
            <field name="arch" type="xml">
                <form>
                    <field name="partner_id" /> is blaclisted. Are you sure want to approve this Sale Order ?
                    
                    <footer>
                        <button name="approve_blacklisted_customer_order" type="object" string="Approve" class="btn-primary" groups="partner_trust.group_approve_sale_order_of_blacklisted_customer" />
                        <button string="Cancel" special="cancel" />
                    </footer>
                </form>                
            </field>
        </record>
    </data>
</odoo>

In odoo 14, you must create an ir.model.access.csv file to create the access rule, to indicate which user that have permission to create the transient model and not. But in odoo 13 and earlier version this is not necessary. This is an example of how to create the rule, to give permission to all user to create a transient model.

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_trust_wizard_user,sale.trust.wizard.user,partner_trust.model_sale_trust_wizard,,1,1,1,1

Next, override the action_confirm method in the sale.order model to display the form of the sale.trust.wizard transient model above if the customer is blacklisted.

# -*- coding: utf-8 -*-

from odoo import api, fields, models, _

class SaleOrder(models.Model):
    _inherit = 'sale.order'

    def action_confirm(self):
        # If partner is not blacklisted, call super, it means that the sale order can be confirmed

        # if there is a context approve_blacklisted_customer_order it means that this method
        # is called from the sale.trust.wizard transient model and the user have
        # 'Approve sale order of blacklisted customer' access rights, so the sale order can be confirmed
        if self.partner_id.trust_state == 'trusted' or 'approve_blacklisted_customer_order' in self._context:
            super(SaleOrder, self).action_confirm()

        else:
            # partners is blacklisted
            # create transient model record with the value of order_id is the current sale order id
            # by inserting the order_id field, later in the sale.trust.wizard transient model
            # we can call the action_confirm method to confirm this sales order
            wizard_id = self.env['sale.trust.wizard'].create({'order_id': self.id})
            return {
                'name': _('Blacklisted Customer'),
                'type': 'ir.actions.act_window',
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': 'sale.trust.wizard',
                'res_id': wizard_id.id,
                'target': 'new',
            }

For example, this customer has been blacklisted. Make sure you have the Edit partner trust state access right to view and edit the Trust Status field.

If user don’t have the Approve sale order of blacklisted customer access right, when user clicks the Confirm button on the sale order, a message will appear as shown below.

If user have the Approve sale order of blacklisted customer access right, when user clicks the Confirm button on the sale order, a message will appear as shown below.

You can see an approve button has been added. When user clicks the button, the approve_blacklisted_customer_order method on the sale.trust.wizard transient model will be executed. Finally, the sale order can be confirmed.

Each time we click the Confirm button on the sale order and the customer is blacklisted, the count of the sale.trust.wizard record will increase.

The question is, when the transient model record will be deleted ?

Odoo has a scheduller / cron job to delete transient model record automatically. In debug mode enter the Settings >> Technical >> Automation >> Scheduled Actions menu, then take a look at Base: Auto-vacuum internal data.

In the picture above, it can be seen that the transient model record will be deleted once a day. If you want to delete the transient model record right now, click the Run Manually button. The picture below is a record of the sale.trust.wizard transient model after clicking the Run Manually button.

It can be seen that not all records of the sale.trust.wizard transient model are deleted. There are 2 conditions to declare a transient model record is deleted or not when Base: Auto-vacuum internal data scheduller / cron job is executed. Please open the tools/config.pyfile in the odoo source code. Take a look at the 2 configurations below.

group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
                 help="Force a limit on the maximum number of records kept in the virtual "
                      "osv_memory tables. The default is False, which means no count-based limit.",
                 type="int")
group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
                 help="Force a limit on the maximum age of records kept in the virtual "
                      "osv_memory tables. This is a decimal value expressed in hours, "
                      "and the default is 1 hour.",
                 type="float")

From the code above, it can be seen that the transient model record will be deleted if it is more than 1 hour old when the Base: Auto-vacuum internal data scheduler is executed. The record’s age is calculated from the write_date fields of each record. We can change this configuration directly in the tools/config.py file or in the configuration — /etc/odoo/odoo.conf — file.

This tutorial is made on odoo 13 enterprise edition. There may be differences in appearance and process with other versions of odoo. Download the source code here.

Related Article

Leave a Reply

Your email address will not be published.