From ddc52a41160ec009d804a3369b1e0ddf93deeb00 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 8 Mar 2023 18:21:13 +0100 Subject: [PATCH 1/3] agendas: add Agenda.minimal_booking_time field (#56284) --- .../0148_agenda_minimal_booking_time.py | 22 ++++++++++++ chrono/agendas/models.py | 9 +++++ chrono/api/serializers.py | 1 + chrono/api/views.py | 1 + chrono/manager/forms.py | 1 + .../chrono/manager_agenda_settings.html | 4 +++ tests/api/test_agenda.py | 36 ++++++++++++------- 7 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 chrono/agendas/migrations/0148_agenda_minimal_booking_time.py diff --git a/chrono/agendas/migrations/0148_agenda_minimal_booking_time.py b/chrono/agendas/migrations/0148_agenda_minimal_booking_time.py new file mode 100644 index 00000000..22377e0c --- /dev/null +++ b/chrono/agendas/migrations/0148_agenda_minimal_booking_time.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.18 on 2023-03-08 17:20 + +import datetime + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agendas', '0147_check_future_events'), + ] + + operations = [ + migrations.AddField( + model_name='agenda', + name='minimal_booking_time', + field=models.TimeField( + blank=True, default=datetime.time(0, 0), null=True, verbose_name='Minimal booking time' + ), + ), + ] diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py index d0953348..097183ed 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -262,6 +262,15 @@ class Agenda(models.Model): null=True, blank=True, ) + minimal_booking_time = models.TimeField( + verbose_name=_('Minimal booking time'), + default=datetime.time(0, 0, 0), # booking is possible starting and finishin at 00:00 + help_text=_( + 'Ex.: 08:00:00. If left empty, available events will be those that are later than the current time.' + ), + null=True, + blank=True, + ) class Meta: ordering = ['label'] diff --git a/chrono/api/serializers.py b/chrono/api/serializers.py index e1ae00d9..2daf5c78 100644 --- a/chrono/api/serializers.py +++ b/chrono/api/serializers.py @@ -521,6 +521,7 @@ class AgendaSerializer(serializers.ModelSerializer): 'minimal_booking_delay', 'minimal_booking_delay_in_working_days', 'maximal_booking_delay', + 'minimal_booking_time', 'anonymize_delay', 'edit_role', 'view_role', diff --git a/chrono/api/views.py b/chrono/api/views.py index 2c253fb6..d5e0e230 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -361,6 +361,7 @@ def get_agenda_detail(request, agenda, check_events=False): 'kind': agenda.kind, 'minimal_booking_delay': agenda.minimal_booking_delay, 'maximal_booking_delay': agenda.maximal_booking_delay, + 'minimal_booking_time': agenda.minimal_booking_time, 'edit_role': agenda.edit_role.name if agenda.edit_role else None, 'view_role': agenda.view_role.name if agenda.view_role else None, 'category': agenda.category.slug if agenda.category else None, diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py index 4110ad55..feae4064 100644 --- a/chrono/manager/forms.py +++ b/chrono/manager/forms.py @@ -129,6 +129,7 @@ class AgendaBookingDelaysForm(forms.ModelForm): 'minimal_booking_delay', 'minimal_booking_delay_in_working_days', 'maximal_booking_delay', + 'minimal_booking_time', ] def __init__(self, *args, **kwargs): diff --git a/chrono/manager/templates/chrono/manager_agenda_settings.html b/chrono/manager/templates/chrono/manager_agenda_settings.html index e31f8e3e..8d5c7cf3 100644 --- a/chrono/manager/templates/chrono/manager_agenda_settings.html +++ b/chrono/manager/templates/chrono/manager_agenda_settings.html @@ -89,6 +89,10 @@ {% if agenda.maximal_booking_delay is not None %} {% blocktrans count count=agenda.maximal_booking_delay %}{{ count }} day{% plural %}{{ count }} days{% endblocktrans %} {% else %}{% trans "undefined" %}{% endif %} + {% if agenda.minimal_booking_delay is not None or agenda.maximal_booking_delay is not None %} +
  • {% trans "Minimal booking time:" %} + {% if agenda.minimal_booking_time is not None %}{{ agenda.minimal_booking_time }}{% else %}{% trans "current time" %}{% endif %}
  • + {% endif %}
    {% trans 'Configure' %} diff --git a/tests/api/test_agenda.py b/tests/api/test_agenda.py index e0fd3577..5ee8d34c 100644 --- a/tests/api/test_agenda.py +++ b/tests/api/test_agenda.py @@ -66,6 +66,7 @@ def test_agendas_api(app): 'minimal_booking_delay': 1, 'minimal_booking_delay_in_working_days': False, 'maximal_booking_delay': 56, + 'minimal_booking_time': '00:00:00', 'edit_role': 'Edit', 'view_role': None, 'category': 'category-a', @@ -85,6 +86,7 @@ def test_agendas_api(app): 'minimal_booking_delay': 1, 'minimal_booking_delay_in_working_days': False, 'maximal_booking_delay': 56, + 'minimal_booking_time': '00:00:00', 'edit_role': None, 'view_role': None, 'category': 'category-a', @@ -104,6 +106,7 @@ def test_agendas_api(app): 'minimal_booking_delay': 1, 'minimal_booking_delay_in_working_days': False, 'maximal_booking_delay': 56, + 'minimal_booking_time': '00:00:00', 'edit_role': None, 'view_role': None, 'category': None, @@ -121,6 +124,7 @@ def test_agendas_api(app): 'slug': 'foo-bar-meeting', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, + 'minimal_booking_time': '00:00:00', 'edit_role': None, 'view_role': 'View', 'category': 'category-b', @@ -144,6 +148,7 @@ def test_agendas_api(app): 'slug': 'foo-bar-meeting-2', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, + 'minimal_booking_time': '00:00:00', 'edit_role': None, 'view_role': None, 'category': None, @@ -164,6 +169,7 @@ def test_agendas_api(app): 'slug': 'virtual-agenda', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, + 'minimal_booking_time': '00:00:00', 'edit_role': 'Edit', 'view_role': 'View', 'category': None, @@ -411,6 +417,7 @@ def test_virtual_agenda_detail(app, virtual_meetings_agenda): 'slug': 'virtual-agenda', 'minimal_booking_delay': None, 'maximal_booking_delay': None, + 'minimal_booking_time': '00:00:00', 'edit_role': None, 'view_role': None, 'category': None, @@ -457,25 +464,25 @@ def test_agenda_api_delete_busy(app, user): assert not Agenda.objects.exists() -@pytest.mark.freeze_time('2021-07-09') +@pytest.mark.freeze_time('2021-07-09T08:00:00.0+02:00') def test_add_agenda(app, user, settings): events_type = EventsType.objects.create(label='Type A') category_a = Category.objects.create(label='Category A') api_url = '/api/agenda/' # no authentication - resp = app.post(api_url, status=401) + resp = app.post_json(api_url, status=401) assert resp.json['detail'] == 'Authentication credentials were not provided.' # wrong password app.authorization = ('Basic', ('john.doe', 'wrong')) - resp = app.post(api_url, status=401) + resp = app.post_json(api_url, status=401) assert resp.json['detail'] == 'Invalid username/password.' app.authorization = ('Basic', ('john.doe', 'password')) # missing fields - resp = app.post(api_url, status=400) + resp = app.post_json(api_url, status=400) assert resp.json['err'] assert resp.json['errors'] == {'label': ['This field is required.'], 'slug': ['This field is required.']} @@ -492,7 +499,7 @@ def test_add_agenda(app, user, settings): 'category': 'oups', 'events_type': 'oups', } - resp = app.post(api_url, params=params, status=400) + resp = app.post_json(api_url, params=params, status=400) assert resp.json['err'] assert resp.json['errors'] == { 'kind': ['"oups" is not a valid choice.'], @@ -512,7 +519,7 @@ def test_add_agenda(app, user, settings): 'label': 'foo', 'slug': meeting_agenda.slug, } - resp = app.post(api_url, params=params, status=400) + resp = app.post_json(api_url, params=params, status=400) assert resp.json['err'] assert resp.json['errors'] == {'slug': ['agenda with this Identifier already exists.']} @@ -523,7 +530,7 @@ def test_add_agenda(app, user, settings): 'kind': 'meetings', 'minimal_booking_delay_in_working_days': True, } - resp = app.post(api_url, params=params, status=400) + resp = app.post_json(api_url, params=params, status=400) assert resp.json['err'] assert resp.json['errors'] == { 'minimal_booking_delay_in_working_days': ['Option not available on meetings agenda'] @@ -534,7 +541,7 @@ def test_add_agenda(app, user, settings): 'kind': 'meetings', 'events_type': 'type-a', } - resp = app.post(api_url, params=params, status=400) + resp = app.post_json(api_url, params=params, status=400) assert resp.json['err'] assert resp.json['errors'] == {'events_type': ['Option not available on meetings agenda']} @@ -543,7 +550,7 @@ def test_add_agenda(app, user, settings): 'label': 'My Agenda', 'slug': 'my-agenda', } - resp = app.post(api_url, params=params) + resp = app.post_json(api_url, params=params) assert not resp.json['err'] assert len(resp.json['data']) == 1 agenda = Agenda.objects.get(slug='my-agenda') @@ -567,11 +574,14 @@ def test_add_agenda(app, user, settings): 'mark_event_checked_auto': True, 'disable_check_update': False, } - resp = app.post(api_url, params=params) + resp = app.post_json(api_url, params=params) assert not resp.json['err'] assert len(resp.json['data']) == 1 agenda = Agenda.objects.get(slug='foo-meetings') - assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 10) + assert agenda.min_booking_datetime == localtime(now()).replace( + day=10, hour=0, minute=0, second=0, microsecond=0 + ) + assert agenda.minimal_booking_time == datetime.time(0) assert agenda.edit_role == edit_group assert agenda.view_role == view_group assert agenda.category == category_a @@ -587,6 +597,7 @@ def test_add_agenda(app, user, settings): 'minimal_booking_delay': 1, 'minimal_booking_delay_in_working_days': True, 'maximal_booking_delay': 3, + 'minimal_booking_time': None, 'anonymize_delay': 30, 'edit_role': 'Edit', 'view_role': 'View', @@ -596,13 +607,14 @@ def test_add_agenda(app, user, settings): 'disable_check_update': True, 'booking_check_filters': 'foo,bar,baz', } - resp = app.post(api_url, params=params) + resp = app.post_json(api_url, params=params) assert not resp.json['err'] assert len(resp.json['data']) == 1 agenda = Agenda.objects.get(slug='foo-events') assert agenda.edit_role == edit_group assert agenda.view_role == view_group assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 12) + assert agenda.minimal_booking_time is None assert agenda.category == category_a assert agenda.events_type == events_type assert agenda.mark_event_checked_auto is False -- 2.39.2 From dbd62e3f3f1eb64e6e0393fa32f14e45ff2a5bd0 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 8 Mar 2023 18:38:37 +0100 Subject: [PATCH 2/3] agendas: reimplement min/max_booking_datetime based on minimal_booking_time (#56284) --- chrono/agendas/models.py | 36 ++++----- tests/api/test_agenda.py | 2 +- tests/test_agendas.py | 154 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 170 insertions(+), 22 deletions(-) diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py index 097183ed..9b903f69 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -643,31 +643,27 @@ class Agenda(models.Model): if self.maximal_booking_delay is None: return None - # compute middle of today with localtime - # 28 Mar 2021 12:00 +01:00 - t = localtime(now()).replace(hour=12, minute=0) + # reference is now, in local timezone + t = localtime(now()) - # advance of self.maximal_booking_delay - 1 days - # 28 Mar 2021 12:00 +01:00 == 28 Mars 2021 13:00 +02:00 as DST happend on 28 Mar 2021. + # add delay t += datetime.timedelta(days=self.maximal_booking_delay) - # move to midnight of the day before, DST happen between 2h/3h so it - # always exists because +/- timedelta does not move the timezone, only - # localtime() does it. - # 27 Mar 2021 12:00 +01:00 == 28 Mars 2021 01:00 +02:00 - return localtime(t).replace(hour=0, minute=0, second=0, microsecond=0) + # replace time if needed + if self.minimal_booking_time: + t = datetime.datetime.combine(t.date(), self.minimal_booking_time, tzinfo=t.tzinfo) + # t could not exist, recompute it as an existing datetime by converting to UTC then to localtime + return localtime(t.astimezone(utc)) @functional.cached_property def min_booking_datetime(self): if self.minimal_booking_delay is None: return None - # compute middle of today with localtime - # 28 Mar 2021 12:00 +01:00 - t = localtime(now()).replace(hour=12, minute=0) + # reference is now, in local timezone + t = localtime(now()) - # advance of self.minimal_booking_delay - 1 days - # 28 Mar 2021 12:00 +01:00 == 28 Mars 2021 13:00 +02:00 as DST happend on 28 Mar 2021. + # add delay if settings.WORKING_DAY_CALENDAR is not None and self.minimal_booking_delay_in_working_days: source_class = import_string(settings.WORKING_DAY_CALENDAR) calendar = source_class() @@ -675,11 +671,11 @@ class Agenda(models.Model): else: t += datetime.timedelta(days=self.minimal_booking_delay) - # move to midnight of the day before, DST happen between 2h/3h so it - # always exists because +/- timedelta does not move the timezone, only - # localtime() does it. - # 27 Mar 2021 12:00 +01:00 == 28 Mars 2021 01:00 +02:00 - return localtime(t).replace(hour=0, minute=0, second=0, microsecond=0) + # replace time if needed + if self.minimal_booking_time: + t = datetime.datetime.combine(t.date(), self.minimal_booking_time, tzinfo=t.tzinfo) + # t could not exist, recompute it as an existing datetime by converting to UTC then to localtime + return localtime(t.astimezone(utc)) def get_open_events( self, diff --git a/tests/api/test_agenda.py b/tests/api/test_agenda.py index 5ee8d34c..de6bc777 100644 --- a/tests/api/test_agenda.py +++ b/tests/api/test_agenda.py @@ -613,7 +613,7 @@ def test_add_agenda(app, user, settings): agenda = Agenda.objects.get(slug='foo-events') assert agenda.edit_role == edit_group assert agenda.view_role == view_group - assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 12) + assert agenda.min_booking_datetime == localtime(now()).replace(day=12) assert agenda.minimal_booking_time is None assert agenda.category == category_a assert agenda.events_type == events_type diff --git a/tests/test_agendas.py b/tests/test_agendas.py index 9cf745c4..5f5d45d8 100644 --- a/tests/test_agendas.py +++ b/tests/test_agendas.py @@ -36,7 +36,7 @@ from chrono.agendas.models import ( UnavailabilityCalendar, VirtualMember, ) -from chrono.utils.timezone import localtime, make_aware, now +from chrono.utils.timezone import localtime, make_aware, make_naive, now pytestmark = pytest.mark.django_db @@ -268,6 +268,158 @@ def test_agenda_maximal_booking_delay(freezer): assert agenda.max_booking_datetime == make_aware(datetime.datetime(2021, 11, 2, 0, 0, 0)) +def delay_parameter_to_label(argvalue): + if isinstance(argvalue, str): + return argvalue + return repr(argvalue) + + +@pytest.mark.parametrize( + 'current_time,min_booking_datetime', + [ + ('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 13, 8)), + ('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 22, 7)), + # summer DST change on sunday 28th + ('2021-03-25T01:30:00+01:00', datetime.datetime(2021, 3, 29, 1, 30)), + ('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 29, 2, 30)), + ('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 29, 3, 30)), + ('2021-03-28T01:30:00+01:00', datetime.datetime(2021, 4, 1, 1, 30)), + ('2021-03-28T03:30:00+02:00', datetime.datetime(2021, 4, 1, 3, 30)), + # winter DST change on sunday 31th + ('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 2, 1, 30)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 2, 30)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 2, 30)), + ('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 4, 1, 30)), + ('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 4, 2, 30)), + ('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 4, 2, 30)), + ('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 4, 3, 30)), + ], + ids=delay_parameter_to_label, +) +def test_agenda_minimal_booking_delay_no_minimal_booking_time(freezer, current_time, min_booking_datetime): + freezer.move_to(current_time) + agenda = Agenda.objects.create(label='Agenda', minimal_booking_delay=4, minimal_booking_time=None) + assert make_naive(agenda.min_booking_datetime) == min_booking_datetime + + +@pytest.mark.parametrize( + 'current_time,min_booking_datetime', + [ + ('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 16, 8)), + ('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 24, 7)), + # summer DST change on sunday 28th + ('2021-03-25T01:30:00+01:00', datetime.datetime(2021, 3, 31, 1, 30)), + ('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 31, 2, 30)), + ('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 31, 3, 30)), + ('2021-03-28T01:30:00+01:00', datetime.datetime(2021, 4, 1, 1, 30)), + ('2021-03-28T03:30:00+02:00', datetime.datetime(2021, 4, 1, 3, 30)), + # winter DST change on sunday 31th + ('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 5, 1, 30)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 5, 2, 30)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 5, 2, 30)), + ('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 5, 1, 30)), + ('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 5, 2, 30)), + ('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 5, 2, 30)), + ('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 5, 3, 30)), + ], + ids=delay_parameter_to_label, +) +def test_agenda_minimal_booking_delay_in_working_days_no_minimal_booking_time( + settings, freezer, current_time, min_booking_datetime +): + settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' + freezer.move_to(current_time) + agenda = Agenda.objects.create( + label='Agenda', + minimal_booking_delay=4, + minimal_booking_time=None, + minimal_booking_delay_in_working_days=True, + ) + assert make_naive(agenda.min_booking_datetime) == min_booking_datetime + + +@pytest.mark.parametrize( + 'current_time,min_booking_datetime', + [ + ('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 13, 8)), + ('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 22, 8)), + # summer DST change on sunday + ('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 29, 8)), + ('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 29, 8)), + # winter DST change on sunday + ('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 2, 8)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 8)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 8)), + ('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 4, 8)), + ('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 4, 8)), + ('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 4, 8)), + ('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 4, 8)), + ], + ids=delay_parameter_to_label, +) +def test_agenda_minimal_booking_delay_minimal_booking_time_at_8(freezer, current_time, min_booking_datetime): + freezer.move_to(current_time) + agenda = Agenda.objects.create( + label='Agenda', minimal_booking_delay=4, minimal_booking_time=datetime.time(8) + ) + assert make_naive(agenda.min_booking_datetime) == min_booking_datetime + + +@pytest.mark.parametrize( + 'current_time,max_booking_datetime', + [ + ('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 13, 8)), + ('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 22, 7)), + # summer DST change on sunday 28th + ('2021-03-25T01:30:00+01:00', datetime.datetime(2021, 3, 29, 1, 30)), + ('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 29, 2, 30)), + ('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 29, 3, 30)), + ('2021-03-28T01:30:00+01:00', datetime.datetime(2021, 4, 1, 1, 30)), + ('2021-03-28T03:30:00+02:00', datetime.datetime(2021, 4, 1, 3, 30)), + # winter DST change on sunday 31th + ('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 2, 1, 30)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 2, 30)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 2, 30)), + ('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 4, 1, 30)), + ('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 4, 2, 30)), + ('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 4, 2, 30)), + ('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 4, 3, 30)), + ], + ids=delay_parameter_to_label, +) +def test_agenda_maximal_booking_delay_no_minimal_booking_time(freezer, current_time, max_booking_datetime): + freezer.move_to(current_time) + agenda = Agenda.objects.create(label='Agenda', maximal_booking_delay=4, minimal_booking_time=None) + assert make_naive(agenda.max_booking_datetime) == max_booking_datetime + + +@pytest.mark.parametrize( + 'current_time,max_booking_datetime', + [ + ('2021-07-09T08:00:00+02:00', datetime.datetime(2021, 7, 13, 8)), + ('2021-03-18T07:00:00+01:00', datetime.datetime(2021, 3, 22, 8)), + # summer DST change on sunday + ('2021-03-25T02:30:00+01:00', datetime.datetime(2021, 3, 29, 8)), + ('2021-03-25T03:30:00+01:00', datetime.datetime(2021, 3, 29, 8)), + # winter DST change on sunday + ('2021-10-29T01:30:00+02:00', datetime.datetime(2021, 11, 2, 8)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 8)), + ('2021-10-29T02:30:00+02:00', datetime.datetime(2021, 11, 2, 8)), + ('2021-10-31T01:30:00+02:00', datetime.datetime(2021, 11, 4, 8)), + ('2021-10-31T02:30:00+02:00', datetime.datetime(2021, 11, 4, 8)), + ('2021-10-31T02:30:00+01:00', datetime.datetime(2021, 11, 4, 8)), + ('2021-10-31T03:30:00+01:00', datetime.datetime(2021, 11, 4, 8)), + ], + ids=delay_parameter_to_label, +) +def test_agenda_maximal_booking_delay_minimal_booking_time_at_8(freezer, current_time, max_booking_datetime): + freezer.move_to(current_time) + agenda = Agenda.objects.create( + label='Agenda', maximal_booking_delay=4, minimal_booking_time=datetime.time(8) + ) + assert make_naive(agenda.max_booking_datetime) == max_booking_datetime + + @pytest.mark.parametrize('with_prefetch', [True, False]) def test_agenda_is_available_for_simple_management(settings, with_prefetch): settings.EXCEPTIONS_SOURCES = { -- 2.39.2 From bdae8cd2176e1e3cb83c7f443b490bba79fff5bc Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 8 Mar 2023 22:11:36 +0100 Subject: [PATCH 3/3] translation update (#56284) --- chrono/locale/fr/LC_MESSAGES/django.po | 29 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/chrono/locale/fr/LC_MESSAGES/django.po b/chrono/locale/fr/LC_MESSAGES/django.po index 1fd9b1aa..3a04c6bb 100644 --- a/chrono/locale/fr/LC_MESSAGES/django.po +++ b/chrono/locale/fr/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: chrono 0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-03-16 17:02+0100\n" -"PO-Revision-Date: 2023-03-02 18:58+0100\n" +"POT-Creation-Date: 2023-03-23 10:33+0100\n" +"PO-Revision-Date: 2023-03-23 10:35+0100\n" "Last-Translator: Frederic Peters \n" "Language: French\n" "MIME-Version: 1.0\n" @@ -207,8 +207,7 @@ msgstr "" #: agendas/models.py msgid "Enable the check of bookings when event has not passed" -msgstr "" -"Activer le pointage des réservations sur les événements à venir" +msgstr "Activer le pointage des réservations sur les événements à venir" #: agendas/models.py msgid "Filters" @@ -248,6 +247,17 @@ msgstr "" msgid "Events type" msgstr "Type d’événements" +#: agendas/models.py +msgid "Minimal booking time" +msgstr "Heure de réservation minimale" + +#: agendas/models.py +msgid "" +"Ex.: 08:00:00. If left empty, available events will be those that are later " +"than the current time." +msgstr "" +"Ex.: 08:00:00. Si ce champ est laissé vide alors les évènements ou rendez-vous disponibles à la réservation sont ceux dont le début est plus tard que l'heure courante, en prenant en compte les délais minimaux et maximaux de réservation en jours" + #: agendas/models.py #, python-format msgid "Missing \"%s\" resource" @@ -2182,6 +2192,14 @@ msgstr[1] "%(count)s jours" msgid "Maximal booking delay:" msgstr "Délai de réservation maximal :" +#: manager/templates/chrono/manager_agenda_settings.html +msgid "Minimal booking time:" +msgstr "Heure de réservation minimale :" + +#: manager/templates/chrono/manager_agenda_settings.html +msgid "current time" +msgstr "heure actuelle" + #: manager/templates/chrono/manager_agenda_settings.html msgid "Edit Role:" msgstr "Rôle d’édition :" @@ -2633,8 +2651,7 @@ msgstr "" #: manager/templates/chrono/manager_events_agenda_settings.html msgid "Enable the check of bookings when event has not passed:" -msgstr "" -"Activer le pointage des réservations sur les événements à venir :" +msgstr "Activer le pointage des réservations sur les événements à venir :" #: manager/templates/chrono/manager_events_agenda_settings.html #, python-format -- 2.39.2