Repository: fossasia/open-event-server

Issue:
No check for CFS and Ticket end date
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
Currently, the CFS end date and ticket end date can be more than the event end date. Proper error must be raised on such cases

**To Reproduce**
Steps to reproduce the behaviour:
1. Go to `Create Event`
2. Create an event with a ticket having end date greater than the event end date
3. Open CFS with end date more than the event end date
4. See error

**Expected behaviour**
<!-- A clear and concise description of what you expected to happen. -->
Proper check must be implemented

**Desktop (please complete the following information):**
 - OS: [e.g. iOS] Ubuntu 16.04
 - Browser [e.g. chrome, safari] Mozilla Firefox

**Additional context**
<!-- Add any other context about the problem here. -->
Working on it



Discussion:
@shreyanshdwivedi I am already working on this issue as opened by me in #2649
@Anupam-dagar I think your issue solely focuses on the changing of ticket sales-ends-at date once the end date of event changes. Title and description states this only. Whereas my issue is more of server side and focuses on checking and raising errors at time of event creation/updation. I'll be opening a PR on server for this

Resolving patch:
diff --git a/app/api/schema/speakers_calls.py b/app/api/schema/speakers_calls.py
--- a/app/api/schema/speakers_calls.py
+++ b/app/api/schema/speakers_calls.py
@@ -32,9 +32,20 @@ def validate_date(self, data, original_data):
             if 'ends_at' not in data:
                 data['ends_at'] = speakers_calls.ends_at
 
+            if 'event_starts_at' not in data:
+                data['event_starts_at'] = speakers_calls.event.starts_at
+
         if data['starts_at'] >= data['ends_at']:
             raise UnprocessableEntity({'pointer': '/data/attributes/ends-at'}, "ends-at should be after starts-at")
 
+        if 'event_starts_at' in data and data['starts_at'] > data['event_starts_at']:
+            raise UnprocessableEntity({'pointer': '/data/attributes/starts-at'},
+                                      "speakers-call starts-at should be before event starts-at")
+
+        if 'event_starts_at' in data and data['ends_at'] > data['event_starts_at']:
+            raise UnprocessableEntity({'pointer': '/data/attributes/ends-at'},
+                                      "speakers-call ends-at should be before event starts-at")
+
     id = fields.Str(dump_only=True)
     announcement = fields.Str(required=True)
     starts_at = fields.DateTime(required=True)
diff --git a/app/api/schema/tickets.py b/app/api/schema/tickets.py
--- a/app/api/schema/tickets.py
+++ b/app/api/schema/tickets.py
@@ -30,10 +30,21 @@ def validate_date(self, data, original_data):
             if 'sales_ends_at' not in data:
                 data['sales_ends_at'] = ticket.sales_ends_at
 
+            if 'event_ends_at' not in data:
+                data['event_ends_at'] = ticket.event.ends_at
+
         if data['sales_starts_at'] >= data['sales_ends_at']:
             raise UnprocessableEntity({'pointer': '/data/attributes/sales-ends-at'},
                                       "sales-ends-at should be after sales-starts-at")
 
+        if 'event_ends_at' in data and data['sales_starts_at'] > data['event_ends_at']:
+            raise UnprocessableEntity({'pointer': '/data/attributes/sales-starts-at'},
+                                      "ticket sales-starts-at should be before event ends-at")
+
+        if 'event_ends_at' in data and data['sales_ends_at'] > data['event_ends_at']:
+            raise UnprocessableEntity({'pointer': '/data/attributes/sales-ends-at'},
+                                      "ticket sales-ends-at should be before event ends-at")
+
     @validates_schema
     def validate_quantity(self, data):
         if 'max_order' in data and 'min_order' in data:


