Exemplo n.º 1
0
class InformalReference(models.Model):
    _name = 'informal.reference'

    table_name = fields.Char(
        'Table Name',
        size=128,
        required=True,
        help='Name of DB table where informal reference are.')
    id_field_name = fields.Char(
        'Id Field Name',
        size=128,
        required=True,
        default='res_id',
        help='Name of field where destination id are saved.')
    model_field_name = fields.Char(
        'Model Field Name',
        size=128,
        required=True,
        default='res_model',
        help='Name of field where destination model are saved.')
    model_field_value = fields.Selection(
        MODEL_FIELD_VALUE_SELECTION,
        'Model Field Value',
        required=True,
        default='0',
        help='How save destination model reference.')
Exemplo n.º 2
0
class Adjustment(models.TransientModel):
    _name = 'xopgi.unrealized_gl_adjustment'

    wizard = fields.Many2one('xopgi.unrealized_gl_wizard')
    account = fields.Many2one('account.account')

    account_name = fields.Char(related="account.name")
    account_code = fields.Char(related="account.code")
    account_currency = fields.Many2one(related="account.currency_id")

    foreign_balance = fields.Float(compute='_compute_all', default=0)
    balance = fields.Float(compute='_compute_all', default=0)
    adjusted_balance = fields.Float(compute='_compute_all', default=0)
    gainloss = fields.Float(compute='_compute_all', default=0)

    @api.depends('account', 'wizard')
    def _compute_all(self):
        precision = DecimalPrecision.precision_get('Account')
        company_currency = self.env.user.company_id.currency_id
        # Map records to accounts so that we can compute the balances in a single
        # DB query
        account_map = dict(zip(self.mapped('account.id'), self))
        assert len(account_map) == len(self)
        close_date = self[0].wizard.close_date
        tables, where_clause, where_params = AccountMoveLine.with_context(
            state='posted', date_to=close_date)._query_get()
        if not tables:
            tables = '"account_move_line"'
        if where_clause.strip():
            filters = [where_clause]
        else:
            filters = []
        filters.append('"account_move_line"."account_id" IN %s')
        where_params.append(tuple(account_map.keys()))
        query = ('''
            SELECT account_id AS id,
                   COALESCE(SUM(debit), 0) - COALESCE(SUM(credit), 0) AS balance,
                   COALESCE(SUM(amount_currency), 0) as foreign_balance
               FROM {tables}
               WHERE {filters}
               GROUP BY account_id
        ''').format(tables=tables, filters=' AND '.join(filters))
        self.env.cr.execute(query, where_params)
        for row in self.env.cr.dictfetchall():
            record = account_map.pop(int(
                row['id']))  # cast to int, otherwise KeyError
            account = record.account
            record.balance = balance = row['balance']
            record.foreign_balance = row['foreign_balance']
            record.adjusted_balance = adjusted = account.currency_id.with_context(
                date=close_date).compute(
                    record.foreign_balance,
                    company_currency,
                    round=False,
                )
            record.gainloss = round(adjusted - balance, precision)
        for record in account_map.values():
            record.balance = record.foreign_balance = 0
            record.adjusted_balance = record.gainloss = 0
Exemplo n.º 3
0
class ProductPricelistType(models.Model):
    _name = "product.pricelist.type"
    _description = "Pricelist Type"

    name = fields.Char(string='Name', required=True, translate=True)
    key = fields.Char(
        string='Key',
        required=True,
        help=
        "Used in the code to select specific prices based on the context. Keep unchanged."
    )
Exemplo n.º 4
0
class XopgiBoardWidget(models.Model):
    _name = WIDGET_MODEL_NAME
    _description = "Board Widget"

    _order = 'category, name'

    name = fields.Char(translate=True)
    category = fields.Many2one('ir.module.category')
    template_name = fields.Char(required=True)
    xml_template = fields.Text(translate=True)
    python_code = fields.Text()

    @api.multi
    def name_get(self):
        '''Returns a list with id, name of widgets or name's template

        '''
        return [(item.id, item.name or item.template_name) for item in self]

    def get_widgets_dict(self):
        '''Returns a dictionary list that represents the widgets that the user
        has access to.

        '''
        widgets = self.env[WIDGET_REL_MODEL_NAME].get_widgets()
        logger.debug('Widgets to show %r' % [w['name'] for w in widgets])
        today = normalize_datetime(fields.Date.today(self))
        for widget in widgets:
            self._eval_python_code(widget, today)
        return widgets

    def _eval_python_code(self, widget, today):
        '''Evaluate the python code of a widget

        '''
        python_code = widget.get('python_code', '')
        if not python_code:
            return
        name = widget.get('name', '')
        env = self.env
        local_dict = locals()
        local_dict.update(globals().get('__builtins__', {}))
        try:
            logger.debug('Starting evaluation of Python code for widget %s' %
                         name)
            safe_eval(python_code, local_dict, mode='exec', nocopy=True)
            logger.debug('Python code for widget %s evaluated sussefully.' %
                         name)
        except ValueError:
            logger.exception(
                'An error happen trying to execute the Python '
                'code for \'%s\' board widget, python code: %s', name,
                python_code)
        widget.update(local_dict.get('result', {}))
Exemplo n.º 5
0
class PartnerClassification(models.Model):
    _name = 'res.partner.classification'

    name = fields.Char(required=True, translate=True)
    partners = fields.Many2many('res.partner',
                                'res_partner_classification_rel',
                                'classification_id', 'partner_id')
Exemplo n.º 6
0
class B(models.Model):
    _name = 'model.b'

    name = fields.Char()
    modela_id = fields.Many2one('model.a', 'Merge')
    melda_ids = fields.Many2many(comodel_name='model.a',
                                 column1='meld_column1_id',
                                 column2='meld_column2_id',
                                 string='relation')
    partner_id = fields.Many2one('res.partner', 'Parent')
Exemplo n.º 7
0
class PriceType(models.Model):
    """The price type is used to points which field in the product form is a price
       and in which currency is this price expressed.  When a field is a
       price, you can use it in pricelists to base sale and purchase prices
       based on some fields of the product.

    """
    _name = "product.price.type"
    _description = "Price Type"

    @api.model
    def _get_currency(self):
        comp = self.env.user.company_id
        if not comp:
            comp = self.env['res.company'].search([], limit=1)
        return comp.currency_id.id

    active = fields.Boolean(
        string="Active",
        default=True
    )
    name = fields.Char(
        string='Price Name',
        required=True,
        translate=True,
        help="Name of this kind of price."
    )
    field = fields.Selection(
        selection="_price_field_get",
        string="Product Field",
        size=32,
        required=True,
        help="Associated field in the product form."
    )
    currency_id = fields.Many2one(
        comodel_name='res.currency',
        string="Currency",
        required=True,
        default=_get_currency,
        help="The currency the field is expressed in."
    )

    @api.model
    def _price_field_get(self):
        mf = self.env['ir.model.fields']
        fields = mf.search(
            [('model', 'in', (('product.product'), ('product.template'))),
             ('ttype', '=', 'float')]
        )
        res = []
        for field in fields:
            if not (field.name, field.field_description) in res:
                res.append((field.name, field.field_description))
        return res
Exemplo n.º 8
0
class ControlVariableTemplate(models.Model):
    '''The template is the key for get a value in a control variable: E.g.

    env['{model}'].browse({instance}).{field} -> One instance field value

    '''
    _name = 'cdr.control.variable.template'

    name = fields.Char(
        translate=True
    )

    reusable = fields.Boolean(
        default=True
    )

    definition = fields.Text(
        help="Python code string. Allow format string arguments in it."
    )

    args_need = fields.Boolean(
        help="Marc if definition need to be formatted."
    )

    eval_mode = fields.Selection(
        [('eval', 'Eval'),
         ('exec', 'Execute')],
        default='eval'
    )

    @api.onchange('reusable')
    def onchange_reusable(self):
        if not self.reusable:
            self.args_need = False

    def compile(self, values):
        '''Compiles the expression with `values`.'''
        source = self.definition
        if self.args_need:
            source = source.format(**values)
        compile(source, '<cdr-variable>', self.eval_mode)  # TODO: safe_compile
        return source

    def eval(self, now, values):
        """Evaluate template definition with given param values.

        :param now: datetime of evaluation cycle start.

        :param values: param values to passe it to str.format() on
                           definition.

        """
        code = self.compile(values)
        return evaluate(code, self.eval_mode, now=now, env=self.env)
