Ejemplo n.º 1
0
class Project(models.Model):
    _inherit = models.get_modelname(BaseProject)

    state = fields.Selection(
        selection=[('draft', 'New'), ('open', 'In Progress'),
                   ('cancelled', 'Cancelled'), ('pending', 'Pending'),
                   ('close', 'Closed')],
        string='Status',
        required=True,
        copy=False,
        default='open',
        track_visibility='onchange',
    )

    def set_done(self):
        return self.write(dict(state='close'))

    def set_cancel(self):
        return self.write(dict(state='cancelled'))

    def set_pending(self):
        return self.write(dict(state='pending'))

    def set_open(self):
        return self.write(dict(state='open'))
Ejemplo n.º 2
0
class CommonThreadWizard(models.TransientModel):
    _name = 'common.thread.wizard'

    model_id = fields.Selection(
        string='Model',
        selection=lambda self: get_model_selection(self),
        required=True)
    view = fields.Many2one('xopgi.selectable.view')
    views_count = fields.Integer(compute='_get_views_count')

    @api.onchange('model_id')
    @api.depends('model_id')
    def _get_views_count(self):
        selectable_view = self.env['xopgi.selectable.view']
        for wizard in self:
            if wizard.model_id:
                conf_views = selectable_view.get_views(self.model_id)
                views_count = len(conf_views)
                self.views_count = views_count
                if views_count >= 1:
                    self.view = conf_views[0]
            else:
                self.views_count = 0
                self.view = False

    def get_thread_action(self, res_id=None):
        """ Returns the action that shows the form of this model
        """
        return self.view.get_action(model=self.model_id, res_id=res_id)
Ejemplo n.º 3
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.')
Ejemplo n.º 4
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
Ejemplo n.º 5
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)
Ejemplo n.º 6
0
class MrpBomLine(models.Model):
    _inherit = models.get_modelname(BomLines)

    type = fields.Selection(
        [('normal', 'Manufacture this product'),
         ('phantom', 'Ship this product as a set of components (kit)')],
        string='BoM Type',
        default='normal',
        required=True,
        help=("Kit (Phantom): When processing a sales order for this product, "
              "the delivery order will contain the raw materials,"
              "instead of the finished product.")
    )
Ejemplo n.º 7
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')
Ejemplo n.º 8
0
class AnalyticAccount(models.Model):
    _inherit = models.get_modelname(AccountAnalyticAccount)

    state = fields.Selection(
        selection=[('draft', 'New'),
                   ('open', 'In Progress'),
                   ('pending', 'To Renew'),
                   ('close', 'Closed'),
                   ('cancelled', 'Cancelled')],
        string='Status',
        required=True,
        default='draft',
        track_visibility='onchange',
        copy=False
    )
Ejemplo n.º 9
0
class crm_valias(models.Model):
    _name = 'crm.valias'
    _inherit = ['xopgi.mail.alias.mocker']

    type = fields.Selection(
        [('lead', 'Lead'), ('opportunity', 'Opportunity')],
        'Type',
        index=True,
        help="Type of object to create by incoming messages.")

    if MAJOR_ODOO_VERSION < 9:
        section_id = fields.Many2one(TEAM_MODEL_NAME, string='Sale Team')
    else:
        team_id = fields.Many2one(TEAM_MODEL_NAME, string='Sale Team')

    user_id = fields.Many2one('res.users', string='Team Leader')
Ejemplo n.º 10
0
class ProductPricelist(models.Model):
    _inherit = get_modelname(BasePricelist)
    _name = _inherit

    @api.model
    def _pricelist_type_get(self):
        pricelists = self.env['product.pricelist.type'].search([],
                                                               order='name')
        return [(p.key, p.name) for p in pricelists]

    @api.model
    def _pricelist_type_default(self):
        pricelist_type1 = self.env['product.pricelist.type'].search([],
                                                                    limit=1)
        return pricelist_type1 and pricelist_type1.key or None

    type = fields.Selection(selection=_pricelist_type_get,
                            string='Pricelist Type',
                            default=_pricelist_type_default)
Ejemplo n.º 11
0
class ProductPricelistItem(models.Model):
    _inherit = get_modelname(BasePricelistItem)
    _name = _inherit

    @api.model
    def _get_price_field_get(self):
        PriceType = self.env['product.price.type']
        types = PriceType.search([])
        result = []
        for line in types:
            result.append((line.field, line.name))
        result.append(('pricelist', _('Other Pricelist')))
        return result

    base = fields.Selection(
        selection="_get_price_field_get",
        string="Based on",
        required=True,
        default="list_price",
        help="Base price for computation."
    )
Ejemplo n.º 12
0
class PurchaseOrder(models.Model):
    _inherit = models.get_modelname(PurchaseOrder)

    bid_date = fields.Datetime(
        string='Bid Received On',
        readonly=True,
        help="Date on which the bid was received"
    )

    state = fields.Selection([
        ('draft', 'Draft PO'),
        ('sent', 'RFQ Sent'),
        ('bid', 'Bid Received'),
        ('to approve', 'To Approve'),
        ('purchase', 'Purchase Order'),
        ('done', 'Done'),
        ('cancel', 'Cancelled')])

    incoterm_id = fields.Many2one(
        'stock.incoterms',
        string="Incoterm",
        help=("International Commercial Terms are a series of predefined "
              "commercial terms used in international transactions.")
    )

    @api.multi
    def accion_bid_received(self):
        return self.write({'state': 'bid', 'bid_date': fields.Datetime.now()})

    @api.multi
    def button_confirm(self):
        approve = self.browse()
        for order in self:
            if order.state in ['draft', 'sent', 'bid']:
                approve |= order
                # Odoo 9+ skips orders that are not in 'draft', or 'sent'.  So
                # we trick it to process orders in state 'bid'.
                if order.state == 'bid':
                    order.state = 'sent'
        return super(PurchaseOrder, approve).button_confirm()
Ejemplo n.º 13
0
class project_valias(models.Model):
    _name = 'project.valias'
    _inherit = ['xopgi.mail.alias.mocker']

    @api.model
    def _get_models(self):
        models = _get_model_ids(self)
        return models.name_get()

    alias_model_id = fields.Selection(
        _get_models,
        'Aliased Model',
        required=True,
        help=("The model (OpenERP Document Kind) to "
              "which this alias corresponds. Any "
              "incoming email that does not reply to an "
              "existing record will cause the creation "
              "of a new record of this model "
              "(e.g. a Project Task)"))

    project_id = fields.Many2one('project.project', string='Project')
    user_id = fields.Many2one('res.users', string='Project Manager')
Ejemplo n.º 14
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
Ejemplo n.º 15
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)
Ejemplo n.º 16
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)
Ejemplo 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