Customer accounts & Memberships (#2024)

This commit is contained in:
Raphael Michel
2021-05-04 16:56:06 +02:00
committed by GitHub
parent 62e412bbc0
commit 8e79eb570e
116 changed files with 7975 additions and 279 deletions

View File

@@ -0,0 +1,235 @@
#
# 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/>.
#
from datetime import timedelta
from decimal import Decimal
import pytest
from bs4 import BeautifulSoup
from django.utils.timezone import now
from django_scopes import scopes_disabled
from tests.base import extract_form_fields
from pretix.base.models import (
Item, Order, OrderPosition, Organizer, Team, User,
)
@pytest.fixture
def organizer():
return Organizer.objects.create(name='Dummy', slug='dummy')
@pytest.fixture
def customer(organizer):
return organizer.customers.create(email="john@example.org")
@pytest.fixture
def membership_type(organizer):
return organizer.membership_types.create(name="Week pass")
@pytest.fixture
def event(organizer):
return organizer.events.create(
name='Conference', slug='conf',
date_from=now() + timedelta(days=10),
live=True, is_public=False
)
@pytest.fixture
def order(event, customer):
ticket = Item.objects.create(event=event, name='Early-bird ticket', default_price=23, admission=True)
o1 = Order.objects.create(
status=Order.STATUS_PENDING,
event=event,
customer=customer,
email='admin@localhost',
datetime=now() - timedelta(days=3),
expires=now() + timedelta(days=11),
total=Decimal("23"),
)
OrderPosition.objects.create(
order=o1,
item=ticket,
variation=None,
price=Decimal("23"),
attendee_name_parts={'full_name': "Peter"}
)
return o1
@pytest.fixture
def admin_user(organizer):
u = User.objects.create_user('dummy@dummy.dummy', 'dummy')
admin_team = Team.objects.create(
organizer=organizer, can_manage_customers=True, can_change_organizer_settings=True,
name='Admin team'
)
admin_team.members.add(u)
return u
@pytest.mark.django_db
def test_list_of_customers(organizer, admin_user, client, customer):
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.get('/control/organizer/dummy/customers')
assert customer.identifier in resp.content.decode()
resp = client.get('/control/organizer/dummy/customers?query=john@example.org')
assert customer.identifier in resp.content.decode()
resp = client.get('/control/organizer/dummy/customers?query=1234_FOO')
assert customer.identifier not in resp.content.decode()
@pytest.mark.django_db
def test_customer_detail_view(organizer, admin_user, customer, client, order):
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.get('/control/organizer/dummy/customer/{}/'.format(customer.identifier))
c = resp.content.decode()
assert customer.email in c
assert order.code in c
@pytest.mark.django_db
def test_customer_update(organizer, admin_user, customer, client):
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.get('/control/organizer/dummy/customer/{}/edit'.format(customer.identifier))
doc = BeautifulSoup(resp.content, "lxml")
d = extract_form_fields(doc)
d['name_parts_0'] = 'John Doe'
d['is_verified'] = 'on'
resp = client.post('/control/organizer/dummy/customer/{}/edit'.format(customer.identifier), d)
assert resp.status_code == 302
customer.refresh_from_db()
assert customer.name == 'John Doe'
assert customer.is_verified
@pytest.mark.django_db
def test_customer_anonymize(organizer, admin_user, customer, client, order):
customer.is_active = True
customer.name_parts = {'_legacy': 'Foo'}
customer.save()
client.login(email='dummy@dummy.dummy', password='dummy')
client.post('/control/organizer/dummy/customer/{}/anonymize'.format(customer.identifier))
customer.refresh_from_db()
order.refresh_from_db()
assert not customer.name_parts
assert not customer.name_cached
assert not customer.email
assert not customer.is_active
assert not customer.is_verified
assert not order.customer
@pytest.mark.django_db
def test_list_of_membership_types(organizer, admin_user, client, customer, membership_type):
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.get('/control/organizer/dummy/membershiptypes'.format(customer.identifier))
c = resp.content.decode()
assert 'Week pass' in c
@pytest.mark.django_db
def test_update_membership_type(organizer, admin_user, customer, client, membership_type):
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.get('/control/organizer/dummy/membershiptype/{}/edit'.format(membership_type.pk))
doc = BeautifulSoup(resp.content, "lxml")
d = extract_form_fields(doc)
d['transferable'] = 'on'
resp = client.post('/control/organizer/dummy/membershiptype/{}/edit'.format(membership_type.pk), d)
assert resp.status_code == 302
membership_type.refresh_from_db()
assert membership_type.transferable
@pytest.mark.django_db
def test_add_membership_type(organizer, admin_user, customer, client):
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.post('/control/organizer/dummy/membershiptype/add', {
'name_0': 'Year pass',
'max_usages': '3'
})
assert resp.status_code == 302
with scopes_disabled():
mt = organizer.membership_types.get()
assert str(mt.name) == 'Year pass'
assert mt.max_usages == 3
@pytest.mark.django_db
def test_delete_membership_type(organizer, admin_user, customer, client, membership_type):
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.post('/control/organizer/dummy/membershiptype/{}/delete'.format(membership_type.pk))
assert resp.status_code == 302
with scopes_disabled():
assert not organizer.membership_types.exists()
@pytest.mark.django_db
def test_delete_membership_type_forbidden(organizer, admin_user, customer, client, membership_type):
client.login(email='dummy@dummy.dummy', password='dummy')
with scopes_disabled():
customer.memberships.create(customer=customer, date_start=now(), date_end=now(), membership_type=membership_type)
resp = client.post('/control/organizer/dummy/membershiptype/{}/delete'.format(membership_type.pk))
assert resp.status_code == 302
with scopes_disabled():
assert organizer.membership_types.exists()
@pytest.mark.django_db
def test_customer_add_and_change_membership(organizer, admin_user, customer, client, membership_type):
client.login(email='dummy@dummy.dummy', password='dummy')
r = client.post('/control/organizer/dummy/customer/{}/membership/add'.format(customer.identifier), {
'membership_type': membership_type.pk,
'date_start_0': '2021-01-01',
'date_start_1': '00:00:00',
'date_end_0': '2021-01-08',
'date_end_1': '23:59:59',
'attendee_name_parts_0': 'John Doe',
})
assert r.status_code == 302
customer.refresh_from_db()
with scopes_disabled():
m = customer.memberships.get()
assert m.membership_type == membership_type
assert m.date_start.isoformat().startswith('2021-01-01')
assert m.date_end.isoformat().startswith('2021-01-08')
assert m.attendee_name == 'John Doe'
r = client.post('/control/organizer/dummy/customer/{}/membership/{}/edit'.format(customer.identifier, m.pk), {
'membership_type': membership_type.pk,
'date_start_0': '2021-01-02',
'date_start_1': '00:00:00',
'date_end_0': '2021-01-09',
'date_end_1': '23:59:59',
'attendee_name_parts_0': 'Maria Doe',
})
assert r.status_code == 302
customer.refresh_from_db()
with scopes_disabled():
m = customer.memberships.get()
assert m.membership_type == membership_type
assert m.date_start.isoformat().startswith('2021-01-02')
assert m.date_end.isoformat().startswith('2021-01-09')
assert m.attendee_name == 'Maria Doe'

View File

@@ -65,7 +65,7 @@ def env():
)
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)
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(
@@ -208,6 +208,22 @@ def test_order_set_contact(client, env):
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():
@@ -1332,6 +1348,39 @@ class OrderChangeTests(SoupTest):
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

View File

@@ -191,6 +191,8 @@ organizer_urls = [
'organizer/abc/webhook/add',
'organizer/abc/webhook/1/edit',
'organizer/abc/webhook/1/logs',
'organizer/abc/customers',
'organizer/abc/customer/1/',
'organizer/abc/giftcards',
'organizer/abc/giftcard/add',
'organizer/abc/giftcard/1/',
@@ -471,6 +473,16 @@ organizer_permission_urls = [
("can_change_organizer_settings", "organizer/dummy/property/add", 200),
("can_change_organizer_settings", "organizer/dummy/property/1/edit", 404),
("can_change_organizer_settings", "organizer/dummy/property/1/delete", 404),
("can_change_organizer_settings", "organizer/dummy/membershiptypes", 200),
("can_change_organizer_settings", "organizer/dummy/membershiptype/add", 200),
("can_change_organizer_settings", "organizer/dummy/membershiptype/1/edit", 404),
("can_change_organizer_settings", "organizer/dummy/membershiptype/1/delete", 404),
("can_manage_customers", "organizer/dummy/customers", 200),
("can_manage_customers", "organizer/dummy/customer/ABC/edit", 404),
("can_manage_customers", "organizer/dummy/customer/ABC/anonymize", 404),
("can_manage_customers", "organizer/dummy/customer/ABC/membership/add", 404),
("can_manage_customers", "organizer/dummy/customer/ABC/membership/1/edit", 404),
("can_manage_customers", "organizer/dummy/customer/ABC/", 404),
("can_manage_gift_cards", "organizer/dummy/giftcards", 200),
("can_manage_gift_cards", "organizer/dummy/giftcard/add", 200),
("can_manage_gift_cards", "organizer/dummy/giftcard/1/", 404),