diff --git a/addons/base_iban/models/res_partner_bank.py b/addons/base_iban/models/res_partner_bank.py index 2c9f4f868b9..dfe1031acd5 100644 --- a/addons/base_iban/models/res_partner_bank.py +++ b/addons/base_iban/models/res_partner_bank.py @@ -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'): diff --git a/addons/board/models/board.py b/addons/board/models/board.py index a3355755b2f..738f9850e08 100644 --- a/addons/board/models/board.py +++ b/addons/board/models/board.py @@ -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 diff --git a/addons/crm_iap_lead_website/models/crm_reveal_rule.py b/addons/crm_iap_lead_website/models/crm_reveal_rule.py index 698a7c64ce4..a2c824d47e6 100644 --- a/addons/crm_iap_lead_website/models/crm_reveal_rule.py +++ b/addons/crm_iap_lead_website/models/crm_reveal_rule.py @@ -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 = { diff --git a/addons/digest/models/digest.py b/addons/digest/models/digest.py index 6d780f8095b..97a8e25001d 100644 --- a/addons/digest/models/digest.py +++ b/addons/digest/models/digest.py @@ -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 diff --git a/addons/event/models/event_event.py b/addons/event/models/event_event.py index 942fa5f4328..e78651d9a55 100644 --- a/addons/event/models/event_event.py +++ b/addons/event/models/event_event.py @@ -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) diff --git a/addons/fetchmail/models/fetchmail.py b/addons/fetchmail/models/fetchmail.py index 0850f5e19ee..2e6c72333fa 100644 --- a/addons/fetchmail/models/fetchmail.py +++ b/addons/fetchmail/models/fetchmail.py @@ -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 diff --git a/addons/gamification/models/badge.py b/addons/gamification/models/badge.py index 81a9d0564e8..9dd276096da 100644 --- a/addons/gamification/models/badge.py +++ b/addons/gamification/models/badge.py @@ -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): diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py index 2a1be3c6b88..02b13f1ba89 100644 --- a/addons/gamification/models/challenge.py +++ b/addons/gamification/models/challenge.py @@ -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'): diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index ef48542a067..dad5b3b04fe 100644 --- a/addons/gamification/models/goal.py +++ b/addons/gamification/models/goal.py @@ -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 diff --git a/addons/hr_holidays/models/mail_message_subtype.py b/addons/hr_holidays/models/mail_message_subtype.py index f650ec1a898..613827e0629 100644 --- a/addons/hr_holidays/models/mail_message_subtype.py +++ b/addons/hr_holidays/models/mail_message_subtype.py @@ -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): diff --git a/addons/l10n_fr/models/res_company.py b/addons/l10n_fr/models/res_company.py index 4088a1afac4..6235d207b9a 100644 --- a/addons/l10n_fr/models/res_company.py +++ b/addons/l10n_fr/models/res_company.py @@ -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) diff --git a/addons/l10n_fr_pos_cert/models/res_company.py b/addons/l10n_fr_pos_cert/models/res_company.py index c701c6f348e..e509dd52005 100644 --- a/addons/l10n_fr_pos_cert/models/res_company.py +++ b/addons/l10n_fr_pos_cert/models/res_company.py @@ -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) diff --git a/addons/mail/models/ir_config_parameter.py b/addons/mail/models/ir_config_parameter.py index 40e153ba2cf..a3f67ce1331 100644 --- a/addons/mail/models/ir_config_parameter.py +++ b/addons/mail/models/ir_config_parameter.py @@ -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) diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py index 1ac59a08f3c..682e7dcddc6 100644 --- a/addons/mail/models/mail_activity.py +++ b/addons/mail/models/mail_activity.py @@ -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'): diff --git a/addons/mail/models/mail_alias.py b/addons/mail/models/mail_alias.py index 246451724c5..b1f61503476 100644 --- a/addons/mail/models/mail_alias.py +++ b/addons/mail/models/mail_alias.py @@ -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: diff --git a/addons/mail/models/mail_alias_mixin.py b/addons/mail/models/mail_alias_mixin.py index 12eb8d4406a..e38c8ccaab7 100644 --- a/addons/mail/models/mail_alias_mixin.py +++ b/addons/mail/models/mail_alias_mixin.py @@ -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) diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 10146ff98e8..5fdc1959bfb 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -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() diff --git a/addons/mail/models/mail_message_subtype.py b/addons/mail/models/mail_message_subtype.py index 43922018e89..ee754acdbed 100644 --- a/addons/mail/models/mail_message_subtype.py +++ b/addons/mail/models/mail_message_subtype.py @@ -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() diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index 06483415ab4..5715129952c 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -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'): diff --git a/addons/mass_mailing/models/mailing_contact.py b/addons/mass_mailing/models/mailing_contact.py index 6ebd1132820..16a13125359 100644 --- a/addons/mass_mailing/models/mailing_contact.py +++ b/addons/mass_mailing/models/mailing_contact.py @@ -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) diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index f750461a3d3..38c30e5988d 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -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) diff --git a/addons/product/models/res_company.py b/addons/product/models/res_company.py index 30ae4e95931..e9420ed5133 100644 --- a/addons/product/models/res_company.py +++ b/addons/product/models/res_company.py @@ -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 diff --git a/addons/rating/models/rating.py b/addons/rating/models/rating.py index c28e19d5890..bdaf8dbe3d9 100644 --- a/addons/rating/models/rating.py +++ b/addons/rating/models/rating.py @@ -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'): diff --git a/addons/sales_team/models/crm_team.py b/addons/sales_team/models/crm_team.py index 104baaf1aee..fd15b3f5e90 100644 --- a/addons/sales_team/models/crm_team.py +++ b/addons/sales_team/models/crm_team.py @@ -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) diff --git a/addons/snailmail/models/snailmail_letter.py b/addons/snailmail/models/snailmail_letter.py index 2eb19271b3c..cdee59e1b15 100644 --- a/addons/snailmail/models/snailmail_letter.py +++ b/addons/snailmail/models/snailmail_letter.py @@ -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): """ diff --git a/addons/survey/wizard/survey_invite.py b/addons/survey/wizard/survey_invite.py index df14dc1afd9..055f086eb7b 100644 --- a/addons/survey/wizard/survey_invite.py +++ b/addons/survey/wizard/survey_invite.py @@ -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 diff --git a/addons/web/models/models.py b/addons/web/models/models.py index 092dd9a1681..ba431b8addc 100644 --- a/addons/web/models/models.py +++ b/addons/web/models/models.py @@ -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) diff --git a/addons/website_event/models/event_event.py b/addons/website_event/models/event_event.py index 53a433df6a4..5dfcaa5a5c0 100644 --- a/addons/website_event/models/event_event.py +++ b/addons/website_event/models/event_event.py @@ -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() diff --git a/addons/website_forum/models/forum.py b/addons/website_forum/models/forum.py index 9ecffd1e5c1..a567da2ad66 100644 --- a/addons/website_forum/models/forum.py +++ b/addons/website_forum/models/forum.py @@ -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: diff --git a/addons/website_sale_slides/models/slide_channel.py b/addons/website_sale_slides/models/slide_channel.py index fd6ea6d1f37..37d158e0898 100644 --- a/addons/website_sale_slides/models/slide_channel.py +++ b/addons/website_sale_slides/models/slide_channel.py @@ -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}) diff --git a/addons/website_slides/models/res_users.py b/addons/website_slides/models/res_users.py index 47f6baa980e..3b72ba7ef0e 100644 --- a/addons/website_slides/models/res_users.py +++ b/addons/website_slides/models/res_users.py @@ -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 """ diff --git a/addons/website_slides/models/slide_slide.py b/addons/website_slides/models/slide_slide.py index 00bd90ea7bc..4a714e07052 100644 --- a/addons/website_slides/models/slide_slide.py +++ b/addons/website_slides/models/slide_slide.py @@ -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() diff --git a/addons/website_slides_forum/models/slide_channel.py b/addons/website_slides_forum/models/slide_channel.py index c45dc14c0d8..e59272e7a52 100644 --- a/addons/website_slides_forum/models/slide_channel.py +++ b/addons/website_slides_forum/models/slide_channel.py @@ -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