Repository: fossasia/open-event-server

Issue:
sub issue : prevent order placement on potential overuse of discount code
Parent Issue : #5865 

This issue prevents the order to be placed in the case the discount codes will be potentially overused.


Discussion:

Resolving patch:
diff --git a/app/api/helpers/ticketing.py b/app/api/helpers/ticketing.py
--- a/app/api/helpers/ticketing.py
+++ b/app/api/helpers/ticketing.py
@@ -10,6 +10,7 @@
 from app.models import db
 from app.models.ticket_fee import TicketFees
 from app.models.ticket_holder import TicketHolder
+from app.models.order import Order
 from flask_jwt_extended import current_user
 
 
@@ -23,16 +24,16 @@ def get_order_expiry():
     @staticmethod
     def match_discount_quantity(discount_code, ticket_holders=None):
         qty = 0
-        old_holders = get_count(TicketHolder.query.filter(TicketHolder.ticket_id.in_(discount_code.tickets.split(","))))
-
+        ticket_ids = [ticket.id for ticket in discount_code.tickets]
+        old_holders = get_count(TicketHolder.query.filter(TicketHolder.ticket_id.in_(ticket_ids))
+                                .join(Order).filter(Order.status.in_(['completed', 'placed'])))
         for holder in ticket_holders:
             ticket_holder = TicketHolder.query.filter_by(id=holder).one()
-            if ticket_holder.ticket.id in discount_code.tickets.split(","):
+            if ticket_holder.ticket.id in ticket_ids:
                 qty += 1
         if (qty + old_holders) <= discount_code.tickets_number and \
             discount_code.min_quantity <= qty <= discount_code.max_quantity:
             return True
-
         return False
 
     @staticmethod
diff --git a/app/api/orders.py b/app/api/orders.py
--- a/app/api/orders.py
+++ b/app/api/orders.py
@@ -1,4 +1,5 @@
 import logging
+import pytz
 from datetime import datetime
 
 import omise
@@ -121,21 +122,20 @@ def before_create_object(self, data, view_kwargs):
         if not data.get('amount'):
             data['amount'] = 0
         # Apply discount only if the user is not event admin
-        if data.get('discount') and not has_access('is_coorganizer', event_id=data['event']):
-            discount_code = safe_query_without_soft_deleted_entries(self, DiscountCode, 'id', data['discount'],
+        if data.get('discount_code') and not has_access('is_coorganizer', event_id=data['event']):
+            discount_code = safe_query_without_soft_deleted_entries(self, DiscountCode, 'id', data['discount_code'],
                                                                     'discount_code_id')
             if not discount_code.is_active:
                 raise UnprocessableEntity({'source': 'discount_code_id'}, "Inactive Discount Code")
             else:
-                now = datetime.utcnow()
-                valid_from = datetime.strptime(discount_code.valid_from, '%Y-%m-%d %H:%M:%S')
-                valid_till = datetime.strptime(discount_code.valid_till, '%Y-%m-%d %H:%M:%S')
+                now = pytz.utc.localize(datetime.utcnow())
+                valid_from = discount_code.valid_from
+                valid_till = discount_code.valid_till
                 if not (valid_from <= now <= valid_till):
                     raise UnprocessableEntity({'source': 'discount_code_id'}, "Inactive Discount Code")
                 if not TicketingManager.match_discount_quantity(discount_code, data['ticket_holders']):
                     raise UnprocessableEntity({'source': 'discount_code_id'}, 'Discount Usage Exceeded')
-
-            if discount_code.event.id != data['event'] and discount_code.user_for == TICKET:
+            if discount_code.event.id != int(data['event']):
                 raise UnprocessableEntity({'source': 'discount_code_id'}, "Invalid Discount Code")
 
     def after_create_object(self, order, data, view_kwargs):


Test patch: