コード例 #1
0
ファイル: account_tax.py プロジェクト: malakhA/coffice
 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)
コード例 #2
0
    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
コード例 #3
0
    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,
            'datas': base64.encodestring(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
コード例 #4
0
ファイル: ir_rule.py プロジェクト: malakhA/coffice
    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)
コード例 #5
0
    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,
                                       })
コード例 #6
0
ファイル: res_lang.py プロジェクト: malakhA/coffice
    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
コード例 #7
0
ファイル: test_base.py プロジェクト: malakhA/coffice
 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")
コード例 #8
0
ファイル: sale_order.py プロジェクト: malakhA/coffice
    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
コード例 #9
0
 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
コード例 #10
0
 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
コード例 #11
0
ファイル: base_automation.py プロジェクト: malakhA/coffice
 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.search(domain), domain
     else:
         return records, None
コード例 #12
0
ファイル: sms_composer.py プロジェクト: malakhA/coffice
 def _compute_recipients_count(self):
     self.res_ids_count = len(literal_eval(
         self.res_ids)) if self.res_ids else 0
     if self.res_model:
         self.active_domain_count = self.env[self.res_model].search_count(
             safe_eval(self.active_domain or '[]'))
     else:
         self.active_domain_count = 0
コード例 #13
0
ファイル: crm_team.py プロジェクト: malakhA/coffice
 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
コード例 #14
0
ファイル: snailmail_letter.py プロジェクト: malakhA/coffice
    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
コード例 #15
0
 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
コード例 #16
0
ファイル: sms_composer.py プロジェクト: malakhA/coffice
 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
コード例 #17
0
    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
コード例 #18
0
ファイル: account_tax.py プロジェクト: malakhA/coffice
 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)
コード例 #19
0
ファイル: project.py プロジェクト: malakhA/coffice
 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)
コード例 #20
0
ファイル: base_automation.py プロジェクト: malakhA/coffice
    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()
コード例 #21
0
    def retrieve_attachment(self, record):
        '''Retrieve an attachment for a specific record.

        :param record: The record owning of the attachment.
        :param attachment_name: The optional name of the attachment.
        :return: A recordset of length <=1 or None
        '''
        attachment_name = safe_eval(self.attachment, {
            'object': record,
            'time': time
        }) if self.attachment else ''
        if not attachment_name:
            return None
        return self.env['ir.attachment'].search(
            [('name', '=', attachment_name), ('res_model', '=', self.model),
             ('res_id', '=', record.id)],
            limit=1)
コード例 #22
0
ファイル: test_base.py プロジェクト: malakhA/coffice
    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 coffice", mode="exec")

        # no dunder
        with self.assertRaises(NameError):
            safe_eval("self.__name__", {'self': self}, mode="exec")
コード例 #23
0
ファイル: crm_team.py プロジェクト: malakhA/coffice
    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 not user_team_id:
            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, COffice 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
コード例 #24
0
    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
コード例 #25
0
    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
コード例 #26
0
ファイル: ir_rule.py プロジェクト: malakhA/coffice
    def _compute_domain(self, model_name, mode="read"):
        rules = self._get_rules(model_name, mode=mode)
        if not rules:
            return

        # browse user and rules as SUPERUSER_ID to avoid access errors!
        eval_context = self._eval_context()
        user_groups = self.env.user.groups_id
        global_domains = []                     # list of domains
        group_domains = []                      # list of domains
        for rule in rules.sudo():
            # evaluate the domain for the current user
            dom = safe_eval(rule.domain_force, eval_context) if rule.domain_force else []
            dom = expression.normalize_domain(dom)
            if not rule.groups:
                global_domains.append(dom)
            elif rule.groups & user_groups:
                group_domains.append(dom)

        # combine global domains and group domains
        if not group_domains:
            return expression.AND(global_domains)
        return expression.AND(global_domains + [expression.OR(group_domains)])
コード例 #27
0
    def _get_recipients(self):
        if self.mailing_domain:
            domain = safe_eval(self.mailing_domain)
            res_ids = self.env[self.mailing_model_real].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_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
コード例 #28
0
ファイル: ir_filters.py プロジェクト: malakhA/coffice
 def _get_eval_domain(self):
     self.ensure_one()
     return safe_eval(self.domain, {
         'datetime': datetime,
         'context_today': datetime.datetime.now,
     })
コード例 #29
0
    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. """
        notif_layout = self._context.get('custom_layout')
        # Several custom layouts make use of the model description at rendering, e.g. in the
        # 'View <document>' button. Some models are used for different business concepts, such as
        # 'purchase.order' which is used for a RFQ and and PO. To avoid confusion, we must use a
        # different wording depending on the state of the object.
        # Therefore, we can set the description in the context from the beginning to avoid falling
        # back on the regular display_name retrieved in '_notify_prepare_template_context'.
        model_description = self._context.get('model_description')
        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)
                new_attachment_ids.reverse()
                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 and hasattr(self.env[wizard.model], 'message_post') else self.env['mail.thread']
            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(safe_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.env['ir.model.data'].xmlid_to_res_id('mail.mt_comment')

            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.items():
                    if wizard.composition_mode == 'mass_mail':
                        batch_mails |= Mail.create(mail_values)
                    else:
                        post_params = dict(
                            message_type=wizard.message_type,
                            subtype_id=subtype_id,
                            email_layout_xmlid=notif_layout,
                            add_sign=not bool(wizard.template_id),
                            mail_auto_delete=wizard.template_id.auto_delete if wizard.template_id else False,
                            model_description=model_description)
                        post_params.update(mail_values)
                        if ActiveModel._name == 'mail.thread':
                            if wizard.model:
                                post_params['model'] = wizard.model
                                post_params['res_id'] = res_id
                            if not ActiveModel.message_notify(**post_params):
                                # if message_notify returns an empty record set, no recipients where found.
                                raise UserError(_("No recipient found."))
                        else:
                            ActiveModel.browse(res_id).message_post(**post_params)

                if wizard.composition_mode == 'mass_mail':
                    batch_mails.send(auto_commit=auto_commit)
コード例 #30
0
 def _check_alias_defaults(self):
     for alias in self:
         try:
             dict(safe_eval(alias.alias_defaults))
         except Exception:
             raise ValidationError(_('Invalid expression, it must be a literal python dictionary definition e.g. "{\'field\': \'value\'}"'))