From 6218ee73cce50c87abdb069ccfcc8aad20ab2903 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Nov 2023 11:36:40 +0100 Subject: [PATCH] [FIX] event_registration_partner_unique: avoid constraint on merge When a user merges two contacts an error will raise if there are event with a unique partner constraint when both partner were atendees. With these changes, we let the user get rid of those duplicated attendees. TT45921 --- event_registration_partner_unique/README.rst | 2 +- event_registration_partner_unique/__init__.py | 1 + .../__manifest__.py | 2 +- .../models/event.py | 35 +++++++++++------- .../static/description/index.html | 2 +- .../tests/test_event.py | 36 ++++++++++++++----- .../wizards/__init__.py | 1 + .../wizards/base_partner_merge.py | 33 +++++++++++++++++ .../wizards/base_partner_merge_views.xml | 12 +++++++ 9 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 event_registration_partner_unique/wizards/__init__.py create mode 100644 event_registration_partner_unique/wizards/base_partner_merge.py create mode 100644 event_registration_partner_unique/wizards/base_partner_merge_views.xml diff --git a/event_registration_partner_unique/README.rst b/event_registration_partner_unique/README.rst index 8a9272d05..03271544e 100644 --- a/event_registration_partner_unique/README.rst +++ b/event_registration_partner_unique/README.rst @@ -7,7 +7,7 @@ Unique Partner per Event !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:fa39a5416b9a53ff743d770b11ab214277fe38d04077dfa770174e259f481c29 + !! source digest: sha256:3d53fec9f6993c3a355d0cac4a4fc844e5290c7466ea731d4f6174c8486d9249 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/event_registration_partner_unique/__init__.py b/event_registration_partner_unique/__init__.py index 83e553ac4..0aa9b03c5 100644 --- a/event_registration_partner_unique/__init__.py +++ b/event_registration_partner_unique/__init__.py @@ -1,3 +1,4 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import models +from . import wizards diff --git a/event_registration_partner_unique/__manifest__.py b/event_registration_partner_unique/__manifest__.py index d589c8108..3075b93d3 100644 --- a/event_registration_partner_unique/__manifest__.py +++ b/event_registration_partner_unique/__manifest__.py @@ -14,5 +14,5 @@ "application": False, "installable": True, "depends": ["event", "partner_event"], - "data": ["views/event_event_view.xml"], + "data": ["views/event_event_view.xml", "wizards/base_partner_merge_views.xml"], } diff --git a/event_registration_partner_unique/models/event.py b/event_registration_partner_unique/models/event.py index 3ed3fa9ca..db25fa958 100644 --- a/event_registration_partner_unique/models/event.py +++ b/event_registration_partner_unique/models/event.py @@ -18,6 +18,8 @@ class EventEvent(models.Model): @api.constrains("forbid_duplicates", "registration_ids") def _check_forbid_duplicates(self): """Ensure no duplicated attendee are found in the event.""" + if self.env.context.get("skip_registration_partner_unique"): + return return self.filtered( "forbid_duplicates" ).registration_ids._check_forbid_duplicates() @@ -29,19 +31,28 @@ class EventRegistration(models.Model): @api.constrains("event_id", "attendee_partner_id") def _check_forbid_duplicates(self): """Ensure no duplicated attendees are found in the event.""" + if self.env.context.get("skip_registration_partner_unique"): + return + for event_reg, dupes in self._find_duplicated_attendees(): + if not dupes: + continue + raise ValidationError( + _("Duplicated partners found in event %(name)s: %(partners)s.") + % { + "name": event_reg.event_id.display_name, + "partners": ", ".join( + partner_id.display_name + for partner_id in dupes.mapped("attendee_partner_id") + ), + } + ) + + def _find_duplicated_attendees(self): for event_reg in self.filtered("event_id.forbid_duplicates"): - dupes = self.search(event_reg._duplicate_search_domain()) - if dupes: - raise ValidationError( - _("Duplicated partners found in event %(name)s: %(partners)s.") - % { - "name": event_reg.event_id.display_name, - "partners": ", ".join( - partner_id.display_name - for partner_id in dupes.mapped("attendee_partner_id") - ), - } - ) + dupes = self.search( + event_reg._duplicate_search_domain(), order="create_date desc" + ) + yield event_reg, dupes def _duplicate_search_domain(self): """What to look for when searching duplicates.""" diff --git a/event_registration_partner_unique/static/description/index.html b/event_registration_partner_unique/static/description/index.html index e4cd2f28a..afbf0e16d 100644 --- a/event_registration_partner_unique/static/description/index.html +++ b/event_registration_partner_unique/static/description/index.html @@ -367,7 +367,7 @@

