Repository: fossasia/open-event-server

Issue:
Delete ticket holders after order_expiry_time + created_at if they don't have order_id
Sometimes, some dummy ticket holders get created with no order_id, it'll take time to create new order API, hence for now, after every `order_expiry_time`, a function should run which will delete all ticket holders with no order_id which have `created_at + order_expiry_time < now`


Discussion:
@iamareebjamal Taking this up, Do i have to write a job for that?

`hence for now, after every order_expiry_time` how will I know that?
It's in the settings

Resolving patch:
diff --git a/app/__init__.py b/app/__init__.py
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -41,7 +41,8 @@
 from app.api.helpers.auth import AuthManager, is_token_blacklisted
 from app.api.helpers.scheduled_jobs import send_after_event_mail, send_event_fee_notification, \
     send_event_fee_notification_followup, change_session_state_on_event_completion, \
-    expire_pending_tickets, send_monthly_event_invoice, event_invoices_mark_due
+    expire_pending_tickets, send_monthly_event_invoice, event_invoices_mark_due, \
+    delete_ticket_holders_no_order_id
 from app.models.event import Event
 from app.models.role_invite import RoleInvite
 from app.views.healthcheck import health_check_celery, health_check_db, health_check_migrations, check_migrations
@@ -274,6 +275,7 @@ def update_sent_state(sender=None, headers=None, **kwargs):
 scheduler.add_job(expire_pending_tickets, 'cron', minute=45)
 scheduler.add_job(send_monthly_event_invoice, 'cron', day=1, month='1-12')
 scheduler.add_job(event_invoices_mark_due, 'cron', hour=5)
+scheduler.add_job(delete_ticket_holders_no_order_id, 'cron', minute=5)
 scheduler.start()
 
 
diff --git a/app/api/helpers/scheduled_jobs.py b/app/api/helpers/scheduled_jobs.py
--- a/app/api/helpers/scheduled_jobs.py
+++ b/app/api/helpers/scheduled_jobs.py
@@ -21,6 +21,7 @@
 from app.models.session import Session
 from app.models.ticket import Ticket
 from app.models.ticket_fee import TicketFees, get_fee
+from app.models.ticket_holder import TicketHolder
 
 from app.settings import get_settings
 
@@ -155,6 +156,16 @@ def expire_pending_tickets():
         db.session.commit()
 
 
+def delete_ticket_holders_no_order_id():
+    from app import current_app as app
+    with app.app_context():
+        order_expiry_time = get_settings()['order_expiry_time']
+        TicketHolder.query.filter(TicketHolder.order_id == None, TicketHolder.deleted_at.is_(None),
+                                  TicketHolder.created_at + datetime.timedelta(minutes=order_expiry_time)
+                                  < datetime.datetime.utcnow()).delete(synchronize_session=False)
+        db.session.commit()
+
+
 def event_invoices_mark_due():
     from app import current_app as app
     with app.app_context():
diff --git a/app/factories/attendee.py b/app/factories/attendee.py
--- a/app/factories/attendee.py
+++ b/app/factories/attendee.py
@@ -2,6 +2,8 @@
 
 import app.factories.common as common
 from app.factories.event import EventFactoryBasic
+from app.factories.ticket import TicketFactory
+from app.factories.order import OrderFactory
 from app.models.ticket_holder import db, TicketHolder
 
 
@@ -11,6 +13,8 @@ class Meta:
         sqlalchemy_session = db.session
 
     event = factory.RelatedFactory(EventFactoryBasic)
+    ticket = factory.RelatedFactory(TicketFactory)
+    order = factory.RelatedFactory(OrderFactory)
     firstname = common.string_
     lastname = common.string_
     email = common.email_
@@ -22,3 +26,6 @@ class Meta:
     pdf_url = common.url_
     event_id = 1
     ticket_id = None
+    order_id = None
+    created_at = common.date_
+    modified_at = common.date_


Test patch:
diff --git a/tests/all/integration/api/helpers/test_scheduled_jobs.py b/tests/all/integration/api/helpers/test_scheduled_jobs.py
--- a/tests/all/integration/api/helpers/test_scheduled_jobs.py
+++ b/tests/all/integration/api/helpers/test_scheduled_jobs.py
@@ -2,8 +2,10 @@
 
 from app import current_app as app, db
 from app.factories.event_invoice import EventInvoiceFactory
+from app.factories.attendee import AttendeeFactory
 from app.models.event_invoice import EventInvoice
-from app.api.helpers.scheduled_jobs import event_invoices_mark_due
+from app.models.ticket_holder import TicketHolder
+from app.api.helpers.scheduled_jobs import event_invoices_mark_due, delete_ticket_holders_no_order_id
 
 from tests.all.integration.utils import OpenEventTestCase
 
@@ -29,3 +31,43 @@ def test_event_invoices_mark_due(self):
 
             self.assertEqual(status_new, "due")
             self.assertNotEqual(status_paid, "due")
+
+    def test_delete_ticket_holders_with_no_order_id(self):
+        """Method to test deleting ticket holders with no order id after expiry time"""
+
+        with app.test_request_context():
+            attendee = AttendeeFactory()
+            db.session.commit()
+            attendee_id = attendee.id
+            delete_ticket_holders_no_order_id()
+            ticket_holder = TicketHolder.query.get(attendee_id)
+            self.assertIs(ticket_holder, None)
+
+    def test_delete_ticket_holder_created_currently(self):
+        """Method to test not deleting ticket holders with no order id but created within expiry time"""
+
+        with app.test_request_context():
+            attendee = AttendeeFactory(created_at=datetime.datetime.utcnow(),
+                                       modified_at=datetime.datetime.utcnow())
+
+            db.session.commit()
+            attendee_id = attendee.id
+            delete_ticket_holders_no_order_id()
+            ticket_holder = TicketHolder.query.get(attendee_id)
+            self.assertIsNot(ticket_holder, None)
+
+    def test_delete_ticket_holder_with_valid_order_id(self):
+        """Method to test not deleting ticket holders with order id after expiry time"""
+
+        with app.test_request_context():
+            attendee = AttendeeFactory(order_id=1, ticket_id=1,
+                                       created_at=datetime.datetime.utcnow() -
+                                       datetime.timedelta(minutes=15),
+                                       modified_at=datetime.datetime.utcnow() -
+                                       datetime.timedelta(minutes=15))
+
+            db.session.commit()
+            attendee_id = attendee.id
+            delete_ticket_holders_no_order_id()
+            ticket_holder = TicketHolder.query.get(attendee_id)
+            self.assertIsNot(ticket_holder, None)