Skip to content

Commit 2a043e3

Browse files
authored
feat: Endpoint to complete an order in new Orders API (#6720)
1 parent 6666331 commit 2a043e3

File tree

1 file changed

+162
-12
lines changed

1 file changed

+162
-12
lines changed

app/api/custom/orders.py

Lines changed: 162 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
1+
import pytz
2+
from datetime import datetime
3+
14
from flask import Blueprint, jsonify, make_response, request
25
from flask_jwt_extended import current_user, jwt_required
36
from sqlalchemy.orm.exc import NoResultFound
47

58
from app.api.auth import return_file
69
from app.api.helpers.db import get_count, safe_query
7-
from app.api.helpers.errors import ForbiddenError, NotFoundError, UnprocessableEntityError
8-
from app.api.helpers.mail import send_email_to_attendees
10+
from app.api.helpers.files import make_frontend_url
11+
from app.api.helpers.errors import ForbiddenError, NotFoundError, \
12+
UnprocessableEntityError
13+
from app.api.helpers.mail import send_email_to_attendees, send_order_cancel_email
914
from app.api.helpers.order import calculate_order_amount, create_pdf_tickets_for_holder
1015
from app.api.helpers.permission_manager import has_access
1116
from app.api.helpers.storage import UPLOAD_PATHS, generate_hash
1217
from app.api.schema.attendees import AttendeeSchema
1318
from app.api.schema.orders import OrderSchema
1419
from app.extensions.limiter import limiter
20+
from app.api.helpers.notification import send_notif_ticket_cancel, \
21+
send_notif_to_attendees, send_notif_ticket_purchase_organizer
1522
from app.models import db
16-
from app.models.discount_code import DiscountCode
1723
from app.models.order import Order, OrderTicket
24+
from app.models.custom_form import CustomForms
25+
from app.models.discount_code import DiscountCode
1826
from app.models.ticket import Ticket
1927
from app.models.ticket_holder import TicketHolder
2028

@@ -91,9 +99,8 @@ def resend_emails():
9199
)
92100
return jsonify(
93101
status=True,
94-
message="Verification emails for order : {} has been sent successfully".format(
95-
order_identifier
96-
),
102+
message="Verification emails for order : {} has been sent successfully"
103+
.format(order_identifier),
97104
)
98105
else:
99106
raise UnprocessableEntityError(
@@ -137,10 +144,8 @@ def create_order():
137144
ticket_ids = {int(ticket['id']) for ticket in tickets}
138145
quantity = {int(ticket['id']): ticket['quantity'] for ticket in tickets}
139146
ticket_list = (
140-
db.session.query(Ticket)
141-
.filter(Ticket.id.in_(ticket_ids))
142-
.filter_by(event_id=data['event_id'], deleted_at=None)
143-
.all()
147+
db.session.query(Ticket).filter(Ticket.id.in_(ticket_ids))
148+
.filter_by(event_id=data['event_id'], deleted_at=None).all()
144149
)
145150
ticket_ids_found = {ticket_information.id for ticket_information in ticket_list}
146151
tickets_not_found = ticket_ids - ticket_ids_found
@@ -175,6 +180,7 @@ def create_order():
175180
)
176181
db.session.add(attendee)
177182
attendee_list.append(attendee)
183+
# created_at not getting filled
178184
ticket_pricing = calculate_order_amount(tickets, discount_code)
179185
if not has_access('is_coorganizer', event_id=data['event_id']):
180186
data['status'] = 'initializing'
@@ -199,8 +205,6 @@ def create_order():
199205
else:
200206
order_tickets[holder.ticket_id] += 1
201207

