Do not CASCADE-delete vouchers when deleting items or quotas

This commit is contained in:
Raphael Michel
2019-07-11 12:33:42 +02:00
parent f066ed01ff
commit d994fc674a
6 changed files with 66 additions and 20 deletions

View File

@@ -174,6 +174,8 @@ def filter_available(qs, channel='web', voucher=None, allow_addons=False):
q &= Q(pk=voucher.item_id)
elif voucher.quota_id:
q &= Q(quotas__in=[voucher.quota_id])
else:
return qs.none()
if not voucher or not voucher.show_hidden_items:
q &= Q(hide_without_voucher=False)
@@ -411,6 +413,7 @@ class Item(LoggedModel):
self.event.cache.clear()
def delete(self, *args, **kwargs):
self.vouchers.update(item=None, variation=None, quota=None)
super().delete(*args, **kwargs)
if self.event:
self.event.cache.clear()
@@ -655,6 +658,7 @@ class ItemVariation(models.Model):
return t
def delete(self, *args, **kwargs):
self.vouchers.update(item=None, variation=None, quota=None)
super().delete(*args, **kwargs)
if self.item:
self.item.event.cache.clear()
@@ -1275,6 +1279,7 @@ class Quota(LoggedModel):
return self.name
def delete(self, *args, **kwargs):
self.vouchers.update(item=None, variation=None, quota=None)
super().delete(*args, **kwargs)
if self.event:
self.event.cache.clear()

View File

@@ -142,22 +142,26 @@ class Voucher(LoggedModel):
item = models.ForeignKey(
Item, related_name='vouchers',
verbose_name=_("Product"),
null=True, blank=True, on_delete=models.CASCADE,
null=True, blank=True,
on_delete=models.PROTECT, # We use a fake version of SET_NULL in Item.delete()
help_text=_(
"This product is added to the user's cart if the voucher is redeemed."
)
)
variation = models.ForeignKey(
ItemVariation, related_name='vouchers',
null=True, blank=True, on_delete=models.CASCADE,
null=True, blank=True,
on_delete=models.PROTECT, # We use a fake version of SET_NULL in ItemVariation.delete() to avoid the semantic change
# that would happen if we just set variation to None
verbose_name=_("Product variation"),
help_text=_(
"This variation of the product select above is being used."
)
)
quota = models.ForeignKey(
Quota, related_name='quota',
null=True, blank=True, on_delete=models.CASCADE,
Quota, related_name='vouchers',
null=True, blank=True,
on_delete=models.PROTECT, # We use a fake version of SET_NULL in Quota.delete()
verbose_name=_("Quota"),
help_text=_(
"If enabled, the voucher is valid for any product affected by this quota."
@@ -408,4 +412,7 @@ class Voucher(LoggedModel):
kwargs['subevent'] = self.subevent
if self.quota_id:
return SeatCategoryMapping.objects.filter(product__quotas__pk=self.quota_id, **kwargs).exists()
return self.item.seat_category_mappings.filter(**kwargs).exists()
elif self.item_id:
return self.item.seat_category_mappings.filter(**kwargs).exists()
else:
return False

View File

@@ -17,6 +17,19 @@
{% csrf_token %}
{% if possible %}
<p>{% blocktrans %}Are you sure you want to delete the product <strong>{{ item }}</strong>?{% endblocktrans %}</p>
{% if vouchers %}
<div class="alert alert-warning">
{% blocktrans trimmed count count=vouchers %}
That will cause {{ count }} voucher to be unusable.
{% plural %}
That will cause {{ count }} voucher to be unusable.
{% endblocktrans %}
<a href="{% url "control:event.vouchers" organizer=request.organizer.slug event=request.event.slug %}?itemvar={{ item.pk }}"
class="btn btn-default">
{% trans "Show affected vouchers" %}
</a>
</div>
{% endif %}
{% else %}
<p>{% blocktrans %}You cannot delete the product <strong>{{ item }}</strong> because it already has been ordered, but you can deactivate it.{% endblocktrans %}</p>
{% endif %}

View File

@@ -3,23 +3,42 @@
{% load bootstrap3 %}
{% block title %}{% trans "Delete quota" %}{% endblock %}
{% block inside %}
<h1>{% trans "Delete quota" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to delete the quota <strong>{{ quota }}</strong>?{% endblocktrans %}</p>
{% if dependent|length > 0 %}
<p>{% blocktrans %}The following products might be no longer available for sale:{% endblocktrans %}</p>
{% for item in dependent %}
<li><a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.pk %}">{{ item.name }}</a></li>
{% endfor %}
{% endif %}
<div class="form-group submit-group">
<h1>{% trans "Delete quota" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to delete the quota <strong>{{ quota }}</strong>?{% endblocktrans %}</p>
{% if dependent|length > 0 %}
<div class="alert alert-info">
<p>{% blocktrans %}The following products might be no longer available for sale:{% endblocktrans %}</p>
<ul>
{% for item in dependent %}
<li>
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.pk %}">{{ item.name }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if vouchers %}
<div class="alert alert-warning">
{% blocktrans trimmed count count=vouchers %}
That will cause {{ count }} voucher to be unusable.
{% plural %}
That will cause {{ count }} voucher to be unusable.
{% endblocktrans %}
<a href="{% url "control:event.vouchers" organizer=request.organizer.slug event=request.event.slug %}?itemvar=q-{{ quota.pk }}"
class="btn btn-default">
{% trans "Show affected vouchers" %}
</a>
</div>
{% endif %}
<div class="form-group submit-group">
<a href="{% url "control:event.items.quotas" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default btn-cancel">
{% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger btn-save">
{% trans "Delete" %}
</button>
</div>
</form>
</div>
</form>
{% endblock %}

View File

@@ -128,7 +128,7 @@
{% if v.variation %}
{{ v.variation }}
{% endif %}
{% else %}
{% elif v.quota %}
{% blocktrans trimmed with quota=v.quota.name %}
Any product in quota "{{ quota }}"
{% endblocktrans %}

View File

@@ -771,7 +771,8 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView):
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['dependent'] = list(self.get_object().items.all())
context['dependent'] = list(self.object.items.all())
context['vouchers'] = self.object.vouchers.count()
return context
@transaction.atomic
@@ -1183,6 +1184,7 @@ class ItemDelete(EventPermissionRequiredMixin, DeleteView):
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['possible'] = self.is_allowed()
context['vouchers'] = self.object.vouchers.count()
return context
def is_allowed(self) -> bool: