...
 
Commits (5)
......@@ -47,7 +47,7 @@ codestyle:
- poetry run python manage.py makemigrations --no-input --check --dry-run
- poetry run python -Wall -mcoverage run manage.py test
- coverage report --fail-under=100 --omit registrations/urls.py registrations/**.py
- coverage report --fail-under=97.32 --omit payments/urls.py payments/**.py
- coverage report --fail-under=100 --omit payments/urls.py payments/**.py
- coverage report
python37-django22:
......
......@@ -31,6 +31,17 @@ dev = ["coverage", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"]
docs = ["sphinx"]
tests = ["coverage", "hypothesis", "pytest"]
[[package]]
category = "main"
description = "ASGI specs, helper code, and adapters"
name = "asgiref"
optional = false
python-versions = "*"
version = "3.2.3"
[package.extras]
tests = ["pytest (>=4.3.0,<4.4.0)", "pytest-asyncio (>=0.10.0,<0.11.0)"]
[[package]]
category = "dev"
description = "An abstract syntax tree for Python with inference support."
......@@ -227,12 +238,13 @@ category = "main"
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
name = "django"
optional = false
python-versions = ">=3.5"
version = "2.2.9"
python-versions = ">=3.6"
version = "3.0.2"
[package.dependencies]
asgiref = ">=3.2,<4.0"
pytz = "*"
sqlparse = "*"
sqlparse = ">=0.2.2"
[package.extras]
argon2 = ["argon2-cffi (>=16.1.0)"]
......@@ -305,10 +317,16 @@ description = "Country-specific Django helpers"
name = "django-localflavor"
optional = false
python-versions = "*"
version = "2.1"
version = "2.2"
[package.dependencies]
django = ">=1.11"
python-stdnum = ">=1.6"
[package.source]
reference = "d2ce849484640999a2fde7106407f7217a3c95c1"
type = "git"
url = "https://github.com/django/django-localflavor.git"
[[package]]
category = "main"
......@@ -848,6 +866,19 @@ optional = false
python-versions = "*"
version = "0.4.15"
[[package]]
category = "main"
description = "Python module to handle standardized numbers and codes"
name = "python-stdnum"
optional = false
python-versions = "*"
version = "1.12"
[package.extras]
soap = ["zeep"]
soap-alt = ["suds"]
soap-fallback = ["pysimplesoap"]
[[package]]
category = "main"
description = "World timezone definitions, modern and historical"
......@@ -1150,7 +1181,7 @@ version = "1.11.2"
docs = ["recommonmark", "sphinx"]
[metadata]
content-hash = "8b78e81a91448240cbd7ea5feffd2b459f748a921d9ad59c76bd66cf07c0554d"
content-hash = "b135864fc79b0bb431aeb699c7019c06b3f8dc0baa9786fe1b165cbece151915"
python-versions = "^3.7"
[metadata.files]
......@@ -1182,6 +1213,10 @@ argon2-cffi = [
{file = "argon2_cffi-19.2.0-cp38-cp38-win32.whl", hash = "sha256:72fae6bf37c25fdb9b4d30c2b9d658a72ac775249dd132ab3ac03adde619dc14"},
{file = "argon2_cffi-19.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:61d2b16c08ce5c24f91d2d2917c2300a90bb78672060876a335e1014727ced57"},
]
asgiref = [
{file = "asgiref-3.2.3-py2.py3-none-any.whl", hash = "sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5"},
{file = "asgiref-3.2.3.tar.gz", hash = "sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0"},
]
astroid = [
{file = "astroid-2.3.3-py3-none-any.whl", hash = "sha256:840947ebfa8b58f318d42301cf8c0a20fd794a33b61cc4638e28e9e61ba32f42"},
{file = "astroid-2.3.3.tar.gz", hash = "sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a"},
......@@ -1325,8 +1360,8 @@ coverage = [
{file = "coverage-4.5.4.tar.gz", hash = "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c"},
]
django = [
{file = "Django-2.2.9-py3-none-any.whl", hash = "sha256:687c37153486cf26c3fdcbdd177ef16de38dc3463f094b5f9c9955d91f277b14"},
{file = "Django-2.2.9.tar.gz", hash = "sha256:662a1ff78792e3fd77f16f71b1f31149489434de4b62a74895bd5d6534e635a5"},
{file = "Django-3.0.2-py3-none-any.whl", hash = "sha256:4f2c913303be4f874015993420bf0bd8fd2097a9c88e6b49c6a92f9bdd3fb13a"},
{file = "Django-3.0.2.tar.gz", hash = "sha256:8c3575f81e11390893860d97e1e0154c47512f180ea55bd84ce8fa69ba8051ca"},
]
django-appconf = [
{file = "django-appconf-1.0.3.tar.gz", hash = "sha256:35f13ca4d567f132b960e2cd4c832c2d03cb6543452d34e29b7ba10371ba80e3"},
......@@ -1347,10 +1382,7 @@ django-ical = [
django-libsass = [
{file = "django-libsass-0.7.tar.gz", hash = "sha256:49db3334b87e1f7955c4f9fb9945bc296f8bfd27a14d6d89706e4b0e5dc5de1c"},
]
django-localflavor = [
{file = "django-localflavor-2.1.tar.gz", hash = "sha256:0cee94c4b8f0214a5ba7be7e935019a8c062f4e7726d1df4b1e453cb812b2039"},
{file = "django_localflavor-2.1-py2.py3-none-any.whl", hash = "sha256:12ce98b13adcd68bb4babcd937d0ae5a0fd5801f71acaf9a6bf1784c218ef53c"},
]
django-localflavor = []
django-sendfile2 = [
{file = "django-sendfile2-0.4.3.tar.gz", hash = "sha256:267cdd817a5fe7e649df9139ac3efbe8675c61ccdab43146d1e8cbd9bab70554"},
]
......@@ -1743,6 +1775,10 @@ python-magic = [
{file = "python-magic-0.4.15.tar.gz", hash = "sha256:f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5"},
{file = "python_magic-0.4.15-py2.py3-none-any.whl", hash = "sha256:f2674dcfad52ae6c49d4803fa027809540b130db1dec928cfbb9240316831375"},
]
python-stdnum = [
{file = "python-stdnum-1.12.tar.gz", hash = "sha256:4c1347c414d7bdffb454924998f62c04d907a5c01faff0e35df659b0b52acba5"},
{file = "python_stdnum-1.12-py2.py3-none-any.whl", hash = "sha256:bb58877dafc2e590dbfddc63fa04876ab2005c3f35c8356a2dd01f62a9bdc4d6"},
]
pytz = [
{file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"},
{file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"},
......
......@@ -7,44 +7,43 @@ license = "AGPL-3.0-or-later"
[tool.poetry.dependencies]
python = "^3.7"
django-localflavor = "~2.1"
freezegun = "~0.3.11"
bleach = "~3.1"
django-tinymce4-lite = "~1.7"
rcssmin = "~1.0"
djangorestframework = "~3.11"
django-ical = "~1.5"
django-libsass = "~0.7.0"
python-magic = "~0.4.15"
Django = "~2.2"
Pillow = "~7.0"
django-compressor = "^2.4"
psycopg2-binary = "~2.8"
bcrypt = "~3.1"
argon2_cffi = "~19.2"
uWSGI = "~2.0"
django-bootstrap4 = "~1.1.0"
firebase-admin = "~3.2"
sentry-sdk = "~0.14.0"
django-sendfile2 = "~0.4.2"
django-localflavor = { git = "https://github.com/django/django-localflavor.git", rev="d2ce849484640999a2fde7106407f7217a3c95c1" }
freezegun = "0.3.12"
bleach = "3.1"
django-tinymce4-lite = "1.7.5"
djangorestframework = "3.11"
django-ical = "1.5"
django-libsass = "0.7.0"
python-magic = "0.4.15"
Django = "3.0.2"
Pillow = "7.0"
django-compressor = "2.4"
psycopg2-binary = "2.8.4"
bcrypt = "3.1.7"
argon2_cffi = "19.2"
uWSGI = "2.0.18"
django-bootstrap4 = "1.1.1"
firebase-admin = "3.2.1"
sentry-sdk = "0.14.0"
django-sendfile2 = "0.4.3"
# docs requirements
recommonmark = { version = "~0.6.0", optional = true }
sphinx = { version = "~2.3", optional = true }
google-api-python-client = "^1.7.11"
recommonmark = { version = "0.6.0", optional = true }
sphinx = { version = "2.3.1", optional = true }
google-api-python-client = "1.7.11"
[tool.poetry.extras]
docs = ["recommonmark", "sphinx"]
[tool.poetry.dev-dependencies]
django-template-check = "~0.3.1"
factory_boy = "~2.12"
pydenticon = "~0.3.1"
pylint = "~2.4"
pylint-django = "~2.0"
Faker = "~3.0"
coverage = "~4.5"
black = "~19.10b0"
django-template-check = "0.3.1"
factory_boy = "2.12"
pydenticon = "0.3.1"
pylint = "2.4.4"
pylint-django = "2.0.13"
Faker = "3.0"
coverage = "4.5.4"
black = "19.10b0"
[tool.black]
exclude = '(/(\.eggs|\.git|\.tox)/|migrations)'
......
......@@ -15,4 +15,4 @@ source = .
precision = 2
skip_covered = True
show_missing = True
fail_under = 60
fail_under = 65
......@@ -20,7 +20,6 @@ from django.utils.crypto import get_random_string
from django.utils.translation import pgettext_lazy, gettext_lazy as _
from payments.models import BankAccount
from thaliawebsite.settings import THALIA_PAY_ENABLED_PAYMENT_METHOD
from activemembers.models import MemberGroup, MemberGroupMembership
from utils import countries
......@@ -203,11 +202,11 @@ class Member(User):
def tpay_enabled(self):
"""Does this user have a bank account with Direct Debit enabled"""
bank_accounts = BankAccount.objects.filter(owner=self)
if THALIA_PAY_ENABLED_PAYMENT_METHOD and bank_accounts.exists():
if bank_accounts.last().valid:
return True
else:
return False
return (
settings.THALIA_PAY_ENABLED_PAYMENT_METHOD
and bank_accounts.exists()
and bank_accounts.last().valid
)
def _profile_image_path(_instance, _filename):
......
......@@ -30,7 +30,9 @@ def process_payment(
# the processing date
for payment in queryset:
if pay_type != Payment.TPAY or (
pay_type == Payment.TPAY and payment.paid_by.tpay_enabled
pay_type == Payment.TPAY
and payment.paid_by
and payment.paid_by.tpay_enabled
):
payment.type = pay_type
payment.processed_by = processed_by
......
......@@ -4,11 +4,11 @@ from freezegun import freeze_time
from members.models import Member
from payments import services
from payments.models import BankAccount
from payments.models import BankAccount, Payment
@freeze_time("2019-01-01")
@override_settings(SUSPEND_SIGNALS=True)
@override_settings(SUSPEND_SIGNALS=True, THALIA_PAY_ENABLED_PAYMENT_METHOD=True)
class ServicesTest(TestCase):
"""
Test for the services
......@@ -20,6 +20,40 @@ class ServicesTest(TestCase):
def setUpTestData(cls):
cls.member = Member.objects.filter(last_name="Wiggers").first()
def test_process_payment(self):
BankAccount.objects.create(
owner=self.member,
initials="J",
last_name="Test",
iban="NL91ABNA0417164300",
mandate_no="11-2",
valid_from=timezone.now().date() - timezone.timedelta(days=5),
last_used=timezone.now().date() - timezone.timedelta(days=5),
signature="base64,png",
)
p1 = Payment.objects.create(type=Payment.NONE, notes="Test payment", amount=1)
r1 = services.process_payment(
Payment.objects.filter(pk=p1.pk), self.member, Payment.CARD
)
self.assertEqual(r1, [p1])
p2 = Payment.objects.create(type=Payment.NONE, notes="Test payment", amount=2)
r2 = services.process_payment(
Payment.objects.filter(pk=p2.pk), self.member, Payment.TPAY
)
self.assertEqual(r2, [])
p3 = Payment.objects.create(
type=Payment.NONE, notes="Test payment", amount=3, paid_by=self.member
)
self.assertTrue(self.member.tpay_enabled)
r3 = services.process_payment(
Payment.objects.filter(pk=p3.pk), self.member, Payment.TPAY
)
self.assertEqual(r3, [p3])
def test_update_last_used(self):
BankAccount.objects.create(
owner=self.member,
......
from django.contrib.auth import get_user_model
from django.test import Client, TestCase, override_settings
from freezegun import freeze_time
from django.urls import reverse
from freezegun import freeze_time
from members.models import Member
from payments.models import BankAccount
from payments.views import BankAccountCreateView, BankAccountListView
from payments.models import BankAccount, Payment
@freeze_time("2019-01-01")
......@@ -34,7 +33,6 @@ class BankAccountCreateViewTest(TestCase):
)
def setUp(self):
self.view = BankAccountCreateView()
self.client = Client()
self.client.force_login(self.login_user)
......@@ -193,51 +191,6 @@ class BankAccountRevokeViewTest(TestCase):
fixtures = ["members.json"]
@classmethod
def setUpTestData(cls):
cls.login_user = Member.objects.filter(last_name="Wiggers").first()
cls.account = BankAccount.objects.create(
owner=cls.login_user,
initials="J",
last_name="Test",
iban="NL91ABNA0417164300",
)
BankAccount.objects.create(
owner=None,
initials="Someone",
last_name="Else",
iban="BE68539007547034",
bic="NBBEBEBB",
)
def setUp(self):
self.view = BankAccountCreateView()
self.client = Client()
self.client.force_login(self.login_user)
def test_not_logged_in(self):
"""
If there is no logged-in user they should redirect
to the authentication page
"""
self.client.logout()
response = self.client.get(reverse("payments:bankaccount-add"), follow=True)
self.assertEqual(200, response.status_code)
self.assertEqual(
[("/user/login/?next=" + reverse("payments:bankaccount-add"), 302)],
response.redirect_chain,
)
@override_settings(SUSPEND_SIGNALS=True)
class BankAccountListViewTest(TestCase):
"""
Test for the BankAccountListView
"""
fixtures = ["members.json"]
@classmethod
def setUpTestData(cls):
cls.login_user = Member.objects.filter(last_name="Wiggers").first()
......@@ -261,7 +214,6 @@ class BankAccountListViewTest(TestCase):
def setUp(self):
self.account1.refresh_from_db()
self.account2.refresh_from_db()
self.view = BankAccountListView()
self.client = Client()
self.client.force_login(self.login_user)
......@@ -348,3 +300,122 @@ class BankAccountListViewTest(TestCase):
.first()
.valid
)
@override_settings(SUSPEND_SIGNALS=True)
class BankAccountListViewTest(TestCase):
"""
Test for the BankAccountListView
"""
fixtures = ["members.json"]
@classmethod
def setUpTestData(cls):
cls.login_user = Member.objects.filter(last_name="Wiggers").first()
cls.account1 = BankAccount.objects.create(
owner=cls.login_user,
initials="J1",
last_name="Test",
iban="NL91ABNA0417164300",
)
cls.account2 = BankAccount.objects.create(
owner=Member.objects.exclude(last_name="Wiggers").first(),
initials="J2",
last_name="Test",
iban="BE68539007547034",
bic="NBBEBEBB",
valid_from="2019-03-01",
signature="sig",
mandate_no="11-2",
)
def setUp(self):
self.account1.refresh_from_db()
self.account2.refresh_from_db()
self.client = Client()
self.client.force_login(self.login_user)
def test_not_logged_in(self):
"""
If there is no logged-in user they should redirect
to the authentication page
"""
self.client.logout()
response = self.client.post(reverse("payments:bankaccount-list"), follow=True,)
self.assertEqual(200, response.status_code)
self.assertEqual(
[("/user/login/?next=" + reverse("payments:bankaccount-list"), 302,)],
response.redirect_chain,
)
def test_accounts(self):
"""
The page should show only accounts of the logged-in user
"""
response = self.client.get(reverse("payments:bankaccount-list"), follow=True,)
self.assertEqual(200, response.status_code)
self.assertContains(response, "NL91ABNA0417164300")
self.assertNotContains(response, "BE68539007547034")
@freeze_time("2019-04-01")
@override_settings(SUSPEND_SIGNALS=True)
class PaymentListViewTest(TestCase):
"""
Test for the PaymentListView
"""
fixtures = ["members.json"]
@classmethod
def setUpTestData(cls):
cls.login_user = Member.objects.filter(last_name="Wiggers").first()
cls.account1 = BankAccount.objects.create(
owner=cls.login_user,
initials="J1",
last_name="Test",
iban="NL91ABNA0417164300",
valid_from="2019-03-01",
signature="sig",
mandate_no="11-2",
)
cls.payment1 = Payment.objects.create(
paid_by=cls.login_user,
notes="Testing Payment 1",
amount=10,
type=Payment.CARD,
processing_date="2019-03-06",
)
def setUp(self):
self.account1.refresh_from_db()
self.payment1.refresh_from_db()
self.client = Client()
self.client.force_login(self.login_user)
def test_not_logged_in(self):
"""
If there is no logged-in user they should redirect
to the authentication page
"""
self.client.logout()
response = self.client.post(reverse("payments:payment-list"), follow=True,)
self.assertEqual(200, response.status_code)
self.assertEqual(
[("/user/login/?next=" + reverse("payments:payment-list"), 302,)],
response.redirect_chain,
)
def test_contents(self):
"""
Test if the view shows payments
"""
response = self.client.get(
reverse("payments:payment-list", kwargs={"year": 2019, "month": 3}),
follow=True,
)
self.assertEqual(200, response.status_code)
self.assertContains(response, "Testing Payment 1")