Exemplo n.º 9
0
class Value(models.AbstractModel):
    '''A variant value mixin.

    Contains a single value that it represented a VARCHAR to the DB but is a
    Python object.

    Any object whose `repr()` result is sensible can be stored/retrieved.

    Provides `value`:attr: with the repr'ed representation and `result`:attr:
    with the Python object.

    '''
    _name = 'cdr.value'

    value = fields.Char(
        # This stores the representation (result of repr) of the value.  You
        # may use `result` to read/update this field.  If you use Odoo's
        # write/create, or set this field directly you MUST ensure to pass the
        # result of `repr()`.  The `write/create` methods below ensure the
        # writing to 'result' works.
        help='Value of the evaluation')

    @api.multi
    def write(self, values):
        # Makes writing to 'result' work.  If you pass both 'value' and
        # 'result', 'result' wins.
        if 'result' in values:
            values['value'] = repr(values.pop('result'))
        return super(Value, self).write(values)

    @api.model
    def create(self, values):
        # Makes writing to 'result' work.  If you pass both 'value' and
        # 'result', 'result' wins.
        if 'result' in values:
            values['value'] = repr(values.pop('result'))
        return super(Value, self).create(values)

    def _get_result(self):
        return safe_eval(self.value) if self.value else self.value

    def _set_result(self, value):
        self.value = repr(value)

    def _del_result(self):
        self.value = None

    result = fields.Property(
        getter=_get_result,
        setter=_set_result,
        deleter=_del_result,
    )
    del _get_result, _set_result, _del_result
Exemplo n.º 10
0
class MailServer(models.Model):
    # Adds the delivered column
    _inherit = 'ir.mail_server'

    delivered_address = fields.Char(
        'Delivered to',
        required=False,
        help=('The email address that should be check as the '
              'recipient of this server.  This way we can match this '
              'server for outgoing messages in response to emails '
              'delivered to this address.'),
    )
Exemplo n.º 11
0
class MailAlias(models.Model):
    _inherit = 'mail.alias'

    custom_domain = fields.Char('Alias domain')
    alias_domain = fields.Char(compute='_compute_alias_domain',
                               inverse='_set_alias_domain',
                               search='_search_alias_domain',
                               string="Alias domain",
                               default=get_default_alias_domain)

    @api.multi
    def _compute_alias_domain(self):
        default_domain = get_default_alias_domain(self)
        for record in self:
            record.alias_domain = record.custom_domain or default_domain

    @api.model
    def _search_alias_domain(self, args):
        res = []
        for cond in args:
            new_arg = cond
            if len(new_arg) == 3 and new_arg[0] == 'alias_domain':
                new_arg = ['custom_domain'] + list(cond)[1:]
            res.append(new_arg)
        return res

    @api.multi
    def _set_alias_domain(self):
        for record in self:
            self.custom_domain = self.alias_domain

    def fields_get(self, *args, **kargs):
        # Hack to make alias_domain editable. I don't know why it's not!
        result = super(MailAlias, self).fields_get(*args, **kargs)
        alias_domain = result.get('alias_domain', None)
        if alias_domain:
            alias_domain['readonly'] = 0
        return result
Exemplo n.º 12
0
class SelectableView(models.Model):
    _name = 'xopgi.selectable.view'
    _rec_name = 'label'
    _order = 'priority'

    label = fields.Char(translate=True, required=True)
    model_id = fields.Selection(
        string='Model',
        selection=lambda self: get_model_selection(self),
        required=True)
    view = fields.Many2one('ir.ui.view', required=True)
    priority = fields.Integer(default=16)

    def get_views(self, model):
        domain = [('model_id', '=', model)]
        return self.search(domain)

    def get_action(self, model=None, target='current', res_id='None'):
        """ Return an ir.actions.act_window
        """
        res = dict(target=target)
        if self:
            view = self[0]
            model = view.model_id

        if res_id is not None:
            # If the recordset is empty in Odoo8 it returns an empty list.In
            # odoo10 gives error.
            values_action = self.env[model].browse(res_id).get_access_action()

            # If values_action contains a list it is because get_acess_action
            # was executed #in odoo8 and returns a [{}], in odoo10 returns a {}.
            if isinstance(values_action, list):
                values_action = values_action[0]
            res = dict(values_action, **res)
        else:
            values = {
                'type': 'ir.actions.act_window',
                'res_model': model,
                'view_type': 'form',
                'view_mode': 'form',
                'views': [(self.view.id, 'form')],
                'context': self._context
            }
            res = dict(values, **res)
        return res

    @api.multi
    def try_selected_view(self):
        return self.get_action(target='new')
Exemplo n.º 13
0
class A(models.Model):
    _name = 'model.a'

    add_char = fields.Char()
    add_text = fields.Text()
    price_int = fields.Integer()
    cost_float = fields.Float()
    min_int = fields.Integer()
    max_int = fields.Integer()
    active = fields.Boolean(default=True)
    parent_id = fields.Many2one('model.a', 'Parent')
    meldb_ids = fields.Many2many(comodel_name='model.b',
                                 column1='meld_column2_id',
                                 column2='meld_column1_id',
                                 string='relation')
Exemplo n.º 14
0
class CDRIdentifier(models.Model):
    _name = 'cdr.identifier'
    _inherit = ['cdr.value']

    name = fields.Char(required=True,
                       help="Must be a available python identifier.\n"
                       "Just letters, digit (never in first position) "
                       "and underscore are allowed.")

    evaluations = fields.One2many('cdr.history', 'identifier')
    _sql_constraints = [('identifier_name_unique', 'unique(name)',
                         'Name already exists')]

    @api.constrains('name')
    def check_name(self):
        '''Check name of a CDRIdentifier

        '''
        if not check_identifier(self.name):
            raise ValidationError(
                _("Name must be a available python identifier"))
Exemplo n.º 15
0
class mail_message(models.Model):
    """Store the translated name of the model that the message reference to."""

    _name = _inherit = get_modelname(MailMessage)

    model_names = fields.Char(
        compute='_get_model_names',
        search='_search_model_names',
        string='Associated to',
    )

    @api.multi
    def _get_model_names(self):
        for message in self:
            if message.model:
                translations = translate_model_name(
                    self,
                    message['model'],
                )
                message.model_names = ' | '.join(translations)

    @api.model
    def _search_model_names(self, operator, value):
        ir_translation = self.env['ir.translation']
        translations = ir_translation.search(
            [
                ('name', '=', 'ir.model,name'),  # Search in model names
                '|',  # If any of:
                ('src', operator, value),  # source matches value
                ('value', operator, value),  # translation matches value
            ], )
        model_names = list({trans.src for trans in translations})
        models = [
            m.model
            for m in self.env['ir.model'].search([('name', 'in', model_names)])
        ]
        return [('model', 'in', models)]
