Projet

Général

Profil

0001-api-cancel-booking-in-recurring-fillslot-instead-of-.patch

Lauréline Guérin, 07 février 2022 17:35

Télécharger (33,9 ko)

Voir les différences:

Subject: [PATCH 1/3] api: cancel booking in recurring fillslot - instead of
 delete (#61066)

 chrono/api/views.py        |  35 ++-
 tests/api/test_fillslot.py | 589 ++++++++++++++++++++++++++++++++++---
 2 files changed, 580 insertions(+), 44 deletions(-)
chrono/api/views.py
1647 1647
                payload['slots'], start_datetime, end_datetime, user_external_id
1648 1648
            ).values_list('pk', flat=True)
1649 1649

  
1650
        events_to_book = events_to_book.exclude(booking__user_external_id=user_external_id)
1650
        # outdated bookings to remove (cancelled bookings to replace by an active booking)
1651
        events_cancelled_to_delete = events_to_book.filter(
1652
            booking__user_external_id=user_external_id,
1653
            booking__cancellation_datetime__isnull=False,
1654
            full=False,
1655
        )
1656
        # book only events without active booking for the user
1657
        events_to_book = events_to_book.exclude(
1658
            pk__in=Booking.objects.filter(
1659
                event__in=events_to_book,
1660
                user_external_id=user_external_id,
1661
                cancellation_datetime__isnull=True,
1662
            ).values('event')
1663
        )
1651 1664

  
1665
        # exclude full events
1652 1666
        full_events = list(events_to_book.filter(full=True))
1653 1667
        events_to_book = events_to_book.filter(full=False)
1654 1668

  
......
1662 1676
        extra_data = {k: v for k, v in request.data.items() if k not in payload}
1663 1677
        bookings = [make_booking(event, payload, extra_data) for event in events_to_book]
1664 1678

  
1679
        bookings_to_cancel = Booking.objects.filter(
1680
            user_external_id=user_external_id, event__in=events_to_unbook, cancellation_datetime__isnull=True
1681
        )
1682

  
1665 1683
        with transaction.atomic():
1666
            deleted_count = Booking.objects.filter(
1667
                user_external_id=user_external_id, event__in=events_to_unbook
1668
            ).delete()[0]
1684
            # cancel existing bookings
1685
            cancelled_count = 0
1686
            for booking in bookings_to_cancel:
1687
                booking.cancel()
1688
                cancelled_count += 1
1689
            # and delete outdated cancelled bookings
1690
            Booking.objects.filter(
1691
                user_external_id=user_external_id, event__in=events_cancelled_to_delete
1692
            ).delete()
1693
            # create missing bookings
1669 1694
            Booking.objects.bulk_create(bookings)
1670 1695

  
1671 1696
        response = {
1672 1697
            'err': 0,
1673 1698
            'booking_count': len(bookings),
1674
            'cancelled_booking_count': deleted_count,
1699
            'cancelled_booking_count': cancelled_count,
1675 1700
            'full_events': [get_event_detail(request, x, multiple_agendas=True) for x in full_events],
1676 1701
        }
1677 1702
        if payload.get('include_booked_events_detail'):
tests/api/test_fillslot.py
2371 2371
    assert events.filter(booked_waiting_list_places=2).count() == 5
2372 2372

  
2373 2373

  
2374
def test_recurring_events_api_fillslots_change_bookings(app, user, freezer):
2374
def test_recurring_events_api_fillslots_book_with_cancelled(app, user, freezer):
2375
    freezer.move_to('2021-09-06 12:00')
2376
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
2377
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
2378
    event = Event.objects.create(
2379
        label='Event',
2380
        start_datetime=now(),
2381
        recurrence_days=[0, 1],  # Monday, Tuesday
2382
        places=1,
2383
        waiting_list_places=1,
2384
        agenda=agenda,
2385
        recurrence_end_date=now() + datetime.timedelta(days=21),
2386
    )
2387
    event.create_all_recurrences()
2388

  
2389
    # create cancelled bookings for the user
2390
    event_1_0 = event.get_or_create_event_recurrence(event.start_datetime)
2391
    booking_1_0 = Booking.objects.create(event=event_1_0, user_external_id='user_id')
2392
    booking_1_0.cancel()
2393
    assert booking_1_0.cancellation_datetime is not None
2394
    event_1_1 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=1))
