[MERGE] various: support batch creation in marketing apps

SPECIFICATIONS

By default, ORM supports creation of records in batch (one SQL INSERT by
record but batching other operations). When a create override on a given
model is specified as api.model or api.model_create_single this ability
is disabled and records are created one by one. This leads to performance
losses.

In this merge we update create to support batch creation. This allows to
gain performances linked to batch code improvements. Note that some of the
changes done in this merge will not have much impact because some records
are always created one by one. Converting them
  * encourages new overrides to support multi record creation;
  * provides the basis for a potential future API change to enforce batch
    creation support;

LINKS

Task ID-2330149
COM PR odoo/odoo#61246
ENT PR odoo/enterprise#14561

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2020-12-03 13:04:47 +01:00
committed by GitHub
33 changed files with 327 additions and 262 deletions
+10 -9
View File
@@ -67,15 +67,16 @@ class ResPartnerBank(models.Model):
raise UserError(_("Cannot compute the BBAN because the account number is not an IBAN."))
return get_bban_from_iban(self.acc_number)
@api.model
def create(self, vals):
if vals.get('acc_number'):
try:
validate_iban(vals['acc_number'])
vals['acc_number'] = pretty_iban(normalize_iban(vals['acc_number']))
except ValidationError:
pass
return super(ResPartnerBank, self).create(vals)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('acc_number'):
try:
validate_iban(vals['acc_number'])
vals['acc_number'] = pretty_iban(normalize_iban(vals['acc_number']))
except ValidationError:
pass
return super(ResPartnerBank, self).create(vals_list)
def write(self, vals):
if vals.get('acc_number'):
+2 -2
View File
@@ -9,8 +9,8 @@ class Board(models.AbstractModel):
_description = "Board"
_auto = False
@api.model
def create(self, vals):
@api.model_create_multi
def create(self, vals_list):
return self
@api.model
@@ -82,11 +82,11 @@ class CRMRevealRule(models.Model):
(self._cr.dbname, 'res.partner', self.env.user.partner_id.id),
{'type': 'simple_notification', 'title': _('Missing Library'), 'message': message, 'sticky': True, 'warning': True})
@api.model
def create(self, vals):
@api.model_create_multi
def create(self, vals_list):
self.clear_caches() # Clear the cache in order to recompute _get_active_rules
self._assert_geoip()
return super(CRMRevealRule, self).create(vals)
return super().create(vals_list)
def write(self, vals):
fields_set = {
+7 -6
View File
@@ -72,12 +72,13 @@ class Digest(models.Model):
def _onchange_periodicity(self):
self.next_run_date = self._get_next_run_date()
@api.model
def create(self, vals):
digest = super(Digest, self).create(vals)
if not digest.next_run_date:
digest.next_run_date = digest._get_next_run_date()
return digest
@api.model_create_multi
def create(self, vals_list):
digests = super().create(vals_list)
for digest in digests:
if not digest.next_run_date:
digest.next_run_date = digest._get_next_run_date()
return digests
# ------------------------------------------------------------
# ACTIONS
+11 -9
View File
@@ -481,16 +481,18 @@ class EventEvent(models.Model):
def _read_group_stage_ids(self, stages, domain, order):
return self.env['event.stage'].search([])
@api.model
def create(self, vals):
# Temporary fix for ``seats_limited`` and ``date_tz`` required fields (see ``_compute_from_event_type``
vals.update(self._sync_required_computed(vals))
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
# Temporary fix for ``seats_limited`` and ``date_tz`` required fields (see ``_compute_from_event_type``
vals.update(self._sync_required_computed(vals))
res = super(EventEvent, self).create(vals)
if res.organizer_id:
res.message_subscribe([res.organizer_id.id])
res.flush()
return res
events = super(EventEvent, self).create(vals_list)
for res in events:
if res.organizer_id:
res.message_subscribe([res.organizer_id.id])
events.flush()
return events
def write(self, vals):
res = super(EventEvent, self).write(vals)
+3 -3
View File
@@ -80,9 +80,9 @@ Example configuration for the postfix mta running locally:
odoo_mailgate: "|/path/to/odoo-mailgate.py --host=localhost -u %(uid)d -p PASSWORD -d %(dbname)s"
""" % conf
@api.model
def create(self, values):
res = super(FetchmailServer, self).create(values)
@api.model_create_multi
def create(self, vals_list):
res = super(FetchmailServer, self).create(vals_list)
self._update_cron()
return res
+5 -4
View File
@@ -52,10 +52,11 @@ class BadgeUser(models.Model):
return True
@api.model
def create(self, vals):
self.env['gamification.badge'].browse(vals['badge_id']).check_granting()
return super(BadgeUser, self).create(vals)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
self.env['gamification.badge'].browse(vals['badge_id']).check_granting()
return super().create(vals_list)
class GamificationBadge(models.Model):
+9 -9
View File
@@ -154,18 +154,18 @@ class Challenge(models.Model):
return template.id if template else False
@api.model
def create(self, vals):
@api.model_create_multi
def create(self, vals_list):
"""Overwrite the create method to add the user of groups"""
for vals in vals_list:
if vals.get('user_domain'):
users = self._get_challenger_users(ustr(vals.get('user_domain')))
if vals.get('user_domain'):
users = self._get_challenger_users(ustr(vals.get('user_domain')))
if not vals.get('user_ids'):
vals['user_ids'] = []
vals['user_ids'].extend((4, user.id) for user in users)
if not vals.get('user_ids'):
vals['user_ids'] = []
vals['user_ids'].extend((4, user.id) for user in users)
return super(Challenge, self).create(vals)
return super().create(vals_list)
def write(self, vals):
if vals.get('user_domain'):
+13 -11
View File
@@ -122,14 +122,16 @@ class GoalDefinition(models.Model):
error=e
))
@api.model
def create(self, vals):
definition = super(GoalDefinition, self).create(vals)
if definition.computation_mode in ('count', 'sum'):
definition._check_domain_validity()
if vals.get('field_id'):
definition._check_model_validity()
return definition
@api.model_create_multi
def create(self, vals_list):
definitions = super(GoalDefinition, self).create(vals_list)
definitions.filtered_domain([
('computation_mode', 'in', ['count', 'sum']),
])._check_domain_validity()
definitions.filtered_domain([
('field_id', '=', 'True'),
])._check_model_validity()
return definitions
def write(self, vals):
res = super(GoalDefinition, self).write(vals)
@@ -386,9 +388,9 @@ class Goal(models.Model):
next goal update."""
return self.write({'state': 'inprogress'})
@api.model
def create(self, vals):
return super(Goal, self.with_context(no_remind_goal=True)).create(vals)
@api.model_create_multi
def create(self, vals_list):
return super(Goal, self.with_context(no_remind_goal=True)).create(vals_list)
def write(self, vals):
"""Overwrite the write method to update the last_update field to today
@@ -34,11 +34,12 @@ class MailMessageSubtype(models.Model):
})
return department_subtype
@api.model
def create(self, vals):
result = super(MailMessageSubtype, self).create(vals)
if result.res_model in ['hr.leave', 'hr.leave.allocation']:
result._update_department_subtype()
@api.model_create_multi
def create(self, vals_list):
result = super(MailMessageSubtype, self).create(vals_list)
result.filtered(
lambda st: st.res_model in ['hr.leave', 'hr.leave.allocation']
)._update_department_subtype()
return result
def write(self, vals):
+9 -8
View File
@@ -23,14 +23,15 @@ class ResCompany(models.Model):
return False
return self.country_id and self.country_id.code in self._get_unalterable_country()
@api.model
def create(self, vals):
company = super(ResCompany, self).create(vals)
#when creating a new french company, create the securisation sequence as well
if company._is_accounting_unalterable():
sequence_fields = ['l10n_fr_closing_sequence_id']
company._create_secure_sequence(sequence_fields)
return company
@api.model_create_multi
def create(self, vals_list):
companies = super().create(vals_list)
for company in companies:
#when creating a new french company, create the securisation sequence as well
if company._is_accounting_unalterable():
sequence_fields = ['l10n_fr_closing_sequence_id']
company._create_secure_sequence(sequence_fields)
return companies
def write(self, vals):
res = super(ResCompany, self).write(vals)
@@ -26,14 +26,15 @@ class ResCompany(models.Model):
l10n_fr_pos_cert_sequence_id = fields.Many2one('ir.sequence')
@api.model
def create(self, vals):
company = super(ResCompany, self).create(vals)
#when creating a new french company, create the securisation sequence as well
if company._is_accounting_unalterable():
sequence_fields = ['l10n_fr_pos_cert_sequence_id']
company._create_secure_sequence(sequence_fields)
return company
@api.model_create_multi
def create(self, vals_list):
companies = super().create(vals_list)
for company in companies:
#when creating a new french company, create the securisation sequence as well
if company._is_accounting_unalterable():
sequence_fields = ['l10n_fr_pos_cert_sequence_id']
company._create_secure_sequence(sequence_fields)
return companies
def write(self, vals):
res = super(ResCompany, self).write(vals)
+2 -2
View File
@@ -11,11 +11,11 @@ class IrConfigParameter(models.Model):
def create(self, vals_list):
for vals in vals_list:
if vals.get('key') in ['mail.bounce.alias', 'mail.catchall.alias']:
vals['value'] = self.env['mail.alias']._clean_and_check_unique(vals.get('value'))
vals['value'] = self.env['mail.alias']._clean_and_check_unique([vals.get('value')])[0]
return super().create(vals_list)
def write(self, vals):
for parameter in self:
if 'value' in vals and parameter.key in ['mail.bounce.alias', 'mail.catchall.alias'] and vals['value'] != parameter.value:
vals['value'] = self.env['mail.alias']._clean_and_check_unique(vals.get('value'))
vals['value'] = self.env['mail.alias']._clean_and_check_unique([vals.get('value')])[0]
return super().write(vals)
+27 -26
View File
@@ -346,34 +346,35 @@ class MailActivity(models.Model):
# ORM overrides
# ------------------------------------------------------
@api.model
def create(self, values):
activity = super(MailActivity, self).create(values)
need_sudo = False
try: # in multicompany, reading the partner might break
partner_id = activity.user_id.partner_id.id
except exceptions.AccessError:
need_sudo = True
partner_id = activity.user_id.sudo().partner_id.id
@api.model_create_multi
def create(self, vals_list):
activities = super(MailActivity, self).create(vals_list)
for activity in activities:
need_sudo = False
try: # in multicompany, reading the partner might break
partner_id = activity.user_id.partner_id.id
except exceptions.AccessError:
need_sudo = True
partner_id = activity.user_id.sudo().partner_id.id
# send a notification to assigned user; in case of manually done activity also check
# target has rights on document otherwise we prevent its creation. Automated activities
# are checked since they are integrated into business flows that should not crash.
if activity.user_id != self.env.user:
if not activity.automated:
activity._check_access_assignation()
if not self.env.context.get('mail_activity_quick_update', False):
if need_sudo:
activity.sudo().action_notify()
else:
activity.action_notify()
# send a notification to assigned user; in case of manually done activity also check
# target has rights on document otherwise we prevent its creation. Automated activities
# are checked since they are integrated into business flows that should not crash.
if activity.user_id != self.env.user:
if not activity.automated:
activity._check_access_assignation()
if not self.env.context.get('mail_activity_quick_update', False):
if need_sudo:
activity.sudo().action_notify()
else:
activity.action_notify()
self.env[activity.res_model].browse(activity.res_id).message_subscribe(partner_ids=[partner_id])
if activity.date_deadline <= fields.Date.today():
self.env['bus.bus'].sendone(
(self._cr.dbname, 'res.partner', activity.user_id.partner_id.id),
{'type': 'activity_updated', 'activity_created': True})
return activity
self.env[activity.res_model].browse(activity.res_id).message_subscribe(partner_ids=[partner_id])
if activity.date_deadline <= fields.Date.today():
self.env['bus.bus'].sendone(
(self._cr.dbname, 'res.partner', activity.user_id.partner_id.id),
{'type': 'activity_updated', 'activity_created': True})
return activities
def write(self, values):
if values.get('user_id'):
+33 -18
View File
@@ -107,22 +107,34 @@ class Alias(models.Model):
except Exception:
raise ValidationError(_('Invalid expression, it must be a literal python dictionary definition e.g. "{\'field\': \'value\'}"'))
@api.model
def create(self, vals):
""" Creates an email.alias record according to the values provided in ``vals``,
with 2 alterations: the ``alias_name`` value may be cleaned by replacing
certain unsafe characters, and the ``alias_model_id`` value will set to the
model ID of the ``model_name`` context value, if provided. Also, it raises
UserError if given alias name is already assigned.
@api.model_create_multi
def create(self, vals_list):
""" Creates email.alias records according to the values provided in
``vals`` with 1 alteration:
* ``alias_name`` value may be cleaned by replacing certain unsafe
characters;
:raise UserError: if given alias_name is already assigned or there are
duplicates in given vals_list;
"""
if vals.get('alias_name'):
vals['alias_name'] = self._clean_and_check_unique(vals.get('alias_name'))
return super(Alias, self).create(vals)
alias_names = [vals['alias_name'] for vals in vals_list if vals.get('alias_name')]
if alias_names:
sanitized_names = self._clean_and_check_unique(alias_names)
for vals in vals_list:
if vals.get('alias_name'):
vals['alias_name'] = sanitized_names[alias_names.index(vals['alias_name'])]
return super(Alias, self).create(vals_list)
def write(self, vals):
""""Raises UserError if given alias name is already assigned"""
if vals.get('alias_name') and self.ids:
vals['alias_name'] = self._clean_and_check_unique(vals.get('alias_name'))
if len(self) > 1:
raise UserError(_(
'Email alias %(alias_name)s cannot be used on %(count)d records at the same time. Please update records one by one.',
alias_name=vals['alias_name'], count=len(self)
))
vals['alias_name'] = self._clean_and_check_unique([vals.get('alias_name')])[0]
return super(Alias, self).write(vals)
def name_get(self):
@@ -140,22 +152,25 @@ class Alias(models.Model):
res.append((record['id'], _("Inactive Alias")))
return res
def _clean_and_check_unique(self, name):
def _clean_and_check_unique(self, names):
"""When an alias name appears to already be an email, we keep the local
part only. A sanitizing / cleaning is also performed on the name. If
name already exists an UserError is raised. """
sanitized_name = remove_accents(name).lower().split('@')[0]
sanitized_name = re.sub(r'[^\w+.]+', '-', sanitized_name)
sanitized_name = sanitized_name.encode('ascii', errors='replace').decode()
sanitized_names = []
for name in names:
sanitized_name = remove_accents(name).lower().split('@')[0]
sanitized_name = re.sub(r'[^\w+.]+', '-', sanitized_name)
sanitized_name = sanitized_name.encode('ascii', errors='replace').decode()
sanitized_names.append(sanitized_name)
catchall_alias = self.env['ir.config_parameter'].sudo().get_param('mail.catchall.alias')
bounce_alias = self.env['ir.config_parameter'].sudo().get_param('mail.bounce.alias')
domain = [('alias_name', '=', sanitized_name)]
domain = [('alias_name', 'in', sanitized_names)]
if self:
domain += [('id', 'not in', self.ids)]
if sanitized_name in [catchall_alias, bounce_alias] or self.search_count(domain):
if any(sanitized_name in [catchall_alias, bounce_alias] for sanitized_name in sanitized_names) or self.search_count(domain):
raise UserError(_('The e-mail alias is already used. Please enter another one.'))
return sanitized_name
return sanitized_names
def open_document(self):
if not self.alias_model_id or not self.alias_force_thread_id:
+18 -3
View File
@@ -27,14 +27,29 @@ class AliasMixin(models.AbstractModel):
@api.model_create_multi
def create(self, vals_list):
""" Create a record with each ``vals`` or ``vals_list`` and create a corresponding alias. """
valid_vals_list = []
# prepare all alias values
alias_vals_list, record_vals_list = [], []
for vals in vals_list:
new_alias = not vals.get('alias_id')
if new_alias:
alias_vals, record_vals = self._alias_filter_fields(vals)
alias_vals.update(self._alias_get_creation_values())
alias = self.env['mail.alias'].sudo().create(alias_vals)
record_vals['alias_id'] = alias.id
alias_vals_list.append(alias_vals)
record_vals_list.append(record_vals)
# create all aliases
alias_ids = []
if alias_vals_list:
alias_ids = iter(self.env['mail.alias'].sudo().create(alias_vals_list).ids)
# update alias values in create vals directly
valid_vals_list = []
record_vals_iter = iter(record_vals_list)
for vals in vals_list:
new_alias = not vals.get('alias_id')
if new_alias:
record_vals = next(record_vals_iter)
record_vals['alias_id'] = next(alias_ids)
valid_vals_list.append(record_vals)
else:
valid_vals_list.append(vals)
+21 -18
View File
@@ -204,26 +204,27 @@ class Channel(models.Model):
else:
self.moderator_ids |= self.env.user
@api.model
def create(self, vals):
# ensure image at quick create
if not vals.get('image_128'):
defaults = self.default_get(['image_128'])
vals['image_128'] = defaults['image_128']
@api.model_create_multi
def create(self, vals_list):
defaults = self.default_get(['image_128'])
for vals in vals_list:
# ensure image at quick create
if not vals.get('image_128'):
vals['image_128'] = defaults['image_128']
# Create channel and alias
channel = super(Channel, self.with_context(
channels = super(Channel, self.with_context(
mail_create_nolog=True, mail_create_nosubscribe=True)
).create(vals)
).create(vals_list)
if vals.get('group_ids'):
channel._subscribe_users()
channels._subscribe_users()
# make channel listen itself: posting on a channel notifies the channel
if not self._context.get('mail_channel_noautofollow'):
channel.message_subscribe(channel_ids=[channel.id])
for channel in channels:
channel.message_subscribe(channel_ids=[channel.id])
return channel
return channels
def unlink(self):
# Delete mail.channel
@@ -266,13 +267,15 @@ class Channel(models.Model):
def _subscribe_users(self):
to_create = []
for mail_channel in self:
partners_to_add = mail_channel.group_ids.users.partner_id - mail_channel.channel_partner_ids
to_create += [{
'channel_id': mail_channel.id,
'partner_id': partner.id,
} for partner in partners_to_add]
if mail_channel.group_ids:
partners_to_add = mail_channel.group_ids.users.partner_id - mail_channel.channel_partner_ids
to_create += [{
'channel_id': mail_channel.id,
'partner_id': partner.id,
} for partner in partners_to_add]
self.env['mail.channel.partner'].create(to_create)
if to_create:
self.env['mail.channel.partner'].create(to_create)
def action_follow(self):
self.ensure_one()
+3 -3
View File
@@ -41,10 +41,10 @@ class MailMessageSubtype(models.Model):
sequence = fields.Integer('Sequence', default=1, help="Used to order subtypes.")
hidden = fields.Boolean('Hidden', help="Hide the subtype in the follower options")
@api.model
def create(self, vals):
@api.model_create_multi
def create(self, vals_list):
self.clear_caches()
return super(MailMessageSubtype, self).create(vals)
return super(MailMessageSubtype, self).create(vals_list)
def write(self, vals):
self.clear_caches()
+9 -7
View File
@@ -293,13 +293,15 @@ class MassMailing(models.Model):
# ORM
# ------------------------------------------------------
@api.model
def create(self, values):
if values.get('subject') and not values.get('name'):
values['name'] = "%s %s" % (values['subject'], datetime.strftime(fields.datetime.now(), tools.DEFAULT_SERVER_DATETIME_FORMAT))
if values.get('body_html'):
values['body_html'] = self._convert_inline_images_to_urls(values['body_html'])
return super(MassMailing, self).create(values)
@api.model_create_multi
def create(self, vals_list):
now = fields.Datetime.now()
for values in vals_list:
if values.get('subject') and not values.get('name'):
values['name'] = "%s %s" % (values['subject'], now)
if values.get('body_html'):
values['body_html'] = self._convert_inline_images_to_urls(values['body_html'])
return super().create(vals_list)
def write(self, values):
if values.get('body_html'):
@@ -27,15 +27,17 @@ class MassMailingContactListRel(models.Model):
'A contact cannot be subscribed multiple times to the same list!')
]
@api.model
def create(self, vals):
if 'opt_out' in vals:
vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now()
return super(MassMailingContactListRel, self).create(vals)
@api.model_create_multi
def create(self, vals_list):
now = fields.Datetime.now()
for vals in vals_list:
if 'opt_out' in vals:
vals['unsubscription_date'] = now if vals['opt_out'] else False
return super().create(vals_list)
def write(self, vals):
if 'opt_out' in vals:
vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now()
vals['unsubscription_date'] = fields.Datetime.now() if vals['opt_out'] else False
return super(MassMailingContactListRel, self).write(vals)
+5 -5
View File
@@ -325,11 +325,11 @@ class PaymentAcquirer(models.Model):
journals += journal
return journals
@api.model
def create(self, vals):
record = super(PaymentAcquirer, self).create(vals)
record._check_required_if_provider()
return record
@api.model_create_multi
def create(self, vals_list):
records = super(PaymentAcquirer, self).create(vals_list)
records._check_required_if_provider()
return records
def write(self, vals):
result = super(PaymentAcquirer, self).write(vals)
+21 -17
View File
@@ -7,24 +7,28 @@ from odoo import api, models, _
class ResCompany(models.Model):
_inherit = "res.company"
@api.model
def create(self, vals):
new_company = super(ResCompany, self).create(vals)
@api.model_create_multi
def create(self, vals_list):
companies = super(ResCompany, self).create(vals_list)
ProductPricelist = self.env['product.pricelist']
pricelist = ProductPricelist.search([('currency_id', '=', new_company.currency_id.id), ('company_id', '=', False)], limit=1)
if not pricelist:
params = {'currency': new_company.currency_id.name}
pricelist = ProductPricelist.create({
'name': _("Default %(currency)s pricelist") % params,
'currency_id': new_company.currency_id.id,
})
self.env['ir.property']._set_default(
'property_product_pricelist',
'res.partner',
pricelist,
new_company,
)
return new_company
for new_company in companies:
pricelist = ProductPricelist.search([
('currency_id', '=', new_company.currency_id.id),
('company_id', '=', False)
], limit=1)
if not pricelist:
params = {'currency': new_company.currency_id.name}
pricelist = ProductPricelist.create({
'name': _("Default %(currency)s pricelist") % params,
'currency_id': new_company.currency_id.id,
})
self.env['ir.property']._set_default(
'property_product_pricelist',
'res.partner',
pricelist,
new_company,
)
return companies
def write(self, values):
# When we modify the currency of the company, we reflect the change on the list0 pricelist, if
+6 -5
View File
@@ -125,11 +125,12 @@ class Rating(models.Model):
else:
rating.rating_text = 'no_rating'
@api.model
def create(self, values):
if values.get('res_model_id') and values.get('res_id'):
values.update(self._find_parent_data(values))
return super(Rating, self).create(values)
@api.model_create_multi
def create(self, vals_list):
for values in vals_list:
if values.get('res_model_id') and values.get('res_id'):
values.update(self._find_parent_data(values))
return super().create(vals_list)
def write(self, values):
if values.get('res_model_id') and values.get('res_id'):
+5 -6
View File
@@ -85,12 +85,11 @@ class CrmTeam(models.Model):
# CRUD
# ------------------------------------------------------------
@api.model
def create(self, values):
team = super(CrmTeam, self.with_context(mail_create_nosubscribe=True)).create(values)
if values.get('member_ids'):
team._add_members_to_favorites()
return team
@api.model_create_multi
def create(self, vals_list):
teams = super(CrmTeam, self.with_context(mail_create_nosubscribe=True)).create(vals_list)
teams.filtered(lambda t: t.member_ids)._add_members_to_favorites()
return teams
def write(self, values):
res = super(CrmTeam, self).write(values)
+31 -26
View File
@@ -77,35 +77,40 @@ class SnailmailLetter(models.Model):
for res in self:
res.reference = "%s,%s" % (res.model, res.res_id)
@api.model
def create(self, vals):
msg_id = self.env[vals['model']].browse(vals['res_id']).message_post(
body=_("Letter sent by post with Snailmail"),
message_type='snailmail'
)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
msg_id = self.env[vals['model']].browse(vals['res_id']).message_post(
body=_("Letter sent by post with Snailmail"),
message_type='snailmail',
)
partner_id = self.env['res.partner'].browse(vals['partner_id'])
vals.update({
'message_id': msg_id.id,
'street': partner_id.street,
'street2': partner_id.street2,
'zip': partner_id.zip,
'city': partner_id.city,
'state_id': partner_id.state_id.id,
'country_id': partner_id.country_id.id,
})
letter = super(SnailmailLetter, self).create(vals)
partner_id = self.env['res.partner'].browse(vals['partner_id'])
vals.update({
'message_id': msg_id.id,
'street': partner_id.street,
'street2': partner_id.street2,
'zip': partner_id.zip,
'city': partner_id.city,
'state_id': partner_id.state_id.id,
'country_id': partner_id.country_id.id,
})
letters = super().create(vals_list)
self.env['mail.notification'].sudo().create({
'mail_message_id': msg_id.id,
'res_partner_id': partner_id.id,
'notification_type': 'snail',
'letter_id': letter.id,
'is_read': True, # discard Inbox notification
'notification_status': 'ready',
})
notification_vals = []
for letter in letters:
notification_vals.append({
'mail_message_id': letter.message_id.id,
'res_partner_id': letter.partner_id.id,
'notification_type': 'snail',
'letter_id': letter.id,
'is_read': True, # discard Inbox notification
'notification_status': 'ready',
})
return letter
self.env['mail.notification'].sudo().create(notification_vals)
return letters
def _fetch_attachment(self):
"""
+10 -9
View File
@@ -149,15 +149,16 @@ class SurveyInvite(models.TransientModel):
invite.subject = invite.template_id.subject
invite.body = invite.template_id.body_html
@api.model
def create(self, values):
if values.get('template_id') and not (values.get('body') or values.get('subject')):
template = self.env['mail.template'].browse(values['template_id'])
if not values.get('subject'):
values['subject'] = template.subject
if not values.get('body'):
values['body'] = template.body_html
return super(SurveyInvite, self).create(values)
@api.model_create_multi
def create(self, vals_list):
for values in vals_list:
if values.get('template_id') and not (values.get('body') or values.get('subject')):
template = self.env['mail.template'].browse(values['template_id'])
if not values.get('subject'):
values['subject'] = template.subject
if not values.get('body'):
values['body'] = template.body_html
return super().create(vals_list)
# ------------------------------------------------------
# Wizard validation and send
+5 -5
View File
@@ -755,13 +755,13 @@ class Base(models.AbstractModel):
class ResCompany(models.Model):
_inherit = 'res.company'
@api.model
def create(self, values):
res = super().create(values)
@api.model_create_multi
def create(self, vals_list):
companies = super().create(vals_list)
style_fields = {'external_report_layout_id', 'font', 'primary_color', 'secondary_color'}
if not style_fields.isdisjoint(values):
if any(not style_fields.isdisjoint(values) for values in vals_list):
self._update_asset_style()
return res
return companies
def write(self, values):
res = super().write(values)
+5 -5
View File
@@ -158,11 +158,11 @@ class Event(models.Model):
# CRUD
# ------------------------------------------------------------
@api.model
def create(self, vals):
res = super(Event, self).create(vals)
res._update_website_menus()
return res
@api.model_create_multi
def create(self, vals_list):
events = super().create(vals_list)
events._update_website_menus()
return events
def write(self, vals):
menus_state_by_field = self._split_menus_state_by_field()
+8 -5
View File
@@ -180,11 +180,14 @@ class Forum(models.Model):
def _set_default_faq(self):
self.faq = self.env['ir.ui.view']._render_template('website_forum.faq_accordion', {"forum": self}).decode('utf-8')
@api.model
def create(self, values):
res = super(Forum, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values)
res._set_default_faq() # will trigger a write and call update_website_count
return res
@api.model_create_multi
def create(self, vals_list):
forums = super(
Forum,
self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)
).create(vals_list)
forums._set_default_faq() # will trigger a write and call update_website_count
return forums
def write(self, vals):
if 'privacy' in vals:
@@ -33,12 +33,11 @@ class Channel(models.Model):
for channel in self:
channel.product_sale_revenues = rg_data.get(channel.product_id.id, 0)
@api.model
def create(self, vals):
channel = super(Channel, self).create(vals)
if channel.enroll == 'payment':
channel._synchronize_product_publish()
return channel
@api.model_create_multi
def create(self, vals_list):
channels = super(Channel, self).create(vals_list)
channels.filtered(lambda channel: channel.enroll == 'payment')._synchronize_product_publish()
return channels
def write(self, vals):
res = super(Channel, self).write(vals)
@@ -47,6 +46,8 @@ class Channel(models.Model):
return res
def _synchronize_product_publish(self):
if not self:
return
self.filtered(lambda channel: channel.is_published and not channel.product_id.is_published).sudo().product_id.write({'is_published': True})
self.filtered(lambda channel: not channel.is_published and channel.product_id.is_published).sudo().product_id.write({'is_published': False})
+8 -5
View File
@@ -7,12 +7,15 @@ from odoo import api, models
class Users(models.Model):
_inherit = 'res.users'
@api.model
def create(self, values):
@api.model_create_multi
def create(self, vals_list):
""" Trigger automatic subscription based on user groups """
user = super(Users, self).create(values)
self.env['slide.channel'].sudo().search([('enroll_group_ids', 'in', user.groups_id.ids)])._action_add_members(user.partner_id)
return user
users = super(Users, self).create(vals_list)
for user in users:
self.env['slide.channel'].sudo().search([
('enroll_group_ids', 'in', user.groups_id.ids)
])._action_add_members(user.partner_id)
return users
def write(self, vals):
""" Trigger automatic subscription based on updated user groups """
+3 -2
View File
@@ -35,8 +35,9 @@ class SlidePartnerRelation(models.Model):
completed = fields.Boolean('Completed')
quiz_attempts_count = fields.Integer('Quiz attempts count', default=0)
def create(self, values):
res = super(SlidePartnerRelation, self).create(values)
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
completed = res.filtered('completed')
if completed:
completed._set_completed_callback()
@@ -25,12 +25,11 @@ class Channel(models.Model):
return action
@api.model
def create(self, vals):
channel = super(Channel, self.with_context(mail_create_nosubscribe=True)).create(vals)
if channel.forum_id:
channel.forum_id.privacy = False
return channel
@api.model_create_multi
def create(self, vals_list):
channels = super(Channel, self.with_context(mail_create_nosubscribe=True)).create(vals_list)
channels.forum_id.privacy = False
return channels
def write(self, vals):
old_forum = self.forum_id