Exemplo n.º 16
0
class object_merger(models.TransientModel):
    _name = 'object.merger'
    _description = 'Merge objects'

    name = fields.Char('Name', size=16)

    @api.model
    def fields_view_get(self,
                        view_id=None,
                        view_type='form',
                        toolbar=False,
                        submenu=False):
        res = super(object_merger, self).fields_view_get(view_id,
                                                         view_type,
                                                         toolbar=toolbar,
                                                         submenu=submenu)
        if view_type == 'form':
            object_ids = self._context.get('active_ids', [])
            active_model = self._context['active_model']
            if object_ids:
                self._check_quantity(active_model)
                field_name = 'x_%s_id' % active_model.replace('.', '_')
                view_part = """
                <field name="{field_name}"/>
                <separator string="{string}" colspan="4"/>
                <field name="info" nolabel="1" colspan="4"/>
                """.format(field_name=field_name, string=_("To merge"))
                res['arch'] = res['arch'].decode('utf8').replace(
                    """<separator string="to_replace"/>""", view_part)
                field = self.fields_get([field_name, 'info'])
                field_data = field[field_name]
                field_data.update(
                    domain=[('id', '=', object_ids)],
                    selection=self.env[active_model].sudo().name_get(),
                    required=True)
                res['fields'].update(field)
        return res

    if MAJOR_ODOO_VERSION < 10:

        @api.model
        def fields_get(self,
                       fields_list=None,
                       write_access=True,
                       attributes=None):
            res = super(object_merger,
                        self).fields_get(allfields=fields_list,
                                         write_access=write_access,
                                         attributes=attributes)
            if fields_list and 'info' in fields_list:
                active_model = self._context.get('active_model')
                fvg = lambda view: self.env[active_model].fields_view_get(
                    view_type=view)
                res.update(
                    info=dict(type='many2many',
                              relation=active_model,
                              views={k: fvg(k)
                                     for k in ['tree', 'form']}))
            return res
    else:

        @api.model
        def fields_get(self, fields_list=None, attributes=None):
            res = super(object_merger, self).fields_get(allfields=fields_list,
                                                        attributes=attributes)
            if fields_list and 'info' in fields_list:
                active_model = self._context.get('active_model')
                fvg = lambda view: self.env[active_model].fields_view_get(
                    view_type=view)
                res.update(
                    info=dict(type='many2many',
                              relation=active_model,
                              views={k: fvg(k)
                                     for k in ['tree', 'form']}))
            return res

    @api.model
    def default_get(self, fields_list):
        res = super(object_merger, self).default_get(fields_list)
        if fields_list and 'info' in fields_list:
            res.update(info=self._context.get('active_ids', False))
        return res

    def _check_quantity(self, active_model):
        '''Check for groups_id of uid and if it are not merger manager check limit
        quantity of objects to merge.

        '''
        if len(self._context.get('active_ids', [])) < 2:
            raise UserError(_('At less two items are necessary for merge.'))
        model = IrModel.sudo().search([('model', '=', active_model)], limit=1)
        if model and 0 < model.merge_limit < len(
                self._context.get('active_ids', [])):
            raise UserError(_('You can`t merge so much objects at one time.'))
        return True

    @api.requires_singleton
    def action_merge(self):
        active_model = self._context.get('active_model')
        if not active_model:
            raise UserError(_('The is no active model defined!'))
        Model = self.env[active_model]
        sources = Model.browse(self._context.get('active_ids', []))
        attr = self._context.get('field_to_read')
        val = getattr(self, attr, None) if attr else None
        if not val:
            raise UserError(_('Please select one value to keep'))
        target = val[0]
        self.merge(sources, target)
        try:
            Model.read([])
            return target.get_formview_action()
        except AccessError:
            return CLOSE_WINDOW

    @api.model
    def merge(self, sources, target=None, deactivate=True):
        '''Merge all recordsets from `source` into `target`.

        If `target` is None, it defaults to the last-created record from
        `source`.

        If `deactivate` is True (the default), and the `sources` have an
        'active' field, we deactivate the sources instead of deleting them.

        Return False if no merge happened.  Otherwise, return True.

        .. rubric:: Merging trees.

        .. rubric:: Merging arbitrary graphs.

        '''
        sources = sources.sudo().exists()
        active_model = sources._name
        merge_way = IrModel.search([('model', '=', active_model)])
        if not target:
            target = sources.sorted(key=lambda date: date.create_date)[-1]
        sources -= target
        if sources:
            merge_way._merge(sources, target)
            self._check_fks(sources, target)
            self._check_references(sources, target)
            has_active = sources._fields.get('active', False)
            if has_active and deactivate:
                sources.write({'active': False})
            else:
                sources.unlink()
            return True
        else:
            return False

    def _get_contraints(self, table, field):
        '''Get unique constraints that involve `column` in `table`.

        :return: list of list of columns per constraint where `field` is
                 involved.

        '''
        self._cr.execute(
            """
            SELECT
              kcu.column_name,
              tc.constraint_name
            FROM
              information_schema.key_column_usage kcu,
              information_schema.key_column_usage kcuw,
              information_schema.table_constraints tc
            WHERE
              kcu.constraint_catalog = tc.constraint_catalog AND
              kcu.constraint_schema = tc.constraint_schema AND
              kcu.constraint_name = tc.constraint_name AND
              kcuw.constraint_catalog = tc.constraint_catalog AND
              kcuw.constraint_schema = tc.constraint_schema AND
              kcuw.constraint_name = tc.constraint_name AND
              tc.constraint_type = 'UNIQUE' AND
              tc.table_name = %s AND
              kcuw.column_name = %s
              """, (table, field))
        constraints = {}
        for column_name, constraint_name in self._cr.fetchall():
            data = constraints.setdefault(constraint_name, [])
            data.append(column_name)
        return constraints.values()

    def _check_constraints(self,
                           table,
                           field,
                           target,
                           filters,
                           model='',
                           subquery='{field} = {id}'):
        '''Check unique constraints.

        :return: False if any violation detected else True

        '''
        constraints = self._get_contraints(table, field)
        for columns in constraints:
            if columns:
                query_filters = ' AND '.join(
                    ['%s %s %s' % f for f in filters] +
                    [subquery.format(field=field, model=model, id=target.id)])
            self._cr.execute(
                "SELECT {field} FROM {table} WHERE {filters}".format(
                    field=field, table=table, filters=query_filters))
            if self._cr.rowcount:
                return False
        return True

    def _upd_del(self,
                 table,
                 field,
                 target,
                 filters,
                 sources,
                 model=None,
                 subquery='{field}={id}'):
        '''
        Update or Delete rows matching with filters passed.
        :param action_update: if True Update query are executed else
        Delete query are executed
        :return:
        '''
        ok = self._check_constraints(table,
                                     field,
                                     target,
                                     filters,
                                     model=model,
                                     subquery=subquery)
        query_filters = ' AND '.join(
            ['%s %s %s' % f
             for f in filters] + [subquery.format(field=field, id=sources)])
        if ok:
            query = "UPDATE {table} SET " + subquery + " WHERE {filters};"
            self._cr.execute(
                query.format(table=table,
                             field=field,
                             model=model,
                             id=str(target.id),
                             filters=query_filters))
        else:
            query = """DELETE FROM {table} WHERE {filters};"""
            self._cr.execute(query.format(table=table, filters=query_filters))

    @api.model
    def _check_fks(self, sources, target):
        '''Get all relational field with active_model and send to update it.
        '''
        query = """
          SELECT name, model, ttype
          FROM ir_model_fields
          WHERE relation=%(model)s AND ttype != 'one2many'"""
        args = dict(model=sources._name)
        model = IrModel.search([('model', '=', sources._name)])
        record = sources + target
        if model:
            self._cr.execute(query, args)
            fks = self._cr.fetchall()
        else:
            fks = []
        for field_name, model_name, ttype in fks:
            # When a model disappears, Odoo won't remove it from
            # ir_models_fields until the addon is removed.  So we can get a
            # model_name that it's not in the registry.  Ignore it.
            model_registry = self.env.registry.get(model_name)
            if not model_registry:
                continue  # noqa
            if not getattr(model_registry, '_auto', False):
                # The model is not created by the ORM
                continue  # noqa
            field = model_registry._fields.get(field_name, False)
            assert not field or field.relational
            if not field or not field.store:
                # Fields which are not in the
                continue  # noqa
            if ttype == 'many2one':
                if hasattr(model_registry, '_table'):
                    table = model_registry._table
                else:
                    table = model_name.replace('.', '_')
            elif ttype == 'many2many':
                if MAJOR_ODOO_VERSION < 10:
                    modelname = self.env[model_name]
                    table, _x, field_name = field.column._sql_names(modelname)
                else:
                    table, field_name = field.relation, field.column2
            else:
                assert False
            self._upd_fk(table=table,
                         field=field_name,
                         sources=sources,
                         target=target)
            field_names = []
            field_names.append(str(field_name))
            record._validate_fields(field_names)

    def _upd_fk(self, table, field, sources, target):
        '''Update foreign key (field) to destination value `target` where
        actual field value are in merging object `sources`.
        if not constraint involve the field to update.
            Update all matching rows
        else
            check one by one if can update and update it or delete.

        '''
        constraints = self._get_contraints(table, field)
        if not constraints:
            query = 'UPDATE {table} SET {field}={target} WHERE {field} IN ({filter});'
            self._cr.execute(
                query.format(table=table,
                             field=field,
                             target=target.id,
                             filter=','.join(str(i) for i in sources.ids)))
        else:
            query = 'SELECT {fields} FROM {model} WHERE {field} IN ({filter})'
            self._cr.execute(
                query.format(fields=', '.join(str(i) for i in constraints[0]),
                             model=table,
                             field=field,
                             filter=','.join(str(i) for i in sources.ids)))
            for row in self._cr.dictfetchall():
                filters = get_filters(row, field)
                self._upd_del(table, field, target, filters, row.get(field))

    def _check_references(self, sources, target):
        '''Get all reference field and send to update it.
        '''
        query = """
          SELECT name, model
          FROM ir_model_fields
          WHERE ttype = 'reference' AND model!=%(model)s;"""
        query_args = dict(model=sources._name)
        self._cr.execute(query, query_args)
        refks = self._cr.fetchall()
        for field_name, model_name in refks:
            model = self.env.registry(model_name)
            if not model:
                continue  # noqa
            if not getattr(model, '_auto', False):
                continue  # noqa
            if hasattr(model, '_fields'):
                field = model._fields.get(field_name, False)
                if not field or not field.store:
                    continue  # noqa
            if hasattr(model, '_table'):
                table = model._table
            else:
                table = model_name.replace('.', '_')
            self._upd_reference(table=table,
                                field=field_name,
                                sources=sources,
                                target=target,
                                model=model_name)
        self._check_informal_reference(sources, target)

    def _upd_reference(self,
                       table,
                       field,
                       sources,
                       target,
                       model_field=None,
                       model=None):
        '''Update reference (field) to destination value (dst_id) where
        actual field value are in merging object ids (src_ids).
        if not constraint involve the field to update.
            Update all matching rows
        else
            check one by one if can update and update it or delete.

        '''
        constraints = self._get_contraints(table, field)
        if model_field:
            SUBQUERY = ["{field} = {id}", "{model_field}='{model}'"]
        else:
            SUBQUERY = ["{field} = '{model},{id}'"]
        if not constraints:
            if model_field:
                query = """UPDATE {table} SET {field}={target}
                               WHERE {field} IN ({filter})
                                  AND {model_field}='{model}';"""
                self._cr.execute(
                    query.format(table=table,
                                 field=field,
                                 target=target.id,
                                 filter=','.join(str(i) for i in sources.ids),
                                 model_field=model_field,
                                 model=model))
                return
            for record in sources:
                query = ("UPDATE {table} "
                         "SET {field}='{model},{target}' " + "WHERE " +
                         SUBQUERY[0])
                self._cr.execute(
                    query.format(table=table,
                                 field=field,
                                 model=model,
                                 target=target.id,
                                 id=record.id))
            return
        else:
            query = ("SELECT {fields} FROM {table} WHERE " +
                     ' AND '.join(SUBQUERY))
            for sources in sources:
                _query = (query.format(fields=', '.join(
                    str(i) for i in constraints[0]),
                                       table=table,
                                       field=field,
                                       model=model,
                                       id=sources.id,
                                       model_field=model_field))
                self._cr.execute(_query)
                for row in self._cr.dictfetchall():
                    filters = get_filters(row, field)
                    self._upd_del(table, field, target, filters, sources.id,
                                  model_field)

    def _check_informal_reference(self, sources, target):
        for ref in self.env['informal.reference'].sudo().search([]):
            if self._check_field_exist(
                    ref.table_name, (ref.id_field_name, ref.model_field_name)):
                model = sources._name
                if ref.model_field_value == IS_MODEL_ID:
                    args = [('model', '=', model)]
                    model_id = IrModel.sudo().search(args)
                    if model_id:
                        self._upd_reference(table=ref.table_name,
                                            field=ref.id_field_name,
                                            sources=sources,
                                            target=target,
                                            model_field=ref.model_field_name,
                                            model=str(model_id.id))
                else:
                    self._upd_reference(table=ref.table_name,
                                        field=ref.id_field_name,
                                        sources=sources,
                                        target=target,
                                        model_field=ref.model_field_name,
                                        model=model)

    def _check_field_exist(self, table_name, column_names):
        query = """
        SELECT column_name
        FROM information_schema.columns
        WHERE table_schema='public' AND table_name=%s AND column_name=%s
        """
        for column_name in column_names:
            self._cr.execute(query, (table_name, column_name))
            if not self._cr.rowcount:
                return False
            return True
