Projet

Général

Profil

0002-api-endpoint-to-resize-a-booking-40039.patch

Lauréline Guérin, 05 mars 2020 14:04

Télécharger (11,5 ko)

Voir les différences:

Subject: [PATCH 2/2] api: endpoint to resize a booking (#40039)

 chrono/api/urls.py  |   1 +
 chrono/api/views.py | 101 ++++++++++++++++++++++++++++++++++++++
 tests/test_api.py   | 117 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 219 insertions(+)
chrono/api/urls.py
49 49
    url(r'^booking/(?P<booking_pk>\w+)/cancel/$', views.cancel_booking, name='api-cancel-booking'),
50 50
    url(r'^booking/(?P<booking_pk>\w+)/accept/$', views.accept_booking, name='api-accept-booking'),
51 51
    url(r'^booking/(?P<booking_pk>\w+)/suspend/$', views.suspend_booking, name='api-suspend-booking'),
52
    url(r'^booking/(?P<booking_pk>\w+)/resize/$', views.resize_booking, name='api-resize-booking'),
52 53
    url(r'^booking/(?P<booking_pk>\w+)/ics/$', views.booking_ics, name='api-booking-ics'),
53 54
]
chrono/api/views.py
842 842
suspend_booking = SuspendBooking.as_view()
843 843

  
844 844

  
845
class ResizeSerializer(serializers.Serializer):
846
    count = serializers.IntegerField(min_value=1)
847

  
848

  
849
class ResizeBooking(APIView):
850
    '''
851
    Resize a booking.
852

  
853
    It will return error codes if the booking was cancelled before (code 1)
854
    if the booking is not primary (code 2)
855
    if the event is sold out (code 3) or
856
    if the booking is on multi events (code 4).
857
    '''
858

  
859
    permission_classes = (permissions.IsAuthenticated,)
860
    serializer_class = ResizeSerializer
861

  
862
    def post(self, request, booking_pk=None, format=None):
863
        serializer = self.serializer_class(data=request.data)
864
        if not serializer.is_valid():
865
            return Response(
866
                {
867
                    'err': 1,
868
                    'err_class': 'invalid payload',
869
                    'err_desc': _('invalid payload'),
870
                    'errors': serializer.errors,
871
                },
872
                status=status.HTTP_400_BAD_REQUEST,
873
            )
874
        payload = serializer.validated_data
875

  
876
        booking = get_object_or_404(Booking, pk=booking_pk)
877
        event = booking.event
878
        if booking.cancellation_datetime:
879
            response = {
880
                'err': 1,
881
                'err_class': 'booking is cancelled',
882
                'err_desc': _('booking is cancelled'),
883
            }
884
            return Response(response)
885
        if booking.primary_booking is not None:
886
            response = {
887
                'err': 2,
888
                'err_class': 'secondary booking',
889
                'err_desc': _('secondary booking'),
890
            }
891
            return Response(response)
892
        event_ids = set([event.pk])
893
        secondary_bookings = booking.secondary_booking_set.all().order_by('-creation_datetime')
894
        for secondary in secondary_bookings:
895
            event_ids.add(secondary.event_id)
896
        if len(event_ids) > 1:
897
            response = {
898
                'err': 4,
899
                'err_class': 'can not resize multi event booking',
900
                'err_desc': _('can not resize multi event booking'),
901
            }
902
            return Response(response)
903

  
904
        count = payload['count']
905
        booked_places = event.waiting_list_places and event.waiting_list or event.booked_places
906
        if booked_places < count:
907
            if (event.waiting_list and count > event.waiting_list_places or
908
                    not event.waiting_list and count > event.places):
909
                response = {
910
                    'err': 3,
911
                    'err_class': 'sold out',
912
                    'err_desc': _('sold out'),
913
                }
914
                return Response(response)
915

  
916
        with transaction.atomic():
917
            if booked_places > count:
918
                # decrease places
919
                for secondary in secondary_bookings[:booked_places - count]:
920
                    secondary.delete()
921
            elif booked_places < count:
922
                # increase places
923
                bulk_bookings = []
924
                for i in range(0, count - booked_places):
925
                    bulk_bookings.append(
926
                        Booking(
927
                            primary_booking=booking,
928
                            event_id=event.pk,
929
                            in_waiting_list=bool(event.waiting_list_places and event.waiting_list),
930
                            label=booking.label,
931
                            user_name=booking.user_name,
932
                            backoffice_url=booking.backoffice_url,
933
                            user_display_label=booking.user_display_label,
934
                            extra_data=booking.extra_data
935
                        )
936
                    )
937
                Booking.objects.bulk_create(bulk_bookings)
938

  
939
        response = {'err': 0, 'booking_id': booking.pk}
940
        return Response(response)
941

  
942

  
943
resize_booking = ResizeBooking.as_view()
944

  
945

  
845 946
class SlotStatus(APIView):
846 947
    permission_classes = (permissions.IsAuthenticated,)
847 948

  
tests/test_api.py
1404 1404
    assert primary.in_waiting_list is False
1405 1405

  
1406 1406

  
1407
def test_resize_booking(app, some_data, user):
1408
    agenda_id = Agenda.objects.filter(label=u'Foo bar')[0].id
1409
    event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0]
1410
    event.places = 5