202-
create_pdf_tickets_for_holder(order)
203-
204208
for ticket in order_tickets:
205209
od = OrderTicket(
206210
order_id=order.id, ticket_id=ticket, quantity=order_tickets[ticket]
@@ -213,3 +217,149 @@ def create_order():
213217
db.session.refresh(order)
214218
order_schema = OrderSchema()
215219
return order_schema.dump(order)
220+
221+
222+
@order_blueprint.route('/complete-order/<order_id>', methods=['PATCH'])
223+
@jwt_required
224+
def complete_order(order_id):
225+
data = request.get_json()
226+
for attribute in data:
227+
data[attribute.replace('-', '_')] = data.pop(attribute)
228+
order = Order.query.filter_by(id=order_id).first()
229+
order_schema = OrderSchema()
230+
if (not has_access('is_coorganizer', event_id=order.event_id)) and \
231+
(not current_user.id == order.user_id):
232+
return make_response(jsonify(status='Access Forbidden',
233+
error='You cannot update an order.'), 403)
234+
if has_access('is_coorganizer', event_id=order.event_id) and 'status' in data:
235+
if data['status'] != 'cancelled':
236+
return make_response(jsonify(status='Access Forbidden',
237+
error='You can only cancel an order.'), 403)
238+
elif data['status'] == 'cancelled':
239+
order.status = 'cancelled'
240+
db.session.add(order)
241+
attendees = db.session.query(TicketHolder).filter_by(
242+
order_id=order_id, deleted_at=None).all()
243+
for attendee in attendees:
244+
attendee.deleted_at = datetime.now(pytz.utc)
245+
db.session.add(attendee)
246+
db.session.commit()
247+
send_order_cancel_email(order)
248+
send_notif_ticket_cancel(order)
249+
return order_schema.dump(order)
250+
updated_attendees = data['attendees']
251+
for updated_attendee in updated_attendees:
252+
for attribute in updated_attendee:
253+
updated_attendee[attribute.replace('-', '_')] = updated_attendee \
254+
.pop(attribute)
255+
if get_count(db.session.query(TicketHolder).filter_by(order_id=order_id)) != \
256+
len(updated_attendees):
257+
return make_response(jsonify(status='Unprocessable Entity',
258+
error='You need to provide info of all attendees.')
259+
, 422)
260+
else:
261+
attendees = db.session.query(TicketHolder).filter_by(order_id=order_id,
262+
deleted_at=None).all()
263+
form_fields = db.session.query(CustomForms).filter_by(
264+
event_id=order.event_id, form='attendee', is_included=True,
265+
deleted_at=None).all()
266+
for attendee, updated_attendee in zip(attendees, updated_attendees):
267+
for field in form_fields:
268+
if field.is_required is True and field.field_identifier \
269+
not in updated_attendee:
270+
return make_response(jsonify(status='Unprocessable Entity',
271+
error='{} is a required field.'
272+
.format(field.field_identifier)), 422)
273+
if field.field_identifier in updated_attendee:
274+
setattr(attendee, field.field_identifier,
275+
updated_attendee[field.field_identifier])
276+
db.session.add(attendee)
277+
# modified_at not getting filled
278+
if order.amount == 0:
279+
order.status = 'completed'
280+
order.completed_at = datetime.utcnow()
281+
elif order.amount > 0:
282+
if 'payment_mode' not in data:
283+
return make_response(jsonify(status='Unprocessable Entity',
284+
error='Payment mode not specified.'), 422)
285+
if data['payment_mode'] in ['bank', 'cheque', 'onsite']:
286+
order.status = 'placed'
287+
order.completed_at = datetime.utcnow()
288+
else:
289+
order.status = 'pending'
290+
if 'is_billing_enabled' in data:
291+
if data['is_billing_enabled']:
292+
if ('company' not in data) or ('address' not in data) or \
293+
('city' not in data) or ('zipcode' not in data) or \
294+
('country' not in data):
295+
return make_response(jsonify(status='Unprocessable Entity',
296+
error='Billing information incomplete.')
297+
, 422)
298+
else:
299+
return make_response(jsonify(status='Unprocessable Entity',
300+
error='Billing information incomplete.')
301+
, 422)
302+
else:
303+
return make_response(jsonify(status='Unprocessable Entity',
304+
error='Billing information is mandatory '
305+
'for this order.'), 422)
306+
order.company = data['company']
307+
order.address = data['address']
308+
order.city = data['city']
309+
order.zipcode = data['zipcode']
310+
order.country = data['country']
311+
order.payment_mode = data['payment_mode']
312+
if 'state' in data:
313+
order.state = data['state']
314+
if 'tax_business_info' in data:
315+
order.tax_business_info = data['tax_business_info']
316+
db.session.add(order)
317+
db.session.commit()
318+
create_pdf_tickets_for_holder(order)
319+
if (order.status == 'completed' or order.status == 'placed') and \
320+
(order.deleted_at is None):
321+
order_identifier = order.identifier
322+
323+
key = UPLOAD_PATHS['pdf']['tickets_all'].format(identifier=order_identifier)
324+
ticket_path = (
325+
'generated/tickets/{}/{}/'.format(key, generate_hash(key))
326+
+ order_identifier
327+
+ '.pdf'
328+
)
329+
330+
key = UPLOAD_PATHS['pdf']['order'].format(identifier=order_identifier)
331+
invoice_path = (
332+
'generated/invoices/{}/{}/'.format(key, generate_hash(key))
333+
+ order_identifier
334+
+ '.pdf'
335+
)
336+
337+
# send email and notifications.
338+
send_email_to_attendees(
339+
order=order,
340+
purchaser_id=current_user.id,
341+
attachments=[ticket_path, invoice_path],
342+
)
343+
344+
send_notif_to_attendees(order, current_user.id)
345+
order_url = make_frontend_url(
346+
path='/orders/{identifier}'.format(identifier=order.identifier)
347+
)
348+
for organizer in order.event.organizers:
349+
send_notif_ticket_purchase_organizer(
350+
organizer,
351+
order.invoice_number,
352+
order_url,
353+
order.event.name,
354+
order.identifier,
355+
)
356+
if order.event.owner:
357+
send_notif_ticket_purchase_organizer(
358+
order.event.owner,
359+
order.invoice_number,
360+
order_url,
361+
order.event.name,
362+
order.identifier,
363+
)
364+
365+
return order_schema.dump(order)

0 commit comments

Comments
 (0)