Exemplo n.º 17
0
class WorkDistributionModel(models.Model):
    _name = WORKDIST_MODELNAME

    model = fields.Many2one(
        'ir.model', required=True,
        help='Model where Work Distribution Strategies will be applied.')
    model_name = fields.Char(related='model.model', readonly=True)
    group_field = fields.Many2one(
        'ir.model.fields', 'Group Field',
        help='many2one field where that it value determine domain of '
             'distribution destination.')
    strategy_by_group = fields.Boolean()
    use_domain_builder = fields.Boolean()
    domain = fields.Text(
        help='Odoo domain to search in destination_field`s model.',
        default=lambda self: _(DOMAIN_DEFAULT))
    build_domain = fields.Char(
        help='Odoo domain to search in destination_field`s model.',
        string='Domain')
    destination_field = fields.Many2one(
        'ir.model.fields', 'Destination Field', required=True,
        help='Field that it value it will determinate by distribution '
             'strategy apply.')
    destination_model = fields.Char(related='destination_field.relation',
                                    readonly=True)
    other_fields = fields.Text(
        'Other fields', help='Python Dictionary with others variables (Keys) '
                             'used on some distribution strategy and '
                             'model\' fields where it value are. \n '
                             'Eg: {"date": "date_from"}', default='{}')
    strategy_ids = fields.Many2many(
        'work.distribution.strategy', 'distribution_model_strategy',
        'model_id', 'strategy_id', 'Strategies', required=True,
        help='Work Distribution Strategies possible to apply on this model '
             'objects.')
    action = fields.Many2one('ir.actions.act_window')
    strategy_field = fields.Many2one('ir.model.fields', 'Setting Field')
    when_apply = fields.Selection(
        [('all', 'Always'), ('no_set', 'When no value set')], default='all')
    effort_domain = fields.Text(help="Odoo domain to use on effort "
                                     "based strategies. \nEg: "
                                     "[('state','not in',('done','cancel')]")
    translations = fields.Many2many('ir.translation')

    @api.onchange('use_domain_builder')
    def onchange_use_domain_builder(self):
        ''' If the domain constructor changes it assigns default values ​​to the
        domain. By default use_domain_builder is 'True'.

        '''
        if self.use_domain_builder:
            self.build_domain = ''
            self.domain = ''
        else:
            self.domain = (self.build_domain or '') + _(DOMAIN_DEFAULT)

    @api.constrains('other_fields')
    def _check_other_fields(self):
        ''' Validates that the field names defined in the 'other_field'
        dictionary are among the names of the fields defined for the selected
        strategy. And that the defined fields are in some existing model.
        '''
        for model in self:
            fields_name = model.strategy_ids.get_fields_name()
            if fields_name:
                other_fields = safe_eval(model.other_fields or '{}')
                if not other_fields or not all(
                        other_fields.get(f, False) for f in fields_name):
                    raise ValidationError(
                        _('Other fields must define all fields used on '
                          'selected strategies.'))
                obj = self.env[model.model.model]
                for field in other_fields.itervalues():
                    if not obj or not obj._fields.get(field, False):
                        raise ValidationError(
                            _('Some Other fields defined not exist on '
                              'destination model'))
        return True

    @api.constrains('strategy_ids')
    def _check_strategy_ids(self):
        '''Validate that when no group field is defined, only one strategy
        can be selected.

        '''
        for model in self:
            if not model.group_field and len(model.strategy_ids) > 1:
                raise ValidationError(
                    _('When no group field is defined only one strategy '
                      'must be selected.'))
        return True

    @api.model
    @api.onchange('strategy_ids')
    def onchange_strategy_ids(self):
        ''' When the selected strategies change, it is validated that the
        field 'Other field' maintains the structure of a dictionary that its
        values ​​are valid.
        '''
        warning = False
        try:
            other_fields = safe_eval(self.other_fields or '{}')
        except (ValueError, SyntaxError, ):
            other_fields = {}
            warning = {
                'title': _('Warning!'),
                'message': _('Other fields must be a python dict.')
            }
        fields_name = self.strategy_ids.get_fields_name()
        other_fields.update({n: n
                             for n in fields_name - set(other_fields)})
        self.other_fields = str(other_fields)
        return {'warning': warning} if warning else None

    @api.model
    @api.onchange('strategy_by_group')
    def onchange_strategy_by_group(self):
        ''' If the field 'Strategy by group' is not selected the field
        'Group field' is assigned a false value and is hidden.

        '''
        if not self.strategy_by_group:
            self.group_field = False

    def applicable(self, values):
        if not self.when_apply or self.when_apply == 'all':
            return True
        else:
            return not values.get(self.destination_field.name, False)

    @api.model
    def unlink_rest(self):
        self.search([('id', 'not in', self.ids)]).unlink()

    @api.model
    @api.returns('self', lambda value: value.id)
    def create(self, values):
        if values.get('group_field', False):
            self.sudo().create_related(values)
        res = super(WorkDistributionModel, self).create(values)
        res.update_translations()
        return res

    def create_related(self, values):
        """ Create action and field associated.

        """
        field_obj = self.env['ir.model.fields']
        destination_field = field_obj.browse(values['destination_field'])
        group_field = field_obj.browse(values['group_field'])
        model_id = values.get('model', False)
        model = self.env['ir.model'].browse(model_id)
        strategy_field = self.create_field(group_field.relation, model,
                                           destination_field)
        action = self.create_actions(
            group_field.relation, model.name,
            destination_field.field_description, strategy_field)
        values.update(dict(strategy_field=strategy_field, action=action))

    def create_field(self, group_model, model, destination_field):
        ''' Create field to save which distribution strategy use on each
        group_field model objects.

        '''
        field_obj = self.env['ir.model.fields']
        model_obj = self.env['ir.model']
        group_model = model_obj.search([('model', '=', group_model)])
        field_base_name = '%s_%s' % (model.model.replace('.', '_'),
                                     destination_field.name)
        field_name = 'x_%s_id' % field_base_name
        field_data = {
            'model': group_model[0].model,
            'model_id': group_model.ids[0],
            'name': field_name,
            'relation': 'work.distribution.strategy',
            'field_description':
                _("Work Distribution Strategy for %s on %s") %
                (destination_field.field_description, model.name),
            'state': 'manual',
            'ttype': 'many2one'
        }
        new_field = field_obj.create(field_data)
        self.create_ir_model_data_reference('ir.model.fields',
                                            new_field.id, field_name)
        return new_field.id

    def create_actions(self, group_model, model_name, destination_field,
                       strategy_field_id):
        ''' Create actions to config with strategy use on each group_field
        model objects.

        '''
        action_obj = self.env['ir.actions.act_window']
        value_obj = self.env['ir.values']
        name = (_("Define Work Distribution Strategy for '%s' on '%s'")
                % (destination_field, model_name))
        rol = self.env.ref('xopgi_work_distributor.group_distributor_manager',
                           raise_if_not_found=False)
        new_act = action_obj.create({
            'name': name,
            'type': 'ir.actions.act_window',
            'res_model': WIZARD_NAME,
            'src_model': group_model,
            'view_type': 'form',
            'view_mode': 'form',
            'target': 'new',
            'groups_id': LINK_RELATED(rol.id) if rol else False,
            'context': {
                'strategy_field_id': strategy_field_id,
                'field_to_show': FIELD_NAME_TO_SHOW_ON_WIZARD(group_model)
            }
        })
        self.create_ir_model_data_reference('ir.actions.act_window',
                                            new_act.id, name)
        new_val = value_obj.create({
            'name': name,
            'model': group_model,
            'key2': 'client_action_multi',
            'value': "ir.actions.act_window," + str(new_act.id)
        })
        self.create_ir_model_data_reference('ir.values',
                                            new_val.id, name)
        return new_act.id

    def create_ir_model_data_reference(self, model, res_id, name):
        '''Create ir.model.data entry for each field, action or value
        created on run time, to ensure it be removed at module
        uninstall.
        '''
        self.env['ir.model.data'].create({
            'model': model,
            'res_id': res_id,
            'module': 'xopgi_work_distributor',
            'name': '%s-%s' % (model, name),
            'noupdate': True
        })

    def unlink(self):
        """ Unlink action and field associated.

        """
        actions_to_unlink = [i.action.id for i in self if i.action]
        fields_to_unlink = [i.strategy_field.id for i in self
                            if i.strategy_field]
        self.update_translations(force_delete=True)
        result = super(WorkDistributionModel, self).unlink()
        self.sudo().unlink_actions(actions_to_unlink)
        self.sudo().unlink_fields(fields_to_unlink)
        return result

    def unlink_actions(self, actions_to_unlink):
        '''Remove actions of distributions model unlinked.

        '''
        if not actions_to_unlink:
            return
        model = 'ir.actions.act_window'
        self.unlink_ir_model_data_reference(model, actions_to_unlink)
        self.env[model].browse(actions_to_unlink).unlink()
        for action_id in actions_to_unlink:
            args = [('value', '=', "%s,%d" % (model, action_id))]
            values = self.env['ir.values'].search(args)
            if values:
                self.unlink_ir_model_data_reference('ir.values', values.ids)
                values.unlink()

    def unlink_fields(self, fields_to_unlink):
        '''Remove fields of distributions model unlinked.

        '''
        if not fields_to_unlink:
            return
        model = 'ir.model.fields'
        self.unlink_ir_model_data_reference(model, fields_to_unlink)
        self.env[model].browse(fields_to_unlink).unlink()

    def unlink_ir_model_data_reference(self, model, ids):
        self.env['ir.model.data'].search([
            ('model', '=', model),
            ('res_id', 'in', ids),
            ('module', '=', 'xopgi_work_distributor')
        ]).unlink()

    @api.multi
    def write(self, values):
        temp_fields = ['destination_field', 'group_field', 'model']
        if any(f in values for f in temp_fields):
            actions_to_unlink = [i.action.id for i in self if i.action]
            fields_to_unlink = [i.strategy_field.id for i in self
                                if i.strategy_field]
            self.sudo().unlink_actions(actions_to_unlink)
            self.sudo().unlink_fields(fields_to_unlink)
            for item in self.sudo():
                if item.translations:
                    item.translations.unlink()
        result = super(WorkDistributionModel, self).write(values)
        group_field = values.get('group_field', False)
        if any(values.get(f, False) for f in temp_fields):
            for item in self:
                temp_fields = ['destination_field', 'group_field', 'model']
                vals = {f: getattr(item, f).id for f in temp_fields}
                self.create_related(vals)
                for f in temp_fields:
                    vals.pop(f, None)
                item.write(vals)
        if any(f in values for f in temp_fields):
            self.update_translations(force_create=group_field)
        return result

    @api.multi
    def update_translations(self, force_create=False, force_delete=False):
        to_unlink = self.env['ir.translation']
        for item in self:
            if not force_delete and item.group_field:
                if force_create or not item.translations:
                    translations = self.create_translations(
                        item.group_field.relation, item.strategy_field.name,
                        item.strategy_field.field_description,
                        type='field')
                    translations |= self.create_translations(
                        'ir.actions.act_window', 'name', item.action.name,
                        res_id=item.action.id)
                    item.write(dict(
                        translations=[REPLACEWITH_RELATED(*translations.ids)]
                    ))
            if item.translations and (force_delete or not item.group_field):
                to_unlink |= item.translations
        if to_unlink:
            to_unlink.unlink()

    def create_translations(self, model, field, src, res_id=0, type='model'):
        result = self.env['ir.translation']
        for lang in self.env['res.lang'].search([]):
            result |= result.create({
                'type': type,
                'res_id': res_id,
                'module': 'xopgi_work_distributor',
                'name': '%s,%s' % (model, field),
                'src': src,
                'value': src,
                'lang': lang.code
            })
        return result
