Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes for release r41 #180

Merged
merged 10 commits into from
Aug 27, 2024
1 change: 1 addition & 0 deletions .github/workflows/respa-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
# the tests for that module which breaks. Also remove the location of source code.
- name: Install python packages
run: |
pip install setuptools wheel
pip install -r requirements.txt

# Not sure why some of the strings are not translated during test. Thus,
Expand Down
5 changes: 3 additions & 2 deletions docker/postgres/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
FROM postgres:10
FROM postgres:14

RUN apt-get update && apt-get install --no-install-recommends -y \
postgis postgresql-10-postgis-2.5 postgresql-10-postgis-2.5-scripts
postgis postgresql-14-postgis-3 postgresql-14-postgis-3-scripts \
&& apt-get clean

RUN localedef -i fi_FI -c -f UTF-8 -A /usr/share/locale/locale.alias fi_FI.UTF-8

Expand Down
24 changes: 24 additions & 0 deletions payments/migrations/0008_add_new_tax_percentage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 2.2.11 on 2024-08-17 13:04

from decimal import Decimal
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('payments', '0007_add_invoice_model'),
]

operations = [
migrations.AlterField(
model_name='orderline',
name='tax_percentage',
field=models.DecimalField(choices=[(Decimal('0.00'), '0.00'), (Decimal('10.00'), '10.00'), (Decimal('14.00'), '14.00'), (Decimal('24.00'), '24.00'), (Decimal('25.50'), '25.50')], decimal_places=2, default=Decimal('25.50'), max_digits=5, verbose_name='tax percentage'),
),
migrations.AlterField(
model_name='sapmaterialcode',
name='tax_percentage',
field=models.DecimalField(choices=[(Decimal('0.00'), '0.00'), (Decimal('10.00'), '10.00'), (Decimal('14.00'), '14.00'), (Decimal('24.00'), '24.00'), (Decimal('25.50'), '25.50')], decimal_places=2, default=Decimal('25.50'), max_digits=5, verbose_name='tax percentage'),
),
]
3 changes: 2 additions & 1 deletion payments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@
"10.00",
"14.00",
"24.00",
"25.50",
)
]

