Facturation: avoir un flag sur Event pour noter que l'événement est verrouillé (#75416) #56
|
@ -0,0 +1,15 @@
|
|||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('agendas', '0149_booking_extra_user_block'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='check_locked',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -1521,6 +1521,7 @@ class Event(models.Model):
|
|||
cancelled = models.BooleanField(default=False)
|
||||
cancellation_scheduled = models.BooleanField(default=False)
|
||||
checked = models.BooleanField(default=False)
|
||||
check_locked = models.BooleanField(default=False)
|
||||
meeting_type = models.ForeignKey(MeetingType, null=True, on_delete=models.CASCADE)
|
||||
desk = models.ForeignKey('Desk', null=True, on_delete=models.CASCADE)
|
||||
resources = models.ManyToManyField('Resource')
|
||||
|
|
|
@ -157,6 +157,18 @@ class MultipleAgendasEventsCheckStatusSerializer(AgendaSlugsMixin, DateRangeMixi
|
|||
return get_objects_from_slugs(value, qs=self.get_agenda_qs())
|
||||
|
||||
|
||||
class MultipleAgendasEventsCheckLockSerializer(AgendaSlugsMixin, DateRangeMixin, serializers.Serializer):
|
||||
check_locked = serializers.BooleanField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for field in ['agendas', 'date_start', 'date_end', 'check_locked']:
|
||||
self.fields[field].required = True
|
||||
|
||||
def validate_agendas(self, value):
|
||||
return get_objects_from_slugs(value, qs=self.get_agenda_qs())
|
||||
|
||||
|
||||
class RecurringFillslotsSerializer(MultipleAgendasEventsFillSlotsSerializer):
|
||||
include_booked_events_detail = serializers.BooleanField(default=False)
|
||||
check_overlaps = CommaSeparatedStringField(
|
||||
|
@ -437,8 +449,10 @@ class EventSerializer(serializers.ModelSerializer):
|
|||
'url',
|
||||
'primary_event',
|
||||
'agenda',
|
||||
'checked',
|
||||
'check_locked',
|
||||
]
|
||||
read_only_fields = ['slug']
|
||||
read_only_fields = ['slug', 'checked', 'check_locked']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
|
|
@ -38,6 +38,11 @@ urlpatterns = [
|
|||
views.agendas_events_check_status,
|
||||
name='api-agendas-events-check-status',
|
||||
),
|
||||
path(
|
||||
'agendas/events/check-lock/',
|
||||
views.agendas_events_check_lock,
|
||||
name='api-agendas-events-check-lock',
|
||||
),
|
||||
re_path(r'^agenda/(?P<agenda_identifier>[\w-]+)/$', views.agenda),
|
||||
re_path(
|
||||
r'^agenda/(?P<agenda_identifier>[\w-]+)/datetimes/$', views.datetimes, name='api-agenda-datetimes'
|
||||
|
|
|
@ -502,6 +502,7 @@ def get_event_detail(
|
|||
'url': event.url,
|
||||
'duration': event.duration,
|
||||
'checked': event.checked,
|
||||
'check_locked': event.check_locked,
|
||||
}
|
||||
for key, value in event.get_custom_fields().items():
|
||||
details['custom_field_%s' % key] = value
|
||||
|
@ -2296,6 +2297,35 @@ class MultipleAgendasEventsCheckStatus(APIView):
|
|||
agendas_events_check_status = MultipleAgendasEventsCheckStatus.as_view()
|
||||
|
||||
|
||||
class MultipleAgendasEventsCheckLock(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
serializer_class = serializers.MultipleAgendasEventsCheckLockSerializer
|
||||
|
||||
def post(self, request):
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
|
||||
if not serializer.is_valid():
|
||||
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors, err=1)
|
||||
agendas = serializer.validated_data['agendas']
|
||||
date_start = serializer.validated_data['date_start']
|
||||
date_end = serializer.validated_data['date_end']
|
||||
check_locked = serializer.validated_data['check_locked']
|
||||
|
||||
events = Event.objects.filter(
|
||||
agenda__in=agendas,
|
||||
recurrence_days__isnull=True,
|
||||
cancelled=False,
|
||||
start_datetime__gte=date_start,
|
||||
start_datetime__lt=date_end,
|
||||
)
|
||||
events.update(check_locked=check_locked)
|
||||
|
||||
return Response({'err': 0})
|
||||
|
||||
|
||||
agendas_events_check_lock = MultipleAgendasEventsCheckLock.as_view()
|
||||
|
||||
|
||||
class SubscriptionFilter(filters.FilterSet):
|
||||
date_start = filters.DateFilter(method='do_nothing')
|
||||
date_end = filters.DateFilter(method='do_nothing')
|
||||
|
|
|
@ -54,6 +54,7 @@ def test_status(app, user):
|
|||
'disabled': False,
|
||||
'duration': None,
|
||||
'checked': False,
|
||||
'check_locked': False,
|
||||
'api': {
|
||||
'bookings_url': 'http://testserver/api/agenda/foo-bar/bookings/event-slug/',
|
||||
'fillslot_url': 'http://testserver/api/agenda/foo-bar/fillslot/event-slug/',
|
||||
|
@ -92,6 +93,7 @@ def test_status(app, user):
|
|||
'disabled': False,
|
||||
'duration': None,
|
||||
'checked': False,
|
||||
'check_locked': False,
|
||||
'custom_field_text': 'foo',
|
||||
'custom_field_textarea': 'foo bar',
|
||||
'custom_field_bool': True,
|
||||
|
@ -880,6 +882,7 @@ def test_events(app, user):
|
|||
places=10,
|
||||
agenda=agenda2,
|
||||
checked=True,
|
||||
check_locked=True,
|
||||
)
|
||||
# cancelled event, not returned
|
||||
Event.objects.create(
|
||||
|
@ -939,6 +942,8 @@ def test_events(app, user):
|
|||
assert resp.json['data'] == [
|
||||
{
|
||||
'agenda': 'bar',
|
||||
'check_locked': True,
|
||||
'checked': True,
|
||||
'description': None,
|
||||
'duration': None,
|
||||
'label': 'Event Label',
|
||||
|
@ -956,6 +961,8 @@ def test_events(app, user):
|
|||
},
|
||||
{
|
||||
'agenda': 'foo',
|
||||
'check_locked': False,
|
||||
'checked': False,
|
||||
'description': None,
|
||||
'duration': None,
|
||||
'label': 'Recurring Event Label',
|
||||
|
@ -973,6 +980,8 @@ def test_events(app, user):
|
|||
},
|
||||
{
|
||||
'agenda': 'foo',
|
||||
'check_locked': False,
|
||||
'checked': False,
|
||||
'description': None,
|
||||
'duration': None,
|
||||
'label': 'Recurring Event Label',
|
||||
|
@ -1301,6 +1310,7 @@ def test_events_check_status_events(app, user):
|
|||
places=10,
|
||||
agenda=agenda,
|
||||
checked=True,
|
||||
check_locked=True,
|
||||
)
|
||||
# not checked event
|
||||
notchecked_event = Event.objects.create(
|
||||
|
@ -1366,6 +1376,8 @@ def test_events_check_status_events(app, user):
|
|||
'waiting_list_places': 0,
|
||||
'agenda': agenda.slug,
|
||||
'primary_event': None,
|
||||
'check_locked': False,
|
||||
'checked': False,
|
||||
'custom_field_bool': None,
|
||||
'custom_field_text': '',
|
||||
'custom_field_textarea': '',
|
||||
|
@ -1390,6 +1402,8 @@ def test_events_check_status_events(app, user):
|
|||
'waiting_list_places': 0,
|
||||
'agenda': agenda.slug,
|
||||
'primary_event': None,
|
||||
'check_locked': True,
|
||||
'checked': True,
|
||||
'custom_field_bool': None,
|
||||
'custom_field_text': '',
|
||||
'custom_field_textarea': '',
|
||||
|
@ -1429,6 +1443,8 @@ def test_events_check_status_events(app, user):
|
|||
'waiting_list_places': 0,
|
||||
'agenda': agenda.slug,
|
||||
'primary_event': recurring_event.slug,
|
||||
'check_locked': False,
|
||||
'checked': True,
|
||||
'custom_field_text': 'foo',
|
||||
'custom_field_textarea': 'foo bar',
|
||||
'custom_field_bool': True,
|
||||
|
@ -1595,3 +1611,279 @@ def test_events_check_status_subscription_filter(app, user, freezer, event_date,
|
|||
}
|
||||
resp = app.get(url, params=params)
|
||||
assert len(resp.json['data']) == int(expected)
|
||||
|
||||
|
||||
def test_events_check_lock_params(app, user):
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
||||
# missing check_locked
|
||||
lguerin marked this conversation as resolved
Outdated
|
||||
resp = app.post_json(
|
||||
'/api/agendas/events/check-lock/',
|
||||
params={'agendas': 'foo', 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['check_locked'] == ['This field is required.']
|
||||
|
||||
# missing agendas
|
||||
resp = app.post_json(
|
||||
'/api/agendas/events/check-lock/',
|
||||
params={'check_locked': True, 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['agendas'] == ['This field is required.']
|
||||
|
||||
# unknown agenda
|
||||
resp = app.post_json(
|
||||
'/api/agendas/events/check-lock/',
|
||||
params={
|
||||
'check_locked': True,
|
||||
'agendas': 'foo, bar',
|
||||
fpeters
commented
Je comprends que c'est parce qu'on partage du code (AgendaSlugsMixin) avec des bouts appelés depuis w.c.s. mais je note que peut-être là-dedans un jour on pourrait imaginer une évolution pour également accepter une liste de chaines. Je comprends que c'est parce qu'on partage du code (AgendaSlugsMixin) avec des bouts appelés depuis w.c.s. mais je note que peut-être là-dedans un jour on pourrait imaginer une évolution pour également accepter une liste de chaines.
|
||||
'date_start': '2022-05-01',
|
||||
'date_end': '2022-06-01',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['agendas'] == ['invalid slugs: bar, foo']
|
||||
Agenda.objects.create(label='Foo')
|
||||
resp = app.post_json(
|
||||
'/api/agendas/events/check-lock/',
|
||||
params={
|
||||
'check_locked': True,
|
||||
'agendas': 'foo, bar',
|
||||
'date_start': '2022-05-01',
|
||||
'date_end': '2022-06-01',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
|
||||
|
||||
# wrong kind
|
||||
wrong_agenda = Agenda.objects.create(label='Bar')
|
||||
for kind in ['meetings', 'virtual']:
|
||||
wrong_agenda.kind = kind
|
||||
wrong_agenda.save()
|
||||
resp = app.post_json(
|
||||
'/api/agendas/events/check-lock/',
|
||||
params={
|
||||
'check_locked': True,
|
||||
'agendas': 'foo, bar',
|
||||
'date_start': '2022-05-01',
|
||||
'date_end': '2022-06-01',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
|
||||
|
||||
# missing date_start
|
||||
resp = app.post_json(
|
||||
'/api/agendas/events/check-lock/',
|
||||
params={'check_locked': True, 'agendas': 'foo', 'date_end': '2022-06-01'},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['date_start'] == ['This field is required.']
|
||||
|
||||
# missing date_end
|
||||
resp = app.post_json(
|
||||
'/api/agendas/events/check-lock/',
|
||||
params={'check_locked': True, 'agendas': 'foo', 'date_start': '2022-05-01'},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['date_end'] == ['This field is required.']
|
||||
|
||||
# bad date format
|
||||
resp = app.post_json(
|
||||
'/api/agendas/events/check-lock/',
|
||||
params={'check_locked': True, 'agendas': 'foo', 'date_start': 'wrong', 'date_end': 'wrong'},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert 'wrong format' in resp.json['errors']['date_start'][0]
|
||||
assert 'wrong format' in resp.json['errors']['date_end'][0]
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-05-30 14:00')
|
||||
def test_events_check_lock(app, user):
|
||||
agenda = Agenda.objects.create(label='Foo')
|
||||
event = Event.objects.create(
|
||||
slug='event-slug',
|
||||
label='Event Label',
|
||||
start_datetime=now(),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
checked=True,
|
||||
)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
url = '/api/agendas/events/check-lock/'
|
||||
params = {
|
||||
'check_locked': True,
|
||||
'agendas': 'foo',
|
||||
'date_start': '2022-05-01',
|
||||
'date_end': '2022-06-01',
|
||||
}
|
||||
resp = app.post_json(url, params=params)
|
||||
assert resp.json['err'] == 0
|
||||
event.refresh_from_db()
|
||||
assert event.check_locked is True
|
||||
|
||||
params['check_locked'] = False
|
||||
resp = app.post_json(url, params=params)
|
||||
assert resp.json['err'] == 0
|
||||
event.refresh_from_db()
|
||||
assert event.check_locked is False
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-05-30 14:00')
|
||||
def test_events_check_lock_events(app, user):
|
||||
events_type = EventsType.objects.create(
|
||||
label='Foo',
|
||||
)
|
||||
agenda = Agenda.objects.create(label='Foo', events_type=events_type)
|
||||
start_datetime = now()
|
||||
# recurring event
|
||||
recurring_event = Event.objects.create(
|
||||
slug='recurring-event-slug',
|
||||
label='Recurring Event Label',
|
||||
start_datetime=start_datetime,
|
||||
recurrence_days=[start_datetime.weekday()],
|
||||
recurrence_end_date=start_datetime + datetime.timedelta(days=7),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
)
|
||||
recurring_event.create_all_recurrences()
|
||||
first_event = recurring_event.recurrences.get()
|
||||
event = Event.objects.create(
|
||||
slug='event-slug',
|
||||
label='Event Label',
|
||||
start_datetime=start_datetime - datetime.timedelta(days=1),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
)
|
||||
# cancelled event, not updated
|
||||
cancelled_event = Event.objects.create(
|
||||
slug='cancelled',
|
||||
label='Cancelled',
|
||||
start_datetime=start_datetime,
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
cancelled=True,
|
||||
)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
url = '/api/agendas/events/check-lock/'
|
||||
params = {
|
||||
'check_locked': True,
|
||||
'agendas': 'foo',
|
||||
'date_start': '2022-05-01',
|
||||
'date_end': '2022-06-01',
|
||||
}
|
||||
resp = app.post_json(url, params=params)
|
||||
assert resp.json['err'] == 0
|
||||
recurring_event.refresh_from_db()
|
||||
assert recurring_event.check_locked is False
|
||||
first_event.refresh_from_db()
|
||||
assert first_event.check_locked is True
|
||||
event.refresh_from_db()
|
||||
assert event.check_locked is True
|
||||
cancelled_event.refresh_from_db()
|
||||
assert cancelled_event.check_locked is False
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-05-30 14:00')
|
||||
def test_events_check_lock_agendas_filter(app, user):
|
||||
agenda1 = Agenda.objects.create(label='Foo')
|
||||
agenda2 = Agenda.objects.create(label='Foo 2')
|
||||
event1 = Event.objects.create(
|
||||
slug='event-1',
|
||||
label='Event 1',
|
||||
start_datetime=now(),
|
||||
places=10,
|
||||
agenda=agenda1,
|
||||
)
|
||||
event2 = Event.objects.create(
|
||||
slug='event-2',
|
||||
label='Event 2',
|
||||
start_datetime=now(),
|
||||
places=10,
|
||||
agenda=agenda2,
|
||||
)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
url = '/api/agendas/events/check-lock/'
|
||||
params = {
|
||||
'check_locked': True,
|
||||
'agendas': 'foo, foo-2',
|
||||
'date_start': '2022-05-01',
|
||||
'date_end': '2022-06-01',
|
||||
}
|
||||
app.post_json(url, params=params)
|
||||
event1.refresh_from_db()
|
||||
assert event1.check_locked is True
|
||||
event2.refresh_from_db()
|
||||
assert event2.check_locked is True
|
||||
|
||||
params['agendas'] = 'foo'
|
||||
params['check_locked'] = False
|
||||
app.post_json(url, params=params)
|
||||
event1.refresh_from_db()
|
||||
assert event1.check_locked is False
|
||||
event2.refresh_from_db()
|
||||
assert event2.check_locked is True
|
||||
|
||||
params['agendas'] = 'foo-2'
|
||||
app.post_json(url, params=params)
|
||||
event1.refresh_from_db()
|
||||
assert event1.check_locked is False
|
||||
event2.refresh_from_db()
|
||||
assert event2.check_locked is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'event_date, expected',
|
||||
[
|
||||
# just before first day
|
||||
((2022, 4, 30, 12, 0), False),
|
||||
# first day
|
||||
((2022, 5, 1, 12, 0), True),
|
||||
# last day
|
||||
((2022, 5, 31, 12, 0), True),
|
||||
# just after last day
|
||||
((2022, 6, 1, 12, 0), False),
|
||||
],
|
||||
)
|
||||
def test_events_check_lock_date_filter(app, user, event_date, expected):
|
||||
agenda = Agenda.objects.create(label='Foo')
|
||||
event = Event.objects.create(
|
||||
slug='event',
|
||||
label='Event',
|
||||
start_datetime=make_aware(datetime.datetime(*event_date)),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
url = '/api/agendas/events/check-lock/'
|
||||
params = {
|
||||
'check_locked': True,
|
||||
'agendas': 'foo',
|
||||
'date_start': '2022-05-01',
|
||||
'date_end': '2022-06-01',
|
||||
}
|
||||
app.post_json(url, params=params)
|
||||
event.refresh_from_db()
|
||||
assert event.check_locked == expected
|
||||
|
|
Loading…
Reference in New Issue
c'est plutôt missing check_locked, non ?
yes