2395
    booking_1_1 = Booking.objects.create(event=event_1_1, user_external_id='user_id')
2396
    booking_1_1.cancel()
2397
    assert booking_1_1.cancellation_datetime is not None
2398
    # and non cancelled bookings for the user
2399
    event_2_0 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=7))
2400
    booking_2_0 = Booking.objects.create(event=event_2_0, user_external_id='user_id')
2401
    assert booking_2_0.cancellation_datetime is None
2402
    event_2_1 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=8))
2403
    booking_2_1 = Booking.objects.create(event=event_2_1, user_external_id='user_id')
2404
    assert booking_2_1.cancellation_datetime is None
2405
    # and bookings for another user
2406
    Booking.objects.create(event=event_1_0, user_external_id='user_id_foobar')
2407
    other_booking = Booking.objects.create(event=event_2_0, user_external_id='user_id_foobar')
2408
    other_booking.cancel()
2409

  
2410
    app.authorization = ('Basic', ('john.doe', 'password'))
2411
    fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=book' % agenda.slug
2412
    params = {'user_external_id': 'user_id'}
2413

  
2414
    # Book Monday
2415
    params['slots'] = 'foo-bar@event:0'
2416
    resp = app.post_json(fillslots_url, params=params)
2417
    assert resp.json['booking_count'] == 2
2418
    assert resp.json['cancelled_booking_count'] == 0
2419
    assert Booking.objects.filter(user_external_id='user_id').count() == 5
2420
    assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 4
2421
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 3
2422
    assert (
2423
        Booking.objects.filter(
2424
            user_external_id='user_id',
2425
            event__start_datetime__week_day=2,
2426
            cancellation_datetime__isnull=True,
2427
        ).count()
2428
        == 3
2429
    )
2430
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 2
2431
    assert (
2432
        Booking.objects.filter(
2433
            user_external_id='user_id',
2434
            event__start_datetime__week_day=3,
2435
            cancellation_datetime__isnull=True,
2436
        ).count()
2437
        == 1
2438
    )
2439

  
2440
    assert Booking.objects.filter(pk=booking_1_0.pk).exists() is False  # cancelled booking deleted
2441
    booking_1_1.refresh_from_db()
2442
    booking_2_0.refresh_from_db()
2443
    booking_2_1.refresh_from_db()
2444
    assert booking_1_1.cancellation_datetime is not None
2445
    assert booking_2_0.cancellation_datetime is None
2446
    assert booking_2_1.cancellation_datetime is None
2447

  
2448
    # Book Tuesday
2449
    params['slots'] = 'foo-bar@event:1'
2450
    resp = app.post_json(fillslots_url, params=params)
2451
    assert resp.json['booking_count'] == 2
2452
    assert resp.json['cancelled_booking_count'] == 0
2453
    assert Booking.objects.filter(user_external_id='user_id').count() == 6
2454
    assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 6
2455
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 3
2456
    assert (
2457
        Booking.objects.filter(
2458
            user_external_id='user_id',
2459
            event__start_datetime__week_day=2,
2460
            cancellation_datetime__isnull=True,
2461
        ).count()
2462
        == 3
2463
    )
2464
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 3
2465
    assert (
2466
        Booking.objects.filter(
2467
            user_external_id='user_id',
2468
            event__start_datetime__week_day=3,
2469
            cancellation_datetime__isnull=True,
2470
        ).count()
2471
        == 3
2472
    )
2473

  
2474
    assert Booking.objects.filter(pk=booking_1_0.pk).exists() is False  # cancelled booking deleted
2475
    assert Booking.objects.filter(pk=booking_1_1.pk).exists() is False  # cancelled booking deleted
2476
    booking_2_0.refresh_from_db()
2477
    booking_2_1.refresh_from_db()
2478
    assert booking_2_0.cancellation_datetime is None
2479
    assert booking_2_1.cancellation_datetime is None
2480

  
2481

  
2482
def test_recurring_events_api_fillslots_update(app, user, freezer):
2375 2483
    freezer.move_to('2021-09-06 12:00')
2376 2484
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
2377 2485
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
......
2413 2521
    resp = app.post_json(fillslots_url, params=params)
2414 2522
    assert resp.json['booking_count'] == 52
2415 2523
    assert resp.json['cancelled_booking_count'] == 104
