Skip to content

Commit bee05bf

Browse files
committed
fix:count query of sold tickets
1 parent 03fa0ea commit bee05bf

File tree

4 files changed

+70
-10
lines changed

4 files changed

+70
-10
lines changed

app/api/attendees.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from datetime import datetime
1+
import datetime
22

33
from flask import Blueprint, request, jsonify, abort, make_response
44
from flask_jwt_extended import current_user
55
from flask_rest_jsonapi import ResourceDetail, ResourceList, ResourceRelationship
66
from flask_rest_jsonapi.exceptions import ObjectNotFound
77
from sqlalchemy.orm.exc import NoResultFound
8+
from sqlalchemy import or_, and_
89

910
from app.api.bootstrap import api
1011
from app.api.helpers.db import safe_query, get_count
@@ -25,9 +26,27 @@
2526
from app.models.ticket_holder import TicketHolder
2627
from app.models.user import User
2728

29+
from app.settings import get_settings
30+
2831
attendee_misc_routes = Blueprint('attendee_misc', __name__, url_prefix='/v1')
2932

3033

34+
def get_sold_and_reserved_tickets_count(event_id):
35+
order_expiry_time = get_settings()['order_expiry_time']
36+
return db.session.query(TicketHolder.id).join(Order).filter(TicketHolder.order_id == Order.id) \
37+
.filter(Order.event_id == int(event_id),
38+
Order.deleted_at.is_(None),
39+
or_(Order.status == 'placed',
40+
Order.status == 'completed',
41+
and_(Order.status == 'initializing',
42+
Order.created_at + datetime.timedelta(
43+
minutes=order_expiry_time) > datetime.datetime.utcnow()),
44+
and_(Order.status == 'pending',
45+
Order.created_at + datetime.timedelta(
46+
minutes=30 + order_expiry_time) > (datetime.datetime.utcnow()))
47+
)).count()
48+
49+
3150
class AttendeeListPost(ResourceList):
3251
"""
3352
List and create Attendees through direct URL
@@ -56,8 +75,7 @@ def before_post(self, args, kwargs, data):
5675
"Ticket belongs to a different Event"
5776
)
5877
# Check if the ticket is already sold out or not.
59-
if get_count(db.session.query(TicketHolder.id).
60-
filter_by(ticket_id=int(data['ticket']), deleted_at=None)) >= ticket.quantity:
78+
if get_sold_and_reserved_tickets_count(ticket.event_id) >= ticket.quantity:
6179
raise ConflictException(
6280
{'pointer': '/data/attributes/ticket_id'},
6381
"Ticket already sold out"

app/factories/attendee.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,11 @@
77
from app.models.ticket_holder import db, TicketHolder
88

99

10-
class AttendeeFactory(factory.alchemy.SQLAlchemyModelFactory):
10+
class AttendeeFactoryBase(factory.alchemy.SQLAlchemyModelFactory):
1111
class Meta:
1212
model = TicketHolder
1313
sqlalchemy_session = db.session
1414

15-
event = factory.RelatedFactory(EventFactoryBasic)
16-
ticket = factory.RelatedFactory(TicketFactory)
17-
order = factory.RelatedFactory(OrderFactory)
1815
firstname = common.string_
1916
lastname = common.string_
2017
email = common.email_
@@ -29,3 +26,9 @@ class Meta:
2926
order_id = None
3027
created_at = common.date_
3128
modified_at = common.date_
29+
30+
31+
class AttendeeFactory(AttendeeFactoryBase):
32+
event = factory.RelatedFactory(EventFactoryBasic)
33+
ticket = factory.RelatedFactory(TicketFactory)
34+
order = factory.RelatedFactory(OrderFactory)

app/models/order.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def __init__(self,
9191
transaction_id=None,
9292
paid_via=None,
9393
is_billing_enabled=False,
94+
created_at=datetime.datetime.now(datetime.timezone.utc),
9495
user_id=None,
9596
discount_code_id=None,
9697
event_id=None,
@@ -114,7 +115,7 @@ def __init__(self,
114115
self.transaction_id = transaction_id
115116
self.paid_via = paid_via
116117
self.is_billing_enabled = is_billing_enabled
117-
self.created_at = datetime.datetime.now(datetime.timezone.utc)
118+
self.created_at = created_at
118119
self.discount_code_id = discount_code_id
119120
self.status = status
120121
self.payment_mode = payment_mode

tests/all/integration/api/helpers/test_order.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33

44
from app.settings import get_settings
55
from app import current_app as app, db
6-
from app.api.helpers.db import save_to_db
76
from app.api.helpers.order import set_expiry_for_order, delete_related_attendees_for_order
8-
from app.factories.attendee import AttendeeFactory
7+
import app.factories.common as common
8+
from app.factories.attendee import AttendeeFactoryBase, AttendeeFactory
99
from app.factories.event import EventFactoryBasic
10+
from app.factories.ticket import TicketFactory
1011
from app.factories.order import OrderFactory
1112
from app.models.order import Order
1213
from app.api.helpers.db import save_to_db
14+
from app.api.attendees import get_sold_and_reserved_tickets_count
1315
from tests.all.integration.setup_database import Setup
1416
from tests.all.integration.utils import OpenEventTestCase
1517

@@ -54,6 +56,42 @@ def test_should_delete_related_attendees(self):
5456
order = db.session.query(Order).filter(Order.id == obj.id).first()
5557
self.assertEqual(len(order.ticket_holders), 0)
5658

59+
def test_count_sold_and_reserved_tickets(self):
60+
"""Method to test the count query of sold tickets"""
61+
62+
with app.test_request_context():
63+
ticket = TicketFactory()
64+
65+
completed_order = OrderFactory(status='completed')
66+
placed_order = OrderFactory(status='placed')
67+
initializing_order = OrderFactory(status='initializing',
68+
created_at=datetime.utcnow() - timedelta(minutes=5))
69+
pending_order = OrderFactory(status='pending',
70+
created_at=datetime.utcnow() - timedelta(minutes=35))
71+
expired_time_order = OrderFactory(status='initializing', created_at=common.date_)
72+
expired_order = OrderFactory(status='expired')
73+
74+
db.session.commit()
75+
76+
# will not be counted as they have no order_id
77+
AttendeeFactoryBase.create_batch(2)
78+
# will be counted as attendee have valid orders
79+
AttendeeFactoryBase.create_batch(6, order_id=completed_order.id)
80+
# will be counted as attendee has valid placed order
81+
AttendeeFactoryBase(order_id=placed_order.id)
82+
# will be counted as attendee has initializing order under order expiry time
83+
AttendeeFactoryBase.create_batch(4, order_id=initializing_order.id)
84+
# will be counted as attendee has pending order under 30+order expiry time
85+
AttendeeFactoryBase.create_batch(2, order_id=pending_order.id)
86+
# will not be counted as the order is not under order expiry time
87+
AttendeeFactoryBase.create_batch(3, order_id=expired_time_order.id)
88+
# will not be counted as the order has an expired state
89+
AttendeeFactoryBase.create_batch(5, order_id=expired_order.id)
90+
91+
count = get_sold_and_reserved_tickets_count(ticket.event_id)
92+
93+
self.assertEqual(count, 13)
94+
5795

5896
if __name__ == '__main__':
5997
unittest.main()

0 commit comments

Comments
 (0)