[REF] *: adapt create to work in batch

This commit is contained in:
Raphael Collet
2018-07-24 16:58:14 +02:00
parent b1e83fd7b8
commit 896e36edab
38 changed files with 446 additions and 406 deletions
+37 -43
View File
@@ -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):
+8 -7
View File
@@ -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):
@@ -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
+9 -8
View File
@@ -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)
@@ -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
+20 -19
View File
@@ -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):
+5 -4
View File
@@ -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):
+10 -9
View File
@@ -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):
+8 -6
View File
@@ -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)
+25 -20
View File
@@ -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):
+6 -5
View File
@@ -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):
+6 -5
View File
@@ -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
+8 -7
View File
@@ -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):
+8 -7
View File
@@ -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):
+51 -45
View File
@@ -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
+5 -6
View File
@@ -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):
+11 -10
View File
@@ -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): <a href=# data-oe-model=project.task data-oe-id=%d>%s</a>") % (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): <a href=# data-oe-model=project.task data-oe-id=%d>%s</a>") % (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):
+9 -8
View File
@@ -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)
+11 -9
View File
@@ -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
+45 -41
View File
@@ -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
+3 -3
View File
@@ -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):
+10 -9
View File
@@ -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):
+7 -6
View File
@@ -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):
+7 -6
View File
@@ -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):
+13 -12
View File
@@ -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):
+9 -8
View File
@@ -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):
@@ -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):
+3 -3
View File
@@ -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):
+3 -3
View File
@@ -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):
+7 -4
View File
@@ -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):
+3 -3
View File
@@ -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
+10 -9
View File
@@ -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):
+6 -5
View File
@@ -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):
+19 -18
View File
@@ -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):
+6 -5
View File
@@ -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):
+3 -3
View File
@@ -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):
+18 -15
View File
@@ -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):
+26 -24
View File
@@ -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):