mirror of
https://github.com/pretix/pretix.git
synced 2026-01-09 22:12:26 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2cd9a2002 | ||
|
|
874b38db17 | ||
|
|
0f58e1c396 | ||
|
|
36e0afc09e | ||
|
|
7164124a70 | ||
|
|
887d8832c0 | ||
|
|
beb144f9a0 | ||
|
|
6d1dea7922 |
@@ -23,4 +23,4 @@ def cleanup_webhook_logs(sender, **kwargs):
|
||||
|
||||
@receiver(periodic_task)
|
||||
def cleanup_api_logs(sender, **kwargs):
|
||||
ApiCall.objects.filter(datetime__lte=now() - timedelta(hours=24)).delete()
|
||||
ApiCall.objects.filter(created__lte=now() - timedelta(hours=24)).delete()
|
||||
|
||||
@@ -1159,7 +1159,7 @@ class OrderPayment(models.Model):
|
||||
self.order.log_action('pretix.event.order.overpaid', {}, user=user, auth=auth)
|
||||
order_paid.send(self.order.event, order=self.order)
|
||||
|
||||
def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text=''):
|
||||
def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='', lock=True):
|
||||
"""
|
||||
Marks the payment as complete. If possible, this also marks the order as paid if no further
|
||||
payment is required
|
||||
@@ -1218,7 +1218,7 @@ class OrderPayment(models.Model):
|
||||
if payment_sum - refund_sum < self.order.total:
|
||||
return
|
||||
|
||||
if self.order.status == Order.STATUS_PENDING and self.order.expires > now() + timedelta(hours=12):
|
||||
if (self.order.status == Order.STATUS_PENDING and self.order.expires > now() + timedelta(hours=12)) or not lock:
|
||||
# Performance optimization. In this case, there's really no reason to lock everything and an atomic
|
||||
# database transaction is more than enough.
|
||||
with transaction.atomic():
|
||||
|
||||
@@ -219,14 +219,23 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
args.append((name, content, ct.type))
|
||||
attach_size += len(content)
|
||||
|
||||
if attach_tickets < 4 * 1024 * 1024:
|
||||
if attach_size < 4 * 1024 * 1024:
|
||||
# Do not attach more than 4MB, it will bounce way to often.
|
||||
|
||||
for a in args:
|
||||
try:
|
||||
email.attach(*a)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
order.log_action(
|
||||
'pretix.event.order.email.error',
|
||||
data={
|
||||
'subject': 'Attachments skipped',
|
||||
'message': 'Attachment have not been send because {} bytes are likely too large to arrive.'.format(attach_size),
|
||||
'recipient': '',
|
||||
'invoices': [],
|
||||
}
|
||||
)
|
||||
|
||||
email = email_filter.send_chained(event, 'message', message=email, order=order)
|
||||
|
||||
|
||||
@@ -567,6 +567,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
||||
meta_info: dict=None, sales_channel: str='web'):
|
||||
fees, pf = _get_fees(positions, payment_provider, address, meta_info, event)
|
||||
total = sum([c.price for c in positions]) + sum([c.value for c in fees])
|
||||
p = None
|
||||
|
||||
with transaction.atomic():
|
||||
order = Order(
|
||||
@@ -600,7 +601,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
||||
fee.save()
|
||||
|
||||
if payment_provider and not order.require_approval:
|
||||
order.payments.create(
|
||||
p = order.payments.create(
|
||||
state=OrderPayment.PAYMENT_STATE_CREATED,
|
||||
provider=payment_provider.identifier,
|
||||
amount=total,
|
||||
@@ -616,7 +617,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
||||
order.log_action('pretix.event.order.consent', data={'msg': msg})
|
||||
|
||||
order_placed.send(event, order=order)
|
||||
return order
|
||||
return order, p
|
||||
|
||||
|
||||
def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||
@@ -648,8 +649,12 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||
if len(position_ids) != len(positions):
|
||||
raise OrderError(error_messages['internal'])
|
||||
_check_positions(event, now_dt, positions, address=addr)
|
||||
order = _create_order(event, email, positions, now_dt, pprov,
|
||||
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel)
|
||||
order, payment = _create_order(event, email, positions, now_dt, pprov,
|
||||
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel)
|
||||
|
||||
free_order_flow = payment and payment_provider == 'free' and order.total == Decimal('0.00')
|
||||
if free_order_flow:
|
||||
payment.confirm(send_mail=False, lock=False)
|
||||
|
||||
invoice = order.invoices.last() # Might be generated by plugin already
|
||||
if event.settings.get('invoice_generate') == 'True' and invoice_qualified(order):
|
||||
@@ -664,7 +669,7 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
|
||||
if order.require_approval:
|
||||
email_template = event.settings.mail_text_order_placed_require_approval
|
||||
log_entry = 'pretix.event.order.email.order_placed_require_approval'
|
||||
elif payment_provider == 'free':
|
||||
elif free_order_flow:
|
||||
email_template = event.settings.mail_text_order_free
|
||||
log_entry = 'pretix.event.order.email.order_free'
|
||||
else:
|
||||
|
||||
@@ -28,8 +28,7 @@ def parse(data, hint):
|
||||
resrow['amount'] = re.sub('[^0-9,+.-]', '', resrow['amount'])
|
||||
if hint.get('date') is not None:
|
||||
resrow['date'] = row[int(hint.get('date'))].strip()
|
||||
if len(resrow['amount']) == 0 or 'amount' not in resrow \
|
||||
or len(resrow['reference']) == 0 or resrow['date'] == '':
|
||||
if len(resrow['amount']) == 0 or 'amount' not in resrow or resrow['date'] == '':
|
||||
# This is probably a headline or something other special.
|
||||
continue
|
||||
if resrow['reference'] or resrow['payer']:
|
||||
|
||||
@@ -14,7 +14,7 @@ from django.utils.translation import (
|
||||
from django.views.generic.base import TemplateResponseMixin
|
||||
|
||||
from pretix.base.models import Order
|
||||
from pretix.base.models.orders import InvoiceAddress
|
||||
from pretix.base.models.orders import InvoiceAddress, OrderPayment
|
||||
from pretix.base.services.cart import (
|
||||
get_fees, set_cart_addons, update_tax_rates,
|
||||
)
|
||||
@@ -721,7 +721,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
return self.get_step_url(self.request)
|
||||
|
||||
def get_order_url(self, order):
|
||||
payment = order.payments.first()
|
||||
payment = order.payments.filter(state__in=[OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_PENDING]).first()
|
||||
if not payment:
|
||||
return eventreverse(self.request.event, 'presale:event.order', kwargs={
|
||||
'order': order.code,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixpresale/event/checkout_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load rich_text %}
|
||||
{% block inner %}
|
||||
<p>
|
||||
{% trans "For some of the products in your cart, you can choose additional options before you continue." %}
|
||||
@@ -30,6 +31,9 @@
|
||||
{% for c in form.categories %}
|
||||
<fieldset>
|
||||
<legend>{{ c.category.name }}</legend>
|
||||
{% if c.category.description %}
|
||||
{{ c.category.description|rich_text }}
|
||||
{% endif %}
|
||||
<p>
|
||||
{% if c.min_count == c.max_count %}
|
||||
{% blocktrans trimmed count min_count=c.min_count %}
|
||||
|
||||
@@ -107,13 +107,13 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if can_download and download_buttons and order.count_positions %}
|
||||
<div class="alert alert-info">
|
||||
<div class="alert alert-info info-download">
|
||||
{% blocktrans trimmed %}
|
||||
You can download your tickets using the buttons below. Please have your ticket ready when entering the event.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% if cart.positions|length > 1 and can_download_multi %}
|
||||
<p>
|
||||
<p class="info-download">
|
||||
{% trans "Download all tickets at once:" %}
|
||||
{% for b in download_buttons %}
|
||||
{% if b.multi %}
|
||||
@@ -131,7 +131,7 @@
|
||||
{% endif %}
|
||||
{% elif not download_buttons and ticket_download_date %}
|
||||
{% if order.status == 'p' %}
|
||||
<div class="alert alert-info">
|
||||
<div class="alert alert-info info-download">
|
||||
{% blocktrans trimmed with date=ticket_download_date|date:"SHORT_DATE_FORMAT" %}
|
||||
You will be able to download your tickets here starting on {{ date }}.
|
||||
{% endblocktrans %}
|
||||
@@ -249,7 +249,7 @@
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% if order.cancel_allowed %}
|
||||
<div class="panel panel-primary cart">
|
||||
<div class="panel panel-primary panel-cancellation">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Cancellation" context "action" %}
|
||||
|
||||
65
src/pretix/static/pretixpresale/scss/_print.scss
Normal file
65
src/pretix/static/pretixpresale/scss/_print.scss
Normal file
@@ -0,0 +1,65 @@
|
||||
@media print {
|
||||
body {
|
||||
font-size: 12px;
|
||||
}
|
||||
a[href]:after {
|
||||
content: none;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
.page-header {
|
||||
margin-top: 0;
|
||||
}
|
||||
.page-header .pull-right,
|
||||
.thank-you,
|
||||
.panel-heading a,
|
||||
.download-desktop,
|
||||
.download-mobile,
|
||||
.info-download,
|
||||
.panel-cancellation,
|
||||
footer {
|
||||
display: none !important;
|
||||
}
|
||||
.cart-row .product {
|
||||
width: 50%;
|
||||
}
|
||||
.cart-row.has-downloads .product {
|
||||
width: 80%;
|
||||
}
|
||||
.cart-row .count {
|
||||
width: 10%;
|
||||
}
|
||||
.cart-row .singleprice {
|
||||
width: 20%;
|
||||
clear: none;
|
||||
}
|
||||
.cart-row .totalprice {
|
||||
width: 20%;
|
||||
}
|
||||
.panel-body .text-right a.btn {
|
||||
display: none;
|
||||
}
|
||||
@media(max-width: $screen-sm-max) {
|
||||
.cart-row .product {
|
||||
width: 50%;
|
||||
}
|
||||
.cart-row.has-downloads .product {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.dl-horizontal dt {
|
||||
width: 100px;
|
||||
}
|
||||
.dl-horizontal dd {
|
||||
margin-left: 120px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 0px) {
|
||||
@include make-grid(sm);
|
||||
@include make-grid(md);
|
||||
@include make-grid(lg);
|
||||
}
|
||||
}
|
||||
@@ -290,3 +290,4 @@ h2 .label {
|
||||
|
||||
@import "_iframe.scss";
|
||||
@import "_a11y.scss";
|
||||
@import "_print.scss";
|
||||
|
||||
@@ -41,7 +41,7 @@ def test_expiry_days(event):
|
||||
event.settings.set('payment_term_weekdays', False)
|
||||
order = _create_order(event, email='dummy@example.org', positions=[],
|
||||
now_dt=today, payment_provider=FreeOrderProvider(event),
|
||||
locale='de')
|
||||
locale='de')[0]
|
||||
assert (order.expires - today).days == 5
|
||||
|
||||
|
||||
@@ -52,14 +52,14 @@ def test_expiry_weekdays(event):
|
||||
event.settings.set('payment_term_weekdays', True)
|
||||
order = _create_order(event, email='dummy@example.org', positions=[],
|
||||
now_dt=today, payment_provider=FreeOrderProvider(event),
|
||||
locale='de')
|
||||
locale='de')[0]
|
||||
assert (order.expires - today).days == 6
|
||||
assert order.expires.weekday() == 0
|
||||
|
||||
today = make_aware(datetime(2016, 9, 19, 15, 0, 0, 0))
|
||||
order = _create_order(event, email='dummy@example.org', positions=[],
|
||||
now_dt=today, payment_provider=FreeOrderProvider(event),
|
||||
locale='de')
|
||||
locale='de')[0]
|
||||
assert (order.expires - today).days == 7
|
||||
assert order.expires.weekday() == 0
|
||||
|
||||
@@ -72,12 +72,12 @@ def test_expiry_last(event):
|
||||
event.settings.set('payment_term_last', now() + timedelta(days=3))
|
||||
order = _create_order(event, email='dummy@example.org', positions=[],
|
||||
now_dt=today, payment_provider=FreeOrderProvider(event),
|
||||
locale='de')
|
||||
locale='de')[0]
|
||||
assert (order.expires - today).days == 3
|
||||
event.settings.set('payment_term_last', now() + timedelta(days=7))
|
||||
order = _create_order(event, email='dummy@example.org', positions=[],
|
||||
now_dt=today, payment_provider=FreeOrderProvider(event),
|
||||
locale='de')
|
||||
locale='de')[0]
|
||||
assert (order.expires - today).days == 5
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ def test_expiry_last_relative(event):
|
||||
))
|
||||
order = _create_order(event, email='dummy@example.org', positions=[],
|
||||
now_dt=today, payment_provider=FreeOrderProvider(event),
|
||||
locale='de')
|
||||
locale='de')[0]
|
||||
assert (order.expires - today).days == 3
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ def test_expiry_last_relative_subevents(event):
|
||||
))
|
||||
order = _create_order(event, email='dummy@example.org', positions=[cp1, cp2],
|
||||
now_dt=today, payment_provider=FreeOrderProvider(event),
|
||||
locale='de')
|
||||
locale='de')[0]
|
||||
assert (order.expires - today).days == 6
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ def test_expiry_dst(event):
|
||||
today = tz.localize(datetime(2016, 10, 29, 12, 0, 0)).astimezone(utc)
|
||||
order = _create_order(event, email='dummy@example.org', positions=[],
|
||||
now_dt=today, payment_provider=FreeOrderProvider(event),
|
||||
locale='de')
|
||||
locale='de')[0]
|
||||
localex = order.expires.astimezone(tz)
|
||||
assert (localex.hour, localex.minute) == (23, 59)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from pretix.base.models import (
|
||||
Event, Item, Order, OrderFee, OrderPayment, OrderPosition, Organizer,
|
||||
Quota, Team, User,
|
||||
)
|
||||
from pretix.plugins.banktransfer.models import BankImportJob
|
||||
from pretix.plugins.banktransfer.models import BankImportJob, BankTransaction
|
||||
from pretix.plugins.banktransfer.tasks import process_banktransfers
|
||||
|
||||
|
||||
@@ -300,6 +300,19 @@ def test_wrong_event_organizer(env, orga_job):
|
||||
assert env[2].status == Order.STATUS_PENDING
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_keep_unmatched(env, orga_job):
|
||||
process_banktransfers(orga_job, [{
|
||||
'payer': 'Karla Kundin',
|
||||
'reference': 'No useful reference',
|
||||
'date': '2016-01-26',
|
||||
'amount': '23.00'
|
||||
}])
|
||||
job = BankImportJob.objects.last()
|
||||
t = job.transactions.last()
|
||||
assert t.state == BankTransaction.STATE_NOMATCH
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_import_very_long_csv_file(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
|
||||
@@ -907,6 +907,24 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase):
|
||||
self.assertEqual(OrderPosition.objects.count(), 1)
|
||||
self.assertEqual(OrderPosition.objects.first().price, 42)
|
||||
|
||||
def test_free_order(self):
|
||||
self.ticket.free_price = True
|
||||
self.ticket.save()
|
||||
cr1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=0, expires=now() + timedelta(minutes=10)
|
||||
)
|
||||
self._set_session('payment', 'free')
|
||||
|
||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||
doc = BeautifulSoup(response.rendered_content, "lxml")
|
||||
self.assertEqual(len(doc.select(".thank-you")), 1)
|
||||
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
|
||||
self.assertEqual(Order.objects.count(), 1)
|
||||
self.assertEqual(OrderPosition.objects.count(), 1)
|
||||
self.assertEqual(OrderPosition.objects.first().price, 0)
|
||||
self.assertEqual(Order.objects.first().status, Order.STATUS_PAID)
|
||||
|
||||
def test_confirm_in_time(self):
|
||||
cr1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -920,6 +938,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase):
|
||||
self.assertFalse(CartPosition.objects.filter(id=cr1.id).exists())
|
||||
self.assertEqual(Order.objects.count(), 1)
|
||||
self.assertEqual(OrderPosition.objects.count(), 1)
|
||||
self.assertEqual(Order.objects.first().status, Order.STATUS_PENDING)
|
||||
|
||||
def test_subevent_confirm_expired_available(self):
|
||||
self.event.has_subevents = True
|
||||
|
||||
Reference in New Issue
Block a user