Compare commits

..

8 Commits
django22 ... s

Author SHA1 Message Date
Raphael Michel
f2cd9a2002 Fix logic bug in attachment size check 2019-04-15 12:58:36 +02:00
Raphael Michel
874b38db17 Mark order as paid immediately 2019-04-15 12:58:20 +02:00
Raphael Michel
0f58e1c396 CSV import: Do not skip rows without a reference 2019-04-08 17:55:28 +02:00
Raphael Michel
36e0afc09e Further improvements to the print stylesheet 2019-04-08 17:42:06 +02:00
Raphael Michel
7164124a70 Display category description in add-on step 2019-04-08 15:23:40 +02:00
Raphael Michel
887d8832c0 Improve print CSS of order details 2019-04-07 18:12:12 +02:00
Raphael Michel
beb144f9a0 Fix API log cleanup 2019-04-07 15:31:35 +02:00
Raphael Michel
6d1dea7922 Upgrade to Django 2.2 and modern DRF and py.test (#1246)
* Upgrade django and stuff

* Update to Django 2.2 and recent versions of similar packages

* Provide explicit orderings to all models used in paginated queries

* Resolve naive datetime warnings in test suite

* Deal with deprecation warnings

* Fix sqlparse version
2019-04-07 14:09:49 +01:00
13 changed files with 142 additions and 27 deletions

View File

@@ -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()

View File

@@ -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():

View File

@@ -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)

View File

@@ -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:

View File

@@ -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']:

View File

@@ -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,

View File

@@ -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 %}

View File

@@ -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" %}

View 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);
}
}

View File

@@ -290,3 +290,4 @@ h2 .label {
@import "_iframe.scss";
@import "_a11y.scss";
@import "_print.scss";

View File

@@ -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)

View File

@@ -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')

View File

@@ -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