class XopgiBoardWidget(models.Model): _name = WIDGET_MODEL_NAME _description = "Board Widget" _order = 'category, name' name = fields.Char(translate=True) category = fields.Many2one('ir.module.category') template_name = fields.Char(required=True) xml_template = fields.Text(translate=True) python_code = fields.Text() @api.multi def name_get(self): '''Returns a list with id, name of widgets or name's template ''' return [(item.id, item.name or item.template_name) for item in self] def get_widgets_dict(self): '''Returns a dictionary list that represents the widgets that the user has access to. ''' widgets = self.env[WIDGET_REL_MODEL_NAME].get_widgets() logger.debug('Widgets to show %r' % [w['name'] for w in widgets]) today = normalize_datetime(fields.Date.today(self)) for widget in widgets: self._eval_python_code(widget, today) return widgets def _eval_python_code(self, widget, today): '''Evaluate the python code of a widget ''' python_code = widget.get('python_code', '') if not python_code: return name = widget.get('name', '') env = self.env local_dict = locals() local_dict.update(globals().get('__builtins__', {})) try: logger.debug('Starting evaluation of Python code for widget %s' % name) safe_eval(python_code, local_dict, mode='exec', nocopy=True) logger.debug('Python code for widget %s evaluated sussefully.' % name) except ValueError: logger.exception( 'An error happen trying to execute the Python ' 'code for \'%s\' board widget, python code: %s', name, python_code) widget.update(local_dict.get('result', {}))
class AccountInvoice(models.Model): _inherit = 'account.invoice' advance_credits_debits_widget = fields.Text( compute='_get_advance_accounts_info_JSON') has_advancements = fields.Boolean( compute='_get_advance_accounts_info_JSON', ) @api.one def _get_advance_accounts_info_JSON(self): if self.type not in ('out_invoice', 'in_invoice'): # TODO: Deal with refunds later. self.advance_credits_debits_widget = json.dumps(False) self.has_advancements = False return if self._advancements: if self.type == 'out_invoice': title = PRECOLLECTION_TITLE elif self.type == 'in_invoice': title = PREPAYMENT_TITLE data = { 'title': title, 'content': self._advancements, 'invoice_id': self.id, 'partner_id': self.partner_id.id } self.advance_credits_debits_widget = json.dumps(data) self.has_advancements = True else: self.advance_credits_debits_widget = json.dumps(False) self.has_advancements = False
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 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 AliasMockerMixin(models.AbstractModel): '''A mixin that mocks mail.alias. True implementations will always read the data from the 'mail.alias' model. A virtual mail alias is simply made available so that other parts of the systems (views, most importantly) are easily made. A virtual alias is by all means a 'mail.alias' that gains other attributes via the 'alias_defaults' dictionary. Proposed usage:: >>> class project_valias(models.Model): ... _auto = False ... _name = 'project.valias' ... _inherit = ['xopgi.mail.alias.mocker'] ... ... project_id = fields.Many2one('project.project', string='Project') Then the 'project_id' attribute of 'project.valias' will be the key 'project_id' in the 'alias_default' attribute of any 'mail_alias'. ''' _name = 'xopgi.mail.alias.mocker' alias_name = fields.Char( 'Alias', required=True, help=("The name of the email alias, e.g. 'jobs' if " "you want to catch emails " "for <*****@*****.**>")) alias_domain = fields.Char( 'Domain', required=True, default=get_default_alias_domain, help=("The domain of the email alias, e.g. " "'example.my.openerp.com' if you want to catch emails " "for <*****@*****.**>")) alias_defaults = fields.Text( 'Default Values', default='{}', help=("A Python dictionary that will be evaluated to " "provide default values when creating new " "records for this alias.")) alias_force_thread_id = fields.Integer( 'Record Thread ID', help=("Optional ID of a thread (record) to which " "all incoming messages will be attached, " "even if they did not reply to it. If set, " "this will disable the creation of new " "records completely.")) @api.multi def read(self, fields=None, load='_classic_read'): """Read the ids from mail.alias if any of fields are not present on mail.alias model then search it on alias_defaults dict. """ Model = self.env['mail.alias'] extraneous = [] for field in fields: if field not in Model._fields.keys(): extraneous.append(field) if extraneous: fields = list(set(fields) - set(extraneous)) if extraneous and 'alias_defaults' not in fields: default_added = True fields.append('alias_defaults') else: default_added = False result = Model.browse(self.ids).read(fields) if not extraneous: return result else: for row in result: defaults = str2dict(row['alias_defaults']) for field in extraneous: row[field] = defaults.pop(field, False) # Restore the defaults but it will have only the keys not # in 'extraneous' (those upgraded to fields) if not default_added: row['alias_defaults'] = repr(defaults) else: row.pop('alias_defaults') return result @api.model def _parse_fields(self, alias_id, vals): '''The fields not present on mail.alias model are include on alias_defaults dictionary. ''' Aliases = self.env['mail.alias'] extraneous = [] fields = set(vals.keys()) for field in vals.keys(): if field not in Aliases._fields.keys(): extraneous.append(field) if extraneous: fields -= set(extraneous) defaults = {} if 'alias_defaults' in fields: defaults = str2dict(vals['alias_defaults'], 'Default Values') else: if alias_id: record = Aliases.browse(alias_id) row = record.read(['alias_defaults']) defaults = str2dict(row[0]['alias_defaults'], 'Default Values') for field in extraneous: value = vals.pop(field, False) if field in defaults: defaults.update({field: value}) else: defaults.setdefault(field, value) vals['alias_defaults'] = repr(defaults) return vals
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 WorkDistributorWizard(models.TransientModel): _name = WIZARD_NAME name = fields.Char() strategy_id = fields.Many2one( 'work.distribution.strategy', 'Strategy', help='Strategy of work distribution to apply to this items.') info = fields.Text() @api.model def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): result = super(WorkDistributorWizard, self).fields_view_get( view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) if view_type != 'form' or not result.get('fields', {}).get( 'strategy_id', False): return result active_model = self.env.context.get('active_model', False) strategy_field = self.env.context.get('strategy_field_id', False) model = self.env[WORKDIST_MODELNAME].search( [ ('group_field.relation', '=', active_model), ('strategy_field', '=', strategy_field) ], limit=1 ) result['fields']['strategy_id']['domain'] = [ ('id', 'in', model.strategy_ids.ids)] return result @api.model def default_get(self, fields_list): values = super(WorkDistributorWizard, self).default_get(fields_list) if 'info' in fields_list: active_ids = self.env.context.get('active_ids', False) active_model = self.env.context.get('active_model', False) ir_model = self.env['ir.model'] model = ir_model.search([('model', '=', active_model)])[0] field = self.env['ir.model.fields'].browse( self.env.context.get('strategy_field_id')) names = [_('%s\n Strategy: %s') % (item.name_get()[0][1], getattr(item, field.name).name or '') for item in self.env[active_model].browse(active_ids)] info = _("%s(s) to set work distribution strategy:\n" " * %s") % (model.name, '\n * '.join(names)) values.update({'info': info}) return values @api.guess def action_config(self, *args, **Kargs): return {'type': 'ir.actions.act_window_close'} @api.model @api.returns('self', lambda value: value.id) def create(self, vals): res = super(WorkDistributorWizard, self).create(vals) active_ids = self.env.context.get('active_ids', False) active_model = self.env.context.get('active_model', False) field = self.env['ir.model.fields'].browse( self.env.context.get('strategy_field_id')) if not active_model: raise Warning(_('Configuration Error!'), _('The is no active model defined!')) self.env[active_model].browse(active_ids).write( {field.name: res.strategy_id.id}) return res
class WorkDistributionStrategy(models.Model): _name = 'work.distribution.strategy' name = fields.Char(required=True, translate=True) predefine = fields.Boolean() code = fields.Text(required=True, default=''' # Python code to return on result var value to set on # destination field or None to no update it. # E.g: result = False # self, dist_model, values, candidates and **kwargs are able to use: # self => active strategy (on new api). # dist_model => active model name. # values => python dict to passed to create method. # **kwargs => python dict set on field ``other fields``. ''') other_fields = fields.Char( help='Python List with others variables' 'needed on this distribution strategy.\n ' 'Eg: ["date", "qtty"]', default='[]') _sql_constraints = [ ('name_unique', 'unique (name)', 'Strategy must be unique!') ] def apply(self, dist_model, values, **kwargs): method = (getattr(self, self.code, None) if self.predefine else self.custom) candidates = _evaluate_domain(dist_model, values) if method and candidates: return method(dist_model, candidates, values, **kwargs) def uniform(self, dist_model, candidates, values, **kwargs): """Detect the next corresponding domain id and update values with it. """ table = self.env[dist_model.model.model]._table if dist_model.group_field: query = ("SELECT %s FROM %s WHERE %s = %%s ORDER BY id DESC" % (dist_model.destination_field.name, table, dist_model.group_field.name)) params = (values.get(dist_model.group_field.name, False),) else: query = ("SELECT %s FROM %s WHERE %s in %%s ORDER BY id DESC" % (dist_model.destination_field.name, table, dist_model.destination_field.name)) params = (tuple(candidates.ids),) self.env.cr.execute(query, params=params) last_dist = self.env.cr.fetchone() last_dist = last_dist[0] if last_dist and last_dist[0] else 0 candidates = candidates.sorted(key=lambda r: r.id) return next( (id for id in candidates.ids if not last_dist or id > last_dist), candidates.ids[0] ) def effort(self, dist_model, candidates, values, **kwargs): return self._effort(dist_model, candidates, values) def _effort_commons(self, dist_model, **kwargs): model = self.env[dist_model.model.model] today = normalize_datetime(fields.Date.context_today(self)) date_field = model._fields[kwargs.get('date_start')] to_str = date2str if isinstance(date_field, fields.Date) else dt2str return model, today, date_field, to_str def effort_month(self, dist_model, candidates, values, **kwargs): model, low_date, date_field, to_str = self._effort_commons(dist_model, **kwargs) DAYS = 30 upp_date = low_date + timedelta(DAYS) strlow_date = to_str(low_date) strupp_date = to_str(upp_date) return self._effort( dist_model, candidates, values, date_field=date_field.name, date_start=strlow_date, date_end=strupp_date) def around_effort(self, dist_model, candidates, values, **kwargs): model, today, date_field, to_str = self._effort_commons(dist_model, **kwargs) DAYS = 7 TOTAL_DAYS = DAYS * 2 + 1 item_date = values.get(kwargs.get('date_start'), False) item_date = normalize_datetime(item_date) if item_date else today low_date = (today if item_date < today + timedelta(DAYS) else item_date - timedelta(DAYS)) upp_date = low_date + timedelta(TOTAL_DAYS) strlow_date = to_str(low_date) strupp_date = to_str(upp_date) return self._effort( dist_model, candidates, values, date_field=date_field.name, date_start=strlow_date, date_end=strupp_date ) def future_effort(self, dist_model, candidates, values, **kwargs): model, today, date_field, to_str = self._effort_commons(dist_model, **kwargs) return self._effort( dist_model, candidates, values, date_field=date_field.name, date_start=to_str(today) ) def _effort(self, dist_model, candidates, values, date_field=False, date_start=False, date_end=False): model = self.env[dist_model.model.model] min_value = None next_dist = False group_field_name = (dist_model.group_field.name if dist_model.group_field else False) args = ([(group_field_name, '=', values[group_field_name])] if group_field_name else []) args.extend(safe_eval(dist_model.effort_domain) if dist_model.effort_domain else []) if date_field: if date_start: args.append((date_field, '>=', date_start)) if date_end: args.append((date_field, '<=', date_end)) for x in candidates.ids: current_effort = model.search_count( args + [(dist_model.destination_field.name, '=', x)]) if min_value is None or min_value > current_effort: min_value = current_effort next_dist = x return next_dist def custom(self, dist_model, candidates, values, **kwargs): if self.code.strip().startswith('{'): return safe_eval(self.code or '{') else: local_dict = locals() local_dict.update(globals().get('__builtins__', {})) safe_eval(self.code, local_dict, mode='exec', nocopy=True) return local_dict.get('result', None) @api.model def get_fields_name(self, mixed=True): result = [] method = getattr(result, 'extend' if mixed else 'append') for strategy in self: method(safe_eval(strategy.other_fields) if strategy.other_fields else []) return set(result) if mixed else result