Projet

Général

Profil

0001-agenda-option-to-have-min-delay-in-working-days-5504.patch

Lauréline Guérin, 08 juillet 2021 09:53

Télécharger (13 ko)

Voir les différences:

Subject: [PATCH] agenda: option to have min delay in working days (#55049)

 .../0089_agenda_event_display_template.py     | 13 ++++++--
 .../0096_min_booking_delay_working_days.py    | 16 ++++++++++
 chrono/agendas/models.py                      | 16 ++++++++--
 chrono/manager/forms.py                       |  3 ++
 .../chrono/manager_agenda_settings.html       | 10 ++++--
 chrono/settings.py                            |  4 ++-
 tests/manager/test_all.py                     | 31 ++++++++++++++++---
 tests/test_agendas.py                         | 21 +++++++++++++
 tests/test_import_export.py                   |  2 ++
 9 files changed, 104 insertions(+), 12 deletions(-)
 create mode 100644 chrono/agendas/migrations/0096_min_booking_delay_working_days.py
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
-