0002-api-cancel-booking-in-events-fillslots-instead-of-de.patch
chrono/api/views.py | ||
---|---|---|
1779 | 1779 |
if end_datetime: |
1780 | 1780 |
already_booked_events = already_booked_events.filter(start_datetime__lt=end_datetime) |
1781 | 1781 |
agendas_by_ids = self.get_agendas_by_ids() |
1782 |
events_to_unbook_ids = []
|
|
1782 |
events_to_unbook = [] |
|
1783 | 1783 |
events_in_request_ids = [e.pk for e in events] |
1784 | 1784 |
for event in already_booked_events: |
1785 | 1785 |
if event.pk in events_in_request_ids: |
... | ... | |
1793 | 1793 |
continue |
1794 | 1794 |
if agenda.max_booking_datetime and event.start_datetime > agenda.max_booking_datetime: |
1795 | 1795 |
continue |
1796 |
events_to_unbook_ids.append(event.pk)
|
|
1796 |
events_to_unbook.append(event)
|
|
1797 | 1797 | |
1798 |
events = events.exclude(booking__user_external_id=user_external_id) |
|
1798 |
# outdated bookings to remove (cancelled bookings to replace by an active booking) |
|
1799 |
events_cancelled_to_delete = events.filter( |
|
1800 |
booking__user_external_id=user_external_id, |
|
1801 |
booking__cancellation_datetime__isnull=False, |
|
1802 |
) |
|
1803 |
# book only events without active booking for the user |
|
1804 |
events = events.exclude( |
|
1805 |
pk__in=Booking.objects.filter( |
|
1806 |
event__in=events, user_external_id=user_external_id, cancellation_datetime__isnull=True |
|
1807 |
).values('event') |
|
1808 |
) |
|
1799 | 1809 | |
1800 | 1810 |
full_events = [str(event) for event in events.filter(full=True)] |
1801 | 1811 |
if full_events: |
... | ... | |
1814 | 1824 |
extra_data = {k: v for k, v in request.data.items() if k not in payload} |
1815 | 1825 |
bookings = [make_booking(event, payload, extra_data) for event in events] |
1816 | 1826 | |
1827 |
bookings_to_cancel = Booking.objects.filter( |
|
1828 |
user_external_id=user_external_id, event__in=events_to_unbook, cancellation_datetime__isnull=True |
|
1829 |
) |
|
1830 | ||
1817 | 1831 |
with transaction.atomic(): |
1818 |
deleted_count = Booking.objects.filter( |
|
1819 |
user_external_id=user_external_id, event__in=events_to_unbook_ids |
|
1820 |
).delete()[0] |
|
1832 |
# cancel existing bookings |
|
1833 |
cancelled_count = 0 |
|
1834 |
for booking in bookings_to_cancel: |
|
1835 |
booking.cancel() |
|
1836 |
cancelled_count += 1 |
|
1837 |
# and delete outdated cancelled bookings |
|
1838 |
Booking.objects.filter( |
|
1839 |
user_external_id=user_external_id, event__in=events_cancelled_to_delete |
|
1840 |
).delete() |
|
1841 |
# create missing bookings |
|
1821 | 1842 |
Booking.objects.bulk_create(bookings) |
1822 | 1843 | |
1823 | 1844 |
response = { |
... | ... | |
1830 | 1851 |
get_event_detail(request, x, multiple_agendas=self.multiple_agendas) |
1831 | 1852 |
for x in waiting_list_events |
1832 | 1853 |
], |
1833 |
'cancelled_booking_count': deleted_count,
|
|
1854 |
'cancelled_booking_count': cancelled_count,
|
|
1834 | 1855 |
} |
1835 | 1856 |
return Response(response) |
1836 | 1857 | |
... | ... | |
1838 | 1859 |
return get_events_from_slots(payload['slots'], request, self.agenda, payload) |
1839 | 1860 | |
1840 | 1861 |
def get_already_booked_events(self, user_external_id): |
1841 |
return self.agenda.event_set.filter(booking__user_external_id=user_external_id) |
|
1862 |
return self.agenda.event_set.filter( |
|
1863 |
booking__user_external_id=user_external_id, booking__cancellation_datetime__isnull=True |
|
1864 |
) |
|
1842 | 1865 | |
1843 | 1866 |
def get_agendas_by_ids(self): |
1844 | 1867 |
return {self.agenda.pk: self.agenda} |
tests/api/test_fillslot.py | ||
---|---|---|
3341 | 3341 |
assert resp.json['booking_count'] == 1 |
3342 | 3342 |
assert resp.json['cancelled_booking_count'] == 1 |
3343 | 3343 | |
3344 |
user_bookings = Booking.objects.filter(user_external_id='user_id') |
|
3344 |
user_bookings = Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True)
|
|
3345 | 3345 |
assert {b.event.slug for b in user_bookings} == {'event-2', 'event-3'} |
3346 |
assert event.booking_set.count() == 2 |
|
3346 |
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 2
|
|
3347 | 3347 |
assert second_event.booking_set.count() == 2 |
3348 | 3348 |
assert third_event.booking_set.count() == 1 |
3349 | 3349 | |
... | ... | |
3370 | 3370 |
params['slots'] = '' |
3371 | 3371 |
resp = app.post_json(fillslots_url, params=params) |
3372 | 3372 |
assert resp.json['cancelled_booking_count'] == 3 |
3373 |
assert Booking.objects.filter(user_external_id='user_id').count() == 0 |
|
3373 |
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 0
|
|
3374 | 3374 | |
3375 | 3375 |
resp = app.post('/api/agenda/foobar/events/fillslots/', status=404) |
3376 | 3376 |
resp = app.post('/api/agenda/0/events/fillslots/', status=404) |
3377 | 3377 | |
3378 | 3378 | |
3379 |
@pytest.mark.freeze_time('2021-09-06 12:00') |
|
3380 |
def test_api_events_fillslots_with_cancelled(app, user): |
|
3381 |
agenda = Agenda.objects.create(label='Foo bar', kind='events') |
|
3382 |
event_1 = Event.objects.create( |
|
3383 |
label='Event 1', |
|
3384 |
start_datetime=now() + datetime.timedelta(days=1), |
|
3385 |
places=2, |
|
3386 |
agenda=agenda, |
|
3387 |
) |
|
3388 |
event_2 = Event.objects.create( |
|
3389 |
label='Event 2', |
|
3390 |
start_datetime=now() + datetime.timedelta(days=2), |
|
3391 |
places=2, |
|
3392 |
agenda=agenda, |
|
3393 |
) |
|
3394 |
Event.objects.create( |
|
3395 |
label='Event 3', |
|
3396 |
start_datetime=now() + datetime.timedelta(days=3), |
|
3397 |
places=2, |
|
3398 |
agenda=agenda, |
|
3399 |
) |
|
3400 | ||
3401 |
# create cancelled booking for the user |
|
3402 |
booking_1 = Booking.objects.create(event=event_1, user_external_id='user_id') |
|
3403 |
booking_1.cancel() |
|
3404 |
assert booking_1.cancellation_datetime is not None |
|
3405 |
# and non cancelled booking for the user |
|
3406 |
booking_2 = Booking.objects.create(event=event_2, user_external_id='user_id') |
|
3407 |
assert booking_2.cancellation_datetime is None |
|
3408 |
# and bookings for another user |
|
3409 |
Booking.objects.create(event=event_1, user_external_id='user_id_foobar') |
|
3410 |
other_booking = Booking.objects.create(event=event_2, user_external_id='user_id_foobar') |
|
3411 |
other_booking.cancel() |
|
3412 | ||
3413 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
3414 |
fillslots_url = '/api/agenda/%s/events/fillslots/' % agenda.slug |
|
3415 | ||
3416 |
params = {'user_external_id': 'user_id', 'slots': 'event-1,event-2,event-3'} |
|
3417 |
resp = app.post_json(fillslots_url, params=params) |
|
3418 |
assert resp.json['booking_count'] == 2 |
|
3419 |
assert len(resp.json['booked_events']) == 2 |
|
3420 |
assert resp.json['cancelled_booking_count'] == 0 |
|
3421 |
assert resp.json['booked_events'][0]['id'] == 'event-1' |
|
3422 |
assert resp.json['booked_events'][1]['id'] == 'event-3' |
|
3423 |
assert Booking.objects.filter(user_external_id='user_id').count() == 3 |
|
3424 |
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 3 |
|
3425 | ||
3426 |
assert Booking.objects.filter(pk=booking_1.pk).exists() is False # cancelled booking deleted |
|
3427 |
booking_2.refresh_from_db() |
|
3428 |
assert booking_2.cancellation_datetime is None |
|
3429 | ||
3430 |
params = {'user_external_id': 'user_id', 'slots': 'event-3'} |
|
3431 |
resp = app.post_json(fillslots_url, params=params) |
|
3432 |
assert resp.json['booking_count'] == 0 |
|
3433 |
assert resp.json['cancelled_booking_count'] == 2 |
|
3434 |
assert Booking.objects.filter(user_external_id='user_id').count() == 3 |
|
3435 |
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 1 |
|
3436 | ||
3437 |
assert Booking.objects.filter(pk=booking_1.pk).exists() is False # cancelled booking deleted |
|
3438 |
booking_2.refresh_from_db() |
|
3439 |
assert booking_2.cancellation_datetime is not None |
|
3440 | ||
3441 | ||
3379 | 3442 |
def test_api_events_fillslots_check_delays(app, user): |
3380 | 3443 |
agenda = Agenda.objects.create( |
3381 | 3444 |
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7 |
3382 |
- |