From 8c2b8ae5ddd00c35a4b6e5a97a2047b395ba4835 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Sun, 19 Jun 2022 20:15:40 +0200
Subject: [PATCH] api: patch booking & use_color_for (#66382)

---
 chrono/api/serializers.py | 18 ++++++++++++------
 chrono/api/views.py       |  3 ++-
 tests/api/test_booking.py | 33 +++++++++++++++++++++++----------
 tests/api/test_event.py   |  6 +++---
 4 files changed, 40 insertions(+), 20 deletions(-)

diff --git a/chrono/api/serializers.py b/chrono/api/serializers.py
index fc52c5d2..7c450e69 100644
--- a/chrono/api/serializers.py
+++ b/chrono/api/serializers.py
@@ -187,11 +187,7 @@ class RecurringFillslotsSerializer(MultipleAgendasEventsSlotsSerializer):
 class BookingSerializer(serializers.ModelSerializer):
     user_absence_reason = serializers.CharField(required=False, allow_blank=True, allow_null=True)
     user_presence_reason = serializers.CharField(required=False, allow_blank=True, allow_null=True)
-    color = serializers.SlugRelatedField(
-        many=False,
-        slug_field='label',
-        queryset=BookingColor.objects.all(),
-    )
+    use_color_for = serializers.CharField(required=False, allow_blank=True, allow_null=True, source='color')
 
     class Meta:
         model = Booking
@@ -205,7 +201,7 @@ class BookingSerializer(serializers.ModelSerializer):
             'user_was_present',
             'user_absence_reason',
             'user_presence_reason',
-            'color',
+            'use_color_for',
             'extra_data',
             'creation_datetime',
             'cancellation_datetime',
@@ -218,6 +214,13 @@ class BookingSerializer(serializers.ModelSerializer):
             'cancellation_datetime',
         ]
 
+    def to_internal_value(self, data):
+        if 'color' in data:
+            # legacy
+            data['use_color_for'] = data['color']
+            del data['color']
+        return super().to_internal_value(data)
+
     def to_representation(self, instance):
         ret = super().to_representation(instance)
         check_type_slug = self.instance.user_check_type.slug if self.instance.user_check_type else ''
@@ -254,6 +257,9 @@ class BookingSerializer(serializers.ModelSerializer):
     def validate_user_presence_reason(self, value):
         return self._validate_check_type('presence', value)
 
+    def validate_use_color_for(self, value):
+        return BookingColor.objects.get_or_create(label=value)[0]
+
     def validate(self, attrs):
         super().validate(attrs)
         if 'user_absence_reason' in attrs and 'user_presence_reason' in attrs:
diff --git a/chrono/api/views.py b/chrono/api/views.py
index 3fa83cde..1535f78e 100644
--- a/chrono/api/views.py
+++ b/chrono/api/views.py
@@ -2496,10 +2496,11 @@ class BookingAPI(APIView):
             'user_last_name',
             'user_email',
             'user_phone_number',
-            'color',
         ]:
             if key in request.data:
                 secondary_bookings_update[key] = getattr(self.booking, key)
+        if 'use_color_for' in request.data:
+            secondary_bookings_update['color'] = self.booking.color
         if 'user_absence_reason' in request.data or 'user_presence_reason' in request.data:
             secondary_bookings_update['user_check_type'] = self.booking.user_check_type
         if extra_data:
diff --git a/tests/api/test_booking.py b/tests/api/test_booking.py
index 126b4c06..934c3d05 100644
--- a/tests/api/test_booking.py
+++ b/tests/api/test_booking.py
@@ -153,7 +153,7 @@ def test_bookings_api(app, user):
             'user_was_present': None,
             'user_absence_reason': '',
             'user_presence_reason': '',
-            'color': None,
+            'use_color_for': None,
             'extra_data': None,
             'cancellation_datetime': None,
             'creation_datetime': localtime(meetings_booking1.creation_datetime).isoformat(),
@@ -168,7 +168,7 @@ def test_bookings_api(app, user):
             'user_was_present': None,
             'user_absence_reason': '',
             'user_presence_reason': '',
-            'color': None,
+            'use_color_for': None,
             'extra_data': None,
             'event': resp.json['data'][1]['event'],
             'cancellation_datetime': None,