DEFAULT_TAX_PERCENTAGE = Decimal("24.00")
DEFAULT_TAX_PERCENTAGE = Decimal("25.50")
PRICE_PER_PERIOD = "per_period"
PRICE_FIXED = "fixed"
PRICE_TYPE_CHOICES = (
Expand Down
2 changes: 2 additions & 0 deletions payments/providers/cpu_ceepos.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def _get_order_line_description(order: Order) -> str:
def _get_ceepos_tax_code(order_line: OrderLine) -> str:
tax_pct = order_line.tax_percentage
ceepos_tax_codes = {
25_500_000: "255",
24_000_000: "24",
14_000_000: "14",
10_000_000: "10",
Expand All @@ -158,6 +159,7 @@ def _get_ceepos_tax_code(order_line: OrderLine) -> str:
# Tampere specific Ceepos tax codes in the production environment
if self.url_payment_api == "https://shop.tampere.fi/maksu.html":
ceepos_tax_codes = {
25_500_000: "35",
24_000_000: "15",
14_000_000: "14",
10_000_000: "13",
Expand Down
45 changes: 45 additions & 0 deletions payments/tests/test_cpu_ceepos.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import hmac
import json
from unittest import mock
from decimal import Decimal

import pytest
from django.http import HttpResponse
Expand Down Expand Up @@ -324,6 +325,50 @@ def test_payload_add_products_success(payment_provider, order_with_products):
assert "Description" in product


@pytest.mark.parametrize(
"tax_percentage,tax_code",
(
(Decimal('0'), "0"),
(Decimal('10.00'), "10"),
(Decimal('14.00'), "14"),
(Decimal('24.00'), "24"),
(Decimal('25.50'), "255"),
),
)
def test_tax_code_mapping_in_qa(payment_provider, order_with_products, tax_percentage, tax_code):
"""Test the tax percentage is mapped to a correct code in qa environment"""
payload = {}

order_with_products.order_lines.all().update(tax_percentage=tax_percentage)
payment_provider.payload_add_products(payload, order_with_products)

for product in payload["Products"]:
assert product["Taxcode"] == tax_code


@pytest.mark.parametrize(
"tax_percentage,tax_code",
(
(Decimal('0'), "18"),
(Decimal('10.00'), "13"),
(Decimal('14.00'), "14"),
(Decimal('24.00'), "15"),
(Decimal('25.50'), "35"),
),
)
def test_tax_code_mapping_in_production(provider_base_config, order_with_products, tax_percentage, tax_code):
"""Test the tax percentage is mapped to a correct code in production environment"""
provider_base_config["RESPA_PAYMENTS_CEEPOS_API_URL"] = "https://shop.tampere.fi/maksu.html"
payment_provider = CPUCeeposProvider(config=provider_base_config)
payload = {}

order_with_products.order_lines.all().update(tax_percentage=tax_percentage)
payment_provider.payload_add_products(payload, order_with_products)

for product in payload["Products"]:
assert product["Taxcode"] == tax_code


def test_payload_add_customer_success(payment_provider, order_with_products):
"""Test the customer data from order is added correctly into payload"""
payload = {}
Expand Down
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ django-ckeditor==5.9.0
django-cors-headers
django-image-cropping
easy_thumbnails
git+https://github.com/Tampere/django-tamusers.git@9eb5f84171b1a2f525f28de408171608dd122574#egg=django-tamusers
git+https://github.com/Tampere/django-tamusers.git@31b9251bcd9e769bd446528009377834cb68e760#egg=django-tamusers
django-allauth
djangorestframework-jwt
django-storages[google]
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile
# pip-compile requirements.in
#
--no-binary psycopg2

Expand Down Expand Up @@ -45,7 +45,7 @@ django-parler==1.9.2 # via -r requirements.in, django-munigeo, django-parle
django-reversion==3.0.2 # via -r requirements.in
django-sanitized-dump==0.2.2 # via -r requirements.in
django-storages[google]==1.12.3 # via -r requirements.in
git+https://github.com/Tampere/django-tamusers.git@9eb5f84171b1a2f525f28de408171608dd122574#egg=django-tamusers # via -r requirements.in
git+https://github.com/Tampere/django-tamusers.git@31b9251bcd9e769bd446528009377834cb68e760#egg=django-tamusers # via -r requirements.in
django==2.2.11 # via -r requirements.in, django-admin-json-editor, django-allauth, django-anymail, django-filter, django-jinja, django-mptt, django-munigeo, django-reversion, django-storages, django-tamusers, drf-oidc-auth, easy-thumbnails
djangorestframework-jwt==1.11.0 # via -r requirements.in
djangorestframework==3.9.1 # via -r requirements.in, django-parler-rest, drf-oidc-auth
Expand Down
24 changes: 24 additions & 0 deletions respa_pricing/migrations/0008_add_new_tax_percentage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 2.2.11 on 2024-08-17 15:15

from decimal import Decimal
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('respa_pricing', '0007_auto_20231218_1250'),
]

operations = [
migrations.AlterField(
model_name='eventtype',
name='tax_percentage',
field=models.DecimalField(choices=[(Decimal('0.00'), '0.00'), (Decimal('10.00'), '10.00'), (Decimal('14.00'), '14.00'), (Decimal('24.00'), '24.00'), (Decimal('25.50'), '25.50')], decimal_places=2, default=Decimal('25.50'), max_digits=5, verbose_name='tax percentage'),
),
migrations.AlterField(
model_name='usergroup',
name='tax_percentage',
field=models.DecimalField(choices=[(Decimal('0.00'), '0.00'), (Decimal('10.00'), '10.00'), (Decimal('14.00'), '14.00'), (Decimal('24.00'), '24.00'), (Decimal('25.50'), '25.50')], decimal_places=2, default=Decimal('25.50'), max_digits=5, verbose_name='tax percentage'),
),
]
3 changes: 2 additions & 1 deletion respa_pricing/models/price.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
"10.00",
"14.00",
"24.00",
"25.50",
)
]

