0002-manager-timesheet-for-one-event-66358.patch
chrono/manager/forms.py | ||
---|---|---|
584 | 584 | |
585 | 585 |
def __init__(self, *args, **kwargs): |
586 | 586 |
self.agenda = kwargs.pop('agenda') |
587 |
self.event = kwargs.pop('event', None) |
|
587 | 588 |
super().__init__(*args, **kwargs) |
589 |
if self.event is not None: |
|
590 |
del self.fields['date_start'] |
|
591 |
del self.fields['date_end'] |
|
588 | 592 | |
589 | 593 |
def get_slots(self): |
590 | 594 |
extra_data = self.cleaned_data['extra_data'].split(',') |
... | ... | |
593 | 597 |
all_extra_data = extra_data[:] |
594 | 598 |
if group_by: |
595 | 599 |
all_extra_data += [group_by] |
596 |
min_start = make_aware( |
|
597 |
datetime.datetime.combine(self.cleaned_data['date_start'], datetime.time(0, 0)) |
|
598 |
) |
|
599 |
max_start = make_aware(datetime.datetime.combine(self.cleaned_data['date_end'], datetime.time(0, 0))) |
|
600 |
max_start = max_start + datetime.timedelta(days=1) |
|
601 | ||
602 |
# fetch all events in this range |
|
603 |
all_events = ( |
|
604 |
self.agenda.event_set.filter( |
|
605 |
recurrence_days__isnull=True, |
|
606 |
start_datetime__gte=min_start, |
|
607 |
start_datetime__lt=max_start, |
|
608 |
cancelled=False, |
|
600 |
if self.event is not None: |
|
601 |
all_events = [self.event] |
|
602 |
min_start = self.event.start_datetime |
|
603 |
max_start = min_start + datetime.timedelta(days=1) |
|
604 |
else: |
|
605 |
min_start = make_aware( |
|
606 |
datetime.datetime.combine(self.cleaned_data['date_start'], datetime.time(0, 0)) |
|
607 |
) |
|
608 |
max_start = make_aware( |
|
609 |
datetime.datetime.combine(self.cleaned_data['date_end'], datetime.time(0, 0)) |
|
610 |
) |
|
611 |
max_start = max_start + datetime.timedelta(days=1) |
|
612 | ||
613 |
# fetch all events in this range |
|
614 |
all_events = ( |
|
615 |
self.agenda.event_set.filter( |
|
616 |
recurrence_days__isnull=True, |
|
617 |
start_datetime__gte=min_start, |
|
618 |
start_datetime__lt=max_start, |
|
619 |
cancelled=False, |
|
620 |
) |
|
621 |
.select_related('primary_event') |
|
622 |
.order_by('start_datetime', 'label') |
|
609 | 623 |
) |
610 |
.select_related('primary_event') |
|
611 |
.order_by('start_datetime', 'label') |
|
612 |
) |
|
613 | 624 |
dates = set() |
614 | 625 |
events = [] |
615 | 626 |
dates_per_event_id = defaultdict(list) |
chrono/manager/templates/chrono/manager_event_detail.html | ||
---|---|---|
7 | 7 |
{% endblock %} |
8 | 8 | |
9 | 9 |
{% block page-title-extra-label %} |
10 |
- {% firstof agenda.label object.label %}
|
|
10 |
- {% firstof agenda.label event.label %}
|
|
11 | 11 |
{% endblock %} |
12 | 12 | |
13 | 13 |
{% block breadcrumb %} |
14 | 14 |
{{ block.super }} |
15 | 15 |
<a href="{% url 'chrono-manager-agenda-view' pk=agenda.id %}">{{agenda.label}}</a> |
16 |
<a href="{% url 'chrono-manager-event-view' pk=agenda.id event_pk=object.id %}">{{object}}</a>
|
|
16 |
<a href="{% url 'chrono-manager-event-view' pk=agenda.id event_pk=event.id %}">{{event}}</a>
|
|
17 | 17 |
{% endblock %} |
18 | 18 | |
19 | 19 |
{% block appbar %} |
20 | 20 |
<h2> |
21 |
{% if object.label %}
|
|
22 |
{{ object.label }} — {{object.start_datetime|date:"DATETIME_FORMAT"}}
|
|
21 |
{% if event.label %}
|
|
22 |
{{ event.label }} — {{event.start_datetime|date:"DATETIME_FORMAT"}}
|
|
23 | 23 |
{% else %} |
24 |
{{ object.start_datetime|date:"DATETIME_FORMAT"}}
|
|
24 |
{{ event.start_datetime|date:"DATETIME_FORMAT"}}
|
|
25 | 25 |
{% endif %} |
26 |
{% if object.cancellation_status %}<span class="tag">{{ event.cancellation_status }}</span>{% endif %}
|
|
26 |
{% if event.cancellation_status %}<span class="tag">{{ event.cancellation_status }}</span>{% endif %}
|
|
27 | 27 |
{% if event.main_list_full %}<span class="tag">{% trans "Full" %}</span>{% endif %} |
28 | 28 |
{% if event.checked %}<span class="tag">{% trans "Checked" %}</span>{% endif %} |
29 | 29 |
</h2> |
30 | 30 |
{% block appbar_actions %} |
31 | 31 |
<span class="actions"> |
32 |
{% if user_can_manage or object.agenda.booking_form_url %}
|
|
32 |
{% if user_can_manage or event.agenda.booking_form_url %}
|
|
33 | 33 |
<a class="extra-actions-menu-opener"></a> |
34 | 34 |
<ul class="extra-actions-menu"> |
35 | 35 |
{% if user_can_manage %} |
36 |
{% if not object.primary_event %}
|
|
37 |
<li><a rel="popup" href="{% url 'chrono-manager-event-delete' pk=object.agenda.id event_pk=object.id %}">{% trans 'Delete' %}</a></li>
|
|
36 |
{% if not event.primary_event %}
|
|
37 |
<li><a rel="popup" href="{% url 'chrono-manager-event-delete' pk=event.agenda.id event_pk=event.id %}">{% trans 'Delete' %}</a></li>
|
|
38 | 38 |
{% endif %} |
39 | 39 |
{% if not event.cancellation_status %} |
40 | 40 |
<li><a rel="popup" href="{% url 'chrono-manager-event-cancel' pk=agenda.pk event_pk=event.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a></li> |
41 | 41 |
{% endif %} |
42 |
<li><a href="{% url 'chrono-manager-event-edit' pk=agenda.id event_pk=object.id %}">{% trans "Options" %}</a></li>
|
|
42 |
<li><a href="{% url 'chrono-manager-event-edit' pk=agenda.id event_pk=event.id %}">{% trans "Options" %}</a></li>
|
|
43 | 43 |
{% endif %} |
44 |
{% if object.agenda.booking_form_url %} |
|
45 |
<li><a href="{{ object.get_booking_form_url }}&ReturnURL={{ request.build_absolute_uri }}">{% trans "Booking form" %}</a></li> |
|
44 |
{% if event.agenda.booking_form_url %} |
|
45 |
<li><a href="{{ event.get_booking_form_url }}&ReturnURL={{ request.build_absolute_uri }}">{% trans "Booking form" %}</a></li> |
|
46 |
{% endif %} |
|
47 |
{% if not event.cancelled %} |
|
48 |
<li><a href="{% url 'chrono-manager-event-timesheet' pk=agenda.pk event_pk=event.pk %}">{% trans "Timesheet" %}</a></li> |
|
46 | 49 |
{% endif %} |
47 | 50 |
</ul> |
48 | 51 |
{% endif %} |
49 |
{% if object.is_day_past and not object.cancelled %}
|
|
50 |
<a href="{% url 'chrono-manager-event-check' pk=agenda.pk event_pk=object.pk %}">{% trans "Check" %}</a>
|
|
52 |
{% if event.is_day_past and not event.cancelled %}
|
|
53 |
<a href="{% url 'chrono-manager-event-check' pk=agenda.pk event_pk=event.pk %}">{% trans "Check" %}</a>
|
|
51 | 54 |
{% endif %} |
52 | 55 |
</span> |
53 | 56 |
{% endblock %} |
chrono/manager/templates/chrono/manager_event_timesheet.html | ||
---|---|---|
1 |
{% extends "chrono/manager_event_detail.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block breadcrumb %} |
|
5 |
{{ block.super }} |
|
6 |
<a href="{% url 'chrono-manager-event-timesheet' pk=agenda.pk event_pk=event.pk %}">{% trans "Timesheet" %}</a> |
|
7 |
{% endblock %} |
|
8 | ||
9 |
{% block appbar_actions %}{% endblock %} |
|
10 | ||
11 |
{% block content %} |
|
12 |
{% include 'chrono/manager_events_timesheet_form_fragment.html' %} |
|
13 |
{% endblock %} |
chrono/manager/templates/chrono/manager_events_timesheet.html | ||
---|---|---|
9 | 9 |
{% block appbar_actions %}{% endblock %} |
10 | 10 | |
11 | 11 |
{% block content %} |
12 |
<div class="section"> |
|
13 |
<h3>{% trans "Timesheet configuration" %}</h3> |
|
14 |
<div> |
|
15 |
<form id="timesheet"> |
|
16 |
{{ form.as_p }} |
|
17 |
<script> |
|
18 |
$(function() { |
|
19 |
$('#id_date_display').on('change', function() { |
|
20 |
if ($(this).val() == 'custom') { |
|
21 |
$('#id_custom_nb_dates_per_page').parent().show(); |
|
22 |
} else { |
|
23 |
$('#id_custom_nb_dates_per_page').parent().hide(); |
|
24 |
} |
|
25 |
}); |
|
26 |
$('#id_date_display').trigger('change'); |
|
27 |
$('#id_group_by').on('change', function() { |
|
28 |
if ($(this).val()) { |
|
29 |
$('#id_with_page_break').parent().show(); |
|
30 |
} else { |
|
31 |
$('#id_with_page_break').parent().hide(); |
|
32 |
} |
|
33 |
}); |
|
34 |
$('#id_group_by').trigger('change'); |
|
35 |
}); |
|
36 |
</script> |
|
37 |
<button class="submit-button">{% trans "See timesheet" %}</button> |
|
38 |
{% if request.GET and form.is_valid %} |
|
39 |
<button class="submit-button" name="pdf">{% trans "Get PDF file" %}</button> |
|
40 |
{% endif %} |
|
41 |
</form> |
|
42 | ||
43 |
{% if request.GET and form.is_valid %} |
|
44 |
<h4>{% blocktrans with start=form.cleaned_data.date_start end=form.cleaned_data.date_end %}Timesheet from {{ start }} to {{ end }}{% endblocktrans %}</h4> |
|
45 | ||
46 |
{% include 'chrono/manager_events_timesheet_fragment.html' %} |
|
47 |
{% endif %} |
|
48 |
</div> |
|
49 |
</div> |
|
12 |
{% include 'chrono/manager_events_timesheet_form_fragment.html' %} |
|
50 | 13 |
{% endblock %} |
chrono/manager/templates/chrono/manager_events_timesheet_form_fragment.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 | ||
3 |
<div class="section"> |
|
4 |
<h3>{% trans "Timesheet configuration" %}</h3> |
|
5 |
<div> |
|
6 |
<form id="timesheet"> |
|
7 |
{{ form.as_p }} |
|
8 |
<script> |
|
9 |
$(function() { |
|
10 |
$('#id_date_display').on('change', function() { |
|
11 |
if ($(this).val() == 'custom') { |
|
12 |
$('#id_custom_nb_dates_per_page').parent().show(); |
|
13 |
} else { |
|
14 |
$('#id_custom_nb_dates_per_page').parent().hide(); |
|
15 |
} |
|
16 |
}); |
|
17 |
$('#id_date_display').trigger('change'); |
|
18 |
$('#id_group_by').on('change', function() { |
|
19 |
if ($(this).val()) { |
|
20 |
$('#id_with_page_break').parent().show(); |
|
21 |
} else { |
|
22 |
$('#id_with_page_break').parent().hide(); |
|
23 |
} |
|
24 |
}); |
|
25 |
$('#id_group_by').trigger('change'); |
|
26 |
}); |
|
27 |
</script> |
|
28 |
<button class="submit-button">{% trans "See timesheet" %}</button> |
|
29 |
{% if request.GET and form.is_valid %} |
|
30 |
<button class="submit-button" name="pdf">{% trans "Get PDF file" %}</button> |
|
31 |
{% endif %} |
|
32 |
</form> |
|
33 | ||
34 |
{% if request.GET and form.is_valid %} |
|
35 |
{% if event %} |
|
36 |
<h4>{% blocktrans %}Timesheet{% endblocktrans %}</h4> |
|
37 |
{% else %} |
|
38 |
<h4>{% blocktrans with start=form.cleaned_data.date_start end=form.cleaned_data.date_end %}Timesheet from {{ start }} to {{ end }}{% endblocktrans %}</h4> |
|
39 |
{% endif %} |
|
40 | ||
41 |
{% include 'chrono/manager_events_timesheet_fragment.html' %} |
|
42 |
{% endif %} |
|
43 |
</div> |
|
44 |
</div> |
chrono/manager/urls.py | ||
---|---|---|
265 | 265 |
views.event_checked, |
266 | 266 |
name='chrono-manager-event-checked', |
267 | 267 |
), |
268 |
url( |
|
269 |
r'^agendas/(?P<pk>\d+)/events/(?P<event_pk>\d+)/timesheet$', |
|
270 |
views.events_timesheet, |
|
271 |
name='chrono-manager-event-timesheet', |
|
272 |
), |
|
268 | 273 |
url( |
269 | 274 |
r'^agendas/(?P<pk>\d+)/event_cancellation_report/(?P<report_pk>\d+)/$', |
270 | 275 |
views.event_cancellation_report, |
chrono/manager/views.py | ||
---|---|---|
2233 | 2233 | |
2234 | 2234 |
class EventsTimesheetView(ViewableAgendaMixin, DetailView): |
2235 | 2235 |
model = Agenda |
2236 |
template_name = 'chrono/manager_events_timesheet.html' |
|
2237 | 2236 | |
2238 | 2237 |
def set_agenda(self, **kwargs): |
2239 | 2238 |
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events') |
2239 |
self.event = None |
|
2240 |
if 'event_pk' in kwargs: |
|
2241 |
self.event = get_object_or_404( |
|
2242 |
Event, |
|
2243 |
pk=kwargs.get('event_pk'), |
|
2244 |
agenda=self.agenda, |
|
2245 |
recurrence_days__isnull=True, |
|
2246 |
cancelled=False, |
|
2247 |
) |
|
2240 | 2248 | |
2241 | 2249 |
def get_object(self, **kwargs): |
2242 | 2250 |
return self.agenda |
2243 | 2251 | |
2244 | 2252 |
def get_context_data(self, **kwargs): |
2245 | 2253 |
context = super().get_context_data(**kwargs) |
2246 |
form = EventsTimesheetForm(agenda=self.agenda, data=self.request.GET or None) |
|
2254 |
form = EventsTimesheetForm(agenda=self.agenda, event=self.event, data=self.request.GET or None)
|
|
2247 | 2255 |
if self.request.GET: |
2248 | 2256 |
form.is_valid() |
2249 | 2257 |
context['form'] = form |
2258 |
context['event'] = self.event |
|
2250 | 2259 |
return context |
2251 | 2260 | |
2261 |
def get_template_names(self): |
|
2262 |
if self.event is not None: |
|
2263 |
return ['chrono/manager_event_timesheet.html'] |
|
2264 |
return ['chrono/manager_events_timesheet.html'] |
|
2265 | ||
2252 | 2266 |
def get(self, request, *args, **kwargs): |
2253 | 2267 |
self.object = self.get_object() |
2254 | 2268 |
context = self.get_context_data(object=self.object) |
... | ... | |
2263 | 2277 |
) |
2264 | 2278 |
pdf = html.write_pdf() |
2265 | 2279 |
response = HttpResponse(pdf, content_type='application/pdf') |
2266 |
response['Content-Disposition'] = 'attachment; filename="timesheet_{}_{}_{}.pdf"'.format( |
|
2267 |
self.agenda.slug, |
|
2268 |
context['form'].cleaned_data['date_start'].strftime('%Y-%m-%d'), |
|
2269 |
context['form'].cleaned_data['date_end'].strftime('%Y-%m-%d'), |
|
2270 |
) |
|
2280 |
if self.event is not None: |
|
2281 |
response['Content-Disposition'] = 'attachment; filename="timesheet_{}_{}.pdf"'.format( |
|
2282 |
self.agenda.slug, |
|
2283 |
self.event.slug, |
|
2284 |
) |
|
2285 |
else: |
|
2286 |
response['Content-Disposition'] = 'attachment; filename="timesheet_{}_{}_{}.pdf"'.format( |
|
2287 |
self.agenda.slug, |
|
2288 |
context['form'].cleaned_data['date_start'].strftime('%Y-%m-%d'), |
|
2289 |
context['form'].cleaned_data['date_end'].strftime('%Y-%m-%d'), |
|
2290 |
) |
|
2271 | 2291 |
return response |
2272 | 2292 | |
2273 | 2293 |
tests/manager/test_event_timesheet.py | ||
---|---|---|
896 | 896 |
% agenda.pk |
897 | 897 |
) |
898 | 898 |
assert resp.context['form'].errors['orientation'] == ['This field is required.'] |
899 | ||
900 | ||
901 |
def test_event_timesheet_wrong_kind(app, admin_user): |
|
902 |
agenda = Agenda.objects.create(label='Foo bar', kind='meetings') |
|
903 |
event = Event.objects.create( |
|
904 |
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), places=10, agenda=agenda |
|
905 |
) |
|
906 | ||
907 |
app = login(app) |
|
908 |
app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk), status=404) |
|
909 |
agenda.kind = 'virtual' |
|
910 |
agenda.save() |
|
911 |
app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk), status=404) |
|
912 | ||
913 | ||
914 |
def test_event_timesheet_wrong_event(app, admin_user): |
|
915 |
agenda = Agenda.objects.create(label='Events', kind='events') |
|
916 |
agenda2 = Agenda.objects.create(label='Events', kind='events') |
|
917 |
event = Event.objects.create( |
|
918 |
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), |
|
919 |
places=10, |
|
920 |
agenda=agenda, |
|
921 |
cancelled=True, |
|
922 |
) |
|
923 | ||
924 |
app = login(app) |
|
925 |
app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk), status=404) |
|
926 |
event.cancelled = False |
|
927 |
event.recurrence_days = [1] |
|
928 |
event.save() |
|
929 |
app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk), status=404) |
|
930 | ||
931 |
event.recurrence_days = None |
|
932 |
event.save() |
|
933 |
app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk), status=200) |
|
934 | ||
935 |
app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda2.pk, event.pk), status=404) |
|
936 |
app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, 0), status=404) |
|
937 | ||
938 | ||
939 |
def test_event_timesheet_form(app, admin_user): |
|
940 |
agenda = Agenda.objects.create(label='Events', kind='events') |
|
941 |
event = Event.objects.create( |
|
942 |
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), places=10, agenda=agenda |
|
943 |
) |
|
944 | ||
945 |
app = login(app) |
|
946 |
resp = app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk)) |
|
947 |
assert 'date_start' not in resp.context['form'].fields |
|
948 |
assert 'date_end' not in resp.context['form'].fields |
|
949 |
assert resp.context['form'].errors == {} |
|
950 | ||
951 | ||
952 |
def test_event_timesheet_slots(app, admin_user): |
|
953 |
agenda = Agenda.objects.create(label='Events', kind='events') |
|
954 |
event = Event.objects.create( |
|
955 |
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), places=10, agenda=agenda |
|
956 |
) |
|
957 |
Subscription.objects.create( |
|
958 |
agenda=agenda, |
|
959 |
user_external_id='user:1', |
|
960 |
user_first_name='Subscription', |
|
961 |
user_last_name='42', |
|
962 |
date_start=datetime.date(2022, 2, 15), |
|
963 |
date_end=datetime.date(2022, 2, 16), |
|
964 |
) |
|
965 | ||
966 |
login(app) |
|
967 |
resp = app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk)) |
|
968 |
with CaptureQueriesContext(connection) as ctx: |
|
969 |
resp = resp.form.submit() |
|
970 |
assert len(ctx.captured_queries) == 7 |
|
971 | ||
972 |
slots = resp.context['form'].get_slots() |
|
973 |
assert slots['dates'] == [ |
|
974 |
[ |
|
975 |
datetime.date(2022, 2, 15), |
|
976 |
] |
|
977 |
] |
|
978 |
assert slots['events'] == [event] |
|
979 |
assert slots['users'][0]['users'] == [ |
|
980 |
{ |
|
981 |
'user_id': 'user:1', |
|
982 |
'user_first_name': 'Subscription', |
|
983 |
'user_last_name': '42', |
|
984 |
'extra_data': {}, |
|
985 |
'events': [ |
|
986 |
{ |
|
987 |
'event': event, |
|
988 |
'dates': {datetime.date(2022, 2, 15): False}, |
|
989 |
}, |
|
990 |
], |
|
991 |
}, |
|
992 |
] |
|
993 |
assert slots['extra_data'] == [] |
|
994 | ||
995 | ||
996 |
def test_event_timesheet_subscription_limits(app, admin_user): |
|
997 |
agenda = Agenda.objects.create(label='Events', kind='events') |
|
998 |
event = Event.objects.create( |
|
999 |
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), places=10, agenda=agenda |
|
1000 |
) |
|
1001 | ||
1002 |
dates = [ |
|
1003 |
('2022-01-31', '2022-02-01'), |
|
1004 |
('2022-02-01', '2022-02-02'), |
|
1005 |
('2022-02-01', '2022-02-15'), |
|
1006 |
('2022-02-01', '2022-02-16'), |
|
1007 |
('2022-02-15', '2022-02-28'), |
|
1008 |
('2022-02-15', '2022-03-01'), |
|
1009 |
('2022-02-16', '2022-03-01'), |
|
1010 |
('2022-02-01', '2022-03-01'), |
|
1011 |
('2022-02-28', '2022-03-01'), |
|
1012 |
('2022-03-01', '2022-03-02'), |
|
1013 |
] |
|
1014 | ||
1015 |
for start, end in dates: |
|
1016 |
Subscription.objects.create( |
|
1017 |
agenda=agenda, |
|
1018 |
user_external_id='user:%s-%s' % (start, end), |
|
1019 |
user_first_name='Subscription', |
|
1020 |
user_last_name='%s - %s' % (start, end), |
|
1021 |
date_start=datetime.datetime.strptime(start, '%Y-%m-%d'), |
|
1022 |
date_end=datetime.datetime.strptime(end, '%Y-%m-%d'), |
|
1023 |
) |
|
1024 | ||
1025 |
login(app) |
|
1026 |
resp = app.get('/manage/agendas/%s/events/%s/timesheet' % (agenda.pk, event.pk)) |
|
1027 |
resp = resp.form.submit() |
|
1028 | ||
1029 |
slots = resp.context['form'].get_slots() |
|
1030 |
assert slots['dates'] == [ |
|
1031 |
[ |
|
1032 |
datetime.date(2022, 2, 15), |
|
1033 |
] |
|
1034 |
] |
|
1035 | ||
1036 |
assert slots['events'] == [ |
|
1037 |
event, |
|
1038 |
] |
|
1039 |
users = slots['users'][0]['users'] |
|
1040 |
assert len(users) == 4 |
|
1041 |
assert users[0]['user_id'] == 'user:2022-02-01-2022-02-16' |
|
1042 |
assert users[1]['user_id'] == 'user:2022-02-01-2022-03-01' |
|
1043 |
assert users[2]['user_id'] == 'user:2022-02-15-2022-02-28' |
|
1044 |
assert users[3]['user_id'] == 'user:2022-02-15-2022-03-01' |
|
1045 | ||
1046 | ||
1047 |
def test_event_timesheet_pdf(app, admin_user): |
|
1048 |
agenda = Agenda.objects.create(label='Foo', kind='events') |
|
1049 |
event = Event.objects.create( |
|
1050 |
label='Bar', |
|
1051 |
start_datetime=make_aware(datetime.datetime(2022, 2, 15, 17, 0)), |
|
1052 |
places=10, |
|
1053 |
agenda=agenda, |
|
1054 |
) |
|
1055 | ||
1056 |
login(app) |
|
1057 |
resp = app.get( |
|
1058 |
'/manage/agendas/%s/events/%s/timesheet?pdf=&sort=lastname,firstname&date_display=all&orientation=portrait' |
|
1059 |
% (agenda.pk, event.pk) |
|
1060 |
) |
|
1061 |
assert resp.headers['Content-Type'] == 'application/pdf' |
|
1062 |
assert resp.headers['Content-Disposition'] == 'attachment; filename="timesheet_foo_bar.pdf"' |
|
1063 | ||
1064 |
# form invalid |
|
1065 |
resp = app.get( |
|
1066 |
'/manage/agendas/%s/events/%s/timesheet?pdf=&sort=lastname,firstname&date_display=all' |
|
1067 |
% (agenda.pk, event.pk) |
|
1068 |
) |
|
1069 |
assert resp.context['form'].errors['orientation'] == ['This field is required.'] |
|
899 |
- |