Compare commits

..

7 Commits

Author SHA1 Message Date
Mira Weller
6d66a351f0 make price asserts more specific 2024-10-31 11:14:08 +01:00
Mira Weller
b61faf7e29 test for adding a fee 2024-10-31 10:54:52 +01:00
Mira Weller
b1c2b0b827 rename prefix, update tests 2024-10-31 10:47:07 +01:00
Mira Weller
8e47c00167 refactor names, ordering 2024-10-29 18:58:02 +01:00
Mira Weller
cac9a39ae2 formatting 2024-10-29 18:56:52 +01:00
Mira Weller
6f0d813cfc control: allow organizers to manually add fees to an existing order 2024-10-29 18:53:42 +01:00
Mira Weller
839aca6561 add missing word 2024-10-29 17:21:32 +01:00
11 changed files with 379 additions and 195 deletions

View File

@@ -609,6 +609,49 @@ class OrderFeeChangeForm(forms.Form):
change_decimal_field(self.fields['value'], instance.order.event.currency)
class OrderFeeAddForm(forms.Form):
fee_type = forms.ChoiceField(choices=OrderFee.FEE_TYPES)
value = forms.DecimalField(
max_digits=13, decimal_places=2,
localize=True,
label=_('Price'),
help_text=_("including all taxes"),
)
tax_rule = forms.ModelChoiceField(
TaxRule.objects.none(),
required=False,
)
description = forms.CharField(required=False)
def __init__(self, *args, **kwargs):
order = kwargs.pop('order')
super().__init__(*args, **kwargs)
self.fields['tax_rule'].queryset = order.event.tax_rules.all()
change_decimal_field(self.fields['value'], order.event.currency)
class OrderFeeAddFormset(forms.BaseFormSet):
def __init__(self, *args, **kwargs):
self.order = kwargs.pop('order', None)
super().__init__(*args, **kwargs)
def _construct_form(self, i, **kwargs):
kwargs['order'] = self.order
return super()._construct_form(i, **kwargs)
@property
def empty_form(self):
form = self.form(
auto_id=self.auto_id,
prefix=self.add_prefix('__prefix__'),
empty_permitted=True,
use_required_attribute=False,
order=self.order,
)
self.add_fields(form, None)
return form
class OrderContactForm(forms.ModelForm):
regenerate_secrets = forms.BooleanField(required=False, label=_('Invalidate secrets'),
help_text=_('Regenerates the order and ticket secrets. You will '

View File

@@ -16,21 +16,9 @@
{% bootstrap_field form.internal_name layout="control" %}
</div>
{% bootstrap_field form.description layout="control" %}
{% bootstrap_field form.category_type layout="control" horizontal_field_class="big-radio-wrapper col-md-9" %}
<div class="row" data-display-dependency="#id_category_type_2">
<div class="col-md-offset-3 col-md-9">
<div class="alert alert-info">
{% blocktrans trimmed %}
Please note that cross-selling categories are intended as a marketing feature and are not
suitable for strictly ensuring that products are only available in certain combinations.
{% endblocktrans %}
</div>
</div>
</div>
<div data-display-dependency="#id_category_type_2,#id_category_type_3">
{% bootstrap_field form.cross_selling_condition layout="control" horizontal_field_class="col-md-9" %}
{% bootstrap_field form.cross_selling_match_products layout="control" %}
</div>
{% bootstrap_field form.category_type layout="control" horizontal_field_class="big-radio-wrapper col-lg-9" %}
{% bootstrap_field form.cross_selling_condition layout="control" horizontal_field_class="col-lg-9" %}
{% bootstrap_field form.cross_selling_match_products layout="control" %}
</fieldset>
</div>
{% if category %}

View File

@@ -296,11 +296,11 @@
{% endfor %}
<div class="formset" data-formset data-formset-prefix="{{ add_formset.prefix }}">
{{ add_formset.management_form }}
{% bootstrap_formset_errors add_formset %}
<div class="formset" data-formset data-formset-prefix="{{ add_position_formset.prefix }}">
{{ add_position_formset.management_form }}
{% bootstrap_formset_errors add_position_formset %}
<div data-formset-body>
{% for add_form in add_formset %}
{% for add_form in add_position_formset %}
<div class="panel panel-default items" data-formset-form data-subevent="0">
<div class="panel-heading">
<h3 class="panel-title">
@@ -351,25 +351,25 @@
</button>
{% trans "Add product" %}
<div class="sr-only">
{{ add_formset.empty_form.id }}
{% bootstrap_field add_formset.empty_form.DELETE form_group_class="" layout="inline" %}
{{ add_position_formset.empty_form.id }}
{% bootstrap_field add_position_formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
</h3>
</div>
<div class="panel-body">
<div class="form-horizontal">
{% bootstrap_field add_formset.empty_form.itemvar layout="control" %}
{% bootstrap_field add_formset.empty_form.price addon_after=request.event.currency layout="control" %}
{% if add_formset.empty_form.addon_to %}
{% bootstrap_field add_formset.empty_form.addon_to layout="control" %}
{% bootstrap_field add_position_formset.empty_form.itemvar layout="control" %}
{% bootstrap_field add_position_formset.empty_form.price addon_after=request.event.currency layout="control" %}
{% if add_position_formset.empty_form.addon_to %}
{% bootstrap_field add_position_formset.empty_form.addon_to layout="control" %}
{% endif %}
{% if add_formset.empty_form.subevent %}
{% bootstrap_field add_formset.empty_form.subevent layout="control" %}
{% if add_position_formset.empty_form.subevent %}
{% bootstrap_field add_position_formset.empty_form.subevent layout="control" %}
{% endif %}
{% if add_formset.empty_form.used_membership %}
{% bootstrap_field add_formset.empty_form.used_membership layout="control" %}
{% if add_position_formset.empty_form.used_membership %}
{% bootstrap_field add_position_formset.empty_form.used_membership layout="control" %}
{% endif %}
{% bootstrap_field add_formset.empty_form.seat layout="control" %}
{% bootstrap_field add_position_formset.empty_form.seat layout="control" %}
</div>
</div>
</div>
@@ -431,13 +431,77 @@
{% bootstrap_field fee.form.operation_cancel layout='inline' %}
{% if fee.fee_type == "payment" %}
<em class="text-danger">
{% trans "Manually modifying payment fees is discouraged since they might automatically be on subsequent order changes or when choosing a different payment method." %}
{% trans "Manually modifying payment fees is discouraged since they might automatically be updated on subsequent order changes or when choosing a different payment method." %}
</em>
{% endif %}
</div>
</div>
</div>
{% endfor %}
<div class="formset" data-formset data-formset-prefix="{{ add_fee_formset.prefix }}">
{{ add_fee_formset.management_form }}
{% bootstrap_formset_errors add_fee_formset %}
<div data-formset-body>
{% for add_form in add_fee_formset %}
<div class="panel panel-default items" data-formset-form data-subevent="0">
<div class="panel-heading">
<h3 class="panel-title">
<button type="button" class="btn btn-danger btn-xs pull-right flip"
data-formset-delete-button>
<i class="fa fa-trash"></i>
</button>
{% trans "Add fee" %}
<div class="sr-only">
{{ add_form.id }}
{% bootstrap_field add_form.DELETE form_group_class="" layout="inline" %}
</div>
</h3>
</div>
<div class="panel-body">
<div class="form-horizontal">
{% bootstrap_field add_form.fee_type layout='control' %}
{% bootstrap_field add_form.value addon_after=request.event.currency layout='control' %}
{% bootstrap_field add_form.tax_rule layout='control' %}
{% bootstrap_field add_form.description layout='control' %}
</div>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default items" data-formset-form data-subevent="0">
<div class="panel-heading">
<h3 class="panel-title">
<button type="button" class="btn btn-danger btn-xs pull-right flip"
data-formset-delete-button>
<i class="fa fa-trash"></i>
</button>
{% trans "Add fee" %}
<div class="sr-only">
{{ add_fee_formset.empty_form.id }}
{% bootstrap_field add_fee_formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
</h3>
</div>
<div class="panel-body">
<div class="form-horizontal">
{% bootstrap_field add_fee_formset.empty_form.fee_type layout='control' %}
{% bootstrap_field add_fee_formset.empty_form.value addon_after=request.event.currency layout='control' %}
{% bootstrap_field add_fee_formset.empty_form.tax_rule layout='control' %}
{% bootstrap_field add_fee_formset.empty_form.description layout='control' %}
</div>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-primary" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add fee" %}</button>
</p>
</div>
<div class="panel panel-default items">
<div class="panel-heading">
<h3 class="panel-title">

View File

@@ -122,10 +122,11 @@ from pretix.control.forms.filter import (
)
from pretix.control.forms.orders import (
CancelForm, CommentForm, DenyForm, EventCancelForm, ExporterForm,
ExtendForm, MarkPaidForm, OrderContactForm, OrderFeeChangeForm,
OrderLocaleForm, OrderMailForm, OrderPositionAddForm,
OrderPositionAddFormset, OrderPositionChangeForm, OrderPositionMailForm,
OrderRefundForm, OtherOperationsForm, ReactivateOrderForm,
ExtendForm, MarkPaidForm, OrderContactForm, OrderFeeAddForm,
OrderFeeAddFormset, OrderFeeChangeForm, OrderLocaleForm, OrderMailForm,
OrderPositionAddForm, OrderPositionAddFormset, OrderPositionChangeForm,
OrderPositionMailForm, OrderRefundForm, OtherOperationsForm,
ReactivateOrderForm,
)
from pretix.control.forms.rrule import RRuleForm
from pretix.control.permissions import EventPermissionRequiredMixin
@@ -1874,18 +1875,30 @@ class OrderChange(OrderView):
data=self.request.POST if self.request.method == "POST" else None)
@cached_property
def add_formset(self):
def add_position_formset(self):
ff = formset_factory(
OrderPositionAddForm, formset=OrderPositionAddFormset,
can_order=False, can_delete=True, extra=0
)
return ff(
prefix='add',
prefix='add_position',
order=self.order,
items=self.items,
data=self.request.POST if self.request.method == "POST" else None
)
@cached_property
def add_fee_formset(self):
ff = formset_factory(
OrderFeeAddForm, formset=OrderFeeAddFormset,
can_order=False, can_delete=True, extra=0
)
return ff(
prefix='add_fee',
order=self.order,
data=self.request.POST if self.request.method == "POST" else None
)
@cached_property
def items(self):
return self.request.event.items.prefetch_related('variations', 'tax_rule').all()
@@ -1914,7 +1927,8 @@ class OrderChange(OrderView):
ctx = super().get_context_data(**kwargs)
ctx['positions'] = self.positions
ctx['fees'] = self.fees
ctx['add_formset'] = self.add_formset
ctx['add_position_formset'] = self.add_position_formset
ctx['add_fee_formset'] = self.add_fee_formset
ctx['other_form'] = self.other_form
ctx['use_revocation_list'] = self.request.event.ticket_secret_generator.use_revocation_list
return ctx
@@ -1929,12 +1943,35 @@ class OrderChange(OrderView):
)
return True
def _process_add(self, ocm):
if not self.add_formset.is_valid():
def _process_add_fees(self, ocm):
if not self.add_fee_formset.is_valid():
return False
else:
for f in self.add_formset.forms:
if f in self.add_formset.deleted_forms or not f.has_changed():
for f in self.add_fee_formset.forms:
if f in self.add_fee_formset.deleted_forms or not f.has_changed():
continue
f = OrderFee(
fee_type=f.cleaned_data['fee_type'],
value=f.cleaned_data['value'],
order=ocm.order,
tax_rule=f.cleaned_data['tax_rule'],
description=f.cleaned_data['description'],
)
f._calculate_tax()
try:
ocm.add_fee(f)
except OrderError as e:
f.custom_error = str(e)
return False
return True
def _process_add_positions(self, ocm):
if not self.add_position_formset.is_valid():
return False
else:
for f in self.add_position_formset.forms:
if f in self.add_position_formset.deleted_forms or not f.has_changed():
continue
if '-' in f.cleaned_data['itemvar']:
@@ -1959,7 +1996,7 @@ class OrderChange(OrderView):
return False
return True
def _process_fees(self, ocm):
def _process_change_fees(self, ocm):
for f in self.fees:
if not f.form.is_valid():
return False
@@ -1980,7 +2017,7 @@ class OrderChange(OrderView):
return False
return True
def _process_change(self, ocm):
def _process_change_positions(self, ocm):
for p in self.positions:
if not p.form.is_valid():
return False
@@ -2061,7 +2098,11 @@ class OrderChange(OrderView):
notify=notify,
reissue_invoice=self.other_form.cleaned_data['reissue_invoice'] if self.other_form.is_valid() else True
)
form_valid = self._process_add(ocm) and self._process_fees(ocm) and self._process_change(ocm) and self._process_other(ocm)
form_valid = (self._process_add_fees(ocm) and
self._process_add_positions(ocm) and
self._process_change_fees(ocm) and
self._process_change_positions(ocm) and
self._process_other(ocm))
if not form_valid:
messages.error(self.request, _('An error occurred. Please see the details below.'))

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-29 08:53+0000\n"
"PO-Revision-Date: 2024-10-29 21:00+0000\n"
"PO-Revision-Date: 2024-09-27 18:00+0000\n"
"Last-Translator: Anarion Dunedain <anarion80@gmail.com>\n"
"Language-Team: Polish <https://translate.pretix.eu/projects/pretix/pretix/pl/"
">\n"
@@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 5.8.1\n"
"X-Generator: Weblate 5.7.2\n"
#: pretix/_base_settings.py:79
msgid "English"
@@ -4606,40 +4606,43 @@ msgstr ""
#: pretix/base/models/items.py:114 pretix/base/models/items.py:159
#: pretix/control/forms/item.py:99
#, fuzzy
#| msgid "No category"
msgid "Normal category"
msgstr "Normalna kategoria"
msgstr "Brak kategorii"
#: pretix/base/models/items.py:115 pretix/control/forms/item.py:112
msgid "Normal + cross-selling category"
msgstr "Normalna + sprzedaż krzyżowa"
msgstr ""
#: pretix/base/models/items.py:116 pretix/control/forms/item.py:107
#, fuzzy
#| msgid "No category"
msgid "Cross-selling category"
msgstr "Kategoria sprzedaży krzyżowej"
msgstr "Brak kategorii"
#: pretix/base/models/items.py:124
msgid "Always show in cross-selling step"
msgstr "Zawsze pokazuj w kroku sprzedaży krzyżowej"
msgstr ""
#: pretix/base/models/items.py:125
msgid ""
"Only show products that qualify for a discount according to discount rules"
msgstr ""
"Pokaż tylko produkty kwalifikujące się do zniżki zgodnie z zasadami "
"rabatowymi"
#: pretix/base/models/items.py:126
msgid "Only show if the cart contains one of the following products"
msgstr ""
"Pokaż tylko, jeśli w koszyku znajduje się jeden z następujących produktów"
#: pretix/base/models/items.py:129
msgid "Cross-selling condition"
msgstr "Warunek sprzedaży krzyżowej"
msgstr ""
#: pretix/base/models/items.py:137
#, fuzzy
#| msgid "Count add-on products"
msgid "Cross-selling condition products"
msgstr "Produkty warunkowej sprzedaży krzyżowej"
msgstr "Policz produkty dodatkowe"
#: pretix/base/models/items.py:143
#: pretix/control/templates/pretixcontrol/items/categories.html:3
@@ -4651,11 +4654,13 @@ msgstr "Kategorie produktów"
#: pretix/base/models/items.py:149
#, python-brace-format
msgid "{category} ({category_type})"
msgstr "{category} ({category_type})"
msgstr ""
#: pretix/base/models/items.py:155
#, fuzzy
#| msgid "No category"
msgid "Add-on category"
msgstr "Kategoria dodatków"
msgstr "Brak kategorii"
#: pretix/base/models/items.py:222 pretix/base/models/items.py:278
msgid "Disable product for this date"
@@ -6018,8 +6023,10 @@ msgid "Ticket"
msgstr "Bilet"
#: pretix/base/models/orders.py:3405
#, fuzzy
#| msgid "Verification"
msgid "Certificate"
msgstr "Certyfikat"
msgstr "Weryfikacja"
#: pretix/base/models/orders.py:3406 pretix/control/views/event.py:367
#: pretix/control/views/event.py:372
@@ -6185,13 +6192,11 @@ msgstr ""
#, python-brace-format
msgid "Seat with zone {zone}, row {row}, and number {number} has no seat ID."
msgstr ""
"Miejsce w strefie {zone}, rzędzie {row} i numerze {number} nie ma "
"identyfikatora miejsca."
#: pretix/base/models/seating.py:71
#, python-brace-format
msgid "Multiple seats have the same ID: {id}"
msgstr "Wiele miejsc ma ten sam identyfikator: {id}"
msgstr ""
#: pretix/base/models/seating.py:199
#, python-brace-format
@@ -9039,8 +9044,10 @@ msgid "Ask for beneficiary"
msgstr "Zapytaj o beneficjenta"
#: pretix/base/settings.py:574
#, fuzzy
#| msgid "Custom recipient field"
msgid "Custom recipient field label"
msgstr "Etykieta pola niestandardowego odbiorcy"
msgstr "Niestandardowe pole odbiorcy"
#: pretix/base/settings.py:576
msgid ""
@@ -9057,8 +9064,10 @@ msgstr ""
"ona wyświetlana na fakturze poniżej nagłówka. Pole nie będzie wymagane."
#: pretix/base/settings.py:589
#, fuzzy
#| msgid "Custom recipient field"
msgid "Custom recipient field help text"
msgstr "Tekst pomocy dotyczący pola odbiorcy niestandardowego"
msgstr "Niestandardowe pole odbiorcy"
#: pretix/base/settings.py:591
msgid ""
@@ -9066,8 +9075,6 @@ msgid ""
"will be displayed underneath the field. It will not be displayed on the "
"invoice."
msgstr ""
"Jeśli używasz niestandardowego pola odbiorcy, możesz określić tekst pomocy, "
"który będzie wyświetlany pod polem. Nie będzie on wyświetlany na fakturze."
#: pretix/base/settings.py:601
msgid "Ask for VAT ID"
@@ -12505,9 +12512,12 @@ msgstr ""
"zakończenia przedsprzedaży"
#: pretix/base/timeline.py:106
#, fuzzy
#| msgctxt "timeline"
#| msgid "Customers can no longer modify their orders"
msgctxt "timeline"
msgid "Customers can no longer modify their order information"
msgstr "Klienci nie mogą już modyfikować swojego zamówienia"
msgstr "Klienci nie mogą już modyfikować swoich zamówień"
#: pretix/base/timeline.py:119
msgctxt "timeline"
@@ -12530,6 +12540,9 @@ msgid "Customers can no longer cancel paid orders"
msgstr "Klienci nie mogą już anulować opłaconych zamówień"
#: pretix/base/timeline.py:167
#, fuzzy
#| msgctxt "timeline"
#| msgid "Customers can no longer modify their orders"
msgctxt "timeline"
msgid "Customers can no longer make changes to their orders"
msgstr "Klienci nie mogą już modyfikować swoich zamówień"
@@ -14075,30 +14088,31 @@ msgstr ""
"wtyczki. Będzie on publicznie dostępny. Upewnij się, że jest on aktualny!"
#: pretix/control/forms/item.py:100
#, fuzzy
#| msgid "Products in this category are add-on products"
msgid ""
"Products in this category are regular products displayed on the front page."
msgstr ""
"Produkty w tej kategorii to zwykłe produkty wyświetlane na stronie głównej."
msgstr "Produkty w tej kategorii są dodatkami"
#: pretix/control/forms/item.py:103
#, fuzzy
#| msgid "Product category"
msgid "Add-on product category"
msgstr "Kategoria produktu dodatkowego"
msgstr "Kategoria produku"
#: pretix/control/forms/item.py:104
#, fuzzy
#| msgid "Products in this category are add-on products"
msgid ""
"Products in this category are add-on products and can only be bought as add-"
"ons."
msgstr ""
"Produkty w tej kategorii są produktami dodatkowymi i można je kupić "
"wyłącznie jako dodatki."
msgstr "Produkty w tej kategorii są dodatkami"
#: pretix/control/forms/item.py:108
msgid ""
"Products in this category are regular products, but are only shown in the "
"cross-selling step, according to the configuration below."
msgstr ""
"Produkty w tej kategorii to zwykłe produkty, ale są wyświetlane tylko na "
"etapie sprzedaży krzyżowej, zgodnie z konfiguracją poniżej."
#: pretix/control/forms/item.py:113
msgid ""
@@ -14106,9 +14120,6 @@ msgid ""
"but are additionally shown in the cross-selling step, according to the "
"configuration below."
msgstr ""
"Produkty w tej kategorii to zwykłe produkty wyświetlane na stronie głównej, "
"ale są dodatkowo pokazywane na etapie sprzedaży krzyżowej, zgodnie z "
"poniższą konfiguracją."
#: pretix/control/forms/item.py:141 pretix/control/forms/item.py:211
msgid "This field is required"
@@ -16526,9 +16537,12 @@ msgid "The check-in of position #{posid} on list \"{list}\" has been reverted."
msgstr "Zameldowanie pozycji #{posid} na liście \"{list}\" zostało cofnięte."
#: pretix/control/logdisplay.py:644
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Position #{posid} has been checked in at {datetime} for list \"{list}\"."
msgid "Position #{posid} has been printed at {datetime} with type \"{type}\"."
msgstr "Pozycja #{posid} została wydrukowana o {datetime} z typem „{type}”."
msgstr ""
"Pozycja #{posid} została zameldowana w {datetime} dla listy \"{list}\"."
#: pretix/control/logdisplay.py:667
#, python-brace-format
@@ -17214,9 +17228,6 @@ msgid ""
"check that you have completed all installation steps and your cronjob is "
"executed correctly."
msgstr ""
"Komponent cronjob pretix nie został uruchomiony w ciągu ostatnich godzin. "
"Sprawdź, czy ukończyłeś wszystkie kroki instalacji i czy twój cronjob został "
"wykonany poprawnie."
#: pretix/control/templates/pretixcontrol/base.html:435
msgid ""
@@ -20068,8 +20079,10 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/item/base.html:24
#: pretix/control/templates/pretixcontrol/item/include_variations.html:79
#, fuzzy
#| msgid "Manage questions"
msgid "Manage quotas"
msgstr "Zarządzaj pulami"
msgstr "Zarządzaj pytaniami"
#: pretix/control/templates/pretixcontrol/item/base.html:27
#: pretix/control/templates/pretixcontrol/item/include_variations.html:82
@@ -20442,8 +20455,10 @@ msgid "Create a new category"
msgstr "Utwórz nową kategorię"
#: pretix/control/templates/pretixcontrol/items/categories.html:34
#, fuzzy
#| msgid "Category name"
msgid "Category type"
msgstr "Typ kategorii"
msgstr "Nazwa kategorii"
#: pretix/control/templates/pretixcontrol/items/categories.html:48
#: pretix/control/templates/pretixcontrol/items/discounts.html:138
@@ -25377,8 +25392,6 @@ msgid ""
"According to your event settings, sold out products are hidden from "
"customers. This way, customers will not be able to discover the waiting list."
msgstr ""
"Zgodnie z ustawieniami wydarzenia, wyprzedane produkty są ukryte przed "
"klientami. W ten sposób klienci nie będą mogli znaleźć listy oczekujących."
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:36
msgid "Send vouchers"
@@ -31806,25 +31819,28 @@ msgid ""
"A product in your cart is only sold in combination with add-on products that "
"are no longer available. Please contact the event organizer."
msgstr ""
"Produkt w Twoim koszyku jest sprzedawany tylko w połączeniu z produktami "
"dodatkowymi, które nie są już dostępne. Skontaktuj się z organizatorem "
"wydarzenia."
#: pretix/presale/templates/pretixpresale/event/checkout_addons.html:20
msgid "We're now trying to book these add-ons for you!"
msgstr "Staramy się teraz zarezerwować te dodatki dla Ciebie!"
#: pretix/presale/templates/pretixpresale/event/checkout_addons.html:28
#, fuzzy
#| msgid "Additional settings"
msgid "Additional options for"
msgstr "Dodatkowe opcje dla"
msgstr "Dodatkowe ustawienia"
#: pretix/presale/templates/pretixpresale/event/checkout_addons.html:64
#, fuzzy
#| msgid "Top recommendation"
msgid "More recommendations"
msgstr "Więcej rekomendacji"
msgstr "Najlepsza rekomendacja"
#: pretix/presale/templates/pretixpresale/event/checkout_addons.html:71
#, fuzzy
#| msgid "Top recommendation"
msgid "Our recommendations"
msgstr "Nasze rekomendacje"
msgstr "Najlepsza rekomendacja"
#: pretix/presale/templates/pretixpresale/event/checkout_addons.html:89
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:201
@@ -32830,8 +32846,10 @@ msgid "Payment pending"
msgstr "W toku płatności"
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:19
#, fuzzy
#| msgid "Your orders for {event}"
msgid "Your order qualifies for a discount"
msgstr "Twoje zamówienia kwalifikuje się do zniżki"
msgstr "Twoje zamówienia na {event}"
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:28
#: pretix/presale/templates/pretixpresale/event/voucher.html:78
@@ -34372,8 +34390,6 @@ msgid ""
"No ticket types are available for the waiting list, have a look at the "
"ticket shop instead."
msgstr ""
"Nie ma dostępnych typów biletów dla listy oczekujących, zamiast tego należy "
"udać się do sklepu z biletami."
#: pretix/presale/views/waiting.py:137 pretix/presale/views/waiting.py:161
msgid "Waiting lists are disabled for this event."

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-29 08:53+0000\n"
"PO-Revision-Date: 2024-10-30 19:00+0000\n"
"PO-Revision-Date: 2024-09-18 14:02+0000\n"
"Last-Translator: Tinna Sandström <tinna@coeo.events>\n"
"Language-Team: Swedish <https://translate.pretix.eu/projects/pretix/pretix/"
"sv/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.8.1\n"
"X-Generator: Weblate 5.7.2\n"
#: pretix/_base_settings.py:79
msgid "English"
@@ -22131,7 +22131,7 @@ msgid ""
"After starting this operation, depending on the size of your event, it might "
"take a few minutes or longer until all orders are processed."
msgstr ""
"Efter att du har startat denna process kan det, beroende på storleken på "
"Efter att du har startat denna operation kan det, beroende på storleken på "
"ditt evenemang, ta några minuter eller längre tid innan alla bokningar är "
"behandlade."
@@ -31866,7 +31866,7 @@ msgstr "Ändra kontaktinformation"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:167
msgid "Confirmations"
msgstr "Villkor"
msgstr "Bekräftelser"
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:185
msgid ""

