Projet

Général

Profil

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

Lauréline Guérin, 09 mars 2020 12:14

Télécharger (11,6 ko)

Voir les différences:

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

 chrono/api/urls.py  |   1 +
 chrono/api/views.py | 105 +++++++++++++++++++++++++++++++++++++++
 tests/test_api.py   | 117 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 223 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
846 846
suspend_booking = SuspendBooking.as_view()
847 847

  
848 848

  
849
class ResizeSerializer(serializers.Serializer):
850
    count = serializers.IntegerField(min_value=1)
851

  
852

  
853
class ResizeBooking(APIView):
854
    '''
855
    Resize a booking.
856

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

  
863
    permission_classes = (permissions.IsAuthenticated,)
864
    serializer_class = ResizeSerializer
865

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

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

  
908
        count = payload['count']
909
        booked_places = event.waiting_list_places and event.waiting_list or event.booked_places
910
        if booked_places < count:
911
            if (
912
                event.waiting_list
913
                and count > event.waiting_list_places
914
                or not event.waiting_list
915
                and count > event.places
916
            ):
917
                response = {
918
                    'err': 3,
919
                    'err_class': 'sold out',
920
                    'err_desc': _('sold out'),
921
                }
922
                return Response(response)
923

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

  
947
        response = {'err': 0, 'booking_id': booking.pk}
948
        return Response(response)
949

  
950

  
951
resize_booking = ResizeBooking.as_view()
952

  
953

  
849 954
class SlotStatus(APIView):
850 955
    permission_classes = (permissions.IsAuthenticated,)
851 956

  
tests/test_api.py
1412 1412
    assert primary.in_waiting_list is False
1413 1413

  
1414 1414

  
1415
def test_resize_booking(app, some_data, user):
1416
    agenda_id = Agenda.objects.filter(label=u'Foo bar')[0].id
1417
    event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0]
1418
    event.places = 5
1419
    event.waiting_list_places = 5
1420
    event.save()
1421
    event2 = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[1]
1422

  
1423
    # create a booking not on the waiting list
1424
    primary = Booking.objects.create(event=event, in_waiting_list=False)
1425
    secondary = Booking.objects.create(event=event, in_waiting_list=False, primary_booking=primary)
1426

  
1427
    assert Booking.objects.filter(in_waiting_list=False).count() == 2
1428

  
1429
    app.authorization = ('Basic', ('john.doe', 'password'))
1430

  
1431
    resp = app.post('/api/booking/%s/resize/' % secondary.pk, params={'count': 42})
1432
    assert resp.json['err'] == 2
1433
    assert resp.json['reason'] == 'secondary booking'  # legacy
1434
    assert resp.json['err_class'] == 'secondary booking'
1435
    assert resp.json['err_desc'] == 'secondary booking'
1436

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

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

  
1444
    # decrease a booking not in waiting list
1445
    assert Booking.objects.filter(in_waiting_list=False).count() == 2
1446
    assert Booking.objects.filter().count() == 2
1447
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1448
    assert resp.json['err'] == 0
1449
    assert Booking.objects.filter(in_waiting_list=False).count() == 1
1450
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1451
    assert Booking.objects.filter().count() == 1
1452

  
1453
    # no changes
1454
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1455
    assert resp.json['err'] == 0
1456
    assert Booking.objects.filter(in_waiting_list=False).count() == 1
1457
    assert Booking.objects.filter().count() == 1
1458

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

  
1481
    # decrease a booking in waiting list
1482
    primary.suspend()
1483
    assert Booking.objects.filter(in_waiting_list=True).count() == 5
1484
    assert Booking.objects.filter().count() == 5
1485
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1486
    assert resp.json['err'] == 0
1487
    assert Booking.objects.filter(in_waiting_list=True).count() == 1
1488
    assert Booking.objects.filter(primary_booking__isnull=True).count() == 1
1489
    assert Booking.objects.filter().count() == 1
1490

  
1491
    # no changes
1492
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 1})
1493
    assert resp.json['err'] == 0
1494
    assert Booking.objects.filter(in_waiting_list=True).count() == 1
1495
    assert Booking.objects.filter().count() == 1
1496

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

  
1519
    # resize a booking that is on multi events
1520
    secondary = Booking.objects.create(event=event2, in_waiting_list=False, primary_booking=primary)
1521
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 42})
1522
    assert resp.json['err'] == 4
1523
    assert resp.json['reason'] == 'can not resize multi event booking'  # legacy
1524
    assert resp.json['err_class'] == 'can not resize multi event booking'
1525
    assert resp.json['err_desc'] == 'can not resize multi event booking'
1526
    # resize a booking that was cancelled before
1527
    primary.cancel()
1528
    resp = app.post('/api/booking/%s/resize/' % primary.pk, params={'count': 42})
1529
    assert resp.json['err'] == 1
1530

  
1531

  
1415 1532
def test_multiple_booking_api(app, some_data, user):
1416 1533
    agenda = Agenda.objects.filter(label=u'Foo bar')[0]
1417 1534
    event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
1418
-