manager: timesheet and date display (#61070)
This commit is contained in:
parent
8ebbbfdfb7
commit
4b708073d4
|
@ -376,6 +376,20 @@ class EventsTimesheetForm(forms.Form):
|
|||
required=False,
|
||||
help_text=_('Comma separated list of keys defined in extra_data.'),
|
||||
)
|
||||
date_display = forms.ChoiceField(
|
||||
label=_('Display'),
|
||||
choices=[
|
||||
('all', _('All on the same page')),
|
||||
('month', _('1 month per page')),
|
||||
('week', _('1 week per page')),
|
||||
('custom', _('Custom')),
|
||||
],
|
||||
initial='all',
|
||||
)
|
||||
custom_nb_dates_per_page = forms.IntegerField(
|
||||
label=_('Number of dates per page'),
|
||||
required=False,
|
||||
)
|
||||
orientation = forms.ChoiceField(
|
||||
label=_('PDF orientation'),
|
||||
choices=[
|
||||
|
@ -421,6 +435,22 @@ class EventsTimesheetForm(forms.Form):
|
|||
dates_per_event_id[real_event.pk].append(date)
|
||||
dates = sorted(dates)
|
||||
|
||||
date_display = self.cleaned_data['date_display']
|
||||
if date_display in ['month', 'week']:
|
||||
grouper = defaultdict(list)
|
||||
for date in dates:
|
||||
if date_display == 'month':
|
||||
attr = date.month
|
||||
else:
|
||||
attr = date.isocalendar().week
|
||||
grouper[(date.year, attr)].append(date)
|
||||
dates = [grouper[g] for g in sorted(grouper.keys())]
|
||||
elif date_display == 'custom':
|
||||
n = self.cleaned_data['custom_nb_dates_per_page']
|
||||
dates = [dates[i : i + n] for i in range(0, len(dates), n)]
|
||||
else:
|
||||
dates = [dates]
|
||||
|
||||
event_slots = []
|
||||
for event in events:
|
||||
event_slots.append(
|
||||
|
@ -493,6 +523,10 @@ class EventsTimesheetForm(forms.Form):
|
|||
elif (cleaned_data['date_start'] + relativedelta(months=3)) < cleaned_data['date_end']:
|
||||
self.add_error('date_end', _('Please select an interval of no more than 3 months.'))
|
||||
|
||||
if cleaned_data.get('date_display') == 'custom':
|
||||
if not cleaned_data.get('custom_nb_dates_per_page'):
|
||||
self.add_error('custom_nb_dates_per_page', _('This field is required.'))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
|
|
|
@ -503,3 +503,7 @@ table.timesheet {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page_break {
|
||||
height: 20px;
|
||||
}
|
||||
|
|
|
@ -36,3 +36,8 @@ table.timesheet {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page_break {
|
||||
display: block;
|
||||
page-break-before: always;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,18 @@
|
|||
<div>
|
||||
<form id="timesheet">
|
||||
{{ form.as_p }}
|
||||
<script>
|
||||
$(function() {
|
||||
$('#id_date_display').on('change', function() {
|
||||
if ($(this).val() == 'custom') {
|
||||
$('#id_custom_nb_dates_per_page').parent().show();
|
||||
} else {
|
||||
$('#id_custom_nb_dates_per_page').parent().hide();
|
||||
}
|
||||
});
|
||||
$('#id_date_display').trigger('change');
|
||||
});
|
||||
</script>
|
||||
<button class="submit-button">{% trans "See timesheet" %}</button>
|
||||
{% if request.GET and form.is_valid %}
|
||||
<button class="submit-button" name="pdf">{% trans "Get PDF file" %}</button>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
{% with slots=form.get_slots %}
|
||||
{% with events_num=slots.events|length %}
|
||||
{% for dates in slots.dates %}
|
||||
<table class="main timesheet">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -9,7 +10,7 @@
|
|||
<th>{% trans "Last name" %}</th>
|
||||
{% for k in slots.extra_data %}<th>{{ k }}</th>{% endfor %}
|
||||
{% if events_num > 1 %}<th>{% trans "Activity" %}</th>{% endif %}
|
||||
{% for date in slots.dates %}<th class="date">{{ date|date:"D d/m" }}</th>{% endfor %}
|
||||
{% for date in dates %}<th class="date">{{ date|date:"D d/m" }}</th>{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -21,12 +22,14 @@
|
|||
{% for k in slots.extra_data %}<td {% if events_num > 1 %}rowspan="{{ events_num }}"{% endif %}>{{ user.extra_data|get:k }}</td>{% endfor %}
|
||||
{% endif %}
|
||||
{% if events_num > 1 %}<td>{{ event.event }}</td>{% endif %}
|
||||
{% for date in slots.dates %}
|
||||
{% for date in dates %}
|
||||
{% with booked=event.dates|get:date %}<td class="date">{% if booked is True %}☐{% elif booked is None %}-{% endif %}</td>{% endwith %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if not forloop.last %}<div class="page_break"></div>{% endif %}
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
|
|
|
@ -2098,18 +2098,20 @@ def test_events_timesheet_slots(app, admin_user):
|
|||
|
||||
slots = resp.context['form'].get_slots()
|
||||
assert slots['dates'] == [
|
||||
datetime.date(2022, 2, 1),
|
||||
datetime.date(2022, 2, 2),
|
||||
datetime.date(2022, 2, 7),
|
||||
datetime.date(2022, 2, 8),
|
||||
datetime.date(2022, 2, 9),
|
||||
datetime.date(2022, 2, 14),
|
||||
datetime.date(2022, 2, 15),
|
||||
datetime.date(2022, 2, 16),
|
||||
datetime.date(2022, 2, 21),
|
||||
datetime.date(2022, 2, 22),
|
||||
datetime.date(2022, 2, 23),
|
||||
datetime.date(2022, 2, 28),
|
||||
[
|
||||
datetime.date(2022, 2, 1),
|
||||
datetime.date(2022, 2, 2),
|
||||
datetime.date(2022, 2, 7),
|
||||
datetime.date(2022, 2, 8),
|
||||
datetime.date(2022, 2, 9),
|
||||
datetime.date(2022, 2, 14),
|
||||
datetime.date(2022, 2, 15),
|
||||
datetime.date(2022, 2, 16),
|
||||
datetime.date(2022, 2, 21),
|
||||
datetime.date(2022, 2, 22),
|
||||
datetime.date(2022, 2, 23),
|
||||
datetime.date(2022, 2, 28),
|
||||
]
|
||||
]
|
||||
assert slots['events'] == [
|
||||
event2,
|
||||
|
@ -2127,23 +2129,27 @@ def test_events_timesheet_slots(app, admin_user):
|
|||
'events': [
|
||||
{
|
||||
'event': event2,
|
||||
'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 1)},
|
||||
'dates': {date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 1)},
|
||||
},
|
||||
{
|
||||
'event': recurring_event1,
|
||||
'dates': {date: False for date in slots['dates'] if date.weekday() in [0, 1]},
|
||||
'dates': {date: False for date in slots['dates'][0] if date.weekday() in [0, 1]},
|
||||
},
|
||||
{
|
||||
'event': recurring_event2,
|
||||
'dates': {date: False for date in slots['dates'] if date.weekday() in [1, 2]},
|
||||
'dates': {date: False for date in slots['dates'][0] if date.weekday() in [1, 2]},
|
||||
},
|
||||
{
|
||||
'event': event3,
|
||||
'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 15)},
|
||||
'dates': {
|
||||
date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 15)
|
||||
},
|
||||
},
|
||||
{
|
||||
'event': event4,
|
||||
'dates': {date: False for date in slots['dates'] if date == datetime.date(2022, 2, 28)},
|
||||
'dates': {
|
||||
date: False for date in slots['dates'][0] if date == datetime.date(2022, 2, 28)
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -2195,9 +2201,11 @@ def test_events_timesheet_subscription_limits(app, admin_user):
|
|||
|
||||
slots = resp.context['form'].get_slots()
|
||||
assert slots['dates'] == [
|
||||
datetime.date(2022, 2, 1),
|
||||
datetime.date(2022, 2, 15),
|
||||
datetime.date(2022, 2, 28),
|
||||
[
|
||||
datetime.date(2022, 2, 1),
|
||||
datetime.date(2022, 2, 15),
|
||||
datetime.date(2022, 2, 28),
|
||||
]
|
||||
]
|
||||
|
||||
assert slots['events'] == [
|
||||
|
@ -2567,12 +2575,163 @@ def test_events_timesheet_extra_data(app, admin_user):
|
|||
assert slots['users'][0]['extra_data']['baz'] == ''
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-04-01')
|
||||
def test_events_timesheet_date_display(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
recurring_event = Event.objects.create(
|
||||
label='recurring 1',
|
||||
start_datetime=make_aware(datetime.datetime(2022, 1, 1, 12, 0)),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
recurrence_days=[0],
|
||||
recurrence_end_date=datetime.date(2022, 4, 1),
|
||||
)
|
||||
recurring_event.create_all_recurrences()
|
||||
Subscription.objects.create(
|
||||
agenda=agenda,
|
||||
user_external_id='user:1',
|
||||
user_first_name='Subscription',
|
||||
user_last_name='42',
|
||||
date_start=datetime.date(2022, 1, 1),
|
||||
date_end=datetime.date(2022, 4, 1),
|
||||
)
|
||||
|
||||
login(app)
|
||||
resp = app.get('/manage/agendas/%s/events/timesheet' % agenda.pk)
|
||||
resp.form['date_start'] = '2022-01-01'
|
||||
resp.form['date_end'] = '2022-03-31'
|
||||
resp = resp.form.submit()
|
||||
slots = resp.context['form'].get_slots()
|
||||
|
||||
assert slots['dates'] == [
|
||||
[
|
||||
datetime.date(2022, 1, 3),
|
||||
datetime.date(2022, 1, 10),
|
||||
datetime.date(2022, 1, 17),
|
||||
datetime.date(2022, 1, 24),
|
||||
datetime.date(2022, 1, 31),
|
||||
datetime.date(2022, 2, 7),
|
||||
datetime.date(2022, 2, 14),
|
||||
datetime.date(2022, 2, 21),
|
||||
datetime.date(2022, 2, 28),
|
||||
datetime.date(2022, 3, 7),
|
||||
datetime.date(2022, 3, 14),
|
||||
datetime.date(2022, 3, 21),
|
||||
datetime.date(2022, 3, 28),
|
||||
]
|
||||
]
|
||||
|
||||
resp.form['date_display'] = 'month'
|
||||
resp = resp.form.submit()
|
||||
slots = resp.context['form'].get_slots()
|
||||
|
||||
assert slots['dates'] == [
|
||||
[
|
||||
datetime.date(2022, 1, 3),
|
||||
datetime.date(2022, 1, 10),
|
||||
datetime.date(2022, 1, 17),
|
||||
datetime.date(2022, 1, 24),
|
||||
datetime.date(2022, 1, 31),
|
||||
],
|
||||
[
|
||||
datetime.date(2022, 2, 7),
|
||||
datetime.date(2022, 2, 14),
|
||||
datetime.date(2022, 2, 21),
|
||||
datetime.date(2022, 2, 28),
|
||||
],
|
||||
[
|
||||
datetime.date(2022, 3, 7),
|
||||
datetime.date(2022, 3, 14),
|
||||
datetime.date(2022, 3, 21),
|
||||
datetime.date(2022, 3, 28),
|
||||
],
|
||||
]
|
||||
|
||||
resp.form['date_display'] = 'week'
|
||||
resp = resp.form.submit()
|
||||
slots = resp.context['form'].get_slots()
|
||||
|
||||
assert slots['dates'] == [
|
||||
[datetime.date(2022, 1, 3)],
|
||||
[datetime.date(2022, 1, 10)],
|
||||
[datetime.date(2022, 1, 17)],
|
||||
[datetime.date(2022, 1, 24)],
|
||||
[datetime.date(2022, 1, 31)],
|
||||
[datetime.date(2022, 2, 7)],
|
||||
[datetime.date(2022, 2, 14)],
|
||||
[datetime.date(2022, 2, 21)],
|
||||
[datetime.date(2022, 2, 28)],
|
||||
[datetime.date(2022, 3, 7)],
|
||||
[datetime.date(2022, 3, 14)],
|
||||
[datetime.date(2022, 3, 21)],
|
||||
[datetime.date(2022, 3, 28)],
|
||||
]
|
||||
|
||||
resp.form['date_display'] = 'custom'
|
||||
resp = resp.form.submit()
|
||||
assert resp.context['form'].errors['custom_nb_dates_per_page'] == ['This field is required.']
|
||||
|
||||
resp.form['custom_nb_dates_per_page'] = 10
|
||||
resp = resp.form.submit()
|
||||
slots = resp.context['form'].get_slots()
|
||||
|
||||
assert slots['dates'] == [
|
||||
[
|
||||
datetime.date(2022, 1, 3),
|
||||
datetime.date(2022, 1, 10),
|
||||
datetime.date(2022, 1, 17),
|
||||
datetime.date(2022, 1, 24),
|
||||
datetime.date(2022, 1, 31),
|
||||
datetime.date(2022, 2, 7),
|
||||
datetime.date(2022, 2, 14),
|
||||
datetime.date(2022, 2, 21),
|
||||
datetime.date(2022, 2, 28),
|
||||
datetime.date(2022, 3, 7),
|
||||
],
|
||||
[
|
||||
datetime.date(2022, 3, 14),
|
||||
datetime.date(2022, 3, 21),
|
||||
datetime.date(2022, 3, 28),
|
||||
],
|
||||
]
|
||||
|
||||
resp.form['custom_nb_dates_per_page'] = 3
|
||||
resp = resp.form.submit()
|
||||
slots = resp.context['form'].get_slots()
|
||||
|
||||
assert slots['dates'] == [
|
||||
[
|
||||
datetime.date(2022, 1, 3),
|
||||
datetime.date(2022, 1, 10),
|
||||
datetime.date(2022, 1, 17),
|
||||
],
|
||||
[
|
||||
datetime.date(2022, 1, 24),
|
||||
datetime.date(2022, 1, 31),
|
||||
datetime.date(2022, 2, 7),
|
||||
],
|
||||
[
|
||||
datetime.date(2022, 2, 14),
|
||||
datetime.date(2022, 2, 21),
|
||||
datetime.date(2022, 2, 28),
|
||||
],
|
||||
[
|
||||
datetime.date(2022, 3, 7),
|
||||
datetime.date(2022, 3, 14),
|
||||
datetime.date(2022, 3, 21),
|
||||
],
|
||||
[
|
||||
datetime.date(2022, 3, 28),
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
def test_events_timesheet_pdf(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
|
||||
login(app)
|
||||
resp = app.get(
|
||||
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&orientation=portrait'
|
||||
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&date_display=all&orientation=portrait'
|
||||
% agenda.pk
|
||||
)
|
||||
assert resp.headers['Content-Type'] == 'application/pdf'
|
||||
|
@ -2583,7 +2742,7 @@ def test_events_timesheet_pdf(app, admin_user):
|
|||
|
||||
# form invalid
|
||||
resp = app.get(
|
||||
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28'
|
||||
'/manage/agendas/%s/events/timesheet?pdf=&date_start=2022-02-01&date_end=2022-02-28&date_display=all'
|
||||
% agenda.pk
|
||||
)
|
||||
assert resp.context['form'].errors['orientation'] == ['This field is required.']
|
||||
|
|
Loading…
Reference in New Issue