1411
    event.waiting_list_places = 5
1412
    event.save()
1413
    event2 = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[1]
1414

  
1415
    # create a booking not on the waiting list
1416
    primary = Booking.objects.create(event=event, in_waiting_list=False)
1417
    secondary = Booking.objects.create(event=event, in_waiting_list=False, primary_booking=primary)
1418

  
1419
    assert Booking.objects.filter(in_waiting_list=False).count() == 2
1420

  
1421
    app.authorization = ('Basic', ('john.doe', 'password'))
1422

  
1423
    resp = app.post('/api/booking/%s/resize/' % secondary.pk, params={'count': 42})
1424
    assert resp.json['err'] == 2
1425
    assert resp.json['reason'] == 'secondary booking'  # legacy
1426
    assert resp.json['err_class'] == 'secondary booking'
1427
    assert resp.json['err_desc'] == 'secondary booking'
1428

  
1429
    # resize a booking that doesn't exist
1430
    resp = app.post('/api/booking/0/resize/', params={'count': 42}, status=404)
1431

  
1432
    # decrease a booking to 0
1433
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 0}, status=400)
1434
    assert resp.json['err'] == 1
1435

  
1436
    # decrease a booking not in waiting list
1437
    assert Booking.objects.filter(in_waiting_list=False).count() == 2
1438
    assert Booking.objects.filter().count() == 2
1439
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1440
    assert resp.json['err'] == 0
1441
    assert Booking.objects.filter(in_waiting_list=False).count() == 1
1442
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1443
    assert Booking.objects.filter().count() == 1
1444

  
1445
    # no changes
1446
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1447
    assert resp.json['err'] == 0
1448
    assert Booking.objects.filter(in_waiting_list=False).count() == 1
1449
    assert Booking.objects.filter().count() == 1
1450

  
1451
    # increase a booking not in waiting list
1452
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 2})
1453
    assert resp.json['err'] == 0
1454
    assert Booking.objects.filter(in_waiting_list=False).count() == 2
1455
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1456
    assert Booking.objects.filter().count() == 2
1457
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 6})
1458
    assert resp.json['err'] == 3
1459
    assert resp.json['reason'] == 'sold out'  # legacy
1460
    assert resp.json['err_class'] == 'sold out'
1461
    assert resp.json['err_desc'] == 'sold out'
1462
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 5})
1463
    assert resp.json['err'] == 0
1464
    assert Booking.objects.filter(in_waiting_list=False).count() == 5
1465
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1466
    assert Booking.objects.filter().count() == 5
1467
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 6})
1468
    assert resp.json['err'] == 3
1469
    assert resp.json['reason'] == 'sold out'  # legacy
1470
    assert resp.json['err_class'] == 'sold out'
1471
    assert resp.json['err_desc'] == 'sold out'
1472

  
1473
    # decrease a booking in waiting list
1474
    primary.suspend()
1475
    assert Booking.objects.filter(in_waiting_list=True).count() == 5
1476
    assert Booking.objects.filter().count() == 5
1477
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1478
    assert resp.json['err'] == 0
1479
    assert Booking.objects.filter(in_waiting_list=True).count() == 1
1480
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1481
    assert Booking.objects.filter().count() == 1
1482

  
1483
    # no changes
1484
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1485
    assert resp.json['err'] == 0
1486
    assert Booking.objects.filter(in_waiting_list=True).count() == 1
1487
    assert Booking.objects.filter().count() == 1
1488

  
1489
    # increase a booking in waiting list
1490
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 2})
1491
    assert resp.json['err'] == 0
1492
    assert Booking.objects.filter(in_waiting_list=True).count() == 2
1493
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1494
    assert Booking.objects.filter().count() == 2
1495
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 6})
1496
    assert resp.json['err'] == 3
1497
    assert resp.json['reason'] == 'sold out'  # legacy
1498
    assert resp.json['err_class'] == 'sold out'
1499
    assert resp.json['err_desc'] == 'sold out'
1500
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 5})
1501
    assert resp.json['err'] == 0
1502
    assert Booking.objects.filter(in_waiting_list=True).count() == 5
1503
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1504
    assert Booking.objects.filter().count() == 5
1505
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 6})
1506
    assert resp.json['err'] == 3
1507
    assert resp.json['reason'] == 'sold out'  # legacy
1508
    assert resp.json['err_class'] == 'sold out'
1509
    assert resp.json['err_desc'] == 'sold out'
1510

  
1511
    # resize a booking that is on multi events
1512
    secondary = Booking.objects.create(event=event2, in_waiting_list=False, primary_booking=primary)
1513
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 42})
1514
    assert resp.json['err'] == 4
1515
    assert resp.json['reason'] == 'can not resize multi event booking'  # legacy
1516
    assert resp.json['err_class'] == 'can not resize multi event booking'
1517
    assert resp.json['err_desc'] == 'can not resize multi event booking'
1518
    # resize a booking that was cancelled before
1519
    primary.cancel()
1520
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 42})
1521
    assert resp.json['err'] == 1
1522

  
1523

  
1407 1524
def test_multiple_booking_api(app, some_data, user):
1408 1525
    agenda = Agenda.objects.filter(label=u'Foo bar')[0]
1409 1526
    event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
1410
-