manager: allow duplication of events (#67292)

This commit is contained in:
Agate 2022-07-12 15:41:34 +02:00 committed by Agate Berriot
parent 963871ce43
commit bca73caaea
7 changed files with 173 additions and 1 deletions

View File

@ -1709,9 +1709,13 @@ class Event(models.Model):
'duration': self.duration,
}
def duplicate(self, agenda_target=None, primary_event=None):
def duplicate(self, agenda_target=None, primary_event=None, label=None, start_datetime=None):
new_event = copy.deepcopy(self)
new_event.pk = None
if label:
new_event.label = label
if start_datetime:
new_event.start_datetime = start_datetime
if agenda_target:
new_event.agenda = agenda_target
else:

View File

@ -352,6 +352,27 @@ class EventForm(NewEventForm):
return self.instance
class EventDuplicateForm(forms.ModelForm):
class Meta:
model = Event
fields = [
'label',
'start_datetime',
]
field_classes = {
'start_datetime': SplitDateTimeField,
}
def save(self, *args, **kwargs):
with transaction.atomic():
self.instance = self.instance.duplicate(
label=self.cleaned_data["label"], start_datetime=self.cleaned_data["start_datetime"]
)
if self.instance.recurrence_days:
self.instance.create_all_recurrences()
return self.instance
class EventsTypeForm(forms.ModelForm):
class Meta:
model = EventsType

View File

@ -0,0 +1,23 @@
{% extends "chrono/manager_agenda_view.html" %}
{% load i18n %}
{% block breadcrumb %}
{{ block.super }}
<a href="{{ agenda.get_settings_url }}">{% trans 'Settings' %}</a>
<a href="">{% trans "Duplicate Event" %}</a>
{% endblock %}
{% block appbar %}
<h2>{% trans "Duplicate Event" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Duplicate" %}</button>
<a class="cancel" href="{% url 'chrono-manager-event-edit' pk=agenda.id event_pk=object.pk %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -20,6 +20,9 @@
{% if not object.primary_event %}
<a rel="popup" href="{% url 'chrono-manager-event-delete' pk=object.agenda.id event_pk=object.id %}?next={{view.request.GET.next}}">{% trans 'Delete' %}</a>
{% endif %}
{% if not object.primary_event %}
<a rel="popup" href="{% url 'chrono-manager-event-duplicate' pk=object.agenda.id event_pk=object.id %}">{% trans 'Duplicate' %}</a>
{% endif %}
</span>
{% else %}
<h2>{% trans "New Event" %}</h2>

View File

@ -199,6 +199,11 @@ urlpatterns = [
views.event_edit,
name='chrono-manager-event-edit',
),
url(
r'^agendas/(?P<pk>\d+)/events/(?P<event_pk>\d+)/duplicate$',
views.event_duplicate,
name='chrono-manager-event-duplicate',
),
url(
r'^agendas/(?P<pk>\d+)/events/(?P<event_pk>\d+)/delete$',
views.event_delete,

View File

@ -111,6 +111,7 @@ from .forms import (
DeskExceptionsImportForm,
DeskForm,
EventCancelForm,
EventDuplicateForm,
EventForm,
EventsTimesheetForm,
ImportEventsForm,
@ -1782,6 +1783,24 @@ class AgendaAddEventView(ManagedAgendaMixin, CreateView):
agenda_add_event = AgendaAddEventView.as_view()
class AgendaEventDuplicateView(ManagedAgendaMixin, UpdateView):
model = Event
queryset = Event.objects.filter(primary_event__isnull=True)
pk_url_kwarg = 'event_pk'
form_class = EventDuplicateForm
template_name = 'chrono/manager_event_duplicate_form.html'
def get_success_url(self):
return reverse('chrono-manager-event-edit', kwargs={'pk': self.agenda.id, 'event_pk': self.object.id})
def form_valid(self, form):
messages.success(self.request, _('Event successfully duplicated.'))
return super().form_valid(form)
event_duplicate = AgendaEventDuplicateView.as_view()
class AgendaImportEventsSampleView(TemplateView):
template_name = 'chrono/manager_sample_events.txt'
content_type = 'text/csv'

View File

@ -2442,6 +2442,103 @@ def test_duplicate_agenda(app, admin_user):
assert 'hop' in resp.text
def test_duplicate_event(app, admin_user):
agenda = Agenda.objects.create(label='Foo Bar', slug='foo-bar', kind='meetings')
event = Event.objects.create(
agenda=agenda,
start_datetime=now(),
places=10,
waiting_list_places=50,
label='Foo',
duration=45,
pricing='200€',
url="http://example.com",
description='foo',
)
assert Event.objects.count() == 1
new_datetime = localtime(event.start_datetime).replace(
year=event.start_datetime.year + 1, hour=17, minute=0
)
app = login(app)
resp = app.get(f'/manage/agendas/{agenda.pk}/events/{event.pk}/edit')
resp = resp.click('Duplicate')
resp.form['label'] = "Bar"
resp.form['start_datetime_0'] = str(new_datetime.date())
resp.form['start_datetime_1'] = '17:00'
resp = resp.form.submit().follow()
assert Event.objects.count() == 2
duplicate = Event.objects.latest('pk')
assert duplicate.start_datetime == new_datetime
assert duplicate.agenda == event.agenda
updated_fields = {"label", "start_datetime"}
identical_fields = {f.name for f in Event._meta.fields} - updated_fields - {'id', 'slug'}
for field in identical_fields:
assert getattr(duplicate, field) == getattr(event, field)
assert 'Event successfully duplicated.' in resp.text
def test_duplicate_event_creates_recurrences(app, admin_user):
agenda = Agenda.objects.create(label='Foo Bar', slug='foo-bar', kind='meetings')
event_start = now().replace(hour=1, minute=0)
recurring_event = Event.objects.create(
agenda=agenda,
start_datetime=event_start,
recurrence_days=[0, 1, 2, 3, 4, 5, 6],
recurrence_end_date=event_start + datetime.timedelta(days=6),
places=10,
label='Foo',
)
assert Event.objects.count() == 1
app = login(app)
resp = app.get(f'/manage/agendas/{agenda.pk}/events/{recurring_event.pk}/edit')
resp = resp.click('Duplicate')
resp.form['label'] = "Bar"
resp.form['start_datetime_0'] = str(event_start.date())
resp.form['start_datetime_1'] = '17:00'
resp = resp.form.submit().follow()
duplicate = Event.objects.filter(primary_event__isnull=True).latest('pk')
assert duplicate != recurring_event
assert duplicate.recurrences.count() == 6
def test_cannot_duplicate_secondary_event(app, admin_user):
agenda = Agenda.objects.create(label='Foo Bar', slug='foo-bar', kind='meetings')
recurring_event = Event.objects.create(
agenda=agenda,
start_datetime=now(),
places=10,
label='Foo',
)
# this event was created automatically as part of a recurring event
# it can't be duplicated
event = Event.objects.create(
agenda=agenda,
start_datetime=now(),
places=10,
primary_event=recurring_event,
)
app = login(app)
resp = app.get(f'/manage/agendas/{agenda.pk}/events/{event.pk}/edit')
assert 'Duplicate' not in resp.text
app.get(f'/manage/agendas/{agenda.pk}/events/{event.pk}/duplicate', status=404)
def test_booking_cancellation_meetings_agenda(app, admin_user, manager_user, managers_group, api_user):
agenda = Agenda.objects.create(label='Passeports', kind='meetings', view_role=managers_group)
desk = Desk.objects.create(agenda=agenda, label='Desk A')