def run_action_code_multi(self, action, eval_context=None): safe_eval(action.sudo().code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action' if 'action' in eval_context: return eval_context['action']
def get_google_drive_config(self, res_model, res_id): ''' Function called by the js, when no google doc are yet associated with a record, with the aim to create one. It will first seek for a google.docs.config associated with the model `res_model` to find out what's the template of google doc to copy (this is usefull if you want to start with a non-empty document, a type or a name different than the default values). If no config is associated with the `res_model`, then a blank text document with a default name is created. :param res_model: the object for which the google doc is created :param ids: the list of ids of the objects for which the google doc is created. This list is supposed to have a length of 1 element only (batch processing is not supported in the code, though nothing really prevent it) :return: the config id and config name ''' # TO DO in master: fix my signature and my model if isinstance(res_model, str): res_model = self.env['ir.model'].search([('model', '=', res_model)]).id if not res_id: raise UserError(_("Creating google drive may only be done by one at a time.")) # check if a model is configured with a template configs = self.search([('model_id', '=', res_model)]) config_values = [] for config in configs.sudo(): if config.filter_id: if config.filter_id.user_id and config.filter_id.user_id.id != self.env.user.id: #Private continue domain = [('id', 'in', [res_id])] + safe_eval(config.filter_id.domain) additionnal_context = safe_eval(config.filter_id.context) google_doc_configs = self.env[config.filter_id.model_id].with_context(**additionnal_context).search(domain) if google_doc_configs: config_values.append({'id': config.id, 'name': config.name}) else: config_values.append({'id': config.id, 'name': config.name}) return config_values
def _compute_amount(self, base_amount, price_unit, quantity=1.0, product=None, partner=None): self.ensure_one() if product and product._name == 'product.template': product = product.product_variant_id if self.amount_type == 'code': company = self.env.company localdict = {'base_amount': base_amount, 'price_unit':price_unit, 'quantity': quantity, 'product':product, 'partner':partner, 'company': company} safe_eval(self.python_compute, localdict, mode="exec", nocopy=True) return localdict['result'] return super(AccountTaxPython, self)._compute_amount(base_amount, price_unit, quantity, product, partner)
def execute_code(self, code_exec): def reconciled_inv(): """ returns the list of invoices that are set as reconciled = True """ return self.env['account.move'].search([('reconciled', '=', True) ]).ids def order_columns(item, cols=None): """ This function is used to display a dictionary as a string, with its columns in the order chosen. :param item: dict :param cols: list of field names :returns: a list of tuples (fieldname: value) in a similar way that would dict.items() do except that the returned values are following the order given by cols :rtype: [(key, value)] """ if cols is None: cols = list(item) return [(col, item.get(col)) for col in cols if col in item] localdict = { 'cr': self.env.cr, 'uid': self.env.uid, 'reconciled_inv': reconciled_inv, # specific function used in different tests 'result': None, # used to store the result of the test 'column_order': None, # used to choose the display order of columns (in case you are returning a list of dict) '_': _, } safe_eval(code_exec, localdict, mode="exec", nocopy=True) result = localdict['result'] column_order = localdict.get('column_order', None) if not isinstance(result, (tuple, list, set)): result = [result] if not result: result = [_('The test was passed successfully')] else: def _format(item): if isinstance(item, dict): return ', '.join([ "%s: %s" % (tup[0], tup[1]) for tup in order_columns(item, column_order) ]) else: return item result = [_format(rec) for rec in result] return result
def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None, is_refund=False, handle_price_include=True): taxes = self.filtered(lambda r: r.amount_type != 'code') company = self.env.company if product and product._name == 'product.template': product = product.product_variant_id for tax in self.filtered(lambda r: r.amount_type == 'code'): localdict = self._context.get('tax_computation_context', {}) localdict.update({'price_unit': price_unit, 'quantity': quantity, 'product': product, 'partner': partner, 'company': company}) safe_eval(tax.python_applicable, localdict, mode="exec", nocopy=True) if localdict.get('result', False): taxes += tax return super(AccountTaxPython, taxes).compute_all(price_unit, currency, quantity, product, partner, is_refund=is_refund, handle_price_include=handle_price_include)
def format(self, percent, value, grouping=False, monetary=False): """ Format() will return the language-specific output for float values""" self.ensure_one() if percent[0] != '%': raise ValueError( _("format() must be given exactly one %char format specifier")) formatted = percent % value # floats and decimal ints need special action! if grouping: lang_grouping, thousands_sep, decimal_point = self._data_get( monetary) eval_lang_grouping = safe_eval(lang_grouping) if percent[-1] in 'eEfFgG': parts = formatted.split('.') parts[0] = intersperse(parts[0], eval_lang_grouping, thousands_sep)[0] formatted = decimal_point.join(parts) elif percent[-1] in 'diu': formatted = intersperse(formatted, eval_lang_grouping, thousands_sep)[0] return formatted
def _is_valid_partner(self, partner): if self.rule_partners_domain: domain = safe_eval( self.rule_partners_domain) + [('id', '=', partner.id)] return bool(self.env['res.partner'].search_count(domain)) else: return True
def action_launch(self): """ Launch Action of Wizard""" self.ensure_one() self.write({'state': 'done'}) # Load action action_type = self.action_id.type action = self.env[action_type].browse(self.action_id.id) result = action.read()[0] if action_type != 'ir.actions.act_window': return result result.setdefault('context', '{}') # Open a specific record when res_id is provided in the context ctx = safe_eval(result['context'], {'user': self.env.user}) if ctx.get('res_id'): result['res_id'] = ctx.pop('res_id') # disable log for automatic wizards ctx['disable_log'] = True result['context'] = ctx return result
def action_view_task(self): self.ensure_one() list_view_id = self.env.ref('project.view_task_tree2').id form_view_id = self.env.ref('project.view_task_form2').id action = {'type': 'ir.actions.act_window_close'} task_projects = self.tasks_ids.mapped('project_id') if len(task_projects) == 1 and len( self.tasks_ids ) > 1: # redirect to task of the project (with kanban stage, ...) action = self.with_context(active_id=task_projects.id).env.ref( 'project.act_project_project_2_project_task_all').read()[0] if action.get('context'): eval_context = self.env[ 'ir.actions.actions']._get_eval_context() eval_context.update({'active_id': task_projects.id}) action['context'] = safe_eval(action['context'], eval_context) else: action = self.env.ref('project.action_view_task').read()[0] action['context'] = { } # erase default context to avoid default filter if len(self.tasks_ids) > 1: # cross project kanban task action['views'] = [[False, 'kanban'], [list_view_id, 'tree'], [form_view_id, 'form'], [False, 'graph'], [False, 'calendar'], [False, 'pivot']] elif len(self.tasks_ids) == 1: # single task -> form view action['views'] = [(form_view_id, 'form')] action['res_id'] = self.tasks_ids.id # filter on the task of the current SO action.setdefault('context', {}) action['context'].update({'search_default_sale_order_id': self.id}) return action
def _filter_post_export_domain(self, records): """ Filter the records that satisfy the postcondition of action ``self``. """ if self.filter_domain and records: domain = [('id', 'in', records.ids)] + safe_eval(self.filter_domain, self._get_eval_context()) return records.sudo().search(domain).with_env(records.env), domain else: return records, None
def generate_coupon(self): """Generates the number of coupons entered in wizard field nbr_coupons """ program = self.env['sale.coupon.program'].browse( self.env.context.get('active_id')) vals = {'program_id': program.id} if self.generation_type == 'nbr_coupon' and self.nbr_coupons > 0: for count in range(0, self.nbr_coupons): self.env['sale.coupon'].create(vals) if self.generation_type == 'nbr_customer' and self.partners_domain: for partner in self.env['res.partner'].search( safe_eval(self.partners_domain)): vals.update({'partner_id': partner.id}) coupon = self.env['sale.coupon'].create(vals) subject = '%s, a coupon has been generated for you' % ( partner.name) template = self.env.ref( 'sale_coupon.mail_template_sale_coupon', raise_if_not_found=False) if template: template.send_mail(coupon.id, email_values={ 'email_to': partner.email, 'email_from': self.env.user.email or '', 'subject': subject, })
def _get_recipients(self): if self.mailing_domain: domain = safe_eval(self.mailing_domain) try: res_ids = self.env[self.mailing_model_real].search(domain).ids except ValueError: res_ids = [] _logger.exception( 'Cannot get the mass mailing recipients, model: %s, domain: %s', self.mailing_model_real, domain) else: res_ids = [] domain = [('id', 'in', res_ids)] # randomly choose a fragment if self.contact_ab_pc < 100: contact_nbr = self.env[self.mailing_model_real].search_count( domain) topick = int(contact_nbr / 100.0 * self.contact_ab_pc) if self.campaign_id and self.unique_ab_testing: already_mailed = self.campaign_id._get_mailing_recipients()[ self.campaign_id.id] else: already_mailed = set([]) remaining = set(res_ids).difference(already_mailed) if topick > len(remaining): topick = len(remaining) res_ids = random.sample(remaining, topick) return res_ids
def process_attrs(expr, get, key, val): """ parse `expr` and collect field names in lhs of conditions. """ for domain in safe_eval(expr).values(): if not isinstance(domain, list): continue for arg in domain: if isinstance(arg, (tuple, list)): process_expr(str(arg[0]), get, key, expr)
def _is_valid_product(self, product): # NOTE: if you override this method, think of also overriding _get_valid_products if self.rule_products_domain: domain = safe_eval( self.rule_products_domain) + [('id', '=', product.id)] return bool(self.env['product.product'].search_count(domain)) else: return True
def get_alias_values(self): has_group_use_lead = self.env.user.has_group('crm.group_use_lead') values = super(Team, self).get_alias_values() values['alias_defaults'] = defaults = safe_eval(self.alias_defaults or "{}") defaults[ 'type'] = 'lead' if has_group_use_lead and self.use_leads else 'opportunity' defaults['team_id'] = self.id return values
def generate(self, uid, dom=None, args=None): Model = request.env[self.model].with_user(uid) # Allow to current_website_id directly in route domain args.update( current_website_id=request.env['website'].get_current_website().id) domain = safe_eval(self.domain, (args or {}).copy()) if dom: domain += dom for record in Model.search_read(domain, ['display_name']): yield {'loc': (record['id'], record['display_name'])}
def _fetch_attachment(self): """ This method will check if we have any existent attachement matching the model and res_ids and create them if not found. """ self.ensure_one() obj = self.env[self.model].browse(self.res_id) if not self.attachment_id: report = self.report_template if not report: report_name = self.env.context.get('report_name') report = self.env['ir.actions.report']._get_report_from_name( report_name) if not report: return False else: self.write({'report_template': report.id}) # report = self.env.ref('account.account_invoices') if report.print_report_name: report_name = safe_eval(report.print_report_name, {'object': obj}) elif report.attachment: report_name = safe_eval(report.attachment, {'object': obj}) else: report_name = 'Document' filename = "%s.%s" % (report_name, "pdf") pdf_bin, _ = report.with_context( snailmail_layout=not self.cover).render_qweb_pdf(self.res_id) attachment = self.env['ir.attachment'].create({ 'name': filename, 'datas': base64.b64encode(pdf_bin), 'res_model': 'snailmail.letter', 'res_id': self.id, 'type': 'binary', # override default_type from context, possibly meant for another model! }) self.write({'attachment_id': attachment.id}) return self.attachment_id
def _get_records(self): if not self.res_model: return None if self.use_active_domain: active_domain = safe_eval(self.active_domain or '[]') records = self.env[self.res_model].search(active_domain) elif self.res_id: records = self.env[self.res_model].browse(self.res_id) else: records = self.env[self.res_model].browse(literal_eval(self.res_ids or '[]')) return records
def eval_value(self, eval_context=None): result = dict.fromkeys(self.ids, False) for line in self: expr = line.value if line.evaluation_type == 'equation': expr = safe_eval(line.value, eval_context) elif line.col1.ttype in ['many2one', 'integer']: try: expr = int(line.value) except Exception: pass result[line.id] = expr return result
def _onchange_filter_id(self): """ If selected Filter has no any product the raise the warning and remove that filter :return: """ if self.filter_id: domain = safe_eval(self.filter_id.domain) domain += ['|', ('website_id', '=', None), ('website_id', '=', self.slider_id.website_id.id), ('website_published', '=', True)] product_count = self.env['product.template'].sudo().search_count(domain) if product_count < 1: self.filter_id = False raise UserError(_('Sorry! You can not set filter which is content zero product.'))
def _get_price_from_picking(self, total, weight, volume, quantity): price = 0.0 criteria_found = False price_dict = {'price': total, 'volume': volume, 'weight': weight, 'wv': volume * weight, 'quantity': quantity} for line in self.price_rule_ids: test = safe_eval(line.variable + line.operator + str(line.max_value), price_dict) if test: price = line.list_base_price + line.list_price * price_dict[line.variable_factor] criteria_found = True break if not criteria_found: raise UserError(_("No price rule matching this order; delivery cost cannot be computed.")) return price
def slider_data(self, **kwargs): """ Return the data for slider for product slider and category Slider If filter ID is not specified then return first filter slider object else it return specified slider filter :param kwargs: :return: """ slider_id = kwargs.get('slider_id', False) filter_id = kwargs.get('filter_id', False) slider_obj = request.env['slider'].sudo().search([('id', '=', int(slider_id))]) vals = {} if slider_obj: filter = slider_obj.slider_filter_ids[ 0] if not filter_id else request.env['slider.filter'].sudo( ).search([('id', '=', int(filter_id))]) if filter.filter_id.domain: domain = safe_eval(filter.filter_id.domain) domain += [ '|', ('website_id', '=', None), ('website_id', '=', request.website.id), ('website_published', '=', True) ] product = request.env['product.template'].sudo().search( domain, limit=slider_obj.slider_limit) vals = { 'slider_obj': slider_obj, 'filter_data': product, # 'active_filter_data': filter_id if filter_id else slider_obj.slider_filter_ids[0].filter_id.id, 'active_filter_data': filter_id if filter_id else slider_obj.slider_filter_ids[0].id, 'is_default': False if filter_id else True, 'show_view_all': True } tmplt_external_id = slider_obj.slider_style_id.get_external_id( ).get(slider_obj.slider_style_id.id) + "_template" tmplt = request.env['ir.ui.view'].sudo().search([ ('key', '=', tmplt_external_id) ]) if tmplt: response = http.Response(template=tmplt_external_id, qcontext=vals) return response.render() else: return False
def action_view_all_rating(self): """ return the action to see all the rating of the project, and activate default filters """ if self.portal_show_rating: return { 'type': 'ir.actions.act_url', 'name': "Redirect to the Website Projcet Rating Page", 'target': 'self', 'url': "/project/rating/%s" % (self.id,) } action = self.env['ir.actions.act_window'].for_xml_id('project', 'rating_rating_action_view_project_rating') action['name'] = _('Ratings of %s') % (self.name,) action_context = safe_eval(action['context']) if action['context'] else {} action_context.update(self._context) action_context['search_default_parent_res_name'] = self.name action_context.pop('group_by', None) return dict(action, context=action_context)
def read(self, fields=None, load='_classic_read'): """ call the method get_empty_list_help of the model and set the window action help message """ result = super(IrActionsActWindow, self).read(fields, load=load) if not fields or 'help' in fields: for values in result: model = values.get('res_model') if model in self.env: eval_ctx = dict(self.env.context) try: ctx = safe_eval(values.get('context', '{}'), eval_ctx) except: ctx = {} values['help'] = self.with_context( **ctx).env[model].get_empty_list_help( values.get('help', '')) return result
def test_05_safe_eval_forbiddon(self): """ Try forbidden expressions in safe_eval to verify they are not allowed""" # no forbidden builtin expression with self.assertRaises(ValueError): safe_eval('open("/etc/passwd","r")') # no forbidden opcodes with self.assertRaises(ValueError): safe_eval("import harpiya", mode="exec") # no dunder with self.assertRaises(NameError): safe_eval("self.__name__", {'self': self}, mode="exec")
def _check(self, automatic=False, use_new_cursor=False): """ This Function is called by scheduler. """ if '__action_done' not in self._context: self = self.with_context(__action_done={}) # retrieve all the action rules to run based on a timed condition eval_context = self._get_eval_context() for action in self.with_context(active_test=True).search([('trigger', '=', 'on_time')]): last_run = fields.Datetime.from_string(action.last_run) or datetime.datetime.utcfromtimestamp(0) # retrieve all the records that satisfy the action's condition domain = [] context = dict(self._context) if action.filter_domain: domain = safe_eval(action.filter_domain, eval_context) records = self.env[action.model_name].with_context(context).search(domain) # determine when action should occur for the records if action.trg_date_id.name == 'date_action_last' and 'create_date' in records._fields: get_record_dt = lambda record: record[action.trg_date_id.name] or record.create_date else: get_record_dt = lambda record: record[action.trg_date_id.name] # process action on the records that should be executed now = datetime.datetime.now() for record in records: record_dt = get_record_dt(record) if not record_dt: continue action_dt = self._check_delay(action, record, record_dt) if last_run <= action_dt < now: try: action._process(record) except Exception: _logger.error(traceback.format_exc()) action.write({'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) if automatic: # auto-commit for batch processing self._cr.commit()
def _check_domain_validity(self): # take admin as should always be present for definition in self: if definition.computation_mode not in ('count', 'sum'): continue Obj = self.env[definition.model_id.model] try: domain = safe_eval( definition.domain, {'user': self.env.user.with_user(self.env.user)}) # dummy search to make sure the domain is valid Obj.search_count(domain) except (ValueError, SyntaxError) as e: msg = e if isinstance(e, SyntaxError): msg = (e.msg + '\n' + e.text) raise exceptions.UserError( _("The domain for the definition %s seems incorrect, please check it.\n\n%s" ) % (definition.name, msg)) return True
def _get_failing(self, for_records, mode='read'): """ Returns the rules for the mode for the current user which fail on the specified records. Can return any global rule and/or all local rules (since local rules are OR-ed together, the entire group succeeds or fails, while global rules get AND-ed and can each fail) """ Model = for_records.browse(()).sudo() eval_context = self._eval_context() all_rules = self._get_rules(Model._name, mode=mode).sudo() # first check if the group rules fail for any record (aka if # searching on (records, group_rules) filters out some of the records) group_rules = all_rules.filtered( lambda r: r.groups and r.groups & self.env.user.groups_id) group_domains = expression.OR([ safe_eval(r.domain_force, eval_context) if r.domain_force else [] for r in group_rules ]) # if all records get returned, the group rules are not failing if Model.search_count( expression.AND([[('id', 'in', for_records.ids)], group_domains])) == len(for_records): group_rules = self.browse(()) # failing rules are previously selected group rules or any failing global rule def is_failing(r, ids=for_records.ids): dom = safe_eval(r.domain_force, eval_context) if r.domain_force else [] return Model.search_count( expression.AND([[('id', 'in', ids)], expression.normalize_domain(dom)])) < len(ids) return all_rules.filtered(lambda r: r in group_rules or (not r.groups and is_failing(r))).with_user( self.env.user)
def get_action(self): """Get the ir.action related to update the goal In case of a manual goal, should return a wizard to update the value :return: action description in a dictionary """ if self.definition_id.action_id: # open a the action linked to the goal action = self.definition_id.action_id.read()[0] if self.definition_id.res_id_field: current_user = self.env.user.with_user(self.env.user) action['res_id'] = safe_eval(self.definition_id.res_id_field, {'user': current_user}) # if one element to display, should see it in form mode if possible action['views'] = [(view_id, mode) for (view_id, mode) in action['views'] if mode == 'form'] or action['views'] return action if self.computation_mode == 'manually': # open a wizard window to update the value manually action = { 'name': _("Update %s") % self.definition_id.name, 'id': self.id, 'type': 'ir.actions.act_window', 'views': [[False, 'form']], 'target': 'new', 'context': { 'default_goal_id': self.id, 'default_current': self.current }, 'res_model': 'gamification.goal.wizard' } return action return False
def action_your_pipeline(self): action = self.env.ref('crm.crm_lead_action_pipeline').read()[0] user_team_id = self.env.user.sale_team_id.id if user_team_id: # To ensure that the team is readable in multi company user_team_id = self.search([('id', '=', user_team_id)], limit=1).id else: user_team_id = self.search([], limit=1).id action['help'] = _( """<p class='o_view_nocontent_smiling_face'>Add new opportunities</p><p> Looks like you are not a member of a Sales Team. You should add yourself as a member of one of the Sales Team. </p>""") if user_team_id: action[ 'help'] += "<p>As you don't belong to any Sales Team, Harpiya opens the first one by default.</p>" action_context = safe_eval(action['context'], {'uid': self.env.uid}) if user_team_id: action_context['default_team_id'] = user_team_id action['context'] = action_context return action