diff --git a/addons/mail/static/src/model/model_manager.js b/addons/mail/static/src/model/model_manager.js index fb3319a6fb3..815a3c05409 100644 --- a/addons/mail/static/src/model/model_manager.js +++ b/addons/mail/static/src/model/model_manager.js @@ -115,47 +115,56 @@ class ModelManager { * from this record creation. * * @param {mail.model} Model class - * @param {Object} [data={}] - * @returns {mail.model} newly created record + * @param {Object|Object[]} [data={}] + * If data is an iterable, multiple records will be created. + * @returns {mail.model|mail.model[]} newly created record(s) */ create(Model, data = {}) { return this._updateCycle(() => { - const record = new Model({ valid: true }); - Object.defineProperty(record, 'env', { get: () => Model.env }); - record.localId = record._createRecordLocalId(data); - // Contains field values of record. - record.__values = {}; - // Contains revNumber of record for checking record update in useStore. - record.__state = 0; + const isMulti = typeof data[Symbol.iterator] === 'function'; + const dataList = isMulti ? data : [data]; + const res = dataList.map(data => { + const record = new Model({ valid: true }); + Object.defineProperty(record, 'env', { get: () => Model.env }); + record.localId = record._createRecordLocalId(data); + // Contains field values of record. + record.__values = {}; + // Contains revNumber of record for checking record update in useStore. + record.__state = 0; - // Make proxified record, so that access to field redirects - // to field getter. - const proxifiedRecord = this._makeProxifiedRecord(record); - this._records[record.localId] = proxifiedRecord; - proxifiedRecord.init(); - this._makeDefaults(proxifiedRecord); + // Make proxified record, so that access to field redirects + // to field getter. + const proxifiedRecord = this._makeProxifiedRecord(record); + this._records[record.localId] = proxifiedRecord; + proxifiedRecord.init(); + this._makeDefaults(proxifiedRecord); - const data2 = Object.assign({}, data); - for (const field of Object.values(Model.fields)) { - if (field.fieldType !== 'relation') { - continue; + const data2 = Object.assign({}, data); + for (const field of Object.values(Model.fields)) { + if (field.fieldType !== 'relation') { + continue; + } + if (!field.autocreate) { + continue; + } + data2[field.fieldName] = [['create']]; } - if (!field.autocreate) { - continue; + + for (const field of Object.values(Model.fields)) { + if (field.compute || field.related) { + // new record should always invoke computed fields. + this.registerToComputeField(record, field); + } } - data2[field.fieldName] = [['create']]; + + this.update(proxifiedRecord, data2); + + return proxifiedRecord; + }); + if (!isMulti) { + return res[0]; } - - for (const field of Object.values(Model.fields)) { - if (field.compute || field.related) { - // new record should always invoke computed fields. - this.registerToComputeField(record, field); - } - } - - this.update(proxifiedRecord, data2); - - return proxifiedRecord; + return res; }); } @@ -259,18 +268,27 @@ class ModelManager { * per "unique find" criteria from data on Model. * * @param {mail.model} Model class - * @param {Object} data - * @returns {mail.model} created or updated record. + * @param {Object|Object[]} data + * If data is an iterable, multiple records will be created/updated. + * @returns {mail.model|mail.model[]} created or updated record(s). */ insert(Model, data) { return this._updateCycle(() => { - let record = Model.find(Model._findFunctionFromData(data)); - if (!record) { - record = Model.create(data); - } else { - record.update(data); + const isMulti = typeof data[Symbol.iterator] === 'function'; + const dataList = isMulti ? data : [data]; + const res = dataList.map(data => { + let record = Model.find(Model._findFunctionFromData(data)); + if (!record) { + record = Model.create(data); + } else { + record.update(data); + } + return record; + }); + if (!isMulti) { + return res[0]; } - return record; + return res; }); } @@ -721,7 +739,10 @@ class ModelManager { if (k === 'constructor') { return target[k]; } - const field = target.constructor.fields[k]; + const fields = target.constructor.fields; + const field = Object.prototype.hasOwnProperty.call(fields, k) + ? fields[k] + : undefined; if (!field) { // No crash, we allow these reads due to patch() // implementation details that read on `this._super` even diff --git a/addons/mail/static/src/models/chatter/chatter.js b/addons/mail/static/src/models/chatter/chatter.js index eb1081ac1b5..4df875a0898 100644 --- a/addons/mail/static/src/models/chatter/chatter.js +++ b/addons/mail/static/src/models/chatter/chatter.js @@ -61,13 +61,9 @@ function factory(dependencies) { method: 'activity_format', args: [newActivityIds] })); - const activities = []; - for (const activityData of activitiesData) { - const activity = this.env.models['mail.activity'].insert( - this.env.models['mail.activity'].convertData(activityData) - ); - activities.push(activity); - } + const activities = this.env.models['mail.activity'].insert(activitiesData.map( + activityData => this.env.models['mail.activity'].convertData(activityData) + )); this.update({ activities: [['replace', activities]] }); } diff --git a/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js b/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js index 5bcebff0af5..6da45ac49ca 100644 --- a/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js +++ b/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js @@ -143,11 +143,10 @@ function factory(dependencies) { channel_direct_message = [], channel_private_group = [], } = {}) { - for (const data of channel_channel.concat(channel_direct_message, channel_private_group)) { - this.env.models['mail.thread'].insert( - this.env.models['mail.thread'].convertData(data), - ); - } + const channelsData = channel_channel.concat(channel_direct_message, channel_private_group); + this.env.models['mail.thread'].insert(channelsData.map( + channelData => this.env.models['mail.thread'].convertData(channelData) + )); } /** @@ -203,10 +202,10 @@ function factory(dependencies) { * @param {Object} mailFailuresData */ _initMailFailures(mailFailuresData) { - for (const messageData of mailFailuresData) { - const message = this.env.models['mail.message'].insert( - this.env.models['mail.message'].convertData(messageData) - ); + const messages = this.env.models['mail.message'].insert(mailFailuresData.map( + messageData => this.env.models['mail.message'].convertData(messageData) + )); + for (const message of messages) { // implicit: failures are sent by the server at initialization // only if the current partner is author of the message if (!message.author && this.messaging.currentPartner) { diff --git a/addons/mail/static/src/models/model/model.js b/addons/mail/static/src/models/model/model.js index 15647acaf49..6ee5125426d 100644 --- a/addons/mail/static/src/models/model/model.js +++ b/addons/mail/static/src/models/model/model.js @@ -61,8 +61,9 @@ function factory() { * function, otherwise the record will not be registered. * * @static - * @param {Object} [data] data object with initial data, including relations. - * @returns {mail.model} newly created record + * @param {Object|Object[]} [data] data object with initial data, including relations. + * If data is an iterable, multiple records will be created. + * @returns {mail.model|mail.model[]} newly created record(s) */ static create(data) { return this.env.modelManager.create(this, data); @@ -99,8 +100,9 @@ function factory() { * on provided data. * * @static - * @param {Object} data - * @returns {mail.model} created or updated record. + * @param {Object|Object[]} data + * If data is an iterable, multiple records will be created/updated. + * @returns {mail.model|mail.model[]} created or updated record(s). */ static insert(data) { return this.env.modelManager.insert(this, data); diff --git a/addons/mail/static/src/models/notification_group_manager/notification_group_manager.js b/addons/mail/static/src/models/notification_group_manager/notification_group_manager.js index be7bef8352a..9c7c38ef52b 100644 --- a/addons/mail/static/src/models/notification_group_manager/notification_group_manager.js +++ b/addons/mail/static/src/models/notification_group_manager/notification_group_manager.js @@ -17,6 +17,7 @@ function factory(dependencies) { group.delete(); } const groups = []; + // TODO batch insert, better logic task-2258605 this.env.messaging.currentPartner.failureNotifications.forEach(notification => { const thread = notification.message.originThread; // Notifications are grouped by model and notification_type. diff --git a/addons/mail/static/src/models/partner/partner.js b/addons/mail/static/src/models/partner/partner.js index 646e3bdd1f7..32b02e6e60c 100644 --- a/addons/mail/static/src/models/partner/partner.js +++ b/addons/mail/static/src/models/partner/partner.js @@ -109,10 +109,10 @@ function factory(dependencies) { }, { shadow: true } ); - for (const data of partnersData) { - const partner = this.insert(this.convertData(data)); - partners.push(partner); - } + const newPartners = this.insert(partnersData.map( + partnerData => this.convertData(partnerData) + )); + partners.push(...newPartners); } callback(partners); } diff --git a/addons/mail/static/src/models/thread/thread.js b/addons/mail/static/src/models/thread/thread.js index cf3ce78b955..ceb4a366ec3 100644 --- a/addons/mail/static/src/models/thread/thread.js +++ b/addons/mail/static/src/models/thread/thread.js @@ -363,19 +363,14 @@ function factory(dependencies) { } return list; }, []); - const messagePreviews = await this.env.services.rpc({ + const channelPreviews = await this.env.services.rpc({ model: 'mail.channel', method: 'channel_fetch_preview', args: [channelIds], }, { shadow: true }); - for (const preview of messagePreviews) { - const messageData = preview.last_message; - if (messageData) { - this.env.models['mail.message'].insert( - this.env.models['mail.message'].convertData(messageData) - ); - } - } + this.env.models['mail.message'].insert(channelPreviews.filter(p => p.last_message).map( + channelPreview => this.env.models['mail.message'].convertData(channelPreview.last_message) + )); } /** diff --git a/addons/mail/static/src/models/thread_cache/thread_cache.js b/addons/mail/static/src/models/thread_cache/thread_cache.js index 81027d665ba..b4fe3ca28c3 100644 --- a/addons/mail/static/src/models/thread_cache/thread_cache.js +++ b/addons/mail/static/src/models/thread_cache/thread_cache.js @@ -266,11 +266,9 @@ function factory(dependencies) { * @param {Object[]} messageData */ _handleMessagesLoaded(messagesData) { - const messages = messagesData.map(data => - this.env.models['mail.message'].insert( - this.env.models['mail.message'].convertData(data) - ) - ); + const messages = this.env.models['mail.message'].insert(messagesData.map( + messageData => this.env.models['mail.message'].convertData(messageData) + )); if (!this.thread) { return;