def tickets_voucher(voucher_code=None): """ A user reaches this endpoint if they're sent a voucher code by email. Set up the voucher details in the session and redirect them to choose their tickets. """ voucher = Voucher.get_by_code(voucher_code) if voucher is None: return abort(404) if voucher.is_used: if current_user.is_authenticated: flash("""The voucher you have supplied has been used. If it was you who used it, the status of your purchase is below. Cancelling the payment made with the voucher will reactivate it so you can try again. """) return redirect(url_for("users.purchases")) else: abort(404) view = voucher.view if view: session["ticket_voucher"] = voucher_code return redirect(url_for("tickets.main", flow=view.name)) if "ticket_voucher" in session: del session["ticket_voucher"] invalid_vouchers.inc() flash("Ticket voucher was invalid") return redirect(url_for("tickets.main"))
def product_view_voucher_detail(view_id, voucher_code): view = ProductView.query.get_or_404(view_id) voucher = Voucher.get_by_code(voucher_code) if voucher is None: abort(404) return render_template("admin/products/view-voucher.html", view=view, voucher=voucher)
def product_view_edit_voucher(view_id, voucher_code): view = ProductView.query.get_or_404(view_id) voucher = Voucher.get_by_code(voucher_code) if voucher is None: abort(404) form = EditVoucherForm() if form.validate_on_submit(): form.update_voucher(voucher) db.session.commit() flash("Voucher updated") return redirect( url_for( ".product_view_voucher_detail", view_id=view_id, voucher_code=voucher_code, )) form.init_with_voucher(voucher) return render_template("admin/products/view-edit-voucher.html", view=view, voucher=voucher, form=form)
def handle_ticket_selection(form, view: ProductView, flow: str, basket: Basket): """ For consistency and to avoid surprises, we try to ensure a few things here: - if the user successfully submits a form with no errors, their basket is updated - if they don't, the basket is left untouched - the basket is updated to match what was submitted, even if they added tickets in another tab - if they already have tickets in their basket, only reserve the extra tickets as necessary - don't unreserve surplus tickets until the payment is created - if the user hasn't submitted anything, we use their current reserved ticket counts - if the user has reserved tickets from exhausted tiers on this view, we still show them - if the user has reserved tickets from other views, don't show and don't mess with them - this means the user can combine tickets from multiple views into a single basket - show the sold-out/unavailable states only when the user doesn't have reserved tickets We currently don't deal with multiple price tiers being available around the same time. Reserved tickets from a previous tier should be cancelled before activating a new one. """ if form.currency_code.data != get_user_currency(): set_user_currency(form.currency_code.data) # Commit so we don't lose the currency change if an error occurs db.session.commit() # Reload the basket because set_user_currency has changed it under us basket = Basket.from_session(current_user, get_user_currency()) form.add_to_basket(basket) if not any(basket.values()): empty_baskets.inc() if view.type == "tickets": flash("Please select at least one ticket to buy.") elif view.type == "hire": flash("Please select at least one item to hire.") else: flash("Please select at least one item to buy.") basket.save_to_session() return redirect(url_for("tickets.main", flow=flow)) # Ensure this purchase is valid for this voucher. voucher = Voucher.get_by_code(basket.voucher) if voucher and not voucher.check_capacity(basket): basket.save_to_session() if voucher.is_used: flash("Your voucher has been used by someone else.") else: flash(f"You can only purchase {voucher.tickets_remaining} adult " "tickets with this voucher. Please select fewer tickets.") return redirect(url_for("tickets.main", flow=flow)) app.logger.info("Saving basket %s", basket) try: # Convert the user's basket into a purchase. basket.create_purchases() basket.ensure_purchase_capacity() db.session.commit() except CapacityException as e: # Damn, capacity's gone since we created the purchases # Redirect back to show what's currently in the basket db.session.rollback() no_capacity.inc() app.logger.warn("Limit exceeded creating tickets: %s", e) flash( "We're very sorry, but there is not enough capacity available to " "allocate these tickets. You may be able to try again with a smaller amount." ) return redirect(url_for("tickets.main", flow=flow)) basket.save_to_session() if basket.total != 0: # Send the user off to pay return redirect(url_for("tickets.pay", flow=flow)) else: return handle_free_tickets(flow, view, basket)