DEFAULT_TAX_PERCENTAGE = Decimal("24.00")
DEFAULT_TAX_PERCENTAGE = Decimal("25.50")

PRICE_PER_PERIOD = "per_period"
PRICE_FIXED = "fixed"
Expand Down
49 changes: 48 additions & 1 deletion users/api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from allauth.socialaccount.models import EmailAddress
from django.contrib.auth import get_user_model
from rest_framework import permissions, serializers, generics, mixins, viewsets
from rest_framework import permissions, serializers, generics, viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response

from resources.models.utils import build_ical_feed_url
from resources.models import Unit
Expand Down Expand Up @@ -69,6 +72,50 @@ def get_object(self):
else:
obj = self.request.user
return obj

@action(detail=True, methods=["post"], url_path="set_email")
def set_email(self, request, pk=None):
user = self.get_object()
email = request.data.get("email")
requesting_user = request.user

if user != requesting_user:
return Response(
{"detail": "Not allowed to set email for this user."},
status=status.HTTP_403_FORBIDDEN
)

if not email:
return Response(
{"detail": "Email address is required."},
status=status.HTTP_400_BAD_REQUEST
)

if user.email:
return Response(
{"detail": "User already has an email set."},
status=status.HTTP_400_BAD_REQUEST
)

if EmailAddress.objects.filter(email=email):
return Response(
{"detail": "The email address is already in use."},
status=status.HTTP_400_BAD_REQUEST
)

# Update the existing EmailAddress
email_address = EmailAddress.objects.filter(user=user)
if email_address:
email_address.update(email=email)

# Update the User
user.email = email
user.save()

return Response(
{"detail": "Email set successfully."},
status=status.HTTP_200_OK
)

permission_classes = [permissions.IsAuthenticated]
queryset = get_user_model().objects.all()
Expand Down
3 changes: 0 additions & 3 deletions users/tests.py

This file was deleted.

Empty file added users/tests/__init__.py
Empty file.
76 changes: 76 additions & 0 deletions users/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from allauth.account.models import EmailAddress
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient, APITestCase

User = get_user_model()


class SetUserEmailTests(APITestCase):
def setUp(self):
# Create a test user
self.user = User.objects.create_user(
username="testuser", password="testpassword", email=""
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
EmailAddress.objects.create(user=self.user, email="")

# Create another user
self.other_user = User.objects.create_user(
username="otheruser", password="otherpassword", email="[email protected]"
)
EmailAddress.objects.create(user=self.other_user, email="[email protected]")

def test_set_email_success(self):
url = reverse("user-set-email", kwargs={"pk": self.user.pk})
data = {"email": "[email protected]"}

response = self.client.post(url, data, format="json")

self.user.refresh_from_db()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {"detail": "Email set successfully."})
self.assertEqual(self.user.email, "[email protected]")
self.assertTrue(
EmailAddress.objects.filter(
user=self.user, email="[email protected]"
).exists()
)

def test_set_email_already_set(self):
# Set an email for the user first
self.user.email = "[email protected]"
self.user.save()

url = reverse("user-set-email", kwargs={"pk": self.user.pk})
data = {"email": "[email protected]"}

response = self.client.post(url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data, {"detail": "User already has an email set."})
self.assertEqual(self.user.email, "[email protected]")

def test_set_email_not_provided(self):
url = reverse("user-set-email", kwargs={"pk": self.user.pk})
data = {"email": ""} # No email in the data

response = self.client.post(url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data, {"detail": "Email address is required."})
self.assertEqual(self.user.email, "")

def test_set_email_already_in_use(self):
url = reverse("user-set-email", kwargs={"pk": self.user.pk})
data = {"email": "[email protected]"} # Email already used by other_user

response = self.client.post(url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data, {"detail": "The email address is already in use."}
)
self.assertEqual(self.user.email, "")
Loading