0001-agenda-option-to-have-min-delay-in-working-days-5504.patch
chrono/agendas/migrations/0089_agenda_event_display_template.py | ||
---|---|---|
1 |
# Generated by Django 2.2.19 on 2021-05-19 12:23 |
|
2 | ||
3 | 1 |
from django.db import migrations, models |
4 | 2 | |
3 |
import chrono.agendas.models |
|
4 | ||
5 | 5 | |
6 | 6 |
class Migration(migrations.Migration): |
7 | 7 | |
... | ... | |
15 | 15 |
name='event_display_template', |
16 | 16 |
field=models.CharField( |
17 | 17 |
blank=True, |
18 |
help_text='By default event labels will be displayed to users. This allows for a custom template to include additional informations. For example, "{{ event.label }} - {{ event.start_datetime }}" will show event datetime after label. Available variables: event.label (label), event.start_datetime (start date/time), event.places (places), event.remaining_places (remaining places), event.duration (duration), event.pricing (pricing).', |
|
18 |
help_text=( |
|
19 |
'By default event labels will be displayed to users. ' |
|
20 |
'This allows for a custom template to include additional informations. ' |
|
21 |
'For example, "{{ event.label }} - {{ event.start_datetime }}" will show event datetime after label. ' |
|
22 |
'Available variables: event.label (label), event.start_datetime (start date/time), event.places (places), ' |
|
23 |
'event.remaining_places (remaining places), event.duration (duration), event.pricing (pricing).' |
|
24 |
), |
|
19 | 25 |
max_length=256, |
26 |
validators=[chrono.agendas.models.event_template_validator], |
|
20 | 27 |
verbose_name='Event display template', |
21 | 28 |
), |
22 | 29 |
), |
chrono/agendas/migrations/0096_min_booking_delay_working_days.py | ||
---|---|---|
1 |
from django.db import migrations, models |
|
2 | ||
3 | ||
4 |
class Migration(migrations.Migration): |
|
5 | ||
6 |
dependencies = [ |
|
7 |
('agendas', '0095_checked'), |
|
8 |
] |
|
9 | ||
10 |
operations = [ |
|
11 |
migrations.AddField( |
|
12 |
model_name='agenda', |
|
13 |
name='minimal_booking_delay_in_working_days', |
|
14 |
field=models.BooleanField(default=False, verbose_name='Minimal booking delay in working days'), |
|
15 |
), |
|
16 |
] |
chrono/agendas/models.py | ||
---|---|---|
164 | 164 |
blank=True, |
165 | 165 |
validators=[MaxValueValidator(10000)], |
166 | 166 |
) |
167 |
minimal_booking_delay_in_working_days = models.BooleanField( |
|
168 |
_('Minimal booking delay in working days'), |
|
169 |
default=False, |
|
170 |
) |
|
167 | 171 |
maximal_booking_delay = models.PositiveIntegerField( |
168 | 172 |
_('Maximal booking delay (in days)'), |
169 | 173 |
default=None, |
... | ... | |
389 | 393 |
self.absence_reasons_group.slug if self.absence_reasons_group else None |
390 | 394 |
) |
391 | 395 |
agenda['exceptions_desk'] = self.desk_set.get().export_json() |
396 |
agenda['minimal_booking_delay_in_working_days'] = self.minimal_booking_delay_in_working_days |
|
392 | 397 |
elif self.kind == 'meetings': |
393 | 398 |
agenda['meetingtypes'] = [x.export_json() for x in self.meetingtype_set.filter(deleted=False)] |
394 | 399 |
agenda['desks'] = [desk.export_json() for desk in self.desk_set.all()] |
... | ... | |
610 | 615 |
# compute middle of today with localtime |
611 | 616 |
# 28 Mar 2021 12:00 +01:00 |
612 | 617 |
t = localtime(now()).replace(hour=12, minute=0) |
613 |
# advance of self.maximal_booking_delay - 1 days |
|
618 | ||
619 |
# advance of self.minimal_booking_delay - 1 days |
|
614 | 620 |
# 28 Mar 2021 12:00 +01:00 == 28 Mars 2021 13:00 +02:00 as DST happend on 28 Mar 2021. |
615 |
t += datetime.timedelta(days=self.minimal_booking_delay) |
|
621 |
if settings.WORKING_DAY_CALENDAR is not None and self.minimal_booking_delay_in_working_days: |
|
622 |
source_class = import_string(settings.WORKING_DAY_CALENDAR) |
|
623 |
calendar = source_class() |
|
624 |
t = calendar.add_working_days(t, self.minimal_booking_delay, keep_datetime=True) |
|
625 |
else: |
|
626 |
t += datetime.timedelta(days=self.minimal_booking_delay) |
|
627 | ||
616 | 628 |
# move to midnight of the day before, DST happen between 2h/3h so it |
617 | 629 |
# always exists because +/- timedelta does not move the timezone, only |
618 | 630 |
# localtime() does it. |
chrono/manager/forms.py | ||
---|---|---|
121 | 121 |
model = Agenda |
122 | 122 |
fields = [ |
123 | 123 |
'minimal_booking_delay', |
124 |
'minimal_booking_delay_in_working_days', |
|
124 | 125 |
'maximal_booking_delay', |
125 | 126 |
] |
126 | 127 | |
... | ... | |
129 | 130 |
if kwargs['instance'].kind != 'virtual': |
130 | 131 |
self.fields['minimal_booking_delay'].required = True |
131 | 132 |
self.fields['maximal_booking_delay'].required = True |
133 |
if kwargs['instance'].kind != 'events' or settings.WORKING_DAY_CALENDAR is None: |
|
134 |
del self.fields['minimal_booking_delay_in_working_days'] |
|
132 | 135 | |
133 | 136 | |
134 | 137 |
class AgendaRolesForm(AgendaAddForm): |
chrono/manager/templates/chrono/manager_agenda_settings.html | ||
---|---|---|
85 | 85 |
<div> |
86 | 86 |
<ul> |
87 | 87 |
<li>{% trans "Minimal booking delay:" %} |
88 |
{% if agenda.minimal_booking_delay is not None %}{{ agenda.minimal_booking_delay }} {% trans "days" %} |
|
88 |
{% if agenda.minimal_booking_delay is not None %} |
|
89 |
{% if agenda.minimal_booking_delay_in_working_days %} |
|
90 |
{% blocktrans count count=agenda.minimal_booking_delay %}{{ count }} working day{% plural %}{{ count }} working days{% endblocktrans %} |
|
91 |
{% else %} |
|
92 |
{% blocktrans count count=agenda.minimal_booking_delay %}{{ count }} day{% plural %}{{ count }} days{% endblocktrans %} |
|
93 |
{% endif %} |
|
89 | 94 |
{% else %}<i>{% trans "undefined" %}</i>{% endif %}</li> |
90 | 95 |
<li>{% trans "Maximal booking delay:" %} |
91 |
{% if agenda.maximal_booking_delay is not None %}{{ agenda.maximal_booking_delay }} {% trans "days" %} |
|
96 |
{% if agenda.maximal_booking_delay is not None %} |
|
97 |
{% blocktrans count count=agenda.maximal_booking_delay %}{{ count }} day{% plural %}{{ count }} days{% endblocktrans %} |
|
92 | 98 |
{% else %}<i>{% trans "undefined" %}</i>{% endif %}</li> |
93 | 99 |
</ul> |
94 | 100 |
</div> |
chrono/settings.py | ||
---|---|---|
176 | 176 |
try: |
177 | 177 |
import workalendar |
178 | 178 | |
179 |
WORKING_DAY_CALENDAR = 'workalendar.europe.France' |
|
179 | 180 |
EXCEPTIONS_SOURCES = { |
180 |
'holidays': {'class': 'workalendar.europe.France', 'label': _('Holidays')},
|
|
181 |
'holidays': {'class': WORKING_DAY_CALENDAR, 'label': _('Holidays')},
|
|
181 | 182 |
} |
182 | 183 |
except ImportError: |
184 |
WORKING_DAY_CALENDAR = None |
|
183 | 185 |
EXCEPTIONS_SOURCES = {} |
184 | 186 | |
185 | 187 |
TEMPLATE_VARS = {} |
tests/manager/test_all.py | ||
---|---|---|
499 | 499 |
assert 'open_events' not in [k for k, v in resp.context['form'].fields['default_view'].choices] |
500 | 500 | |
501 | 501 | |
502 |
def test_options_agenda_cant_unset_delays(app, admin_user): |
|
502 |
def test_options_events_agenda_delays(settings, app, admin_user): |
|
503 |
settings.WORKING_DAY_CALENDAR = None |
|
503 | 504 |
agenda = Agenda.objects.create(label=u'Foo bar') |
504 | 505 |
assert agenda.minimal_booking_delay == 1 |
505 | 506 |
app = login(app) |
506 | 507 |
url = '/manage/agendas/%s/booking-delays' % agenda.pk |
507 | 508 |
resp = app.get(url) |
509 |
assert 'minimal_booking_delay_in_working_days' not in resp.context['form'].fields |
|
508 | 510 |
resp.form['minimal_booking_delay'] = None |
509 | 511 |
resp = resp.form.submit() |
510 |
agenda = Agenda.objects.get(label=u'Foo bar')
|
|
512 |
agenda.refresh_from_db()
|
|
511 | 513 |
assert agenda.minimal_booking_delay == 1 |
512 | 514 | |
515 |
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' |
|
516 |
resp = app.get(url) |
|
517 |
resp.form['minimal_booking_delay_in_working_days'] = True |
|
518 |
resp = resp.form.submit() |
|
519 |
agenda.refresh_from_db() |
|
520 |
assert agenda.minimal_booking_delay_in_working_days is True |
|
521 | ||
522 | ||
523 |
def test_options_meetings_agenda_delays(app, admin_user): |
|
524 |
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings', maximal_booking_delay=2) |
|
525 |
assert agenda.maximal_booking_delay == 2 |
|
526 |
app = login(app) |
|
527 |
url = '/manage/agendas/%s/booking-delays' % agenda.pk |
|
528 |
resp = app.get(url) |
|
529 |
assert 'minimal_booking_delay_in_working_days' not in resp.context['form'].fields |
|
530 |
resp.form['maximal_booking_delay'] = None |
|
531 |
resp = resp.form.submit() |
|
532 |
agenda.refresh_from_db() |
|
533 |
assert agenda.maximal_booking_delay == 2 |
|
534 | ||
513 | 535 | |
514 |
def test_options_virtual_agenda_can_unset_delays(app, admin_user):
|
|
536 |
def test_options_virtual_agenda_delays(app, admin_user): |
|
515 | 537 |
agenda = Agenda.objects.create(label=u'Foo bar', kind='virtual', maximal_booking_delay=2) |
516 | 538 |
assert agenda.maximal_booking_delay == 2 |
517 | 539 |
app = login(app) |
518 | 540 |
url = '/manage/agendas/%s/booking-delays' % agenda.pk |
519 | 541 |
resp = app.get(url) |
542 |
assert 'minimal_booking_delay_in_working_days' not in resp.context['form'].fields |
|
520 | 543 |
resp.form['maximal_booking_delay'] = None |
521 | 544 |
resp = resp.form.submit() |
522 |
agenda = Agenda.objects.get(label=u'Foo bar')
|
|
545 |
agenda.refresh_from_db()
|
|
523 | 546 |
assert agenda.maximal_booking_delay is None |
524 | 547 | |
525 | 548 |
tests/test_agendas.py | ||
---|---|---|
196 | 196 |
assert group.slug == 'foo-baz-2' |
197 | 197 | |
198 | 198 | |
199 |
@pytest.mark.freeze_time('2021-07-09') |
|
200 |
def test_agenda_minimal_booking_delay_in_working_days(settings): |
|
201 |
agenda = Agenda.objects.create(label='Agenda', minimal_booking_delay=1) |
|
202 | ||
203 |
settings.WORKING_DAY_CALENDAR = None |
|
204 |
agenda.minimal_booking_delay_in_working_days = True |
|
205 |
agenda.save() |
|
206 |
assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 10) |
|
207 |
agenda.minimal_booking_delay_in_working_days = False |
|
208 |
agenda.save() |
|
209 |
assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 10) |
|
210 | ||
211 |
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' |
|
212 |
agenda.minimal_booking_delay_in_working_days = True |
|
213 |
agenda.save() |
|
214 |
assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 12) |
|
215 |
agenda.minimal_booking_delay_in_working_days = False |
|
216 |
agenda.save() |
|
217 |
assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 10) |
|
218 | ||
219 | ||
199 | 220 |
@pytest.mark.parametrize('with_prefetch', [True, False]) |
200 | 221 |
def test_agenda_is_available_for_simple_management(settings, with_prefetch): |
201 | 222 |
settings.EXCEPTIONS_SOURCES = { |
tests/test_import_export.py | ||
---|---|---|
150 | 150 |
kind='events', |
151 | 151 |
default_view='open_events', |
152 | 152 |
booking_form_url='{{ eservices_url }}backoffice/submission/inscription-aux-activites/', |
153 |
minimal_booking_delay_in_working_days=True, |
|
153 | 154 |
) |
154 | 155 |
Desk.objects.create(agenda=agenda, slug='_exceptions_holder') |
155 | 156 | |
... | ... | |
166 | 167 |
agenda = Agenda.objects.first() |
167 | 168 |
assert agenda.default_view == 'open_events' |
168 | 169 |
assert agenda.booking_form_url == '{{ eservices_url }}backoffice/submission/inscription-aux-activites/' |
170 |
assert agenda.minimal_booking_delay_in_working_days is True |
|
169 | 171 | |
170 | 172 | |
171 | 173 |
def test_import_export_event_details(app): |
172 |
- |