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')
class WorkDistributionSettings(models.TransientModel): _name = 'work.distribution.settings' _inherit = 'res.config.settings' @api.multi def _set_work_distribution(self): models = self.mapped('models_ids') models.unlink_rest() self.invalidate_cache() def _compute_work_distribution_models(self): for settings in self: settings.models_ids = self._get_work_distribution_models() @api.model def _get_work_distribution_models(self): return self.env[WORKDIST_MODELNAME].search([]).ids models_ids = fields.Many2many( WORKDIST_MODELNAME, string='Models', compute=_compute_work_distribution_models, inverse=_set_work_distribution, default=_get_work_distribution_models ) @api.multi def install(self): self._set_work_distribution() return RELOAD_UI
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')
class AccountPartnerLedger(models.TransientModel): _inherit = "account.report.partner.ledger" partner_ids = fields.Many2many(comodel_name='res.partner', string='Partners') def _print_report(self, data): if self.partner_ids: data['form'].update(partner_ids=self.partner_ids.ids) return super(AccountPartnerLedger, self)._print_report(data)
class MailConfig(models.TransientModel): _inherit = 'base.config.settings' default_views = fields.Many2many( 'xopgi.selectable.view', default=lambda self: self.env['xopgi.selectable.view'].search([])) @api.multi def execute(self): result = super(MailConfig, self).execute() self.default_views.search([('id', 'not in', self.default_views.ids) ]).unlink() return result
class ResPartner(models.Model): _inherit = 'res.partner' classifications = fields.Many2many('res.partner.classification', 'res_partner_classification_rel', 'partner_id', 'classification_id') employee = fields.Boolean( compute='_get_classification', inverse=lambda self: self._set_classification('employee'), store=True) customer = fields.Boolean( compute='_get_classification', inverse=lambda self: self._set_classification('customer'), store=True) supplier = fields.Boolean( compute='_get_classification', inverse=lambda self: self._set_classification('supplier'), store=True) @api.one @api.depends('classifications') def _get_classification(self): classes_ids = self.classifications.ids ref = lambda xml_id: self.env.ref(xml_id, raise_if_not_found=False) for attr, classname in ATTR2CLASSIFIER_MAPPING.items(): classific = ref(classname) setattr(self, attr, classific.id in classes_ids) def _set_classification(self, attrname): ref = lambda xml_id: self.env.ref(xml_id, raise_if_not_found=False) classific = ref(ATTR2CLASSIFIER_MAPPING.get(attrname, 'null')) if classific: if getattr(self, attrname, False): self.write({'classifications': [LINK_RELATED(classific.id)]}) else: self.write({'classifications': [FORGET_RELATED(classific.id)]}) @api.model def migrate_classifications(self): """This is needed because a migration not occur on module installation. """ ref = lambda xml_id: self.env.ref(xml_id, raise_if_not_found=False) partners = self.with_context(active_test=False) for attr, classname in ATTR2CLASSIFIER_MAPPING.items(): partners = partners.search([(attr, '=', True)]) classification = ref(classname) if partners and classification: partners.write( {'classifications': [LINK_RELATED(classification.id)]})
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')
class MoveLine(models.Model): _inherit = 'account.move.line' @api.multi @api.depends('move_id.line_ids') def _compute_counterpart_accounts(self): for line in self: line.counterpart_account_ids = line.move_id.mapped( 'line_ids.account_id').filtered( lambda account: account != line.account_id) def _search_counterpart_accounts(self, operator, value): # XXX: This will search in any of the accounts (included the line's # account). This is because I can't express the predicate of being # not equal to the line's account_id. return [('move_id.line_ids.account_id', operator, value)] counterpart_account_ids = fields.Many2many( 'account.account', string='Counterpart accounts', compute=_compute_counterpart_accounts, search=_search_counterpart_accounts, )
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 object_merger_settings(models.Model): _name = 'object.merger.settings' _inherit = 'res.config.settings' def _get_default_object_merger_models(self): return IrModel.sudo().search([('object_merger_model', '=', True)]) models_ids = fields.Many2many('ir.model', 'object_merger_settings_model_rel', 'object_merger_id', 'model_id', string='Models', domain=_NON_TRANSIENT_MODEL_DOMAIN, context={'object_merger_settings': True}, default=_get_default_object_merger_models) informal_reference_ids = fields.Many2many( 'informal.reference', 'object_merger_informal_reference_rel', 'object_merger_id', 'informal_reference_id', 'Informal References', help='Know cases of pair of field represented a ' 'reference to an specific object from ' 'especific model.', default=lambda s: s.env['informal.reference'].search([])) @api.multi def update_field(self): model_obj = self.env['ir.model'] action_obj = self.env['ir.actions.act_window'] value_obj = self.env['ir.values'] field_obj = self.env['ir.model.fields'] template_action = self.sudo().env.ref( 'xopgi_object_merger.act_merge_template', raise_if_not_found=False) if not self: return False # Unlink Previous Actions unlink_records = action_obj.search([('res_model', '=', 'object.merger'), ('id', '!=', template_action.id)]) for unlink_id in unlink_records: unlink_id.unlink() un_val = value_obj.search([ ('value', '=', "ir.actions.act_window," + str(unlink_id.id)) ]) un_val.unlink() # Put all models which were selected before back to not an object_merger model_not_merge = model_obj.search([ ('id', 'not in', self.models_ids.ids), ('object_merger_model', '=', True) ]) model_not_merge.write({'object_merger_model': False}) # Put all models which are selected to be an object_merger self.models_ids.write({'object_merger_model': True}) object_merger = self.models_ids.search( [('model', '=', 'object.merger')], limit=1) for model in self.models_ids: field_name = 'x_' + model.model.replace('.', '_') + '_id' name = "Merge" default = { 'src_model': model.model, 'context': { 'field_to_read': field_name } } temp_acc = template_action.copy(default=default) value_obj.create({ 'name': name, 'model': model.model, 'key2': 'client_action_multi', 'value': "ir.actions.act_window," + str(temp_acc.id) }) found = field_obj.search([('name', '=', field_name), ('model', '=', 'object.merger')]) if not found: field_data = { 'model': 'object.merger', 'model_id': object_merger.id, 'name': field_name, 'relation': model.model, 'field_description': _('To keep'), 'state': 'manual', 'ttype': 'many2one', } field_obj.sudo().create(field_data) return True @api.multi def install(self): self.update_field() return { 'type': 'ir.actions.client', 'tag': 'reload', }
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()
class AccountInvoice(models.Model): _inherit = 'account.invoice' partner_tags = fields.Many2many(related='partner_id.category_id', string="Partner's Tags")
class CreateInvoiceWizard(models.TransientModel): _name = "xopgi.salesperson_wizard" _description = "Create supplier invoice in form of commission" def _get_salesperson(self): return self.env["account.analytic.account"].browse( self._context.get("active_id")).primary_salesperson_id primary_salesperson_id = fields.Many2one("res.users", string="Salesperson", required=True, default=_get_salesperson) analytic_account_ids = fields.Many2many("account.analytic.account", string="Operations", required=True, domain=[("state", "=", "close"), ("supplier_invoice_id", "=", False)]) @api.multi def onchange(self, values, field_name, field_onchange): # Sanitize the result of the onchange. Odoo and the Web Client seems # not to look eye to eye when dealing with x2many. Odoo sends # UNLINKALL and the UPDATE commands. I thought that if Odoo would # send the LINK_RELATED before, it would fix all the issues. However, # it does not work. # # This hack solves this for this specific case: Replace all # UPDATE_RELATED by LINK_RELATED. We can do this at this point, # because we're no editing any of the values of the accounts. def correct(cmd): if cmd[COMMAND_INDEX] == UPDATE_RELATED: return LINK_RELATED(cmd[ID_INDEX]) else: return cmd result = super(CreateInvoiceWizard, self).onchange(values, field_name, field_onchange) value = result.setdefault('value', {}) links = value.setdefault('analytic_account_ids', []) if links and links[0] == (UNLINKALL_RELATED, ): links[1:] = [correct(cmd) for cmd in links[1:]] return result @api.onchange('primary_salesperson_id') def _update_accounts(self): accounts = self.env["account.analytic.account"].search([ ("primary_salesperson_id.id", "=", self.primary_salesperson_id.id), ("state", "=", "close"), ("supplier_invoice_id", "=", False) ]) self.analytic_account_ids = accounts @api.requires_singleton def generate_supplier_invoice(self): partner = self.primary_salesperson_id.partner_id account = partner.property_account_payable_id invoice = self._supplier_invoice_generator(partner, account, self.analytic_account_ids) return invoice.get_formview_action() @api.model def enqueue_generate_supplier_invoice_cron(self): from xoeuf.odoo.jobs import Deferred # Avoid performing a big computation within the WorkerCron process # which is under tight timeout restriction (because the same # restriction applies to Cron and HTTP workers). Jobs are allowed to # take longer. Deferred(self.generate_supplier_invoice_cron) @api.model def generate_supplier_invoice_cron(self): Account = self.env["account.analytic.account"] accounts = Account.search([('state', '=', 'close'), ('supplier_invoice_id', '=', False), ('primary_salesperson_id', '!=', False)], order='primary_salesperson_id') for user, partner_accounts in groupby( accounts, attrgetter('primary_salesperson_id')): self._supplier_invoice_generator( user.partner_id, user.property_account_payable_id, reduce(operator.or_, partner_accounts, Account)) def _supplier_invoice_generator(self, partner, account, analytic_account_ids): d = date.today() company = self._context.get('company_id', self.env.user.company_id) journal = self.env['account.journal'].search( [('type', '=', 'purchase'), ('company_id', '=', company.id)], limit=1) # The context allows Odoo to compute the defaults we're missing here. result = Invoice.sudo().with_context(journal_id=journal.id).create( dict( partner_id=partner.id, account_id=account.id, type='in_invoice', # supplier invoice name=_(u"Commission. ") + safe_decode(d.strftime("%B")) + u" / " + safe_decode(partner.name), origin=_(u"Commission. ") + safe_decode(d.strftime("%B")) + u" / " + safe_decode(partner.name), journal_id=journal.id, invoice_line_ids=[ CREATE_RELATED(quantity=1, account_analytic_id=analytic_account.id, name=_("Operation") + safe_decode(analytic_account.name), price_unit=analytic_account.commission) for analytic_account in analytic_account_ids ])) analytic_account_ids.write(dict(supplier_invoice_id=result.id)) followers = [partner.id] employees = self.env["hr.employee"].search([("user_id.partner_id", "=", partner.id)]) for employee in employees: if employee.parent_id: manager = employee.parent_id.user_id.partner_id if any(manager): followers.append(manager.id) result.message_subscribe(partner_ids=followers) return result
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
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
class MoveMessageWizard(models.TransientModel): _name = 'move.message.wizard' _inherit = 'common.thread.wizard' thread_id = fields.Reference( string='Destination Mail Thread', size=128, selection=lambda self: get_model_selection(self), required=True) message_ids = fields.Many2many('mail.message', 'message_move_rel', 'move_id', 'message_id', 'Messages', readonly=True) leave_msg = fields.Boolean( 'Preserve original messages', help="Check for no remove message from original thread.", default=True) @api.model def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): if self._uid != SUPERUSER_ID and not self.env['res.users'].has_group( 'xopgi_mail_move_message.group_move_message'): raise AccessError(_('Access denied.')) result = super(MoveMessageWizard, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) return result @api.onchange('thread_id') @api.depends('thread_id') def onchange_thread_id(self): if self.thread_id: self.model_id = self.thread_id._name @api.model def default_get(self, fields_list): values = super(MoveMessageWizard, self).default_get(fields_list) if 'message_ids' in fields_list: values['message_ids'] = self._context.get('active_ids', []) return values @api.multi def confirm(self): '''Create a new mail thread, remove original message if not leave_msg and open new thread on edit form. ''' self.message_ids.do_move_message(self.thread_id._name, self.thread_id.id, self.leave_msg) try: # Can I read the thread model? If not, the message it's there but # I cannot be redirected to the model's view. self.thread_id.read([]) except AccessError: return RELOAD_UI else: # Returns an ir.actions.act_window given an res_id return self.get_thread_action(res_id=self.thread_id.id)