コード例 #1
0
ファイル: test_product_view.py プロジェクト: emfcamp/Website
def test_product_view_accessible_voucher_expiry(db, user, monkeypatch):
    EXPIRED_YESTERDAY = "test1"
    EXPIRES_TOMORROW = "test2"
    product_view = ProductView(name="other", type="ticket", vouchers_only=True)
    db.session.add(product_view)
    db.session.add(
        Voucher(
            view=product_view,
            code=EXPIRED_YESTERDAY,
            expiry=datetime.utcnow() - timedelta(days=1) - VOUCHER_GRACE_PERIOD,
        )
    )
    db.session.add(
        Voucher(
            view=product_view,
            code=EXPIRES_TOMORROW,
            expiry=datetime.utcnow() + timedelta(days=1),
        )
    )
    db.session.commit()

    assert not product_view.is_accessible(
        user, voucher=EXPIRED_YESTERDAY
    ), "View should be inaccessible with expired voucher"

    assert product_view.is_accessible(
        user, voucher=EXPIRES_TOMORROW
    ), "View should be accessible with in-date voucher"
コード例 #2
0
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"))
コード例 #3
0
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)
コード例 #4
0
def product_view_bulk_add_vouchers_by_email(view_id):
    view = ProductView.query.get_or_404(view_id)
    form = BulkVoucherEmailForm()

    if form.validate_on_submit():
        emails = set(e.strip() for e in re.split(r"[\s,]+", form.emails.data))

        if len(emails) > 250:
            flash(
                "More than 250 emails provided. Please submit <250 at a time.")
            return redirect(
                url_for(".product_view_bulk_add_vouchers_by_email",
                        view_id=view_id))

        added = existing = 0

        for email in emails:
            if Voucher.query.filter_by(email=email).first():
                existing += 1
                continue

            voucher = Voucher(
                view,
                code=random_voucher(),
                email=email,
                expiry=form.expires.data,
                purchases_remaining=form.num_purchases.data,
                tickets_remaining=form.num_tickets.data,
            )

            msg = Message(
                f"Your voucher for Electromagnetic Field {event_year()}",
                sender=app.config["TICKETS_EMAIL"],
                recipients=[email],
            )

            msg.body = render_template("emails/volunteer-voucher.txt",
                                       voucher=voucher)

            app.logger.info("Emailing %s volunteer voucher: %s", email,
                            voucher.code)
            db.session.commit()
            mail.send(msg)
            added += 1

        flash(f"{added} vouchers added, {existing} duplicates skipped.")

        return redirect(
            url_for(".product_view_bulk_add_vouchers_by_email",
                    view_id=view_id))

    return render_template("admin/products/view-bulk-add-voucher.html",
                           view=view,
                           form=form)
コード例 #5
0
def test_product_view_accessible_voucher_expiry(db, user, monkeypatch):
    # Vouchers should work regardless of SALES_START so set it into the future
    def sales_start_mock_future(key):
        return TOMORROW

    # Patch config_date rather than utcnow as apparently you can't patch builtins
    monkeypatch.setattr("models.product.config_date", sales_start_mock_future)

    product_view = ProductView(name="other", type="ticket", vouchers_only=True)
    db.session.add(product_view)
    db.session.add(Voucher(view=product_view, code="test1", expiry=YESTERDAY))
    db.session.add(Voucher(view=product_view, code="test2", expiry=TOMORROW))
    db.session.commit()

    assert not product_view.is_accessible(
        user, voucher="test1"
    ), "View should be inaccessible with expired voucher"

    assert product_view.is_accessible(
        user, voucher="test2"
    ), "View should be accessible with in-date voucher"