@@ -184,7 +184,7 @@ def test_bookings_api(app, user):
             'user_was_present': None,
             'user_absence_reason': '',
             'user_presence_reason': '',
-            'color': None,
+            'use_color_for': None,
             'extra_data': None,
             'event': resp.json['data'][1]['event'],
             'cancellation_datetime': None,
@@ -880,6 +880,7 @@ def test_booking_patch_api_user_fields(app, user):
     agenda = Agenda.objects.create(kind='events')
     event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
     BookingColor.objects.create(label='the warmest color')
+    BookingColor.objects.create(label='the coolest color')
     color = BookingColor.objects.create(label='meh')
     booking = Booking.objects.create(event=event, color=color)
     # make secondary bookings
@@ -899,7 +900,8 @@ def test_booking_patch_api_user_fields(app, user):
         ('user_email', ''),
         ('user_phone_number', '0606'),
         ('user_phone_number', ''),
-        ('color', 'the warmest color'),
+        ('color', 'the warmest color'),  # legacy
+        ('use_color_for', 'the coolest color'),
     ]
     for key, value in to_test:
         params = {
@@ -907,6 +909,8 @@ def test_booking_patch_api_user_fields(app, user):
         }
         app.patch_json('/api/booking/%s/' % booking.pk, params=params)
         booking.refresh_from_db()
+        if key == 'use_color_for':
+            key = 'color'
         assert str(getattr(booking, key)) == value
         # all secondary bookings are updated
         for secondary_booking in booking.secondary_booking_set.all():
@@ -914,16 +918,25 @@ def test_booking_patch_api_user_fields(app, user):
         other_booking.refresh_from_db()
         assert not getattr(other_booking, key)  # not changed
 
-    # try again with a non existing color, it should fail
+    # try again with a non existing color
     params = {
         'color': 'unicorn crimson',
     }
-    response = app.patch_json('/api/booking/%s/' % booking.pk, params=params, status=400)
+    app.patch_json('/api/booking/%s/' % booking.pk, params=params)
     booking.refresh_from_db()
-    assert booking.color.label == 'the warmest color'
-    assert response.json['err_desc'] == 'invalid payload'
-    assert list(response.json['errors'].keys()) == ['color']
-    assert response.json['errors']['color'] == ['Object with label=unicorn crimson does not exist.']
+    assert booking.color.label == 'unicorn crimson'
+    for secondary_booking in booking.secondary_booking_set.all():
+        assert secondary_booking.color.label == 'unicorn crimson'
+    assert BookingColor.objects.count() == 4
+    params = {
+        'use_color_for': 'unicorn crimson again',
+    }
+    app.patch_json('/api/booking/%s/' % booking.pk, params=params)
+    booking.refresh_from_db()
+    assert booking.color.label == 'unicorn crimson again'
+    for secondary_booking in booking.secondary_booking_set.all():
+        assert secondary_booking.color.label == 'unicorn crimson again'
+    assert BookingColor.objects.count() == 5
 
 
 def test_booking_patch_api_extra_data(app, user):
diff --git a/tests/api/test_event.py b/tests/api/test_event.py
index c861200f..32d6e997 100644
--- a/tests/api/test_event.py
+++ b/tests/api/test_event.py
@@ -990,7 +990,7 @@ def test_events_check_status(app, user):
         'user_was_present',
         'user_absence_reason',
         'user_presence_reason',
-        'color',
+        'use_color_for',
         'extra_data',
         'creation_datetime',
         'cancellation_datetime',
@@ -1204,7 +1204,7 @@ def test_events_check_status_events(app, user):
             'check_status': {'check_type': 'foo-reason', 'status': 'presence'},
             'booking': {
                 'cancellation_datetime': None,
-                'color': None,
+                'use_color_for': None,
                 'creation_datetime': localtime(now()).isoformat(),
                 'extra_data': None,
                 'id': booking2.pk,
@@ -1242,7 +1242,7 @@ def test_events_check_status_events(app, user):
             'check_status': {'check_type': 'foo-reason', 'status': 'presence'},
             'booking': {
                 'cancellation_datetime': None,
-                'color': None,
+                'use_color_for': None,
                 'creation_datetime': localtime(now()).isoformat(),
                 'extra_data': None,
                 'id': booking1.pk,
-- 
2.35.1