Exemplo n.º 18
0
class FieldMergeWay(models.Model):
    _name = 'field.merge.way'

    name = fields.Char(required=True, translate=True)
    code = fields.Selection(
        [('add', 'Add both fields'), ('min', 'Keep the minimal'),
         ('max', 'Keep the maximal'), ('average', 'Find the average'),
         ('sum', 'Find the sum')],
        required=True,
    )

    _sql_constraints = [('name_unique', 'unique (name)',
                         'Strategy must be unique!')]

    def apply(self, sources, target, field):
        method = getattr(self, 'apply_' + self.code, None)
        if method:
            return method(sources, target, field)
        else:
            return None

    def apply_add(self, sources, target, field):
        result = getattr(target, field.name, None)
        if field.ttype == 'char':
            union = ' '
        elif field.ttype == 'text':
            union = ' '
        else:
            union = False
        for obj in sources:
            temp = getattr(obj, field.name, None)
            if result and temp:
                result += union + temp if union else temp
            elif not result:
                result = temp
        return result

    def apply_average(self, sources, target, field):
        result = getattr(target, field.name, 0)
        count = 1 if result not in [False, None] else 0
        for obj in sources:
            temp = getattr(obj, field.name, None)
            if not any(var in [False, None] for var in (result, temp)):
                result += temp
                count += 1
        return result / count if result and count else 0

    def apply_sum(self, sources, target, field):
        return self._reduce(operator.add, sources, target, field, 0)

    def apply_max(self, sources, target, field):
        from xoutil.infinity import Infinity
        return self._reduce(max, sources, target, field, -Infinity)

    def apply_min(self, sources, target, field):
        from xoutil.infinity import Infinity
        return self._reduce(min, sources, target, field, -Infinity)

    def _reduce(self, f, sources, target, field, initial):
        result = getattr(target, field.name, initial)
        for temp in sources.mapped(field.name):
            result = f(result, temp)
        return result
Exemplo n.º 19
0
class BounceRecord(models.Model):
    '''An index for bounce address to message, thread and recipient.

    This model encodes the same information of old VERP addresses but allow a
    simpler address, like: ``[email protected]``.

    '''
    _name = 'xopgi.verp.record'

    bounce_alias = fields.Char(
        help=('The alias where the bounce was sent to. You may change the'
              'alias configuration midways and this will still work'),
        required=True,
        default='bounces'
    )

    thread_index = fields.Char(
        help='The unique index reference for the thread.',
        required=True,
        index=True,
    )

    message_id = fields.Many2one(
        'mail.message',
        required=True,
        # ondelete=cascade: If the message is delete remove the VERP
        # address.  This happens for invitations, for instance.  The
        # message is create and the bounce address is properly generated,
        # but afterwards the message is removed.  This make the bounce
        # reference ephemeral for these cases, but if the message is lost
        # we won't be able to know who to notify.
        ondelete="cascade",
        help=('The message id originating this notification. This allows '
              'to know who to notify about bounces.')
    )
    reference = fields.Char(
        help='The unique part for the bounce address.',
        size=100,
        required=True,
        index=True,
    )

    recipient = fields.Char(
        help='The recipient for which this VERP address was created',
        required=True,
        index=True,
    )

    _sql_constraints = [
        ('verp_unique', 'unique (reference)', 'The VERP is duplicated.'),
    ]

    @api.model
    @api.returns('self', lambda r: r.reference)  # return the reference
    def create(self, vals):
        try:
            from openerp.addons.mail.xopgi.index import generate_reference
        except ImportError:
            try:  # Odoo 9
                from openerp.addons.mail.models.xopgi.index import generate_reference
            except ImportError:  # Odoo 10
                from odoo.addons.mail.models.xopgi.index import generate_reference
        reference = generate_reference(
            lambda r: self.search([('reference', '=', r)]),
            start=3,
            lower=True
        )
        assert reference
        vals.update(reference=reference)
        return super(BounceRecord, self).create(vals)

    @api.model
    def cleanup(self):
        '''Remove all bounce address references that are too old.

        This should be called in a cron task.

        '''
        self._cr.execute('''
           WITH aged AS (
              SELECT id, ((NOW() at time zone 'UTC') - create_date) AS age
              FROM xopgi_verp_record
           ) SELECT id FROM aged WHERE age >= %s
        ''', ('10 days', ))
        elders = [row[0] for row in self._cr.fetchall()]
        if elders:
            self.sudo().browse(elders).unlink()