2416
    assert Booking.objects.count() == 104
2417
    assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 52
2418
    assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 52
2524
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 104
2525
    assert (
2526
        Booking.objects.filter(
2527
            event__start_datetime__week_day=2,
2528
            cancellation_datetime__isnull=True,
2529
        ).count()
2530
        == 52
2531
    )
2532
    assert (
2533
        Booking.objects.filter(
2534
            event__start_datetime__week_day=3,
2535
            cancellation_datetime__isnull=True,
2536
        ).count()
2537
        == 52
2538
    )
2419 2539

  
2420 2540
    # Booking again does nothing
2421 2541
    resp = app.post_json(fillslots_url, params=params)
2422 2542
    assert resp.json['booking_count'] == 0
2423 2543
    assert resp.json['cancelled_booking_count'] == 0
2424
    assert Booking.objects.count() == 104
2544
    assert Booking.objects.count() == 208
2545
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 104
2425 2546

  
2426 2547
    params = {'user_external_id': 'user_id_2'}
2427 2548
    params['slots'] = 'foo-bar@event:0,foo-bar@event:3'
2428 2549
    resp = app.post_json(fillslots_url, params=params)
2429 2550
    assert resp.json['booking_count'] == 104
2430 2551
    assert resp.json['cancelled_booking_count'] == 0
2431
    assert Booking.objects.count() == 208
2432
    assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 104
2433
    assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 52
2552
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 208
2553
    assert (
2554
        Booking.objects.filter(
2555
            event__start_datetime__week_day=2,
2556
            cancellation_datetime__isnull=True,
2557
        ).count()
2558
        == 104
2559
    )
2560
    assert (
2561
        Booking.objects.filter(
2562
            event__start_datetime__week_day=3,
2563
            cancellation_datetime__isnull=True,
2564
        ).count()
2565
        == 52
2566
    )
2567
    assert (
2568
        Booking.objects.filter(
2569
            event__start_datetime__week_day=5,
2570
            cancellation_datetime__isnull=True,
2571
        ).count()
2572
        == 52
2573
    )
2434 2574
    events = Event.objects.filter(primary_event__isnull=False)
2435 2575
    assert events.filter(booked_places=1).count() == 156
2436 2576
    assert events.filter(booked_waiting_list_places=1).count() == 52
......
2439 2579
    resp = app.post_json(fillslots_url, params=params)
2440 2580
    assert resp.json['booking_count'] == 104
2441 2581
    assert resp.json['cancelled_booking_count'] == 104
2442
    assert Booking.objects.count() == 208
2443
    assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 104
2444
    assert Booking.objects.filter(event__start_datetime__week_day=6).count() == 52
2582
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 208
2583
    assert (
2584
        Booking.objects.filter(
2585
            event__start_datetime__week_day=2,
2586
            cancellation_datetime__isnull=True,
2587
        ).count()
2588
        == 52
2589
    )
2590
    assert (
2591
        Booking.objects.filter(
2592
            event__start_datetime__week_day=3,
2593
            cancellation_datetime__isnull=True,
2594
        ).count()
2595
        == 104
2596
    )
2597
    assert (
2598
        Booking.objects.filter(
2599
            event__start_datetime__week_day=6,
2600
            cancellation_datetime__isnull=True,
2601
        ).count()
2602
        == 52
2603
    )
2445 2604
    events = Event.objects.filter(primary_event__isnull=False)
2446 2605
    assert events.filter(booked_places=1).count() == 156
2447 2606
    assert events.filter(booked_waiting_list_places=1).count() == 52
......
2450 2609
    resp = app.post_json(fillslots_url + '&date_end=2022-01-01', params=params)
2451 2610
    assert resp.json['booking_count'] == 0
2452 2611
    assert resp.json['cancelled_booking_count'] == 70
2453
    assert Booking.objects.filter(user_external_id='user_id_2').count() == 34
2612
    assert (
2613
        Booking.objects.filter(
2614
            user_external_id='user_id_2',
2615
            cancellation_datetime__isnull=True,
2616
        ).count()
2617
        == 34
2618
    )
2454 2619

  
2455 2620
    # past bookings are left in place
2456 2621
    freezer.move_to('2021-10-04 12:00')  # first Monday of october
......
2458 2623
    resp = app.post_json(fillslots_url + '&date_start=2021-10-11&date_end=2022-01-01', params=params)