View File

@@ -462,28 +462,14 @@ class BadgeExporter(BaseExporter):
)),
('date_from',
forms.DateField(
label=_('Start event date'),
label=_('Start date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
help_text=_('Only include tickets for dates on or after this date.')
)),
('date_to',
forms.DateField(
label=_('End event date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
help_text=_('Only include tickets ordered on or before this date.')
)),
('order_date_from',
forms.DateField(
label=_('Start order date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
help_text=_('Only include tickets ordered on or after this date.')
)),
('order_date_to',
forms.DateField(
label=_('End order date'),
label=_('End date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
help_text=_('Only include tickets for dates on or before this date.')
@@ -495,7 +481,6 @@ class BadgeExporter(BaseExporter):
('name', _('Attendee name')),
('company', _('Attendee company')),
('code', _('Order code')),
('order_date', _('Order date')),
('date', _('Event date')),
] + ([
('name:{}'.format(k), _('Attendee name: {part}').format(part=label))
@@ -548,24 +533,6 @@ class BadgeExporter(BaseExporter):
), self.event.timezone)
qs = qs.filter(Q(subevent__date_from__lt=dt) | Q(subevent__isnull=True, order__event__date_from__lt=dt))
if form_data.get('order_date_from'):
if not isinstance(form_data.get('order_date_from'), date):
form_data['order_date_from'] = dateutil.parser.parse(form_data['order_date_from']).date()
df = make_aware(datetime.combine(
form_data['order_date_from'],
time(hour=0, minute=0, second=0)
), self.event.timezone)
qs = qs.filter(order__datetime__gte=df)
if form_data.get('order_date_to'):
if not isinstance(form_data.get('order_date_to'), date):
form_data['order_date_to'] = dateutil.parser.parse(form_data['order_date_to']).date()
dt = make_aware(datetime.combine(
form_data['order_date_to'] + timedelta(days=1),
time(hour=0, minute=0, second=0)
), self.event.timezone)
qs = qs.filter(order__datetime__lt=dt)
if form_data.get('order_by') == 'name':
qs = qs.annotate(
resolved_name=Case(
@@ -586,8 +553,6 @@ class BadgeExporter(BaseExporter):
).order_by('resolved_company', 'order__code')
elif form_data.get('order_by') == 'code':
qs = qs.order_by('order__code')
elif form_data.get('order_by') == 'order_date':
qs = qs.order_by('order__datetime')
elif form_data.get('order_by') == 'date':
qs = qs.annotate(ed=Coalesce('subevent__date_from', 'order__event__date_from')).order_by('ed', 'order__code')
elif form_data.get('order_by', '').startswith('name:'):

View File

@@ -21,7 +21,6 @@
#
from django import forms
from django.conf import settings
from django.utils.translation import pgettext
from i18nfield.strings import LazyI18nString
from pretix.base.models import EventMetaValue, SubEventMetaValue
@@ -67,7 +66,7 @@ def meta_filtersets(organizer, event=None):
).values_list("value", flat=True).distinct())
choices = [(k, k) for k in sorted(existing_values)]
choices.insert(0, ("", "-- %s --" % pgettext("filter_empty", "all")))
choices.insert(0, ("", ""))
if len(choices) > 1:
fields[f"attr[{prop.name}]"] = {
"label": str(prop.public_label) or prop.name,

View File

@@ -1309,10 +1309,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'op-{}-itemvar'.format(self.op1.pk): str(self.shirt.pk),
'op-{}-price'.format(self.op1.pk): str('12.00'),
})
@@ -1341,10 +1345,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'op-{}-subevent'.format(self.op1.pk): str(se2.pk),
})
self.op1.refresh_from_db()
@@ -1373,10 +1381,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'op-{}-used_membership'.format(self.op1.pk): str(m_correct1.pk),
'op-{}-used_membership'.format(self.op2.pk): str(m_correct1.pk),
'op-{}-used_membership'.format(self.op3.pk): str(m_correct1.pk),
@@ -1389,10 +1401,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'op-{}-operation'.format(self.op1.pk): 'price',
'op-{}-itemvar'.format(self.op1.pk): str(self.ticket.pk),
'op-{}-price'.format(self.op1.pk): '24.00',
@@ -1409,10 +1425,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'op-{}-operation_cancel'.format(self.op1.pk): 'on',
})
self.order.refresh_from_db()
@@ -1424,13 +1444,17 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '1',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add-0-itemvar': str(self.shirt.pk),
'add-0-do': 'on',
'add-0-price': '14.00',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '1',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'add_position-0-itemvar': str(self.shirt.pk),
'add_position-0-do': 'on',
'add_position-0-price': '14.00',
})
with scopes_disabled():
assert self.order.positions.count() == 3
@@ -1453,10 +1477,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'other-recalculate_taxes': 'net',
'op-{}-operation'.format(self.op1.pk): '',
'op-{}-operation'.format(self.op2.pk): '',
@@ -1489,10 +1517,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'other-recalculate_taxes': 'gross',
'op-{}-operation'.format(self.op1.pk): '',
'op-{}-operation'.format(self.op2.pk): '',
@@ -1517,10 +1549,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'op-{}-price'.format(self.op1.pk): '24.00',
'op-{}-operation'.format(self.op2.pk): '',
'op-{}-itemvar'.format(self.op2.pk): str(self.ticket.pk),
@@ -1544,10 +1580,14 @@ class OrderChangeTests(SoupTest):
self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add-TOTAL_FORMS': '0',
'add-INITIAL_FORMS': '0',
'add-MIN_NUM_FORMS': '0',
'add-MAX_NUM_FORMS': '100',
'add_fee-TOTAL_FORMS': '0',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'op-{}-operation'.format(self.op1.pk): 'price',
'op-{}-itemvar'.format(self.op1.pk): str(self.ticket.pk),
'op-{}-price'.format(self.op1.pk): '24.00',
@@ -1563,6 +1603,34 @@ class OrderChangeTests(SoupTest):
self.op2.refresh_from_db()
assert self.order.total == self.op1.price + self.op2.price
def test_add_fee_success(self):
old_total = self.order.total
r = self.client.post('/control/event/{}/{}/orders/{}/change'.format(
self.event.organizer.slug, self.event.slug, self.order.code
), {
'add_fee-TOTAL_FORMS': '1',
'add_fee-INITIAL_FORMS': '0',
'add_fee-MIN_NUM_FORMS': '0',
'add_fee-MAX_NUM_FORMS': '100',
'add_position-TOTAL_FORMS': '0',
'add_position-INITIAL_FORMS': '0',
'add_position-MIN_NUM_FORMS': '0',
'add_position-MAX_NUM_FORMS': '100',
'add_fee-0-do': 'on',
'add_fee-0-fee_type': 'other',
'add_fee-0-description': 'Surprise Fee',
'add_fee-0-value': '5.00',
})
assert r.status_code == 302
self.order.refresh_from_db()
with scopes_disabled():
fee = self.order.fees.get()
assert fee.fee_type == OrderFee.FEE_TYPE_OTHER
assert fee.description == 'Surprise Fee'
assert fee.value == Decimal('5.00')
assert not fee.canceled
assert self.order.total == old_total + 5
@pytest.mark.django_db
def test_check_vatid(client, env):