Exemplo n.º 20
0
class MergePartnerGroup(models.Model):
    """A group of partner which are deemed duplicates.

    - Is a partner when `parent_id` points to another instance of the same
      type representing the group.

    - Is a group when has no `parent_id`  and several partners point to here.
      In this case the referenced partner is the destination partner.

    """
    _name = 'xopgi.partner.merge.group'
    _order = "name asc"

    dest_partner_id = fields.Many2one(
        'res.partner',
        string='Destination partner'
    )

    partner_ids = fields.Many2many(
        comodel_name='res.partner',
        relation='xopgi_partner_merge_group_partners',
        column1='category_id',
        column2='partner_id',
        string='Partners'
    )

    name = fields.Char(
        related=('dest_partner_id', 'name'),
        string='Name',
        readonly=True,
        store=True,
    )

    @api.multi
    @mute_logger('openerp.osv.expression', 'openerp.models')
    def merge(self):
        """Merge several `partners` into a single destination partner.

        Original `partners` will be removed from the DB afterwards.  Only
        target will remain.  All references to the original partners
        will be re-establish to the target partner.

        If `partners` constains less that 2 partners, do nothing.  All
        partners must have the same email.

        If sources `partner` is none, the target partner defaults to the last
        created record in `partners`.

        :param sources: The source partners.
        :type sources: A recordset of 'res.partners'.

        :param target: The target partner.
        :type target: A singleton recordset of 'res.partners' or None.

        """
        sources = self.partner_ids
        target = self.dest_partner_id
        if sources.sudo().exists() and len(sources) < 2:
            raise UserError(_("Constains less that 2 partners, do nothing"))
        partner_different_emails = {
            p.email
            for p in sources
            if p.email and p.email.strip()
        }
        if len(partner_different_emails) > 1:
            user = self.env.user
            if user.has_group('xopgi_partner_merge.base_parter_merger'):
                object_merger = self.env['object.merger']
                object_merger.merge(sources, target)
            else:
                raise UserError(
                    _("All contacts must have the same email. Only the "
                      "users with Partner Merge rights can merge contacts "
                      "with different emails.")
                )
        object_merger = self.env['object.merger']
        object_merger.merge(sources, target)
        self.unlink()
Exemplo n.º 21
0
class MessageRecipients(models.Model):
    _name = get_modelname(Message)
    _inherit = _name

    recipients = fields.Char()
Exemplo n.º 22
0
class ControlVariable(models.Model):
    '''The `control variables` define basic mostly numeric evaluations of a
    single basic indicator. For instance, you may define 'the total amount of
    liquidity' you have available.

    '''
    _name = 'cdr.control.variable'
    _inherits = {'cdr.identifier': 'identifier_id'}

    identifier_id = fields.Many2one(
        'cdr.identifier',
        required=True,
        ondelete='cascade',
        help='Identifier the control variable: name, value, evaluations'
    )

    description = fields.Char(
        translate=True,
        help='Description of control variable'
    )

    template = fields.Many2one(
        'cdr.control.variable.template',
        required=True,
        ondelete='restrict',
        help='The template is the key for get a value in a control variable'
    )

    args_need = fields.Boolean(
        related='template.args_need'
    )

    args = fields.Text(
        help="Python dictionary with arguments that template expect."
    )

    @fields.Property
    def arguments(self):
        return evaluate(self.args) if self.args else {}

    evidences = fields.Many2many(
        'cdr.evidence',
        'evidence_control_variable_rel',
        'var_id',
        'evidence_id',
        ondelete='restrict',
        help='Are predicates over several control variables'
    )

    active = fields.Boolean(
        default=True
    )

    cycle = fields.Many2one(
        'cdr.evaluation.cycle',
        help='The control variable are evaluate in evaluation cycle'
    )

    @api.onchange('template', 'template', 'args_need')
    def onchange_template(self):
        '''Take values of
        '''
        if self.template and self.args_need:
            args = [
                "\n\t'%s': " % arg
                for _, arg, _, _ in self.template.definition._formatter_parser()
                if arg
            ] if self.args_need else []
            self.args = "{%s\n}" % ",".join(args)
        else:
            self.args = ""

    def get_value(self):
        '''Get name: value dictionary

        '''
        return {v.name: v.result for v in self}

    def _value(self):
        '''If value: Verify that the value of control variable is evaluated as
        python code else return None.

        '''
        import warnings
        warnings.warn('The _value() method of control variables is '
                      'deprecated.  Use the `result` property.', stacklevel=2)
        return self.result

    @api.requires_singleton
    def _evaluate(self, now=None):
        '''Allow to make python expression for 'args' in the template. The
        field 'args' represent a string of type text.

        '''
        logger.debug('Evaluating %r', self.name)
        from xoeuf.tools import normalize_datetime
        result = self.template.eval(
            normalize_datetime(now or fields.Datetime.now()),
            self.arguments
        )
        logger.debug('Evaluated %r', self.name)
        return result

    @api.constrains('template', 'args')
    def _check_definition(self):
        '''Check the control variable definition.

        '''
        for variable in self:
            try:
                variable.template.compile(variable.arguments)
            except Exception as e:
                raise exceptions.ValidationError(
                    _("Wrong definition: %s") % e.message
                )

    def evaluate(self, cycle):
        '''Evaluate the control variables in a evaluation cycle.

        '''
        if isinstance(cycle, int):
            cycle = self.env['cdr.evaluation.cycle'].browse(cycle)
        import psycopg2
        logger.debug('Start evaluation of %r, cycle: %r', self.mapped('name'), cycle)
        from celery.exceptions import SoftTimeLimitExceeded
        for var in self:
            try:
                value = var._evaluate(cycle.create_date)
            except SoftTimeLimitExceeded:
                raise
            except (psycopg2.InterfaceError, psycopg2.InternalError):
                # This means the cursor is unusable, so there's no point in
                # trying to do anything else with it.
                raise
            except Exception:
                logger.exception(
                    'Error evaluating control variable %s.',
                    var.name
                )
            else:
                var.write(dict(
                    result=value,
                    cycle=cycle.id,
                    evaluations=[CREATE_RELATED(result=value,
                                                cycle=cycle.id)]
                ))
        logger.debug('Done computing variable %r', self.mapped('name'))

    @api.model
    @api.returns('self', lambda value: value.id)
    def create(self, vals):
        logger.debug('Creating variable %r', vals)
        res = super(ControlVariable, self).create(vals)
        logger.debug('Created variable %r', res.name)
        # evaluate by first time to get init value.
        self.env['cdr.evaluation.cycle'].create_and_evaluate(variables=res)
        logger.debug('Evaluated variable %r', res.name)
        return res
Exemplo n.º 23
0
class SystemEvent(models.Model):
    '''Generic CDR event definition. For specifics CDR events
    implementations just need to inherit by delegation of this model and
    override evaluate method.

    Recommendation: define _description for specific event implementations
    to allow correct user identification.

    '''

    _name = 'cdr.system.event'
    _description = "Generic CDR event"

    _order = 'priority'

    name = fields.Char(translate=True)

    definition = fields.Char(
        required=True,
        help=("Boolean expression combining evidences and operators.  "
              "For example: evidence1 or evidence2 and evidence3"))

    next_call = fields.Datetime(
        default=fields.Datetime.now(),
        help=("The date and time at which this event will be checked again.  "
              "This is when the evidences and its variables will be computed "
              "again."))

    priority = fields.Integer(
        default=10,
        help=("Priority in which events are to be evaluated in an evaluation "
              "cycle. When you run a search they will come sorted according "
              "to your priority."))

    active = fields.Boolean(default=True)

    evidences = fields.Many2many('cdr.evidence',
                                 'event_evidence_rel',
                                 'event_id',
                                 'evidence_id',
                                 compute='get_evidences',
                                 store=True)

    specific_event = fields.Reference(
        [],
        compute='_get_specific_event',
        help='Reference at specific event: basic or recurrent event')

    state = fields.Selection(EVENT_STATES,
                             help='State of the event: Raising or Not raising')

    action = fields.Selection(EVENT_ACTIONS_SELECTION, string="Last action")

    def get_specific_event_models(self):
        '''Get models that look like specific event.

        '''
        result = []
        for model_name in self.env.registry.keys():
            model = self.env[model_name]
            if 'cdr.system.event' in getattr(model, '_inherits', {}):
                result.append(model)
        return result

    def _get_specific_event(self):
        '''Get specific event reference from it's generic one.

        '''
        for event in self:
            for model in self.get_specific_event_models():
                field = model._inherits.get('cdr.system.event')
                result = model.search([(field, '=', event.id)], limit=1)
                if result:
                    event.specific_event = "%s,%d" % (result._name, result.id)

    @api.depends('definition')
    @api.onchange('definition')
    def get_evidences(self):
        '''Get evidences referenced on event definition.

        '''
        evidences = self.env['cdr.evidence']
        for event in self:
            if event.active and event.definition:
                domain = [('name', 'in',
                           tuple(get_free_names(event.definition)))]
                event.evidences = evidences.search(domain)
            else:
                event.evidences = evidences

    def _get_evidences_to_evaluate(self):
        return self.mapped('evidences')

    def _evaluate(self):
        return evaluate(self.definition, **self.evidences.get_bool_value())

    @api.constrains('definition')
    def _check_definition(self):
        for record in self:
            try:
                record._evaluate()
            except Exception as e:
                raise exceptions.ValidationError(
                    _("Wrong definition: %s") % e.message)

    def evaluate(self, cycle):
        if isinstance(cycle, int):
            cycle = self.env['cdr.evaluation.cycle'].browse(cycle)
        for event in self:
            # call specific event evaluate method to get each
            # corresponding behavior.
            event.specific_event.evaluate(cycle)
        for signalname, signal in EVENT_SIGNALS.items():
            # Do not use groupby because a recordset is needed on signaling
            # send.
            sender = self.filtered(lambda event: event.action == signalname)
            if sender:
                logger.debug('Sending signal (%s) for Events: (%s)',
                             signalname, ', '.join(e.name for e in sender))
                signal.send(sender=sender)