Unique Partner per Event

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:fa39a5416b9a53ff743d770b11ab214277fe38d04077dfa770174e259f481c29 +!! source digest: sha256:3d53fec9f6993c3a355d0cac4a4fc844e5290c7466ea731d4f6174c8486d9249 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/event Translate me on Weblate Try me on Runboat

This module is intended for backend use only, and extends the functionality diff --git a/event_registration_partner_unique/tests/test_event.py b/event_registration_partner_unique/tests/test_event.py index 49cc43702..9394e5e40 100644 --- a/event_registration_partner_unique/tests/test_event.py +++ b/event_registration_partner_unique/tests/test_event.py @@ -2,21 +2,39 @@ # Copyright 2020 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import fields from odoo.exceptions import ValidationError from odoo.tests.common import TransactionCase class DuplicatedPartnerCase(TransactionCase): - def setUp(self): - super().setUp() - self.event = self.env.ref("event.event_0") - self.event.forbid_duplicates = False - self.partner = self.env.ref("base.res_partner_1") - self.registration = self.env["event.registration"].create( + @classmethod + def setUpClass(cls): + super().setUpClass() + # Remove this variable in v16 and put instead: + # from odoo.addons.base.tests.common import DISABLED_MAIL_CONTEXT + DISABLED_MAIL_CONTEXT = { + "tracking_disable": True, + "mail_create_nolog": True, + "mail_create_nosubscribe": True, + "mail_notrack": True, + "no_reset_password": True, + } + cls.env = cls.env(context=dict(cls.env.context, **DISABLED_MAIL_CONTEXT)) + cls.event = cls.env["event.event"].create( { - "event_id": self.event.id, - "partner_id": self.partner.id, - "attendee_partner_id": self.partner.id, + "name": "Test event", + "date_begin": fields.Datetime.now(), + "date_end": fields.Datetime.now(), + } + ) + cls.event.forbid_duplicates = False + cls.partner = cls.env["res.partner"].create({"name": "Mr. Odoo"}) + cls.registration = cls.env["event.registration"].create( + { + "event_id": cls.event.id, + "partner_id": cls.partner.id, + "attendee_partner_id": cls.partner.id, } ) diff --git a/event_registration_partner_unique/wizards/__init__.py b/event_registration_partner_unique/wizards/__init__.py new file mode 100644 index 000000000..e3fc7010c --- /dev/null +++ b/event_registration_partner_unique/wizards/__init__.py @@ -0,0 +1 @@ +from . import base_partner_merge diff --git a/event_registration_partner_unique/wizards/base_partner_merge.py b/event_registration_partner_unique/wizards/base_partner_merge.py new file mode 100644 index 000000000..ce716b560 --- /dev/null +++ b/event_registration_partner_unique/wizards/base_partner_merge.py @@ -0,0 +1,33 @@ +# Copyright 2023 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import fields, models + + +class BasePartnerMergeAutomaticWizard(models.TransientModel): + _inherit = "base.partner.merge.automatic.wizard" + + merge_duplicated_registrations = fields.Boolean( + help="If the merged partners were linked to registrations in an event that had " + "unique attendees flag we'll take only the oldest one", + ) + + def action_merge(self): + """Allow to get rid of duplicated registrations linked to the merged partners""" + if not self.merge_duplicated_registrations: + return super().action_merge() + # Skip the constraints and do the partner merge first + res = super( + BasePartnerMergeAutomaticWizard, + self.with_context(skip_registration_partner_unique=True), + ).action_merge() + # Now let's merge the attendees + dupes_to_unlink = self.env["event.registration"] + partner_registrations = self.dst_partner_id.event_registration_ids + for attendee, dupes in partner_registrations._find_duplicated_attendees(): + # Keep the oldest one -> min id will be + event_attendees_for_partner = attendee + dupes + attendee_to_keep = min(event_attendees_for_partner, key=lambda x: x.id) + dupes_to_unlink += event_attendees_for_partner - attendee_to_keep + # Call with skipping context to avoid triggering the constraint again + dupes_to_unlink.with_context(skip_registration_partner_unique=True).unlink() + return res diff --git a/event_registration_partner_unique/wizards/base_partner_merge_views.xml b/event_registration_partner_unique/wizards/base_partner_merge_views.xml new file mode 100644 index 000000000..d361e888b --- /dev/null +++ b/event_registration_partner_unique/wizards/base_partner_merge_views.xml @@ -0,0 +1,12 @@ + + + + + base.partner.merge.automatic.wizard + + + + + + +