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 = 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: 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 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(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(eval(self.amount_percentage_base, localdict)), float(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: 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 _compute_price_unit(self, prix_gpl=0.0, remise_fournisseur=0.0, marge=0.0, frais_approche=0.0, discount=0.0, tva=0.0): data = {} if self.type_methode == 'pourcentage': print self.type_methode data['price_unit'] = 0 elif self.type_methode == 'fixed': data['price_unit'] = 0 print self.type_methode elif self.type_methode == 'code_python': localdict = { 'prix_gpl': prix_gpl, 'remise_fournisseur': remise_fournisseur, 'marge': marge, 'frais_approche': frais_approche, 'discount': discount, 'tva': tva } # print "[!] Displaying dictionary we got as parameter: {}".format( # encapsulated_dictionary) eval(self.code_python, localdict, mode="exec", nocopy=True) print "[! ] Before returning, got data_to_return: {}".format( localdict) data['price_unit'] = localdict['result'] return data
def _check_condition_code(btn): locals_dict = { 'env': this.env, 'model': this.env[model], 'obj': None, 'user': this.env['res.users'].browse(this.env.uid), 'datetime': datetime, 'time': time, 'date': date, 'timedelta': timedelta, 'workflow': this.env['odoo.workflow'], 'warning': this.warning, 'syslog': this.syslog, } try: eval(btn.condition_code, locals_dict=locals_dict, mode='exec', nocopy=True) result = 'result' in locals_dict and locals_dict[ 'result'] or False return result except Warning as ex: raise ex except: raise UserError(_("Wrong python condition defined."))
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: 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])] + eval(config.filter_id.domain) additionnal_context = 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 button_to_get_approve_list(self): """计算当前RFQ的签核角色信息列表""" expressions = self.env['iac.rfq.qh'].search([]) # 遍历选中的RFQ计算规则 for rfq_line in self: role_list = [] proc_ex_list = [] rule_name_list = [] for exp in expressions: eval_context = {"r": rfq_line} # 判断规则是否判断通过 try: if eval(exp.key, eval_context): role_list += eval(exp.value) rule_name_list.append(exp.name) except: traceback.print_exc() err_msg = "rule eval error,name is (%s);\n error info is %s" % ( exp.name, traceback.format_exc()) proc_ex_list.append(err_msg) # 写入获取角色列表信息 approve_role_list = { "role_list": sorted(list(set(role_list))), "rule_name_list": rule_name_list } vals = { "approve_role_list": approve_role_list, "webflow_result": proc_ex_list, } rfq_line.write(vals)
def _execute(self): self.ensure_one() model_obj = self.env[self.import_tmpl_id.model_id.model] if self._context.get('original_cr'): new_env = self.env(cr=self._context['original_cr']) model_obj = model_obj.with_env(new_env) args = eval(self.args or '[]') kwargs = eval(self.import_tmpl_id.method_args or '{}') return getattr(model_obj, self.import_tmpl_id.method)(*args, **kwargs)
def check_condition(self, document): self.ensure_one() result = False localdict = self._get_localdict(document) try: eval(self.domain, localdict, mode="exec", nocopy=True) result = localdict["result"] except: result = False return result
def get_needaction_data(self): """ Return for each menu entry in ``self``: - whether it uses the needaction mechanism (needaction_enabled) - the needaction counter of the related action, taking into account the action domain """ menu_ids = set() for menu in self: menu_ids.add(menu.id) ctx = {} if menu.action and menu.action.type in ( 'ir.actions.act_window', 'ir.actions.client') and menu.action.context: with tools.ignore(Exception): # use magical UnquoteEvalContext to ignore undefined client-side variables such as `active_id` eval_ctx = tools.UnquoteEvalContext(self._context) ctx = eval(menu.action.context, locals_dict=eval_ctx, nocopy=True) or {} menu_refs = ctx.get('needaction_menu_ref') if menu_refs: if not isinstance(menu_refs, list): menu_refs = [menu_refs] for menu_ref in menu_refs: record = self.env.ref(menu_ref, False) if record and record._name == 'ir.ui.menu': menu_ids.add(record.id) res = {} for menu in self.browse(menu_ids): res[menu.id] = { 'needaction_enabled': False, 'needaction_counter': False, } if menu.action and menu.action.type in ( 'ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: if menu.action.res_model in self.env: model = self.env[menu.action.res_model] if model._needaction: if menu.action.type == 'ir.actions.act_window': eval_context = self.env[ 'ir.actions.act_window']._get_eval_context() dom = eval(menu.action.domain or '[]', eval_context) else: dom = eval(menu.action.params_store or '{}', { 'uid': self._uid }).get('domain') res[menu.id]['needaction_enabled'] = model._needaction res[menu.id][ 'needaction_counter'] = model._needaction_count( dom) return res
def _filter_post(self, records): """ Filter the records that satisfy the postcondition of action ``self``. """ if self.filter_id and records: domain = [('id', 'in', records.ids)] + eval(self.filter_id.domain, self._get_eval_context()) ctx = eval(self.filter_id.context) return records.with_context(**ctx).search(domain).with_env(records.env) elif self.filter_domain and records: domain = [('id', 'in', records.ids)] + eval(self.filter_domain, self._get_eval_context()) return records.search(domain) else: return records
def satisfy_condition(self, localdict): """ @return: returns True if the given rule matches. Return False otherwise. """ self.ensure_one() try: eval(self.condition_python, localdict, mode="exec", nocopy=True) return "result" in localdict and localdict["result"] or False except Exception as e: raise UserError("Wrong python condition defined for disbursement " "exception rule %s. \n %s" % (self.name, str(e)))
def get_avance(p, code, is_interest, categories, inputs): if p.simulation_ok: if not p.simulate_elements_ok: return 0.0 plafond = False rate = False if is_interest: localdict = { 'categories': categories, 'inputs': inputs, 'self': self, } expression_id = self.search([('code', '=', 'NET_IMPOSABLE')], limit=1) expression = expression_id expression = expression and expression.amount_python_compute or False eval(expression, localdict, mode='exec', nocopy=True) result = 'result' in localdict and localdict['result'] or 0.0 plafond = result somme = 0.0 # Avances line_obj = self.env['hr.avance.line.line'] # Domain linked to saisie.py domain = [ ('state', '=', 'done'), ('avance_line_id.code', '=', code), ('avance_line_id.employee_id', '=', p.employee_id), ('date', '>=', p.date_from), ('date', '<=', p.date_to), ] line_ids = line_obj.search(domain) lines = line_ids somme += sum([x.amount for x in lines]) # Rates for line in lines: rate = line.avance_line_id.avance_id.interest_rate break # Notes de frais line_obj = self.env['hr.expense'] domain = [ ('state', '=', 'paid'), ('avance_id.code', '=', code), ('employee_id', '=', p.employee_id), ('payroll_date', '<=', p.date_to), ('payroll_date', '>=', p.date_from), ] line_ids = line_obj.search(domain) lines = line_ids somme += sum([x.total_amount for x in lines]) if rate and plafond: if somme > plafond * rate / 100.: return plafond * rate / 100. return somme
def _get_sequence_id(self, document): self.ensure_one() result = False localdict = self._get_localdict(document) try: eval(self.sequence_computation_code, localdict, mode="exec", nocopy=True) result = localdict["result"] except: raise UserError(_("Error on get sequence")) return result
def _execute(self): self.ensure_one() if not self.record_ids: raise UserError(_("You cannot regenerate this export because records to export didn't store")) record_ids = eval(self.record_ids) if record_ids or self.export_tmpl_id.force_execute_action: records = self.env[self.export_tmpl_id.model_id.model].browse(record_ids) if self.export_tmpl_id.method: if self._context.get('original_cr'): new_env = self.env(cr=self._context['original_cr']) records = records.with_env(new_env) args = eval(self.args or '[]') kwargs = eval(self.export_tmpl_id.method_args or '{}') return getattr(records, self.export_tmpl_id.method)(*args, **kwargs)
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([('kind', '=', '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 = eval(action.filter_domain, eval_context) elif action.filter_id: domain = eval(action.filter_id.domain, eval_context) context.update(eval(action.filter_id.context)) if 'lang' not in context: # Filters might be language-sensitive, attempt to reuse creator lang # as we are usually running this as super-user in background filter_meta = action.filter_id.get_metadata()[0] user_id = (filter_meta['write_uid'] or filter_meta['create_uid'])[0] context['lang'] = self.env['res.users'].browse(user_id).lang records = self.env[action.model].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': fields.Datetime.now()}) if automatic: # auto-commit for batch processing self._cr.commit()
def amount_to_text(self, value): val = {} self.ensure_one() val = "-" if self.python_amount2text: try: localdict = {'value': value} eval(self.python_amount2text, localdict, mode='exec', nocopy=True) val = localdict['result'] except: pass return val
def add_terms_and_conditions(self, res_id, original_report_pdf): model = self.model object = self.env[model].browse(res_id) company = object.company_id if not company.terms_and_conditions: return original_report_pdf language_field = self.terms_conditions_language_field localdict = {'o': object} eval('document_language = o.%s' % language_field, localdict, mode='exec', nocopy=True) document_language = localdict.get('document_language', self._context.get('lang')) # Try to find the terms and condition matching the document_language terms_and_conditions = company.terms_and_conditions.filtered( lambda t: t.language_id.code == document_language) if not terms_and_conditions: # Try to find the default terms and conditions (no language set) terms_and_conditions = company.terms_and_conditions.filtered( lambda t: not t.language_id) if not terms_and_conditions: return original_report_pdf terms_and_conditions_decoded = base64.b64decode(terms_and_conditions.datas) if terms_and_conditions_decoded: writer = PdfFileWriter() stream_original_report = io.BytesIO(original_report_pdf[0]) reader_original_report = PdfFileReader(stream_original_report) stream_terms_and_conditions = io.BytesIO( terms_and_conditions_decoded) reader_terms_and_conditions = PdfFileReader( stream_terms_and_conditions) for page in range(0, reader_original_report.getNumPages()): writer.addPage(reader_original_report.getPage(page)) for page in range(0, reader_terms_and_conditions.getNumPages()): writer.addPage(reader_terms_and_conditions.getPage(page)) stream_to_write = io.BytesIO() writer.write(stream_to_write) combined_pdf = stream_to_write.getvalue() return combined_pdf, 'pdf' else: return original_report_pdf
def _compute_data(self): for tile in self: if not tile.model_id or not tile.active: return model = self.env[tile.model_id.model] eval_context = self._get_eval_context() domain = tile.domain or "[]" try: count = model.search_count(eval(domain, eval_context)) except Exception as e: tile.primary_value = 0.0 tile.primary_formated_value =\ tile.secondary_formated_value = _("Error") tile.error = str(e) return fields = [ f.name for f in [tile.primary_field_id, tile.secondary_field_id] if f ] read_vals = (fields and model.search_read( eval(domain, eval_context), fields) or []) for f in ["primary_", "secondary_"]: f_function = f + "function" f_field_id = f + "field_id" f_format = f + "format" f_value = f + "value" f_formated_value = f + "formated_value" value = 0 if not tile[f_function]: tile[f_value] = 0.0 tile[f_formated_value] = False else: if tile[f_function] == "count": value = count else: func = FIELD_FUNCTIONS[tile[f_function]]["func"] vals = [x[tile[f_field_id].name] for x in read_vals] value = func(vals or [0.0]) try: tile[f_value] = value tile[f_formated_value] = (tile[f_format] or "{:,}").format(value) if tile.hide_if_null and not value: tile.hidden = True except ValueError as e: tile[f_value] = 0.0 tile[f_formated_value] = _("Error") tile.error = str(e)
def run(self): for rec in self: # Check Condition Before Executing Action result = False cx = self.env.context.copy() or {} locals_dict = { 'env': self.env, 'model': self.env[cx.get('active_model', False)], 'obj': self.env[cx.get('active_model', False)].browse(cx.get('active_id', 0)), 'user': self.env.user, 'datetime': datetime, 'time': time, 'date': date, 'timedelta': timedelta, 'workflow': self.env['odoo.workflow'], 'warning': self.warning, 'syslog': self.syslog, } try: eval(rec.condition_code, locals_dict=locals_dict, mode='exec', nocopy=True) result = 'result' in locals_dict and locals_dict[ 'result'] or False except ValidationError as ex: raise ex except SyntaxError as ex: raise UserError( _("Wrong python code defined.\n\nError: %s\nLine: %s, Column: %s\n\n%s" % (ex.args[0], ex.args[1][1], ex.args[1][2], ex.args[1][3]))) if result: # Run Proper Action func = getattr(self, "_run_%s" % rec.action_type) return func()
def many2one_search(self, word, model, domain): return request.make_response( simplejson.dumps([{ 'id': record[0], 'value': record[1] } for record in request.env[model].name_search( word, args=eval(domain), limit=20)]))
def _get_res_ids(self, *args): model_obj = self.env[self.model_id.model] if self._context.get('original_cr'): model_obj = model_obj.with_env(self.env(cr=self._context['original_cr'])) if self.filter_type == 'domain': domain = eval(self.filter_domain or '[]', self._get_eval_context()) res_ids = set(model_obj.search(domain, order=self.order or '')._ids) else: # elif self.filter_type == 'method': if not (self.filter_method and hasattr(model_obj, self.filter_method)): raise UserError(_("Can't find method: %s on object: %s") % (self.filter_method, self.model_id.model)) res_ids = set(getattr(model_obj, self.filter_method)(*args)) if 'active_ids' in self._context: res_ids &= set(self._context['active_ids']) if self.unique: res_ids -= set(sum([eval(export.record_ids) for export in self.export_ids], [])) return list(res_ids)
def _get_upgrades(self): upgrades_path = upgrade_config.get('upgrades_path') if not upgrades_path: return [] if not self.db_in_creation: self._try_lock('Upgrade in progress') upgrades = [] for dir in os.listdir(upgrades_path): dir_path = os.path.join(upgrades_path, dir) if os.path.isdir(dir_path): file_path = os.path.join(dir_path, '__upgrade__.py') if not os.path.exists(file_path): _logger.warning(u"%s doesn't exist", file_path) continue if not os.path.isfile(file_path): _logger.warning(u'%s is not a file', file_path) continue with open(file_path) as f: try: upgrade_infos = eval(f.read()) upgrade = Upgrade(dir_path, upgrade_infos) if (not upgrade.databases or self.db_name in upgrade.databases) \ and self.db_version < upgrade.version <= self.code_version: upgrades.append(upgrade) except Exception, e: _logger.error('%s is not valid: %s', file_path, repr(e))
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 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, Odoo opens the first one by default.</p>" action_context = 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 _get_formatted_template(self, w): payload = w.template_id.template.strip() v = re.findall(r'(\$\{\s.+\s\})', payload) v += re.findall(r'(\$\{.+\})', payload) v += re.findall(r'(\$\{[\w\s.]+\})', payload) v = list(set(v)) employee = w.employee_id contract_id = self.env['hr.contract'].search([ ('employee_id', '=', employee.id), ('is_contract_valid_by_context', '=', w.action_date) ]) contract = contract_id and self.env['hr.contract'].browse( contract_id[0]) or False company = contract and contract.company_id or employee.company_id for element in v: localdict = { 'object': w, 'contract': contract, 'employee': employee, 'company': company, } try: m = eval(element[2:-1].strip(), localdict, mode='eval', nocopy=True) payload = payload.replace(element, m) except: pass return payload
def construction_action_url_list(self, action, action_url_list): """ 把所有的数据整合,经过深度处理放进 action_url_list :param action: [home.page]对象,数据的来源。 :param action_url_list: 存放数据的处理结果。 :return: """ action_vals = self.constract_action_vals(action) if action.menu_type == 'all_business': action_url_list['main'].append(action_vals) elif action.menu_type == 'amount_summary': # 金额汇总类 compute_domain = eval(action.domain or '[]') res_model_objs = self.env[action.action.res_model].search(compute_domain) field_compute = action.compute_field_one.name # 最新的金额 compute_value = sum([res_model_obj[field_compute] for res_model_obj in res_model_objs]) # 返回结果 action_vals[0] = "%s %s" % (action_vals[0], compute_value) action_url_list['top'].append(action_vals) else: action_vals[0] = "%s " % action_vals[0] type_sequence_str = "%s;%s" % (action.report_type_id.sequence, action.report_type_id.name) if action_url_list['right'].get(type_sequence_str): action_url_list['right'][type_sequence_str].append(action_vals) else: action_url_list['right'].update({type_sequence_str: [action_vals]})
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 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, Odoo opens the first one by default.</p>" action_context = 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 construction_action_url_list(self, action, action_url_list): """ 把所有的数据整合,经过深度处理放进 action_url_list :param action: [home.page]对象,数据的来源。 :param action_url_list: 存放数据的处理结果。 :return: """ action_vals = self.constract_action_vals(action) if action.menu_type == 'all_business': action_url_list['main'].append(action_vals) elif action.menu_type == 'amount_summary': # 性能如果急需提升,就只有sum计算和那个search 是瓶颈了。 这个功能可以尽量少的用,或者用在小数据量的数据字段统计。 # 如果数据量很大就不要用这种方式。 res_model_objs = self.env[action.action.res_model].search(eval(action.domain or '[]')) field_compute = action.compute_field_one.name action_vals[0] = "%s %s" % (action_vals[0], sum([res_model_obj[field_compute] for res_model_obj in res_model_objs])) action_url_list['top'].append(action_vals) else: action_vals[0] = "%s " % action_vals[0] type_sequence_str = "%s;%s" % (action.report_type_id.sequence, action.report_type_id.name) if action_url_list['right'].get(type_sequence_str): action_url_list['right'][type_sequence_str].append(action_vals) else: action_url_list['right'].update({type_sequence_str: [action_vals]})
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 = 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 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 = eval(lang_grouping) if percent[-1] in 'eEfFgG': parts = formatted.split('.') parts[0], _ = intersperse(parts[0], eval_lang_grouping, thousands_sep) formatted = decimal_point.join(parts) elif percent[-1] in 'diu': formatted = intersperse(formatted, eval_lang_grouping, thousands_sep)[0] return formatted
def _force_domain(self): eval_context = self._eval_context() for rule in self: if rule.domain_force: rule.domain = expression.normalize_domain(eval(rule.domain_force, eval_context)) else: rule.domain = []
def construction_action_url_list(self, action, action_url_list): """ 把所有的数据整合,经过深度处理放进 action_url_list :param action: [home.page]对象,数据的来源。 :param action_url_list: 存放数据的处理结果。 :return: """ action_vals = self.constract_action_vals(action) if action.menu_type == 'all_business': action_url_list['main'].append(action_vals) elif action.menu_type == 'amount_summary': # 金额汇总类 compute_domain = eval(action.domain or '[]') res_model_objs = self.env[action.action.res_model].search( compute_domain) field_compute = action.compute_field_one.name # 最新的金额 compute_value = sum([ res_model_obj[field_compute] for res_model_obj in res_model_objs ]) # 返回结果 action_vals[0] = "%s %s" % (action_vals[0], compute_value) action_url_list['top'].append(action_vals) else: action_vals[0] = "%s " % action_vals[0] type_sequence_str = "%s;%s" % (action.report_type_id.sequence, action.report_type_id.name) if action_url_list['right'].get(type_sequence_str): action_url_list['right'][type_sequence_str].append(action_vals) else: action_url_list['right'].update( {type_sequence_str: [action_vals]})
def _run_code(self): # Variables cx = self.env.context.copy() or {} locals_dict = { 'env': self.env, 'model': self.env[cx.get('active_model', False)], 'obj': self.env[cx.get('active_model', False)].browse(cx.get('active_id', 0)), 'user': self.env.user, 'datetime': datetime, 'time': time, 'date': date, 'timedelta': timedelta, 'workflow': self.env['odoo.workflow'], 'warning': self.warning, 'syslog': self.syslog, } # Run Code for rec in self: try: eval(rec.code, locals_dict=locals_dict, mode='exec', nocopy=True) action = 'action' in locals_dict and locals_dict[ 'action'] or False if action: return action except Warning as ex: raise ex except SyntaxError as ex: raise UserError( _("Wrong python code defined.\n\nError: %s\nLine: %s, Column: %s\n\n%s" % (ex.args[0], ex.args[1][1], ex.args[1][2], ex.args[1][3]))) return True
def _get_format_domain(self, name, domain): view = request.env['mobile.view'].search([('name', '=', name)]) res = view.domain and eval(view.domain) or [] res.extend([(item.get('name'), item.get('operator') or 'ilike', item.get('operator') and float(item.get('word')) or item.get('word')) for item in domain]) return res
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 = 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 _force_domain(self): eval_context = self._eval_context() for rule in self: if rule.domain_force: rule.domain = expression.normalize_domain( eval(rule.domain_force, eval_context)) else: rule.domain = []
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 = item.keys() return [(col, item.get(col)) for col in cols if col in item.keys()] 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) } 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 get_needaction_data(self): """ Return for each menu entry in ``self``: - whether it uses the needaction mechanism (needaction_enabled) - the needaction counter of the related action, taking into account the action domain """ menu_ids = set() for menu in self: menu_ids.add(menu.id) ctx = {} if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.context: with tools.ignore(Exception): # use magical UnquoteEvalContext to ignore undefined client-side variables such as `active_id` eval_ctx = tools.UnquoteEvalContext(self._context) ctx = eval(menu.action.context, locals_dict=eval_ctx, nocopy=True) or {} menu_refs = ctx.get('needaction_menu_ref') if menu_refs: if not isinstance(menu_refs, list): menu_refs = [menu_refs] for menu_ref in menu_refs: record = self.env.ref(menu_ref, False) if record and record._name == 'ir.ui.menu': menu_ids.add(record.id) res = {} for menu in self.browse(menu_ids): res[menu.id] = { 'needaction_enabled': False, 'needaction_counter': False, } if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: if menu.action.res_model in self.env: model = self.env[menu.action.res_model] if model._needaction: if menu.action.type == 'ir.actions.act_window': eval_context = self.env['ir.actions.act_window']._get_eval_context() dom = eval(menu.action.domain or '[]', eval_context) else: dom = eval(menu.action.params_store or '{}', {'uid': self._uid}).get('domain') res[menu.id]['needaction_enabled'] = model._needaction res[menu.id]['needaction_counter'] = model._needaction_count(dom) return res
def _check_grouping(self): warning = _('The Separator Format should be like [,n] where 0 < n :starting from Unit digit. ' '-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500;' '[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. ' 'Provided as the thousand separator in each case.') for lang in self: try: if not all(isinstance(x, int) for x in eval(lang.grouping)): raise ValidationError(warning) except Exception: raise ValidationError(warning)
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 = 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: 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 report_download(self, data, token): """This function is used by 'qwebactionmanager.js' in order to trigger the download of a pdf/controller report. :param data: a javascript array JSON.stringified containg report internal url ([0]) and type [1] :returns: Response with a filetoken cookie and an attachment header """ requestcontent = json.loads(data) url, type = requestcontent[0], requestcontent[1] try: if type == 'qweb-pdf': reportname = url.split('/report/pdf/')[1].split('?')[0] docids = None if '/' in reportname: reportname, docids = reportname.split('/') if docids: # Generic report: response = self.report_routes(reportname, docids=docids, converter='pdf') else: # Particular report: data = url_decode(url.split('?')[1]).items() # decoding the args represented in JSON response = self.report_routes(reportname, converter='pdf', **dict(data)) report = request.env['report']._get_report_from_name(reportname) filename = "%s.%s" % (report.name, "pdf") if docids: ids = [int(x) for x in docids.split(",")] obj = request.env[report.model].browse(ids) if report.print_report_name and not len(obj) > 1: filename = eval(report.print_report_name, {'object': obj, 'time': time}) response.headers.add('Content-Disposition', content_disposition(filename)) response.set_cookie('fileToken', token) return response elif type == 'controller': reqheaders = Headers(request.httprequest.headers) response = Client(request.httprequest.app, BaseResponse).get(url, headers=reqheaders, follow_redirects=True) response.set_cookie('fileToken', token) return response else: return except Exception, e: se = _serialize_exception(e) error = { 'code': 200, 'message': "Odoo Server Error", 'data': se } return request.make_response(html_escape(json.dumps(error)))
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 = 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(_("Selected product in the delivery method doesn't fulfill any of the delivery carrier(s) criteria.")) return price
def _search_qty_available_new(self, operator, value, lot_id=False, owner_id=False, package_id=False): # TDE FIXME: should probably clean the search methods product_ids = set() domain_quant = self._get_domain_locations()[0] if lot_id: domain_quant.append(('lot_id', '=', lot_id)) if owner_id: domain_quant.append(('owner_id', '=', owner_id)) if package_id: domain_quant.append(('package_id', '=', package_id)) quants_groupby = self.env['stock.quant'].read_group(domain_quant, ['product_id', 'qty'], ['product_id']) for quant in quants_groupby: if eval('%s %s %s' % (quant['qty'], operator, value)): product_ids.add(quant['product_id'][0]) return list(product_ids)
def message_get_email_values(self, notif_mail=None): self.ensure_one() res = super(MailGroup, self).message_get_email_values(notif_mail=notif_mail) base_url = self.env['ir.config_parameter'].get_param('web.base.url') headers = {} if res.get('headers'): try: headers = eval(res['headers']) except Exception: pass headers.update({ 'List-Archive': '<%s/groups/%s>' % (base_url, slug(self)), 'List-Subscribe': '<%s/groups>' % (base_url), 'List-Unsubscribe': '<%s/groups?unsubscribe>' % (base_url,), }) res['headers'] = repr(headers) return res
def process_segment(self): Workitems = self.env['marketing.campaign.workitem'] Activities = self.env['marketing.campaign.activity'] if not self: self = self.search([('state', '=', 'running')]) action_date = fields.Datetime.now() campaigns = self.env['marketing.campaign'] for segment in self: if segment.campaign_id.state != 'running': continue campaigns |= segment.campaign_id activity_ids = Activities.search([('start', '=', True), ('campaign_id', '=', segment.campaign_id.id)]).ids criteria = [] if segment.sync_last_date and segment.sync_mode != 'all': criteria += [(segment.sync_mode, '>', segment.sync_last_date)] if segment.ir_filter_id: criteria += eval(segment.ir_filter_id.domain) # XXX TODO: rewrite this loop more efficiently without doing 1 search per record! for record in self.env[segment.object_id.model].search(criteria): # avoid duplicate workitem for the same resource if segment.sync_mode in ('write_date', 'all'): if segment.campaign_id._find_duplicate_workitems(record): continue wi_vals = { 'segment_id': segment.id, 'date': action_date, 'state': 'todo', 'res_id': record.id } partner = segment.campaign_id._get_partner_for(record) if partner: wi_vals['partner_id'] = partner.id for activity_id in activity_ids: wi_vals['activity_id'] = activity_id Workitems.create(wi_vals) segment.write({'sync_last_date': action_date}) Workitems.process_all(campaigns.ids) return True
def message_get_email_values(self, notif_mail=None): self.ensure_one() res = super(ProjectIssue, self).message_get_email_values(notif_mail=notif_mail) headers = {} if res.get('headers'): try: headers.update(eval(res['headers'])) except Exception: pass if self.project_id: current_objects = filter(None, headers.get('X-Odoo-Objects', '').split(',')) current_objects.insert(0, 'project.project-%s, ' % self.project_id.id) headers['X-Odoo-Objects'] = ','.join(current_objects) if self.tag_ids: headers['X-Odoo-Tags'] = ','.join(self.tag_ids.mapped('name')) res['headers'] = repr(headers) return res
def _search_product_quantity(self, operator, value, field): # TDE FIXME: should probably clean the search methods # to prevent sql injections if field not in ('qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty'): raise UserError('Invalid domain left operand') if operator not in ('<', '>', '=', '!=', '<=', '>='): raise UserError('Invalid domain operator') if not isinstance(value, (float, int)): raise UserError('Invalid domain right operand') if operator == '=': operator = '==' # TODO: Still optimization possible when searching virtual quantities ids = [] for product in self.search([]): if eval(str(product[field]) + operator + str(value)): ids.append(product.id) return [('id', 'in', ids)]
def get_recipients(self): if self.mailing_domain: domain = eval(self.mailing_domain) res_ids = self.env[self.mailing_model].search(domain).ids 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].search_count(domain) topick = int(contact_nbr / 100.0 * self.contact_ab_pc) if self.mass_mailing_campaign_id and self.mass_mailing_campaign_id.unique_ab_testing: already_mailed = self.mass_mailing_campaign_id.get_recipients()[self.mass_mailing_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 construction_action_url_list(self,action,action_url_list): if action.menu_type == 'all_business': action_url_list['main'].append([action.note_one, action.action.view_mode, action.action.res_model, action.action.domain, action.id, action.action.context, action.view_id.id, action.action.name, action.action.target]) elif action.menu_type == 'amount_summary': res_model_objs = self.env[action.action.res_model].search(eval(action.domain or '[]')) field_compute = action.compute_field_one.name note = action.note_one note = "%s %s" % (note, sum([res_model_obj[field_compute] for res_model_obj in res_model_objs])) action_url_list['top'].append([note, action.action.view_mode, action.action.res_model, action.domain, action.context, action.view_id.id, action.action.name, action.action.target]) else: vals_list = ["%s " % (action.note_one), action.action.view_mode, action.action.res_model, action.domain, action.context, action.view_id.id, action.action.name, action.action.target] type_sequence_str = "%s;%s" % (action.report_type_id.sequence, action.report_type_id.name) if action_url_list['right'].get(type_sequence_str): action_url_list['right'][type_sequence_str].append(vals_list) else: action_url_list['right'].update({type_sequence_str: [vals_list]})
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(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 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 = eval(lang_grouping) if percent[-1] in "eEfFgG": parts = formatted.split(".") parts[0], _ = intersperse(parts[0], eval_lang_grouping, thousands_sep) formatted = decimal_point.join(parts) elif percent[-1] in "diu": formatted = intersperse(formatted, eval_lang_grouping, thousands_sep)[0] return formatted
def str2tuple(s): return eval('tuple(%s)' % (s or ''))
def _attachment_filename(self, records, report): return dict((record.id, eval(report.attachment, {'object': record, 'time': time})) for record in records)
def send(self, auto_commit=False, raise_exception=False): """ Sends the selected emails immediately, ignoring their current state (mails that have already been sent should not be passed unless they should actually be re-sent). Emails successfully delivered are marked as 'sent', and those that fail to be deliver are marked as 'exception', and the corresponding error mail is output in the server logs. :param bool auto_commit: whether to force a commit of the mail status after sending each mail (meant only for scheduler processing); should never be True during normal transactions (default: False) :param bool raise_exception: whether to raise an exception if the email sending process has failed :return: True """ IrMailServer = self.env['ir.mail_server'] for mail in self: try: # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method if mail.model: model = self.env['ir.model'].sudo().search([('model', '=', mail.model)])[0] else: model = None if model: mail = mail.with_context(model_name=model.name) # load attachment binary data with a separate read(), as prefetching all # `datas` (binary field) could bloat the browse cache, triggerring # soft/hard mem limits with temporary data. attachments = [(a['datas_fname'], base64.b64decode(a['datas'])) for a in mail.attachment_ids.sudo().read(['datas_fname', 'datas'])] # specific behavior to customize the send email for notified partners email_list = [] if mail.email_to: email_list.append(mail.send_get_email_dict()) for partner in mail.recipient_ids: email_list.append(mail.send_get_email_dict(partner=partner)) # headers headers = {} bounce_alias = self.env['ir.config_parameter'].get_param("mail.bounce.alias") catchall_domain = self.env['ir.config_parameter'].get_param("mail.catchall.domain") if bounce_alias and catchall_domain: if mail.model and mail.res_id: headers['Return-Path'] = '%s-%d-%s-%d@%s' % (bounce_alias, mail.id, mail.model, mail.res_id, catchall_domain) else: headers['Return-Path'] = '%s-%d@%s' % (bounce_alias, mail.id, catchall_domain) if mail.headers: try: headers.update(eval(mail.headers)) except Exception: pass # Writing on the mail object may fail (e.g. lock on user) which # would trigger a rollback *after* actually sending the email. # To avoid sending twice the same email, provoke the failure earlier mail.write({ 'state': 'exception', 'failure_reason': _('Error without exception. Probably due do sending an email without computed recipients.'), }) mail_sent = False # build an RFC2822 email.message.Message object and send it without queuing res = None for email in email_list: msg = IrMailServer.build_email( email_from=mail.email_from, email_to=email.get('email_to'), subject=mail.subject, body=email.get('body'), body_alternative=email.get('body_alternative'), email_cc=tools.email_split(mail.email_cc), reply_to=mail.reply_to, attachments=attachments, message_id=mail.message_id, references=mail.references, object_id=mail.res_id and ('%s-%s' % (mail.res_id, mail.model)), subtype='html', subtype_alternative='plain', headers=headers) try: res = IrMailServer.send_email(msg, mail_server_id=mail.mail_server_id.id) except AssertionError as error: if error.message == IrMailServer.NO_VALID_RECIPIENT: # No valid recipient found for this particular # mail item -> ignore error to avoid blocking # delivery to next recipients, if any. If this is # the only recipient, the mail will show as failed. _logger.info("Ignoring invalid recipients for mail.mail %s: %s", mail.message_id, email.get('email_to')) else: raise if res: mail.write({'state': 'sent', 'message_id': res, 'failure_reason': False}) mail_sent = True # /!\ can't use mail.state here, as mail.refresh() will cause an error # see revid:[email protected] in 6.1 if mail_sent: _logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id) mail._postprocess_sent_message(mail_sent=mail_sent) except MemoryError: # prevent catching transient MemoryErrors, bubble up to notify user or abort cron job # instead of marking the mail as failed _logger.exception( 'MemoryError while processing mail with ID %r and Msg-Id %r. Consider raising the --limit-memory-hard startup option', mail.id, mail.message_id) raise except psycopg2.Error: # If an error with the database occurs, chances are that the cursor is unusable. # This will lead to an `psycopg2.InternalError` being raised when trying to write # `state`, shadowing the original exception and forbid a retry on concurrent # update. Let's bubble it. raise except Exception as e: failure_reason = tools.ustr(e) _logger.exception('failed sending mail (id: %s) due to %s', mail.id, failure_reason) mail.write({'state': 'exception', 'failure_reason': failure_reason}) mail._postprocess_sent_message(mail_sent=False) if raise_exception: if isinstance(e, AssertionError): # get the args of the original error, wrap into a value and throw a MailDeliveryException # that is an except_orm, with name and value as arguments value = '. '.join(e.args) raise MailDeliveryException(_("Mail Delivery Failed"), value) raise if auto_commit is True: self._cr.commit() return True
def get_diagram_info(self, req, id, model, node, connector, src_node, des_node, label, **kw): visible_node_fields = kw.get('visible_node_fields',[]) invisible_node_fields = kw.get('invisible_node_fields',[]) node_fields_string = kw.get('node_fields_string',[]) connector_fields = kw.get('connector_fields',[]) connector_fields_string = kw.get('connector_fields_string',[]) bgcolors = {} shapes = {} bgcolor = kw.get('bgcolor','') shape = kw.get('shape','') if bgcolor: for color_spec in bgcolor.split(';'): if color_spec: colour, color_state = color_spec.split(':') bgcolors[colour] = color_state if shape: for shape_spec in shape.split(';'): if shape_spec: shape_colour, shape_color_state = shape_spec.split(':') shapes[shape_colour] = shape_color_state ir_view = req.session.model('ir.ui.view') graphs = ir_view.graph_get( int(id), model, node, connector, src_node, des_node, label, (140, 180), req.session.context) nodes = graphs['nodes'] transitions = graphs['transitions'] isolate_nodes = {} for blnk_node in graphs['blank_nodes']: isolate_nodes[blnk_node['id']] = blnk_node else: y = map(lambda t: t['y'],filter(lambda x: x['y'] if x['x']==20 else None, nodes.values())) y_max = (y and max(y)) or 120 connectors = {} list_tr = [] for tr in transitions: list_tr.append(tr) connectors.setdefault(tr, { 'id': int(tr), 's_id': transitions[tr][0], 'd_id': transitions[tr][1] }) connector_tr = req.session.model(connector) connector_ids = connector_tr.search([('id', 'in', list_tr)], context=req.session.context) data_connectors =connector_tr.read(connector_ids, connector_fields, req.session.context) for tr in data_connectors: transition_id = str(tr['id']) _sourceid, label = graphs['label'][transition_id] t = connectors[transition_id] t.update( source=tr[src_node][1], destination=tr[des_node][1], options={}, signal=label ) for i, fld in enumerate(connector_fields): t['options'][connector_fields_string[i]] = tr[fld] fields = req.session.model('ir.model.fields') field_ids = fields.search([('model', '=', model), ('relation', '=', node)], context=req.session.context) field_data = fields.read(field_ids, ['relation_field'], req.session.context) node_act = req.session.model(node) search_acts = node_act.search([(field_data[0]['relation_field'], '=', id)], context=req.session.context) data_acts = node_act.read(search_acts, invisible_node_fields + visible_node_fields, req.session.context) for act in data_acts: n = nodes.get(str(act['id'])) if not n: n = isolate_nodes.get(act['id'], {}) y_max += 140 n.update(x=20, y=y_max) nodes[act['id']] = n n.update( id=act['id'], color='white', options={} ) for color, expr in bgcolors.items(): if eval(expr, act): n['color'] = color for shape, expr in shapes.items(): if eval(expr, act): n['shape'] = shape for i, fld in enumerate(visible_node_fields): n['options'][node_fields_string[i]] = act[fld] _id, name = req.session.model(model).name_get([id], req.session.context)[0] return dict(nodes=nodes, conn=connectors, name=name, parent_field=graphs['node_parent_field'])
def _process_one(self): self.ensure_one() if self.state != 'todo': return False activity = self.activity_id resource = self.env[self.object_id.model].browse(self.res_id) eval_context = { 'activity': activity, 'workitem': self, 'object': resource, 'resource': resource, 'transitions': activity.to_ids, 're': re, } try: condition = activity.condition campaign_mode = self.campaign_id.mode if condition: if not eval(condition, eval_context): if activity.keep_if_condition_not_met: self.write({'state': 'cancelled'}) else: self.unlink() return result = True if campaign_mode in ('manual', 'active'): result = activity.process(self) values = {'state': 'done'} if not self.date: values['date'] = fields.Datetime.now() self.write(values) if result: # process _chain self.refresh() # reload execution_date = fields.Datetime.from_string(self.date) for transition in activity.to_ids: if transition.trigger == 'cosmetic': continue launch_date = False if transition.trigger == 'auto': launch_date = execution_date elif transition.trigger == 'time': launch_date = execution_date + transition._delta() if launch_date: launch_date = fields.Datetime.to_string(launch_date) values = { 'date': launch_date, 'segment_id': self.segment_id.id, 'activity_id': transition.activity_to_id.id, 'partner_id': self.partner_id.id, 'res_id': self.res_id, 'state': 'todo', } workitem = self.create(values) # Now, depending on the trigger and the campaign mode # we know whether we must run the newly created workitem. # # rows = transition trigger \ colums = campaign mode # # test test_realtime manual normal (active) # time Y N N N # cosmetic N N N N # auto Y Y N Y # run = (transition.trigger == 'auto' and campaign_mode != 'manual') or (transition.trigger == 'time' and campaign_mode == 'test') if run: workitem._process_one() except Exception: tb = "".join(format_exception(*exc_info())) self.write({'state': 'exception', 'error_msg': tb})
def send_mail(self, auto_commit=False): """ Process the wizard content and proceed with sending the related email(s), rendering any template patterns on the fly if needed. """ for wizard in self: # Duplicate attachments linked to the email.template. # Indeed, basic mail.compose.message wizard duplicates attachments in mass # mailing mode. But in 'single post' mode, attachments of an email template # also have to be duplicated to avoid changing their ownership. if wizard.attachment_ids and wizard.composition_mode != 'mass_mail' and wizard.template_id: new_attachment_ids = [] for attachment in wizard.attachment_ids: if attachment in wizard.template_id.attachment_ids: new_attachment_ids.append(attachment.copy({'res_model': 'mail.compose.message', 'res_id': wizard.id}).id) else: new_attachment_ids.append(attachment.id) wizard.write({'attachment_ids': [(6, 0, new_attachment_ids)]}) # Mass Mailing mass_mode = wizard.composition_mode in ('mass_mail', 'mass_post') Mail = self.env['mail.mail'] ActiveModel = self.env[wizard.model if wizard.model else 'mail.thread'] if wizard.template_id: # template user_signature is added when generating body_html # mass mailing: use template auto_delete value -> note, for emails mass mailing only Mail = Mail.with_context(mail_notify_user_signature=False) ActiveModel = ActiveModel.with_context(mail_notify_user_signature=False, mail_auto_delete=wizard.template_id.auto_delete) if not hasattr(ActiveModel, 'message_post'): ActiveModel = self.env['mail.thread'].with_context(thread_model=wizard.model) if wizard.composition_mode == 'mass_post': # do not send emails directly but use the queue instead # add context key to avoid subscribing the author ActiveModel = ActiveModel.with_context(mail_notify_force_send=False, mail_create_nosubscribe=True) # wizard works in batch mode: [res_id] or active_ids or active_domain if mass_mode and wizard.use_active_domain and wizard.model: res_ids = self.env[wizard.model].search(eval(wizard.active_domain)).ids elif mass_mode and wizard.model and self._context.get('active_ids'): res_ids = self._context['active_ids'] else: res_ids = [wizard.res_id] batch_size = int(self.env['ir.config_parameter'].sudo().get_param('mail.batch_size')) or self._batch_size sliced_res_ids = [res_ids[i:i + batch_size] for i in range(0, len(res_ids), batch_size)] if wizard.composition_mode == 'mass_mail' or wizard.is_log or (wizard.composition_mode == 'mass_post' and not wizard.notify): # log a note: subtype is False subtype_id = False elif wizard.subtype_id: subtype_id = wizard.subtype_id.id else: subtype_id = self.sudo().env.ref('mail.mt_comment', raise_if_not_found=False).id for res_ids in sliced_res_ids: batch_mails = Mail all_mail_values = wizard.get_mail_values(res_ids) for res_id, mail_values in all_mail_values.iteritems(): if wizard.composition_mode == 'mass_mail': batch_mails |= Mail.create(mail_values) else: ActiveModel.browse(res_id).message_post( message_type=wizard.message_type, subtype_id=subtype_id, **mail_values) if wizard.composition_mode == 'mass_mail': batch_mails.send(auto_commit=auto_commit) return {'type': 'ir.actions.act_window_close'}