2459 2624
    assert resp.json['booking_count'] == 0
2460 2625
    assert resp.json['cancelled_booking_count'] == 2  # only first week of october was cancelled
2461
    assert Booking.objects.filter(user_external_id='user_id_2').count() == 32
2626
    assert (
2627
        Booking.objects.filter(
2628
            user_external_id='user_id_2',
2629
            cancellation_datetime__isnull=True,
2630
        ).count()
2631
        == 32
2632
    )
2462 2633

  
2463 2634
    # passing empty slots cancels all bookings
2464 2635
    params['slots'] = ''
2465 2636
    resp = app.post_json(fillslots_url, params=params)
2466 2637
    assert resp.json['cancelled_booking_count'] == 24
2467
    assert Booking.objects.filter(user_external_id='user_id_2').count() == 8
2468
    assert Booking.objects.filter(user_external_id='user_id_2', event__start_datetime__gt=now()).count() == 0
2638
    assert (
2639
        Booking.objects.filter(
2640
            user_external_id='user_id_2',
2641
            cancellation_datetime__isnull=True,
2642
        ).count()
2643
        == 8
2644
    )
2645
    assert (
2646
        Booking.objects.filter(
2647
            user_external_id='user_id_2',
2648
            event__start_datetime__gt=now(),
2649
            cancellation_datetime__isnull=True,
2650
        ).count()
2651
        == 0
2652
    )
2469 2653

  
2470 2654
    # only recurring events are impacted
2471 2655
    normal_event = Event.objects.create(
......
2477 2661
    assert Booking.objects.filter(user_external_id='user_id', event=normal_event).count() == 1
2478 2662

  
2479 2663

  
2480
def test_recurring_events_api_unbook(app, user, freezer):
2664
def test_recurring_events_api_fillslots_update_with_cancelled(app, user, freezer):
2665
    freezer.move_to('2021-09-06 12:00')
2666
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
2667
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
2668
    event = Event.objects.create(
2669
        label='Event',
2670
        start_datetime=now(),
2671
        recurrence_days=[0, 1],  # Monday, Tuesday
2672
        places=1,
2673
        waiting_list_places=1,
2674
        agenda=agenda,
2675
        recurrence_end_date=now() + datetime.timedelta(days=21),
2676
    )
2677
    event.create_all_recurrences()
2678

  
2679
    # create cancelled bookings for the user
2680
    event_1_0 = event.get_or_create_event_recurrence(event.start_datetime)
2681
    booking_1_0 = Booking.objects.create(event=event_1_0, user_external_id='user_id')
2682
    booking_1_0.cancel()
2683
    assert booking_1_0.cancellation_datetime is not None
2684
    event_1_1 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=1))
2685
    booking_1_1 = Booking.objects.create(event=event_1_1, user_external_id='user_id')
2686
    booking_1_1.cancel()
2687
    assert booking_1_1.cancellation_datetime is not None
2688
    # and non cancelled bookings for the user
2689
    event_2_0 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=7))
2690
    booking_2_0 = Booking.objects.create(event=event_2_0, user_external_id='user_id')
2691
    assert booking_2_0.cancellation_datetime is None
2692
    event_2_1 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=8))
2693
    booking_2_1 = Booking.objects.create(event=event_2_1, user_external_id='user_id')
2694
    assert booking_2_1.cancellation_datetime is None
2695
    # and bookings for another user
2696
    Booking.objects.create(event=event_1_0, user_external_id='user_id_foobar')
2697
    other_booking = Booking.objects.create(event=event_2_0, user_external_id='user_id_foobar')
2698
    other_booking.cancel()
2699

  
2700
    app.authorization = ('Basic', ('john.doe', 'password'))
2701
    fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=update' % agenda.slug
2702
    params = {'user_external_id': 'user_id'}
2703

  
2704
    # Book Monday
2705
    params['slots'] = 'foo-bar@event:0'
2706
    resp = app.post_json(fillslots_url, params=params)
2707
    assert resp.json['booking_count'] == 2
2708
    assert resp.json['cancelled_booking_count'] == 1
2709
    assert Booking.objects.filter(user_external_id='user_id').count() == 5
2710
    assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 3
