Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion app/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,18 @@
from app.api.helpers.mail import send_email_with_action, \
send_email_confirmation
from app.api.helpers.notification import send_notification_with_action
from app.api.helpers.order import create_pdf_tickets_for_holder
from app.api.helpers.order import create_pdf_tickets_for_holder, calculate_order_amount
from app.api.helpers.storage import UPLOAD_PATHS
from app.api.helpers.storage import generate_hash
from app.api.helpers.third_party_auth import GoogleOAuth, FbOAuth, TwitterOAuth, InstagramOAuth
from app.api.helpers.ticketing import TicketingManager
from app.api.helpers.utilities import get_serializer, str_generator
from app.api.helpers.permission_manager import has_access
from app.models import db
from app.models.mail import PASSWORD_RESET, PASSWORD_CHANGE, \
PASSWORD_RESET_AND_VERIFY
from app.models.notification import PASSWORD_CHANGE as PASSWORD_CHANGE_NOTIF
from app.models.discount_code import DiscountCode
from app.models.order import Order
from app.models.user import User
from app.models.event_invoice import EventInvoice
Expand Down Expand Up @@ -503,3 +505,18 @@ def resend_emails():
"Only placed and completed orders have confirmation").respond()
else:
return ForbiddenError({'source': ''}, "Co-Organizer Access Required").respond()


@ticket_blueprint.route('/orders/calculate-amount', methods=['POST'])
@jwt_required
def calculate_amount():
data = request.get_json()
tickets = data['tickets']
discount_code = None
if 'discount-code' in data:
discount_code_id = data['discount-code']
discount_code = safe_query(db, DiscountCode, 'id', discount_code_id, 'id')
if not TicketingManager.match_discount_quantity(discount_code, tickets, None):
return UnprocessableEntityError({'source': 'discount-code'}, 'Discount Usage Exceeded').respond()

return jsonify(calculate_order_amount(tickets, discount_code))
84 changes: 84 additions & 0 deletions app/api/helpers/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from app.api.helpers.storage import UPLOAD_PATHS
from app.models import db
from app.models.ticket import Ticket
from app.models.ticket_fee import TicketFees
from app.models.ticket_holder import TicketHolder
from app.models.order import OrderTicket

Expand Down Expand Up @@ -131,3 +132,86 @@ def create_onsite_attendees_for_order(data):

# delete from the data.
del data['on_site_tickets']


def calculate_order_amount(tickets, discount_code):
event = tax = tax_included = fees = None
total_amount = total_tax = total_discount = 0.0
ticket_list = []
for ticket_info in tickets:
tax_amount = tax_percent = 0.0
tax_data = {}
discount_amount = discount_percent = 0.0
discount_data = {}
sub_total = ticket_fee = 0.0

ticket_identifier = ticket_info['id']
quantity = ticket_info['quantity']
ticket = safe_query_without_soft_deleted_entries(db, Ticket, 'id', ticket_identifier, 'id')
if not event:
event = ticket.event
fees = TicketFees.query.filter_by(currency=event.payment_currency).first()
elif ticket.event.id != event.id:
raise UnprocessableEntity({'source': 'data/tickets'}, "Invalid Ticket")

if not tax and event.tax:
tax = event.tax
tax_included = tax.is_tax_included_in_price

if ticket.type == 'donation':
price = ticket_info.get('price')
if not price or price > ticket.max_price or price < ticket.min_price:
raise UnprocessableEntity({'source': 'data/tickets'}, "Price for donation ticket invalid")
else:
price = ticket.price

if discount_code:
for code in ticket.discount_codes:
if discount_code.id == code.id:
if code.type == 'amount':
discount_amount = code.value
discount_percent = (discount_amount / price) * 100
else:
discount_amount = (price * code.value)/100
discount_percent = code.value
discount_data = {
'code': discount_code.code,
'percent': round(discount_percent, 2),
'amount': round(discount_amount, 2)
}

if tax:
if not tax_included:
tax_amount = ((price - discount_amount) * tax.rate)/100
tax_percent = tax.rate
else:
tax_amount = ((price - discount_amount) * tax.rate)/(100 + tax.rate)
tax_percent = tax.rate
tax_data = {
'percent': round(tax_percent, 2),
'amount': round(tax_amount, 2),
}

total_tax = total_tax + tax_amount * quantity
total_discount = total_discount + discount_amount*quantity
if fees and not ticket.is_fee_absorbed:
ticket_fee = fees.service_fee * (price * quantity) / 100
if ticket_fee > fees.maximum_fee:
ticket_fee = fees.maximum_fee
if tax_included:
sub_total = ticket_fee + (price - discount_amount)*quantity
else:
sub_total = ticket_fee + (price + tax_amount - discount_amount)*quantity
total_amount = total_amount + sub_total
ticket_list.append({
'id': ticket.id,
'name': ticket.name,
'price': price,
'quantity': quantity,
'discount': discount_data,
'tax': tax_data,
'ticket_fee': round(ticket_fee, 2),
'sub_total': round(sub_total, 2)
})
return dict(tax_included=tax_included, total_amount=round(total_amount, 2), total_tax=round(total_tax, 2),
total_discount=round(total_discount, 2), tickets=ticket_list)
15 changes: 10 additions & 5 deletions app/api/helpers/ticketing.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@ def get_order_expiry():
return 10

@staticmethod
def match_discount_quantity(discount_code, ticket_holders=None):
def match_discount_quantity(discount_code, tickets=None, ticket_holders=None):
qty = 0
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 ticket_ids:
qty += 1
if ticket_holders:
for holder in ticket_holders:
ticket_holder = TicketHolder.query.filter_by(id=holder).one()
if ticket_holder.ticket.id in ticket_ids:
qty += 1
elif tickets:
for ticket in tickets:
if int(ticket['id']) in ticket_ids:
qty += ticket['quantity']
if (qty + old_holders) <= discount_code.tickets_number and \
discount_code.min_quantity <= qty <= discount_code.max_quantity:
return True
Expand Down
2 changes: 1 addition & 1 deletion app/api/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def before_create_object(self, data, view_kwargs):
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']):
if not TicketingManager.match_discount_quantity(discount_code, None, data['ticket_holders']):
raise UnprocessableEntity({'source': 'discount_code_id'}, 'Discount Usage Exceeded')
if discount_code.event.id != int(data['event']):
raise UnprocessableEntity({'source': 'discount_code_id'}, "Invalid Discount Code")
Expand Down