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 @@
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 @@
+
+