2711
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 3
2712
    assert (
2713
        Booking.objects.filter(
2714
            user_external_id='user_id',
2715
            event__start_datetime__week_day=2,
2716
            cancellation_datetime__isnull=True,
2717
        ).count()
2718
        == 3
2719
    )
2720
    assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 2
2721
    assert (
2722
        Booking.objects.filter(
2723
            user_external_id='user_id',
2724
            event__start_datetime__week_day=3,
2725
            cancellation_datetime__isnull=True,
2726
        ).count()
2727
        == 0
2728
    )
2729

  
2730
    assert Booking.objects.filter(pk=booking_1_0.pk).exists() is False  # cancelled booking deleted
2731
    booking_1_1.refresh_from_db()
2732
    booking_2_0.refresh_from_db()
2733
    booking_2_1.refresh_from_db()
2734
    assert booking_1_1.cancellation_datetime is not None
2735
    assert booking_2_0.cancellation_datetime is None
2736
    assert booking_2_1.cancellation_datetime is not None
2737

  
2738
    # Book Tuesday
2739
    params['slots'] = 'foo-bar@event:1'
2740
    resp = app.post_json(fillslots_url, params=params)
2741
    assert resp.json['booking_count'] == 3
2742
    assert resp.json['cancelled_booking_count'] == 3
2743
    assert Booking.objects.filter(user_external_id='user_id').count() == 6
2744
    assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 3
2745
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 3
2746
    assert (
2747
        Booking.objects.filter(
2748
            user_external_id='user_id',
2749
            event__start_datetime__week_day=2,
2750
            cancellation_datetime__isnull=True,
2751
        ).count()
2752
        == 0
2753
    )
2754
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 3
2755
    assert (
2756
        Booking.objects.filter(
2757
            user_external_id='user_id',
2758
            event__start_datetime__week_day=3,
2759
            cancellation_datetime__isnull=True,
2760
        ).count()
2761
        == 3
2762
    )
2763

  
2764
    assert Booking.objects.filter(pk=booking_1_0.pk).exists() is False  # cancelled booking deleted
2765
    assert Booking.objects.filter(pk=booking_1_1.pk).exists() is False  # cancelled booking deleted
2766
    assert Booking.objects.filter(pk=booking_2_1.pk).exists() is False  # cancelled booking deleted
2767
    booking_2_0.refresh_from_db()
2768
    assert booking_2_0.cancellation_datetime is not None
2769

  
2770

  
2771
def test_recurring_events_api_fillslots_unbook(app, user, freezer):
2481 2772
    freezer.move_to('2021-09-06 12:00')
2482 2773
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
2483 2774
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
......
2518 2809
    assert resp.json['booking_count'] == 0
2519 2810
    assert resp.json['cancelled_booking_count'] == 52
2520 2811

  
2521
    assert Booking.objects.count() == 104
2522
    assert Booking.objects.filter(event__primary_event=event).count() == 52
2523
    assert Booking.objects.filter(event__primary_event=sunday_event).count() == 52
2812
    assert Booking.objects.count() == 156
2813
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 104
2814
    assert (
2815
        Booking.objects.filter(
2816
            event__primary_event=event,
2817
            cancellation_datetime__isnull=True,
2818
        ).count()
2819
        == 52
2820
    )
2821
    assert (
2822
        Booking.objects.filter(
2823
            event__primary_event=event,
2824
            cancellation_datetime__isnull=False,
2825
        ).count()
2826
        == 52
2827
    )
2828
    assert (
2829
        Booking.objects.filter(
2830
            event__primary_event=sunday_event,
2831
            cancellation_datetime__isnull=True,
2832
        ).count()
2833
        == 52
2834
    )
2524 2835

  
2525 2836
    params['slots'] = 'foo-bar@sunday-event:6'
