From af55603e088a373efbed0e2f986225d2ee2a23a7 Mon Sep 17 00:00:00 2001 From: Cyril VINH-TUNG Date: Sun, 28 Jun 2026 15:38:11 -1000 Subject: [PATCH] [IMP] partner_event: avoid matching partner_id as attendee when email is shared --- partner_event/README.rst | 4 + partner_event/models/event_registration.py | 54 ++++++++----- partner_event/readme/CONTRIBUTORS.md | 2 + partner_event/static/description/index.html | 4 + .../tests/test_event_registration.py | 77 +++++++++++++++++++ 5 files changed, 122 insertions(+), 19 deletions(-) diff --git a/partner_event/README.rst b/partner_event/README.rst index 9606002a6..9c3966d0d 100644 --- a/partner_event/README.rst +++ b/partner_event/README.rst @@ -111,6 +111,10 @@ Contributors - Bhavesh Heliconia +- `INVITU `__ + + - Cyril Vinh-Tung + Maintainers ----------- diff --git a/partner_event/models/event_registration.py b/partner_event/models/event_registration.py index 04f56addc..16d3f08a2 100644 --- a/partner_event/models/event_registration.py +++ b/partner_event/models/event_registration.py @@ -54,29 +54,45 @@ def action_create_attendee_partner(self): @api.model def _update_attendee_partner_id(self, vals): # Don't update if doing a partner merging - if ( - not vals.get("attendee_partner_id") - and vals.get("email") - and not self.env.context.get("partner_event_merging") + if not vals.get("attendee_partner_id") and not self.env.context.get( + "partner_event_merging" ): Partner = self.env["res.partner"] Event = self.env["event.event"] - # Look for a partner with that email - email = vals.get("email").replace("%", "").replace("_", "\\_") - # Order was done for avoiding extra queries for sorting the results - attendee_partner = Partner.search( - [("email", "=ilike", email)], limit=1, order="id" - ) - event = Event.browse() - if vals.get("event_id"): - event = Event.browse(vals["event_id"]) + attendee_partner = Partner.browse() + attendee_email = vals.get("email") + if attendee_email: + # Don't search by email if it belongs to partner_id: + # the attendee may be a different person (e.g. a child + # registered with partner_id's email as fallback). + partner_email = False + if vals.get("partner_id"): + partner_email = Partner.browse(vals["partner_id"]).email + if attendee_email != partner_email: + # Look for a partner with that email + clean_email = attendee_email.replace("%", "").replace("_", "\\_") + # Order was done for avoiding extra queries for sorting + attendee_partner = Partner.search( + [("email", "=ilike", clean_email)], limit=1, order="id" + ) + if attendee_partner: + for field in {"name", "phone"}: + vals[field] = vals.get(field) or attendee_partner[field] + if not attendee_partner: + event = Event.browse() + if vals.get("event_id"): + event = Event.browse(vals["event_id"]) + if ( + event + and event.create_partner + and (vals.get("name") or vals.get("email")) + ): + # Create partner + attendee_partner = Partner.sudo().create( + self._prepare_partner(vals) + ) if attendee_partner: - for field in {"name", "phone"}: - vals[field] = vals.get(field) or attendee_partner[field] - elif event and event.create_partner: - # Create partner - attendee_partner = Partner.sudo().create(self._prepare_partner(vals)) - vals["attendee_partner_id"] = attendee_partner.id + vals["attendee_partner_id"] = attendee_partner.id return vals @api.model_create_multi diff --git a/partner_event/readme/CONTRIBUTORS.md b/partner_event/readme/CONTRIBUTORS.md index 2f363146c..5488a84af 100644 --- a/partner_event/readme/CONTRIBUTORS.md +++ b/partner_event/readme/CONTRIBUTORS.md @@ -14,3 +14,5 @@ - Anil Kesariya - [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io) - Bhavesh Heliconia +- [INVITU](https://www.invitu.com) + - Cyril Vinh-Tung diff --git a/partner_event/static/description/index.html b/partner_event/static/description/index.html index 24ab94e80..7b82cf19e 100644 --- a/partner_event/static/description/index.html +++ b/partner_event/static/description/index.html @@ -456,6 +456,10 @@

Contributors

  • Bhavesh Heliconia
  • +
  • INVITU
      +
    • Cyril Vinh-Tung
    • +
    +
  • diff --git a/partner_event/tests/test_event_registration.py b/partner_event/tests/test_event_registration.py index 59ce3e037..1b5e00250 100644 --- a/partner_event/tests/test_event_registration.py +++ b/partner_event/tests/test_event_registration.py @@ -111,3 +111,80 @@ def test_delete_registered_partner(self): partner3 = self.env["res.partner"].create({"name": "unregistered partner"}) partner3.unlink() self.assertFalse(partner3.exists()) + + @mute_logger("odoo.models.unlink") + def test_action_merge(self): + partner_1 = self.env["res.partner"].create( + {"name": "Merge Partner 1", "email": "merge1@test.com"} + ) + partner_2 = self.env["res.partner"].create( + {"name": "Merge Partner 2", "email": "merge2@test.com"} + ) + self.registration_01.partner_id = partner_1 + self.registration_02.partner_id = partner_2 + partners = partner_1 + partner_2 + wizard = ( + self.env["base.partner.merge.automatic.wizard"] + .with_context(active_ids=partners.ids, active_model=partners._name) + .create({}) + ) + self.assertEqual(wizard.dst_partner_id, partner_2) + wizard.action_merge() + self.assertEqual(self.registration_01.partner_id, partner_2) + self.assertEqual(self.registration_02.partner_id, partner_2) + + def test_attendee_with_partner_id_email_gets_own_partner(self): + """When attendee uses partner_id email as fallback, a new partner is created + instead of matching partner_id.""" + reg = self.env["event.registration"].create( + { + "event_id": self.event_0.id, + "partner_id": self.partner_01.id, + "name": "Child Attendee with partner email", + "email": self.partner_01.email, + } + ) + self.assertNotEqual(reg.attendee_partner_id, self.partner_01) + self.assertEqual( + reg.attendee_partner_id.name, "Child Attendee with partner email" + ) + + def test_attendee_without_email_gets_own_partner(self): + """When attendee has no email, a partner is still created if create_partner + is checked.""" + reg = self.env["event.registration"].create( + { + "event_id": self.event_0.id, + "partner_id": self.partner_01.id, + "name": "Child No Email", + } + ) + self.assertEqual(reg.attendee_partner_id.name, "Child No Email") + + def test_attendee_with_own_email_still_gets_matched(self): + """When attendee has their own email, normal matching still occurs.""" + reg = self.env["event.registration"].create( + { + "event_id": self.event_0.id, + "partner_id": self.partner_01.id, + "name": "Child Attendee with email", + "email": self.registration_02.attendee_partner_id.email, + } + ) + self.assertEqual( + reg.attendee_partner_id, self.registration_02.attendee_partner_id + ) + + def test_write_without_email_preserves_attendee_partner(self): + """Writing to a registration without email must not clear + attendee_partner_id.""" + reg = self.env["event.registration"].create( + { + "event_id": self.event_0.id, + "email": self.registration_02.attendee_partner_id.email, + } + ) + existing_attendee = reg.attendee_partner_id + self.assertTrue(existing_attendee) + reg.write({"name": "Updated Name"}) + self.assertEqual(reg.attendee_partner_id, existing_attendee)