mirror of
https://github.com/pretix/pretix.git
synced 2026-09-14 16:24:43 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0423de48bc | ||
|
|
6df638a3ac | ||
|
|
c05dee7af5 | ||
|
|
e399a49c8b | ||
|
|
b9b951e56b | ||
|
|
88bb1f4008 | ||
|
|
24e7db7ddd | ||
|
|
96ed6fe1a6 |
@@ -49,7 +49,6 @@ const ignores = globalIgnores([
|
||||
'src/pretix/static/pretixcontrol/js/ui/editor.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/geo.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/main.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/plugins.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/subevent.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/variations.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/webauthn.js',
|
||||
|
||||
@@ -343,7 +343,7 @@ class Discount(LoggedModel):
|
||||
if collect_potential_discounts is not None:
|
||||
if n_groups * self.benefit_only_apply_to_cheapest_n_matches > len(benefit_idx_group):
|
||||
# partially used discount ("for each 1 ticket you buy, get 50% on 2 t-shirts", cart content: 1 ticket
|
||||
# but only 1 t-shirt) -> 1 shirt definitively potential discount
|
||||
# but only 1 t-shirt) -> 1 shirt definitiv potential discount
|
||||
for idx in consume_idx:
|
||||
collect_potential_discounts[idx] = [
|
||||
(self, n_groups * self.benefit_only_apply_to_cheapest_n_matches - len(benefit_idx_group), -1, subevent_id)
|
||||
|
||||
@@ -510,9 +510,7 @@ class Item(LoggedModel):
|
||||
verbose_name=_("Free price input"),
|
||||
help_text=_("If this option is active, your users can choose the price themselves. The price configured above "
|
||||
"is then interpreted as the minimum price a user has to enter. You could use this e.g. to collect "
|
||||
"additional donations for your event. We recommend against combining this feature with automatic "
|
||||
"discounts since discounts are applied as the last step of price computation, which means that the "
|
||||
"price entered by the customer will be modified again.")
|
||||
"additional donations for your event.")
|
||||
)
|
||||
free_price_suggestion = models.DecimalField(
|
||||
verbose_name=_("Suggested price"),
|
||||
|
||||
@@ -51,7 +51,6 @@ class CrossSellingService:
|
||||
self.sales_channel = sales_channel
|
||||
self.cartpositions = cartpositions
|
||||
self.customer = customer
|
||||
self._discount_cache = {}
|
||||
|
||||
def get_data(self):
|
||||
if self.event.has_subevents:
|
||||
@@ -178,7 +177,6 @@ class CrossSellingService:
|
||||
)
|
||||
if self.customer else None
|
||||
),
|
||||
_discount_cache=self._discount_cache,
|
||||
)
|
||||
new_items = list()
|
||||
for item in items:
|
||||
|
||||
@@ -8,16 +8,16 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
|
||||
"PO-Revision-Date: 2026-08-24 14:30+0000\n"
|
||||
"PO-Revision-Date: 2026-09-04 14:29+0000\n"
|
||||
"Last-Translator: Albizuri <oier@puntu.eus>\n"
|
||||
"Language-Team: Basque <https://translate.pretix.eu/projects/pretix/pretix/eu/"
|
||||
">\n"
|
||||
"Language-Team: Basque <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"eu/>\n"
|
||||
"Language: eu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 2026.8.1\n"
|
||||
"X-Generator: Weblate 2026.9\n"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "English"
|
||||
@@ -891,7 +891,7 @@ msgstr "Produktuaren data"
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html
|
||||
msgid "Order details"
|
||||
msgstr ""
|
||||
msgstr "Eskaeraren xehetasunak"
|
||||
|
||||
#: pretix/base/datasync/sourcefields.py pretix/base/modelimport_orders.py
|
||||
#: pretix/control/forms/filter.py
|
||||
@@ -17296,7 +17296,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/logdisplay.py
|
||||
msgid "The order details have been changed."
|
||||
msgstr ""
|
||||
msgstr "Eskaeraren xehetasunak aldatu dira."
|
||||
|
||||
#: pretix/control/logdisplay.py
|
||||
msgid "The order has been marked as unpaid."
|
||||
@@ -23106,7 +23106,7 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html
|
||||
#, python-format
|
||||
msgid "Order details: %(code)s"
|
||||
msgstr ""
|
||||
msgstr "Eskaeraren xehetasunak: %(code)s"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html
|
||||
#: pretix/control/templates/pretixcontrol/orders/index.html
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
|
||||
"PO-Revision-Date: 2026-08-16 22:00+0000\n"
|
||||
"Last-Translator: Hijiri Umemoto <hijiri@umemoto.org>\n"
|
||||
"PO-Revision-Date: 2026-09-07 18:00+0000\n"
|
||||
"Last-Translator: Yasunobu YesNo Kawaguchi <kawaguti@gmail.com>\n"
|
||||
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"ja/>\n"
|
||||
"Language: ja\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 2026.8.1\n"
|
||||
"X-Generator: Weblate 2026.9\n"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "English"
|
||||
@@ -93,7 +93,7 @@ msgstr "ヘブライ語"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
msgstr "ハンガリー語"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "Indonesian"
|
||||
@@ -220,7 +220,7 @@ msgstr "ターゲットURL"
|
||||
#: pretix/api/models.py pretix/base/models/devices.py
|
||||
#: pretix/base/models/organizer.py
|
||||
msgid "All events (including newly created ones)"
|
||||
msgstr "すべてのイベント(最近作成されたイベントを含む)"
|
||||
msgstr "すべてのイベント(今後作成されるものを含む)"
|
||||
|
||||
#: pretix/api/models.py pretix/base/models/devices.py
|
||||
#: pretix/base/models/organizer.py
|
||||
@@ -428,10 +428,8 @@ msgid "You cannot exchange a medium for a medium."
|
||||
msgstr "メディアを別のメディアに変更することはできません。"
|
||||
|
||||
#: pretix/api/views/checkin.py
|
||||
#, fuzzy
|
||||
#| msgid "Product does not support medium exchange."
|
||||
msgid "You cannot simulate a medium exchange."
|
||||
msgstr "本製品は中程度の交換に対応していません。"
|
||||
msgstr "メディアの交換をシミュレートすることはできません。"
|
||||
|
||||
#: pretix/api/views/oauth.py pretix/control/logdisplay.py
|
||||
#, python-brace-format
|
||||
@@ -5691,7 +5689,7 @@ msgstr "国コード(ISO 3166-1 alpha-2)"
|
||||
|
||||
#: pretix/base/models/items.py
|
||||
msgid "Asked on"
|
||||
msgstr ""
|
||||
msgstr "質問日"
|
||||
|
||||
#: pretix/base/models/items.py pretix/base/models/organizer.py
|
||||
msgid ""
|
||||
@@ -7340,12 +7338,9 @@ msgid "The payment for this invoice has already been received."
|
||||
msgstr "この請求書の支払いはすでに受領済みです。"
|
||||
|
||||
#: pretix/base/payment.py
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "This payment is already being processed and can not be canceled any more."
|
||||
msgid ""
|
||||
"This payment is already being processed and cannot be canceled any more."
|
||||
msgstr "この支払いは処理中のため、キャンセルできません。"
|
||||
msgstr "この支払いはすでに処理中のため、キャンセルできません。"
|
||||
|
||||
#: pretix/base/payment.py
|
||||
msgid "Automatic refunds are not supported by this payment provider."
|
||||
@@ -8105,16 +8100,12 @@ msgid "Presale end"
|
||||
msgstr "前売り終了"
|
||||
|
||||
#: pretix/base/reldate.py
|
||||
#, fuzzy
|
||||
#| msgid "Order email"
|
||||
msgid "Order creation"
|
||||
msgstr "注文者メール"
|
||||
msgstr "注文の作成"
|
||||
|
||||
#: pretix/base/reldate.py
|
||||
#, fuzzy
|
||||
#| msgid "Order expired"
|
||||
msgid "Order expiry"
|
||||
msgstr "注文の有効期限が切れました"
|
||||
msgstr "注文の失効"
|
||||
|
||||
#: pretix/base/reldate.py
|
||||
msgid "before"
|
||||
@@ -8143,22 +8134,22 @@ msgstr "未設定"
|
||||
#: pretix/base/reldate.py
|
||||
#, python-brace-format
|
||||
msgid "A relative date cannot be expressed as \"before\" for \"{}\""
|
||||
msgstr ""
|
||||
msgstr "相対的な日付は、「{}」に対して「before」で表すことはできません"
|
||||
|
||||
#: pretix/base/reldate.py
|
||||
#, python-brace-format
|
||||
msgid "A relative date cannot be expressed as \"after\" for \"{}\""
|
||||
msgstr ""
|
||||
msgstr "相対的な日付は「{}」に対して「after」として表すことはできません"
|
||||
|
||||
#: pretix/base/reldate.py
|
||||
#, python-brace-format
|
||||
msgid "A relative time cannot be expressed as \"before\" for \"{}\""
|
||||
msgstr ""
|
||||
msgstr "相対時間は「{}」に対して「before」で表すことはできません"
|
||||
|
||||
#: pretix/base/reldate.py
|
||||
#, python-brace-format
|
||||
msgid "A relative time cannot be expressed as \"after\" for \"{}\""
|
||||
msgstr ""
|
||||
msgstr "相対時間は「{}」に対して「after」で表すことはできません"
|
||||
|
||||
#: pretix/base/secrets.py
|
||||
msgid "Random (default, works with all pretix apps)"
|
||||
@@ -10067,12 +10058,12 @@ msgstr ""
|
||||
|
||||
#: pretix/base/settings.py
|
||||
msgid "No dates match your criteria."
|
||||
msgstr ""
|
||||
msgstr "条件に合致する日付はありません。"
|
||||
|
||||
#: pretix/base/settings.py
|
||||
msgctxt "subevents"
|
||||
msgid "Text for empty date results"
|
||||
msgstr ""
|
||||
msgstr "空の日付結果のテキスト"
|
||||
|
||||
#: pretix/base/settings.py
|
||||
msgctxt "subevents"
|
||||
@@ -10083,6 +10074,10 @@ msgid ""
|
||||
"touch with you to arrange further dates. We do not recommend more than one "
|
||||
"or two sentences."
|
||||
msgstr ""
|
||||
"このテキストは、カレンダーまたは日付リストが空の場合に表示されます。たとえば"
|
||||
"、月に日付が含まれていない場合や、ユーザーが選択したフィルターに結果が見つか"
|
||||
"らない場合です。これをご利用いただくことで、今後の日程調整のために連絡する方"
|
||||
"法を宣伝できます。1文または2文以上は推奨いたしません。"
|
||||
|
||||
#: pretix/base/settings.py
|
||||
msgid "Guidance text"
|
||||
@@ -13902,26 +13897,20 @@ msgid ""
|
||||
msgstr "ギフトカードの有効期限を発行年を含めた{}年に設定しました。"
|
||||
|
||||
#: pretix/control/forms/event.py
|
||||
#, fuzzy
|
||||
#| msgid "Payment date"
|
||||
msgid "Payment term"
|
||||
msgstr "支払い日"
|
||||
msgstr "支払条件"
|
||||
|
||||
#: pretix/control/forms/event.py
|
||||
msgid "same as above"
|
||||
msgstr ""
|
||||
msgstr "上記と同じ"
|
||||
|
||||
#: pretix/control/forms/event.py
|
||||
#, fuzzy
|
||||
#| msgid "Payment term in days"
|
||||
msgid "different payment term in days"
|
||||
msgstr "支払い条件(日数)"
|
||||
msgstr "日数で表す異なる支払条件"
|
||||
|
||||
#: pretix/control/forms/event.py
|
||||
#, fuzzy
|
||||
#| msgid "Payment term in minutes"
|
||||
msgid "different payment term in minutes"
|
||||
msgstr "支払い期限(分単位)"
|
||||
msgstr "分で表す異なる支払条件"
|
||||
|
||||
#: pretix/control/forms/event.py
|
||||
msgid "Prices including tax"
|
||||
@@ -22533,28 +22522,24 @@ msgstr ""
|
||||
"食事を提供する場合、ユーザーに食事制限について尋ねることができる一例です。"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
#, fuzzy
|
||||
#| msgid "Create a new question"
|
||||
msgid "Create a new per-ticket question"
|
||||
msgstr "新しい質問を作成"
|
||||
msgstr "チケットごとに新しい質問を作成"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
#, fuzzy
|
||||
#| msgid "Create a new question"
|
||||
msgid "Create a new order-level question"
|
||||
msgstr "新しい質問を作成"
|
||||
msgstr "注文ごとに新しい質問を作成"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
#, fuzzy
|
||||
#| msgid "Delete question"
|
||||
msgid "Per-ticket questions"
|
||||
msgstr "質問を削除"
|
||||
msgstr "チケットごとの質問"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
msgid ""
|
||||
"These questions are asked for every ticket, so possibly multiple times in "
|
||||
"the same order."
|
||||
msgstr ""
|
||||
"これらの質問はすべてのチケットに対して尋ねられるため、同じ順序で複数回になる"
|
||||
"可能性があります。"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
msgid "Create a new question"
|
||||
@@ -22573,28 +22558,24 @@ msgid "All personalized products"
|
||||
msgstr "すべてのパーソナライズされた製品"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
#, fuzzy
|
||||
#| msgid "Include questions"
|
||||
msgid "Per-order questions"
|
||||
msgstr "質問を含む"
|
||||
msgstr "注文ごとの質問"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
msgid ""
|
||||
"This functionality is in active development and expected to change "
|
||||
"significantly over the coming months."
|
||||
msgstr ""
|
||||
msgstr "この機能は現在開発が活発で、今後数か月で大幅に変化すると予想されています。"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
msgid ""
|
||||
"Per-order questions are currently not supported and will not be displayed in "
|
||||
"pretixPOS."
|
||||
msgstr ""
|
||||
msgstr "注文ごとの質問は現在サポートされておらず、pretixPOSでは表示されません。"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/questions.html
|
||||
#, fuzzy
|
||||
#| msgid "The question has been reordered."
|
||||
msgid "These questions are asked once per order."
|
||||
msgstr "質問の並び順が変更されました。"
|
||||
msgstr "これらの質問は、注文ごとに1回ずつ尋ねられます。"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/quota.html
|
||||
#: pretix/control/templates/pretixcontrol/items/quota_edit.html
|
||||
@@ -23186,10 +23167,8 @@ msgstr "(任意)"
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_questions.html
|
||||
#: pretix/presale/templates/pretixpresale/event/order_modify.html
|
||||
#, fuzzy
|
||||
#| msgid "Additional information"
|
||||
msgid "Additional order information"
|
||||
msgstr "追加情報"
|
||||
msgstr "追加の注文情報"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/order/delete.html
|
||||
msgid "Delete order"
|
||||
@@ -24856,14 +24835,13 @@ msgid "Hardware model"
|
||||
msgstr "ハードウェアの機種"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/devices.html
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Begin: %(time)s"
|
||||
#, python-format
|
||||
msgid "Last seen: %(time)s"
|
||||
msgstr "開始: %(time)s"
|
||||
msgstr "最後の閲覧: %(time)s"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/devices.html
|
||||
msgid "No recent contact"
|
||||
msgstr ""
|
||||
msgstr "最近の連絡なし"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/devices.html
|
||||
msgid "Not yet initialized"
|
||||
@@ -28220,19 +28198,14 @@ msgstr ""
|
||||
"に高いことを意味します。ドメインのDNS設定を更新すべきです。"
|
||||
|
||||
#: pretix/control/views/mailsetup.py
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "We did not find DMARC record for your domain. This means that there is a "
|
||||
#| "very high chance most of the emails will be rejected or marked as spam. "
|
||||
#| "You should update the DNS settings of your domain."
|
||||
msgid ""
|
||||
"We did not find a DMARC record for your domain. This means that there is a "
|
||||
"very high chance most of the emails will be rejected or marked as spam. You "
|
||||
"should update the DNS settings of your domain."
|
||||
msgstr ""
|
||||
"お客様のドメインのDMARCレコードが見つかりませんでした。これは、ほとんどのメー"
|
||||
"ルが拒否されたりスパムとしてマークされたりする可能性が非常に高いことを意味し"
|
||||
"ます。ドメインのDNS設定を更新すべきです。"
|
||||
"あなたのドメインのDMARCレコードが見つかりませんでした。これは、ほとんどの"
|
||||
"メールが拒否されたかスパムとしてマークされた可能性が非常に高いことを意味しま"
|
||||
"す。ドメインのDNS設定を更新すべきです。"
|
||||
|
||||
#: pretix/control/views/mailsetup.py
|
||||
msgid "The verification code was incorrect, please try again."
|
||||
@@ -30906,7 +30879,7 @@ msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/payment.py
|
||||
msgid "Allow further payments during compliance hold"
|
||||
msgstr ""
|
||||
msgstr "コンプライアンス保留の間、追加の支払いを許可する"
|
||||
|
||||
#: pretix/plugins/paypal2/payment.py
|
||||
msgid ""
|
||||
@@ -30916,16 +30889,20 @@ msgid ""
|
||||
"attempts during that window. This might result in them being charged twice "
|
||||
"if the original payment is approved."
|
||||
msgstr ""
|
||||
"PayPalの不正防止は、個々の支払いの処理をかなりの期間ブロックする可能性があり"
|
||||
"ます。この期間中、支払いは「保留中」とマークされています。顧客がその期間中に"
|
||||
"別の支払い試行を開始できるように許可することができます。元の支払いが承認され"
|
||||
"た場合、二重に請求される可能性があります。"
|
||||
|
||||
#: pretix/plugins/paypal2/payment.py
|
||||
msgid "Timeout further payment attempts"
|
||||
msgstr ""
|
||||
msgstr "追加の支払い試行がタイムアウト"
|
||||
|
||||
#: pretix/plugins/paypal2/payment.py
|
||||
msgid ""
|
||||
"Time duration in minutes after which another payment attempt is possible, "
|
||||
"while the last payment is still under investigation."
|
||||
msgstr ""
|
||||
msgstr "最後の支払いがまだ調査中である間、別の支払い試行が可能となる時間(分)。"
|
||||
|
||||
#: pretix/plugins/paypal2/payment.py
|
||||
msgid "-- Automatic --"
|
||||
@@ -31190,6 +31167,10 @@ msgid ""
|
||||
"twice in case PayPal allows your initial payment attempt. Please contact us "
|
||||
"to resolve this case."
|
||||
msgstr ""
|
||||
"支払いはPayPalで処理されています。通常より時間がかかります。PayPalが支払いを"
|
||||
"確認するまでお待ちいただくか、こちらまたは別の支払い方法で再度お支払いをお試"
|
||||
"しください。PayPalが最初の支払い試行を許可した場合、二重に請求される可能性が"
|
||||
"あります。この件を解決するために、弊社までご連絡ください。"
|
||||
|
||||
#: pretix/plugins/paypal2/views.py
|
||||
msgid ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
|
||||
"PO-Revision-Date: 2026-03-30 21:00+0000\n"
|
||||
"PO-Revision-Date: 2026-09-02 08:35+0000\n"
|
||||
"Last-Translator: Renne Rocha <renne@rocha.dev.br>\n"
|
||||
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/pt_BR/>\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.16.2\n"
|
||||
"X-Generator: Weblate 2026.8.1\n"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "English"
|
||||
@@ -93,7 +93,7 @@ msgstr "Hebraico"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
msgstr "Húngaro"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "Indonesian"
|
||||
@@ -153,7 +153,7 @@ msgstr "Espanhol (América latina)"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "Thai"
|
||||
msgstr ""
|
||||
msgstr "Tailandês"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "Turkish"
|
||||
@@ -3022,11 +3022,12 @@ msgid ""
|
||||
"The field \"%(label)s\" may not contain special characters such as "
|
||||
"\"%(chars)s\"."
|
||||
msgstr ""
|
||||
"O campo \"%(label)s\" não pode conter caracteres especiais como %(chars)s\"."
|
||||
|
||||
#: pretix/base/forms/questions.py
|
||||
#, python-format
|
||||
msgid "The field \"%(label)s\" may not contain an URL (%(url)s)."
|
||||
msgstr ""
|
||||
msgstr "O campo \"%(label)s\" não pode conter uma URL (%(url)s)."
|
||||
|
||||
#: pretix/base/forms/questions.py
|
||||
msgctxt "phonenumber"
|
||||
@@ -8173,7 +8174,7 @@ msgstr "Permitida"
|
||||
#: pretix/base/permissions.py
|
||||
msgctxt "permission_level"
|
||||
msgid "Access existing events"
|
||||
msgstr ""
|
||||
msgstr "Acessar eventos existentes"
|
||||
|
||||
#: pretix/base/permissions.py
|
||||
msgctxt "permission_level"
|
||||
@@ -13607,11 +13608,15 @@ msgstr "Por favor, continue em uma nova aba"
|
||||
#: pretix/base/templates/pretixbase/framebreak.html
|
||||
msgid "For security reasons, the following step is only possible in a new tab."
|
||||
msgstr ""
|
||||
"Por razões de segurança, o passo seguinte só é possível de ser feito em uma "
|
||||
"nova aba."
|
||||
|
||||
#: pretix/base/templates/pretixbase/framebreak.html
|
||||
msgid ""
|
||||
"If the new tab did not open automatically, please click the following button:"
|
||||
msgstr ""
|
||||
"Se uma nova aba não abrir automaticamente, por favor, clique no botão a "
|
||||
"seguir:"
|
||||
|
||||
#: pretix/base/templates/pretixbase/framebreak.html
|
||||
#: pretix/presale/templates/pretixpresale/event/cookies.html
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
|
||||
"PO-Revision-Date: 2026-08-09 06:00+0000\n"
|
||||
"Last-Translator: Julien <julien@circusiloveyou.com>\n"
|
||||
"PO-Revision-Date: 2026-09-04 14:29+0000\n"
|
||||
"Last-Translator: Linnea Thelander <linnea@coeo.events>\n"
|
||||
"Language-Team: Swedish <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"sv/>\n"
|
||||
"Language: 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 2026.8.1\n"
|
||||
"X-Generator: Weblate 2026.9\n"
|
||||
|
||||
#: pretix/_base_settings.py
|
||||
msgid "English"
|
||||
@@ -19170,8 +19170,9 @@ msgstr ""
|
||||
#: pretix/presale/templates/pretixpresale/event/order_pay.html
|
||||
#: pretix/presale/templates/pretixpresale/event/order_pay_change.html
|
||||
#: pretix/presale/templates/pretixpresale/event/position_change.html
|
||||
#, fuzzy
|
||||
msgid "Continue"
|
||||
msgstr "Fortsätta"
|
||||
msgstr "Fortsätt"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/oauth_authorization.html
|
||||
msgid "Authorize an application"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-06 15:52+0000\n"
|
||||
"PO-Revision-Date: 2026-08-09 06:00+0000\n"
|
||||
"Last-Translator: Julien <julien@circusiloveyou.com>\n"
|
||||
"PO-Revision-Date: 2026-09-04 14:29+0000\n"
|
||||
"Last-Translator: Linnea Thelander <linnea@coeo.events>\n"
|
||||
"Language-Team: Swedish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/sv/>\n"
|
||||
"Language: 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 2026.8.1\n"
|
||||
"X-Generator: Weblate 2026.9\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js
|
||||
msgid "Marked as paid"
|
||||
@@ -133,8 +133,9 @@ msgstr "Mercado Pago"
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js
|
||||
#, fuzzy
|
||||
msgid "Continue"
|
||||
msgstr "Fortsätta"
|
||||
msgstr "Fortsätt"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
#
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import List, Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import (
|
||||
@@ -30,13 +29,11 @@ from django.db.models import (
|
||||
)
|
||||
from django.db.models.lookups import Exact
|
||||
|
||||
from pretix.base.decimal import round_decimal
|
||||
from pretix.base.models import (
|
||||
Discount, Event, Item, ItemVariation, Quota, SalesChannel,
|
||||
SeatCategoryMapping, SubEvent, Voucher,
|
||||
ItemVariation, Quota, SalesChannel, SeatCategoryMapping,
|
||||
)
|
||||
from pretix.base.models.items import (
|
||||
ItemAddOn, ItemBundle, SubEventItem, SubEventItemVariation,
|
||||
Item, ItemAddOn, ItemBundle, SubEventItem, SubEventItemVariation,
|
||||
)
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.base.timemachine import time_machine_now
|
||||
@@ -57,39 +54,10 @@ def item_group_by_category(items):
|
||||
)
|
||||
|
||||
|
||||
def _single_item_discounts(event: Event, sales_channel: Union[str, SalesChannel],
|
||||
subevent: SubEvent=None, voucher: Voucher=None, is_addons=False) -> List[Discount]:
|
||||
discount_qs = event.discounts.filter(
|
||||
Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now()),
|
||||
Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now()),
|
||||
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=sales_channel),
|
||||
active=True,
|
||||
# Only discounts that can be applied before we know the full cart
|
||||
benefit_same_products=True,
|
||||
benefit_only_apply_to_cheapest_n_matches__isnull=True,
|
||||
condition_min_value=Decimal("0.00"),
|
||||
condition_min_count=1,
|
||||
).prefetch_related('condition_limit_products').order_by('position', 'pk')
|
||||
|
||||
if subevent:
|
||||
discount_qs = discount_qs.filter(
|
||||
Q(subevent_date_from__isnull=True) | Q(subevent_date_from__lte=subevent.date_from),
|
||||
Q(subevent_date_until__isnull=True) | Q(subevent_date_until__gte=subevent.date_from),
|
||||
)
|
||||
|
||||
if is_addons:
|
||||
discount_qs = discount_qs.filter(condition_apply_to_addons=True, benefit_apply_to_addons=True)
|
||||
|
||||
if voucher and voucher.price_mode != "none":
|
||||
discount_qs = discount_qs.filter(condition_ignore_voucher_discounted=False, benefit_ignore_voucher_discounted=False)
|
||||
|
||||
return list(discount_qs)
|
||||
|
||||
|
||||
def prepare_item_list_for_shop(event, *, channel: SalesChannel, subevent=None, voucher=None, require_seat=0, base_qs=None,
|
||||
allow_addons=False, allow_cross_sell=False,
|
||||
quota_cache=None, filter_items=None, filter_categories=None, memberships=None,
|
||||
ignore_hide_sold_out_for_item_ids=None, _discount_cache=None):
|
||||
ignore_hide_sold_out_for_item_ids=None):
|
||||
base_qs_set = base_qs is not None
|
||||
base_qs = base_qs if base_qs is not None else event.items
|
||||
|
||||
@@ -222,23 +190,6 @@ def prepare_item_list_for_shop(event, *, channel: SalesChannel, subevent=None, v
|
||||
if filter_categories:
|
||||
items = items.filter(category_id__in=[a for a in filter_categories if a.isdigit()])
|
||||
|
||||
# We pre-computate discounts that do not rely on specific combinations in the cart.
|
||||
# This is not the same order of operations that is applied in the cart, so there could be some differences
|
||||
# when it comes to tax rate handling, but we don't have that information in the product list anyway, so
|
||||
# that is acceptable.
|
||||
cache_key = (event, channel, voucher, subevent, allow_addons)
|
||||
if _discount_cache is None:
|
||||
_discount_cache = {}
|
||||
if cache_key not in _discount_cache:
|
||||
_discount_cache[cache_key] = list(_single_item_discounts(
|
||||
event=event,
|
||||
sales_channel=channel,
|
||||
voucher=voucher,
|
||||
subevent=subevent,
|
||||
is_addons=allow_addons,
|
||||
))
|
||||
discounts = _discount_cache[cache_key]
|
||||
|
||||
display_add_to_cart = False
|
||||
quota_cache_key = f'item_quota_cache:{subevent.id if subevent else 0}:{channel.identifier}:{bool(require_seat)}'
|
||||
quota_cache = quota_cache or event.cache.get(quota_cache_key) or {}
|
||||
@@ -321,10 +272,6 @@ def prepare_item_list_for_shop(event, *, channel: SalesChannel, subevent=None, v
|
||||
if resp:
|
||||
item.description += ("<br/>" if item.description else "") + resp
|
||||
|
||||
matching_discounts = [
|
||||
d for d in discounts if d.condition_all_products or item in d.condition_limit_products.all()
|
||||
]
|
||||
|
||||
if not item.has_variations:
|
||||
item._remove = False
|
||||
if not bool(item._subevent_quotas):
|
||||
@@ -352,51 +299,30 @@ def prepare_item_list_for_shop(event, *, channel: SalesChannel, subevent=None, v
|
||||
max_per_order
|
||||
)
|
||||
|
||||
configured_price = item_price_override.get(item.pk, item.default_price)
|
||||
original_price = item_price_override.get(item.pk, item.default_price)
|
||||
voucher_reduced = False
|
||||
if voucher:
|
||||
price = voucher.calculate_price(configured_price)
|
||||
voucher_reduced = price < configured_price
|
||||
price = voucher.calculate_price(original_price)
|
||||
voucher_reduced = price < original_price
|
||||
include_bundled = not voucher.all_bundles_included
|
||||
else:
|
||||
price = configured_price
|
||||
price = original_price
|
||||
include_bundled = True
|
||||
|
||||
if matching_discounts and not item.free_price:
|
||||
# Discounts and free prices are a non-recommended combination that behaves unintuitively, so we can
|
||||
# accept it not being handled here.
|
||||
discount = matching_discounts[0] # First matching discount rule always wins
|
||||
# First handle taxes because discount is always computed on gross
|
||||
taxed_price = item.tax(price, currency=event.currency, include_bundled=include_bundled)
|
||||
price_without_bundles = taxed_price.gross - sum(b.count * b.designated_price for b in item.bundles.all())
|
||||
discounted_price = round_decimal(
|
||||
taxed_price.gross - price_without_bundles * discount.benefit_discount_matching_percent / Decimal('100.00'),
|
||||
event.currency,
|
||||
)
|
||||
item.display_price = item.tax(discounted_price, currency=event.currency, include_bundled=include_bundled,
|
||||
base_price_is='gross')
|
||||
else:
|
||||
discount = None
|
||||
item.display_price = item.tax(price, currency=event.currency, include_bundled=include_bundled)
|
||||
|
||||
item.display_price = item.tax(price, currency=event.currency, include_bundled=include_bundled)
|
||||
if item.free_price and item.free_price_suggestion is not None and not voucher_reduced:
|
||||
item.suggested_price = item.tax(max(price, item.free_price_suggestion), currency=event.currency, include_bundled=include_bundled)
|
||||
else:
|
||||
item.suggested_price = item.display_price
|
||||
|
||||
if voucher_reduced or (discount and not item.original_price):
|
||||
# If a voucher is used, we use the non-voucher price as the "original price", even if a different
|
||||
# original price is set, to highlight the voucher's impact. We also use the configured price as
|
||||
# original price if a discount is applied and no explicit original price is set
|
||||
item.original_price = item.tax(configured_price, currency=event.currency, include_bundled=True)
|
||||
elif item.original_price:
|
||||
item.original_price = item.tax(
|
||||
item.original_price, currency=event.currency, include_bundled=True,
|
||||
base_price_is='net' if event.settings.display_net_prices else 'gross' # backwards-compat
|
||||
)
|
||||
if price != original_price:
|
||||
item.original_price = item.tax(original_price, currency=event.currency, include_bundled=True)
|
||||
else:
|
||||
item.original_price = None
|
||||
|
||||
item.original_price = (
|
||||
item.tax(item.original_price, currency=event.currency, include_bundled=True,
|
||||
base_price_is='net' if event.settings.display_net_prices else 'gross') # backwards-compat
|
||||
if item.original_price else None
|
||||
)
|
||||
if not display_add_to_cart:
|
||||
display_add_to_cart = not item.requires_seat and item.order_max > 0
|
||||
else:
|
||||
@@ -426,53 +352,35 @@ def prepare_item_list_for_shop(event, *, channel: SalesChannel, subevent=None, v
|
||||
max_per_order
|
||||
)
|
||||
|
||||
configured_price = var_price_override.get(var.pk, var.price)
|
||||
original_price = var_price_override.get(var.pk, var.price)
|
||||
voucher_reduced = False
|
||||
if voucher:
|
||||
price = voucher.calculate_price(configured_price)
|
||||
voucher_reduced = price < configured_price
|
||||
price = voucher.calculate_price(original_price)
|
||||
voucher_reduced = price < original_price
|
||||
include_bundled = not voucher.all_bundles_included
|
||||
else:
|
||||
price = configured_price
|
||||
price = original_price
|
||||
include_bundled = True
|
||||
|
||||
if matching_discounts and not item.free_price:
|
||||
# Discounts and free prices are a non-recommended combination that behaves unintuitively, so we can
|
||||
# accept it not being handled here.
|
||||
discount = matching_discounts[0] # First matching discount rule always wins
|
||||
# First handle taxes because discount is always computed on gross
|
||||
taxed_price = var.tax(price, currency=event.currency, include_bundled=include_bundled)
|
||||
price_without_bundles = taxed_price.gross - sum(b.count * b.designated_price for b in item.bundles.all())
|
||||
discounted_price = round_decimal(
|
||||
taxed_price.gross - price_without_bundles * discount.benefit_discount_matching_percent / Decimal('100.00'),
|
||||
event.currency,
|
||||
)
|
||||
var.display_price = var.tax(discounted_price, currency=event.currency,
|
||||
include_bundled=include_bundled,
|
||||
base_price_is='gross')
|
||||
else:
|
||||
discount = None
|
||||
var.display_price = var.tax(price, currency=event.currency, include_bundled=include_bundled)
|
||||
var.display_price = var.tax(price, currency=event.currency, include_bundled=include_bundled)
|
||||
|
||||
if item.free_price and var.free_price_suggestion is not None and not voucher_reduced:
|
||||
var.suggested_price = var.tax(max(price, var.free_price_suggestion), currency=event.currency,
|
||||
include_bundled=include_bundled)
|
||||
var.suggested_price = item.tax(max(price, var.free_price_suggestion), currency=event.currency,
|
||||
include_bundled=include_bundled)
|
||||
elif item.free_price and item.free_price_suggestion is not None and not voucher_reduced:
|
||||
var.suggested_price = var.tax(max(price, item.free_price_suggestion), currency=event.currency,
|
||||
include_bundled=include_bundled)
|
||||
var.suggested_price = item.tax(max(price, item.free_price_suggestion), currency=event.currency,
|
||||
include_bundled=include_bundled)
|
||||
else:
|
||||
var.suggested_price = var.display_price
|
||||
|
||||
if voucher_reduced or (discount and not item.original_price and not var.original_price):
|
||||
var.original_price = var.tax(configured_price, currency=event.currency, include_bundled=True)
|
||||
elif item.original_price or var.original_price:
|
||||
var.original_price = var.tax(
|
||||
var.original_price or item.original_price, currency=event.currency,
|
||||
include_bundled=True,
|
||||
base_price_is='net' if event.settings.display_net_prices else 'gross' # backwards-compat
|
||||
)
|
||||
if price != original_price:
|
||||
var.original_price = var.tax(original_price, currency=event.currency, include_bundled=True)
|
||||
else:
|
||||
var.original_price = None
|
||||
var.original_price = (
|
||||
var.tax(var.original_price or item.original_price, currency=event.currency,
|
||||
include_bundled=True,
|
||||
base_price_is='net' if event.settings.display_net_prices else 'gross') # backwards-compat
|
||||
) if var.original_price or item.original_price else None
|
||||
|
||||
var.current_unavailability_reason = _get_variant_unavailability_reason(var, has_voucher=voucher, subevent=subevent)
|
||||
|
||||
|
||||
@@ -67,13 +67,14 @@ $(function () {
|
||||
continue
|
||||
matches.push([rank, plugin])
|
||||
}
|
||||
matches.sort(function (a, b) { return (b[0] - a[0]) || strcmp(a[1].sortName, b[1].sortName) })
|
||||
$results.append(matches.map(function (res) { return $(res[1].html).prepend('<span class="pull-right">' + res[1].categoryLabel + '</span>') }))
|
||||
matches.sort((a, b) => ((b[0] - a[0]) || strcmp(a[1].sortName, b[1].sortName)))
|
||||
$results.append(matches.map((res) => $(res[1].html).prepend('<span class="pull-right">' + res[1].categoryLabel + '</span>')))
|
||||
$results.find('.panel-body, .panel, .featured-plugin, .btn-lg').removeClass('panel-body panel featured-plugin btn-lg')
|
||||
if (matches.length === 0) {
|
||||
$results.append(gettext('No results'))
|
||||
}
|
||||
}
|
||||
search() // if pre-filled by browser (e.g. after reload)
|
||||
$('#plugin_search_input').on('input', search)
|
||||
$('input[name=plugin_state_filter]').on('change', search)
|
||||
$results_box.find('button.close').on('click', function () {
|
||||
@@ -81,9 +82,9 @@ $(function () {
|
||||
$('#plugin_search_input').val('').trigger('input')
|
||||
})
|
||||
if (location.search) {
|
||||
var search = new URLSearchParams(location.search)
|
||||
if (search.has('q')) {
|
||||
$('#plugin_search_input').val(search.get('q')).trigger('input')
|
||||
const search_params = new URLSearchParams(location.search)
|
||||
if (search_params.has('q')) {
|
||||
$('#plugin_search_input').val(search_params.get('q')).trigger('input')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -749,22 +749,22 @@ def test_query_count_many_items(event, itemcount):
|
||||
''',
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
''',
|
||||
expect_num_queries=8,
|
||||
)
|
||||
check_cart_behaviour(
|
||||
event,
|
||||
cart_contents=''' Price Discounted Subev
|
||||
Ticket 1 42.00 42.00 0
|
||||
Ticket 1 42.00 42.00 0
|
||||
Ticket 1 42.00 42.00 0
|
||||
Ticket 1 42.00 42.00 0
|
||||
Ticket 1 42.00 42.00 0
|
||||
''',
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
Tickets Ticket 2 42.00 0.00 1 -
|
||||
''',
|
||||
expect_num_queries=9,
|
||||
)
|
||||
check_cart_behaviour(
|
||||
event,
|
||||
cart_contents=''' Price Discounted Subev
|
||||
Ticket 1 42.00 42.00 0
|
||||
Ticket 1 42.00 42.00 0
|
||||
Ticket 1 42.00 42.00 0
|
||||
Ticket 1 42.00 42.00 0
|
||||
Ticket 1 42.00 42.00 0
|
||||
''',
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
Tickets Ticket 2 42.00 0.00 1 -
|
||||
''',
|
||||
expect_num_queries=10,
|
||||
)
|
||||
check_cart_behaviour(
|
||||
event,
|
||||
cart_contents=''' Price Discounted Subev
|
||||
@@ -779,7 +779,7 @@ def test_query_count_many_items(event, itemcount):
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
Tickets Ticket 2 42.00 0.00 1 -
|
||||
''',
|
||||
expect_num_queries=10,
|
||||
expect_num_queries=9,
|
||||
)
|
||||
|
||||
|
||||
@@ -803,22 +803,22 @@ def test_query_count_many_categories_and_discounts(event, catcount):
|
||||
''',
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
''',
|
||||
expect_num_queries=8,
|
||||
)
|
||||
check_cart_behaviour(
|
||||
event,
|
||||
cart_contents=''' Price Discounted Subev
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
''',
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
Category 1 Ticket 1-B 42.00 0.00 1 -
|
||||
''',
|
||||
expect_num_queries=9,
|
||||
)
|
||||
check_cart_behaviour(
|
||||
event,
|
||||
cart_contents=''' Price Discounted Subev
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
Ticket 1-A 42.00 42.00 0
|
||||
''',
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
Category 1 Ticket 1-B 42.00 0.00 1 -
|
||||
''',
|
||||
expect_num_queries=10,
|
||||
)
|
||||
check_cart_behaviour(
|
||||
event,
|
||||
cart_contents=''' Price Discounted Subev
|
||||
@@ -833,7 +833,7 @@ def test_query_count_many_categories_and_discounts(event, catcount):
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
Category 1 Ticket 1-B 42.00 0.00 1 -
|
||||
''',
|
||||
expect_num_queries=10,
|
||||
expect_num_queries=9,
|
||||
)
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@ def test_query_count_many_cartpos(event, catcount):
|
||||
''',
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
''',
|
||||
expect_num_queries=9,
|
||||
expect_num_queries=8,
|
||||
)
|
||||
check_cart_behaviour(
|
||||
event,
|
||||
@@ -871,7 +871,7 @@ def test_query_count_many_cartpos(event, catcount):
|
||||
recommendations=''' Price Discounted Price Max Count Prefix
|
||||
Category 1 Ticket 1-B 42.00 0.00 1 -
|
||||
''',
|
||||
expect_num_queries=10,
|
||||
expect_num_queries=9,
|
||||
)
|
||||
check_cart_behaviour(
|
||||
event,
|
||||
@@ -893,5 +893,5 @@ def test_query_count_many_cartpos(event, catcount):
|
||||
Category 1 Ticket 1-B 42.00 0.00 1 -
|
||||
Category 2 Ticket 2-B 42.00 0.00 1 -
|
||||
''',
|
||||
expect_num_queries=14,
|
||||
expect_num_queries=13,
|
||||
)
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
from django.utils.timezone import now
|
||||
from django_scopes import scope
|
||||
|
||||
from pretix.base.models import Event, Organizer
|
||||
from pretix.presale.productlist import prepare_item_list_for_shop
|
||||
|
||||
# Tests for prepare_item_list_for_shop are really incomplete since historically, most features are
|
||||
# tested on the test_event or test_widget layer. We'll slowly add new tests here to test closer to the
|
||||
# source.
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def event():
|
||||
o = Organizer.objects.create(name='MRMCD', slug='mrmcd')
|
||||
e = Event.objects.create(
|
||||
organizer=o, name='MRMCD2015', slug='2015',
|
||||
date_from=now(), live=True
|
||||
)
|
||||
with scope(organizer=o):
|
||||
yield e
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def quota(event):
|
||||
return event.quotas.create(name="Tickets", size=500)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def item(event, quota):
|
||||
i = event.items.create(name="Ticket", default_price=Decimal("42.00"))
|
||||
quota.items.add(i)
|
||||
return i
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def variation(event, quota):
|
||||
i = event.items.create(name="Ticket with variants", default_price=Decimal("99.00"))
|
||||
v = i.variations.create(value="Default", default_price=Decimal("42.00"))
|
||||
quota.items.add(i)
|
||||
quota.variations.add(v)
|
||||
return v
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def discount(event):
|
||||
return event.discounts.create(
|
||||
internal_name="Early-Bird-Discount",
|
||||
all_sales_channels=True,
|
||||
available_from=now() - timedelta(days=2),
|
||||
available_until=now() + timedelta(days=2),
|
||||
condition_all_products=True,
|
||||
condition_min_count=1,
|
||||
benefit_discount_matching_percent=Decimal("10.00"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def channel(event):
|
||||
return event.organizer.sales_channels.get(identifier="web")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_default_price(event, item, variation, channel):
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("42")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("42")
|
||||
|
||||
|
||||
def _test_no_discount(event, channel):
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("42")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("42")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discount_applied(event, item, variation, channel, discount):
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("37.80")
|
||||
assert items[0].original_price.gross == Decimal("42.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("37.80")
|
||||
assert items[1].available_variations[0].original_price.gross == Decimal("42.00")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discount_for_groups_ignored(event, item, variation, channel, discount):
|
||||
discount.condition_min_count = 2
|
||||
discount.save()
|
||||
_test_no_discount(event, channel)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discount_original_price_kept(event, item, variation, channel, discount):
|
||||
item.original_price = Decimal("46.00")
|
||||
item.save()
|
||||
variation.original_price = Decimal("46.00")
|
||||
variation.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("37.80")
|
||||
assert items[0].original_price.gross == Decimal("46.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("37.80")
|
||||
assert items[1].available_variations[0].original_price.gross == Decimal("46.00")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discount_out_of_timeframe(event, item, variation, channel, discount):
|
||||
discount.available_from = now() + timedelta(days=2)
|
||||
discount.save()
|
||||
_test_no_discount(event, channel)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discount_wrong_channel(event, item, variation, channel, discount):
|
||||
discount.all_sales_channels = False
|
||||
discount.save()
|
||||
_test_no_discount(event, channel)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discount_benefits_other_products_ignored(event, item, variation, channel, discount):
|
||||
discount.benefit_same_products = False
|
||||
discount.save()
|
||||
_test_no_discount(event, channel)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discounts_for_addons(event, item, variation, channel, discount):
|
||||
discount.condition_apply_to_addons = False
|
||||
discount.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel, allow_addons=True)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("42.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("42.00")
|
||||
|
||||
discount.condition_apply_to_addons = True
|
||||
discount.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel, allow_addons=True)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("37.80")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("37.80")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discounts_with_voucher(event, item, variation, channel, discount):
|
||||
voucher = event.vouchers.create(code="FOO", price_mode="subtract", value=Decimal("10.00"))
|
||||
voucher2 = event.vouchers.create(code="BAR")
|
||||
discount.condition_ignore_voucher_discounted = True
|
||||
discount.save()
|
||||
item.original_price = Decimal("46.00")
|
||||
item.save()
|
||||
variation.item.original_price = Decimal("46.00")
|
||||
variation.item.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel, voucher=voucher)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("32.00")
|
||||
assert items[0].original_price.gross == Decimal("42.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("32.00")
|
||||
assert items[1].available_variations[0].original_price.gross == Decimal("42.00")
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel, voucher=voucher2)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("37.80")
|
||||
assert items[0].original_price.gross == Decimal("46.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("37.80")
|
||||
assert items[1].available_variations[0].original_price.gross == Decimal("46.00")
|
||||
|
||||
discount.condition_ignore_voucher_discounted = False
|
||||
discount.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel, voucher=voucher)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("28.80")
|
||||
assert items[0].original_price.gross == Decimal("42.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("28.80")
|
||||
assert items[1].available_variations[0].original_price.gross == Decimal("42.00")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discounts_for_subevent_timeframe(event, quota, item, variation, channel, discount):
|
||||
event.has_subevents = True
|
||||
event.save()
|
||||
se = event.subevents.create(
|
||||
name="Foobar", date_from=datetime(2028, 12, 27, 10, 0, 0, tzinfo=UTC)
|
||||
)
|
||||
quota.subevent = se
|
||||
quota.save()
|
||||
|
||||
discount.subevent_date_from = se.date_from + timedelta(days=1)
|
||||
discount.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel, subevent=se)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("42.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("42.00")
|
||||
|
||||
discount.subevent_date_from = se.date_from - timedelta(days=1)
|
||||
discount.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel, subevent=se)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("37.80")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("37.80")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discounts_for_products(event, quota, item, variation, channel, discount):
|
||||
discount.condition_all_products = False
|
||||
discount.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("42.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("42.00")
|
||||
|
||||
discount.condition_limit_products.add(item)
|
||||
discount.condition_limit_products.add(variation.item)
|
||||
discount.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("37.80")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("37.80")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discounts_for_products_tax_additive(event, quota, item, variation, channel, discount):
|
||||
tr = event.tax_rules.create(rate=Decimal("19.00"), price_includes_tax=False)
|
||||
item.tax_rule = tr
|
||||
item.save()
|
||||
variation.item.tax_rule = tr
|
||||
variation.item.save()
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("44.98")
|
||||
assert items[0].original_price.gross == Decimal("49.98")
|
||||
assert items[0].original_price.net == Decimal("42.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("44.98")
|
||||
assert items[1].available_variations[0].original_price.gross == Decimal("49.98")
|
||||
assert items[1].available_variations[0].original_price.net == Decimal("42.00")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_discounts_for_products_tax_additive_bundle_included(event, quota, item, variation, channel, discount):
|
||||
tr = event.tax_rules.create(rate=Decimal("19.00"), price_includes_tax=False)
|
||||
b = event.items.create(name="Bundled product", default_price=Decimal("10.00"), tax_rule=tr, require_bundling=True)
|
||||
quota.items.add(b)
|
||||
item.tax_rule = tr
|
||||
item.save()
|
||||
variation.item.tax_rule = tr
|
||||
variation.item.save()
|
||||
item.bundles.create(bundled_item=b, count=2, designated_price=Decimal("5.00"))
|
||||
variation.item.bundles.create(bundled_item=b, count=2, designated_price=Decimal("5.00"))
|
||||
items, _ = prepare_item_list_for_shop(event, channel=channel)
|
||||
assert len(items) == 2
|
||||
assert items[0].display_price.gross == Decimal("45.98")
|
||||
assert items[0].original_price.gross == Decimal("49.98")
|
||||
assert items[0].original_price.net == Decimal("42.00")
|
||||
assert items[1].available_variations[0].display_price.gross == Decimal("45.98")
|
||||
assert items[1].available_variations[0].original_price.gross == Decimal("49.98")
|
||||
assert items[1].available_variations[0].original_price.net == Decimal("42.00")
|
||||
Reference in New Issue
Block a user