def form_valid(self, form): if "confirm_cancel" in form.data: ticket_booking = form.save(commit=False) err_msg = self.check_and_redirect(self.request, ticket_booking) if err_msg: messages.info(self.request, err_msg) return HttpResponseRedirect(reverse('booking:ticket_bookings')) ticket_booking.cancelled = True ticket_booking.save() ActivityLog.objects.create( log='Ticket booking ref {} (for {}) has been cancelled by ' 'user {}'.format( ticket_booking.booking_reference, ticket_booking.ticketed_event, ticket_booking.user.username, ) ) try: # send email and set messages host = 'http://{}'.format(self.request.META.get('HTTP_HOST')) # send email to user; no need to send to studio as cancelled # before payment ctx = { 'host': host, 'ticket_booking': ticket_booking, } send_mail('{} Ticket booking ref {} cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticket_booking.booking_reference, ), get_template( 'booking/email/ticket_booking_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [self.request.user.email], html_message=get_template( 'booking/email/ticket_booking_cancelled.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "TicketBookingCancelView - user email" ) messages.error( self.request, "An error occured, please contact the studio for " "information" ) messages.success( self.request, self.success_message.format( ticket_booking.booking_reference ) ) return HttpResponseRedirect(self.get_success_url())
def form_valid(self, form): if "confirm_cancel" in form.data: ticket_booking = form.save(commit=False) err_msg = self.check_and_redirect(self.request, ticket_booking) if err_msg: messages.info(self.request, err_msg) return HttpResponseRedirect(reverse('booking:ticket_bookings')) ticket_booking.cancelled = True ticket_booking.save() ActivityLog.objects.create( log='Ticket booking ref {} (for {}) has been cancelled by ' 'user {}'.format( ticket_booking.booking_reference, ticket_booking.ticketed_event, ticket_booking.user.username, )) try: # send email and set messages host = 'http://{}'.format(self.request.META.get('HTTP_HOST')) # send email to user; no need to send to studio as cancelled # before payment ctx = { 'host': host, 'ticket_booking': ticket_booking, } send_mail( '{} Ticket booking ref {} cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticket_booking.booking_reference, ), get_template('booking/email/ticket_booking_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [self.request.user.email], html_message=get_template( 'booking/email/ticket_booking_cancelled.html').render( ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "TicketBookingCancelView - user email") messages.error( self.request, "An error occured, please contact the studio for " "information") messages.success( self.request, self.success_message.format(ticket_booking.booking_reference)) return HttpResponseRedirect(self.get_success_url())
def _email_free_class_request(request, booking, booking_status): # if user is requesting a free class, send email to studio and # make booking unpaid (admin will update) ActivityLog.objects.create( log='Free class requested ({}) by user {}'.format( booking.event, request.user.username) ) booking.free_class_requested = True booking.paid = False booking.payment_confirmed = False booking.block = None try: # send email and set messages host = 'http://{}'.format(request.META.get('HTTP_HOST')) # send email to studio ctx = { 'host': host, 'event': booking.event, 'user': request.user, 'booking_status': booking_status, } send_mail('{} Request to claim free class from {} {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, request.user.first_name, request.user.last_name ), get_template( 'studioadmin/email/free_class_request_to_studio.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'studioadmin/email/free_class_request_to_studio.html' ).render(ctx), fail_silently=False) messages.success( request, "Your request to claim {} as a free class has been " "sent to the studio. Your booking has been " "provisionally made and your place will be secured once " "your request has been approved.".format(booking.event) ) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "UpdateBookingView - claim free class email") messages.error(request, "An error occured, please contact " "the studio for information")
def _send_confirmation_email(self, request, ticket_booking, action): try: # send confirmation email host = 'http://{}'.format(request.META.get('HTTP_HOST')) # send email to studio ctx = { 'host': host, 'ticketed_event': self.ticketed_event, 'action': action, } send_mail('{} Your ticket booking ref {} for {} has been {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticket_booking.booking_reference, self.ticketed_event, action ), get_template( 'studioadmin/email/ticket_booking_change_confirmation.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [ticket_booking.user.email], html_message=get_template( 'studioadmin/email/ticket_booking_change_confirmation.html' ).render(ctx), fail_silently=False) send_confirmation_msg = "Confirmation email was sent to " \ "user {}.".format( ticket_booking.user.username ) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "ticketed_event_booking_list - " "send confirmation email" ) send_confirmation_msg = "There was a " \ "problem sending the confirmation email to " \ "user {}. Tech support has been notified.".format( ticket_booking.user.username ) return send_confirmation_msg
def _send_confirmation_email(self, request, ticket_booking, action): try: # send confirmation email host = 'http://{}'.format(request.META.get('HTTP_HOST')) # send email to studio ctx = { 'host': host, 'ticketed_event': self.ticketed_event, 'action': action, } send_mail( '{} Your ticket booking ref {} for {} has been {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticket_booking.booking_reference, self.ticketed_event, action), get_template( 'studioadmin/email/ticket_booking_change_confirmation.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [ticket_booking.user.email], html_message=get_template( 'studioadmin/email/ticket_booking_change_confirmation.html' ).render(ctx), fail_silently=False) send_confirmation_msg = "Confirmation email was sent to " \ "user {}.".format( ticket_booking.user.username ) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "ticketed_event_booking_list - " "send confirmation email") send_confirmation_msg = "There was a " \ "problem sending the confirmation email to " \ "user {}. Tech support has been notified.".format( ticket_booking.user.username ) return send_confirmation_msg
def user_bookings_view(request, user_id, booking_status='future'): user = get_object_or_404(User, id=user_id) if request.method == 'POST': booking_status = request.POST.getlist('booking_status')[0] userbookingformset = UserBookingFormSet( request.POST.copy(), instance=user, user=user, ) if userbookingformset.is_valid(): if not userbookingformset.has_changed() and \ request.POST.get('formset_submitted'): messages.info(request, "No changes were made") else: for form in userbookingformset: if form.is_valid(): if form.has_changed(): if form.changed_data == ['send_confirmation']: messages.info( request, "'Send confirmation' checked for '{}' " "but no changes were made; email has not been " "sent to user.".format(form.instance.event)) else: extra_msgs = [] # these will be displayed as a list in the email to the user booking = form.save(commit=False) event_was_full = booking.event.spaces_left == 0 action = 'updated' if form.instance.id else 'created' transfer_block_created = False block_removed = False if 'status' in form.changed_data and action == 'updated': if booking.status == 'CANCELLED': if booking.block: booking.block = None block_removed = True elif booking.paid \ and booking.event.event_type.event_type != 'EV': block_type, _ = BlockType.objects.get_or_create( event_type=booking.event.event_type, size=1, cost=0, duration=1, identifier='transferred', active=False ) Block.objects.create( block_type=block_type, user=booking.user, transferred_booking_id=booking.id ) transfer_block_created = True booking.deposit_paid = False booking.paid = False booking.payment_confirmed = False booking.free_class = False action = 'cancelled' elif booking.status == 'OPEN': action = 'reopened' extra_msgs.append("Booking status changed " "to {}".format(action) ) elif 'no_show' in form.changed_data \ and action == 'updated' \ and booking.status == 'OPEN': action = 'cancelled' if booking.no_show \ else 'reopened' extra_msgs.append( "Booking {} as 'no-show'".format(action) ) if booking.block: booking.paid = True booking.payment_confirmed = True elif 'block' in form.changed_data: booking.block = None booking.paid = False booking.payment_confirmed = False # check for existence of free child block on pre-saved booking has_free_block_pre_save = False if booking.block and booking.block.children.exists(): has_free_block_pre_save = True if 'deposit_paid' in form.changed_data: if booking.deposit_paid: extra_msgs.append( "Booking payment status changed to " "'deposit paid'" ) if 'paid' in form.changed_data: if booking.paid: # assume that if booking is being done via # studioadmin, marking paid also means payment # is confirmed booking.payment_confirmed = True extra_msgs.append( "Booking payment status changed to " "'fully paid and confirmed'" ) else: booking.payment_confirmed = False booking.save() set_as_free = 'free_class' in \ form.changed_data and \ booking.free_class if 'send_confirmation' in form.changed_data: try: # send confirmation email host = 'http://{}'.format(request.META.get('HTTP_HOST')) # send email to studio ctx = { 'host': host, 'event': booking.event, 'user': booking.user, 'action': action, 'set_as_free': set_as_free, 'extra_msgs': extra_msgs } send_mail('{} Your booking for {} has been {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, booking.event, action ), get_template( 'studioadmin/email/booking_change_confirmation.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [booking.user.email], html_message=get_template( 'studioadmin/email/booking_change_confirmation.html' ).render(ctx), fail_silently=False) send_confirmation_msg = "and confirmation " \ "email sent to user" except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "user_booking_list - " "send confirmation email" ) send_confirmation_msg = ". There was a " \ "problem sending the confirmation email to the " \ "user. Tech support has been notified." else: send_confirmation_msg = "" messages.success( request, 'Booking for {} has been {} {}'.format( booking.event, action, send_confirmation_msg ) ) if set_as_free: extra_msg = "and marked as free class" elif transfer_block_created: extra_msg = "and transfer block created as credit" else: extra_msg = '' ActivityLog.objects.create( log='Booking id {} (user {}) for "{}" {} ' 'by admin user {} {}'.format( booking.id, booking.user.username, booking.event, action, request.user.username, extra_msg ) ) if not booking.block \ and 'block' in form.changed_data: messages.info( request, 'Block removed for {}; booking is ' 'now marked as unpaid'.format( booking.event ), ) if action == 'reopened': messages.info( request, mark_safe( 'Note: this booking was previously ' 'cancelled and has now been reopened. ' '<span class="cancel-warning">Payment ' 'status has not been automatically ' 'updated. Please review the booking ' 'and update if paid ' 'and/or block used.</span>' ) ) elif action == 'cancelled': if transfer_block_created: messages.info( request, mark_safe("Note: this booking has been " "cancelled. The booking has " "automatically been marked as " "unpaid and a transfer block " "has been created as credit. If you wish to " "refund the user instead, go " "to the <a href={}>user's blocks</a> " "and delete " "the transfer block first.".format( reverse( 'studioadmin:user_blocks_list', args=[booking.user.id] ) )) ) elif block_removed: messages.info( request, 'Note: this booking has been ' 'cancelled. The booking has ' 'automatically been marked as ' 'unpaid and the block ' 'used has been updated.' ) else: messages.info( request, 'Note: this booking has been ' 'cancelled. The booking has automatically ' 'been marked as unpaid (refunded).') if event_was_full: waiting_list_users = WaitingListUser.objects.filter( event=booking.event ) if waiting_list_users: try: send_waiting_list_email( booking.event, [wluser.user for \ wluser in waiting_list_users], host='http://{}'.format( request.META.get('HTTP_HOST') ) ) ActivityLog.objects.create( log='Waiting list email sent to ' 'user(s) {} for event {}'.format( ', '.join( [wluser.user.username \ for wluser in \ waiting_list_users] ), booking.event ) ) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Studioadmin user booking list - waiting list email" ) if action == 'created' or action == 'reopened': try: waiting_list_user = WaitingListUser.objects.get( user=booking.user, event=booking.event ) waiting_list_user.delete() ActivityLog.objects.create( log='User {} has been removed from the ' 'waiting list for {}'.format( booking.user.username, booking.event ) ) except WaitingListUser.DoesNotExist: pass if booking.block and not booking.block.active_block(): if booking.block.children.exists() \ and not has_free_block_pre_save: messages.info( request, 'You have added the last booking ' 'to a 10 class block; free class ' 'block has been created.' ) userbookingformset.save(commit=False) return HttpResponseRedirect( reverse( 'studioadmin:user_bookings_list', kwargs={ 'user_id': user.id, 'booking_status': booking_status } ) ) else: messages.error( request, mark_safe( "Please correct the following errors:\n{}".format( '\n'.join( [ "{}".format(error) for error in userbookingformset.errors ] ) ) ) ) else: all_bookings = Booking.objects.filter(user=user) if booking_status == 'past': queryset = all_bookings.filter( event__date__lt=timezone.now() ).order_by('-event__date') userbookingformset = UserBookingFormSet( queryset=queryset, instance=user, user=user, ) else: # 'future' by default queryset = all_bookings.filter( event__date__gte=timezone.now() ).order_by('event__date') userbookingformset = UserBookingFormSet( queryset=queryset, instance=user, user=user, ) userbookingformset = UserBookingFormSet( instance=user, queryset=queryset, user=user ) booking_status_filter = BookingStatusFilter( initial={'booking_status': booking_status} ) template = 'studioadmin/user_booking_list.html' return TemplateResponse( request, template, { 'userbookingformset': userbookingformset, 'user': user, 'sidenav_selection': 'users', 'booking_status_filter': booking_status_filter, 'booking_status': booking_status } )
def cancel_event_view(request, slug): event = get_object_or_404(Event, slug=slug) ev_type = 'class' if event.event_type.event_type == 'CL' else 'event' open_bookings = Booking.objects.filter( event=event, status='OPEN', no_show=False ) no_shows_all = Booking.objects.filter( event=event, status='OPEN', no_show=True ) no_shows = [bk for bk in no_shows_all if bk.paid or bk.deposit_paid] open_block_bookings = [ bk for bk in open_bookings if bk.block and not bk.free_class ] open_unpaid_bookings = [ bk for bk in open_bookings if not bk.deposit_paid and not bk.paid] open_free_non_block = [ bk for bk in open_bookings if bk.free_class and not bk.block ] open_free_block = [bk for bk in open_bookings if bk.free_class and bk.block] open_direct_paid_deposit_only = [ bk for bk in open_bookings if not bk.block and not bk.free_class and bk.deposit_paid and not bk.paid ] open_direct_paid = [ bk for bk in open_bookings if not bk.block and not bk.free_class and bk.paid ] if request.method == 'POST': if 'confirm' in request.POST: transfer_direct_paid = request.POST.get('direct_paid_action') == 'transfer' for booking in open_bookings: transfer_block_created = False block_paid = bool(booking.block) # block paid and free blocks free_block = bool(booking.block and booking.free_class) direct_paid = booking in open_direct_paid or \ booking in open_free_non_block deposit_only_paid = booking in open_direct_paid_deposit_only if booking.block: booking.block = None booking.deposit_paid = False booking.paid = False booking.payment_confirmed = False # in case this was paid with a free class block booking.free_class = False elif direct_paid and transfer_direct_paid: # direct paid = paypal and free non-block paid # create transfer block and make this booking unpaid if booking.event.event_type.event_type != 'EV': booking.deposit_paid = False booking.paid = False booking.payment_confirmed = False booking.free_class = False block_type, _ = BlockType.objects.get_or_create( event_type=booking.event.event_type, size=1, cost=0, duration=1, identifier='transferred', active=False ) Block.objects.create( block_type=block_type, user=booking.user, transferred_booking_id=booking.id ) transfer_block_created = True booking.status = "CANCELLED" booking.save() try: # send notification email to user host = 'http://{}'.format(request.META.get('HTTP_HOST')) ctx = { 'host': host, 'event_type': ev_type, 'block_paid': block_paid, 'direct_paid': direct_paid or deposit_only_paid, 'free_block': free_block, 'transfer_block_created': transfer_block_created, 'event': event, 'user': booking.user, } send_mail('{} {} has been cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ev_type.title(), ), get_template( 'studioadmin/email/event_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [booking.user.email], html_message=get_template( 'studioadmin/email/event_cancelled.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "cancel event - " "send notification email to user" ) event.cancelled = True event.booking_open = False event.payment_open = False event.save() # email studio with links for confirming refunds # direct paid (full and deposit only) and free non-block # if action selected is not transfer, otherwise just email for # deposits as we don't create # transfer blocks for deposit-only try: host = 'http://{}'.format(request.META.get('HTTP_HOST')) # send email to studio ctx = { 'host': host, 'event_type': ev_type, 'transfer_direct_paid': transfer_direct_paid, 'open_bookings': open_bookings, 'open_direct_paid_bookings': open_direct_paid, 'open_block_bookings': open_block_bookings, 'open_deposit_only_paid_bookings': open_direct_paid_deposit_only, 'open_unpaid_bookings': open_unpaid_bookings, 'open_free_non_block_bookings': open_free_non_block, 'open_free_block_bookings': open_free_block, 'no_shows': no_shows, 'event': event, } send_mail( '{} {} has been cancelled - please review for refunds ' 'required'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ev_type.title(), ), get_template( 'studioadmin/email/to_studio_event_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'studioadmin/email/to_studio_event_cancelled.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "cancel event - " "send refund notification email to studio" ) if open_bookings: booking_cancelled_msg = 'open ' \ 'booking(s) have been cancelled{} ' \ 'Notification emails have been ' \ 'sent to {}.'.format( ' and transfer blocks created for direct paid bookings.' if request.POST.get('direct_paid_action') == 'transfer' else '.', ', '.join( ['{} {}'.format(booking.user.first_name, booking.user.last_name) for booking in open_bookings] ) ) else: booking_cancelled_msg = 'there were ' \ 'no open bookings for this {}'.format( ev_type ) messages.info( request, '{} has been cancelled; '.format( ev_type.title() ) + booking_cancelled_msg ) ActivityLog.objects.create( log="{} {} cancelled by admin user {}; {}".format( ev_type.title(), event, request.user.username, booking_cancelled_msg ) ) return HttpResponseRedirect( reverse('studioadmin:{}'.format( 'events' if ev_type == 'event' else 'lessons' )) ) elif 'cancel' in request.POST: return HttpResponseRedirect( reverse('studioadmin:{}'.format( 'events' if ev_type == 'event' else 'lessons' )) ) context = { 'event': event, 'event_type': ev_type, 'open_bookings': bool(open_bookings), 'open_direct_paid_bookings': open_direct_paid, 'open_block_bookings': open_block_bookings, 'open_deposit_only_paid_bookings': open_direct_paid_deposit_only, 'open_unpaid_bookings': open_unpaid_bookings, 'open_free_non_block_bookings': open_free_non_block, 'open_free_block_bookings': open_free_block, 'no_shows': no_shows } return TemplateResponse( request, 'studioadmin/cancel_event.html', context )
def handle(self, *args, **options): action = options['action'][0] prices = { 'on': { 'pc': 6, 'pp': 2.75 }, 'off': { 'pc': 7.50, 'pp': 4 }, } pole_classes = Event.objects.filter( date__gt=timezone.now(), event_type__subtype='Pole level class') pole_practices = Event.objects.filter( date__gt=timezone.now(), event_type__subtype='Pole practice') for pole_class in pole_classes: pole_class.cost = prices[action]['pc'] if action == 'on': pole_class.booking_open = True pole_class.payment_open = True pole_class.save() for pole_practice in pole_practices: pole_practice.cost = prices[action]['pp'] if action == 'on': pole_practice.booking_open = True pole_practice.payment_open = True pole_practice.save() if pole_classes: message = 'Pole classes have been updated with {} prices ' \ '(ids {})'.format( 'sale' if action == 'on' else 'non-sale', ', '.join([str(pc.id) for pc in pole_classes]) ) ActivityLog.objects.create(log=message) self.stdout.write(message) if pole_practices: message = 'Pole practices have been updated with {} prices ' \ '(ids {})'.format( 'sale' if action == 'on' else 'non-sale', ', '.join([str(pp.id) for pp in pole_practices]) ) ActivityLog.objects.create(log=message) self.stdout.write(message) if pole_classes or pole_practices: try: send_mail( '{} Pole classes and/or practices sale {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, 'activated' if action == 'on' else 'deactivated'), 'The following pole class ids have ' 'been {actioned}:\n{pc_ids}\n' 'The following pole practice ids have ' 'been {actioned}:\n{pp_ids}'.format( pc_ids=', '.join([str(pc.id) for pc in pole_classes]), pp_ids=', '.join([str(pp.id) for pp in pole_practices]), actioned='activated' if action == 'on' else 'deactivated', ), settings.DEFAULT_FROM_EMAIL, [settings.SUPPORT_EMAIL], fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Activate class/practice sale - support email") else: self.stdout.write('No classes/practices to {}'.format( 'activate' if action == 'on' else 'deactivate'))
def email_users_view(request, mailing_list=False, template_name='studioadmin/email_users_form.html'): if mailing_list: subscribed, _ = Group.objects.get_or_create(name='subscribed') users_to_email = subscribed.user_set.all() else: users_to_email = User.objects.filter( id__in=request.session['users_to_email']) if request.method == 'POST': form = EmailUsersForm(request.POST) test_email = request.POST.get('send_test', False) if form.is_valid(): subject = '{}{}'.format(form.cleaned_data['subject'], ' [TEST EMAIL]' if test_email else '') from_address = form.cleaned_data['from_address'] message = form.cleaned_data['message'] cc = form.cleaned_data['cc'] # bcc recipients email_addresses = [user.email for user in users_to_email] email_count = len(email_addresses) number_of_emails = ceil(email_count / 99) if test_email: email_lists = [[from_address]] else: email_lists = [email_addresses] # will be a list of lists # split into multiple emails of 99 bcc plus 1 cc if email_count > 99: email_lists = [ email_addresses[i:i + 99] for i in range(0, email_count, 99) ] host = 'http://{}'.format(request.META.get('HTTP_HOST')) try: for i, email_list in enumerate(email_lists): ctx = { 'subject': subject, 'message': message, 'number_of_emails': number_of_emails, 'email_count': email_count, 'is_test': test_email, 'mailing_list': mailing_list, 'host': host, } msg = EmailMultiAlternatives( subject, get_template( 'studioadmin/email/email_users.txt').render(ctx), bcc=email_list, cc=[from_address] if (i == 0 and cc and not test_email) else [], reply_to=[from_address]) msg.attach_alternative( get_template( 'studioadmin/email/email_users.html').render(ctx), "text/html") msg.send(fail_silently=False) if not test_email: ActivityLog.objects.create( log='{} email with subject "{}" sent to users {} by' ' admin user {}'.format( 'Mailing list' if mailing_list else 'Bulk', subject, ', '.join( email_list), request.user.username)) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "Bulk Email to students") ActivityLog.objects.create( log="Possible error with sending {} email; " "notification sent to tech support".format( 'mailing list' if mailing_list else 'bulk')) if not test_email: ActivityLog.objects.create( log='{} email error ' '(email subject "{}"), sent by ' 'by admin user {}'.format( 'Mailing list' if mailing_list else 'Bulk', subject, request.user.username)) if not test_email: messages.success( request, '{} email with subject "{}" has been sent to ' 'users'.format('Mailing list' if mailing_list else 'Bulk', subject)) return HttpResponseRedirect(reverse('studioadmin:users')) else: messages.success( request, 'Test email has been sent to {} only. Click ' '"Send Email" below to send this email to ' 'users.'.format(from_address)) # Do this if form not valid OR sending test email event_ids = request.session.get('events', []) lesson_ids = request.session.get('lessons', []) events = Event.objects.filter(id__in=event_ids) lessons = Event.objects.filter(id__in=lesson_ids) if form.errors: totaleventids = event_ids + lesson_ids totalevents = Event.objects.filter(id__in=totaleventids) messages.error( request, mark_safe("Please correct errors in form: {}".format( form.errors))) form = EmailUsersForm( initial={ 'subject': "; ".join((str(event) for event in totalevents)) }) if test_email: form = EmailUsersForm(request.POST) else: event_ids = ast.literal_eval(request.GET.get('events', '[]')) events = Event.objects.filter(id__in=event_ids) lesson_ids = ast.literal_eval(request.GET.get('lessons', '[]')) lessons = Event.objects.filter(id__in=lesson_ids) totaleventids = event_ids + lesson_ids totalevents = Event.objects.filter(id__in=totaleventids) form = EmailUsersForm( initial={ 'subject': "; ".join((str(event) for event in totalevents)) }) return TemplateResponse( request, template_name, { 'form': form, 'users_to_email': users_to_email, 'sidenav_selection': 'mailing_list' if mailing_list else 'email_users', 'events': events, 'lessons': lessons, 'mailing_list': mailing_list })
def email_users_view(request, mailing_list=False, template_name='studioadmin/email_users_form.html'): if mailing_list: subscribed, _ = Group.objects.get_or_create(name='subscribed') users_to_email = subscribed.user_set.all() else: users_to_email = User.objects.filter( id__in=request.session['users_to_email'] ) if request.method == 'POST': form = EmailUsersForm(request.POST) test_email = request.POST.get('send_test', False) if form.is_valid(): subject = '{}{}'.format( form.cleaned_data['subject'], ' [TEST EMAIL]' if test_email else '' ) from_address = form.cleaned_data['from_address'] message = form.cleaned_data['message'] cc = form.cleaned_data['cc'] # bcc recipients email_addresses = [user.email for user in users_to_email] email_count = len(email_addresses) number_of_emails = ceil(email_count / 99) if test_email: email_lists = [[from_address]] else: email_lists = [email_addresses] # will be a list of lists # split into multiple emails of 99 bcc plus 1 cc if email_count > 99: email_lists = [ email_addresses[i : i + 99] for i in range(0, email_count, 99) ] host = 'http://{}'.format(request.META.get('HTTP_HOST')) try: for i, email_list in enumerate(email_lists): ctx = { 'subject': subject, 'message': message, 'number_of_emails': number_of_emails, 'email_count': email_count, 'is_test': test_email, 'mailing_list': mailing_list, 'host': host, } msg = EmailMultiAlternatives( subject, get_template( 'studioadmin/email/email_users.txt').render( ctx ), bcc=email_list, cc=[from_address] if (i == 0 and cc and not test_email) else [], reply_to=[from_address] ) msg.attach_alternative( get_template( 'studioadmin/email/email_users.html').render( ctx ), "text/html" ) msg.send(fail_silently=False) if not test_email: ActivityLog.objects.create( log='{} email with subject "{}" sent to users {} by' ' admin user {}'.format( 'Mailing list' if mailing_list else 'Bulk', subject, ', '.join(email_list), request.user.username ) ) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Bulk Email to students" ) ActivityLog.objects.create( log="Possible error with sending {} email; " "notification sent to tech support".format( 'mailing list' if mailing_list else 'bulk' ) ) if not test_email: ActivityLog.objects.create( log='{} email error ' '(email subject "{}"), sent by ' 'by admin user {}'.format( 'Mailing list' if mailing_list else 'Bulk', subject, request.user.username ) ) if not test_email: messages.success( request, '{} email with subject "{}" has been sent to ' 'users'.format( 'Mailing list' if mailing_list else 'Bulk', subject ) ) return HttpResponseRedirect(reverse('studioadmin:users')) else: messages.success( request, 'Test email has been sent to {} only. Click ' '"Send Email" below to send this email to ' 'users.'.format( from_address ) ) # Do this if form not valid OR sending test email event_ids = request.session.get('events', []) lesson_ids = request.session.get('lessons', []) events = Event.objects.filter(id__in=event_ids) lessons = Event.objects.filter(id__in=lesson_ids) if form.errors: totaleventids = event_ids + lesson_ids totalevents = Event.objects.filter(id__in=totaleventids) messages.error( request, mark_safe( "Please correct errors in form: {}".format(form.errors) ) ) form = EmailUsersForm( initial={ 'subject': "; ".join( (str(event) for event in totalevents) ) } ) if test_email: form = EmailUsersForm(request.POST) else: event_ids = ast.literal_eval(request.GET.get('events', '[]')) events = Event.objects.filter(id__in=event_ids) lesson_ids = ast.literal_eval(request.GET.get('lessons', '[]')) lessons = Event.objects.filter(id__in=lesson_ids) totaleventids = event_ids + lesson_ids totalevents = Event.objects.filter(id__in=totaleventids) form = EmailUsersForm( initial={ 'subject': "; ".join((str(event) for event in totalevents)) } ) return TemplateResponse( request, template_name, { 'form': form, 'users_to_email': users_to_email, 'sidenav_selection': 'mailing_list' if mailing_list else 'email_users', 'events': events, 'lessons': lessons, 'mailing_list': mailing_list } )
def handle(self, *args, **options): identifiers = options['identifiers'] action = options['action'][0] actions = { 'on': True, 'off': False } blocktypes = BlockType.objects.filter(identifier__in=identifiers) for blocktype in blocktypes: blocktype.active = actions[action] blocktype.save() if blocktypes: message = 'Blocktypes with the identifier(s) "{}" {} ' \ '(ids {})'.format( ', '.join(identifiers), 'activated' if action == 'on' else 'deactivated', ', '.join([str(bt.id) for bt in blocktypes]) ) ActivityLog.objects.create(log=message) self.stdout.write(message) try: send_mail('{} Block types {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, 'activated' if action == 'on' else 'deactivated' ), 'The following blocktype ids with the identifier(s) "{}" have ' 'been {}:\n{}'.format( ', '.join(identifiers), 'activated' if action == 'on' else 'deactivated', ', '.join([str(bt.id) for bt in blocktypes]) ), settings.DEFAULT_FROM_EMAIL, [settings.SUPPORT_EMAIL], fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Activate blocktypes - support email" ) else: self.stdout.write('No blocktypes matching identifiers') try: send_mail('{} Block types {} attempt failed'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, 'activation' if action == 'on' else 'deactivation' ), 'Blocktype {} command run but no blocktypes matched the ' 'identifier(s) "{}"'.format( 'activation' if action == 'on' else 'deactivation', ', '.join(identifiers), ), settings.DEFAULT_FROM_EMAIL, [settings.SUPPORT_EMAIL], fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Activate blocktypes - support email" )
def handle(self, *args, **options): bookings = [] for booking in Booking.objects.filter( event__date__gte=timezone.now(), event__advance_payment_required=True, status='OPEN', paid=False, payment_confirmed=False, date_booked__lte=timezone.now() - timedelta(hours=6)): # ignore any which have been rebooked in the past 6 hrs if booking.date_rebooked and \ (booking.date_rebooked >= (timezone.now() - timedelta(hours=6))): pass elif booking.event.date - timedelta( hours=booking.event.cancellation_period ) < timezone.now() and booking.warning_sent: bookings.append(booking) elif booking.event.payment_due_date and booking.warning_sent: if booking.event.payment_due_date < timezone.now(): bookings.append(booking) elif booking.event.payment_time_allowed: # if there's a payment time allowed, cancel bookings booked # longer ago than this (bookings already filtered out # any booked or rebooked within 6 hrs) # don't check for warning sent this time # for free class requests, always allow them 24 hrs so admin # have time to mark classes as free (i.e.paid) last_booked_date = booking.date_rebooked \ if booking.date_rebooked else booking.date_booked if booking.free_class_requested: if last_booked_date < timezone.now() - timedelta(hours=24): bookings.append(booking) elif last_booked_date < timezone.now() \ - timedelta(hours=booking.event.payment_time_allowed): bookings.append(booking) for booking in bookings: event_was_full = booking.event.spaces_left == 0 ctx = { 'booking': booking, 'event': booking.event, 'date': booking.event.date.strftime('%A %d %B'), 'time': booking.event.date.strftime('%I:%M %p'), } # send mails to users try: send_mail('{} Booking cancelled: {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, booking.event.name), get_template( 'booking/email/booking_auto_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [booking.user.email], html_message=get_template( 'booking/email/booking_auto_cancelled.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Automatic cancel job - cancelled email" ) booking.status = 'CANCELLED' booking.block = None booking.save() ActivityLog.objects.create( log='Unpaid booking id {} for event {}, user {} ' 'automatically cancelled'.format( booking.id, booking.event, booking.user ) ) if event_was_full: waiting_list_users = WaitingListUser.objects.filter( event=booking.event ) try: send_waiting_list_email( booking.event, [user.user for user in waiting_list_users] ) ActivityLog.objects.create( log='Waiting list email sent to user(s) {} for ' 'event {}'.format( ', '.join( [wluser.user.username for \ wluser in waiting_list_users] ), booking.event ) ) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Automatic cancel job - waiting list email" ) if bookings: if settings.SEND_ALL_STUDIO_EMAILS: # send single mail to Studio try: send_mail('{} Booking{} been automatically cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ' has' if len(bookings) == 1 else 's have'), get_template( 'booking/email/booking_auto_cancelled_studio_email.txt' ).render({'bookings': bookings}), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'booking/email/booking_auto_cancelled_studio_email.html' ).render({'bookings': bookings}), fail_silently=False) self.stdout.write( 'Cancellation emails sent for booking ids {}'.format( ', '.join([str(booking.id) for booking in bookings]) ) ) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Automatic cancel job - studio email" ) else: self.stdout.write('No bookings to cancel')
def handle(self, *args, **options): # get relevant ticketed_events ticketed_events = TicketedEvent.objects.filter( date__gte=timezone.now(), cancelled=False, ticket_cost__gt=0, advance_payment_required=True, ) bookings_to_cancel = [] # import ipdb; ipdb.set_trace() for event in ticketed_events: # get open unpaid ticket bookings made > 6 hrs ago ticket_bookings = [ bkg for bkg in event.ticket_bookings.all() if not bkg.cancelled and not bkg.paid and (bkg.date_booked < timezone.now() - timedelta(hours=6)) ] # if payment due date is past and warning has been sent, cancel if event.payment_due_date and event.payment_due_date < timezone.now(): for bkg in ticket_bookings: if bkg.warning_sent: bookings_to_cancel.append(bkg) elif event.payment_time_allowed: # if there's a payment time allowed, cancel bookings booked # longer ago than this (ticket_bookings already filtered out # any booked within 6 hrs) # don't check for warning sent this time for bkg in ticket_bookings: if bkg.date_booked < timezone.now() \ - timedelta(hours=event.payment_time_allowed): bookings_to_cancel.append(bkg) for ticket_booking in bookings_to_cancel: ctx = { 'ticket_booking': ticket_booking, 'ticketed_event': ticket_booking.ticketed_event, } # send mails to users try: send_mail('{} Ticket Booking ref {} cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticket_booking.booking_reference ), get_template( 'booking/email/ticket_booking_auto_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [ticket_booking.user.email], html_message=get_template( 'booking/email/ticket_booking_auto_cancelled.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Automatic cancel ticket booking job - cancelled email" ) ticket_booking.cancelled = True ticket_booking.save() ActivityLog.objects.create( log='Unpaid ticket booking ref {} for event {}, user {} ' 'automatically cancelled'.format( ticket_booking.booking_reference, ticket_booking.ticketed_event, ticket_booking.user.username ) ) if bookings_to_cancel: if settings.SEND_ALL_STUDIO_EMAILS: try: # send single mail to Studio send_mail('{} Ticket Booking{} been automatically cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ' has' if len(bookings_to_cancel) == 1 else 's have'), get_template( 'booking/email/ticket_booking_auto_cancelled_studio_email.txt' ).render({'bookings': bookings_to_cancel}), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'booking/email/ticket_booking_auto_cancelled_studio_email.html' ).render({'bookings': bookings_to_cancel}), fail_silently=False) self.stdout.write( 'Cancellation emails sent for ticket booking refs {}'.format( ', '.join( [str(booking.booking_reference) for booking in bookings_to_cancel] ) ) ) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Automatic cancel ticket booking job - studio email" ) else: self.stdout.write('No ticket bookings to cancel')
def handle(self, *args, **options): # get relevant users expire_date = timezone.now() - timedelta(days=365) expired_users = [] for user in User.objects.all(): if not user.bookings.exists(): expired_users.append(user) else: recent_bookings = [ True for booking in user.bookings.all() if (booking.event.date > expire_date) and booking.paid ] if not recent_bookings: expired_users.append(user) old_print_disclaimers = PrintDisclaimer.objects.filter( date__lt=expire_date) old_online_disclaimers = OnlineDisclaimer.objects.filter( Q(date__lt=expire_date) & (Q(date_updated__isnull=True) | Q(date_updated__lt=expire_date))) print_disclaimer_users_to_delete = old_print_disclaimers.filter( user__in=expired_users) print_disclaimer_users = [ '{} {}'.format(disc.user.first_name, disc.user.last_name) for disc in print_disclaimer_users_to_delete ] online_disclaimer_users_to_delete = old_online_disclaimers.filter( user__in=expired_users) online_disclaimer_users = [ '{} {}'.format(disc.user.first_name, disc.user.last_name) for disc in online_disclaimer_users_to_delete ] print_disclaimer_users_to_delete.delete() online_disclaimer_users_to_delete.delete() if print_disclaimer_users or online_disclaimer_users: # email studio ctx = { 'print_disclaimer_users': print_disclaimer_users, 'online_disclaimer_users': online_disclaimer_users } # send mail to studio try: send_mail( '{} Disclaimers deleted for expired users'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ), get_template( 'account/email/delete_disclaimers.txt').render(ctx), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'account/email/delete_disclaimers.html').render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Automatic disclaimer deletion - studio email") ActivityLog.objects.create( log='Print disclaimers deleted for expired users: {}'.format( ', '.format(print_disclaimer_users))) ActivityLog.objects.create( log='Online disclaimers deleted for expired users: {}'.format( ', '.format(online_disclaimer_users))) else: self.stdout.write('No disclaimers to delete') ActivityLog.objects.create( log='Delete disclaimers job run; no expired users')
def post(self, request, *args, **kwargs): if 'cancel' in request.POST: self.ticket_booking.delete() messages.info( request, 'Ticket booking for {} has been cancelled.'.format( self.ticketed_event)) return HttpResponseRedirect(reverse('booking:ticketed_events')) context = self.get_context_data() ticket_purchase_form = context['ticket_purchase_form'] ticket_formset = context['ticket_formset'] if ticket_purchase_form.has_changed(): # tickets on current booking are only included in the tickets_left # calculation if purchase has been confirmed old_tickets = self.ticket_booking.tickets.all() old_ticket_count = old_tickets.count() if self.ticket_booking.purchase_confirmed: tickets_left_excl_this = self.ticketed_event.tickets_left() \ + old_ticket_count else: tickets_left_excl_this = self.ticketed_event.tickets_left() new_quantity = int( request.POST.get('ticket_purchase_form-quantity')) if new_quantity > tickets_left_excl_this: messages.error( request, 'Cannot purchase the number of tickets requested. ' 'Only {} tickets left.'.format( self.ticketed_event.tickets_left())) else: # create the correct number of tickets on this booking if old_ticket_count < new_quantity: for i in range(new_quantity - old_ticket_count): Ticket.objects.create( ticket_booking=self.ticket_booking) if old_ticket_count > new_quantity: for ticket in old_tickets[new_quantity:]: ticket.delete() if old_ticket_count > 0: ActivityLog.objects.create( log="Ticket quantity updated on booking ref {}".format( self.ticket_booking.booking_reference)) tickets = self.ticket_booking.tickets.all() context['tickets'] = tickets return TemplateResponse(request, self.template_name, context) if 'ticket_formset-submit' in request.POST: if ticket_formset.is_valid(): ticket_formset.save() # we only create the paypal form if there is a ticket cost and # online payments are open if self.ticketed_event.ticket_cost and \ self.ticketed_event.payment_open: invoice_id = create_ticket_booking_paypal_transaction( self.request.user, self.ticket_booking).invoice_id host = 'http://{}'.format( self.request.META.get('HTTP_HOST')) paypal_form = PayPalPaymentsUpdateForm( initial=context_helpers.get_paypal_dict( host, self.ticketed_event.ticket_cost, self.ticketed_event, invoice_id, '{} {}'.format('ticket_booking', self.ticket_booking.id), paypal_email=self.ticketed_event.paypal_email, quantity=self.ticket_booking.tickets.count())) context["paypalform"] = paypal_form self.ticket_booking.purchase_confirmed = True # reset the ticket_booking booked date to the date user confirms context['purchase_confirmed'] = True self.ticket_booking.date_booked = timezone.now() self.ticket_booking.save() ActivityLog.objects.create( log="Ticket Purchase confirmed: event {}, user {}, " "booking ref {}".format( self.ticketed_event.name, request.user.username, self.ticket_booking.booking_reference)) host = 'http://{}'.format(request.META.get('HTTP_HOST')) ctx = { 'host': host, 'ticketed_event': self.ticketed_event, 'ticket_booking': self.ticket_booking, 'ticket_count': self.ticket_booking.tickets.count(), 'user': request.user, } try: # send notification email to user send_mail( '{} Ticket booking confirmed for {}: ref {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, self.ticketed_event, self.ticket_booking.booking_reference, ), get_template('booking/email/ticket_booking_made.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [request.user.email], html_message=get_template( 'booking/email/ticket_booking_made.html').render( ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "ticket booking created - " "send email to user") if self.ticketed_event.email_studio_when_purchased: try: send_mail( '{} Ticket booking confirmed for {}: ref {}'. format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, self.ticketed_event, self.ticket_booking.booking_reference, ), get_template( 'booking/email/to_studio_ticket_booking_made.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'booking/email/to_studio_ticket_booking_made.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "ticket booking created - " "send email to studio") else: messages.error(request, "Please correct errors in the form below") tickets = self.ticket_booking.tickets.all() context['tickets'] = tickets ticket_purchase_form = TicketPurchaseForm( prefix='ticket_purchase_form', ticketed_event=self.ticketed_event, ticket_booking=self.ticket_booking, initial={'quantity': self.ticket_booking.tickets.count()}) context["ticket_purchase_form"] = ticket_purchase_form return TemplateResponse(request, self.template_name, context)
def cancel_ticketed_event_view(request, slug): ticketed_event = get_object_or_404(TicketedEvent, slug=slug) open_paid_ticket_bookings = [ booking for booking in ticketed_event.ticket_bookings.all() if not booking.cancelled and booking.purchase_confirmed and booking.tickets.exists() and booking.paid ] open_unpaid_ticket_bookings = [ booking for booking in ticketed_event.ticket_bookings.all() if not booking.cancelled and booking.purchase_confirmed and booking.tickets.exists() and not booking.paid ] unconfirmed_ticket_bookings = TicketBooking.objects.filter( ticketed_event=ticketed_event, purchase_confirmed=False) if request.method == 'POST': if 'confirm' in request.POST: host = 'http://{}'.format(request.META.get('HTTP_HOST')) for booking in open_paid_ticket_bookings + \ open_unpaid_ticket_bookings: booking.cancelled = True booking.save() try: # send notification email to user to all ticket booking, # paid or unpaid ctx = { 'host': host, 'ticketed_event': ticketed_event, 'ticket_booking': booking, } send_mail( '{} {} has been cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticketed_event.name, ), get_template( 'studioadmin/email/ticketed_event_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [booking.user.email], html_message=get_template( 'studioadmin/email/ticketed_event_cancelled.html'). render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "cancel ticketed event - " "send notification email to user") for booking in unconfirmed_ticket_bookings: booking.delete() ticketed_event.cancelled = True ticketed_event.show_on_site = False ticketed_event.payment_open = False ticketed_event.save() if open_paid_ticket_bookings: # email studio with links for confirming refunds for paid only try: # send email to studio ctx = { 'host': host, 'open_paid_ticket_bookings': open_paid_ticket_bookings, 'ticketed_event': ticketed_event, } send_mail( '{} Refunds due for ticket bookings for ' 'cancelled event {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticketed_event.name, ), get_template( 'studioadmin/email/to_studio_ticketed_event_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'studioadmin/email/to_studio_ticketed_event_cancelled.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "cancel ticketed event - " "send refund notification email to studio") if open_paid_ticket_bookings and open_unpaid_ticket_bookings: booking_cancelled_msg = '{} has been cancelled; open ticket ' \ 'booking refs {} have been ' \ 'cancelled'.format( ticketed_event, ', '.join(['{}'.format(booking.booking_reference) for booking in open_paid_ticket_bookings] ) ) messages.info( request, booking_cancelled_msg + 'and notification emails have ' 'been sent.') else: booking_cancelled_msg = '{} has been cancelled; there were ' \ 'no open ticket bookings for this ' \ 'event'.format(ticketed_event) messages.info(request, booking_cancelled_msg) ActivityLog.objects.create( log="{} cancelled by admin user {}. {}".format( ticketed_event, request.user.username, booking_cancelled_msg)) return HttpResponseRedirect(reverse('studioadmin:ticketed_events')) elif 'cancel' in request.POST: return HttpResponseRedirect(reverse('studioadmin:ticketed_events')) context = { 'ticketed_event': ticketed_event, 'open_paid_ticket_bookings': open_paid_ticket_bookings, 'open_unpaid_ticket_bookings': open_unpaid_ticket_bookings, 'already_cancelled': ticketed_event.cancelled } return TemplateResponse(request, 'studioadmin/cancel_ticketed_event.html', context)
def delete(self, request, *args, **kwargs): booking = self.get_object() # Booking can be fully cancelled if the event allows cancellation AND # the cancellation period is not past # If not, we let people cancel but leave the booking status OPEN and # set to no-show can_cancel_and_refund = booking.event.allow_booking_cancellation \ and booking.event.can_cancel() event_was_full = booking.event.spaces_left == 0 host = 'http://{}'.format(self.request.META.get('HTTP_HOST')) # send email to user ctx = { 'host': host, 'booking': booking, 'event': booking.event, 'date': booking.event.date.strftime('%A %d %B'), 'time': booking.event.date.strftime('%I:%M %p'), } try: send_mail('{} Booking for {} cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, booking.event.name), get_template('booking/email/booking_cancelled.txt').render(ctx), settings.DEFAULT_FROM_EMAIL, [booking.user.email], html_message=get_template( 'booking/email/booking_cancelled.html').render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "DeleteBookingView - cancelled email") messages.error(self.request, "An error occured, please contact " "the studio for information") if can_cancel_and_refund: transfer_block_created = False if booking.paid and (not booking.block or booking.block.expired): # booking was paid directly, either in cash or by paypal # OR booking was free class but not made with free block # OR booking was made with block (normal/free/transfer) but # block has now expired and we can't credit by reassigning # space to block. # NOTE: this does mean that potentially someone could defer # a class indefinitely by cancelling and rebooking, but # let's assume that would be a rare occurrence # If event is CL or RH, get or create transfer block type, # create transfer block for user and set transferred_ # booking_id to the cancelled one if booking.event.event_type.event_type != 'EV': block_type, _ = BlockType.objects.get_or_create( event_type=booking.event.event_type, size=1, cost=0, duration=1, identifier='transferred', active=False ) Block.objects.create( block_type=block_type, user=booking.user, transferred_booking_id=booking.id ) transfer_block_created = True booking.deposit_paid = False booking.paid = False booking.payment_confirmed = False # send email to studio only for 'EV' which are not transferable else: send_mail( '{} {} {} has just cancelled a booking for {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, 'ACTION REQUIRED!' if not booking.block else '', booking.user.username, booking.event.name ), get_template( 'booking/email/to_studio_booking_cancelled.txt' ).render( { 'host': host, 'booking': booking, 'event': booking.event, 'date': booking.event.date.strftime('%A %d %B'), 'time': booking.event.date.strftime('%I:%M %p'), } ), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], fail_silently=False) # if booking was bought with a block, remove from block and set # paid and payment_confirmed to False. If paid directly, paid is only # changed to False for bookings that have created transfer blocks; for # EV event types, leave paid as True as refunds need to be dealt with # manually but change payment_confirmed to False # reassigning free class blocks is done in model save if booking.block: booking.block = None booking.paid = False if booking.free_class: booking.free_class = False booking.paid = False booking.status = 'CANCELLED' booking.payment_confirmed = False booking.save() messages.success( self.request, self.success_message.format(booking.event) ) ActivityLog.objects.create( log='Booking id {} for event {}, user {}, was cancelled by user ' '{}'.format( booking.id, booking.event, booking.user.username, self.request.user.username ) ) if transfer_block_created: ActivityLog.objects.create( log='Transfer block created for user {} (for {}; transferred ' 'booking id {} '.format( booking.user.username, booking.event.event_type.subtype, booking.id ) ) messages.info( self.request, mark_safe( 'A transfer block has been created for you as ' 'credit for your cancelled booking and is valid for ' '1 month (<a href="/blocks">View your blocks</a>)' ) ) else: # if the booking wasn't paid, just cancel it if not booking.paid: booking.status = 'CANCELLED' booking.payment_confirmed = False booking.save() messages.success( self.request, self.success_message.format(booking.event) ) ActivityLog.objects.create( log='Booking id {} for event {}, user {}, was cancelled by user ' '{}'.format( booking.id, booking.event, booking.user.username, self.request.user.username ) ) else: # set to no-show booking.no_show = True booking.save() if not booking.event.allow_booking_cancellation: messages.success( self.request, self.success_message.format(booking.event) + ' Please note that this booking is not eligible for refunds ' 'or transfer credit.' ) ActivityLog.objects.create( log='Booking id {} for NON-CANCELLABLE event {}, user {}, ' 'was cancelled and set to no-show'.format( booking.id, booking.event, booking.user.username, self.request.user.username ) ) else: messages.success( self.request, self.success_message.format(booking.event) + ' Please note that this booking is not eligible for ' 'refunds or transfer credit as the allowed ' 'cancellation period has passed.' ) ActivityLog.objects.create( log='Booking id {} for event {}, user {}, was cancelled ' 'after the cancellation period and set to ' 'no-show'.format( booking.id, booking.event, booking.user.username, self.request.user.username ) ) # if applicable, email users on waiting list if event_was_full: waiting_list_users = WaitingListUser.objects.filter( event=booking.event ) if waiting_list_users: try: send_waiting_list_email( booking.event, [wluser.user for wluser in waiting_list_users], host='http://{}'.format(request.META.get('HTTP_HOST')) ) ActivityLog.objects.create( log='Waiting list email sent to user(s) {} for ' 'event {}'.format( ', '.join( [wluser.user.username for \ wluser in waiting_list_users] ), booking.event ) ) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "DeleteBookingView - waiting list email") messages.error(self.request, "An error occured, please contact " "the studio for information") return HttpResponseRedirect(self.get_success_url())
def handle(self, *args, **options): inputfilepath = options.get('file') with open(inputfilepath, 'r') as file: reader = csv.reader(file) fail_count = 0 for i, row in enumerate(reader): if i == 0: # header row pass else: name = row[0] first_name = name.split()[0] username1 = row[1] email1 = row[2] email1_used = True if row[3].lower() == 'y' else False email1_is_fb = True if row[6].lower() == 'y' else False username2 = row[7] email2 = row[8] email2_used = True if row[9].lower() == 'y' else False email2_is_fb = True if row[12].lower() == 'y' else False username3 = row[13] if row[13] else None email3 = row[14] if row[14] else None email3_used = True if row[15].lower() == 'y' else False email3_is_fb = True if row[18].lower() == 'y' else False emails = [ em for em in [email1, email2, email3] if em is not None ] email_used = None num_used = sum([email1_used, email2_used, email3_used]) if num_used == 1: # only include email_used if one # and only one used email_used = [ em[0] for em in [(email1, email1_used), ( email2, email2_used), (email3, email3_used)] if em[1] is True ][0] ctx = { 'first_name': first_name, 'username1': username1, 'email1': email1, 'email1_is_fb': email1_is_fb, 'username2': username2, 'email2': email2, 'email2_is_fb': email2_is_fb, 'username3': username3, 'email3': email3, 'email3_is_fb': email3_is_fb, 'email_count': 3 if username3 else 2, 'more_than_1_used': True if num_used > 1 else False, 'email_used': email_used } # send email try: email_msg = EmailMessage( '{} Duplicate accounts on booking system'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ), get_template('account/email/duplicate_account.txt' ).render(ctx), from_email=settings.DEFAULT_FROM_EMAIL, to=emails, reply_to=[settings.SUPPORT_EMAIL]) email_msg.send() except Exception as e: fail_count += 1 # send mail to tech support with Exception send_support_email(e, __name__, "Duplicate account emails") self.stdout.write('Emails sent for {} accounts'.format(i - fail_count)) self.stdout.write('{} failed sends'.format(fail_count))
def form_valid(self, form): booking = form.save(commit=False) try: # We shouldn't even get here with a booking that isn't either # cancelled or no_show; those get redirected in the dispatch() existing_booking = Booking.objects.get( user=self.request.user, event=booking.event, ) booking = existing_booking if booking.status == 'CANCELLED': previously_cancelled = True previously_no_show = False elif booking.no_show: previously_no_show = True previously_cancelled = False booking.status = 'OPEN' booking.no_show = False except Booking.DoesNotExist: previously_cancelled = False previously_no_show = False booking.user = self.request.user transaction_id = None invoice_id = None previously_cancelled_and_direct_paid = False if "claim_free" in form.data: _email_free_class_request( self.request, booking, 'rebook' if previously_cancelled else 'create' ) elif previously_cancelled and booking.paid: previously_cancelled_and_direct_paid = True pptrans = PaypalBookingTransaction.objects.filter(booking=booking)\ .exclude(transaction_id__isnull=True) if pptrans: transaction_id = pptrans[0].transaction_id invoice_id = pptrans[0].invoice_id elif previously_no_show and booking.paid: # leave paid no_show booking with existing payment method pass elif 'block_book' in form.data: active_block = _get_active_user_block(self.request.user, booking) if active_block: booking.block = active_block booking.paid = True booking.payment_confirmed = True # check for existence of free child block on pre-saved booking # note for prev no-shows booked with block, any free child blocks should # have already been created. Rebooking prev no-show doesn;t add a new # block booking has_free_block_pre_save = False if booking.block and booking.block.children.exists(): has_free_block_pre_save = True try: booking.save() ActivityLog.objects.create( log='Booking {} {} for "{}" by user {}'.format( booking.id, 'created' if not (previously_cancelled or previously_no_show) else 'rebooked', booking.event, booking.user.username) ) except ValidationError: # pragma: no cover # we shouldn't ever get here, because the dispatch should deal # with it logger.warning( 'Validation error, most likely due to duplicate booking ' 'attempt; redirected to duplicate booking page' ) return HttpResponseRedirect(reverse('booking:duplicate_booking', args=[self.event.slug])) # set flag on session so if user clicks "back" after posting the form, # we can redirect self.request.session['booking_created_{}'.format(booking.id)] = True blocks_used, total_blocks = _get_block_status(booking, self.request) host = 'http://{}'.format(self.request.META.get('HTTP_HOST')) # send email to user ctx = { 'host': host, 'booking': booking, 'event': booking.event, 'date': booking.event.date.strftime('%A %d %B'), 'time': booking.event.date.strftime('%H:%M'), 'blocks_used': blocks_used, 'total_blocks': total_blocks, 'prev_cancelled_and_direct_paid': previously_cancelled_and_direct_paid, 'claim_free': True if "claim_free" in form.data else False, 'ev_type': self.ev_type[:-1] } try: send_mail('{} Booking for {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, booking.event.name), get_template('booking/email/booking_received.txt').render(ctx), settings.DEFAULT_FROM_EMAIL, [booking.user.email], html_message=get_template( 'booking/email/booking_received.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "BookingCreateView") messages.error(self.request, "An error occured, please contact " "the studio for information") # send email to studio if flagged for the event or if previously # cancelled and direct paid if (booking.event.email_studio_when_booked or previously_cancelled_and_direct_paid): additional_subject = "" if previously_cancelled_and_direct_paid: additional_subject = "ACTION REQUIRED!" try: send_mail('{} {} {} {} has just booked for {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, additional_subject, booking.user.first_name, booking.user.last_name, booking.event.name), get_template( 'booking/email/to_studio_booking.txt' ).render( { 'host': host, 'booking': booking, 'event': booking.event, 'date': booking.event.date.strftime('%A %d %B'), 'time': booking.event.date.strftime('%H:%M'), 'prev_cancelled_and_direct_paid': previously_cancelled_and_direct_paid, 'transaction_id': transaction_id, 'invoice_id': invoice_id } ), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "BookingCreateView") extra_msg = '' if 'claim_free' in form.data: extra_msg = 'Your place will be secured once your free class ' \ 'request has been reviewed and approved. ' elif previously_cancelled_and_direct_paid: extra_msg = 'You previously paid for this booking; your booking ' \ 'will remain as pending until the organiser has ' \ 'reviewed your payment status.' elif previously_no_show: if booking.block: extra_msg = "You previously paid for this booking with a " \ "block and your booking has been reopened." elif booking.paid: extra_msg = "You previously paid for this booking and your " \ "booking has been reopened." elif not booking.block: if booking.event.cost and not booking.paid: # prev no_show could still be paid cancellation_warning = "" if booking.event.advance_payment_required and \ booking.event.allow_booking_cancellation: if booking.event.payment_due_date: cancel_str = "by the payment due date" elif booking.event.payment_time_allowed: cancel_str = "within {} hours".format( booking.event.payment_time_allowed ) else: cancel_str = "by the cancellation period" cancellation_warning = "Note that if payment " \ "has not been received {}, " \ "your booking will be automatically cancelled.".format( cancel_str ) extra_msg = 'Please make your payment as soon as possible. ' \ '<strong>{}</strong>'.format(cancellation_warning) elif not booking.block.active_block(): transfer_block = booking.block.block_type.identifier\ .startswith('transferred') \ if booking.block.block_type.identifier else False if not transfer_block: extra_msg = 'You have just used the last space in your block. ' if booking.block.children.exists() and not has_free_block_pre_save: extra_msg += '</br><span style="color: #9A2EFE;">' \ '<strong>You have qualified for a extra free ' \ 'class which has been added to ' \ '<a href="/blocks">your blocks</a></strong><span> ' else: extra_msg += 'Go to <a href="/blocks">Your Blocks</a> to ' \ 'buy a new one.' messages.success( self.request, mark_safe("{}<br>{}".format( self.success_message.format(booking.event), extra_msg)) ) try: waiting_list_user = WaitingListUser.objects.get( user=booking.user, event=booking.event ) waiting_list_user.delete() ActivityLog.objects.create( log='User {} removed from waiting list ' 'for {}'.format( booking.user.username, booking.event ) ) except WaitingListUser.DoesNotExist: pass if not booking.paid and booking.event.cost: return HttpResponseRedirect( reverse('booking:update_booking', args=[booking.id]) ) return HttpResponseRedirect(reverse('booking:bookings'))
def handle(self, *args, **options): inputfilepath = options.get('file') with open(inputfilepath, 'r') as file: reader = csv.reader(file) fail_count = 0 for i, row in enumerate(reader): if i == 0: # header row pass else: name = row[0] first_name = name.split()[0] username1 = row[1] email1 = row[2] email1_used = True if row[3].lower() == 'y' else False email1_is_fb = True if row[6].lower() == 'y' else False username2 = row[7] email2 = row[8] email2_used = True if row[9].lower() == 'y' else False email2_is_fb = True if row[12].lower() == 'y' else False username3 = row[13] if row[13] else None email3 = row[14] if row[14] else None email3_used = True if row[15].lower() == 'y' else False email3_is_fb = True if row[18].lower() == 'y' else False emails = [ em for em in [email1, email2, email3] if em is not None ] email_used = None num_used = sum([email1_used, email2_used, email3_used]) if num_used == 1: # only include email_used if one # and only one used email_used = [ em[0] for em in [ (email1, email1_used), (email2, email2_used), (email3, email3_used) ] if em[1] is True ][0] ctx = { 'first_name': first_name, 'username1': username1, 'email1': email1, 'email1_is_fb': email1_is_fb, 'username2': username2, 'email2': email2, 'email2_is_fb': email2_is_fb, 'username3': username3, 'email3': email3, 'email3_is_fb': email3_is_fb, 'email_count': 3 if username3 else 2, 'more_than_1_used': True if num_used > 1 else False, 'email_used': email_used } # send email try: email_msg = EmailMessage( '{} Duplicate accounts on booking system'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ), get_template( 'account/email/duplicate_account.txt' ).render(ctx), from_email=settings.DEFAULT_FROM_EMAIL, to=emails, reply_to=[settings.SUPPORT_EMAIL] ) email_msg.send() except Exception as e: fail_count += 1 # send mail to tech support with Exception send_support_email( e, __name__, "Duplicate account emails" ) self.stdout.write( 'Emails sent for {} accounts'.format(i - fail_count) ) self.stdout.write('{} failed sends'.format(fail_count))
def handle(self, *args, **options): bookings = [] for booking in Booking.objects.filter( event__date__gte=timezone.now(), event__advance_payment_required=True, status='OPEN', paid=False, payment_confirmed=False, date_booked__lte=timezone.now() - timedelta(hours=6)): # ignore any which have been rebooked in the past 6 hrs if booking.date_rebooked and \ (booking.date_rebooked >= (timezone.now() - timedelta(hours=6))): pass elif booking.event.date - timedelta( hours=booking.event.cancellation_period) < timezone.now( ) and booking.warning_sent: bookings.append(booking) elif booking.event.payment_due_date and booking.warning_sent: if booking.event.payment_due_date < timezone.now(): bookings.append(booking) elif booking.event.payment_time_allowed: # if there's a payment time allowed, cancel bookings booked # longer ago than this (bookings already filtered out # any booked or rebooked within 6 hrs) # don't check for warning sent this time # for free class requests, always allow them 24 hrs so admin # have time to mark classes as free (i.e.paid) last_booked_date = booking.date_rebooked \ if booking.date_rebooked else booking.date_booked if booking.free_class_requested: if last_booked_date < timezone.now() - timedelta(hours=24): bookings.append(booking) elif last_booked_date < timezone.now() \ - timedelta(hours=booking.event.payment_time_allowed): bookings.append(booking) for booking in bookings: event_was_full = booking.event.spaces_left == 0 ctx = { 'booking': booking, 'event': booking.event, 'date': booking.event.date.strftime('%A %d %B'), 'time': booking.event.date.strftime('%I:%M %p'), } # send mails to users try: send_mail( '{} Booking cancelled: {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, booking.event.name), get_template('booking/email/booking_auto_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [booking.user.email], html_message=get_template( 'booking/email/booking_auto_cancelled.html').render( ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "Automatic cancel job - cancelled email") booking.status = 'CANCELLED' booking.block = None booking.save() ActivityLog.objects.create( log='Unpaid booking id {} for event {}, user {} ' 'automatically cancelled'.format(booking.id, booking.event, booking.user)) if event_was_full: waiting_list_users = WaitingListUser.objects.filter( event=booking.event) try: send_waiting_list_email( booking.event, [user.user for user in waiting_list_users]) ActivityLog.objects.create( log='Waiting list email sent to user(s) {} for ' 'event {}'.format( ', '.join( [wluser.user.username for \ wluser in waiting_list_users] ), booking.event ) ) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "Automatic cancel job - waiting list email") if bookings: if settings.SEND_ALL_STUDIO_EMAILS: # send single mail to Studio try: send_mail( '{} Booking{} been automatically cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ' has' if len(bookings) == 1 else 's have'), get_template( 'booking/email/booking_auto_cancelled_studio_email.txt' ).render({'bookings': bookings}), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'booking/email/booking_auto_cancelled_studio_email.html' ).render({'bookings': bookings}), fail_silently=False) self.stdout.write( 'Cancellation emails sent for booking ids {}'.format( ', '.join( [str(booking.id) for booking in bookings]))) except Exception as e: # send mail to tech support with Exception send_support_email(e, __name__, "Automatic cancel job - studio email") else: self.stdout.write('No bookings to cancel')
def cancel_ticketed_event_view(request, slug): ticketed_event = get_object_or_404(TicketedEvent, slug=slug) open_paid_ticket_bookings = [ booking for booking in ticketed_event.ticket_bookings.all() if not booking.cancelled and booking.purchase_confirmed and booking.tickets.exists() and booking.paid ] open_unpaid_ticket_bookings = [ booking for booking in ticketed_event.ticket_bookings.all() if not booking.cancelled and booking.purchase_confirmed and booking.tickets.exists() and not booking.paid ] unconfirmed_ticket_bookings = TicketBooking.objects.filter( ticketed_event=ticketed_event, purchase_confirmed=False ) if request.method == 'POST': if 'confirm' in request.POST: host = 'http://{}'.format(request.META.get('HTTP_HOST')) for booking in open_paid_ticket_bookings + \ open_unpaid_ticket_bookings: booking.cancelled = True booking.save() try: # send notification email to user to all ticket booking, # paid or unpaid ctx = { 'host': host, 'ticketed_event': ticketed_event, 'ticket_booking': booking, } send_mail('{} {} has been cancelled'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticketed_event.name, ), get_template( 'studioadmin/email/ticketed_event_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [booking.user.email], html_message=get_template( 'studioadmin/email/ticketed_event_cancelled.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "cancel ticketed event - " "send notification email to user" ) for booking in unconfirmed_ticket_bookings: booking.delete() ticketed_event.cancelled = True ticketed_event.show_on_site = False ticketed_event.payment_open = False ticketed_event.save() if open_paid_ticket_bookings: # email studio with links for confirming refunds for paid only try: # send email to studio ctx = { 'host': host, 'open_paid_ticket_bookings': open_paid_ticket_bookings, 'ticketed_event': ticketed_event, } send_mail('{} Refunds due for ticket bookings for ' 'cancelled event {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, ticketed_event.name, ), get_template( 'studioadmin/email/to_studio_ticketed_event_cancelled.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'studioadmin/email/to_studio_ticketed_event_cancelled.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "cancel ticketed event - " "send refund notification email to studio" ) if open_paid_ticket_bookings and open_unpaid_ticket_bookings: booking_cancelled_msg = '{} has been cancelled; open ticket ' \ 'booking refs {} have been ' \ 'cancelled'.format( ticketed_event, ', '.join(['{}'.format(booking.booking_reference) for booking in open_paid_ticket_bookings] ) ) messages.info( request, booking_cancelled_msg + 'and notification emails have ' 'been sent.' ) else: booking_cancelled_msg = '{} has been cancelled; there were ' \ 'no open ticket bookings for this ' \ 'event'.format(ticketed_event) messages.info(request, booking_cancelled_msg) ActivityLog.objects.create( log="{} cancelled by admin user {}. {}".format( ticketed_event, request.user.username, booking_cancelled_msg ) ) return HttpResponseRedirect(reverse('studioadmin:ticketed_events')) elif 'cancel' in request.POST: return HttpResponseRedirect(reverse('studioadmin:ticketed_events')) context = { 'ticketed_event': ticketed_event, 'open_paid_ticket_bookings': open_paid_ticket_bookings, 'open_unpaid_ticket_bookings': open_unpaid_ticket_bookings, 'already_cancelled': ticketed_event.cancelled } return TemplateResponse( request, 'studioadmin/cancel_ticketed_event.html', context )
def post(self, request, *args, **kwargs): if 'cancel' in request.POST: self.ticket_booking.delete() messages.info( request, 'Ticket booking for {} has been cancelled.'.format( self.ticketed_event ) ) return HttpResponseRedirect(reverse('booking:ticketed_events')) context = self.get_context_data() ticket_purchase_form = context['ticket_purchase_form'] ticket_formset = context['ticket_formset'] if ticket_purchase_form.has_changed(): # tickets on current booking are only included in the tickets_left # calculation if purchase has been confirmed old_tickets = self.ticket_booking.tickets.all() old_ticket_count = old_tickets.count() if self.ticket_booking.purchase_confirmed: tickets_left_excl_this = self.ticketed_event.tickets_left() \ + old_ticket_count else: tickets_left_excl_this = self.ticketed_event.tickets_left() new_quantity = int(request.POST.get('ticket_purchase_form-quantity')) if new_quantity > tickets_left_excl_this: messages.error( request, 'Cannot purchase the number of tickets requested. ' 'Only {} tickets left.'.format( self.ticketed_event.tickets_left() ) ) else: # create the correct number of tickets on this booking if old_ticket_count < new_quantity: for i in range(new_quantity-old_ticket_count): Ticket.objects.create(ticket_booking=self.ticket_booking) if old_ticket_count > new_quantity: for ticket in old_tickets[new_quantity:]: ticket.delete() if old_ticket_count > 0: ActivityLog.objects.create( log="Ticket quantity updated on booking ref {}".format( self.ticket_booking.booking_reference ) ) tickets = self.ticket_booking.tickets.all() context['tickets'] = tickets return TemplateResponse(request, self.template_name, context) if 'ticket_formset-submit' in request.POST: if ticket_formset.is_valid(): ticket_formset.save() # we only create the paypal form if there is a ticket cost and # online payments are open if self.ticketed_event.ticket_cost and \ self.ticketed_event.payment_open: invoice_id = create_ticket_booking_paypal_transaction( self.request.user, self.ticket_booking ).invoice_id host = 'http://{}'.format(self.request.META.get('HTTP_HOST') ) paypal_form = PayPalPaymentsUpdateForm( initial=context_helpers.get_paypal_dict( host, self.ticketed_event.ticket_cost, self.ticketed_event, invoice_id, '{} {}'.format('ticket_booking', self.ticket_booking.id), paypal_email=self.ticketed_event.paypal_email, quantity=self.ticket_booking.tickets.count() ) ) context["paypalform"] = paypal_form self.ticket_booking.purchase_confirmed = True # reset the ticket_booking booked date to the date user confirms context['purchase_confirmed'] = True self.ticket_booking.date_booked = timezone.now() self.ticket_booking.save() ActivityLog.objects.create( log="Ticket Purchase confirmed: event {}, user {}, " "booking ref {}".format( self.ticketed_event.name, request.user.username, self.ticket_booking.booking_reference ) ) host = 'http://{}'.format(request.META.get('HTTP_HOST')) ctx = { 'host': host, 'ticketed_event': self.ticketed_event, 'ticket_booking': self.ticket_booking, 'ticket_count': self.ticket_booking.tickets.count(), 'user': request.user, } try: # send notification email to user send_mail('{} Ticket booking confirmed for {}: ref {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, self.ticketed_event, self.ticket_booking.booking_reference, ), get_template( 'booking/email/ticket_booking_made.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [request.user.email], html_message=get_template( 'booking/email/ticket_booking_made.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "ticket booking created - " "send email to user" ) if self.ticketed_event.email_studio_when_purchased: try: send_mail('{} Ticket booking confirmed for {}: ref {}'.format( settings.ACCOUNT_EMAIL_SUBJECT_PREFIX, self.ticketed_event, self.ticket_booking.booking_reference, ), get_template( 'booking/email/to_studio_ticket_booking_made.txt' ).render(ctx), settings.DEFAULT_FROM_EMAIL, [settings.DEFAULT_STUDIO_EMAIL], html_message=get_template( 'booking/email/to_studio_ticket_booking_made.html' ).render(ctx), fail_silently=False) except Exception as e: # send mail to tech support with Exception send_support_email( e, __name__, "ticket booking created - " "send email to studio" ) else: messages.error( request, "Please correct errors in the form below" ) tickets = self.ticket_booking.tickets.all() context['tickets'] = tickets ticket_purchase_form = TicketPurchaseForm( prefix='ticket_purchase_form', ticketed_event=self.ticketed_event, ticket_booking=self.ticket_booking, initial={'quantity': self.ticket_booking.tickets.count()} ) context["ticket_purchase_form"] = ticket_purchase_form return TemplateResponse(request, self.template_name, context)