Event: avoir une page pour lister les events d'une liste d'agendas non pointés/non facturés sur une période (#75417) #61
|
@ -89,6 +89,7 @@ from chrono.apps.snapshot.models import (
|
|||
from chrono.utils.date import get_weekday_index
|
||||
from chrono.utils.db import ArraySubquery, SumCardinality
|
||||
from chrono.utils.interval import Interval, IntervalSet
|
||||
from chrono.utils.lingo import get_lingo_service
|
||||
from chrono.utils.misc import AgendaImportError, ICSError, clean_import_data, generate_slug
|
||||
from chrono.utils.publik_urls import translate_from_publik_url
|
||||
from chrono.utils.requests_wrapper import requests as requests_wrapper
|
||||
|
@ -371,9 +372,9 @@ class Agenda(WithSnapshotMixin, WithApplicationMixin, models.Model):
|
|||
return self.get_kind_display()
|
||||
|
||||
def get_lingo_url(self):
|
||||
if not settings.KNOWN_SERVICES.get('lingo'):
|
||||
lingo = get_lingo_service()
|
||||
if not lingo:
|
||||
return
|
||||
lingo = list(settings.KNOWN_SERVICES['lingo'].values())[0]
|
||||
lingo_url = lingo.get('url') or ''
|
||||
return '%smanage/pricing/agenda/%s/' % (lingo_url, self.slug)
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ from django.utils.formats import date_format
|
|||
from django.utils.html import format_html, mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import pgettext
|
||||
from gadjo.forms.widgets import MultiSelectWidget
|
||||
|
||||
from chrono.agendas.models import (
|
||||
WEEK_CHOICES,
|
||||
|
@ -978,6 +979,74 @@ class EventsTimesheetForm(forms.Form):
|
|||
return cleaned_data
|
||||
|
||||
|
||||
class EventsReportForm(forms.Form):
|
||||
date_start = forms.DateField(
|
||||
label=_('Start date'),
|
||||
widget=forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
|
||||
)
|
||||
date_end = forms.DateField(
|
||||
label=_('End date'),
|
||||
widget=forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
|
||||
)
|
||||
agendas = forms.ModelMultipleChoiceField(
|
||||
label=_('Agendas'),
|
||||
queryset=Agenda.objects.none(),
|
||||
widget=MultiSelectWidget,
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
label=_('Status'),
|
||||
choices=[
|
||||
('not-checked', _('Not checked')),
|
||||
('not-invoiced', _('Not invoiced')),
|
||||
],
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
agendas = [a for a in Agenda.objects.filter(kind='events') if a.can_be_viewed(user)]
|
||||
self.fields['agendas'].queryset = Agenda.objects.filter(pk__in=[a.pk for a in agendas])
|
||||
|
||||
def get_events(self):
|
||||
min_start = make_aware(
|
||||
datetime.datetime.combine(self.cleaned_data['date_start'], datetime.time(0, 0))
|
||||
)
|
||||
max_start = make_aware(datetime.datetime.combine(self.cleaned_data['date_end'], datetime.time(0, 0)))
|
||||
max_start = max_start + datetime.timedelta(days=1)
|
||||
event_queryset = (
|
||||
Event.objects.filter(
|
||||
recurrence_days__isnull=True,
|
||||
start_datetime__gte=min_start,
|
||||
start_datetime__lt=max_start,
|
||||
cancelled=False,
|
||||
agenda__in=self.cleaned_data['agendas'],
|
||||
)
|
||||
.select_related('agenda')
|
||||
.order_by('start_datetime', 'duration', 'label')
|
||||
)
|
||||
|
||||
result = {}
|
||||
|
||||
if 'not-checked' in self.cleaned_data['status']:
|
||||
result['not_checked'] = event_queryset.filter(checked=False)
|
||||
if 'not-invoiced' in self.cleaned_data['status']:
|
||||
result['not_invoiced'] = event_queryset.filter(invoiced=False)
|
||||
|
||||
return result
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if 'date_start' in cleaned_data and 'date_end' in cleaned_data:
|
||||
if cleaned_data['date_end'] < cleaned_data['date_start']:
|
||||
self.add_error('date_end', _('End date must be greater than start date.'))
|
||||
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.'))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class AgendaResourceForm(forms.Form):
|
||||
resource = forms.ModelChoiceField(label=_('Resource'), queryset=Resource.objects.none())
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
{% extends "chrono/manager_home.html" %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block css %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" media="all" href="{% static "css/gadjo.multiselectwidget.css" %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
{{ block.super }}
|
||||
<script src="{% static "js/gadjo.multiselectwidget.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'chrono-manager-events-report' %}">{% trans "Events report" %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Events report' %}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="section">
|
||||
<h3>{% trans "Report configuration" %}</h3>
|
||||
<div>
|
||||
<form>
|
||||
{{ form.as_p }}
|
||||
<button class="submit-button">{% trans "See report" %}</button>
|
||||
{% if request.GET and form.is_valid %}
|
||||
<button class="submit-button" name="csv">{% trans "Get CSV file" %}</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
{% if request.GET and form.is_valid %}
|
||||
{% with events=form.get_events %}
|
||||
|
||||
{% if 'not-checked' in form.cleaned_data.status %}
|
||||
<h4>{% blocktrans with start=form.cleaned_data.date_start end=form.cleaned_data.date_end %}Report from {{ start }} to {{ end }} - not checked events{% endblocktrans %}</h4>
|
||||
<ul class="objects-list single-links">
|
||||
{% for event in events.not_checked %}
|
||||
<li>
|
||||
<a href="{% url 'chrono-manager-event-view' pk=event.agenda_id event_pk=event.pk %}">
|
||||
{% if event.label %}{{ event.label }} / {% endif %} {{ event.start_datetime }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if 'not-invoiced' in form.cleaned_data.status %}
|
||||
<h4>{% blocktrans with start=form.cleaned_data.date_start end=form.cleaned_data.date_end %}Report from {{ start }} to {{ end }} - not invoiced events{% endblocktrans %}</h4>
|
||||
<ul class="objects-list single-links">
|
||||
{% for event in events.not_invoiced %}
|
||||
<li>
|
||||
<a href="{% url 'chrono-manager-event-view' pk=event.agenda_id event_pk=event.pk %}">
|
||||
{% if event.label %}{{ event.label }} / {% endif %} {{ event.start_datetime }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -71,6 +71,9 @@
|
|||
<a class="button button-paragraph" href="{% url 'chrono-manager-ants-hub' %}">{% trans 'ANTS Hub' %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if lingo_enabled %}
|
||||
<a class="button button-paragraph" href="{% url 'chrono-manager-events-report' %}">{% trans "Events report" %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% include 'chrono/includes/application_list_fragment.html' with title_no_application=_('Agendas outside applications') %}
|
||||
|
|
|
@ -444,6 +444,11 @@ urlpatterns = [
|
|||
views.subscription_extra_user_block,
|
||||
name='chrono-manager-subscription-extra-user-block',
|
||||
),
|
||||
path(
|
||||
'agendas/events/report/',
|
||||
views.events_report,
|
||||
name='chrono-manager-events-report',
|
||||
),
|
||||
path(
|
||||
'agendas/<int:pk>/events.csv',
|
||||
views.agenda_import_events_sample_csv,
|
||||
|
|
|
@ -93,6 +93,7 @@ from chrono.agendas.models import (
|
|||
)
|
||||
from chrono.apps.export_import.models import Application
|
||||
from chrono.utils.date import get_weekday_index
|
||||
from chrono.utils.lingo import get_lingo_service
|
||||
from chrono.utils.timezone import localtime, make_aware, make_naive, now
|
||||
|
||||
from .forms import (
|
||||
|
@ -121,6 +122,7 @@ from .forms import (
|
|||
EventCancelForm,
|
||||
EventDuplicateForm,
|
||||
EventForm,
|
||||
EventsReportForm,
|
||||
EventsTimesheetForm,
|
||||
ExcludedPeriodAddForm,
|
||||
ImportEventsForm,
|
||||
|
@ -214,6 +216,7 @@ class HomepageView(WithApplicationsMixin, ListView):
|
|||
context['shared_custody_enabled'] = settings.SHARED_CUSTODY_ENABLED
|
||||
context['ants_hub_enabled'] = bool(settings.CHRONO_ANTS_HUB_URL)
|
||||
context['with_sidebar'] = True
|
||||
context['lingo_enabled'] = bool(get_lingo_service())
|
||||
return self.with_applications_context_data(context)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
@ -4357,6 +4360,72 @@ class EventCancellationReportListView(ViewableAgendaMixin, ListView):
|
|||
event_cancellation_report_list = EventCancellationReportListView.as_view()
|
||||
|
||||
|
||||
class EventsReportView(View):
|
||||
template_name = 'chrono/manager_event_report.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
form = EventsReportForm(user=self.request.user, data=self.request.GET or None)
|
||||
if self.request.GET:
|
||||
form.is_valid()
|
||||
context = {'form': form}
|
||||
return context
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
context = self.get_context_data()
|
||||
if 'csv' in request.GET and context['form'].is_valid():
|
||||
return self.csv(request, context)
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
def csv(self, request, context):
|
||||
form = context['form']
|
||||
filename = 'events_report_{}_{}'.format(
|
||||
form.cleaned_data['date_start'].strftime('%Y-%m-%d'),
|
||||
form.cleaned_data['date_end'].strftime('%Y-%m-%d'),
|
||||
)
|
||||
response = HttpResponse(content_type='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename="%s.csv"' % filename
|
||||
writer = csv.writer(response)
|
||||
events = form.get_events()
|
||||
|
||||
headers = [
|
||||
_('agenda'),
|
||||
_('label'),
|
||||
_('date'),
|
||||
_('time'),
|
||||
]
|
||||
|
||||
if 'not-checked' in form.cleaned_data['status']:
|
||||
writer.writerow([_('Not checked')])
|
||||
writer.writerow(headers)
|
||||
for event in events['not_checked']:
|
||||
writer.writerow(
|
||||
[
|
||||
event.agenda.label,
|
||||
event.label,
|
||||
event.start_datetime.strftime('%Y-%m-%d'),
|
||||
event.start_datetime.strftime('%H:%M'),
|
||||
]
|
||||
)
|
||||
|
||||
if 'not-invoiced' in form.cleaned_data['status']:
|
||||
writer.writerow([_('Not invoiced')])
|
||||
writer.writerow(headers)
|
||||
for event in events['not_invoiced']:
|
||||
writer.writerow(
|
||||
[
|
||||
event.agenda.label,
|
||||
event.label,
|
||||
event.start_datetime.strftime('%Y-%m-%d'),
|
||||
event.start_datetime.strftime('%H:%M'),
|
||||
]
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
events_report = EventsReportView.as_view()
|
||||
|
||||
|
||||
class TimePeriodExceptionSourceToggleView(ManagedTimePeriodExceptionMixin, DetailView):
|
||||
model = TimePeriodExceptionSource
|
||||
|
||||
|
|
|
@ -76,6 +76,20 @@ def test_access(app, admin_user):
|
|||
assert "This site doesn't have any agenda yet." in resp.text
|
||||
|
||||
|
||||
def test_events_report_link(settings, app, admin_user):
|
||||
settings.KNOWN_SERVICES = {}
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/')
|
||||
assert 'Events report' not in resp
|
||||
assert '/manage/agendas/events/report/' not in resp
|
||||
|
||||
settings.KNOWN_SERVICES['lingo'] = {'default': {'url': 'https://lingo.dev/'}}
|
||||
resp = app.get('/manage/')
|
||||
assert 'Events report' in resp
|
||||
assert '/manage/agendas/events/report/' in resp
|
||||
|
||||
|
||||
def test_logout(app, admin_user):
|
||||
app = login(app)
|
||||
app.get('/logout/')
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import codecs
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
from unittest import mock
|
||||
|
@ -3417,3 +3418,254 @@ def test_event_detail_lease_display(app, admin_user):
|
|||
assert 'Bookings (1/10)' in resp.text
|
||||
assert 'Waiting List (1/2): 1 remaining place' in resp.text
|
||||
assert resp.text.count('Currently being booked...') == 2
|
||||
|
||||
|
||||
def test_events_report_form(app, admin_user):
|
||||
login(app)
|
||||
resp = app.get('/manage/agendas/events/report/')
|
||||
resp.form['date_start'] = '2022-01-01'
|
||||
resp.form['date_end'] = '2021-12-31'
|
||||
resp = resp.form.submit()
|
||||
assert resp.context['form'].errors['date_end'] == ['End date must be greater than start date.']
|
||||
|
||||
resp.form['date_end'] = '2022-04-02'
|
||||
resp = resp.form.submit()
|
||||
assert resp.context['form'].errors['date_end'] == ['Please select an interval of no more than 3 months.']
|
||||
|
||||
resp.form['date_end'] = '2022-04-01'
|
||||
resp = resp.form.submit()
|
||||
assert resp.context['form'].errors == {
|
||||
'agendas': ['This field is required.'],
|
||||
'status': ['This field is required.'],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-02-15')
|
||||
def test_events_report(app, admin_user):
|
||||
start, end = (
|
||||
now() - datetime.timedelta(days=15),
|
||||
now() + datetime.timedelta(days=14),
|
||||
) # 2022-02-31, 2022-03-01
|
||||
agenda1 = Agenda.objects.create(label='Events 1', kind='events')
|
||||
agenda2 = Agenda.objects.create(label='Events 2', kind='events')
|
||||
Event.objects.create(
|
||||
label='event 0',
|
||||
start_datetime=start,
|
||||
places=10,
|
||||
agenda=agenda1,
|
||||
checked=False,
|
||||
invoiced=False,
|
||||
)
|
||||
event1 = Event.objects.create(
|
||||
label='event 1',
|
||||
start_datetime=start + datetime.timedelta(days=1),
|
||||
places=10,
|
||||
agenda=agenda1,
|
||||
checked=False,
|
||||
invoiced=False,
|
||||
)
|
||||
event2 = Event.objects.create(
|
||||
label='event 2',
|
||||
start_datetime=start + datetime.timedelta(days=2),
|
||||
places=10,
|
||||
agenda=agenda1,
|
||||
checked=True,
|
||||
invoiced=False,
|
||||
)
|
||||
event3 = Event.objects.create(
|
||||
label='event 3',
|
||||
start_datetime=now(),
|
||||
places=10,
|
||||
agenda=agenda2,
|
||||
checked=False,
|
||||
invoiced=True,
|
||||
)
|
||||
Event.objects.create(
|
||||
label='event 4',
|
||||
start_datetime=end - datetime.timedelta(days=1),
|
||||
places=10,
|
||||
agenda=agenda2,
|
||||
checked=True,
|
||||
invoiced=True,
|
||||
)
|
||||
Event.objects.create(
|
||||
label='event 5',
|
||||
start_datetime=end,
|
||||
places=10,
|
||||
agenda=agenda2,
|
||||
checked=False,
|
||||
invoiced=False,
|
||||
)
|
||||
Event.objects.create(
|
||||
label='event cancelled',
|
||||
start_datetime=now() + datetime.timedelta(days=4),
|
||||
places=10,
|
||||
agenda=agenda1,
|
||||
cancelled=True,
|
||||
checked=False,
|
||||
invoiced=False,
|
||||
)
|
||||
recurring_event = Event.objects.create(
|
||||
label='recurring',
|
||||
start_datetime=start + datetime.timedelta(days=1),
|
||||
places=10,
|
||||
agenda=agenda2,
|
||||
recurrence_days=[1, 2],
|
||||
recurrence_end_date=now(),
|
||||
checked=False,
|
||||
invoiced=False,
|
||||
)
|
||||
recurring_event.create_all_recurrences()
|
||||
recurrences = recurring_event.recurrences.all()
|
||||
assert recurrences.count() == 4
|
||||
recurrent_event1 = recurrences[0]
|
||||
recurrent_event1.checked = False
|
||||
recurrent_event1.invoiced = False
|
||||
recurrent_event1.save()
|
||||
recurrent_event2 = recurrences[1]
|
||||
recurrent_event2.checked = True
|
||||
recurrent_event2.invoiced = False
|
||||
recurrent_event2.save()
|
||||
recurrent_event3 = recurrences[2]
|
||||
recurrent_event3.checked = False
|
||||
recurrent_event3.invoiced = True
|
||||
recurrent_event3.save()
|
||||
recurrent_event4 = recurrences[3]
|
||||
recurrent_event4.checked = True
|
||||
recurrent_event4.invoiced = True
|
||||
recurrent_event4.save()
|
||||
|
||||
def add_agenda(resp):
|
||||
select = copy.copy(resp.form.fields['agendas'][0])
|
||||
select.id = 'id_agendas_1'
|
||||
resp.form.fields['agendas'].append(select)
|
||||
resp.form.field_order.append(('agendas', select))
|
||||
|
||||
login(app)
|
||||
resp = app.get('/manage/agendas/events/report/')
|
||||
add_agenda(resp)
|
||||
resp.form['date_start'] = '2022-02-01'
|
||||
resp.form['date_end'] = '2022-02-28'
|
||||
resp.form.get('agendas', 0).value = agenda1.pk
|
||||
resp.form.get('agendas', 1).value = agenda2.pk
|
||||
resp.form['status'] = ['not-checked', 'not-invoiced']
|
||||
resp = resp.form.submit()
|
||||
events = resp.context['form'].get_events()
|
||||
assert list(events['not_checked']) == [event1, recurrent_event1, recurrent_event3, event3]
|
||||
assert list(events['not_invoiced']) == [event1, recurrent_event1, event2, recurrent_event2]
|
||||
|
||||
resp = app.get('/manage/agendas/events/report/')
|
||||
add_agenda(resp)
|
||||
resp.form['date_start'] = '2022-02-01'
|
||||
resp.form['date_end'] = '2022-02-28'
|
||||
resp.form.get('agendas', 0).value = agenda1.pk
|
||||
resp.form.get('agendas', 1).value = agenda2.pk
|
||||
resp.form['status'] = ['not-checked']
|
||||
resp = resp.form.submit()
|
||||
events = resp.context['form'].get_events()
|
||||
assert list(events['not_checked']) == [event1, recurrent_event1, recurrent_event3, event3]
|
||||
assert 'not_invoiced' not in events
|
||||
|
||||
resp = app.get('/manage/agendas/events/report/')
|
||||
add_agenda(resp)
|
||||
resp.form['date_start'] = '2022-02-01'
|
||||
resp.form['date_end'] = '2022-02-28'
|
||||
resp.form.get('agendas', 0).value = agenda1.pk
|
||||
resp.form.get('agendas', 1).value = agenda2.pk
|
||||
resp.form['status'] = ['not-invoiced']
|
||||
resp = resp.form.submit()
|
||||
events = resp.context['form'].get_events()
|
||||
assert 'not_checked' not in events
|
||||
assert list(events['not_invoiced']) == [event1, recurrent_event1, event2, recurrent_event2]
|
||||
|
||||
resp = app.get('/manage/agendas/events/report/')
|
||||
resp.form['date_start'] = '2022-02-01'
|
||||
resp.form['date_end'] = '2022-02-28'
|
||||
resp.form.get('agendas', 0).value = agenda2.pk
|
||||
resp.form['status'] = ['not-invoiced']
|
||||
resp = resp.form.submit()
|
||||
events = resp.context['form'].get_events()
|
||||
assert 'not_checked' not in events
|
||||
assert list(events['not_invoiced']) == [recurrent_event1, recurrent_event2]
|
||||
|
||||
resp = app.get('/manage/agendas/events/report/')
|
||||
resp.form['date_start'] = '2022-02-01'
|
||||
resp.form['date_end'] = '2022-02-28'
|
||||
resp.form.get('agendas', 0).value = agenda2.pk
|
||||
resp.form['status'] = ['not-checked', 'not-invoiced']
|
||||
resp = resp.form.submit()
|
||||
events = resp.context['form'].get_events()
|
||||
assert list(events['not_checked']) == [recurrent_event1, recurrent_event3, event3]
|
||||
assert list(events['not_invoiced']) == [recurrent_event1, recurrent_event2]
|
||||
|
||||
|
||||
def test_events_report_csv(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
Event.objects.create(
|
||||
label='event',
|
||||
start_datetime=make_aware(datetime.datetime(2022, 2, 1, 12, 0)),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
checked=False,
|
||||
invoiced=False,
|
||||
)
|
||||
recurring_event = Event.objects.create(
|
||||
label='recurring',
|
||||
start_datetime=make_aware(datetime.datetime(2022, 2, 1, 12, 0)),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
recurrence_days=[2],
|
||||
recurrence_end_date=datetime.date(2022, 2, 2),
|
||||
checked=False,
|
||||
invoiced=False,
|
||||
)
|
||||
recurring_event.create_all_recurrences()
|
||||
|
||||
login(app)
|
||||
resp = app.get(
|
||||
'/manage/agendas/events/report/?csv=&date_start=2022-02-01&date_end=2022-02-28&agendas=%s&status=not-checked&status=not-invoiced'
|
||||
% agenda.pk
|
||||
)
|
||||
assert resp.headers['Content-Type'] == 'text/csv'
|
||||
assert (
|
||||
resp.headers['Content-Disposition']
|
||||
== 'attachment; filename="events_report_2022-02-01_2022-02-28.csv"'
|
||||
)
|
||||
assert resp.text == (
|
||||
'Not checked\r\n'
|
||||
'agenda,label,date,time\r\n'
|
||||
'Events,event,2022-02-01,11:00\r\n'
|
||||
'Events,recurring,2022-02-01,11:00\r\n'
|
||||
'Not invoiced\r\n'
|
||||
'agenda,label,date,time\r\n'
|
||||
'Events,event,2022-02-01,11:00\r\n'
|
||||
'Events,recurring,2022-02-01,11:00\r\n'
|
||||
)
|
||||
|
||||
resp = app.get(
|
||||
'/manage/agendas/events/report/?csv=&date_start=2022-02-01&date_end=2022-02-28&agendas=%s&status=not-checked'
|
||||
% agenda.pk
|
||||
)
|
||||
assert resp.text == (
|
||||
'Not checked\r\n'
|
||||
'agenda,label,date,time\r\n'
|
||||
'Events,event,2022-02-01,11:00\r\n'
|
||||
'Events,recurring,2022-02-01,11:00\r\n'
|
||||
)
|
||||
|
||||
resp = app.get(
|
||||
'/manage/agendas/events/report/?csv=&date_start=2022-02-01&date_end=2022-02-28&agendas=%s&status=not-invoiced'
|
||||
% agenda.pk
|
||||
)
|
||||
assert resp.text == (
|
||||
'Not invoiced\r\n'
|
||||
'agenda,label,date,time\r\n'
|
||||
'Events,event,2022-02-01,11:00\r\n'
|
||||
'Events,recurring,2022-02-01,11:00\r\n'
|
||||
)
|
||||
|
||||
# form invalid
|
||||
resp = app.get(
|
||||
'/manage/agendas/events/report/?csv=&date_start=2022-02-01&date_end=2022-02-28&agendas=%s' % agenda.pk
|
||||
)
|
||||
assert resp.context['form'].errors['status'] == ['This field is required.']
|
||||
|
|
Loading…
Reference in New Issue