Exemplo n.º 24
0
class RecurrentRuleDefinition(models.AbstractModel):
    '''A recurrence definition mixin.

    The model behind this definition is that of the python module
    `dateutil.rrule`:mod:.

    '''
    _name = RECURRENT_DESCRIPTION_MODEL
    _inherit = ['recurrence.abs.day_of_week']

    date_from = fields.Date(
        'Initial date',
        required=True,
        default=fields.Date.today,
        # XXX: When we return an occurrence this matches the date of the
        # occurrence.
        help='Date at which this recurrent event starts.')

    duration = fields.Integer('Duration',
                              default=1,
                              help='Duration (days) of each occurrence')

    allday = fields.Boolean(
        'All Day',
        default=True,
        help='Is this a day-long occurrence?',
    )

    freq = fields.Selection(
        FREQ,
        'Frequency type',
        default=DEFAULT_FREQ,
        # XXX: Don't put this, because there's an error in the JS client that
        # messes up with the invisible toggling we have there.
        # help='Frecuency type (Daily/Weekly/Monthly/Yearly)'
    )

    interval = fields.Integer(
        'Repeat Every',
        default=DEFAULT_INTERVAL,
        help='Repeat every ...',
    )

    # Recurrence by month data.
    days_option = fields.Selection(
        SELECT_WEEKDAY_MONTHDAY,
        'Option',
        default=DEFAULT_WEEK_MONTH,
        help='Does this occur on the same day of the month, or the week',
    )

    monthly_day = fields.Integer(
        'Days of month', default=lambda *args: datetime.date.today().day)

    by_week_day = fields.Selection(
        BYWEEKDAY,
        'Reference',
        default=DEFAULT_BYWEEKDAY,
        help=("Used in combination with each week day selection.  If you "
              "check Monday, then this can mean: every Monday, the first "
              "Monday, the last Monday, etc... You may choose several week "
              "days."))

    months = fields.Selection(
        MONTHS,
        'Month',
        deafault=lambda *args: str(datetime.date.today().month),
        help="The month of the year at which this event reoccurs.")

    is_easterly = fields.Boolean(
        'By easter',
        help="For events that reoccurs based on Western Easter Sunday.")

    byeaster = fields.Integer(
        'By easter',
        default=0,
        help='Number of days from Western Easter Sunday.')

    # In this model, the end of recurrence is EITHER: a) does not have and
    # end, b) after many instances,  c) after a given date.
    end_type = fields.Selection(
        END_TYPE,
        'Recurrence Termination',
        default=DOES_NOT_END,
        help="How this recurrence stops from happening.")

    count = fields.Integer('Repeat',
                           default=5,
                           help='Repeat recurrent event by x times')

    until = fields.Date('Repeat Until',
                        help='Date end for the recurrent termination')

    # TODO: Should we need to store this in the DB?
    rrule = fields.Char(
        'RRULE',
        size=124,
        readonly=True,
        default='',
        compute='_compute_rrule_string',
        help=(
            'This field is update after to creates or to update the recurrent'
            ' model by the function  _update_rrule. By default it is taken'
            ' value but then it is calculated and takes a similar value.'
            ' E.g. rrule=FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,MO'),
    )

    @api.depends('count', 'until', 'end_type', 'mo', 'tu', 'we', 'th', 'fr',
                 'sa', 'su', 'is_easterly', 'byeaster', 'months',
                 'monthly_day', 'by_week_day', 'freq', 'days_option',
                 'interval')
    def _compute_rrule_string(self):
        for record in self:
            record.rrule = str(record.get_rrule_from_description())

    @api.requires_singleton
    def get_rrule_from_description(self):
        '''The recurrent rule that describes this recurrent event.

        :returns: `dateutil.rrule.rrule`:class:.

        '''
        from xoeuf.tools import normalize_date
        kwargs = dict(
            dtstart=normalize_date(self.date_from),
            interval=max(1, self.interval or 0),
        )
        if self.end_type == ENDS_AT:
            kwargs['until'] = max(normalize_date(self.date_from),
                                  normalize_date(self.until))  # or date_to?
        elif self.end_type == ENDS_AFTER_MANY:
            kwargs['count'] = min(1, self.count)
        else:
            assert self.end_type == DOES_NOT_END
        if self.days_option == USE_WEEK_DAY:
            kwargs['byweekday'] = self.byweekday
        elif self.days_option == USE_MONTH_DAY:
            kwargs['bymonthday'] = self.monthly_day
        else:
            assert False
        if self.is_easterly:
            kwargs['byeaster'] = self.byeaster
        else:
            kwargs['bymonth'] = int(self.months)
        return _rrule.rrule(FREQ_TO_RULE[self.freq], **kwargs)

    @fields.Property
    def byweekday(self):
        return self._weekno

    @api.constrains('monthly_day')
    def _check_monthly_day_no_longer_than_month(self):
        for record in self:
            if not (-31 <= record.monthly_day <= 31):
                raise ValidationError(
                    'You must provide a valid day of the month')

    @api.constrains('byeaster', 'is_easterly')
    def _check_byeaster_no_longer_than_year(self):
        for record in self:
            if record.is_easterly:
                if not (-365 <= record.byeaster <= 365):
                    raise ValidationError(
                        'By Easter must not extend longer than a year')

    @api.requires_singleton
    def iter_from(self, start=None):
        '''Return an iterator that yields each occurrence after `start` date.

        If `start` is None, start at the first `date` (field ``date_from``).

        :returns: A generator of the dates of the occurrences.

        .. warning:: This can be an infinite iterator.

        '''
        from xoeuf.tools import normalize_date
        if start is None:
            start = normalize_date(self.date_from)
        return self.get_rrule_from_description().xafter(start)
Exemplo n.º 25
0
class WorkDistributionStrategy(models.Model):
    _name = 'work.distribution.strategy'

    name = fields.Char(required=True, translate=True)
    predefine = fields.Boolean()
    code = fields.Text(required=True, default='''
        #  Python code to return on result var value to set on
        #  destination field or None to no update it.
        #  E.g: result = False
        #  self, dist_model, values, candidates and **kwargs are able to use:
        #  self => active strategy (on new api).
        #  dist_model => active model name.
        #  values => python dict to passed to create method.
        #  **kwargs => python dict set on field ``other fields``.
        ''')
    other_fields = fields.Char(
        help='Python List with others variables'
             'needed on this distribution strategy.\n '
             'Eg: ["date", "qtty"]', default='[]')

    _sql_constraints = [
        ('name_unique', 'unique (name)', 'Strategy must be unique!')
    ]

    def apply(self, dist_model, values, **kwargs):
        method = (getattr(self, self.code, None)
                  if self.predefine else self.custom)
        candidates = _evaluate_domain(dist_model, values)
        if method and candidates:
            return method(dist_model, candidates, values, **kwargs)

    def uniform(self, dist_model, candidates, values, **kwargs):
        """Detect the next corresponding domain id and update values with it.

        """
        table = self.env[dist_model.model.model]._table
        if dist_model.group_field:
            query = ("SELECT %s FROM %s WHERE %s = %%s ORDER BY id DESC" %
                     (dist_model.destination_field.name, table,
                      dist_model.group_field.name))
            params = (values.get(dist_model.group_field.name, False),)
        else:
            query = ("SELECT %s FROM %s WHERE %s in %%s ORDER BY id DESC" %
                     (dist_model.destination_field.name, table,
                      dist_model.destination_field.name))
            params = (tuple(candidates.ids),)
        self.env.cr.execute(query, params=params)
        last_dist = self.env.cr.fetchone()
        last_dist = last_dist[0] if last_dist and last_dist[0] else 0
        candidates = candidates.sorted(key=lambda r: r.id)
        return next(
            (id for id in candidates.ids if not last_dist or id > last_dist),
            candidates.ids[0]
        )

    def effort(self, dist_model, candidates, values, **kwargs):
        return self._effort(dist_model, candidates, values)

    def _effort_commons(self, dist_model, **kwargs):
        model = self.env[dist_model.model.model]
        today = normalize_datetime(fields.Date.context_today(self))
        date_field = model._fields[kwargs.get('date_start')]
        to_str = date2str if isinstance(date_field, fields.Date) else dt2str
        return model, today, date_field, to_str

    def effort_month(self, dist_model, candidates, values, **kwargs):
        model, low_date, date_field, to_str = self._effort_commons(dist_model,
                                                                   **kwargs)
        DAYS = 30
        upp_date = low_date + timedelta(DAYS)
        strlow_date = to_str(low_date)
        strupp_date = to_str(upp_date)
        return self._effort(
            dist_model, candidates, values, date_field=date_field.name,
            date_start=strlow_date, date_end=strupp_date)

    def around_effort(self, dist_model, candidates, values, **kwargs):
        model, today, date_field, to_str = self._effort_commons(dist_model,
                                                                **kwargs)
        DAYS = 7
        TOTAL_DAYS = DAYS * 2 + 1
        item_date = values.get(kwargs.get('date_start'), False)
        item_date = normalize_datetime(item_date) if item_date else today
        low_date = (today
                    if item_date < today + timedelta(DAYS)
                    else item_date - timedelta(DAYS))
        upp_date = low_date + timedelta(TOTAL_DAYS)
        strlow_date = to_str(low_date)
        strupp_date = to_str(upp_date)
        return self._effort(
            dist_model, candidates, values,
            date_field=date_field.name, date_start=strlow_date,
            date_end=strupp_date
        )

    def future_effort(self, dist_model, candidates, values, **kwargs):
        model, today, date_field, to_str = self._effort_commons(dist_model,
                                                                **kwargs)
        return self._effort(
            dist_model, candidates, values,
            date_field=date_field.name, date_start=to_str(today)
        )

    def _effort(self, dist_model, candidates, values, date_field=False,
                date_start=False, date_end=False):
        model = self.env[dist_model.model.model]
        min_value = None
        next_dist = False
        group_field_name = (dist_model.group_field.name
                            if dist_model.group_field else False)
        args = ([(group_field_name, '=', values[group_field_name])]
                if group_field_name else [])
        args.extend(safe_eval(dist_model.effort_domain)
                    if dist_model.effort_domain else [])
        if date_field:
            if date_start:
                args.append((date_field, '>=', date_start))
            if date_end:
                args.append((date_field, '<=', date_end))
        for x in candidates.ids:
            current_effort = model.search_count(
                args + [(dist_model.destination_field.name, '=', x)])
            if min_value is None or min_value > current_effort:
                min_value = current_effort
                next_dist = x
        return next_dist

    def custom(self, dist_model, candidates, values, **kwargs):
        if self.code.strip().startswith('{'):
            return safe_eval(self.code or '{')
        else:
            local_dict = locals()
            local_dict.update(globals().get('__builtins__', {}))
            safe_eval(self.code, local_dict, mode='exec', nocopy=True)
            return local_dict.get('result', None)

    @api.model
    def get_fields_name(self, mixed=True):
        result = []
        method = getattr(result, 'extend' if mixed else 'append')
        for strategy in self:
            method(safe_eval(strategy.other_fields)
                   if strategy.other_fields else [])
        return set(result) if mixed else result
