From 85c5e57def6b09b5a5fa0c2787e84de91e0796ab Mon Sep 17 00:00:00 2001 From: iamareebjamal Date: Wed, 13 May 2020 22:39:05 +0530 Subject: [PATCH] Add basic discount codes test --- app/api/events.py | 2 +- app/api/helpers/order.py | 21 +++-- app/api/helpers/ticketing.py | 13 +-- .../order/test_calculate_order_amount.py | 89 +++++++++++++++++-- tests/factories/discount_code.py | 15 +++- 5 files changed, 113 insertions(+), 27 deletions(-) diff --git a/app/api/events.py b/app/api/events.py index bac6021677..dfd04a16e0 100644 --- a/app/api/events.py +++ b/app/api/events.py @@ -385,7 +385,7 @@ def after_create_object(self, event, data, view_kwargs): role_name=role.title_name, event=event, role=role, - status='accepted' + status='accepted', ) save_to_db(role_invite, 'Owner Role Invite Added') diff --git a/app/api/helpers/order.py b/app/api/helpers/order.py index 83ef1e2f37..4d2ab38b62 100644 --- a/app/api/helpers/order.py +++ b/app/api/helpers/order.py @@ -197,7 +197,7 @@ def calculate_order_amount(tickets, discount_code=None): ticket_list = [] for ticket_info in tickets: discount_amount = 0.0 - discount_data = {} + discount_data = None ticket_fee = 0.0 ticket_identifier = ticket_info['id'] @@ -209,11 +209,15 @@ def calculate_order_amount(tickets, discount_code=None): event = ticket.event if event.deleted_at: - raise ObjectNotFound({'pointer': 'tickets/event'}, f'Event: {event.id} not found') + raise ObjectNotFound( + {'pointer': 'tickets/event'}, f'Event: {event.id} not found' + ) fees = TicketFees.query.filter_by(currency=event.payment_currency).first() elif ticket.event.id != event.id: - raise UnprocessableEntity({'pointer': 'tickets'}, "All tickets must belong to same event") + raise UnprocessableEntity( + {'pointer': 'tickets'}, "All tickets must belong to same event" + ) if not tax and event.tax: tax = event.tax @@ -223,8 +227,9 @@ def calculate_order_amount(tickets, discount_code=None): price = ticket_info.get('price') if not price or price > ticket.max_price or price < ticket.min_price: raise UnprocessableEntity( - {'pointer': 'tickets/price'}, f"Price for donation ticket should be present and within range " - f"{ticket.min_price} to {ticket.max_price}" + {'pointer': 'tickets/price'}, + f"Price for donation ticket should be present and within range " + f"{ticket.min_price} to {ticket.max_price}", ) else: price = ticket.price @@ -242,9 +247,11 @@ def calculate_order_amount(tickets, discount_code=None): 'code': discount_code.code, 'percent': round(discount_percent, 2), 'amount': round(discount_amount, 2), + 'total': round(discount_amount * quantity, 2), } + break - total_discount = total_discount + discount_amount * quantity + total_discount += round(discount_amount * quantity, 2) if fees and not ticket.is_fee_absorbed: ticket_fee = fees.service_fee * (price * quantity) / 100 if ticket_fee > fees.maximum_fee: @@ -265,7 +272,7 @@ def calculate_order_amount(tickets, discount_code=None): sub_total = total_amount if tax: - total_tax = total_amount * tax.rate/100 + total_tax = total_amount * tax.rate / 100 if not tax_included: total_amount += total_tax diff --git a/app/api/helpers/ticketing.py b/app/api/helpers/ticketing.py index f7c78a4c30..1764f207c8 100644 --- a/app/api/helpers/ticketing.py +++ b/app/api/helpers/ticketing.py @@ -23,10 +23,6 @@ class TicketingManager: """All ticketing and orders related helper functions""" - @staticmethod - def get_order_expiry(): - return 10 - @staticmethod def match_discount_quantity(discount_code, tickets=None, ticket_holders=None): qty = 0 @@ -44,14 +40,13 @@ def match_discount_quantity(discount_code, tickets=None, ticket_holders=None): elif tickets: for ticket in tickets: if int(ticket['id']) in ticket_ids: - qty += ticket['quantity'] - if ( + qty += ticket.get('quantity', 1) + return ( (qty + old_holders) <= discount_code.tickets_number and discount_code.min_quantity <= qty <= discount_code.max_quantity - ): - return True - return False + ) + # TODO(Areeb): Remove after validating logic @staticmethod def calculate_update_amount(order): discount = None diff --git a/tests/all/integration/api/helpers/order/test_calculate_order_amount.py b/tests/all/integration/api/helpers/order/test_calculate_order_amount.py index 0b63d5bbe3..2eeb11ac87 100644 --- a/tests/all/integration/api/helpers/order/test_calculate_order_amount.py +++ b/tests/all/integration/api/helpers/order/test_calculate_order_amount.py @@ -5,6 +5,7 @@ from app.api.helpers.exceptions import UnprocessableEntity from app.api.helpers.order import calculate_order_amount +from tests.factories.discount_code import DiscountCodeTicketSubFactory from tests.factories.event import EventFactoryBasic from tests.factories.tax import TaxSubFactory from tests.factories.ticket import TicketSubFactory @@ -40,7 +41,10 @@ def test_single_ticket(db): def _create_ticket_dict(tickets, quantities): - return [dict(id=ticket.id, quantity=quantity) for ticket, quantity in zip(tickets, quantities)] + return [ + dict(id=ticket.id, quantity=quantity) + for ticket, quantity in zip(tickets, quantities) + ] def test_multiple_tickets_different_event(db): @@ -51,7 +55,9 @@ def test_multiple_tickets_different_event(db): ticket_dict = _create_ticket_dict([ticket1, ticket2], [1, 2]) - with pytest.raises(UnprocessableEntity, match='All tickets must belong to same event'): + with pytest.raises( + UnprocessableEntity, match='All tickets must belong to same event' + ): calculate_order_amount(ticket_dict) @@ -89,15 +95,20 @@ def _create_tickets(prices, **kwargs): def _create_donation_tickets(db): event = EventFactoryBasic() tickets = _create_tickets([10, 20], event=event) - tickets.append(TicketSubFactory(type='donation', max_price=20, min_price=10, event=event)) + tickets.append( + TicketSubFactory(type='donation', max_price=20, min_price=10, event=event) + ) db.session.commit() return _create_ticket_dict(tickets, [3, 1, 2]) def _expect_donation_error(ticket_dict): - with pytest.raises(UnprocessableEntity, match='Price for donation ticket should be present and within range ' - '10.0 to 20.0'): + with pytest.raises( + UnprocessableEntity, + match='Price for donation ticket should be present and within range ' + '10.0 to 20.0', + ): calculate_order_amount(ticket_dict) @@ -142,7 +153,11 @@ def test_donation_ticket(db): def _create_taxed_tickets(db, tax_included=True): tax = TaxSubFactory(rate=18.0, is_tax_included_in_price=tax_included) tickets = _create_tickets([123.5, 456.3], event=tax.event) - tickets.append(TicketSubFactory(type='donation', event=tax.event, min_price=500.0, max_price=1000.0)) + tickets.append( + TicketSubFactory( + type='donation', event=tax.event, min_price=500.0, max_price=1000.0 + ) + ) db.session.commit() tickets_dict = _create_ticket_dict(tickets, [2, 4, 3]) @@ -177,6 +192,68 @@ def test_tax_excluded(db): assert amount_data['discount'] == 0.0 +def test_discount_code(db): + ticket = TicketSubFactory(price=100.0) + discount_code = DiscountCodeTicketSubFactory( + type='percent', value=10.0, tickets=[ticket] + ) + db.session.commit() + + amount_data = calculate_order_amount([{'id': ticket.id}], discount_code) + + assert amount_data['total'] == 90.0 + assert amount_data['tax_included'] is None + assert amount_data['tax'] == 0.0 + assert amount_data['discount'] == 10.0 + ticket_dict = amount_data['tickets'][0] + assert ticket_dict['id'] == ticket.id + assert ticket_dict['name'] == ticket.name + assert ticket_dict['price'] == ticket.price + assert ticket_dict['quantity'] == 1 + assert ticket_dict['ticket_fee'] == 0.0 + assert ticket_dict['sub_total'] == 90.0 + assert ticket_dict['discount']['total'] == 10.0 + assert ticket_dict['discount']['amount'] == 10.0 + assert ticket_dict['discount']['percent'] == 10.0 + assert ticket_dict['discount']['code'] == discount_code.code + + +def test_multiple_tickets_discount(db): + ticket_a = TicketSubFactory(price=50.0) + ticket_b = TicketSubFactory(price=495.8, event=ticket_a.event) + ticket_c = TicketSubFactory(price=321.3, event=ticket_a.event) + + discount = DiscountCodeTicketSubFactory( + type='percent', value=50.0, tickets=[ticket_a, ticket_b] + ) + DiscountCodeTicketSubFactory(type='amount', value=100.0, tickets=[ticket_c]) + + db.session.commit() + + tickets_dict = _create_ticket_dict([ticket_a, ticket_b, ticket_c], [2, 3, 1]) + + amount_data = calculate_order_amount(tickets_dict, discount) + + assert amount_data['total'] == 1115.0 + assert amount_data['discount'] == 793.7 + assert amount_data['tickets'][0]['quantity'] == 2 + assert amount_data['tickets'][0]['price'] == 50.0 + assert amount_data['tickets'][0]['sub_total'] == 50.0 + assert amount_data['tickets'][0]['discount']['total'] == 50.0 + assert amount_data['tickets'][0]['discount']['amount'] == 25.0 + assert amount_data['tickets'][0]['discount']['percent'] == 50.0 + assert amount_data['tickets'][1]['quantity'] == 3 + assert amount_data['tickets'][1]['price'] == 495.8 + assert amount_data['tickets'][1]['sub_total'] == 743.7 + assert amount_data['tickets'][1]['discount']['total'] == 743.7 + assert amount_data['tickets'][1]['discount']['amount'] == 247.9 + assert amount_data['tickets'][1]['discount']['percent'] == 50.0 + assert amount_data['tickets'][2]['quantity'] == 1 + assert amount_data['tickets'][2]['price'] == 321.3 + assert amount_data['tickets'][2]['sub_total'] == 321.3 + assert amount_data['tickets'][2]['discount'] is None + + def test_deleted_ticket(db): ticket = TicketSubFactory(deleted_at=datetime.now()) db.session.commit() diff --git a/tests/factories/discount_code.py b/tests/factories/discount_code.py index 13ae43ed0f..47d2c50b84 100644 --- a/tests/factories/discount_code.py +++ b/tests/factories/discount_code.py @@ -19,7 +19,7 @@ class Meta: type = "amount" is_active = True tickets_number = 30 - min_quantity = 10 + min_quantity = 1 max_quantity = 20 valid_from = common.date_ valid_till = common.dateEnd_ @@ -28,12 +28,10 @@ class Meta: event_id = None -class DiscountCodeTicketFactory(BaseFactory): +class DiscountCodeTicketFactoryBase(BaseFactory): class Meta: model = DiscountCode - marketer = factory.RelatedFactory(UserFactory) - tickets = factory.RelatedFactory(TicketFactory) code = common.string_ discount_url = common.url_ value = common.float_ @@ -45,5 +43,14 @@ class Meta: valid_from = common.date_ valid_till = common.dateEnd_ used_for = "ticket" + + +class DiscountCodeTicketSubFactory(DiscountCodeTicketFactoryBase): + tickets = factory.SubFactory(TicketFactory) + + +class DiscountCodeTicketFactory(DiscountCodeTicketFactoryBase): + marketer = factory.RelatedFactory(UserFactory) + tickets = factory.RelatedFactory(TicketFactory) marketer_id = 1 event_id = None