2526 2837
    resp = app.post_json(
......
2529 2840
    assert resp.json['booking_count'] == 0
2530 2841
    assert resp.json['cancelled_booking_count'] == 9
2531 2842

  
2532
    assert Booking.objects.count() == 95
2533
    assert Booking.objects.filter(event__primary_event=event).count() == 52
2534
    assert Booking.objects.filter(event__primary_event=sunday_event).count() == 43
2843
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 95
2844
    assert (
2845
        Booking.objects.filter(
2846
            event__primary_event=event,
2847
            cancellation_datetime__isnull=True,
2848
        ).count()
2849
        == 52
2850
    )
2851
    assert (
2852
        Booking.objects.filter(
2853
            event__primary_event=event,
2854
            cancellation_datetime__isnull=False,
2855
        ).count()
2856
        == 52
2857
    )
2858
    assert (
2859
        Booking.objects.filter(
2860
            event__primary_event=sunday_event,
2861
            cancellation_datetime__isnull=True,
2862
        ).count()
2863
        == 43
2864
    )
2865
    assert (
2866
        Booking.objects.filter(
2867
            event__primary_event=sunday_event,
2868
            cancellation_datetime__isnull=False,
2869
        ).count()
2870
        == 9
2871
    )
2535 2872
    assert not Booking.objects.filter(
2536 2873
        event__primary_event=sunday_event,
2537 2874
        event__start_datetime__range=(
2538 2875
            datetime.date(year=2022, month=1, day=1),
2539 2876
            datetime.date(year=2022, month=3, day=1),
2540 2877
        ),
2878
        cancellation_datetime__isnull=True,
2541 2879
    ).exists()
2542 2880

  
2543 2881
    freezer.move_to('2022-01-01 12:00')
......
2547 2885
    assert resp.json['booking_count'] == 0
2548 2886
    assert resp.json['cancelled_booking_count'] == 35
2549 2887

  
2550
    assert Booking.objects.count() == 60
2551
    assert Booking.objects.filter(event__primary_event=event).count() == 17
2888
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 60
2889
    assert (
2890
        Booking.objects.filter(
2891
            event__primary_event=event,
2892
            cancellation_datetime__isnull=True,
2893
        ).count()
2894
        == 17
2895
    )
2552 2896
    assert not Booking.objects.filter(
2553
        event__primary_event=event, event__start_datetime__gt=datetime.date(year=2022, month=1, day=1)
2897
        event__primary_event=event,
2898
        event__start_datetime__gt=datetime.date(year=2022, month=1, day=1),
2899
        cancellation_datetime__isnull=True,
2554 2900
    ).exists()
2555
    assert Booking.objects.filter(event__primary_event=sunday_event).count() == 43
2901
    assert (
2902
        Booking.objects.filter(
2903
            event__primary_event=sunday_event,
2904
            cancellation_datetime__isnull=True,
2905
        ).count()
2906
        == 43
2907
    )
2556 2908

  
2557 2909
    # unbooking when there are no bookings does nothing
2558 2910
    params['user_external_id'] = 'user_id_2'
......
2561 2913
    assert resp.json['cancelled_booking_count'] == 0
2562 2914

  
2563 2915

  
2916
def test_recurring_events_api_fillslots_unbook_with_cancelled(app, user, freezer):
2917
    freezer.move_to('2021-09-06 12:00')
2918
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
2919
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
2920
    event = Event.objects.create(
2921
        label='Event',
2922
        start_datetime=now(),
2923
        recurrence_days=[0, 1],  # Monday, Tuesday
2924
        places=1,
2925
        waiting_list_places=1,
2926
        agenda=agenda,
2927
        recurrence_end_date=now() + datetime.timedelta(days=21),
2928
    )
2929
    event.create_all_recurrences()
2930

  
2931
    # create cancelled bookings for the user
2932
    event_1_0 = event.get_or_create_event_recurrence(event.start_datetime)
2933
    booking_1_0 = Booking.objects.create(event=event_1_0, user_external_id='user_id')
2934
    booking_1_0.cancel()
2935
    assert booking_1_0.cancellation_datetime is not None
2936
    event_1_1 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=1))
2937
    booking_1_1 = Booking.objects.create(event=event_1_1, user_external_id='user_id')
2938
    booking_1_1.cancel()
2939
    assert booking_1_1.cancellation_datetime is not None
2940
    # and non cancelled bookings for the user
2941
    event_2_0 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=7))
2942
    booking_2_0 = Booking.objects.create(event=event_2_0, user_external_id='user_id')
2943
    assert booking_2_0.cancellation_datetime is None
2944
    event_2_1 = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=8))
2945
    booking_2_1 = Booking.objects.create(event=event_2_1, user_external_id='user_id')
2946
    assert booking_2_1.cancellation_datetime is None
2947
    # and bookings for another user
2948
    Booking.objects.create(event=event_1_0, user_external_id='user_id_foobar')
2949
    other_booking = Booking.objects.create(event=event_2_0, user_external_id='user_id_foobar')