Test patch:
diff --git a/tests/all/integration/api/validation/test_speakers_call.py b/tests/all/integration/api/validation/test_speakers_call.py
--- a/tests/all/integration/api/validation/test_speakers_call.py
+++ b/tests/all/integration/api/validation/test_speakers_call.py
@@ -27,7 +27,8 @@ def test_date_pass(self):
         }
         data = {
             'starts_at': datetime(2003, 8, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
-            'ends_at': datetime(2003, 9, 4, 12, 30, 45).replace(tzinfo=timezone('UTC'))
+            'ends_at': datetime(2003, 9, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'event_starts_at': datetime(2003, 9, 10, 12, 30, 45).replace(tzinfo=timezone('UTC'))
         }
         SpeakersCallSchema.validate_date(schema, data, original_data)
 
@@ -42,7 +43,42 @@ def test_date_start_gt_end(self):
         }
         data = {
             'starts_at': datetime(2003, 9, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
-            'ends_at': datetime(2003, 8, 4, 12, 30, 45).replace(tzinfo=timezone('UTC'))
+            'ends_at': datetime(2003, 8, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'event_starts_at': datetime(2003, 9, 10, 12, 30, 45).replace(tzinfo=timezone('UTC'))
+        }
+        with self.assertRaises(UnprocessableEntity):
+            SpeakersCallSchema.validate_date(schema, data, original_data)
+
+    def test_date_start_gt_event_end(self):
+        """
+        Speakers Call Validate Date - Tests if exception is raised when speakers_call starts_at is after event starts_at
+        :return:
+        """
+        schema = SpeakersCallSchema()
+        original_data = {
+            'data': {}
+        }
+        data = {
+            'starts_at': datetime(2003, 9, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'ends_at': datetime(2003, 9, 10, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'event_starts_at': datetime(2003, 9, 2, 12, 30, 45).replace(tzinfo=timezone('UTC'))
+        }
+        with self.assertRaises(UnprocessableEntity):
+            SpeakersCallSchema.validate_date(schema, data, original_data)
+
+    def test_date_end_gt_event_end(self):
+        """
+        Speakers Call Validate Date - Tests if exception is raised when speakers_call ends_at is after event starts_at
+        :return:
+        """
+        schema = SpeakersCallSchema()
+        original_data = {
+            'data': {}
+        }
+        data = {
+            'starts_at': datetime(2003, 9, 2, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'ends_at': datetime(2003, 9, 10, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'event_starts_at': datetime(2003, 9, 5, 12, 30, 45).replace(tzinfo=timezone('UTC'))
         }
         with self.assertRaises(UnprocessableEntity):
             SpeakersCallSchema.validate_date(schema, data, original_data)
diff --git a/tests/all/integration/api/validation/test_tickets.py b/tests/all/integration/api/validation/test_tickets.py
--- a/tests/all/integration/api/validation/test_tickets.py
+++ b/tests/all/integration/api/validation/test_tickets.py
@@ -27,7 +27,8 @@ def test_date_pass(self):
         }
         data = {
             'sales_starts_at': datetime(2003, 8, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
-            'sales_ends_at': datetime(2003, 9, 4, 12, 30, 45).replace(tzinfo=timezone('UTC'))
+            'sales_ends_at': datetime(2003, 9, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'event_ends_at': datetime(2003, 9, 10, 12, 30, 45).replace(tzinfo=timezone('UTC'))
         }
         TicketSchema.validate_date(schema, data, original_data)
 
@@ -42,7 +43,42 @@ def test_date_start_gt_end(self):
         }
         data = {
             'sales_starts_at': datetime(2003, 9, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
-            'sales_ends_at': datetime(2003, 8, 4, 12, 30, 45).replace(tzinfo=timezone('UTC'))
+            'sales_ends_at': datetime(2003, 8, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'event_ends_at': datetime(2003, 8, 10, 12, 30, 45).replace(tzinfo=timezone('UTC'))
+        }
+        with self.assertRaises(UnprocessableEntity):
+            TicketSchema.validate_date(schema, data, original_data)
+
+    def test_date_start_gt_event_end(self):
+        """
+        Tickets Validate Date - Tests if exception is raised when sales_starts_at is after event ends_at
+        :return:
+        """
+        schema = TicketSchema()
+        original_data = {
+            'data': {}
+        }
+        data = {
+            'sales_starts_at': datetime(2003, 8, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'sales_ends_at': datetime(2003, 9, 4, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'event_ends_at': datetime(2003, 8, 2, 12, 30, 45).replace(tzinfo=timezone('UTC'))
+        }
+        with self.assertRaises(UnprocessableEntity):
+            TicketSchema.validate_date(schema, data, original_data)
+
+    def test_date_end_gt_event_end(self):
+        """
+        Tickets Validate Date - Tests if exception is raised when sales_ends_at is after event ends_at
+        :return:
+        """
+        schema = TicketSchema()
+        original_data = {
+            'data': {}
+        }
+        data = {
+            'sales_starts_at': datetime(2003, 8, 1, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'sales_ends_at': datetime(2003, 8, 10, 12, 30, 45).replace(tzinfo=timezone('UTC')),
+            'event_ends_at': datetime(2003, 8, 2, 12, 30, 45).replace(tzinfo=timezone('UTC'))
         }
         with self.assertRaises(UnprocessableEntity):
             TicketSchema.validate_date(schema, data, original_data)