Skip to content

feature:implement ticket option - one ticket per user #604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: development
Choose a base branch
from

Conversation

22f1001635
Copy link

@22f1001635 22f1001635 commented Apr 3, 2025

This PR enforces a one-ticket-per-user limit when the one_ticket_per_user event setting is enabled. The validation:

  • Checks the attendee's email against existing cart positions and paid/pending orders
  • Shows clear error messages if the limit is exceeded
  • Works alongside existing cart validation rules

No changes to existing behavior when the setting is disabled.

The restriction helps prevent duplicate ticket purchases while maintaining a smooth user experience.

Fixes #259

Summary by Sourcery

Implement a one-ticket-per-user restriction for event ticket purchases when enabled

New Features:

  • Add a configurable setting to limit ticket purchases to one per user for an event

Bug Fixes:

  • Resolve issue with potential multiple ticket purchases by the same user

Enhancements:

  • Implement validation to prevent duplicate ticket purchases by checking against existing cart positions and orders

Copy link
Contributor

sourcery-ai bot commented Apr 3, 2025

Reviewer's Guide by Sourcery

This PR implements a 'one ticket per user' feature. It adds a setting to the event configuration and enforces the restriction during the cart validation process by checking the attendee's email against existing cart positions and orders.

Sequence diagram for cart validation with one-ticket-per-user enabled

sequenceDiagram
    participant User
    participant CartService
    participant CartPosition
    participant OrderPosition
    participant Order

    User->>CartService: Adds item to cart
    CartService->>CartService: _check_item_constraints()
    alt event.settings.one_ticket_per_user is enabled
        CartService->>User: Get user email
        User->>CartService: Returns user email
        CartService->>CartPosition: Check existing cart positions with the same email
        CartPosition-->>CartService: Returns existing cart positions
        CartService->>OrderPosition: Check existing order positions with the same email
        OrderPosition-->>CartService: Returns existing order positions
        alt existing cart positions or orders found
            CartService-->>User: Raise CartError (one_ticket_per_user)
        else no existing cart positions or orders found
            CartService->>CartService: Continue with other validations
        end
    else event.settings.one_ticket_per_user is disabled
        CartService->>CartService: Continue with other validations
    end
    CartService-->>User: Item added to cart (or error)
Loading

File-Level Changes

Change Details Files
Implements one-ticket-per-user restriction by validating the attendee's email against existing cart positions and paid/pending orders.
  • Added a new error message for the one-ticket-per-user restriction.
  • Checks if the one_ticket_per_user setting is enabled.
  • Retrieves the user's email address from the widget data.
  • Queries existing cart positions and orders associated with the user's email.
  • Raises a CartError if existing positions or orders are found.
  • Added email address is required validation.
src/pretix/base/services/cart.py
Adds a setting to the event settings form to enable or disable the one-ticket-per-user restriction.
  • Added a one_ticket_per_user field to the TicketSettingsForm.
  • Added a condition to remove the one_ticket_per_user field if attendee_emails_asked is not enabled.
  • Added a help text to the one_ticket_per_user field.
  • Added a translation for the one_ticket_per_user label and help text.
src/pretix/control/forms/event.py
src/pretix/control/templates/pretixcontrol/event/tickets.html

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!
  • Generate a plan of action for an issue: Comment @sourcery-ai plan on
    an issue to generate a plan of action for it.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @22f1001635 - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding a database index on attendee_email in CartPosition and OrderPosition to improve query performance.
  • It might be better to perform the email check in a separate service function to keep the cart service focused.
Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@@ -310,6 +312,29 @@ def _check_item_constraints(self, op, current_ops=[]):
), self.event.timezone)
if term_last < self.now_dt:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider extracting the 'one ticket per user' conditional into a helper method to improve readability and reduce redundancy by avoiding duplicate setting checks and reducing nesting complexity within the main function.

Consider extracting the "one ticket per user" conditional into its own helper method to reduce nesting and eliminate the redundant setting check. For example, you could add a helper similar to:

def _validate_one_ticket(self, error_messages):
    user_email = self._widget_data.get('email')
    if not user_email:
        raise CartError(_('Email address is required for ticket purchase'))
    user_email = user_email.lower().strip()
    existing_positions = CartPosition.objects.filter(
        event=self.event,
        attendee_email=user_email,
    ).exclude(
        pk__in=[op.position.id for op in self._operations if isinstance(op, self.RemoveOperation)]
    )
    existing_orders = OrderPosition.objects.filter(
        order__event=self.event,
        attendee_email=user_email,
        order__status__in=[Order.STATUS_PENDING, Order.STATUS_PAID]
    )
    if existing_positions.exists() or existing_orders.exists():
        raise CartError(error_messages['one_ticket_per_user'])

Then simplify your main flow by replacing the nested block with a single call:

if self.event.settings.one_ticket_per_user:
    self._validate_one_ticket(error_messages)

This flattening avoids duplicate setting checks and clarifies the one-ticket validation logic without altering functionality.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@22f1001635 please consider this suggestion

@norbusan
Copy link
Member

norbusan commented Apr 7, 2025

How have you tested these changes?
Do you have unit tests to be added?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement ticket option - one ticket per user
2 participants