2950
    other_booking.cancel()
2951

  
2952
    app.authorization = ('Basic', ('john.doe', 'password'))
2953
    fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=unbook' % agenda.slug
2954
    params = {'user_external_id': 'user_id'}
2955

  
2956
    # unbook Monday
2957
    params['slots'] = 'foo-bar@event:0'
2958
    resp = app.post_json(fillslots_url, params=params)
2959
    assert resp.json['booking_count'] == 0
2960
    assert resp.json['cancelled_booking_count'] == 1
2961
    assert Booking.objects.filter(user_external_id='user_id').count() == 4
2962
    assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 1
2963
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 2
2964
    assert (
2965
        Booking.objects.filter(
2966
            user_external_id='user_id',
2967
            event__start_datetime__week_day=2,
2968
            cancellation_datetime__isnull=True,
2969
        ).count()
2970
        == 0
2971
    )
2972
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 2
2973
    assert (
2974
        Booking.objects.filter(
2975
            user_external_id='user_id',
2976
            event__start_datetime__week_day=3,
2977
            cancellation_datetime__isnull=True,
2978
        ).count()
2979
        == 1
2980
    )
2981

  
2982
    booking_1_0.refresh_from_db()
2983
    booking_1_1.refresh_from_db()
2984
    booking_2_0.refresh_from_db()
2985
    booking_2_1.refresh_from_db()
2986
    assert booking_1_0.cancellation_datetime is not None
2987
    assert booking_1_1.cancellation_datetime is not None
2988
    assert booking_2_0.cancellation_datetime is not None
2989
    assert booking_2_1.cancellation_datetime is None
2990

  
2991
    # unbook Tuesday
2992
    params['slots'] = 'foo-bar@event:1'
2993
    resp = app.post_json(fillslots_url, params=params)
2994
    assert resp.json['booking_count'] == 0
2995
    assert resp.json['cancelled_booking_count'] == 1
2996
    assert Booking.objects.filter(user_external_id='user_id').count() == 4
2997
    assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 0
2998
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 2
2999
    assert (
3000
        Booking.objects.filter(
3001
            user_external_id='user_id',
3002
            event__start_datetime__week_day=2,
3003
            cancellation_datetime__isnull=True,
3004
        ).count()
3005
        == 0
3006
    )
3007
    assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 2
3008
    assert (
3009
        Booking.objects.filter(
3010
            user_external_id='user_id',
3011
            event__start_datetime__week_day=3,
3012
            cancellation_datetime__isnull=True,
3013
        ).count()
3014
        == 0
3015
    )
3016

  
3017
    booking_1_0.refresh_from_db()
3018
    booking_1_1.refresh_from_db()
3019
    booking_2_0.refresh_from_db()
3020
    booking_2_1.refresh_from_db()
3021
    assert booking_1_0.cancellation_datetime is not None
3022
    assert booking_1_1.cancellation_datetime is not None
3023
    assert booking_2_0.cancellation_datetime is not None
3024
    assert booking_2_1.cancellation_datetime is not None
3025

  
3026

  
2564 3027
@pytest.mark.freeze_time('2021-09-06 12:00')
2565 3028
def test_recurring_events_api_fillslots_subscribed(app, user):
2566 3029
    category = Category.objects.create(label='Category A')
......
2629 3092
    resp = app.post_json(fillslots_url % 'all', params=params)
2630 3093
    assert resp.json['booking_count'] == 12
2631 3094
    assert resp.json['cancelled_booking_count'] == 10
2632
    assert Booking.objects.count() == 12
2633
    booked_events_first_agenda = Event.objects.filter(primary_event=event, booking__isnull=False)
3095
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 12
3096
    booked_events_first_agenda = Event.objects.filter(
3097
        primary_event=event,
3098
        booking__isnull=False,
3099
        booking__cancellation_datetime__isnull=True,
3100
    )
2634 3101
    assert [
2635 3102
        x.strftime('%d/%m/%Y') for x in booked_events_first_agenda.values_list('start_datetime', flat=True)
2636 3103
    ] == ['21/09/2021', '28/09/2021', '05/10/2021', '12/10/2021', '19/10/2021']
2637
    booked_events_second_agenda = Event.objects.filter(primary_event=sunday_event, booking__isnull=False)
