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'))
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)
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.')
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
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)
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.") )
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')
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 )
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')
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)
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." )
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()
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')
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
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)
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)
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