mirror of
https://github.com/pretix/pretix.git
synced 2026-09-29 18:54:42 +00:00
Use fragment_product_list in voucher redemption view (Z#23246929) (#6567)
* Use fragment_product_list in voucher redemption view (Z#23246929) * Formatting * Fix usage in templates * Review comments * handle form prefix in fragment_product_list.html --------- Co-authored-by: Raphael Michel <michel@pretix.eu>
This commit is contained in:
@@ -459,3 +459,16 @@ def _get_variant_unavailability_reason(variant, now_dt: Optional[datetime]=None,
|
||||
return 'available_until'
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _item_num_options(item):
|
||||
if item.current_unavailability_reason:
|
||||
return 0
|
||||
if item.has_variations:
|
||||
return len([v for v in item.available_variations if not v.current_unavailability_reason])
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
def get_item_option_count(items):
|
||||
return sum(_item_num_options(item) for item in items)
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
{% endif %}
|
||||
{% for item in items %}
|
||||
{% if item.has_variations %}
|
||||
<article aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="{{ form_prefix }}item-{{ item.pk }}-description"{% endif %} class="item-with-variations{% if event.settings.show_variations_expanded %} details-open{% endif %}" id="{{ form_prefix }}item-{{ item.pk }}">
|
||||
<article aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="{{ form_prefix }}item-{{ item.pk }}-description"{% endif %} class="item-with-variations{% if event.settings.show_variations_expanded or always_show_open %} details-open{% endif %}" id="{{ form_prefix }}item-{{ item.pk }}">
|
||||
<div class="row product-row headline">
|
||||
<div class="col-md-8 col-sm-6 col-xs-12">
|
||||
{% if item.picture %}
|
||||
@@ -85,7 +85,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box">
|
||||
{% if not event.settings.show_variations_expanded %}
|
||||
{% if not event.settings.show_variations_expanded and not always_show_open %}
|
||||
{% if item.best_variation_availability <= 10 %}
|
||||
{% if not item.min_price %}
|
||||
<strong class="gone">{% trans "FULLY BOOKED" %}</strong>
|
||||
@@ -116,7 +116,7 @@
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="variations {% if not event.settings.show_variations_expanded %}variations-collapsed{% endif %}" id="{{ form_prefix }}item-{{ item.pk }}-variations">
|
||||
<div class="variations {% if not event.settings.show_variations_expanded and not always_show_open %}variations-collapsed{% endif %}" id="{{ form_prefix }}item-{{ item.pk }}-variations">
|
||||
{% for var in item.available_variations %}
|
||||
<article aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-legend"{% if var.description %} aria-describedby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-description"{% endif %} class="row product-row variation" id="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}"
|
||||
{% if not item.free_price %}
|
||||
@@ -222,37 +222,54 @@
|
||||
</div>
|
||||
{% if var.cached_availability.0 == 100 and not item.current_unavailability_reason and not var.current_unavailability_reason %}
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box available">
|
||||
{% if var.order_max == 1 %}
|
||||
<label class="btn btn-default btn-checkbox{% if not ev.presale_is_running %} disabled{% endif %}">
|
||||
<input type="checkbox" value="1"
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-variation-{{ item.pk }}-{{ var.pk }}"
|
||||
{% endif %}
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
id="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
name="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-legend {{ form_prefix }}item-{{ item.pk }}-legend"
|
||||
{% if var.description %} aria-describedby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-description"{% endif %}>
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
{% trans "Select" context "checkbox" %}
|
||||
{% if max_choices == 1 and not form_prefix %}
|
||||
{# items_from_post_data which parses _selected_item does not support form_prefix at the moment #}
|
||||
<label>
|
||||
<input type="radio" name="{{ form_prefix }}_selected_item"
|
||||
{% if itemnum == 1 %}checked="checked"{% endif %}
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
id="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
value="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-legend"
|
||||
{% if var.description %}aria-describedby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-description"{% endif %}
|
||||
{% if item.free_price %}data-checked-onchange="{{ form_prefix }}price-variation-{{ item.pk }}-{{ var.pk }}"{% endif %}
|
||||
>
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% trans "Quantity" %}</legend>
|
||||
<button type="button" data-step="-1" data-controls="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="- {{ item }}, {{ var }}: {% trans "Decrease quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if var.order_max == 1 %}
|
||||
<label class="btn btn-default btn-checkbox{% if not ev.presale_is_running %} disabled{% endif %}">
|
||||
<input type="checkbox" value="1"
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-variation-{{ item.pk }}-{{ var.pk }}"
|
||||
data-checked-onchange="price-variation-{{ item.pk }}-{{ var.pk }}"
|
||||
{% endif %}
|
||||
max="{{ var.order_max }}"
|
||||
id="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
name="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-legend {{ form_prefix }}item-{{ item.pk }}-legend">
|
||||
<button type="button" data-step="1" data-controls="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="+ {{ item }}, {{ var }}: {% trans "Increase quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>+</button>
|
||||
</fieldset>
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if itemnum == 1 %}checked="checked"{% endif %}
|
||||
id="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
name="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-legend {{ form_prefix }}item-{{ item.pk }}-legend"
|
||||
{% if var.description %}aria-describedby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-description"{% endif %}>
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% trans "Quantity" %}</legend>
|
||||
<button type="button" data-step="-1" data-controls="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="- {{ item }}, {{ var }}: {% trans "Decrease quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-variation-{{ item.pk }}-{{ var.pk }}"
|
||||
{% endif %}
|
||||
max="{{ var.order_max }}"
|
||||
{% if itemnum == 1 %}checked="checked"{% endif %}
|
||||
id="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
name="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-{{ var.pk }}-legend {{ form_prefix }}item-{{ item.pk }}-legend">
|
||||
<button type="button" data-step="1" data-controls="{{ form_prefix }}variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="+ {{ item }}, {{ var }}: {% trans "Increase quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>+</button>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -387,37 +404,52 @@
|
||||
</div>
|
||||
{% if item.cached_availability.0 == 100 and not item.current_unavailability_reason %}
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box available">
|
||||
{% if item.order_max == 1 %}
|
||||
<label class="btn btn-default btn-checkbox{% if not ev.presale_is_running %} disabled{% endif %}">
|
||||
<input type="checkbox" value="1" {% if itemnum == 1 %}checked{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-item-{{ item.pk }}"
|
||||
{% endif %}
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
name="{{ form_prefix }}item_{{ item.id }}" id="{{ form_prefix }}item_{{ item.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-legend"
|
||||
{% if item.description %} aria-describedby="{{ form_prefix }}item-{{ item.id }}-description"{% endif %}>
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
{% trans "Select" context "checkbox" %}
|
||||
{% if max_choices == 1 and not form_prefix %}
|
||||
{# items_from_post_data which parses _selected_item does not support form_prefix at the moment #}
|
||||
<label>
|
||||
<input type="radio" name="{{ form_prefix }}_selected_item"
|
||||
{% if itemnum == 1 %}checked="checked"{% endif %}
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
id="{{ form_prefix }}item_{{ item.id }}"
|
||||
value="{{ form_prefix }}item_{{ item.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-legend"
|
||||
{% if item.description %}aria-describedby="{{ form_prefix }}item-{{ item.id }}-description"{% endif %}
|
||||
{% if item.free_price %}data-checked-onchange="{{ form_prefix }}price-item-{{ item.pk }}"{% endif %}
|
||||
>
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% trans "Quantity" %}</legend>
|
||||
<button type="button" data-step="-1" data-controls="{{ form_prefix }}item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="- {{ item }}: {% trans "Decrease quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if itemnum == 1 %}value="1"{% endif %}
|
||||
{% if item.order_max == 1 %}
|
||||
<label class="btn btn-default btn-checkbox{% if not ev.presale_is_running %} disabled{% endif %}">
|
||||
<input type="checkbox" value="1" {% if itemnum == 1 %}checked{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-item-{{ item.pk }}"
|
||||
data-checked-onchange="price-item-{{ item.pk }}"
|
||||
{% endif %}
|
||||
max="{{ item.order_max }}"
|
||||
name="{{ form_prefix }}item_{{ item.id }}"
|
||||
id="{{ form_prefix }}item_{{ item.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-legend">
|
||||
<button type="button" data-step="1" data-controls="{{ form_prefix }}item_{{ item.id }}" class="btn btn-default input-item-count-inc" aria-label="+ {{ item }}: {% trans "Increase quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>+</button>
|
||||
</fieldset>
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
name="{{ form_prefix }}item_{{ item.id }}" id="{{ form_prefix }}item_{{ item.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-legend"
|
||||
{% if item.description %}aria-describedby="{{ form_prefix }}item-{{ item.id }}-description"{% endif %}>
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% trans "Quantity" %}</legend>
|
||||
<button type="button" data-step="-1" data-controls="{{ form_prefix }}item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="- {{ item }}: {% trans "Decrease quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if itemnum == 1 %}value="1"{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-item-{{ item.pk }}"
|
||||
{% endif %}
|
||||
max="{{ item.order_max }}"
|
||||
name="{{ form_prefix }}item_{{ item.id }}"
|
||||
id="{{ form_prefix }}item_{{ item.id }}"
|
||||
aria-labelledby="{{ form_prefix }}item-{{ item.pk }}-legend">
|
||||
<button type="button" data-step="1" data-controls="{{ form_prefix }}item_{{ item.id }}" class="btn btn-default input-item-count-inc" aria-label="+ {{ item }}: {% trans "Increase quantity" %}"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}>+</button>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
{% if options == 0 and not seating_available %}
|
||||
{% if itemnum == 0 and not seating_available %}
|
||||
{% if request.event.has_subevents and not voucher.subevent %}
|
||||
{% blocktrans trimmed %}
|
||||
For the selected date, there are currently no products available that can be bought with this voucher. Please try a different date or a different voucher.
|
||||
@@ -73,353 +73,10 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% for tup in items_by_category %}
|
||||
<section {% if tup.0 %}aria-labelledby="category-{{ tup.0.id }}"{% else %}aria-label="{% trans "Uncategorized items" %}"{% endif %}{% if tup.0.description %} aria-describedby="category-info-{{ tup.0.id }}"{% endif %}>
|
||||
{% if tup.0 %}
|
||||
<h3 id="category-{{ tup.0.id }}">{{ tup.0.name }}</h3>
|
||||
{% if tup.0.description %}
|
||||
<div id="category-info-{{ tup.0.id }}">{{ tup.0.description|localize|rich_text }}</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% for item in tup.1 %}
|
||||
{% if item.has_variations %}
|
||||
<article aria-labelledby="item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="item-{{ item.pk }}-description"{% endif %} class="item-with-variations" id="item-{{ item.pk }}">
|
||||
<div class="row product-row headline">
|
||||
<div class="col-md-8 col-sm-6 col-xs-12">
|
||||
{% if item.picture %}
|
||||
<a href="{{ item.picture.url }}" class="productpicture"
|
||||
data-title="{{ item.name|force_escape|force_escape }}"
|
||||
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||
data-lightbox="{{ item.id }}"
|
||||
aria-label="{% blocktrans trimmed with item=item.name %}Show full-size image of {{ item }}{% endblocktrans %}">
|
||||
<img src="{{ item.picture|thumb:'60x60^' }}"
|
||||
alt="{{ item.name }}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
<div class="product-description {% if item.picture %}with-picture{% endif %}">
|
||||
<h4 id="item-{{ item.pk }}-legend">
|
||||
{{ item.name }}
|
||||
</h4>
|
||||
{% if item.description %}
|
||||
<div id="item-{{ item.pk }}-description" class="product-description">
|
||||
{{ item.description|localize|rich_text }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 price">
|
||||
{% if item.min_price != item.max_price or item.free_price %}
|
||||
{% blocktrans trimmed with minprice=item.min_price|money:event.currency %}
|
||||
from {{ minprice }}
|
||||
{% endblocktrans %}
|
||||
{% elif not item.min_price and not item.max_price %}
|
||||
{% if not item.mandatory_priced_addons or voucher.all_addons_included %}
|
||||
<span class="text-uppercase">{% trans "free" context "price" %}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ item.min_price|money:event.currency }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box">
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="variations">
|
||||
{% for var in item.available_variations %}
|
||||
<article aria-labelledby="item-{{ item.pk }}-{{ var.pk }}-legend"{% if var.description %} aria-describedby="item-{{ item.pk }}-{{ var.pk }}-description"{% endif %} class="row product-row variation" id="item-{{ item.pk }}-{{ var.pk }}"
|
||||
{% if not item.free_price %}
|
||||
data-price="{% if event.settings.display_net_prices %}{{ var.display_price.net|unlocalize }}{% else %}{{ var.display_price.gross|unlocalize }}{% endif %}"
|
||||
{% endif %}>
|
||||
<div class="col-md-8 col-sm-6 col-xs-12">
|
||||
<h5 id="item-{{ item.pk }}-{{ var.pk }}-legend">{{ var }}</h5>
|
||||
{% if var.description %}
|
||||
<div id="item-{{ item.pk }}-{{ var.pk }}-description" class="variation-description">
|
||||
{{ var.description|localize|rich_text }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if item.do_show_quota_left %}
|
||||
{% include "pretixpresale/event/fragment_quota_left.html" with avail=var.cached_availability %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 price">
|
||||
{% if var.original_price %}
|
||||
<p>
|
||||
<del><span class="sr-only">{% trans "Original price:" %}</span>
|
||||
{% if event.settings.display_net_prices %}
|
||||
{{ var.original_price.net|money:event.currency }}
|
||||
{% else %}
|
||||
{{ var.original_price.gross|money:event.currency }}
|
||||
{% endif %}
|
||||
</del>
|
||||
{% if item.free_price %}
|
||||
</p>
|
||||
{% else %}
|
||||
<ins><span class="sr-only">{% trans "New price:" %}</span>
|
||||
{% endif %}
|
||||
{% elif not item.free_price %}
|
||||
<p>
|
||||
{% endif %}
|
||||
{% if item.free_price %}
|
||||
<label class="sr-only" for="price-variation-{{ item.pk }}-{{ var.pk }}">{% blocktrans trimmed with item=var.value %}Modify price for {{ item }}{% endblocktrans %}</label>
|
||||
<div class="input-group input-group-price">
|
||||
<span class="input-group-addon">{{ event.currency }}</span>
|
||||
<input type="number" class="form-control input-item-price"
|
||||
id="price-variation-{{ item.pk }}-{{ var.pk }}"
|
||||
placeholder="0"
|
||||
min="{% if event.settings.display_net_prices %}{{ var.display_price.net|stringformat:"0.2f" }}{% else %}{{ var.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
name="price_{{ item.id }}_{{ var.id }}"
|
||||
{% if var.suggested_price.gross != var.display_price.gross %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
title="{% blocktrans trimmed with item=var.value price=var.display_price.net|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=var.value price=var.display_price.gross|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=var.value %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
value="{% if event.settings.display_net_prices %}{{var.suggested_price.net|stringformat:"0.2f" }}{% else %}{{ var.suggested_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
step="any">
|
||||
</div>
|
||||
<p>
|
||||
{% elif not var.display_price.gross %}
|
||||
{% if not item.mandatory_priced_addons or var.original_price or voucher.all_addons_included %}
|
||||
<span class="text-uppercase">{% trans "free" context "price" %}</span>
|
||||
{% endif %}
|
||||
{% elif event.settings.display_net_prices %}
|
||||
{{ var.display_price.net|money:event.currency }}
|
||||
{% else %}
|
||||
{{ var.display_price.gross|money:event.currency }}
|
||||
{% endif %}
|
||||
{% if item.original_price or var.original_price %}
|
||||
</ins>
|
||||
{% endif %}
|
||||
{% if item.includes_mixed_tax_rate %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
<small>{% trans "plus taxes" %}</small>
|
||||
{% else %}
|
||||
<small>{% trans "incl. taxes" %}</small>
|
||||
{% endif %}
|
||||
{% elif var.display_price.rate and var.display_price.gross and event.settings.display_net_prices %}
|
||||
<small data-toggle="tooltip" title="{% blocktrans trimmed with value=var.display_price.gross|money:event.currency %}{{ value }} incl. taxes{% endblocktrans %}" data-placement="bottom">
|
||||
{% blocktrans trimmed with rate=var.display_price.rate|tax_rate_format name=var.display_price.name %}
|
||||
<strong>plus</strong> {{ rate }}% {{ name }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% elif var.display_price.rate and var.display_price.gross %}
|
||||
<small data-toggle="tooltip" title="{% blocktrans trimmed with value=var.display_price.net|money:event.currency %}{{ value }} without taxes{% endblocktrans %}" data-placement="bottom">
|
||||
{% blocktrans trimmed with rate=var.display_price.rate|tax_rate_format name=var.display_price.name %}
|
||||
incl. {{ rate }}% {{ name }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% if var.cached_availability.0 == 100 and not item.current_unavailability_reason %}
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box available radio-box">
|
||||
{% if max_times > 1 %}
|
||||
{% if var.order_max == 1 %}
|
||||
<label class="btn btn-default btn-checkbox">
|
||||
<input type="checkbox"
|
||||
value="1"
|
||||
id="variation_{{ item.id }}_{{ var.id }}"
|
||||
name="variation_{{ item.id }}_{{ var.id }}"
|
||||
{% if options == 1 %}checked{% endif %}
|
||||
aria-labelledby="item-{{ item.pk }}-{{ var.pk }}-legend item-{{ item.pk }}-legend"
|
||||
{% if var.description %} aria-describedby="item-{{ item.pk }}-{{ var.pk }}-description"{% endif %}>
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% trans "Quantity" %}</legend>
|
||||
<button type="button" data-step="-1" data-controls="variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="- {{ item }}, {{ var }}: {% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
max="{{ var.order_max }}"
|
||||
id="variation_{{ item.id }}_{{ var.id }}"
|
||||
name="variation_{{ item.id }}_{{ var.id }}"
|
||||
{% if options == 1 %}value="1"{% endif %}
|
||||
aria-labelledby="item-{{ item.pk }}-{{ var.pk }}-legend item-{{ item.pk }}-legend">
|
||||
<button type="button" data-step="1" data-controls="variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="+ {{ item }}, {{ var }}: {% trans "Increase quantity" %}">+</button>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<label>
|
||||
<input type="radio" name="_voucher_item"
|
||||
{% if options == 1 %}checked="checked"{% endif %}
|
||||
id="variation_{{ item.id }}_{{ var.id }}"
|
||||
value="variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-labelledby="item-{{ item.pk }}-{{ var.pk }}-legend"
|
||||
{% if var.description %} aria-describedby="item-{{ item.pk }}-{{ var.pk }}-description"{% endif %}>
|
||||
</label>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% include "pretixpresale/event/fragment_availability.html" with price=var.display_price.gross original_price=var.original_price.gross avail=var.cached_availability.0 item=item %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
{% else %}
|
||||
<article aria-labelledby="item-{{ item.pk }}-legend"{% if item.description %} aria-describedby="item-{{ item.pk }}-description"{% endif %} class="row product-row simple" id="item-{{ item.pk }}"
|
||||
{% if not item.free_price %}
|
||||
data-price="{% if event.settings.display_net_prices %}{{ item.display_price.net|unlocalize }}{% else %}{{ item.display_price.gross|unlocalize }}{% endif %}"
|
||||
{% endif %}>
|
||||
<div class="col-md-8 col-sm-6 col-xs-12">
|
||||
{% if item.picture %}
|
||||
<a href="{{ item.picture.url }}" class="productpicture"
|
||||
data-title="{{ item.name|force_escape|force_escape }}"
|
||||
{# Yes, double-escape to prevent XSS in lightbox #}
|
||||
data-lightbox="{{ item.id }}"
|
||||
aria-label="{% blocktrans trimmed with item=item.name %}Show full-size image of {{ item }}{% endblocktrans %}">
|
||||
<img src="{{ item.picture|thumb:'60x60^' }}"
|
||||
alt="{{ item.name }}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
<div class="product-description {% if item.picture %}with-picture{% endif %}">
|
||||
<h4 id="item-{{ item.pk }}-legend">{{ item.name }}</h4>
|
||||
{% if item.description %}
|
||||
<div id="item-{{ item.pk }}-description" class="product-description">
|
||||
{{ item.description|localize|rich_text }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if item.do_show_quota_left %}
|
||||
{% include "pretixpresale/event/fragment_quota_left.html" with avail=item.cached_availability %}
|
||||
{% endif %}
|
||||
{% if item.min_per_order and item.min_per_order > 1 %}
|
||||
<p>
|
||||
<small>
|
||||
{% blocktrans trimmed with num=item.min_per_order %}
|
||||
minimum amount to order: {{ num }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 price">
|
||||
{% if item.original_price %}
|
||||
<p>
|
||||
<del><span class="sr-only">{% trans "Original price:" %}</span>
|
||||
{% if event.settings.display_net_prices %}
|
||||
{{ item.original_price.net|money:event.currency }}
|
||||
{% else %}
|
||||
{{ item.original_price.gross|money:event.currency }}
|
||||
{% endif %}
|
||||
</del>
|
||||
{% if item.free_price %}
|
||||
</p>
|
||||
{% else %}
|
||||
<ins><span class="sr-only">{% trans "New price:" %}</span>
|
||||
{% endif %}
|
||||
{% elif not item.free_price %}
|
||||
<p>
|
||||
{% endif %}
|
||||
{% if item.free_price %}
|
||||
<label class="sr-only" for="price-item-{{ item.pk }}">{% blocktrans trimmed with item=item.name currency=event.currency %}Set price in {{ currency }} for {{ item }}{% endblocktrans %}</label>
|
||||
<div class="input-group input-group-price">
|
||||
<span class="input-group-addon">{{ event.currency }}</span>
|
||||
<input type="number" class="form-control input-item-price" placeholder="0"
|
||||
min="{% if event.settings.display_net_prices %}{{ item.display_price.net|stringformat:"0.2f" }}{% else %}{{ item.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
name="price_{{ item.id }}"
|
||||
{% if item.suggested_price.gross != item.display_price.gross %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
title="{% blocktrans trimmed with item=item.name price=item.display_price.net|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=item.name price=item.display_price.gross|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=item.name %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
value="{% if event.settings.display_net_prices %}{{ item.suggested_price.net|stringformat:"0.2f" }}{% else %}{{ item.suggested_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
step="any">
|
||||
</div>
|
||||
<p>
|
||||
{% elif not item.display_price.gross %}
|
||||
{% if not item.mandatory_priced_addons or item.original_price or voucher.all_addons_included %}
|
||||
<span class="text-uppercase">{% trans "free" context "price" %}</span>
|
||||
{% endif %}
|
||||
{% elif event.settings.display_net_prices %}
|
||||
{{ item.display_price.net|money:event.currency }}
|
||||
{% else %}
|
||||
{{ item.display_price.gross|money:event.currency }}
|
||||
{% endif %}
|
||||
{% if item.original_price %}
|
||||
</ins>
|
||||
{% endif %}
|
||||
{% if item.includes_mixed_tax_rate %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
<small>{% trans "plus taxes" %}</small>
|
||||
{% else %}
|
||||
<small>{% trans "incl. taxes" %}</small>
|
||||
{% endif %}
|
||||
{% elif item.display_price.rate and item.display_price.gross and event.settings.display_net_prices %}
|
||||
<small data-toggle="tooltip" title="{% blocktrans trimmed with value=item.display_price.gross|money:event.currency %}{{ value }} incl. taxes{% endblocktrans %}" data-placement="bottom">
|
||||
{% blocktrans trimmed with rate=item.display_price.rate|tax_rate_format name=item.display_price.name %}
|
||||
<strong>plus</strong> {{ rate }}% {{ name }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% elif item.display_price.rate and item.display_price.gross %}
|
||||
<small data-toggle="tooltip" title="{% blocktrans trimmed with value=item.display_price.net|money:event.currency %}{{ value }} without taxes{% endblocktrans %}" data-placement="bottom">
|
||||
{% blocktrans trimmed with rate=item.display_price.rate|tax_rate_format name=item.display_price.name %}
|
||||
incl. {{ rate }}% {{ name }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% if item.cached_availability.0 == 100 and not item.current_unavailability_reason %}
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box available radio-box">
|
||||
{% if max_times > 1 %}
|
||||
{% if item.order_max == 1 %}
|
||||
<label class="btn btn-default btn-checkbox">
|
||||
<input type="checkbox"
|
||||
value="1"
|
||||
id="item_{{ item.id }}"
|
||||
name="item_{{ item.id }}"
|
||||
{% if options == 1 %}checked{% endif %}
|
||||
aria-labelledby="item-{{ item.pk }}-legend"
|
||||
{% if item.description %} aria-describedby="item-{{ item.id }}-description"{% endif %}>
|
||||
<i class="fa fa-shopping-cart" aria-hidden="true"></i>
|
||||
{% trans "Select" context "checkbox" %}
|
||||
</label>
|
||||
{% else %}
|
||||
<fieldset class="input-item-count-group">
|
||||
<legend class="sr-only">{% trans "Quantity" %}</legend>
|
||||
<button type="button" data-step="-1" data-controls="item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="- {{ item }}: {% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count"
|
||||
placeholder="0" min="0"
|
||||
max="{{ item.order_max }}"
|
||||
id="item_{{ item.id }}"
|
||||
name="item_{{ item.id }}"
|
||||
{% if options == 1 %}value="1"{% endif %}
|
||||
aria-labelledby="item-{{ item.pk }}-legend">
|
||||
<button type="button" data-step="1" data-controls="item_{{ item.id }}" class="btn btn-default input-item-count-inc" aria-label="+ {{ item }}: {% trans "Increase quantity" %}">+</button>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<label>
|
||||
<input type="radio" name="_voucher_item"
|
||||
{% if options == 1 %}checked="checked"{% endif %}
|
||||
id="item_{{ item.id }}"
|
||||
value="item_{{ item.id }}"
|
||||
aria-labelledby="item-{{ item.pk }}-legend"
|
||||
{% if item.description %} aria-describedby="item-{{ item.id }}-description"{% endif %}>
|
||||
</label>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% include "pretixpresale/event/fragment_availability.html" with price=item.display_price.gross original_price=item.original_price.gross avail=item.cached_availability.0 item=item %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
{% include "pretixpresale/event/fragment_product_list.html" with ev=event always_show_open=True %}
|
||||
|
||||
{% eventsignal event "pretix.presale.signals.voucher_redeem_info" voucher=voucher %}
|
||||
{% if event.presale_is_running and options > 0 %}
|
||||
{% if event.presale_is_running and itemnum > 0 %}
|
||||
<div class="row checkout-button-row">
|
||||
<div class="col-md-4 col-md-offset-8 col-xs-12 text-center">
|
||||
{% if voucher.min_usages_remaining > 1 %}
|
||||
|
||||
@@ -70,7 +70,7 @@ from pretix.helpers.http import redirect_to_url
|
||||
from pretix.helpers.safedownload import check_token
|
||||
from pretix.multidomain.urlreverse import eventreverse
|
||||
from pretix.presale.productlist import (
|
||||
item_group_by_category, prepare_item_list_for_shop,
|
||||
get_item_option_count, item_group_by_category, prepare_item_list_for_shop,
|
||||
)
|
||||
from pretix.presale.views import (
|
||||
CartMixin, EventViewMixin, allow_cors_if_namespaced,
|
||||
@@ -239,9 +239,9 @@ def _items_from_post_data(request, warn_if_empty=True):
|
||||
|
||||
# Compatibility patch that makes the frontend code a lot easier
|
||||
req_items = list(request.POST.lists())
|
||||
if '_voucher_item' in request.POST and '_voucher_code' in request.POST:
|
||||
if '_selected_item' in request.POST:
|
||||
req_items.append((
|
||||
'%s' % request.POST['_voucher_item'], ('1',)
|
||||
'%s' % request.POST['_selected_item'], ('1',)
|
||||
))
|
||||
pass
|
||||
|
||||
@@ -669,7 +669,7 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['voucher'] = self.voucher
|
||||
context['max_times'] = self.voucher.max_usages - self.voucher.redeemed
|
||||
context['max_choices'] = self.voucher.max_usages - self.voucher.redeemed
|
||||
|
||||
# Fetch all items
|
||||
items, display_add_to_cart = prepare_item_list_for_shop(
|
||||
@@ -687,8 +687,7 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
|
||||
|
||||
# Calculate how many options the user still has. If there is only one option, we can
|
||||
# check the box right away ;)
|
||||
context['options'] = sum([(len(item.available_variations) if item.has_variations else 1)
|
||||
for item in items])
|
||||
context['itemnum'] = get_item_option_count(items)
|
||||
|
||||
context['allfree'] = all(
|
||||
item.display_price.gross == Decimal('0.00') and not item.mandatory_priced_addons
|
||||
|
||||
@@ -73,7 +73,7 @@ from pretix.helpers.http import redirect_to_url
|
||||
from pretix.multidomain.urlreverse import eventreverse
|
||||
from pretix.presale.ical import get_public_ical
|
||||
from pretix.presale.productlist import (
|
||||
item_group_by_category, prepare_item_list_for_shop,
|
||||
get_item_option_count, item_group_by_category, prepare_item_list_for_shop,
|
||||
)
|
||||
from pretix.presale.signals import seatingframe_html_head
|
||||
from pretix.presale.views.organizer import (
|
||||
@@ -228,7 +228,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
break
|
||||
|
||||
items = [i for i in items if not i.requires_seat]
|
||||
context['itemnum'] = len(items)
|
||||
context['itemnum'] = get_item_option_count(items)
|
||||
context['allfree'] = all(
|
||||
item.display_price.gross == Decimal('0.00') and not item.mandatory_priced_addons
|
||||
for item in items if not item.has_variations
|
||||
|
||||
@@ -713,7 +713,7 @@ $(function () {
|
||||
$('[data-checked-onchange]').each(function () {
|
||||
let countInput = this
|
||||
$('#' + this.getAttribute('data-checked-onchange')).on('input', function () {
|
||||
if (countInput.type === 'checkbox') {
|
||||
if (countInput.type === 'checkbox' || countInput.type == "radio") {
|
||||
if (countInput.checked) return
|
||||
countInput.checked = true
|
||||
} else if (countInput.type === 'number' && !countInput.valueAsNumber) {
|
||||
|
||||
@@ -913,7 +913,7 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
|
||||
self.q.size = 0
|
||||
self.q.save()
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))
|
||||
assert "_voucher_item" not in html.rendered_content
|
||||
assert "_selected_item" not in html.rendered_content
|
||||
|
||||
def test_sold_out_blocking(self):
|
||||
self.q.size = 0
|
||||
@@ -921,7 +921,7 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
|
||||
self.v.block_quota = True
|
||||
self.v.save()
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))
|
||||
assert "_voucher_item" in html.rendered_content
|
||||
assert "_selected_item" in html.rendered_content
|
||||
|
||||
def test_sold_out_ignore(self):
|
||||
self.q.size = 0
|
||||
@@ -929,7 +929,7 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
|
||||
self.v.allow_ignore_quota = True
|
||||
self.v.save()
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))
|
||||
assert "_voucher_item" in html.rendered_content
|
||||
assert "_selected_item" in html.rendered_content
|
||||
|
||||
def test_variations_sold_out(self):
|
||||
with scopes_disabled():
|
||||
@@ -940,7 +940,7 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
|
||||
self.q.size = 0
|
||||
self.q.save()
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))
|
||||
assert "_voucher_item" not in html.rendered_content
|
||||
assert "_selected_item" not in html.rendered_content
|
||||
|
||||
def test_variations_sold_out_blocking(self):
|
||||
with scopes_disabled():
|
||||
@@ -953,7 +953,27 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
|
||||
self.v.block_quota = True
|
||||
self.v.save()
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))
|
||||
assert "_voucher_item" in html.rendered_content
|
||||
assert "_selected_item" in html.rendered_content
|
||||
|
||||
def test_variation_not_available(self):
|
||||
with scopes_disabled():
|
||||
var1 = ItemVariation.objects.create(item=self.item, value='Red', position=1)
|
||||
var2 = ItemVariation.objects.create(item=self.item, value='Black', position=2, available_from=now() + datetime.timedelta(days=2))
|
||||
var3 = ItemVariation.objects.create(item=self.item, value='Purple', position=3,
|
||||
available_from=now() + datetime.timedelta(days=2), available_from_mode=Item.UNAVAIL_MODE_INFO)
|
||||
self.q.variations.add(var1)
|
||||
self.q.variations.add(var2)
|
||||
self.q.variations.add(var3)
|
||||
self.q.size = 0
|
||||
self.q.save()
|
||||
self.v.block_quota = True
|
||||
self.v.save()
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))
|
||||
assert "_selected_item" in html.rendered_content
|
||||
assert "Black" not in html.rendered_content
|
||||
assert "Red" in html.rendered_content
|
||||
assert "Purple" in html.rendered_content
|
||||
assert "Not available yet" in html.rendered_content
|
||||
|
||||
def test_voucher_price(self):
|
||||
self.v.value = Decimal("10.00")
|
||||
|
||||
Reference in New Issue
Block a user