3104
    booked_events_second_agenda = Event.objects.filter(
3105
        primary_event=sunday_event,
3106
        booking__isnull=False,
3107
        booking__cancellation_datetime__isnull=True,
3108
    )
2638 3109
    assert [
2639 3110
        x.strftime('%d/%m/%Y') for x in booked_events_second_agenda.values_list('start_datetime', flat=True)
2640 3111
    ] == ['19/12/2021', '26/12/2021', '02/01/2022', '09/01/2022', '16/01/2022', '23/01/2022', '30/01/2022']
......
2656 3127
    params = {'user_external_id': 'yyy', 'slots': 'second-agenda@sunday-event:6'}
2657 3128
    resp = app.post_json(fillslots_url % 'category-b', params=params)
2658 3129
    assert resp.json['booking_count'] == 3
2659
    assert Booking.objects.count() == 15
2660
    booked_events_user_yyy = Event.objects.filter(primary_event=sunday_event, booking__user_external_id='yyy')
3130
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 15
3131
    booked_events_user_yyy = Event.objects.filter(
3132
        primary_event=sunday_event,
3133
        booking__user_external_id='yyy',
3134
        booking__cancellation_datetime__isnull=True,
3135
    )
2661 3136
    assert [
2662 3137
        x.strftime('%d/%m/%Y') for x in booked_events_user_yyy.values_list('start_datetime', flat=True)
2663 3138
    ] == ['12/09/2021', '07/11/2021', '14/11/2021']
......
2727 3202
    assert resp.json['booking_count'] == 0
2728 3203
    assert resp.json['cancelled_booking_count'] == 2
2729 3204

  
2730
    assert Booking.objects.count() == 19
2731
    assert Booking.objects.filter(event__primary_event=event_a).count() == 12
2732
    assert Booking.objects.filter(event__primary_event=event_b).count() == 0
2733
    assert Booking.objects.filter(event__primary_event=event_c).count() == 7
3205
    assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 19
3206
    assert (
3207
        Booking.objects.filter(
3208
            event__primary_event=event_a,
3209
            cancellation_datetime__isnull=True,
3210
        ).count()
3211
        == 12
3212
    )
3213
    assert (
3214
        Booking.objects.filter(
3215
            event__primary_event=event_b,
3216
            cancellation_datetime__isnull=True,
3217
        ).count()
3218
        == 0
3219
    )
3220
    assert (
3221
        Booking.objects.filter(
3222
            event__primary_event=event_c,
3223
            cancellation_datetime__isnull=True,
3224
        ).count()
3225
        == 7
3226
    )
2734 3227

  
2735 3228
    # update bookings
2736 3229
    params = {'user_external_id': 'user_id', 'slots': 'first-agenda@b:1'}
......
2738 3231

  
2739 3232
    assert resp.json['booking_count'] == 5
2740 3233
    assert resp.json['cancelled_booking_count'] == 19
2741
    assert Booking.objects.filter(event__primary_event=event_a).count() == 0
2742
    assert Booking.objects.filter(event__primary_event=event_b).count() == 5
2743
    assert Booking.objects.filter(event__primary_event=event_c).count() == 0
3234
    assert (
3235
        Booking.objects.filter(
3236
            event__primary_event=event_a,
3237
            cancellation_datetime__isnull=True,
3238
        ).count()
3239
        == 0
3240
    )
3241
    assert (
3242
        Booking.objects.filter(
3243
            event__primary_event=event_b,
3244
            cancellation_datetime__isnull=True,
3245
        ).count()
3246
        == 5
3247
    )
3248
    assert (
3249
        Booking.objects.filter(
3250
            event__primary_event=event_c,
3251
            cancellation_datetime__isnull=True,
3252
        ).count()
3253
        == 0
3254
    )
2744 3255

  
2745 3256
    # error if slot's agenda is not in querystring
2746 3257
    resp = app.post_json(fillslots_url % ('update', 'second-agenda'), params=params, status=400)
......
2772 3283
            params={'slots': events_to_book, 'user_external_id': 'user'},
2773 3284
        )
2774 3285
        assert resp.json['booking_count'] == 180
2775
        assert len(ctx.captured_queries) == 16
3286
        assert len(ctx.captured_queries) == 17
2776 3287

  
2777 3288

  
2778 3289
@pytest.mark.freeze_time('2021-09-06 12:00')
2779
-