View File

@@ -405,11 +405,11 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
SubEventItem.objects.create(subevent=se1, item=item, price=12)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk))
self.assertIn("12.00", resp.rendered_content)
self.assertNotIn("15.00", resp.rendered_content)
self.assertIn("12.00", resp.rendered_content)
self.assertNotIn("15.00", resp.rendered_content)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
self.assertIn("15.00", resp.rendered_content)
self.assertNotIn("12.00", resp.rendered_content)
self.assertIn("15.00", resp.rendered_content)
self.assertNotIn("12.00", resp.rendered_content)
def test_subevent_net_prices(self):
self.event.has_subevents = True
@@ -429,14 +429,14 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk))
doc = BeautifulSoup(resp.rendered_content, "lxml")
self.assertIn("10.08", doc.text)
self.assertNotIn("12.00", doc.text)
self.assertNotIn("15.00", doc.text)
self.assertIn("10.08", doc.text)
self.assertNotIn("12.00", doc.text)
self.assertNotIn("15.00", doc.text)
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
doc = BeautifulSoup(resp.rendered_content, "lxml")
self.assertIn("12.61", doc.text)
self.assertNotIn("12.00", doc.text)
self.assertNotIn("15.00", doc.text)
self.assertIn("12.61", doc.text)
self.assertNotIn("12.00", doc.text)
self.assertNotIn("15.00", doc.text)
def test_variations_subevent_disabled(self):
self.event.has_subevents = True

View File

@@ -812,7 +812,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
data = json.loads(response.content.decode())
assert data["meta_filter_fields"] == [
{
"choices": [["", "-- all --"], ["EN", "English"], ["DE", "German"]],
"choices": [["", ""], ["EN", "English"], ["DE", "German"]],
"key": "attr[Language]",
"label": "Language"
}
@@ -838,7 +838,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
data = json.loads(response.content.decode())
assert data["meta_filter_fields"] == [
{
"choices": [["", "-- all --"], ["DE", "DE"], ["EN", "EN"]],
"choices": [["", ""], ["DE", "DE"], ["EN", "EN"]],
"key": "attr[Language]",
"label": "Language"
}
@@ -848,7 +848,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
data = json.loads(response.content.decode())
assert data["meta_filter_fields"] == [
{
"choices": [["", "-- all --"], ["DE", "DE"]],
"choices": [["", ""], ["DE", "DE"]],
"key": "attr[Language]",
"label": "Language"
}