forked from CGM_Public/pretix_original
2365 lines
91 KiB
Python
2365 lines
91 KiB
Python
#
|
|
# This file is part of pretix (Community Edition).
|
|
#
|
|
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
|
# Copyright (C) 2020-2021 rami.io 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/>.
|
|
#
|
|
|
|
# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
|
|
# the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
|
|
#
|
|
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
|
|
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
|
|
#
|
|
# This file contains Apache-licensed contributions copyrighted by: Daniel, Flavia Bastos, Jahongir
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
|
|
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations under the License.
|
|
|
|
from datetime import timedelta
|
|
from decimal import Decimal
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from bs4 import BeautifulSoup
|
|
from django.core import mail
|
|
from django.utils.timezone import now
|
|
from django_countries.fields import Country
|
|
from django_scopes import scopes_disabled
|
|
from tests.base import SoupTest
|
|
from tests.plugins.stripe.test_provider import MockedCharge
|
|
|
|
from pretix.base.models import (
|
|
Event, GiftCard, InvoiceAddress, Item, Order, OrderFee, OrderPayment,
|
|
OrderPosition, OrderRefund, Organizer, Question, QuestionAnswer, Quota,
|
|
Team, User,
|
|
)
|
|
from pretix.base.payment import PaymentException
|
|
from pretix.base.services.invoices import (
|
|
generate_cancellation, generate_invoice,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def env():
|
|
o = Organizer.objects.create(name='Dummy', slug='dummy')
|
|
event = Event.objects.create(
|
|
organizer=o, name='Dummy', slug='dummy',
|
|
date_from=now(), plugins='pretix.plugins.banktransfer,pretix.plugins.stripe,tests.testdummy'
|
|
)
|
|
event.settings.set('ticketoutput_testdummy__enabled', True)
|
|
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
|
|
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True, can_manage_customers=True)
|
|
t.members.add(user)
|
|
t.limit_events.add(event)
|
|
o = Order.objects.create(
|
|
code='FOO', event=event, email='dummy@dummy.test',
|
|
status=Order.STATUS_PENDING,
|
|
datetime=now(), expires=now() + timedelta(days=10),
|
|
total=14, locale='en'
|
|
)
|
|
o.payments.create(
|
|
amount=o.total, provider='banktransfer', state=OrderPayment.PAYMENT_STATE_PENDING
|
|
)
|
|
ticket = Item.objects.create(event=event, name='Early-bird ticket',
|
|
category=None, default_price=23,
|
|
admission=True)
|
|
event.settings.set('attendee_names_asked', True)
|
|
event.settings.set('locales', ['en', 'de'])
|
|
OrderPosition.objects.create(
|
|
order=o,
|
|
item=ticket,
|
|
variation=None,
|
|
price=Decimal("14"),
|
|
attendee_name_parts={'full_name': "Peter", "_scheme": "full"}
|
|
)
|
|
OrderPosition.objects.create(
|
|
order=o,
|
|
item=ticket,
|
|
variation=None,
|
|
price=Decimal("14"),
|
|
canceled=True,
|
|
attendee_name_parts={'full_name': "Lukas Gelöscht", "_scheme": "full"}
|
|
)
|
|
return event, user, o, ticket
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_list(client, env):
|
|
with scopes_disabled():
|
|
otherticket = Item.objects.create(event=env[0], name='Early-bird ticket',
|
|
category=None, default_price=23,
|
|
admission=True)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/')
|
|
assert 'FOO' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?query=peter')
|
|
assert 'FOO' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?query=hans')
|
|
assert 'FOO' not in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?query=dummy')
|
|
assert 'FOO' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=p')
|
|
assert 'FOO' not in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=n')
|
|
assert 'FOO' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=ne')
|
|
assert 'FOO' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?item=%s' % otherticket.id)
|
|
assert 'FOO' not in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?item=%s' % env[3].id)
|
|
assert 'FOO' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?provider=free')
|
|
assert 'FOO' not in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?provider=banktransfer')
|
|
assert 'FOO' in response.content.decode()
|
|
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=o')
|
|
assert 'FOO' not in response.content.decode()
|
|
env[2].expires = now() - timedelta(days=10)
|
|
env[2].save()
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=o')
|
|
assert 'FOO' in response.content.decode()
|
|
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=pa')
|
|
assert 'FOO' not in response.content.decode()
|
|
env[2].require_approval = True
|
|
env[2].save()
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=pa')
|
|
assert 'FOO' in response.content.decode()
|
|
|
|
with scopes_disabled():
|
|
q = Question.objects.create(event=env[0], question="Q", type="N", required=True)
|
|
q.items.add(env[3])
|
|
op = env[2].positions.first()
|
|
qa = QuestionAnswer.objects.create(question=q, orderposition=op, answer="12")
|
|
response = client.get('/control/event/dummy/dummy/orders/?question=%d&answer=12' % q.pk)
|
|
assert 'FOO' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?question=%d&answer=13' % q.pk)
|
|
assert 'FOO' not in response.content.decode()
|
|
|
|
q.type = "C"
|
|
q.save()
|
|
with scopes_disabled():
|
|
qo1 = q.options.create(answer="Foo")
|
|
qo2 = q.options.create(answer="Bar")
|
|
qa.options.add(qo1)
|
|
response = client.get('/control/event/dummy/dummy/orders/?question=%d&answer=%d' % (q.pk, qo1.pk))
|
|
assert 'FOO' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/?question=%d&answer=%d' % (q.pk, qo2.pk))
|
|
assert 'FOO' not in response.content.decode()
|
|
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=testmode')
|
|
assert 'FOO' not in response.content.decode()
|
|
assert 'TEST MODE' not in response.content.decode()
|
|
env[2].testmode = True
|
|
env[2].save()
|
|
response = client.get('/control/event/dummy/dummy/orders/?status=testmode')
|
|
assert 'FOO' in response.content.decode()
|
|
assert 'TEST MODE' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_detail(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/FOO/')
|
|
assert 'Early-bird' in response.content.decode()
|
|
assert 'Peter' in response.content.decode()
|
|
assert 'Lukas Gelöscht' in response.content.decode()
|
|
assert 'TEST MODE' not in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_detail_show_test_mode(client, env):
|
|
env[2].testmode = True
|
|
env[2].save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/FOO/')
|
|
assert 'TEST MODE' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_set_contact(client, env):
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/contact', {
|
|
'email': 'admin@rami.io'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.email == 'admin@rami.io'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_set_customer(client, env):
|
|
with scopes_disabled():
|
|
org = env[0].organizer
|
|
c = org.customers.create(email='foo@example.org')
|
|
org.settings.customer_accounts = True
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/contact', {
|
|
'email': 'admin@rami.io',
|
|
'customer': c.pk
|
|
}, follow=True)
|
|
env[2].refresh_from_db()
|
|
assert env[2].email == 'admin@rami.io'
|
|
assert env[2].customer == c
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_set_locale(client, env):
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/locale', {
|
|
'locale': 'de'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.locale == 'de'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_set_locale_with_invalid_locale_value(client, env):
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/locale', {
|
|
'locale': 'fr'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.locale == 'en'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_set_comment(client, env):
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/comment', {
|
|
'comment': 'Foo'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.comment == 'Foo'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_transition_to_expired_success(client, env):
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'e'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_transition_to_paid_in_time_success(client, env):
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'amount': str(env[2].pending_sum),
|
|
'payment_date': now().date().isoformat(),
|
|
'status': 'p'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_transition_to_paid_expired_quota_left(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=10)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
res = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'p',
|
|
'payment_date': now().date().isoformat(),
|
|
'amount': str(o.pending_sum),
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert res.status_code < 400
|
|
assert o.status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_approve(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_PENDING
|
|
o.require_approval = True
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=10)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
res = client.post('/control/event/dummy/dummy/orders/FOO/approve', {
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert res.status_code < 400
|
|
assert o.status == Order.STATUS_PENDING
|
|
assert not o.require_approval
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_deny(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_PENDING
|
|
o.require_approval = True
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=10)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
res = client.post('/control/event/dummy/dummy/orders/FOO/deny', {
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert res.status_code < 400
|
|
assert o.status == Order.STATUS_CANCELED
|
|
assert o.require_approval
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_delete_require_testmode(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
res = client.get('/control/event/dummy/dummy/orders/FOO/delete', {}, follow=True)
|
|
assert 'alert-danger' in res.content.decode()
|
|
assert 'Only orders created in test mode can be deleted' in res.content.decode()
|
|
client.post('/control/event/dummy/dummy/orders/FOO/delete', {}, follow=True)
|
|
with scopes_disabled():
|
|
assert Order.objects.get(id=env[2].id)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_delete(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.testmode = True
|
|
o.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/delete', {}, follow=True)
|
|
with scopes_disabled():
|
|
assert not Order.objects.filter(id=env[2].id).exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
@pytest.mark.parametrize("process", [
|
|
# (Old status, new status, success expected)
|
|
(Order.STATUS_CANCELED, Order.STATUS_PAID, False),
|
|
(Order.STATUS_CANCELED, Order.STATUS_PENDING, False),
|
|
(Order.STATUS_CANCELED, Order.STATUS_EXPIRED, False),
|
|
|
|
(Order.STATUS_PAID, Order.STATUS_PENDING, False),
|
|
(Order.STATUS_PAID, Order.STATUS_CANCELED, True),
|
|
(Order.STATUS_PAID, Order.STATUS_EXPIRED, False),
|
|
|
|
(Order.STATUS_PENDING, Order.STATUS_CANCELED, True),
|
|
(Order.STATUS_PENDING, Order.STATUS_PAID, True),
|
|
(Order.STATUS_PENDING, Order.STATUS_EXPIRED, True),
|
|
])
|
|
def test_order_transition(client, env, process):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = process[0]
|
|
o.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=' + process[1])
|
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'amount': str(o.pending_sum),
|
|
'payment_date': now().date().isoformat(),
|
|
'status': process[1]
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
if process[2]:
|
|
assert o.status == process[1]
|
|
else:
|
|
assert o.status == process[0]
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_cancel_free(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_PAID
|
|
o.total = Decimal('0.00')
|
|
o.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'c'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_CANCELED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_cancel_paid_keep_fee(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.payments.create(state=OrderPayment.PAYMENT_STATE_CONFIRMED, amount=o.total)
|
|
o.status = Order.STATUS_PAID
|
|
o.save()
|
|
tr7 = o.event.tax_rules.create(rate=Decimal('7.00'))
|
|
o.event.settings.tax_rate_default = tr7
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'c',
|
|
'cancellation_fee': '6.00'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert not o.positions.exists()
|
|
assert o.all_positions.exists()
|
|
f = o.fees.get()
|
|
assert f.fee_type == OrderFee.FEE_TYPE_CANCELLATION
|
|
assert f.value == Decimal('6.00')
|
|
assert f.tax_value == Decimal('0.39')
|
|
assert f.tax_rate == Decimal('7')
|
|
assert f.tax_rule == tr7
|
|
assert o.status == Order.STATUS_PAID
|
|
assert o.total == Decimal('6.00')
|
|
assert o.pending_sum == Decimal('-8.00')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_cancel_pending_keep_fee(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.payments.create(state=OrderPayment.PAYMENT_STATE_CONFIRMED, amount=Decimal('8.00'))
|
|
o.status = Order.STATUS_PENDING
|
|
o.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'c',
|
|
'cancellation_fee': '6.00'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert not o.positions.exists()
|
|
assert o.all_positions.exists()
|
|
f = o.fees.get()
|
|
assert f.fee_type == OrderFee.FEE_TYPE_CANCELLATION
|
|
assert f.value == Decimal('6.00')
|
|
assert o.status == Order.STATUS_PAID
|
|
assert o.total == Decimal('6.00')
|
|
assert o.pending_sum == Decimal('-2.00')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_cancel_pending_fee_too_high(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.payments.create(state=OrderPayment.PAYMENT_STATE_CONFIRMED, amount=Decimal('4.00'))
|
|
o.status = Order.STATUS_PENDING
|
|
o.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'c',
|
|
'cancellation_fee': '6.00'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.positions.exists()
|
|
assert not o.fees.exists()
|
|
assert o.status == Order.STATUS_PENDING
|
|
assert o.total == Decimal('14.00')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_cancel_unpaid_no_fees_allowed(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
|
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'c',
|
|
'cancellation_fee': '6.00'
|
|
})
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.positions.exists()
|
|
assert not o.fees.exists()
|
|
assert o.status == Order.STATUS_CANCELED
|
|
assert o.total == Decimal('14.00')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_create_forbidden(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
env[0].settings.set('invoice_generate', 'no')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_create_duplicate(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
generate_invoice(env[2])
|
|
env[0].settings.set('invoice_generate', 'admin')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_create_ok(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
env[0].settings.set('invoice_generate', 'admin')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoice', {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
assert env[2].invoices.exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_regenerate(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
i = generate_invoice(env[2])
|
|
InvoiceAddress.objects.create(name_parts={'full_name': 'Foo', "_scheme": "full"}, order=env[2])
|
|
env[0].settings.set('invoice_generate', 'admin')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % i.pk, {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
i.refresh_from_db()
|
|
assert 'Foo' in i.invoice_to
|
|
with scopes_disabled():
|
|
assert env[2].invoices.exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_regenerate_canceled(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
i = generate_invoice(env[2])
|
|
generate_cancellation(i)
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % i.pk, {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_regenerate_unknown(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/regenerate' % 3, {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_reissue(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
i = generate_invoice(env[2])
|
|
InvoiceAddress.objects.create(name_parts={'full_name': 'Foo', "_scheme": "full"}, order=env[2])
|
|
env[0].settings.set('invoice_generate', 'admin')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % i.pk, {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
i.refresh_from_db()
|
|
with scopes_disabled():
|
|
assert env[2].invoices.count() == 3
|
|
assert 'Foo' not in env[2].invoices.all()[0].invoice_to
|
|
assert 'Foo' not in env[2].invoices.all()[1].invoice_to
|
|
assert 'Foo' in env[2].invoices.all()[2].invoice_to
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_reissue_canceled(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
i = generate_invoice(env[2])
|
|
generate_cancellation(i)
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % i.pk, {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_invoice_reissue_unknown(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/invoices/%d/reissue' % 3, {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_resend_link(client, env):
|
|
mail.outbox = []
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/resend', {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
assert 'FOO' in mail.outbox[0].body
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_reactivate_not_canceled(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_PAID
|
|
o.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/FOO/reactivate', follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/reactivate', follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_reactivate(client, env):
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=3)
|
|
q.items.add(env[3])
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_CANCELED
|
|
o.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/reactivate', {
|
|
}, follow=True)
|
|
print(response.content.decode())
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_not_pending(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_PAID
|
|
o.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/FOO/extend', follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_not_expired(client, env):
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
o = Order.objects.get(id=env[2].id)
|
|
generate_invoice(o)
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
assert o.invoices.count() == 1
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_overdue_quota_empty(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_overdue_quota_blocked_by_waiting_list(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.expires = now() - timedelta(days=5)
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=1)
|
|
q.items.add(env[3])
|
|
env[0].waitinglistentries.create(item=env[3], email='foo@bar.com')
|
|
generate_cancellation(generate_invoice(o))
|
|
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
assert o.status == Order.STATUS_PENDING
|
|
assert o.invoices.count() == 3
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_quota_left(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.save()
|
|
generate_cancellation(generate_invoice(o))
|
|
q = Quota.objects.create(event=env[0], size=3)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
assert o.invoices.count() == 2
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert b'alert-success' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
assert o.status == Order.STATUS_PENDING
|
|
assert o.invoices.count() == 3
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_quota_empty(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
olddate = o.expires
|
|
o.save()
|
|
with scopes_disabled():
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert b'alert-danger' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == olddate.strftime("%Y-%m-%d %H:%M:%S")
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_quota_empty_ignore(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate,
|
|
'quota_ignore': 'on'
|
|
}, follow=True)
|
|
assert b'alert-success' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_seat_free(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.save()
|
|
generate_cancellation(generate_invoice(o))
|
|
seat_a1 = env[0].seats.create(seat_number="A1", product=env[3], seat_guid="A1")
|
|
p = o.positions.first()
|
|
p.seat = seat_a1
|
|
p.save()
|
|
q = Quota.objects.create(event=env[0], size=3)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
assert o.invoices.count() == 2
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert b'alert-success' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59"
|
|
assert o.status == Order.STATUS_PENDING
|
|
assert o.invoices.count() == 3
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_seat_blocked(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
olddate = o.expires
|
|
o.save()
|
|
seat_a1 = env[0].seats.create(seat_number="A1", product=env[3], seat_guid="A1", blocked=True)
|
|
p = o.positions.first()
|
|
p.seat = seat_a1
|
|
p.save()
|
|
|
|
q = Quota.objects.create(event=env[0], size=100)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert b'alert-danger' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == olddate.strftime("%Y-%m-%d %H:%M:%S")
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_seat_taken(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
olddate = o.expires
|
|
o.save()
|
|
seat_a1 = env[0].seats.create(seat_number="A1", product=env[3], seat_guid="A1")
|
|
p = o.positions.first()
|
|
p.seat = seat_a1
|
|
p.save()
|
|
|
|
o = Order.objects.create(
|
|
code='BAR', event=env[0], email='dummy@dummy.test',
|
|
status=Order.STATUS_PENDING,
|
|
datetime=now(), expires=now() + timedelta(days=10),
|
|
total=14, locale='en'
|
|
)
|
|
OrderPosition.objects.create(
|
|
order=o,
|
|
item=env[3],
|
|
variation=None,
|
|
price=Decimal("14"),
|
|
attendee_name_parts={'full_name': "Peter", "_scheme": "full"},
|
|
seat=seat_a1
|
|
)
|
|
|
|
q = Quota.objects.create(event=env[0], size=100)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert b'alert-danger' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == olddate.strftime("%Y-%m-%d %H:%M:%S")
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_quota_partial(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
OrderPosition.objects.create(
|
|
order=o,
|
|
item=env[3],
|
|
variation=None,
|
|
price=Decimal("14"),
|
|
attendee_name_parts={'full_name': "Peter", "_scheme": "full"}
|
|
)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
olddate = o.expires
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=1)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert b'alert-danger' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == olddate.strftime("%Y-%m-%d %H:%M:%S")
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_voucher_budget_ok(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.save()
|
|
v = env[0].vouchers.create(
|
|
code="foo", price_mode='subtract', value=Decimal('1.50'), budget=Decimal('1.50')
|
|
)
|
|
p = o.positions.first()
|
|
p.voucher = v
|
|
p.price_before_voucher = p.price
|
|
p.price -= Decimal('1.50')
|
|
p.save()
|
|
|
|
q = Quota.objects.create(event=env[0], size=100)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert b'alert-success' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_PENDING
|
|
assert v.budget_used() == Decimal('1.50')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_extend_expired_voucher_budget_fail(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
olddate = o.expires
|
|
o.save()
|
|
v = env[0].vouchers.create(
|
|
code="foo", price_mode='subtract', value=Decimal('1.50'), budget=Decimal('0.00')
|
|
)
|
|
p = o.positions.first()
|
|
p.voucher = v
|
|
p.price_before_voucher = p.price
|
|
p.price -= Decimal('1.50')
|
|
p.save()
|
|
|
|
q = Quota.objects.create(event=env[0], size=100)
|
|
q.items.add(env[3])
|
|
newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d")
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/extend', {
|
|
'expires': newdate
|
|
}, follow=True)
|
|
assert b'alert-danger' in response.content
|
|
assert b'The voucher "FOO" no longer has sufficient budget.' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == olddate.strftime("%Y-%m-%d %H:%M:%S")
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
assert v.budget_used() == Decimal('0.00')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_overdue_quota_blocked_by_waiting_list(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.expires = now() - timedelta(days=5)
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=1)
|
|
q.items.add(env[3])
|
|
env[0].waitinglistentries.create(item=env[3], email='foo@bar.com')
|
|
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'p',
|
|
'payment_date': now().date().isoformat(),
|
|
'amount': str(o.pending_sum),
|
|
}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_blocked(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.expires = now() - timedelta(days=5)
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'amount': str(o.pending_sum),
|
|
'payment_date': now().date().isoformat(),
|
|
'status': 'p'
|
|
}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_overpaid_expired(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.expires = now() - timedelta(days=5)
|
|
o.save()
|
|
o.payments.create(state=OrderPayment.PAYMENT_STATE_CONFIRMED, amount=o.total * 2)
|
|
assert o.pending_sum == -1 * o.total
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'p',
|
|
'payment_date': now().date().isoformat(),
|
|
'amount': '0.00',
|
|
'force': 'on'
|
|
}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_PAID
|
|
assert o.payments.last().amount == 0
|
|
assert o.pending_sum == -1 * o.total
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_forced(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.status = Order.STATUS_EXPIRED
|
|
o.expires = now() - timedelta(days=5)
|
|
o.save()
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'p',
|
|
'payment_date': now().date().isoformat(),
|
|
'amount': str(o.pending_sum),
|
|
'force': 'on'
|
|
}, follow=True)
|
|
print(response.content.decode())
|
|
assert 'alert-success' in response.content.decode()
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_mark_paid_expired_seat_taken(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.expires = now() - timedelta(days=5)
|
|
o.status = Order.STATUS_EXPIRED
|
|
olddate = o.expires
|
|
o.save()
|
|
seat_a1 = env[0].seats.create(seat_number="A1", product=env[3], seat_guid="A1")
|
|
p = o.positions.first()
|
|
p.seat = seat_a1
|
|
p.save()
|
|
|
|
o = Order.objects.create(
|
|
code='BAR', event=env[0], email='dummy@dummy.test',
|
|
status=Order.STATUS_PENDING,
|
|
datetime=now(), expires=now() + timedelta(days=10),
|
|
total=14, locale='en'
|
|
)
|
|
OrderPosition.objects.create(
|
|
order=o,
|
|
item=env[3],
|
|
variation=None,
|
|
price=Decimal("14"),
|
|
attendee_name_parts={'full_name': "Peter", "_scheme": "full"},
|
|
seat=seat_a1
|
|
)
|
|
|
|
q = Quota.objects.create(event=env[0], size=100)
|
|
q.items.add(env[3])
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
|
'status': 'p',
|
|
'payment_date': now().date().isoformat(),
|
|
'amount': str(o.pending_sum),
|
|
'force': 'on'
|
|
}, follow=True)
|
|
assert b'alert-danger' in response.content
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == olddate.strftime("%Y-%m-%d %H:%M:%S")
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_go_lowercase(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/go?code=DuMmyfoO')
|
|
assert response['Location'].endswith('/control/event/dummy/dummy/orders/FOO/')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_go_with_slug(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/go?code=DUMMYFOO')
|
|
assert response['Location'].endswith('/control/event/dummy/dummy/orders/FOO/')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_go_found(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/go?code=FOO')
|
|
assert response['Location'].endswith('/control/event/dummy/dummy/orders/FOO/')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_go_not_found(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/go?code=BAR')
|
|
assert response['Location'].endswith('/control/event/dummy/dummy/orders/')
|
|
|
|
|
|
@pytest.fixture
|
|
def order_url(env):
|
|
event = env[0]
|
|
order = env[2]
|
|
url = '/control/event/{orga}/{event}/orders/{code}'.format(
|
|
event=event.slug, orga=event.organizer.slug, code=order.code
|
|
)
|
|
return url
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_sendmail_view(client, order_url):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
sendmail_url = order_url + '/sendmail'
|
|
response = client.get(sendmail_url)
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_sendmail_simple_case(client, order_url, env):
|
|
order = env[2]
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
sendmail_url = order_url + '/sendmail'
|
|
mail.outbox = []
|
|
response = client.post(
|
|
sendmail_url,
|
|
{
|
|
'sendto': order.email,
|
|
'subject': 'Test subject',
|
|
'message': 'This is a test file for sending mails.'
|
|
},
|
|
follow=True)
|
|
|
|
assert response.status_code == 200
|
|
assert 'alert-success' in response.content.decode()
|
|
|
|
assert len(mail.outbox) == 1
|
|
assert mail.outbox[0].to == [order.email]
|
|
assert mail.outbox[0].subject == 'Test subject'
|
|
assert 'This is a test file for sending mails.' in mail.outbox[0].body
|
|
|
|
mail_history_url = order_url + '/mail_history'
|
|
response = client.get(mail_history_url)
|
|
|
|
assert response.status_code == 200
|
|
assert 'Test subject' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_sendmail_preview(client, order_url, env):
|
|
order = env[2]
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
sendmail_url = order_url + '/sendmail'
|
|
mail.outbox = []
|
|
response = client.post(
|
|
sendmail_url,
|
|
{
|
|
'sendto': order.email,
|
|
'subject': 'Test subject',
|
|
'message': 'This is a test file for sending mails.',
|
|
'action': 'preview'
|
|
},
|
|
follow=True)
|
|
|
|
assert response.status_code == 200
|
|
assert 'E-mail preview' in response.content.decode()
|
|
assert len(mail.outbox) == 0
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_order_sendmail_invalid_data(client, order_url, env):
|
|
order = env[2]
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
sendmail_url = order_url + '/sendmail'
|
|
mail.outbox = []
|
|
response = client.post(
|
|
sendmail_url,
|
|
{
|
|
'sendto': order.email,
|
|
'subject': 'Test invalid mail',
|
|
},
|
|
follow=True)
|
|
|
|
assert 'has-error' in response.content.decode()
|
|
assert len(mail.outbox) == 0
|
|
|
|
mail_history_url = order_url + '/mail_history'
|
|
response = client.get(mail_history_url)
|
|
|
|
assert response.status_code == 200
|
|
assert 'Test invalid mail' not in response.content.decode()
|
|
|
|
|
|
class OrderChangeTests(SoupTest):
|
|
@scopes_disabled()
|
|
def setUp(self):
|
|
super().setUp()
|
|
o = Organizer.objects.create(name='Dummy', slug='dummy')
|
|
self.event = Event.objects.create(organizer=o, name='Dummy', slug='dummy', date_from=now(),
|
|
plugins='pretix.plugins.banktransfer')
|
|
self.order = Order.objects.create(
|
|
code='FOO', event=self.event, email='dummy@dummy.test',
|
|
status=Order.STATUS_PENDING,
|
|
datetime=now(), expires=now() + timedelta(days=10),
|
|
total=Decimal('46.00'),
|
|
)
|
|
self.tr7 = self.event.tax_rules.create(rate=Decimal('7.00'))
|
|
self.tr19 = self.event.tax_rules.create(rate=Decimal('19.00'))
|
|
self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket', tax_rule=self.tr7,
|
|
default_price=Decimal('23.00'), admission=True)
|
|
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', tax_rule=self.tr19,
|
|
default_price=Decimal('12.00'))
|
|
self.op1 = OrderPosition.objects.create(
|
|
order=self.order, item=self.ticket, variation=None,
|
|
price=Decimal("23.00"), attendee_name_parts={'full_name': "Peter", "_scheme": "full"}
|
|
)
|
|
self.op2 = OrderPosition.objects.create(
|
|
order=self.order, item=self.ticket, variation=None,
|
|
price=Decimal("23.00"), attendee_name_parts={'full_name': "Dieter", "_scheme": "full"}
|
|
)
|
|
self.op3 = OrderPosition.objects.create(
|
|
order=self.order, item=self.ticket, variation=None,
|
|
price=Decimal("23.00"), attendee_name_parts={'full_name': "Lukas", "_scheme": "full"},
|
|
canceled=True
|
|
)
|
|
self.quota = self.event.quotas.create(name="All", size=100)
|
|
self.quota.items.add(self.ticket)
|
|
self.quota.items.add(self.shirt)
|
|
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
|
|
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True)
|
|
t.members.add(user)
|
|
t.limit_events.add(self.event)
|
|
self.client.login(email='dummy@dummy.dummy', password='dummy')
|
|
|
|
def test_do_not_show_canceled(self):
|
|
r = self.client.get('/control/event/{}/{}/orders/{}/change'.format(
|
|
self.event.organizer.slug, self.event.slug, self.order.code
|
|
))
|
|
assert self.op1.secret[:5] in r.content.decode()
|
|
assert self.op2.secret[:5] in r.content.decode()
|
|
assert self.op3.secret[:5] not in r.content.decode()
|
|
|
|
def test_change_item_success(self):
|
|
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',
|
|
'op-{}-itemvar'.format(self.op1.pk): str(self.shirt.pk),
|
|
'op-{}-price'.format(self.op1.pk): str('12.00'),
|
|
})
|
|
self.op1.refresh_from_db()
|
|
self.order.refresh_from_db()
|
|
assert self.op1.item == self.shirt
|
|
assert self.op1.price == self.shirt.default_price
|
|
assert self.op1.tax_rate == self.shirt.tax_rule.rate
|
|
assert self.order.total == self.op1.price + self.op2.price
|
|
|
|
def test_change_subevent_success(self):
|
|
self.event.has_subevents = True
|
|
self.event.save()
|
|
with scopes_disabled():
|
|
se1 = self.event.subevents.create(name='Foo', date_from=now())
|
|
se2 = self.event.subevents.create(name='Bar', date_from=now())
|
|
self.op1.subevent = se1
|
|
self.op1.save()
|
|
self.op2.subevent = se1
|
|
self.op2.save()
|
|
self.quota.subevent = se1
|
|
self.quota.save()
|
|
q2 = self.event.quotas.create(name='Q2', size=100, subevent=se2)
|
|
q2.items.add(self.ticket)
|
|
q2.items.add(self.shirt)
|
|
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',
|
|
'op-{}-subevent'.format(self.op1.pk): str(se2.pk),
|
|
})
|
|
self.op1.refresh_from_db()
|
|
self.op2.refresh_from_db()
|
|
self.order.refresh_from_db()
|
|
assert self.op1.subevent == se2
|
|
assert self.op2.subevent == se1
|
|
|
|
def test_change_membership_success(self):
|
|
self.event.organizer.settings.customer_accounts = True
|
|
with scopes_disabled():
|
|
mtype = self.event.organizer.membership_types.create(name='Week pass', transferable=True, allow_parallel_usage=True)
|
|
self.ticket.require_membership = True
|
|
self.ticket.require_membership_types.add(mtype)
|
|
self.ticket.admission = True
|
|
self.ticket.save()
|
|
customer = self.event.organizer.customers.create(email='john@example.org', is_verified=True)
|
|
self.order.customer = customer
|
|
self.order.save()
|
|
m_correct1 = customer.memberships.create(
|
|
membership_type=mtype,
|
|
date_start=self.event.date_from - timedelta(days=1),
|
|
date_end=self.event.date_from + timedelta(days=1),
|
|
attendee_name_parts={'_scheme': 'full', 'full_name': 'John Doe'},
|
|
)
|
|
r = 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',
|
|
'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),
|
|
}, follow=True)
|
|
print(r.content)
|
|
self.op1.refresh_from_db()
|
|
self.order.refresh_from_db()
|
|
assert self.op1.used_membership == m_correct1
|
|
|
|
def test_change_price_success(self):
|
|
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',
|
|
'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',
|
|
'op-{}-operation'.format(self.op2.pk): '',
|
|
'op-{}-itemvar'.format(self.op2.pk): str(self.ticket.pk),
|
|
})
|
|
self.op1.refresh_from_db()
|
|
self.order.refresh_from_db()
|
|
assert self.op1.item == self.ticket
|
|
assert self.op1.price == Decimal('24.00')
|
|
assert self.order.total == self.op1.price + self.op2.price
|
|
|
|
def test_cancel_success(self):
|
|
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',
|
|
'op-{}-operation_cancel'.format(self.op1.pk): 'on',
|
|
})
|
|
self.order.refresh_from_db()
|
|
with scopes_disabled():
|
|
assert self.order.positions.count() == 1
|
|
assert self.order.total == self.op2.price
|
|
|
|
def test_add_item_success(self):
|
|
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',
|
|
})
|
|
with scopes_disabled():
|
|
assert self.order.positions.count() == 3
|
|
assert self.order.positions.last().item == self.shirt
|
|
assert self.order.positions.last().price == 14
|
|
|
|
def test_recalculate_reverse_charge(self):
|
|
self.tr7.eu_reverse_charge = True
|
|
self.tr7.home_country = Country('DE')
|
|
self.tr7.save()
|
|
self.tr19.eu_reverse_charge = True
|
|
self.tr19.home_country = Country('DE')
|
|
self.tr19.save()
|
|
with scopes_disabled():
|
|
InvoiceAddress.objects.create(
|
|
order=self.order, is_business=True, vat_id='ATU1234567', vat_id_validated=True,
|
|
country=Country('AT')
|
|
)
|
|
|
|
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',
|
|
'other-recalculate_taxes': 'net',
|
|
'op-{}-operation'.format(self.op1.pk): '',
|
|
'op-{}-operation'.format(self.op2.pk): '',
|
|
'op-{}-itemvar'.format(self.op2.pk): str(self.ticket.pk),
|
|
'op-{}-price'.format(self.op2.pk): str(self.op2.price),
|
|
'op-{}-itemvar'.format(self.op1.pk): str(self.ticket.pk),
|
|
'op-{}-price'.format(self.op1.pk): str(self.op1.price),
|
|
})
|
|
|
|
with scopes_disabled():
|
|
ops = list(self.order.positions.all())
|
|
for op in ops:
|
|
assert op.price == Decimal('21.50')
|
|
assert op.tax_value == Decimal('0.00')
|
|
assert op.tax_rate == Decimal('0.00')
|
|
|
|
def test_recalculate_reverse_charge_keep_gross(self):
|
|
self.tr7.eu_reverse_charge = True
|
|
self.tr7.home_country = Country('DE')
|
|
self.tr7.save()
|
|
self.tr19.eu_reverse_charge = True
|
|
self.tr19.home_country = Country('DE')
|
|
self.tr19.save()
|
|
with scopes_disabled():
|
|
InvoiceAddress.objects.create(
|
|
order=self.order, is_business=True, vat_id='ATU1234567', vat_id_validated=True,
|
|
country=Country('AT')
|
|
)
|
|
|
|
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',
|
|
'other-recalculate_taxes': 'gross',
|
|
'op-{}-operation'.format(self.op1.pk): '',
|
|
'op-{}-operation'.format(self.op2.pk): '',
|
|
'op-{}-itemvar'.format(self.op2.pk): str(self.ticket.pk),
|
|
'op-{}-price'.format(self.op2.pk): str(self.op2.price),
|
|
'op-{}-itemvar'.format(self.op1.pk): str(self.ticket.pk),
|
|
'op-{}-price'.format(self.op1.pk): str(self.op1.price),
|
|
})
|
|
|
|
with scopes_disabled():
|
|
ops = list(self.order.positions.all())
|
|
for op in ops:
|
|
assert op.price == Decimal('23.00')
|
|
assert op.tax_value == Decimal('0.00')
|
|
assert op.tax_rate == Decimal('0.00')
|
|
|
|
def test_change_fee_value_success(self):
|
|
with scopes_disabled():
|
|
fee = self.order.fees.create(fee_type="shipping", value=Decimal('5.00'), tax_rule=self.tr19)
|
|
self.order.total += Decimal('5.00')
|
|
self.order.save()
|
|
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',
|
|
'op-{}-price'.format(self.op1.pk): '24.00',
|
|
'op-{}-operation'.format(self.op2.pk): '',
|
|
'op-{}-itemvar'.format(self.op2.pk): str(self.ticket.pk),
|
|
'of-{}-value'.format(fee.pk): '3.50',
|
|
})
|
|
self.op1.refresh_from_db()
|
|
self.order.refresh_from_db()
|
|
assert self.op1.item == self.ticket
|
|
assert self.op1.price == Decimal('24.00')
|
|
fee.refresh_from_db()
|
|
self.op1.refresh_from_db()
|
|
self.op2.refresh_from_db()
|
|
assert self.order.total == self.op1.price + self.op2.price + Decimal('3.50')
|
|
assert fee.value == Decimal('3.50')
|
|
|
|
def test_cancel_fee_success(self):
|
|
with scopes_disabled():
|
|
fee = self.order.fees.create(fee_type="shipping", value=Decimal('5.00'), tax_rule=self.tr19)
|
|
self.order.total += Decimal('5.00')
|
|
self.order.save()
|
|
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',
|
|
'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',
|
|
'op-{}-operation'.format(self.op2.pk): '',
|
|
'op-{}-itemvar'.format(self.op2.pk): str(self.ticket.pk),
|
|
'of-{}-value'.format(fee.pk): '5.00',
|
|
'of-{}-operation_cancel'.format(fee.pk): 'on',
|
|
})
|
|
self.order.refresh_from_db()
|
|
fee.refresh_from_db()
|
|
assert fee.canceled
|
|
self.op1.refresh_from_db()
|
|
self.op2.refresh_from_db()
|
|
assert self.order.total == self.op1.price + self.op2.price
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_check_vatid(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='ATU1234567', country=Country('AT'))
|
|
with mock.patch('vat_moss.id.validate') as mock_validate:
|
|
mock_validate.return_value = ('AT', 'AT123456', 'Foo')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
ia.refresh_from_db()
|
|
assert ia.vat_id_validated
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_check_vatid_no_entered(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, country=Country('AT'))
|
|
with mock.patch('vat_moss.id.validate') as mock_validate:
|
|
mock_validate.return_value = ('AT', 'AT123456', 'Foo')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
ia.refresh_from_db()
|
|
assert not ia.vat_id_validated
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_check_vatid_invalid_country(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='ATU1234567', country=Country('FR'))
|
|
with mock.patch('vat_moss.id.validate') as mock_validate:
|
|
mock_validate.return_value = ('AT', 'AT123456', 'Foo')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
ia.refresh_from_db()
|
|
assert not ia.vat_id_validated
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_check_vatid_noneu_country(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='CHU1234567', country=Country('CH'))
|
|
with mock.patch('vat_moss.id.validate') as mock_validate:
|
|
mock_validate.return_value = ('AT', 'AT123456', 'Foo')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
ia.refresh_from_db()
|
|
assert not ia.vat_id_validated
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_check_vatid_no_country(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='ATU1234567')
|
|
with mock.patch('vat_moss.id.validate') as mock_validate:
|
|
mock_validate.return_value = ('AT', 'AT123456', 'Foo')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
ia.refresh_from_db()
|
|
assert not ia.vat_id_validated
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_check_vatid_no_invoiceaddress(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with mock.patch('vat_moss.id.validate') as mock_validate:
|
|
mock_validate.return_value = ('AT', 'AT123456', 'Foo')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_check_vatid_invalid(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='ATU1234567', country=Country('AT'))
|
|
with mock.patch('vat_moss.id.validate') as mock_validate:
|
|
def raiser(*args, **kwargs):
|
|
import vat_moss.errors
|
|
raise vat_moss.errors.InvalidError('Fail')
|
|
|
|
mock_validate.side_effect = raiser
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
ia.refresh_from_db()
|
|
assert not ia.vat_id_validated
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_check_vatid_unavailable(client, env):
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
with scopes_disabled():
|
|
ia = InvoiceAddress.objects.create(order=env[2], is_business=True, vat_id='ATU1234567', country=Country('AT'))
|
|
with mock.patch('vat_moss.id.validate') as mock_validate:
|
|
def raiser(*args, **kwargs):
|
|
import vat_moss.errors
|
|
raise vat_moss.errors.WebServiceUnavailableError('Fail')
|
|
|
|
mock_validate.side_effect = raiser
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/checkvatid', {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
ia.refresh_from_db()
|
|
assert not ia.vat_id_validated
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_cancel_payment(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/cancel'.format(p.pk), {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CANCELED
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/cancel'.format(p.pk), {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_cancel_refund(client, env):
|
|
with scopes_disabled():
|
|
r = env[2].refunds.create(
|
|
provider='stripe',
|
|
state='transit',
|
|
source='admin',
|
|
amount=Decimal('23.00'),
|
|
execution_date=now(),
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/cancel'.format(r.pk), {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
r.refresh_from_db()
|
|
assert r.state == OrderRefund.REFUND_STATE_CANCELED
|
|
r.state = OrderRefund.REFUND_STATE_DONE
|
|
r.save()
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/cancel'.format(r.pk), {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
r.refresh_from_db()
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_process_refund(client, env):
|
|
with scopes_disabled():
|
|
r = env[2].refunds.create(
|
|
provider='stripe',
|
|
state='external',
|
|
source='external',
|
|
amount=Decimal('23.00'),
|
|
execution_date=now(),
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
r.refresh_from_db()
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
env[2].refresh_from_db()
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_process_refund_overpaid_externally(client, env):
|
|
with scopes_disabled():
|
|
env[2].payments.first().confirm()
|
|
env[2].payments.create(
|
|
state='confirmed',
|
|
provider='stripe',
|
|
amount=Decimal('14.00'),
|
|
payment_date=now()
|
|
)
|
|
assert env[2].pending_sum == -14
|
|
r = env[2].refunds.create(
|
|
provider='stripe',
|
|
state='external',
|
|
source='external',
|
|
amount=Decimal('14.00'),
|
|
execution_date=now(),
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
r.refresh_from_db()
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
env[2].refresh_from_db()
|
|
assert env[2].status == Order.STATUS_PAID
|
|
assert env[2].pending_sum == 0
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_process_refund_invalid_state(client, env):
|
|
with scopes_disabled():
|
|
r = env[2].refunds.create(
|
|
provider='stripe',
|
|
state='canceled',
|
|
source='external',
|
|
amount=Decimal('23.00'),
|
|
execution_date=now(),
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
r.refresh_from_db()
|
|
assert r.state == OrderRefund.REFUND_STATE_CANCELED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_process_refund_mark_refunded(client, env):
|
|
with scopes_disabled():
|
|
r = env[2].refunds.create(
|
|
provider='stripe',
|
|
state='external',
|
|
source='external',
|
|
amount=Decimal('23.00'),
|
|
execution_date=now(),
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/process'.format(r.pk), {'action': 'r'},
|
|
follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
r.refresh_from_db()
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
env[2].refresh_from_db()
|
|
assert env[2].status == Order.STATUS_CANCELED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_done_refund(client, env):
|
|
with scopes_disabled():
|
|
r = env[2].refunds.create(
|
|
provider='stripe',
|
|
state='transit',
|
|
source='admin',
|
|
amount=Decimal('23.00'),
|
|
execution_date=now(),
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/done'.format(r.pk), {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
r.refresh_from_db()
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_done_refund_invalid_state(client, env):
|
|
with scopes_disabled():
|
|
r = env[2].refunds.create(
|
|
provider='stripe',
|
|
state='external',
|
|
source='external',
|
|
amount=Decimal('23.00'),
|
|
execution_date=now(),
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refunds/{}/done'.format(r.pk), {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
r.refresh_from_db()
|
|
assert r.state == OrderRefund.REFUND_STATE_EXTERNAL
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_confirm_payment(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
assert env[2].status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_confirm_payment_invalid_state(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.state = OrderPayment.PAYMENT_STATE_FAILED
|
|
p.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {}, follow=True)
|
|
assert 'alert-danger' in response.content.decode()
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_FAILED
|
|
env[2].refresh_from_db()
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_confirm_payment_partal_amount(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.amount -= Decimal(5.00)
|
|
p.save()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/payments/{}/confirm'.format(p.pk), {}, follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_fully_mark_as_refunded(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '14.00',
|
|
'start-mode': 'full',
|
|
'start-action': 'mark_refunded'
|
|
}, follow=True)
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '14.00',
|
|
'start-mode': 'full',
|
|
'start-action': 'mark_refunded',
|
|
'refund-manual': '14.00',
|
|
'manual_state': 'done',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
p.refresh_from_db()
|
|
with scopes_disabled():
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
r = env[2].refunds.last()
|
|
assert r.provider == "manual"
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
assert r.amount == Decimal('14.00')
|
|
assert env[2].status == Order.STATUS_CANCELED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_fully_mark_as_pending(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '14.00',
|
|
'start-mode': 'full',
|
|
'start-action': 'mark_pending',
|
|
'refund-manual': '14.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
with scopes_disabled():
|
|
r = env[2].refunds.last()
|
|
assert r.provider == "manual"
|
|
assert r.state == OrderRefund.REFUND_STATE_CREATED
|
|
assert r.amount == Decimal('14.00')
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_partially_mark_as_pending(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert doc.select("input[name$=partial_amount]")[0]["value"] == "14.00"
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending'
|
|
}, follow=True)
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-manual': '7.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
with scopes_disabled():
|
|
r = env[2].refunds.last()
|
|
assert r.provider == "manual"
|
|
assert r.state == OrderRefund.REFUND_STATE_CREATED
|
|
assert r.amount == Decimal('7.00')
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_propose_lower_payment(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.amount = Decimal('8.00')
|
|
p.confirm()
|
|
p2 = env[2].payments.create(
|
|
amount=Decimal('6.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending'
|
|
}, follow=True)
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert doc.select("input[name=refund-{}]".format(p2.pk))[0]['value'] == '6.00'
|
|
assert doc.select("input[name=refund-manual]".format(p2.pk))[0]['value'] == '1.00'
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_propose_equal_payment(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.amount = Decimal('7.00')
|
|
p.confirm()
|
|
p2 = env[2].payments.create(
|
|
amount=Decimal('7.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending'
|
|
}, follow=True)
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert doc.select("input[name=refund-{}]".format(p2.pk))[0]['value'] == '7.00'
|
|
assert not doc.select("input[name=refund-manual]".format(p2.pk))[0].get('value')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_propose_higher_payment(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.amount = Decimal('6.00')
|
|
p.confirm()
|
|
p2 = env[2].payments.create(
|
|
amount=Decimal('8.00'), provider='stripe', state=OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
client.get('/control/event/dummy/dummy/orders/FOO/refund')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending'
|
|
}, follow=True)
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert doc.select("input[name=refund-{}]".format(p2.pk))[0]['value'] == '7.00'
|
|
assert not doc.select("input[name=refund-manual]".format(p2.pk))[0].get('value')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_amount_does_not_match_or_invalid(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-manual': '4.00',
|
|
'refund-{}'.format(p.pk): '4.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
assert b'alert-danger' in resp.content
|
|
assert b'do not match the' in resp.content
|
|
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '15.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-manual': '0.00',
|
|
'refund-{}'.format(p.pk): '15.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
assert b'alert-danger' in resp.content
|
|
assert b'The refund amount needs to be positive' in resp.content
|
|
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-manual': '-3.00',
|
|
'refund-{}'.format(p.pk): '10.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
assert b'alert-danger' in resp.content
|
|
assert b'do not match the' in resp.content
|
|
resp = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-manual': 'AA',
|
|
'refund-{}'.format(p.pk): '10.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
assert b'alert-danger' in resp.content
|
|
assert b'invalid number' in resp.content
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_automatically_failed(client, env, monkeypatch):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.provider = 'stripe'
|
|
p.info_data = {
|
|
'id': 'foo'
|
|
}
|
|
p.save()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
|
|
def charge_retr(*args, **kwargs):
|
|
def refund_create(amount):
|
|
raise PaymentException('This failed.')
|
|
|
|
c = MockedCharge()
|
|
c.refunds.create = refund_create
|
|
return c
|
|
|
|
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
|
|
|
|
r = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-{}'.format(p.pk): '7.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
assert b'This failed.' in r.content
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
with scopes_disabled():
|
|
r = env[2].refunds.last()
|
|
assert r.provider == "stripe"
|
|
assert r.state == OrderRefund.REFUND_STATE_FAILED
|
|
assert r.amount == Decimal('7.00')
|
|
assert env[2].status == Order.STATUS_PAID
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_automatically(client, env, monkeypatch):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.provider = 'stripe'
|
|
p.info_data = {
|
|
'id': 'foo'
|
|
}
|
|
p.save()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
|
|
def charge_retr(*args, **kwargs):
|
|
def refund_create(amount):
|
|
r = MockedCharge()
|
|
r.id = 'foo'
|
|
r.status = 'succeeded'
|
|
return r
|
|
|
|
c = MockedCharge()
|
|
c.refunds.create = refund_create
|
|
return c
|
|
|
|
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
|
|
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '7.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-{}'.format(p.pk): '7.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
with scopes_disabled():
|
|
r = env[2].refunds.last()
|
|
assert r.provider == "stripe"
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
assert r.amount == Decimal('7.00')
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_offsetting_to_unknown(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
|
|
r = client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '5.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-offsetting': '5.00',
|
|
'order-offsetting': 'BAZ',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
assert b'alert-danger' in r.content
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_offsetting_to_expired(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
o = Order.objects.create(
|
|
code='BAZ', event=env[0], email='dummy@dummy.test',
|
|
status=Order.STATUS_EXPIRED,
|
|
datetime=now(), expires=now() + timedelta(days=10),
|
|
total=5, locale='en'
|
|
)
|
|
o.positions.create(price=5, item=env[3])
|
|
q = Quota.objects.create(event=env[0], size=0)
|
|
q.items.add(env[3])
|
|
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '5.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-offsetting': '5.00',
|
|
'order-offsetting': 'BAZ',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
with scopes_disabled():
|
|
r = env[2].refunds.last()
|
|
assert r.provider == "offsetting"
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
assert r.amount == Decimal('5.00')
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
o.refresh_from_db()
|
|
assert o.status == Order.STATUS_EXPIRED
|
|
p2 = o.payments.first()
|
|
assert p2.provider == "offsetting"
|
|
assert p2.amount == Decimal('5.00')
|
|
assert p2.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_offsetting(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
o = Order.objects.create(
|
|
code='BAZ', event=env[0], email='dummy@dummy.test',
|
|
status=Order.STATUS_PENDING,
|
|
datetime=now(), expires=now() + timedelta(days=10),
|
|
total=5, locale='en'
|
|
)
|
|
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '5.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-offsetting': '5.00',
|
|
'order-offsetting': 'BAZ',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
with scopes_disabled():
|
|
r = env[2].refunds.last()
|
|
assert r.provider == "offsetting"
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
assert r.amount == Decimal('5.00')
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
o.refresh_from_db()
|
|
assert o.status == Order.STATUS_PAID
|
|
p2 = o.payments.first()
|
|
assert p2.provider == "offsetting"
|
|
assert p2.amount == Decimal('5.00')
|
|
assert p2.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_paid_order_giftcard(client, env):
|
|
with scopes_disabled():
|
|
p = env[2].payments.last()
|
|
p.confirm()
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
|
|
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
|
|
'start-partial_amount': '5.00',
|
|
'start-mode': 'partial',
|
|
'start-action': 'mark_pending',
|
|
'refund-new-giftcard': '5.00',
|
|
'manual_state': 'pending',
|
|
'perform': 'on'
|
|
}, follow=True)
|
|
p.refresh_from_db()
|
|
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
|
|
env[2].refresh_from_db()
|
|
with scopes_disabled():
|
|
r = env[2].refunds.last()
|
|
assert r.provider == "giftcard"
|
|
assert r.state == OrderRefund.REFUND_STATE_DONE
|
|
assert r.amount == Decimal('5.00')
|
|
assert env[2].status == Order.STATUS_PENDING
|
|
gk = GiftCard.objects.get(pk=r.info_data['gift_card'])
|
|
assert gk.value == Decimal('5.00')
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_refund_list(client, env):
|
|
with scopes_disabled():
|
|
env[2].refunds.create(
|
|
provider='banktransfer',
|
|
state='done',
|
|
source='admin',
|
|
amount=Decimal('23.00'),
|
|
execution_date=now(),
|
|
)
|
|
env[2].refunds.create(
|
|
provider='manual',
|
|
state='created',
|
|
source='admin',
|
|
amount=Decimal('23.00'),
|
|
execution_date=now(),
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/refunds/')
|
|
assert 'R-1' not in response.content.decode()
|
|
assert 'R-2' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=all')
|
|
assert 'R-1' in response.content.decode()
|
|
assert 'R-2' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=created')
|
|
assert 'R-1' not in response.content.decode()
|
|
assert 'R-2' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=done')
|
|
assert 'R-1' in response.content.decode()
|
|
assert 'R-2' not in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=all&provider=manual')
|
|
assert 'R-1' not in response.content.decode()
|
|
assert 'R-2' in response.content.decode()
|
|
response = client.get('/control/event/dummy/dummy/orders/refunds/?status=all&provider=banktransfer')
|
|
assert 'R-1' in response.content.decode()
|
|
assert 'R-2' not in response.content.decode()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_delete_cancellation_request(client, env):
|
|
with scopes_disabled():
|
|
r = env[2].cancellation_requests.create(
|
|
cancellation_fee=Decimal('4.00'),
|
|
refund_as_giftcard=True
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/cancellationrequests/{}/delete'.format(r.pk), {},
|
|
follow=True)
|
|
assert 'alert-success' in response.content.decode()
|
|
assert not env[2].cancellation_requests.exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_approve_cancellation_request(client, env):
|
|
with scopes_disabled():
|
|
o = Order.objects.get(id=env[2].id)
|
|
o.payments.create(state=OrderPayment.PAYMENT_STATE_CONFIRMED, amount=o.total)
|
|
o.status = Order.STATUS_PAID
|
|
o.save()
|
|
r = env[2].cancellation_requests.create(
|
|
cancellation_fee=Decimal('4.00'),
|
|
refund_as_giftcard=True
|
|
)
|
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
|
response = client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c&req={}'.format(r.pk), {})
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert doc.select('input[name=cancellation_fee]')[0]['value'] == '4.00'
|
|
response = client.post('/control/event/dummy/dummy/orders/FOO/transition?req={}'.format(r.pk), {
|
|
'status': 'c',
|
|
'cancellation_fee': '4.00'
|
|
}, follow=True)
|
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
|
assert doc.select('input[name=refund-new-giftcard]')[0]['value'] == '10.00'
|
|
assert not env[2].cancellation_requests.exists()
|