1+ import pytz
2+ from datetime import datetime
3+
14from flask import Blueprint , jsonify , make_response , request
25from flask_jwt_extended import current_user , jwt_required
36from sqlalchemy .orm .exc import NoResultFound
47
58from app .api .auth import return_file
69from 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
914from app .api .helpers .order import calculate_order_amount , create_pdf_tickets_for_holder
1015from app .api .helpers .permission_manager import has_access
1116from app .api .helpers .storage import UPLOAD_PATHS , generate_hash
1217from app .api .schema .attendees import AttendeeSchema
1318from app .api .schema .orders import OrderSchema
1419from 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
1522from app .models import db
16- from app .models .discount_code import DiscountCode
1723from app .models .order import Order , OrderTicket
24+ from app .models .custom_form import CustomForms
25+ from app .models .discount_code import DiscountCode
1826from app .models .ticket import Ticket
1927from 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