def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None): taxes = self.filtered(lambda r: r.amount_type != 'code') company = self.env.user.company_id for tax in self.filtered(lambda r: r.amount_type == 'code'): localdict = { '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)
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 _compute_rule(self, localdict): """ :param localdict: dictionary containing the environement in which to compute the rule :return: returns a tuple build as the base/amount computed, the quantity and the rate :rtype: (float, float, float) """ self.ensure_one() if self.amount_select == 'fix': try: return self.amount_fix, float(safe_eval(self.quantity, localdict)), 100.0 except: raise UserError(_('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code)) elif self.amount_select == 'percentage': try: return (float(safe_eval(self.amount_percentage_base, localdict)), float(safe_eval(self.quantity, localdict)), self.amount_percentage) except: raise UserError(_('Wrong percentage base or quantity defined for salary rule %s (%s).') % (self.name, self.code)) else: try: safe_eval(self.amount_python_compute, localdict, mode='exec', nocopy=True) return float(localdict['result']), 'result_qty' in localdict and localdict['result_qty'] or 1.0, 'result_rate' in localdict and localdict['result_rate'] or 100.0 except: raise UserError(_('Wrong python code defined for salary rule %s (%s).') % (self.name, self.code))
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 ''' 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 transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False): if node.get('attrs'): modifiers.update(safe_eval(node.get('attrs'))) if node.get('states'): if 'invisible' in modifiers and isinstance(modifiers['invisible'], list): # TODO combine with AND or OR, use implicit AND for now. modifiers['invisible'].append( ('state', 'not in', node.get('states').split(','))) else: modifiers['invisible'] = [('state', 'not in', node.get('states').split(','))] for a in ('invisible', 'readonly', 'required'): if node.get(a): v = bool(safe_eval(node.get(a), {'context': context or {}})) if in_tree_view and a == 'invisible': # Invisible in a tree view has a specific meaning, make it a # new key in the modifiers attribute. modifiers['column_invisible'] = v elif v or (a not in modifiers or not isinstance(modifiers[a], list)): # Don't set the attribute to False if a dynamic value was # provided (i.e. a domain from attrs or states). modifiers[a] = v
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 _satisfy_condition(self, localdict): """ @param contract_id: id of hr.contract to be tested @return: returns True if the given rule match the condition for the given contract. Return False otherwise. """ self.ensure_one() if self.condition_select == 'none': return True elif self.condition_select == 'range': try: result = safe_eval(self.condition_range, localdict) return self.condition_range_min <= result and result <= self.condition_range_max or False except: raise UserError( _('Wrong range condition defined for salary rule %s (%s).') % (self.name, self.code)) else: # python code try: safe_eval(self.condition_python, localdict, mode='exec', nocopy=True) return 'result' in localdict and localdict['result'] or False except: raise UserError( _('Wrong python condition defined for salary rule %s (%s).' ) % (self.name, self.code))
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 execute_code(self, code_exec): def reconciled_inv(): """ returns the list of invoices that are set as reconciled = True """ return self.env['account.invoice'].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 _eval(self, code, locals_dict=None, tsession=None): """Prepare data for rendering""" _logger.debug("_eval locals_dict: %s" % locals_dict) t0 = time.time() locals_dict = self._update_locals_dict(locals_dict, tsession) globals_dict = self._get_globals_dict() if code: safe_eval(code, globals_dict, locals_dict, mode="exec", nocopy=True) eval_time = time.time() - t0 _logger.debug('Eval in %.2fs \nlocals_dict:\n%s\nCode:\n%s\n', eval_time, locals_dict, code) return locals_dict
def get_action_domain(self, action): used_filters = [] eval_vars = {'uid': self.env.uid} filters = self.env['ir.filters'].sudo().get_filters(action.res_model, action.id) personal_filter = None # get_default_filter function from js: for f in filters: if f['user_id'] and f['is_default']: personal_filter = f break if not personal_filter: for f in filters: if not f['user_id'] and f['is_default']: personal_filter = f break if personal_filter: personal_filter['string'] = personal_filter['name'] default_domains = [personal_filter['domain']] used_filters = [personal_filter] else: # find filter from context, i.e. the same as UI works default_domains = [] # parse search view search_view = self.env[action.res_model].sudo().fields_view_get(view_id=action.search_view_id.id, view_type='search')['arch'] search_view_filters = {} for el in etree.fromstring(search_view): if el.tag != 'filter': continue f = el.attrib search_view_filters[f['name']] = f # proceed context action_context = safe_eval(action.context, eval_vars) for k, v in action_context.items(): if not k.startswith('search_default'): continue filter_name = k.split('search_default_')[1] filter = search_view_filters[filter_name] default_domains.append(filter['domain']) used_filters.append(filter) # eval and combine default_domains into one domain = [] for d in default_domains: domain += safe_eval(d, eval_vars) return domain, used_filters
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 } if self.free_over and total >= self.amount: return 0 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 action_your_pipeline(self): action = self.env.ref('crm.crm_lead_opportunities_tree_view').read()[0] user_team_id = self.env.user.sale_team_id.id if not user_team_id: user_team_id = self.search([], limit=1).id action[ 'help'] = """<p class='oe_view_nocontent_create'>Click here to add new opportunities</p><p> Looks like you are not a member of a sales channel. You should add yourself as a member of one of the sales channel. </p>""" if user_team_id: action[ 'help'] += "<p>As you don't belong to any sales channel, Flectra 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 tree_view_id = self.env.ref('crm.crm_case_tree_view_oppor').id form_view_id = self.env.ref('crm.crm_case_form_view_oppor').id kanb_view_id = self.env.ref('crm.crm_case_kanban_view_leads').id action['views'] = [[kanb_view_id, 'kanban'], [tree_view_id, 'tree'], [form_view_id, 'form'], [False, 'graph'], [False, 'calendar'], [False, 'pivot']] action['context'] = action_context return action
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 assemble_tasks(self, docids, data, report, ctx): code = report.out_format.code result = self.single_report(docids, data, report, ctx) print_report_name = 'report' if report.print_report_name and not len(docids) > 1: obj = self.env[report.model].browse(docids) print_report_name = safe_eval( report.print_report_name, {'object': obj, 'time': time}) if report.in_format == code: filename = '%s.%s' % ( print_report_name, mime_dict[report.in_format]) return result[0], result[1], filename else: try: self.get_docs_conn() result = self._generate_doc(result[0], report) filename = '%s.%s' % ( print_report_name, mime_dict[report.out_format.code]) return result, mime_dict[code], filename except Exception as e: _logger.exception(_("Aeroo DOCS error!\n%s") % str(e)) if report.disable_fallback: result = None _logger.exception(e[0]) raise ConnectionError(_('Could not connect Aeroo DOCS!')) # only if fallback filename = '%s.%s' % (print_report_name, mime_dict[report.in_format]) return result[0], result[1], filename
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['ir.actions.actions']._for_xml_id( 'project.act_project_project_2_project_task_all') action['domain'] = [('id', 'in', self.tasks_ids.ids)] 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) action_context.update(eval_context) action['context'] = action_context else: action = self.env["ir.actions.actions"]._for_xml_id("project.action_view_task") 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 _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_alias_values(self): self.ensure_one() values = super(QcTeam, self).get_alias_values() values['alias_defaults'] = defaults = safe_eval( self.alias_defaults or "{}") defaults['qc_team_id'] = self.id return values
def print_test_label(self): for label in self: if label.test_print_mode and label.record_id and label.printer_id: record = label._get_record() extra = safe_eval(label.extra, {'env': self.env}) if record: label.print_label(label.printer_id, record, **extra)
def _filter_post(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.search(domain) else: return records
def test_01_safe_eval(self): """ Try a few common expressions to verify they work with safe_eval """ expected = (1, {"a": 9 * 2}, (True, False, None)) actual = safe_eval('(1, {"a": 9 * 2}, (True, False, None))') self.assertEqual( actual, expected, "Simple python expressions are not working with safe_eval")
def action_launch(self, context=None): """ Launch Action of Wizard""" self.ensure_one() self.write({'state': 'done'}) # Load action action = self.env[self.action_id.type].browse(self.action_id.id) result = action.read()[0] if action._name != '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 _postprocess_pdf_report(self, record, buffer): '''Hook to handle post processing during the pdf report generation. The basic behavior consists to create a new attachment containing the pdf base64 encoded. :param record_id: The record that will own the attachment. :param pdf_content: The optional name content of the file to avoid reading both times. :return: A modified buffer if the previous one has been modified, None otherwise. ''' attachment_name = safe_eval(self.attachment, { 'object': record, 'time': time }) if not attachment_name: return None attachment_vals = { 'name': attachment_name, 'raw': buffer.getvalue(), 'res_model': self.model, 'res_id': record.id, 'type': 'binary', } try: self.env['ir.attachment'].create(attachment_vals) except AccessError: _logger.info("Cannot save PDF report %r as attachment", attachment_vals['name']) else: _logger.info('The PDF document %s is now saved in the database', attachment_vals['name']) return buffer
def postprocess_pdf_report(self, record, buffer): '''Hook to handle post processing during the pdf report generation. The basic behavior consists to create a new attachment containing the pdf base64 encoded. :param record_id: The record that will own the attachment. :param pdf_content: The optional name content of the file to avoid reading both times. :return: The newly generated attachment if no AccessError, else None. ''' attachment_name = safe_eval(self.attachment, { 'object': record, 'time': time }) if not attachment_name: return None attachment_vals = { 'name': attachment_name, 'datas': base64.encodestring(buffer.getvalue()), 'datas_fname': attachment_name, 'res_model': self.model, 'res_id': record.id, } attachment = None try: attachment = self.env['ir.attachment'].create(attachment_vals) except AccessError: _logger.info("Cannot save PDF report %r as attachment", attachment_vals['name']) else: _logger.info('The PDF document %s is now saved in the database', attachment_vals['name']) return attachment
def action_your_pipeline(self): action = self.env["ir.actions.actions"]._for_xml_id( "crm.crm_lead_action_pipeline") 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, Flectra 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
def message_get_email_values(self, notif_mail=None): self.ensure_one() res = super(Channel, self).message_get_email_values(notif_mail=notif_mail) headers = {} if res.get('headers'): try: headers.update(safe_eval(res['headers'])) except Exception: pass headers['Precedence'] = 'list' # avoid out-of-office replies from MS Exchange # http://blogs.technet.com/b/exchange/archive/2006/10/06/3395024.aspx headers['X-Auto-Response-Suppress'] = 'OOF' if self.alias_domain and self.alias_name: headers['List-Id'] = '<%s.%s>' % (self.alias_name, self.alias_domain) headers['List-Post'] = '<mailto:%s@%s>' % (self.alias_name, self.alias_domain) # Avoid users thinking it was a personal message # X-Forge-To: will replace To: after SMTP envelope is determined by ir.mail.server list_to = '"%s" <%s@%s>' % (self.name, self.alias_name, self.alias_domain) headers['X-Forge-To'] = list_to res['headers'] = repr(headers) return res
def action_view_project_ids(self): self.ensure_one() if len(self.project_ids) == 1: if self.env.user.has_group("hr_timesheet.group_hr_timesheet_user"): action = self.project_ids.action_view_timesheet_plan() else: action = self.env.ref( "project.act_project_project_2_project_task_all").read()[0] action['context'] = safe_eval( action.get('context', '{}'), { 'active_id': self.project_ids.id, 'active_ids': self.project_ids.ids }) else: view_form_id = self.env.ref('project.edit_project').id view_kanban_id = self.env.ref('project.view_project_kanban').id action = { 'type': 'ir.actions.act_window', 'domain': [('id', 'in', self.project_ids.ids)], 'views': [(view_kanban_id, 'kanban'), (view_form_id, 'form')], 'view_mode': 'kanban,form', 'name': _('Projects'), 'res_model': 'project.project', } return action
def _check_alias_defaults(self): try: dict(safe_eval(self.alias_defaults)) except Exception: raise ValidationError( _('Invalid expression, it must be a literal python dictionary definition e.g. "{\'field\': \'value\'}"' ))
def action_view_all_rating(self): """ return the action to see all the rating of the project, and activate default filters """ action = self.env['ir.actions.act_window'].for_xml_id('rating_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_rating_tasks'] = 1 return dict(action, context=action_context)
def _get_cache_key(self, req): # Always call me with super() AT THE END to have cache_key_expr appended as last element # It is the only way for end user to not use cache via expr. # E.g (None if 'token' in request.params else 1,) will bypass cache_time cache_key = (req.website.id, req.lang, req.httprequest.path) if self.cache_key_expr: # e.g. (request.session.geoip.get('country_code'),) cache_key += safe_eval(self.cache_key_expr, {'request': req}) return cache_key