コード例 #6
0
ファイル: test_product_view.py プロジェクト: emfcamp/Website
def test_product_view_accessible(db, user, monkeypatch):
    product_view = ProductView(name="other", type="ticket")
    assert product_view.is_accessible(user), "Default view should be visible"

    product_view = ProductView(name="another-other", type="ticket", vouchers_only=True)
    voucher = Voucher(view=product_view)

    db.session.add(product_view)
    db.session.add(voucher)
    db.session.commit()

    assert product_view.is_accessible(
        user, voucher.code
    ), "Product should be visible with voucher"

    assert not product_view.is_accessible(
        user
    ), "Product should be inaccessible without voucher"

    assert not product_view.is_accessible(
        user, "wrong"
    ), "Product should be inaccessible with incorrect voucher"

    product_view = ProductView(name="cfp", cfp_accepted_only=True, type="ticket")
    assert not product_view.is_accessible(
        user
    ), "CfP products should not be visible without accepted proposal"

    proposal = TalkProposal()
    proposal.title = "title"
    proposal.description = "description"
    proposal.requirements = "requirements"
    proposal.user = user
    db.session.add(proposal)
    db.session.commit()
    proposal.set_state("accepted")

    assert product_view.is_accessible(
        user
    ), "CfP products should be visible with accepted proposal"
コード例 #7
0
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)
コード例 #8
0
def product_view_add_voucher(view_id):
    view = ProductView.query.get_or_404(view_id)
    form = NewVoucherForm()

    if form.validate_on_submit():
        voucher = Voucher(
            view,
            code=form.voucher.data,
            expiry=form.expires.data,
            purchases_remaining=form.num_purchases.data,
            tickets_remaining=form.num_tickets.data,
        )
        db.session.commit()
        flash("Voucher successfully created")
        return redirect(
            url_for(
                ".product_view_voucher_detail",
                view_id=view_id,
                voucher_code=voucher.code,
            ))

    return render_template("admin/products/view-add-voucher.html",
                           view=view,
                           form=form)
コード例 #9
0
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)
コード例 #10
0
def product_view_bulk_add_vouchers_by_email(view_id):
    view = ProductView.query.get_or_404(view_id)
    form = BulkVoucherEmailForm()

    if not form.validate_on_submit() or form.preview.data:
        preview = None
        if form.preview.data:
            preview = format_trusted_html_email(
                form.text.data,
                form.subject.data,
                voucher_url="http://VOUCHER_URL_PLACEHOLDER",
                expiry=form.expires.data,
                reason=form.reason.data,
            )

        return render_template(
            "admin/products/view-bulk-add-voucher.html",
            view=view,
            form=form,
            preview=preview,
        )

    emails = set(e.strip() for e in re.split(r"[\s,]+", form.emails.data))

    if len(emails) >= 250:
        flash("More than 250 emails provided. Please submit <250 at a time.")
        return redirect(
            url_for(".product_view_bulk_add_vouchers_by_email",
                    view_id=view_id))

    added = existing = sent_total = 0

    with mail.get_connection() as conn:
        for email in emails:
            if Voucher.query.filter_by(email=email).first():
                existing += 1
                continue

            voucher = Voucher(
                view,
                code=random_voucher(),
                email=email,
                expiry=form.expires.data,
                purchases_remaining=form.num_purchases.data,
                tickets_remaining=form.num_tickets.data,
            )

            voucher_url = external_url("tickets.tickets_voucher",
                                       voucher_code=voucher.code)

            plaintext = format_trusted_plaintext_email(
                form.text.data,
                voucher_url=voucher_url,
                expiry=form.expires.data)
            html = format_trusted_html_email(
                form.text.data,
                form.subject.data,
                voucher_url=voucher_url,
                expiry=form.expires.data,
                reason=form.reason.data,
            )

            app.logger.info("Emailing %s volunteer voucher: %s", email,
                            voucher.code)
            sent_count = mail.send_mail(
                subject=form.subject.data,
                message=plaintext,
                from_email=from_email("TICKETS_EMAIL"),
                recipient_list=[email],
                fail_silently=True,
                connection=conn,
                html_message=html,
            )
            db.session.commit()
            sent_total += sent_count
            added += 1

    flash(
        f"{added} vouchers added, {sent_total} emails sent, {existing} duplicates skipped."
    )

    return redirect(
        url_for(".product_view_bulk_add_vouchers_by_email", view_id=view_id))