sale_order 的 tree视图全选,跳转wizard进行开发票操作的时候,
当我勾选“全选复选框”,context中带有动作视图的domain,但是当我选择了几个项目并执行向导时,上下文中找不到active_domain,我这边需要的是全选又不想带active_domain
源码的accoun_invoice是继承有发邮件通知的功能
class MailComposer(models.TransientModel):
""" Generic message composition wizard. You may inherit from this wizard
at model and view levels to provide specific features.
The behavior of the wizard depends on the composition_mode field:
- 'comment': post on a record. The wizard is pre-populated via ``get_record_data``
- 'mass_mail': wizard in mass mailing mode where the mail details can
contain template placeholders that will be merged with actual data
before being sent to each recipient.
"""
_name = 'mail.compose.message'
_inherit = 'mail.message'
_description = 'Email composition wizard'
_log_access = True
_batch_size = 500
@api.model
def default_get(self, fields):
""" Handle composition mode. Some details about context keys:
- comment: default mode, model and ID of a record the user comments
- default_model or active_model
- default_res_id or active_id
- reply: active_id of a message the user replies to
- default_parent_id or message_id or active_id: ID of the
mail.message we reply to
- message.res_model or default_model
- message.res_id or default_res_id
- mass_mail: model and IDs of records the user mass-mails
- active_ids: record IDs
- default_model or active_model
"""
result = super(MailComposer, self).default_get(fields)
# v6.1 compatibility mode
result['composition_mode'] = result.get('composition_mode', self._context.get('mail.compose.message.mode', 'comment'))
result['model'] = result.get('model', self._context.get('active_model'))
result['res_id'] = result.get('res_id', self._context.get('active_id'))
result['parent_id'] = result.get('parent_id', self._context.get('message_id'))
if 'no_auto_thread' not in result and (result['model'] not in self.env or not hasattr(self.env[result['model']], 'message_post')):
result['no_auto_thread'] = True
# default values according to composition mode - NOTE: reply is deprecated, fall back on comment
if result['composition_mode'] == 'reply':
result['composition_mode'] = 'comment'
vals = {}
if 'active_domain' in self._context: # not context.get() because we want to keep global [] domains
vals['use_active_domain'] = True
vals['active_domain'] = '%s' % self._context.get('active_domain')
if result['composition_mode'] == 'comment':
vals.update(self.get_record_data(result))
for field in vals:
if field in fields:
result[field] = vals[field]
# TDE HACK: as mailboxes used default_model='res.users' and default_res_id=uid
# (because of lack of an accessible pid), creating a message on its own
# profile may crash (res_users does not allow writing on it)
# Posting on its own profile works (res_users redirect to res_partner)
# but when creating the mail.message to create the mail.compose.message
# access rights issues may rise
# We therefore directly change the model and res_id
if result['model'] == 'res.users' and result['res_id'] == self._uid:
result['model'] = 'res.partner'
result['res_id'] = self.env.user.partner_id.id
if fields is not None:
[result.pop(field, None) for field in result.keys() if field not in fields]
return result
@api.model
def _get_composition_mode_selection(self):
return [('comment', 'Post on a document'),
('mass_mail', 'Email Mass Mailing'),
('mass_post', 'Post on Multiple Documents')]
composition_mode = fields.Selection(selection=_get_composition_mode_selection, string='Composition mode', default='comment')
partner_ids = fields.Many2many(
'res.partner', 'mail_compose_message_res_partner_rel',
'wizard_id', 'partner_id', 'Additional Contacts')
use_active_domain = fields.Boolean('Use active domain')
active_domain = fields.Text('Active domain', readonly=True)
attachment_ids = fields.Many2many(
'ir.attachment', 'mail_compose_message_ir_attachments_rel',
'wizard_id', 'attachment_id', 'Attachments')
is_log = fields.Boolean('Log an Internal Note',
help='Whether the message is an internal note (comment mode only)')
subject = fields.Char(default=False)
# mass mode options
notify = fields.Boolean('Notify followers', help='Notify followers of the document (mass post only)')
auto_delete = fields.Boolean('Delete Emails', help='Delete sent emails (mass mailing only)')
auto_delete_message = fields.Boolean('Delete Message Copy', help='Do not keep a copy of the email in the document communication history (mass mailing only)')
template_id = fields.Many2one(
'mail.template', 'Use template', index=True,
domain="[('model', '=', model)]")
# mail_message updated fields
message_type = fields.Selection(default="comment")
subtype_id = fields.Many2one(default=lambda self: self.sudo().env.ref('mail.mt_comment', raise_if_not_found=False).id)
触发邮件需要判断use_active_domain字段,在default_get中有此段判断赋值
if 'active_domain' in self._context: # not context.get() because we want to keep global [] domains
vals['use_active_domain'] = True
vals['active_domain'] = '%s' % self._context.get('active_domain')
会导致send_mail方法中查询account_invoice模型里面sale_order视图带有的domain,报错
ValueError: Invalid field u'invoice_status' in leaf "<osv.ExtendedLeaf: (u'invoice_status', u'=', u'to invoice') on account_invoice (ctx: )>"
send_mail 方法如下:
@api.multi
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)]
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)
elif wizard.composition_mode == 'single_mail': # 新增的邮件发送类型
mail_values['attachment_ids'] = [(6, 0, mail_values['attachment_ids'] or [])]
mail_values['partner_ids'] = [(6, 0, mail_values['partner_ids'] or [])]
mail_values['body_html'] = mail_values['body']
mail_values['recipient_ids'] = mail_values['partner_ids']
mail_id = Mail.create(mail_values)
Mail.browse([mail_id]).send() # 立即发送邮件
else:
subtype = 'mail.mt_comment'
if wizard.is_log or (wizard.composition_mode == 'mass_post' and not wizard.notify): # log a note: subtype is False
subtype = False
ActiveModel.browse(res_id).message_post(message_type='comment', subtype=subtype, **mail_values)
if wizard.composition_mode == 'mass_mail':
batch_mails.send(auto_commit=auto_commit)
return {'type': 'ir.actions.act_window_close'}
判断有误的代码是这段
if mass_mode and wizard.use_active_domain and wizard.model:
res_ids = self.env[wizard.model].search(eval(wizard.active_domain)).ids
解决方法:
由于是在context中传值的,所以起初想要想办法删除全选附带的active_domain,但是context是一个frozendict,只能另辟蹊径,从判断条件入手:
重写了default_get,增加对account_invoice模型的筛选,针对这一功能,跳过account_invoice 模型有active_domain的对use_active_domain的改写
if 'active_domain' in self._context: # not context.get() because we want to keep global [] domains
if result['model'] == 'account.invoice': # ➡️暂时修改
vals['use_active_domain'] = False
else:
vals['use_active_domain'] = True
vals['active_domain'] = '%s' % self._context.get('active_domain')
我在stackoverflow上找到一篇相反的帖子
active_domain not found in context when using wizard
I’m trying to pass the active_domain to a wizard.When I use :
self.env.context.get('active_doamin',False)
This works only when I check “select all check box” but when I select a few items and execute the wizard, I cannot find the active_domain in context, is this a bug or I’m doing it wrong?
P.S: I’m using Odoo 9
我正在尝试将 active_domain 传递给向导。当我使用:
self.env.context.get('active_doamin',False)
这仅在我选中“全选复选框”时有效,但是当我选择几个项目并执行向导时,我无法在上下文中找到 active_domain,这是一个错误还是我做错了?
四年多没有答案,希望能解决的指点一下。