diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index 63f70d03c4..1e6c174611 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -883,6 +883,8 @@ class Event(EventMixin, LoggedModel): ItemProgramTime, ItemVariationMetaValue, Question, Quota, ) + is_cross_organizer = other.organizer_id != self.organizer_id + # Note: avoid self.set_active_plugins(), it causes trouble e.g. for the badges plugin. # Plugins can create data in installed() hook based on existing data of the event. # Calling set_active_plugins() results in defaults being created while actually data @@ -911,6 +913,15 @@ class Event(EventMixin, LoggedModel): for emv in EventMetaValue.objects.filter(event=other): emv.pk = None emv.event = self + if is_cross_organizer: + try: + emv.property = self.organizer.meta_properties.get(name=emv.property.name) + except EventMetaProperty.DoesNotExist: + meta_prop = emv.property + meta_prop.pk = None + meta_prop.organizer = self.organizer + meta_prop.save(force_insert=True) + emv.property = meta_prop emv.save(force_insert=True) for fl in EventFooterLink.objects.filter(event=other): @@ -964,13 +975,13 @@ class Event(EventMixin, LoggedModel): if i.tax_rule_id: i.tax_rule = tax_map[i.tax_rule_id] - if i.grant_membership_type and other.organizer_id != self.organizer_id: + if i.grant_membership_type and is_cross_organizer: i.grant_membership_type = None i.save() # no force_insert since i.picture.save could have already inserted i.log_action('pretix.object.cloned') - if require_membership_types and other.organizer_id == self.organizer_id: + if require_membership_types and not is_cross_organizer: i.require_membership_types.set(require_membership_types) if not i.all_sales_channels: @@ -985,7 +996,7 @@ class Event(EventMixin, LoggedModel): v._prefetched_objects_cache = {} v.save(force_insert=True) - if require_membership_types and other.organizer_id == self.organizer_id: + if require_membership_types and not is_cross_organizer: v.require_membership_types.set(require_membership_types) if not v.all_sales_channels: v.limit_sales_channels.set(self.organizer.sales_channels.filter(identifier__in=[s.identifier for s in limit_sales_channels])) @@ -1869,6 +1880,8 @@ class EventMetaValue(LoggedModel): self.event.cache.clear() def save(self, *args, **kwargs): + if self.event and self.event.organizer != self.property.organizer: + raise ValidationError(_("Property and event must belong to the same organizer.")) super().save(*args, **kwargs) if self.event: self.event.cache.clear() diff --git a/src/tests/base/test_event_clone.py b/src/tests/base/test_event_clone.py index a003bf54ec..ed2259672f 100644 --- a/src/tests/base/test_event_clone.py +++ b/src/tests/base/test_event_clone.py @@ -243,6 +243,9 @@ def test_full_clone_cross_organizer_differences(): sc1_c = organizer.sales_channels.create(identifier="c") sc2_a = organizer2.sales_channels.get(identifier="web") sc2_c = organizer2.sales_channels.create(identifier="c") + o1_meta_prop_a = organizer.meta_properties.create(name="Prop to copy") + o1_meta_prop_b = organizer.meta_properties.create(name="Prop to find") + o2_meta_prop_b = organizer2.meta_properties.create(name="Prop to find") event = Event.objects.create( organizer=organizer, name='Dummy', slug='dummy', @@ -267,6 +270,9 @@ def test_full_clone_cross_organizer_differences(): event.settings.payment_giftcard__enabled = True event.settings.payment_giftcard__restrict_to_sales_channels = ['web', 'b', 'c'] + event.meta_values.create(property=o1_meta_prop_a, value='a') + event.meta_values.create(property=o1_meta_prop_b, value='b') + copied_event = Event.objects.create( organizer=organizer2, name='Dummy2', slug='dummy2', date_from=datetime.datetime(2022, 4, 15, 9, 0, 0, tzinfo=datetime.timezone.utc), @@ -289,3 +295,9 @@ def test_full_clone_cross_organizer_differences(): assert event.settings.get('payment_giftcard__restrict_to_sales_channels', as_type=list) == ['web', 'b', 'c'] assert copied_event.settings.get('payment_giftcard__restrict_to_sales_channels', as_type=list) == ['web', 'c'] + + assert event.meta_values.get(property__name=o1_meta_prop_a.name).property.organizer == organizer + assert copied_event.meta_values.get(property__name=o1_meta_prop_a.name).value == 'a' + assert copied_event.meta_values.get(property__name=o1_meta_prop_a.name).property.organizer == organizer2 + assert copied_event.meta_values.get(property=o2_meta_prop_b).value == 'b' + assert copied_event.meta_values.get(property=o2_meta_prop_b).property.organizer == organizer2