[IMP] mail: make inverses optional when unused

IdentifyingFields and fields marked `required` must have their inverse
with `isCausal: true`. This led to having to systematically declare
inverses on the related models in order to explicitly add `isCausal:
true` to them, even when these inverses are not used for anything else.

This commit automatically adds `isCausal: true` to the inverses
generated by the framework when they are related to required fields or
identifyingFields. Thus, it is no longer necessary to declare inverses
if they are not used for anything else.

This commit also removes all the inverses that are no longer needed
thanks to this change.

closes odoo/odoo#81679

Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Louis Wicket (wil)
2021-12-20 17:59:01 +00:00
parent 6c39b566d6
commit 99b2c61d92
21 changed files with 11 additions and 61 deletions
@@ -638,6 +638,9 @@ export class ModelManager {
) {
throw new Error(`Mismatched relation types: ${field} on ${Model} (${field.relationType}) and ${inverseField} on ${RelatedModel} (${inverseField.relationType}).`);
}
if (field.required && !inverseField.isCausal) {
throw new Error(`${field} on ${Model} is required but its inverse ${inverseField} on ${RelatedModel} is not causal.`);
}
}
for (const identifyingField of Model.__identifyingFieldsFlattened) {
const field = Model.__fieldMap[identifyingField];
@@ -1070,7 +1073,10 @@ export class ModelManager {
relFunc(Model.name, { inverse: field.fieldName }),
{
fieldName: `_inverse_${Model}/${field.fieldName}`,
}
// Note that an identifying field is not necessarily defined as
// `required` (example: identifyingFields with an OR).
isCausal: field.required || Model.__identifyingFieldsFlattened.has(field.fieldName),
},
));
return inverseField;
}
@@ -279,29 +279,12 @@ registerModel({
activities: many2many('Activity', {
inverse: 'attachments',
}),
/**
* States the attachment cards that are displaying this attachment.
*/
attachmentCards: one2many('AttachmentCard', {
inverse: 'attachment',
isCausal: true,
}),
/**
* States the attachment images that are displaying this attachment.
*/
attachmentImages: one2many('AttachmentImage', {
inverse: 'attachment',
isCausal: true,
}),
/**
* States the attachment lists that are displaying this attachment.
*/
attachmentLists: many2many('AttachmentList', {
inverse: 'attachments',
}),
attachmentViewers: many2many('AttachmentViewer', {
inverse: 'attachments',
}),
checksum: attr(),
/**
* States on which composer this attachment is currently being created.
@@ -66,7 +66,6 @@ registerModel({
* Determines the attachment of this card.
*/
attachment: many2one('Attachment', {
inverse: 'attachmentCards',
readonly: true,
required: true,
}),
@@ -108,7 +108,6 @@ registerModel({
* Determines the attachment of this attachment image..
*/
attachment: many2one('Attachment', {
inverse: 'attachmentImages',
readonly: true,
required: true,
}),
@@ -80,13 +80,6 @@ registerModel({
inverse: 'attachmentList',
isCausal: true,
}),
/**
* Determines the attachment viewers displaying this attachment list (if any).
*/
attachmentViewers: one2many('AttachmentViewer', {
inverse: 'attachmentList',
isCausal: true,
}),
/**
* Link with a chatter to handle attachments.
*/
@@ -40,12 +40,10 @@ registerModel({
}),
attachment: many2one('Attachment'),
attachmentList: many2one('AttachmentList', {
inverse: 'attachmentViewers',
readonly: true,
required: true,
}),
attachments: many2many('Attachment', {
inverse: 'attachmentViewers',
related: 'attachmentList.viewableAttachments',
}),
/**
@@ -210,10 +210,6 @@ registerModel({
inviteButtonText: attr({
compute: '_computeInviteButtonText',
}),
popoverView: one2one('PopoverView', {
inverse: 'channelInvitationForm',
isCausal: true,
}),
/**
* States the OWL ref of the "search" input of this channel invitation
* form. Useful to be able to focus it.
@@ -135,10 +135,6 @@ registerModel({
compute: '_computeCanPostMessage',
default: false,
}),
composerViews: one2many('ComposerView', {
inverse: 'composer',
isCausal: true,
}),
/**
* This field determines whether some attachments linked to this
* composer are being uploaded.
@@ -871,7 +871,6 @@ registerModel({
*/
composer: many2one('Composer', {
compute: '_computeComposer',
inverse: 'composerViews',
required: true,
}),
/**
@@ -701,10 +701,6 @@ registerModel({
isCausal: true,
}),
message_type: attr(),
messageSeenIndicators: one2many('MessageSeenIndicator', {
inverse: 'message',
isCausal: true,
}),
/**
* States the views that are displaying this message.
*/
@@ -226,7 +226,6 @@ registerModel({
* The message concerned by this seen indicator.
*/
message: many2one('Message', {
inverse: 'messageSeenIndicators',
readonly: true,
required: true,
}),
@@ -446,10 +446,6 @@ registerModel({
nameOrDisplayName: attr({
compute: '_computeNameOrDisplayName',
}),
partnerSeenInfos: one2many('ThreadPartnerSeenInfo', {
inverse: 'partner',
isCausal: true,
}),
rtcSessions: one2many('RtcSession', {
inverse: 'partner',
}),
@@ -68,7 +68,6 @@ registerModel({
* The record that represents the content inside the popover view.
*/
channelInvitationForm: one2one('ChannelInvitationForm', {
inverse: 'popoverView',
isCausal: true,
readonly: true,
required: true,
@@ -366,7 +366,6 @@ registerModel({
rtcController: one2one('RtcController', {
default: insertAndReplace(),
readonly: true,
required: true,
inverse: 'callViewer',
isCausal: true,
}),
@@ -87,7 +87,6 @@ registerModel({
userSetting: one2one('UserSetting', {
inverse: 'rtcConfigurationMenu',
readonly: true,
required: true,
}),
},
});
@@ -92,7 +92,6 @@ registerModel({
default: insertAndReplace(),
inverse: 'rtcController',
isCausal: true,
required: true,
}),
},
});
@@ -403,6 +403,7 @@ registerModel({
}),
thread: one2one('Thread', {
inverse: 'cache',
isCausal: true,
readonly: true,
required: true,
}),
@@ -13,7 +13,6 @@ registerModel({
* Partner that this seen info is related to.
*/
partner: many2one('Partner', {
inverse: 'partnerSeenInfos',
readonly: true,
required: true,
}),
@@ -61,10 +61,12 @@ registerModel({
fields: {
chatter: one2one('Chatter', {
inverse: 'threadViewer',
isCausal: true,
readonly: true,
}),
chatWindow: one2one('ChatWindow', {
inverse: 'threadViewer',
isCausal: true,
readonly: true,
}),
/**
@@ -75,6 +77,7 @@ registerModel({
}),
discuss: one2one('Discuss', {
inverse: 'threadViewer',
isCausal: true,
readonly: true,
}),
discussPublicView: one2one('DiscussPublicView', {
@@ -255,7 +255,6 @@ registerModel({
default: insertAndReplace(),
inverse: 'userSetting',
isCausal: true,
required: true,
}),
/**
* layout of the rtc session display chosen by the user
@@ -282,12 +281,5 @@ registerModel({
voiceActiveDuration: attr({
default: 0,
}),
/**
* Models that represent the volume chosen by the user for each partner.
*/
volumeSettings: one2many('VolumeSetting', {
inverse: 'userSetting',
isCausal: true,
}),
},
});
@@ -39,7 +39,6 @@ registerModel({
inverse: 'volumeSetting',
}),
userSetting: many2one('UserSetting', {
inverse: 'volumeSettings',
required: true,
}),
volume: attr({