diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 343a08ac9e0..404a8b8d1c7 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1025,59 +1025,53 @@ class AccountMoveLine(models.Model): # CRUD methods #################################################### - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): """ :context's key `check_move_validity`: check data consistency after move line creation. Eg. set to false to disable verification that the move debit-credit == 0 while creating the move lines composing the move. """ - amount = vals.get('debit', 0.0) - vals.get('credit', 0.0) - move = self.env['account.move'].browse(vals['move_id']) - account = self.env['account.account'].browse(vals['account_id']) - if account.deprecated: - raise UserError(_('The account %s (%s) is deprecated.') %(account.name, account.code)) - journal = vals.get('journal_id') and self.env['account.journal'].browse(vals['journal_id']) or move.journal_id - vals['date_maturity'] = vals.get('date_maturity') or vals.get('date') or move.date - ok = not (journal.type_control_ids or journal.account_control_ids) + for vals in vals_list: + amount = vals.get('debit', 0.0) - vals.get('credit', 0.0) + move = self.env['account.move'].browse(vals['move_id']) + account = self.env['account.account'].browse(vals['account_id']) + if account.deprecated: + raise UserError(_('The account %s (%s) is deprecated.') %(account.name, account.code)) + journal = vals.get('journal_id') and self.env['account.journal'].browse(vals['journal_id']) or move.journal_id + vals['date_maturity'] = vals.get('date_maturity') or vals.get('date') or move.date - if journal.type_control_ids: - type = account.user_type_id - for t in journal.type_control_ids: - if type == t: - ok = True - break - if journal.account_control_ids and not ok: - for a in journal.account_control_ids: - if a.id == vals['account_id']: - ok = True - break - # Automatically convert in the account's secondary currency if there is one and - # the provided values were not already multi-currency - if account.currency_id and 'amount_currency' not in vals and account.currency_id.id != account.company_id.currency_id.id: - vals['currency_id'] = account.currency_id.id - ctx = {} - if 'date' in vals: - ctx['date'] = vals['date'] - vals['amount_currency'] = account.company_id.currency_id._convert(amount, account.currency_id, account.company_id, vals.get('date', fields.Date.today())) + ok = ( + (not journal.type_control_ids and not journal.account_control_ids) + or account.user_type_id in journal.type_control_ids + or account in journal.account_control_ids + ) + if not ok: + raise UserError(_('You cannot use this general account in this journal, check the tab \'Entry Controls\' on the related journal.')) - if not ok: - raise UserError(_('You cannot use this general account in this journal, check the tab \'Entry Controls\' on the related journal.')) + # Automatically convert in the account's secondary currency if there is one and + # the provided values were not already multi-currency + if account.currency_id and 'amount_currency' not in vals and account.currency_id.id != account.company_id.currency_id.id: + vals['currency_id'] = account.currency_id.id + ctx = {} + if 'date' in vals: + ctx['date'] = vals['date'] + vals['amount_currency'] = account.company_id.currency_id._convert(amount, account.currency_id, account.company_id, vals.get('date', fields.Date.today())) - #Toggle the 'tax_exigible' field to False in case it is not yet given and the tax in 'tax_line_id' or one of - #the 'tax_ids' is a cash based tax. - taxes = False - if vals.get('tax_line_id'): - taxes = [{'tax_exigibility': self.env['account.tax'].browse(vals['tax_line_id']).tax_exigibility}] - if vals.get('tax_ids'): - taxes = self.env['account.move.line'].resolve_2many_commands('tax_ids', vals['tax_ids']) - if taxes and any([tax['tax_exigibility'] == 'on_payment' for tax in taxes]) and not vals.get('tax_exigible'): - vals['tax_exigible'] = False + #Toggle the 'tax_exigible' field to False in case it is not yet given and the tax in 'tax_line_id' or one of + #the 'tax_ids' is a cash based tax. + taxes = False + if vals.get('tax_line_id'): + taxes = [{'tax_exigibility': self.env['account.tax'].browse(vals['tax_line_id']).tax_exigibility}] + if vals.get('tax_ids'): + taxes = self.env['account.move.line'].resolve_2many_commands('tax_ids', vals['tax_ids']) + if taxes and any([tax['tax_exigibility'] == 'on_payment' for tax in taxes]) and not vals.get('tax_exigible'): + vals['tax_exigible'] = False - new_line = super(AccountMoveLine, self).create(vals) + lines = super(AccountMoveLine, self).create(vals_list) if self._context.get('check_move_validity', True): - move._post_validate() + lines.mapped('move_id')._post_validate() - return new_line + return lines @api.multi def unlink(self): diff --git a/addons/anonymization/models/anonymization.py b/addons/anonymization/models/anonymization.py index 3d82981a226..666a45393ca 100644 --- a/addons/anonymization/models/anonymization.py +++ b/addons/anonymization/models/anonymization.py @@ -64,15 +64,16 @@ class IrModelFieldsAnonymization(models.Model): return (field.model_id.id, field.id) return (False, False) - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): # check field state: all should be clear before we can add a new field to anonymize: self._check_write() - if vals.get('field_name') and vals.get('model_name'): - vals['model_id'], vals['field_id'] = self._get_model_and_field_ids(vals) - # check not existing fields: - vals['state'] = self._get_global_state() if vals.get('field_id') else 'not_existing' - return super(IrModelFieldsAnonymization, self).create(vals) + for vals in vals_list: + if vals.get('field_name') and vals.get('model_name'): + vals['model_id'], vals['field_id'] = self._get_model_and_field_ids(vals) + # check not existing fields: + vals['state'] = self._get_global_state() if vals.get('field_id') else 'not_existing' + return super(IrModelFieldsAnonymization, self).create(vals_list) @api.multi def write(self, vals): diff --git a/addons/base_automation/models/base_automation.py b/addons/base_automation/models/base_automation.py index 1724286b7a8..ac3e6305c6b 100644 --- a/addons/base_automation/models/base_automation.py +++ b/addons/base_automation/models/base_automation.py @@ -212,16 +212,16 @@ class BaseAutomation(models.Model): def make_create(): """ Instanciate a create method that processes action rules. """ - @api.model - def create(self, vals, **kw): + @api.model_create_multi + def create(self, vals_list, **kw): # retrieve the action rules to possibly execute actions = self.env['base.automation']._get_actions(self, ['on_create', 'on_create_or_write']) # call original method - record = create.origin(self.with_env(actions.env), vals, **kw) + records = create.origin(self.with_env(actions.env), vals_list, **kw) # check postconditions, and execute actions on the records that satisfy them for action in actions.with_context(old_values=None): - action._process(action._filter_post(record)) - return record.with_env(self.env) + action._process(action._filter_post(records)) + return records.with_env(self.env) return create diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index 26db5ff6cbe..49ab6f28fb6 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -142,14 +142,15 @@ class Attendee(models.Model): """ Make entry on email and availability on change of partner_id field. """ self.email = self.partner_id.email - @api.model - def create(self, values): - if not values.get("email") and values.get("common_name"): - common_nameval = values.get("common_name").split(':') - email = [x for x in common_nameval if '@' in x] # TODO JEM : should be refactored - values['email'] = email and email[0] or '' - values['common_name'] = values.get("common_name") - return super(Attendee, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for values in vals_list: + if not values.get("email") and values.get("common_name"): + common_nameval = values.get("common_name").split(':') + email = [x for x in common_nameval if '@' in x] # TODO JEM : should be refactored + values['email'] = email and email[0] or '' + values['common_name'] = values.get("common_name") + return super(Attendee, self).create(vals_list) @api.multi @api.returns('self', lambda value: value.id) diff --git a/addons/decimal_precision/models/decimal_precision.py b/addons/decimal_precision/models/decimal_precision.py index 8ac6589bc8a..eaf0f362bd9 100644 --- a/addons/decimal_precision/models/decimal_precision.py +++ b/addons/decimal_precision/models/decimal_precision.py @@ -25,9 +25,9 @@ class DecimalPrecision(models.Model): """ Deprecated, use `clear_caches` instead. """ self.clear_caches() - @api.model - def create(self, data): - res = super(DecimalPrecision, self).create(data) + @api.model_create_multi + def create(self, vals_list): + res = super(DecimalPrecision, self).create(vals_list) self.clear_caches() return res diff --git a/addons/fleet/models/fleet_vehicle_cost.py b/addons/fleet/models/fleet_vehicle_cost.py index 305ca2dfb83..95d7b5fc9ca 100644 --- a/addons/fleet/models/fleet_vehicle_cost.py +++ b/addons/fleet/models/fleet_vehicle_cost.py @@ -49,25 +49,26 @@ class FleetVehicleCost(models.Model): }) self.odometer_id = odometer - @api.model - def create(self, data): - # make sure that the data are consistent with values of parent and contract records given - if 'parent_id' in data and data['parent_id']: - parent = self.browse(data['parent_id']) - data['vehicle_id'] = parent.vehicle_id.id - data['date'] = parent.date - data['cost_type'] = parent.cost_type - if 'contract_id' in data and data['contract_id']: - contract = self.env['fleet.vehicle.log.contract'].browse(data['contract_id']) - data['vehicle_id'] = contract.vehicle_id.id - data['cost_subtype_id'] = contract.cost_subtype_id.id - data['cost_type'] = contract.cost_type - if 'odometer' in data and not data['odometer']: - # if received value for odometer is 0, then remove it from the - # data as it would result to the creation of a - # odometer log with 0, which is to be avoided - del data['odometer'] - return super(FleetVehicleCost, self).create(data) + @api.model_create_multi + def create(self, vals_list): + for data in vals_list: + # make sure that the data are consistent with values of parent and contract records given + if 'parent_id' in data and data['parent_id']: + parent = self.browse(data['parent_id']) + data['vehicle_id'] = parent.vehicle_id.id + data['date'] = parent.date + data['cost_type'] = parent.cost_type + if 'contract_id' in data and data['contract_id']: + contract = self.env['fleet.vehicle.log.contract'].browse(data['contract_id']) + data['vehicle_id'] = contract.vehicle_id.id + data['cost_subtype_id'] = contract.cost_subtype_id.id + data['cost_type'] = contract.cost_type + if 'odometer' in data and not data['odometer']: + # if received value for odometer is 0, then remove it from the + # data as it would result to the creation of a + # odometer log with 0, which is to be avoided + del data['odometer'] + return super(FleetVehicleCost, self).create(vals_list) class FleetVehicleLogContract(models.Model): diff --git a/addons/fleet/models/fleet_vehicle_model.py b/addons/fleet/models/fleet_vehicle_model.py index 4de611b2823..55a76f2e769 100644 --- a/addons/fleet/models/fleet_vehicle_model.py +++ b/addons/fleet/models/fleet_vehicle_model.py @@ -52,10 +52,11 @@ class FleetVehicleModelBrand(models.Model): "resized as a 64x64px image, with aspect ratio preserved. " "Use this field anywhere a small image is required.") - @api.model - def create(self, vals): - tools.image_resize_images(vals) - return super(FleetVehicleModelBrand, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + tools.image_resize_images(vals) + return super(FleetVehicleModelBrand, self).create(vals_list) @api.multi def write(self, vals): diff --git a/addons/hr_payroll/models/hr_payslip.py b/addons/hr_payroll/models/hr_payslip.py index 19692265971..18eb8408c4a 100644 --- a/addons/hr_payroll/models/hr_payslip.py +++ b/addons/hr_payroll/models/hr_payslip.py @@ -514,15 +514,16 @@ class HrPayslipLine(models.Model): for line in self: line.total = float(line.quantity) * line.amount * line.rate / 100 - @api.model - def create(self, values): - if 'employee_id' not in values or 'contract_id' not in values: - payslip = self.env['hr.payslip'].browse(values.get('slip_id')) - values['employee_id'] = values.get('employee_id') or payslip.employee_id.id - values['contract_id'] = values.get('contract_id') or payslip.contract_id and payslip.contract_id.id - if not values['contract_id']: - raise UserError(_('You must set a contract to create a payslip line.')) - return super(HrPayslipLine, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for values in vals_list: + if 'employee_id' not in values or 'contract_id' not in values: + payslip = self.env['hr.payslip'].browse(values.get('slip_id')) + values['employee_id'] = values.get('employee_id') or payslip.employee_id.id + values['contract_id'] = values.get('contract_id') or payslip.contract_id and payslip.contract_id.id + if not values['contract_id']: + raise UserError(_('You must set a contract to create a payslip line.')) + return super(HrPayslipLine, self).create(vals_list) class HrPayslipWorkedDays(models.Model): diff --git a/addons/mail/models/mail_followers.py b/addons/mail/models/mail_followers.py index 469ab1d747c..55b155d85f5 100644 --- a/addons/mail/models/mail_followers.py +++ b/addons/mail/models/mail_followers.py @@ -46,9 +46,9 @@ class Followers(models.Model): if record.res_id: self.env[record.res_model].invalidate_cache(ids=[record.res_id]) - @api.model - def create(self, vals): - res = super(Followers, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + res = super(Followers, self).create(vals_list) res._invalidate_documents() return res @@ -151,9 +151,11 @@ GROUP BY fol.id%s""" % ( new, upd = self._add_default_followers(res_model, res_ids, partner_ids, channel_ids, customer_ids=customer_ids) else: new, upd = self._add_followers(res_model, res_ids, partner_ids, partner_subtypes, channel_ids, channel_subtypes, check_existing=check_existing, existing_policy=existing_policy) - for res_id, values_list in new.items(): - for values in values_list: - sudo_self.create(dict(values, res_id=res_id)).id + sudo_self.create([ + dict(values, res_id=res_id) + for res_id, values_list in new.items() + for values in values_list + ]) for fol_id, values in upd.items(): sudo_self.browse(fol_id).write(values) diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index df27d62a71b..13c4c65e9a5 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -238,47 +238,52 @@ class MailThread(models.AbstractModel): # CRUD overrides for automatic subscription and logging # ------------------------------------------------------ - @api.model - def create(self, values): + @api.model_create_multi + def create(self, vals_list): """ Chatter override : - subscribe uid - subscribe followers of parent - log a creation message """ if self._context.get('tracking_disable'): - return super(MailThread, self).create(values) + return super(MailThread, self).create(vals_list) # subscribe uid unless asked not to if not self._context.get('mail_create_nosubscribe'): - message_follower_ids = values.get('message_follower_ids') or [] - message_follower_ids += [(0, 0, fol_vals) for fol_vals in self.env['mail.followers']._add_default_followers(self._name, [], self.env.user.partner_id.ids, customer_ids=[])[0][0]] - values['message_follower_ids'] = message_follower_ids - thread = super(MailThread, self).create(values) + for values in vals_list: + message_follower_ids = values.get('message_follower_ids') or [] + message_follower_ids += [(0, 0, fol_vals) for fol_vals in self.env['mail.followers']._add_default_followers(self._name, [], self.env.user.partner_id.ids, customer_ids=[])[0][0]] + values['message_follower_ids'] = message_follower_ids + + threads = super(MailThread, self).create(vals_list) # automatic logging unless asked not to (mainly for various testing purpose) if not self._context.get('mail_create_nolog'): doc_name = self.env['ir.model']._get(self._name).name - thread._message_log(body=_('%s created') % doc_name) + for thread in threads: + thread._message_log(body=_('%s created') % doc_name) # auto_subscribe: take values and defaults into account - create_values = dict(values) - for key, val in self._context.items(): - if key.startswith('default_') and key[8:] not in create_values: - create_values[key[8:]] = val - thread._message_auto_subscribe(create_values) + for thread, values in pycompat.izip(threads, vals_list): + create_values = dict(values) + for key, val in self._context.items(): + if key.startswith('default_') and key[8:] not in create_values: + create_values[key[8:]] = val + thread._message_auto_subscribe(create_values) # track values if not self._context.get('mail_notrack'): if 'lang' not in self._context: - track_thread = thread.with_context(lang=self.env.user.lang) + track_threads = threads.with_context(lang=self.env.user.lang) else: - track_thread = thread - tracked_fields = track_thread._get_tracked_fields(list(values)) - if tracked_fields: - initial_values = {thread.id: dict.fromkeys(tracked_fields, False)} - track_thread.message_track(tracked_fields, initial_values) + track_threads = threads + for thread, values in pycompat.izip(track_threads, vals_list): + tracked_fields = thread._get_tracked_fields(list(values)) + if tracked_fields: + initial_values = {thread.id: dict.fromkeys(tracked_fields, False)} + thread.message_track(tracked_fields, initial_values) - return thread + return threads @api.multi def write(self, values): diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index 17d6a6f1614..5711847d3bf 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -162,11 +162,12 @@ class MassMailingContact(models.Model): country_id = fields.Many2one('res.country', string='Country') tag_ids = fields.Many2many('res.partner.category', string='Tags') - @api.model - def create(self, vals): - if 'opt_out' in vals: - vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now() - return super(MassMailingContact, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if 'opt_out' in vals: + vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now() + return super(MassMailingContact, self).create(vals_list) @api.multi def write(self, vals): diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index e5788d9b9c7..6e6f28b8686 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -274,11 +274,12 @@ class MrpBomLine(models.Model): if self.product_id: self.product_uom_id = self.product_id.uom_id.id - @api.model - def create(self, values): - if 'product_id' in values and 'product_uom_id' not in values: - values['product_uom_id'] = self.env['product.product'].browse(values['product_id']).uom_id.id - return super(MrpBomLine, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for values in vals_list: + if 'product_id' in values and 'product_uom_id' not in values: + values['product_uom_id'] = self.env['product.product'].browse(values['product_id']).uom_id.id + return super(MrpBomLine, self).create(vals_list) def _skip_bom_line(self, product): """ Control if a BoM line should be produce, can be inherited for add diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 3d11e31db0a..826aeef6620 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -507,13 +507,14 @@ class PaymentIcon(models.Model): image_payment_form = fields.Binary( "Image displayed on the payment form", attachment=True) - @api.model - def create(self, vals): - if 'image' in vals: - image = ustr(vals['image'] or '').encode('utf-8') - vals['image_payment_form'] = image_resize_image(image, size=(45,30)) - vals['image'] = image_resize_image(image, size=(64,64)) - return super(PaymentIcon, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if 'image' in vals: + image = ustr(vals['image'] or '').encode('utf-8') + vals['image_payment_form'] = image_resize_image(image, size=(45,30)) + vals['image'] = image_resize_image(image, size=(64,64)) + return super(PaymentIcon, self).create(vals_list) @api.multi def write(self, vals): diff --git a/addons/product/models/product.py b/addons/product/models/product.py index 17d6f29c9b1..95b23f2d68a 100644 --- a/addons/product/models/product.py +++ b/addons/product/models/product.py @@ -298,13 +298,14 @@ class ProductProduct(models.Model): if self.uom_id and self.uom_po_id and self.uom_id.category_id != self.uom_po_id.category_id: self.uom_po_id = self.uom_id - @api.model - def create(self, vals): - product = super(ProductProduct, self.with_context(create_product_product=True)).create(vals) - # When a unique variant is created from tmpl then the standard price is set by _set_standard_price - if not (self.env.context.get('create_from_tmpl') and len(product.product_tmpl_id.product_variant_ids) == 1): - product._set_standard_price(vals.get('standard_price') or 0.0) - return product + @api.model_create_multi + def create(self, vals_list): + products = super(ProductProduct, self.with_context(create_product_product=True)).create(vals_list) + for product, vals in pycompat.izip(products, vals_list): + # When a unique variant is created from tmpl then the standard price is set by _set_standard_price + if not (self.env.context.get('create_from_tmpl') and len(product.product_tmpl_id.product_variant_ids) == 1): + product._set_standard_price(vals.get('standard_price') or 0.0) + return products @api.multi def write(self, values): diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index b2eb5c7675f..deb5addf2e7 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -309,30 +309,33 @@ class ProductTemplate(models.Model): if self.uom_id: self.uom_po_id = self.uom_id.id - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): ''' Store the initial standard price in order to be able to retrieve the cost of a product template for a given date''' # TDE FIXME: context brol - tools.image_resize_images(vals) - template = super(ProductTemplate, self).create(vals) + for vals in vals_list: + tools.image_resize_images(vals) + templates = super(ProductTemplate, self).create(vals_list) if "create_product_product" not in self._context: - template.with_context(create_from_tmpl=True).create_variant_ids() + templates.with_context(create_from_tmpl=True).create_variant_ids() # This is needed to set given values to first variant after creation - related_vals = {} - if vals.get('barcode'): - related_vals['barcode'] = vals['barcode'] - if vals.get('default_code'): - related_vals['default_code'] = vals['default_code'] - if vals.get('standard_price'): - related_vals['standard_price'] = vals['standard_price'] - if vals.get('volume'): - related_vals['volume'] = vals['volume'] - if vals.get('weight'): - related_vals['weight'] = vals['weight'] - if related_vals: - template.write(related_vals) - return template + for template, vals in pycompat.izip(templates, vals_list): + related_vals = {} + if vals.get('barcode'): + related_vals['barcode'] = vals['barcode'] + if vals.get('default_code'): + related_vals['default_code'] = vals['default_code'] + if vals.get('standard_price'): + related_vals['standard_price'] = vals['standard_price'] + if vals.get('volume'): + related_vals['volume'] = vals['volume'] + if vals.get('weight'): + related_vals['weight'] = vals['weight'] + if related_vals: + template.write(related_vals) + + return templates @api.multi def write(self, vals): @@ -425,6 +428,11 @@ class ProductTemplate(models.Model): def create_variant_ids(self): Product = self.env["product.product"] AttributeValues = self.env['product.attribute.value'] + + variants_to_create = [] + variants_to_activate = [] + variants_to_unlink = [] + for tmpl_id in self.with_context(active_test=False): # adding an attribute with only one value should not recreate product # write this attribute on every product to make sure we don't lose them @@ -443,39 +451,37 @@ class ProductTemplate(models.Model): existing_variants = {frozenset(variant.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant).ids) for variant in tmpl_id.product_variant_ids} # -> for each value set, create a recordset of values to create a # variant for if the value set isn't already a variant - to_create_variants = [ - value_ids - for value_ids in variant_matrix - if set(value_ids.ids) not in existing_variants - ] + for value_ids in variant_matrix: + if set(value_ids.ids) not in existing_variants: + variants_to_create.append({ + 'product_tmpl_id': tmpl_id.id, + 'attribute_value_ids': [(6, 0, value_ids.ids)] + }) # check product - variants_to_activate = self.env['product.product'] - variants_to_unlink = self.env['product.product'] for product_id in tmpl_id.product_variant_ids: if not product_id.active and product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant) in variant_matrix: - variants_to_activate |= product_id + variants_to_activate.append(product_id) elif product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant) not in variant_matrix: - variants_to_unlink |= product_id - if variants_to_activate: - variants_to_activate.write({'active': True}) + variants_to_unlink.append(product_id) - # create new product - for variant_ids in to_create_variants: - new_variant = Product.create({ - 'product_tmpl_id': tmpl_id.id, - 'attribute_value_ids': [(6, 0, variant_ids.ids)] - }) + if variants_to_activate: + Product.concat(*variants_to_activate).write({'active': True}) + + # create new products + if variants_to_create: + Product.create(variants_to_create) + + # unlink or inactive product + for variant in variants_to_unlink: + try: + with self._cr.savepoint(), tools.mute_logger('odoo.sql_db'): + variant.unlink() + # We catch all kind of exception to be sure that the operation doesn't fail. + except (psycopg2.Error, except_orm): + variant.write({'active': False}) + pass - # unlink or inactive product - for variant in variants_to_unlink: - try: - with self._cr.savepoint(), tools.mute_logger('odoo.sql_db'): - variant.unlink() - # We catch all kind of exception to be sure that the operation doesn't fail. - except (psycopg2.Error, except_orm): - variant.write({'active': False}) - pass return True @api.model diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py index 32a8a6345b2..e545f2b1d6f 100644 --- a/addons/sale_stock/models/sale_order.py +++ b/addons/sale_stock/models/sale_order.py @@ -172,12 +172,11 @@ class SaleOrderLine(models.Model): qty -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom) line.qty_delivered = qty - @api.model - def create(self, values): - line = super(SaleOrderLine, self).create(values) - if line.state == 'sale': - line._action_launch_stock_rule() - return line + @api.model_create_multi + def create(self, vals_list): + lines = super(SaleOrderLine, self).create(vals_list) + lines.filtered(lambda line: line.state == 'sale')._action_launch_stock_rule() + return lines @api.multi def write(self, values): diff --git a/addons/sale_timesheet/models/sale_order.py b/addons/sale_timesheet/models/sale_order.py index 2c80fa04888..1820031f76d 100644 --- a/addons/sale_timesheet/models/sale_order.py +++ b/addons/sale_timesheet/models/sale_order.py @@ -164,18 +164,19 @@ class SaleOrderLine(models.Model): else: super(SaleOrderLine, line)._compute_product_updatable() - @api.model - def create(self, values): - line = super(SaleOrderLine, self).create(values) + @api.model_create_multi + def create(self, vals_list): + lines = super(SaleOrderLine, self).create(vals_list) # Do not generate task/project when expense SO line, but allow # generate task with hours=0. - if line.state == 'sale' and not line.is_expense: - line.sudo()._timesheet_service_generation() - # if the SO line created a task, post a message on the order - if line.task_id: - msg_body = _("Task Created (%s): %s") % (line.product_id.name, line.task_id.id, line.task_id.name) - line.order_id.message_post(body=msg_body) - return line + for line in lines: + if line.state == 'sale' and not line.is_expense: + line.sudo()._timesheet_service_generation() + # if the SO line created a task, post a message on the order + if line.task_id: + msg_body = _("Task Created (%s): %s") % (line.product_id.name, line.task_id.id, line.task_id.name) + line.order_id.message_post(body=msg_body) + return lines @api.multi def write(self, values): diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py index d7cabb6d3d1..9c9625e8730 100644 --- a/addons/stock/models/stock_inventory.py +++ b/addons/stock/models/stock_inventory.py @@ -380,11 +380,12 @@ class InventoryLine(models.Model): self._compute_theoretical_qty() self.product_qty = self.theoretical_qty - @api.model - def create(self, values): - if 'product_id' in values and 'product_uom_id' not in values: - values['product_uom_id'] = self.env['product.product'].browse(values['product_id']).uom_id.id - res = super(InventoryLine, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for values in vals_list: + if 'product_id' in values and 'product_uom_id' not in values: + values['product_uom_id'] = self.env['product.product'].browse(values['product_id']).uom_id.id + res = super(InventoryLine, self).create(vals_list) res._check_no_duplicate_line() return res @@ -456,7 +457,7 @@ class InventoryLine(models.Model): } def _generate_moves(self): - moves = self.env['stock.move'] + vals_list = [] for line in self: if float_utils.float_compare(line.theoretical_qty, line.product_qty, precision_rounding=line.product_id.uom_id.rounding) == 0: continue @@ -465,5 +466,5 @@ class InventoryLine(models.Model): vals = line._get_move_values(abs(diff), line.product_id.property_stock_inventory.id, line.location_id.id, False) else: vals = line._get_move_values(abs(diff), line.location_id.id, line.product_id.property_stock_inventory.id, True) - moves |= self.env['stock.move'].create(vals) - return moves + vals_list.append(vals) + return self.env['stock.move'].create(vals_list) diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 95077f6fde9..35b18f0f0af 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -376,16 +376,18 @@ class StockMove(models.Model): move.location_id.name, move.location_dest_id.name))) return res - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): # TDE CLEANME: why doing this tracking on picking here ? seems weird - perform_tracking = not self.env.context.get('mail_notrack') and vals.get('picking_id') - if perform_tracking: - picking = self.env['stock.picking'].browse(vals['picking_id']) - initial_values = {picking.id: {'state': picking.state}} - vals['ordered_qty'] = vals.get('product_uom_qty') - res = super(StockMove, self).create(vals) - if perform_tracking: + tracking = [] + for vals in vals_list: + vals['ordered_qty'] = vals.get('product_uom_qty') + if not self.env.context.get('mail_notrack') and vals.get('picking_id'): + picking = self.env['stock.picking'].browse(vals['picking_id']) + initial_values = {picking.id: {'state': picking.state}} + tracking.append((picking, initial_values)) + res = super(StockMove, self).create(vals_list) + for picking, initial_values in tracking: picking.message_track(picking.fields_get(['state']), initial_values) return res diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 989749a5cbd..dfa7b63fd3c 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -153,49 +153,53 @@ class StockMoveLine(models.Model): lines |= picking_id.move_line_ids.filtered(lambda ml: ml.product_id == self.product_id and (ml.lot_id or ml.lot_name)) return lines - @api.model - def create(self, vals): - vals['ordered_qty'] = vals.get('product_uom_qty') + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + vals['ordered_qty'] = vals.get('product_uom_qty') - # If the move line is directly create on the picking view. - # If this picking is already done we should generate an - # associated done move. - if 'picking_id' in vals and not vals.get('move_id'): - picking = self.env['stock.picking'].browse(vals['picking_id']) - if picking.state == 'done': - product = self.env['product.product'].browse(vals['product_id']) - new_move = self.env['stock.move'].create({ - 'name': _('New Move:') + product.display_name, - 'product_id': product.id, - 'product_uom_qty': 'qty_done' in vals and vals['qty_done'] or 0, - 'product_uom': vals['product_uom_id'], - 'location_id': 'location_id' in vals and vals['location_id'] or picking.location_id.id, - 'location_dest_id': 'location_dest_id' in vals and vals['location_dest_id'] or picking.location_dest_id.id, - 'state': 'done', - 'additional': True, - 'picking_id': picking.id, - }) - vals['move_id'] = new_move.id + # If the move line is directly create on the picking view. + # If this picking is already done we should generate an + # associated done move. + if 'picking_id' in vals and not vals.get('move_id'): + picking = self.env['stock.picking'].browse(vals['picking_id']) + if picking.state == 'done': + product = self.env['product.product'].browse(vals['product_id']) + new_move = self.env['stock.move'].create({ + 'name': _('New Move:') + product.display_name, + 'product_id': product.id, + 'product_uom_qty': 'qty_done' in vals and vals['qty_done'] or 0, + 'product_uom': vals['product_uom_id'], + 'location_id': 'location_id' in vals and vals['location_id'] or picking.location_id.id, + 'location_dest_id': 'location_dest_id' in vals and vals['location_dest_id'] or picking.location_dest_id.id, + 'state': 'done', + 'additional': True, + 'picking_id': picking.id, + }) + vals['move_id'] = new_move.id - ml = super(StockMoveLine, self).create(vals) - if ml.state == 'done': - if ml.product_id.type == 'product': - Quant = self.env['stock.quant'] - quantity = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id,rounding_method='HALF-UP') - in_date = None - available_qty, in_date = Quant._update_available_quantity(ml.product_id, ml.location_id, -quantity, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) - if available_qty < 0 and ml.lot_id: - # see if we can compensate the negative quants with some untracked quants - untracked_qty = Quant._get_available_quantity(ml.product_id, ml.location_id, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) - if untracked_qty: - taken_from_untracked_qty = min(untracked_qty, abs(quantity)) - Quant._update_available_quantity(ml.product_id, ml.location_id, -taken_from_untracked_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id) - Quant._update_available_quantity(ml.product_id, ml.location_id, taken_from_untracked_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) - Quant._update_available_quantity(ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id, package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date) - next_moves = ml.move_id.move_dest_ids.filtered(lambda move: move.state not in ('done', 'cancel')) - next_moves._do_unreserve() - next_moves._action_assign() - return ml + mls = super(StockMoveLine, self).create(vals_list) + + for ml in mls: + if ml.state == 'done': + if ml.product_id.type == 'product': + Quant = self.env['stock.quant'] + quantity = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id,rounding_method='HALF-UP') + in_date = None + available_qty, in_date = Quant._update_available_quantity(ml.product_id, ml.location_id, -quantity, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) + if available_qty < 0 and ml.lot_id: + # see if we can compensate the negative quants with some untracked quants + untracked_qty = Quant._get_available_quantity(ml.product_id, ml.location_id, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) + if untracked_qty: + taken_from_untracked_qty = min(untracked_qty, abs(quantity)) + Quant._update_available_quantity(ml.product_id, ml.location_id, -taken_from_untracked_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id) + Quant._update_available_quantity(ml.product_id, ml.location_id, taken_from_untracked_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) + Quant._update_available_quantity(ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id, package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date) + next_moves = ml.move_id.move_dest_ids.filtered(lambda move: move.state not in ('done', 'cancel')) + next_moves._do_unreserve() + next_moves._action_assign() + + return mls def write(self, vals): """ Through the interface, we allow users to change the charateristics of a move line. If a diff --git a/addons/stock/models/stock_production_lot.py b/addons/stock/models/stock_production_lot.py index dac5d276a9f..1d618bf5fb1 100644 --- a/addons/stock/models/stock_production_lot.py +++ b/addons/stock/models/stock_production_lot.py @@ -28,14 +28,14 @@ class ProductionLot(models.Model): ('name_ref_uniq', 'unique (name, product_id)', 'The combination of serial number and product must be unique !'), ] - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): active_picking_id = self.env.context.get('active_picking_id', False) if active_picking_id: picking_id = self.env['stock.picking'].browse(active_picking_id) if picking_id and not picking_id.picking_type_id.use_create_lots: raise UserError(_("You are not allowed to create a lot for this operation type.")) - return super(ProductionLot, self).create(vals) + return super(ProductionLot, self).create(vals_list) @api.multi def write(self, vals): diff --git a/addons/stock_account/models/stock.py b/addons/stock_account/models/stock.py index 6acdb0d927f..6f2cbc97638 100644 --- a/addons/stock_account/models/stock.py +++ b/addons/stock_account/models/stock.py @@ -62,15 +62,16 @@ class StockLocation(models.Model): class StockMoveLine(models.Model): _inherit = 'stock.move.line' - @api.model - def create(self, vals): - res = super(StockMoveLine, self).create(vals) - move = res.move_id - if move.state == 'done': - correction_value = move._run_valuation(res.qty_done) - if move.product_id.valuation == 'real_time' and (move._is_in() or move._is_out()): - move.with_context(force_valuation_amount=correction_value)._account_entry_move() - return res + @api.model_create_multi + def create(self, vals_list): + lines = super(StockMoveLine, self).create(vals_list) + for line in lines: + move = line.move_id + if move.state == 'done': + correction_value = move._run_valuation(line.qty_done) + if move.product_id.valuation == 'real_time' and (move._is_in() or move._is_out()): + move.with_context(force_valuation_amount=correction_value)._account_entry_move() + return lines @api.multi def write(self, vals): diff --git a/addons/survey/models/survey.py b/addons/survey/models/survey.py index 97e58f3a5df..210c8153eca 100644 --- a/addons/survey/models/survey.py +++ b/addons/survey/models/survey.py @@ -823,12 +823,13 @@ class SurveyUserInputLine(models.Model): mark = label.quizz_mark if label.exists() else 0.0 return mark - @api.model - def create(self, vals): - value_suggested = vals.get('value_suggested') - if value_suggested: - vals.update({'quizz_mark': self._get_mark(value_suggested)}) - return super(SurveyUserInputLine, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + value_suggested = vals.get('value_suggested') + if value_suggested: + vals.update({'quizz_mark': self._get_mark(value_suggested)}) + return super(SurveyUserInputLine, self).create(vals_list) @api.multi def write(self, vals): diff --git a/addons/uom/models/uom_uom.py b/addons/uom/models/uom_uom.py index 5782c7f4dac..3b709b552ed 100644 --- a/addons/uom/models/uom_uom.py +++ b/addons/uom/models/uom_uom.py @@ -87,12 +87,13 @@ class UoM(models.Model): if uom_data['uom_count'] > 1: raise ValidationError(_("UoM category %s should only have one reference unit of measure.") % (self.env['uom.category'].browse(uom_data['category_id']).name,)) - @api.model - def create(self, values): - if 'factor_inv' in values: - factor_inv = values.pop('factor_inv') - values['factor'] = factor_inv and (1.0 / factor_inv) or 0.0 - return super(UoM, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for values in vals_list: + if 'factor_inv' in values: + factor_inv = values.pop('factor_inv') + values['factor'] = factor_inv and (1.0 / factor_inv) or 0.0 + return super(UoM, self).create(vals_list) @api.multi def write(self, values): diff --git a/odoo/addons/base/models/ir_actions.py b/odoo/addons/base/models/ir_actions.py index 5403990ea24..9b856cbf9e8 100644 --- a/odoo/addons/base/models/ir_actions.py +++ b/odoo/addons/base/models/ir_actions.py @@ -43,9 +43,9 @@ class IrActions(models.Model): for record in self: record.xml_id = res.get(record.id) - @api.model - def create(self, vals): - res = super(IrActions, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + res = super(IrActions, self).create(vals_list) # self.get_bindings() depends on action records self.clear_caches() return res @@ -220,10 +220,10 @@ class IrActionsActWindow(models.Model): record = self.env.ref("%s.%s" % (module, xml_id)) return record.read()[0] - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): self.clear_caches() - return super(IrActionsActWindow, self).create(vals) + return super(IrActionsActWindow, self).create(vals_list) @api.multi def unlink(self): @@ -614,12 +614,13 @@ class IrActionsTodo(models.Model): state = fields.Selection([('open', 'To Do'), ('done', 'Done')], string='Status', default='open', required=True) name = fields.Char() - @api.model - def create(self, vals): - todo = super(IrActionsTodo, self).create(vals) - if todo.state == "open": - self.ensure_one_open_todo() - return todo + @api.model_create_multi + def create(self, vals_list): + todos = super(IrActionsTodo, self).create(vals_list) + for todo in todos: + if todo.state == "open": + self.ensure_one_open_todo() + return todos @api.multi def write(self, vals): diff --git a/odoo/addons/base/models/ir_attachment.py b/odoo/addons/base/models/ir_attachment.py index bae99e919a6..05a881ff0fb 100644 --- a/odoo/addons/base/models/ir_attachment.py +++ b/odoo/addons/base/models/ir_attachment.py @@ -430,14 +430,15 @@ class IrAttachment(models.Model): return res - @api.model - def create(self, values): - # remove computed field depending of datas - for field in ('file_size', 'checksum'): - values.pop(field, False) - values = self._check_contents(values) - self.browse().check('write', values=values) - return super(IrAttachment, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for values in vals_list: + # remove computed field depending of datas + for field in ('file_size', 'checksum'): + values.pop(field, False) + values = self._check_contents(values) + self.browse().check('write', values=values) + return super(IrAttachment, self).create(vals_list) @api.one def generate_access_token(self): diff --git a/odoo/addons/base/models/ir_config_parameter.py b/odoo/addons/base/models/ir_config_parameter.py index c53946a9d22..d3d6b6f0554 100644 --- a/odoo/addons/base/models/ir_config_parameter.py +++ b/odoo/addons/base/models/ir_config_parameter.py @@ -89,10 +89,10 @@ class IrConfigParameter(models.Model): self.create({'key': key, 'value': value}) return False - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): self.clear_caches() - return super(IrConfigParameter, self).create(vals) + return super(IrConfigParameter, self).create(vals_list) @api.multi def write(self, vals): diff --git a/odoo/addons/base/models/ir_default.py b/odoo/addons/base/models/ir_default.py index e5f3f387c6b..93751c1a9a7 100644 --- a/odoo/addons/base/models/ir_default.py +++ b/odoo/addons/base/models/ir_default.py @@ -21,10 +21,10 @@ class IrDefault(models.Model): condition = fields.Char('Condition', help="If set, applies the default upon condition.") json_value = fields.Char('Default Value (JSON format)', required=True) - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): self.clear_caches() - return super(IrDefault, self).create(vals) + return super(IrDefault, self).create(vals_list) @api.multi def write(self, vals): diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py index 66f693a79c7..4cc2a2d7913 100644 --- a/odoo/addons/base/models/ir_model.py +++ b/odoo/addons/base/models/ir_model.py @@ -1238,10 +1238,10 @@ class IrModelAccess(models.Model): # # Check rights on actions # - @api.model - def create(self, values): + @api.model_create_multi + def create(self, vals_list): self.call_cache_clearing_methods() - return super(IrModelAccess, self).create(values) + return super(IrModelAccess, self).create(vals_list) @api.multi def write(self, values): diff --git a/odoo/addons/base/models/ir_property.py b/odoo/addons/base/models/ir_property.py index 63ca6d2d6d0..8ec49e16ea7 100644 --- a/odoo/addons/base/models/ir_property.py +++ b/odoo/addons/base/models/ir_property.py @@ -89,9 +89,10 @@ class Property(models.Model): def write(self, values): return super(Property, self).write(self._update_values(values)) - @api.model - def create(self, values): - return super(Property, self).create(self._update_values(values)) + @api.model_create_multi + def create(self, vals_list): + vals_list = [self._update_values(vals) for vals in vals_list] + return super(Property, self).create(vals_list) @api.multi def get_by_record(self): @@ -221,10 +222,11 @@ class Property(models.Model): prop.write({'value': value}) # create new properties for records that do not have one yet + vals_list = [] for ref, id in refs.items(): value = clean(values[id]) if value != default_value: - self.create({ + vals_list.append({ 'fields_id': field_id, 'company_id': company_id, 'res_id': ref, @@ -232,6 +234,7 @@ class Property(models.Model): 'value': value, 'type': self.env[model]._fields[name].type, }) + self.create(vals_list) @api.model def search_multi(self, name, model, operator, value): diff --git a/odoo/addons/base/models/ir_rule.py b/odoo/addons/base/models/ir_rule.py index 13068389cce..32e03715eaa 100644 --- a/odoo/addons/base/models/ir_rule.py +++ b/odoo/addons/base/models/ir_rule.py @@ -121,9 +121,9 @@ class IrRule(models.Model): self.clear_caches() return res - @api.model - def create(self, vals): - res = super(IrRule, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + res = super(IrRule, self).create(vals_list) self.clear_caches() return res diff --git a/odoo/addons/base/models/ir_translation.py b/odoo/addons/base/models/ir_translation.py index 0ad132d4c33..b9ca4d2cc5f 100644 --- a/odoo/addons/base/models/ir_translation.py +++ b/odoo/addons/base/models/ir_translation.py @@ -323,8 +323,7 @@ class IrTranslation(models.Model): existing_ids = [row[0] for row in self._cr.fetchall()] # create missing translations - for res_id in set(ids) - set(existing_ids): - self.create({ + self.create([{ 'lang': lang, 'type': tt, 'name': name, @@ -332,7 +331,9 @@ class IrTranslation(models.Model): 'value': value, 'src': src, 'state': 'translated', - }) + } + for res_id in set(ids) - set(existing_ids) + ]) return len(ids) @api.model @@ -557,12 +558,12 @@ class IrTranslation(models.Model): if value2 != value0: raise ValidationError(_("Translation is not valid:\n%s") % val) - @api.model - def create(self, vals): - record = super(IrTranslation, self.sudo()).create(vals).with_env(self.env) - record.check('create') - record._modified() - return record + @api.model_create_multi + def create(self, vals_list): + records = super(IrTranslation, self.sudo()).create(vals_list).with_env(self.env) + records.check('create') + records._modified() + return records @api.multi def write(self, vals): diff --git a/odoo/addons/base/models/ir_ui_menu.py b/odoo/addons/base/models/ir_ui_menu.py index c9e01938690..a721c74d335 100644 --- a/odoo/addons/base/models/ir_ui_menu.py +++ b/odoo/addons/base/models/ir_ui_menu.py @@ -142,12 +142,13 @@ class IrUiMenu(models.Model): def name_get(self): return [(menu.id, menu._get_full_name()) for menu in self] - @api.model - def create(self, values): + @api.model_create_multi + def create(self, vals_list): self.clear_caches() - if 'web_icon' in values: - values['web_icon_data'] = self._compute_web_icon_data(values.get('web_icon')) - return super(IrUiMenu, self).create(values) + for values in vals_list: + if 'web_icon' in values: + values['web_icon_data'] = self._compute_web_icon_data(values.get('web_icon')) + return super(IrUiMenu, self).create(vals_list) @api.multi def write(self, values): diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py index 04f2afb7240..569c33ac92b 100644 --- a/odoo/addons/base/models/ir_ui_view.py +++ b/odoo/addons/base/models/ir_ui_view.py @@ -377,27 +377,28 @@ actual arch. values.setdefault('mode', 'extension' if values['inherit_id'] else 'primary') return values - @api.model - def create(self, values): - if not values.get('type'): - if values.get('inherit_id'): - values['type'] = self.browse(values['inherit_id']).type - else: + @api.model_create_multi + def create(self, vals_list): + for values in vals_list: + if not values.get('type'): + if values.get('inherit_id'): + values['type'] = self.browse(values['inherit_id']).type + else: - try: - if not values.get('arch') and not values.get('arch_base'): - raise ValidationError(_('Missing view architecture.')) - values['type'] = etree.fromstring(values.get('arch') or values.get('arch_base')).tag - except LxmlError: - # don't raise here, the constraint that runs `self._check_xml` will - # do the job properly. - pass - - if not values.get('name'): - values['name'] = "%s %s" % (values.get('model'), values['type']) + try: + if not values.get('arch') and not values.get('arch_base'): + raise ValidationError(_('Missing view architecture.')) + values['type'] = etree.fromstring(values.get('arch') or values.get('arch_base')).tag + except LxmlError: + # don't raise here, the constraint that runs `self._check_xml` will + # do the job properly. + pass + if not values.get('name'): + values['name'] = "%s %s" % (values.get('model'), values['type']) + values.update(self._compute_defaults(values)) self.clear_caches() - return super(View, self).create(self._compute_defaults(values)) + return super(View, self).create(vals_list) @api.multi def write(self, vals): diff --git a/odoo/addons/base/models/res_country.py b/odoo/addons/base/models/res_country.py index e9727ef5661..3c6434cf18f 100644 --- a/odoo/addons/base/models/res_country.py +++ b/odoo/addons/base/models/res_country.py @@ -75,11 +75,12 @@ class Country(models.Model): name_search = location_name_search - @api.model - def create(self, vals): - if vals.get('code'): - vals['code'] = vals['code'].upper() - return super(Country, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get('code'): + vals['code'] = vals['code'].upper() + return super(Country, self).create(vals_list) @api.multi def write(self, vals): diff --git a/odoo/addons/base/models/res_lang.py b/odoo/addons/base/models/res_lang.py index fa93289c4e1..56fea211a11 100644 --- a/odoo/addons/base/models/res_lang.py +++ b/odoo/addons/base/models/res_lang.py @@ -213,10 +213,10 @@ class Lang(models.Model): langs = self.with_context(active_test=True).search([]) return sorted([(lang.code, lang.name) for lang in langs], key=itemgetter(1)) - @api.model - def create(self, vals): + @api.model_create_multi + def create(self, vals_list): self.clear_caches() - return super(Lang, self).create(vals) + return super(Lang, self).create(vals_list) @api.multi def write(self, vals): diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py index 0efcc576da2..108b44ba9f9 100644 --- a/odoo/addons/base/models/res_partner.py +++ b/odoo/addons/base/models/res_partner.py @@ -17,6 +17,7 @@ from odoo import api, fields, models, tools, SUPERUSER_ID, _ from odoo.modules import get_module_resource from odoo.osv.expression import get_unaccent_wrapper from odoo.exceptions import UserError, ValidationError +from odoo.tools import pycompat # Global variables used for the warning fields declared on the res.partner # in the following modules : sale, purchase, account, stock @@ -535,21 +536,23 @@ class Partner(models.Model): partner._fields_sync(vals) return result - @api.model - def create(self, vals): - if vals.get('website'): - vals['website'] = self._clean_website(vals['website']) - if vals.get('parent_id'): - vals['company_name'] = False - # compute default image in create, because computing gravatar in the onchange - # cannot be easily performed if default images are in the way - if not vals.get('image'): - vals['image'] = self._get_default_image(vals.get('type'), vals.get('is_company'), vals.get('parent_id')) - tools.image_resize_images(vals, sizes={'image': (1024, None)}) - partner = super(Partner, self).create(vals) - partner._fields_sync(vals) - partner._handle_first_contact_creation() - return partner + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get('website'): + vals['website'] = self._clean_website(vals['website']) + if vals.get('parent_id'): + vals['company_name'] = False + # compute default image in create, because computing gravatar in the onchange + # cannot be easily performed if default images are in the way + if not vals.get('image'): + vals['image'] = self._get_default_image(vals.get('type'), vals.get('is_company'), vals.get('parent_id')) + tools.image_resize_images(vals, sizes={'image': (1024, None)}) + partners = super(Partner, self).create(vals_list) + for partner, vals in pycompat.izip(partners, vals_list): + partner._fields_sync(vals) + partner._handle_first_contact_creation() + return partners @api.multi def create_company(self): diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py index 6d29690b7e9..f4c6cfea0cc 100644 --- a/odoo/addons/base/models/res_users.py +++ b/odoo/addons/base/models/res_users.py @@ -404,14 +404,14 @@ class Users(models.Model): return super(Users, self)._search(args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid) - @api.model - def create(self, vals): - # import pudb; pudb.set_trace() - user = super(Users, self).create(vals) - user.partner_id.active = user.active - if user.partner_id.company_id: - user.partner_id.write({'company_id': user.company_id.id}) - return user + @api.model_create_multi + def create(self, vals_list): + users = super(Users, self).create(vals_list) + for user in users: + user.partner_id.active = user.active + if user.partner_id.company_id: + user.partner_id.write({'company_id': user.company_id.id}) + return users @api.multi def write(self, values): @@ -842,14 +842,15 @@ class GroupsImplied(models.Model): for g in self: g.trans_implied_ids = g.implied_ids | g.mapped('implied_ids.trans_implied_ids') - @api.model - def create(self, values): - user_ids = values.pop('users', None) - group = super(GroupsImplied, self).create(values) - if user_ids: - # delegate addition of users to add implied groups - group.write({'users': user_ids}) - return group + @api.model_create_multi + def create(self, vals_list): + user_ids_list = [vals.pop('users', None) for vals in vals_list] + groups = super(GroupsImplied, self).create(vals_list) + for group, user_ids in pycompat.izip(groups, user_ids_list): + if user_ids: + # delegate addition of users to add implied groups + group.write({'users': user_ids}) + return groups @api.multi def write(self, values): @@ -866,14 +867,15 @@ class GroupsImplied(models.Model): class UsersImplied(models.Model): _inherit = 'res.users' - @api.model - def create(self, values): - if 'groups_id' in values: - # complete 'groups_id' with implied groups - user = self.new(values) - gs = user.groups_id | user.groups_id.mapped('trans_implied_ids') - values['groups_id'] = type(self).groups_id.convert_to_write(gs, user.groups_id) - return super(UsersImplied, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for values in vals_list: + if 'groups_id' in values: + # complete 'groups_id' with implied groups + user = self.new(values) + gs = user.groups_id | user.groups_id.mapped('trans_implied_ids') + values['groups_id'] = type(self).groups_id.convert_to_write(gs, user.groups_id) + return super(UsersImplied, self).create(vals_list) @api.multi def write(self, values):