Exemplo n.º 26
0
class WorkDistributorWizard(models.TransientModel):
    _name = WIZARD_NAME

    name = fields.Char()
    strategy_id = fields.Many2one(
        'work.distribution.strategy', 'Strategy',
        help='Strategy of work distribution to apply to this items.')
    info = fields.Text()

    @api.model
    def fields_view_get(self, view_id=None, view_type='form',
                        toolbar=False, submenu=False):
        result = super(WorkDistributorWizard, self).fields_view_get(
            view_id=view_id, view_type=view_type, toolbar=toolbar,
            submenu=submenu)
        if view_type != 'form' or not result.get('fields', {}).get(
                'strategy_id', False):
            return result
        active_model = self.env.context.get('active_model', False)
        strategy_field = self.env.context.get('strategy_field_id', False)
        model = self.env[WORKDIST_MODELNAME].search(
            [
                ('group_field.relation', '=', active_model),
                ('strategy_field', '=', strategy_field)
            ],
            limit=1
        )
        result['fields']['strategy_id']['domain'] = [
            ('id', 'in', model.strategy_ids.ids)]
        return result

    @api.model
    def default_get(self, fields_list):
        values = super(WorkDistributorWizard, self).default_get(fields_list)
        if 'info' in fields_list:
            active_ids = self.env.context.get('active_ids', False)
            active_model = self.env.context.get('active_model', False)
            ir_model = self.env['ir.model']
            model = ir_model.search([('model', '=', active_model)])[0]
            field = self.env['ir.model.fields'].browse(
                self.env.context.get('strategy_field_id'))
            names = [_('%s\n       Strategy: %s') %
                     (item.name_get()[0][1],
                      getattr(item, field.name).name or '')
                     for item in self.env[active_model].browse(active_ids)]
            info = _("%s(s) to set work distribution strategy:\n"
                     "     * %s") % (model.name, '\n     * '.join(names))
            values.update({'info': info})
        return values

    @api.guess
    def action_config(self, *args, **Kargs):
        return {'type': 'ir.actions.act_window_close'}

    @api.model
    @api.returns('self', lambda value: value.id)
    def create(self, vals):
        res = super(WorkDistributorWizard, self).create(vals)
        active_ids = self.env.context.get('active_ids', False)
        active_model = self.env.context.get('active_model', False)
        field = self.env['ir.model.fields'].browse(
            self.env.context.get('strategy_field_id'))
        if not active_model:
            raise Warning(_('Configuration Error!'),
                          _('The is no active model defined!'))
        self.env[active_model].browse(active_ids).write(
            {field.name: res.strategy_id.id})
        return res
Exemplo n.º 27
0
class AnalyticAccount(models.Model):
    _inherit = get_modelname(AccountAnalyticAccount)

    code = fields.Char(default=lambda self: self.env['ir.sequence'].get(
        'account.analytic.account'))
Exemplo n.º 28
0
class AliasMockerMixin(models.AbstractModel):
    '''A mixin that mocks mail.alias.

    True implementations will always read the data from the 'mail.alias'
    model.  A virtual mail alias is simply made available so that other parts
    of the systems (views, most importantly) are easily made.

    A virtual alias is by all means a 'mail.alias' that gains other attributes
    via the 'alias_defaults' dictionary.

    Proposed usage::

        >>> class project_valias(models.Model):
        ... _auto = False
        ... _name = 'project.valias'
        ... _inherit = ['xopgi.mail.alias.mocker']
        ...
        ... project_id = fields.Many2one('project.project', string='Project')

    Then the 'project_id' attribute of 'project.valias' will be the key
    'project_id' in the 'alias_default' attribute of any 'mail_alias'.

    '''
    _name = 'xopgi.mail.alias.mocker'

    alias_name = fields.Char(
        'Alias',
        required=True,
        help=("The name of the email alias, e.g. 'jobs' if "
              "you want to catch emails "
              "for <*****@*****.**>"))

    alias_domain = fields.Char(
        'Domain',
        required=True,
        default=get_default_alias_domain,
        help=("The domain of the email alias, e.g. "
              "'example.my.openerp.com' if you want to catch emails "
              "for <*****@*****.**>"))

    alias_defaults = fields.Text(
        'Default Values',
        default='{}',
        help=("A Python dictionary that will be evaluated to "
              "provide default values when creating new "
              "records for this alias."))

    alias_force_thread_id = fields.Integer(
        'Record Thread ID',
        help=("Optional ID of a thread (record) to which "
              "all incoming messages will be attached, "
              "even if they did not reply to it. If set, "
              "this will disable the creation of new "
              "records completely."))

    @api.multi
    def read(self, fields=None, load='_classic_read'):
        """Read the ids from mail.alias if any of fields are not present on
        mail.alias model then search it on alias_defaults dict.

        """
        Model = self.env['mail.alias']
        extraneous = []
        for field in fields:
            if field not in Model._fields.keys():
                extraneous.append(field)
        if extraneous:
            fields = list(set(fields) - set(extraneous))
        if extraneous and 'alias_defaults' not in fields:
            default_added = True
            fields.append('alias_defaults')
        else:
            default_added = False
        result = Model.browse(self.ids).read(fields)
        if not extraneous:
            return result
        else:
            for row in result:
                defaults = str2dict(row['alias_defaults'])
                for field in extraneous:
                    row[field] = defaults.pop(field, False)
                # Restore the defaults but it will have only the keys not
                # in 'extraneous' (those upgraded to fields)
                if not default_added:
                    row['alias_defaults'] = repr(defaults)
                else:
                    row.pop('alias_defaults')
        return result

    @api.model
    def _parse_fields(self, alias_id, vals):
        '''The fields not present on mail.alias model are include on
           alias_defaults dictionary.
        '''
        Aliases = self.env['mail.alias']
        extraneous = []
        fields = set(vals.keys())
        for field in vals.keys():
            if field not in Aliases._fields.keys():
                extraneous.append(field)
        if extraneous:
            fields -= set(extraneous)
            defaults = {}
            if 'alias_defaults' in fields:
                defaults = str2dict(vals['alias_defaults'], 'Default Values')
            else:
                if alias_id:
                    record = Aliases.browse(alias_id)
                    row = record.read(['alias_defaults'])
                    defaults = str2dict(row[0]['alias_defaults'],
                                        'Default Values')
            for field in extraneous:
                value = vals.pop(field, False)
                if field in defaults:
                    defaults.update({field: value})
                else:
                    defaults.setdefault(field, value)
            vals['alias_defaults'] = repr(defaults)
        return vals
Exemplo n.º 29
0
class Message(models.Model):
    _inherit = 'mail.message'
    # Make the email_from create an index, the 'search' in the router is slow
    # without it.
    email_from = fields.Char(index=True)