File: ir_default.py

package info (click to toggle)
oca-core 11.0.20180730-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 509,684 kB
  • sloc: xml: 258,806; python: 164,081; sql: 217; sh: 92; makefile: 16
file content (170 lines) | stat: -rw-r--r-- 7,441 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import json

from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError


class IrDefault(models.Model):
    """ User-defined default values for fields. """
    _name = 'ir.default'
    _rec_name = 'field_id'

    field_id = fields.Many2one('ir.model.fields', string="Field", required=True,
                               ondelete='cascade', index=True)
    user_id = fields.Many2one('res.users', string='User', ondelete='cascade', index=True,
                              help="If set, action binding only applies for this user.")
    company_id = fields.Many2one('res.company', string='Company', ondelete='cascade', index=True,
                                 help="If set, action binding only applies for this company")
    condition = fields.Char('Condition', help="If set, applies the default upon condition.")
    json_value = fields.Char('Default Value (JSON format)', required=True)

    @api.model
    def create(self, vals):
        self.clear_caches()
        return super(IrDefault, self).create(vals)

    @api.multi
    def write(self, vals):
        if self:
            self.clear_caches()
        return super(IrDefault, self).write(vals)

    @api.multi
    def unlink(self):
        if self:
            self.clear_caches()
        return super(IrDefault, self).unlink()

    @api.model
    def set(self, model_name, field_name, value, user_id=False, company_id=False, condition=False):
        """ Defines a default value for the given field. Any entry for the same
            scope (field, user, company) will be replaced. The value is encoded
            in JSON to be stored to the database.

            :param user_id: may be ``False`` for all users, ``True`` for the
                            current user, or any user id
            :param company_id: may be ``False`` for all companies, ``True`` for
                               the current user's company, or any company id
            :param condition: optional condition that restricts the
                              applicability of the default value; this is an
                              opaque string, but the client typically uses
                              single-field conditions in the form ``'key=val'``.
        """
        if user_id is True:
            user_id = self.env.uid
        if company_id is True:
            company_id = self.env.user.company_id.id

        # check consistency of model_name, field_name, and value
        try:
            model = self.env[model_name]
            field = model._fields[field_name]
            field.convert_to_cache(value, model)
            json_value = json.dumps(value, ensure_ascii=False)
        except KeyError:
            raise ValidationError(_("Invalid field %s.%s") % (model_name, field_name))
        except Exception:
            raise ValidationError(_("Invalid value for %s.%s: %s") % (model_name, field_name, value))

        # update existing default for the same scope, or create one
        field = self.env['ir.model.fields']._get(model_name, field_name)
        default = self.search([
            ('field_id', '=', field.id),
            ('user_id', '=', user_id),
            ('company_id', '=', company_id),
            ('condition', '=', condition),
        ])
        if default:
            default.write({'json_value': json_value})
        else:
            self.create({
                'field_id': field.id,
                'user_id': user_id,
                'company_id': company_id,
                'condition': condition,
                'json_value': json_value,
            })
        return True

    @api.model
    def get(self, model_name, field_name, user_id=False, company_id=False, condition=False):
        """ Return the default value for the given field, user and company, or
            ``None`` if no default is available.

            :param user_id: may be ``False`` for all users, ``True`` for the
                            current user, or any user id
            :param company_id: may be ``False`` for all companies, ``True`` for
                               the current user's company, or any company id
            :param condition: optional condition that restricts the
                              applicability of the default value; this is an
                              opaque string, but the client typically uses
                              single-field conditions in the form ``'key=val'``.
        """
        if user_id is True:
            user_id = self.env.uid
        if company_id is True:
            company_id = self.env.user.company_id.id

        field = self.env['ir.model.fields']._get(model_name, field_name)
        default = self.search([
            ('field_id', '=', field.id),
            ('user_id', '=', user_id),
            ('company_id', '=', company_id),
            ('condition', '=', condition),
        ], limit=1)
        return json.loads(default.json_value) if default else None

    @api.model
    @tools.ormcache('self.env.uid', 'model_name', 'condition')
    # Note about ormcache invalidation: it is not needed when deleting a field,
    # a user, or a company, as the corresponding defaults will no longer be
    # requested. It must only be done when a user's company is modified.
    def get_model_defaults(self, model_name, condition=False):
        """ Return the available default values for the given model (for the
            current user), as a dict mapping field names to values.
        """
        cr = self.env.cr
        query = """ SELECT f.name, d.json_value FROM ir_default d
                    JOIN ir_model_fields f ON d.field_id=f.id
                    JOIN res_users u ON u.id=%s
                    WHERE f.model=%s
                        AND (d.user_id IS NULL OR d.user_id=u.id)
                        AND (d.company_id IS NULL OR d.company_id=u.company_id)
                        AND {}
                    ORDER BY d.user_id, d.company_id, d.id
                """
        params = [self.env.uid, model_name]
        if condition:
            query = query.format("d.condition=%s")
            params.append(condition)
        else:
            query = query.format("d.condition IS NULL")
        cr.execute(query, params)
        result = {}
        for row in cr.fetchall():
            # keep the highest priority default for each field
            if row[0] not in result:
                result[row[0]] = json.loads(row[1])
        return result

    @api.model
    def discard_records(self, records):
        """ Discard all the defaults of many2one fields using any of the given
            records.
        """
        json_vals = [json.dumps(id) for id in records.ids]
        domain = [('field_id.ttype', '=', 'many2one'),
                  ('field_id.relation', '=', records._name),
                  ('json_value', 'in', json_vals)]
        return self.search(domain).unlink()

    @api.model
    def discard_values(self, model_name, field_name, values):
        """ Discard all the defaults for any of the given values. """
        field = self.env['ir.model.fields']._get(model_name, field_name)
        json_vals = [json.dumps(value, ensure_ascii=False) for value in values]
        domain = [('field_id', '=', field.id), ('json_value', 'in', json_vals)]
        return self.search(domain).unlink()