mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-11-03 10:33:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			369 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import itertools
 | 
						|
import string
 | 
						|
from datetime import timedelta
 | 
						|
 | 
						|
import pytest
 | 
						|
from django.conf import settings
 | 
						|
from django.contrib.auth.base_user import make_password
 | 
						|
from django.test import Client, TestCase
 | 
						|
from django.urls import reverse
 | 
						|
from django.utils.timezone import now
 | 
						|
from model_bakery import baker
 | 
						|
 | 
						|
from club.models import Membership
 | 
						|
from core.baker_recipes import board_user, subscriber_user
 | 
						|
from core.models import User
 | 
						|
from counter.baker_recipes import product_recipe, refill_recipe, sale_recipe
 | 
						|
from counter.models import (
 | 
						|
    Counter,
 | 
						|
    Customer,
 | 
						|
    Refilling,
 | 
						|
    ReturnableProduct,
 | 
						|
    Selling,
 | 
						|
    StudentCard,
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
class TestStudentCard(TestCase):
 | 
						|
    """Tests for adding and deleting Stundent Cards
 | 
						|
    Test that an user can be found with it's student card.
 | 
						|
    """
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def setUpTestData(cls):
 | 
						|
        cls.customer = subscriber_user.make()
 | 
						|
        cls.barmen = subscriber_user.make(password=make_password("plop"))
 | 
						|
        cls.board_admin = board_user.make()
 | 
						|
        cls.club_admin = baker.make(User)
 | 
						|
        cls.root = baker.make(User, is_superuser=True)
 | 
						|
        cls.subscriber = subscriber_user.make()
 | 
						|
 | 
						|
        cls.counter = baker.make(Counter, type="BAR")
 | 
						|
        cls.counter.sellers.add(cls.barmen)
 | 
						|
 | 
						|
        cls.club_counter = baker.make(Counter)
 | 
						|
        baker.make(
 | 
						|
            Membership,
 | 
						|
            start_date=now() - timedelta(days=30),
 | 
						|
            club=cls.club_counter.club,
 | 
						|
            role=settings.SITH_CLUB_ROLES_ID["Board member"],
 | 
						|
            user=cls.club_admin,
 | 
						|
        )
 | 
						|
 | 
						|
        cls.valid_card = baker.make(
 | 
						|
            StudentCard, customer=cls.customer.customer, uid="8A89B82018B0A0"
 | 
						|
        )
 | 
						|
 | 
						|
    def login_in_counter(self):
 | 
						|
        self.client.post(
 | 
						|
            reverse("counter:login", args=[self.counter.id]),
 | 
						|
            {"username": self.barmen.username, "password": "plop"},
 | 
						|
        )
 | 
						|
 | 
						|
    def invalid_uids(self) -> list[tuple[str, str]]:
 | 
						|
        """Return a list of invalid uids, with the associated error message"""
 | 
						|
        return [
 | 
						|
            ("8B90734A802A8", ""),  # too short
 | 
						|
            (
 | 
						|
                "8B90734A802A8FA",
 | 
						|
                "Assurez-vous que cette valeur comporte au plus 14 caractères (actuellement 15).",
 | 
						|
            ),  # too long
 | 
						|
            ("8b90734a802a9f", ""),  # has lowercases
 | 
						|
            (" " * 14, "Ce champ est obligatoire."),  # empty
 | 
						|
            (
 | 
						|
                self.customer.customer.student_card.uid,
 | 
						|
                "Un objet Carte étudiante avec ce champ Uid existe déjà.",
 | 
						|
            ),
 | 
						|
        ]
 | 
						|
 | 
						|
    def test_search_user_with_student_card(self):
 | 
						|
        self.login_in_counter()
 | 
						|
        response = self.client.post(
 | 
						|
            reverse("counter:details", args=[self.counter.id]),
 | 
						|
            {"code": self.valid_card.uid},
 | 
						|
        )
 | 
						|
 | 
						|
        assert response.url == reverse(
 | 
						|
            "counter:click",
 | 
						|
            kwargs={"counter_id": self.counter.id, "user_id": self.customer.pk},
 | 
						|
        )
 | 
						|
 | 
						|
    def test_add_student_card_from_counter(self):
 | 
						|
        self.login_in_counter()
 | 
						|
        for uid in ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"]:
 | 
						|
            customer = subscriber_user.make().customer
 | 
						|
            response = self.client.post(
 | 
						|
                reverse(
 | 
						|
                    "counter:add_student_card", kwargs={"customer_id": customer.pk}
 | 
						|
                ),
 | 
						|
                {"uid": uid},
 | 
						|
                HTTP_REFERER=reverse(
 | 
						|
                    "counter:click",
 | 
						|
                    kwargs={"counter_id": self.counter.id, "user_id": customer.pk},
 | 
						|
                ),
 | 
						|
            )
 | 
						|
            assert response.status_code == 302
 | 
						|
            customer.refresh_from_db()
 | 
						|
            assert hasattr(customer, "student_card")
 | 
						|
            assert customer.student_card.uid == uid
 | 
						|
 | 
						|
    def test_add_student_card_from_counter_fail(self):
 | 
						|
        self.login_in_counter()
 | 
						|
        customer = subscriber_user.make().customer
 | 
						|
        for uid, error_msg in self.invalid_uids():
 | 
						|
            response = self.client.post(
 | 
						|
                reverse(
 | 
						|
                    "counter:add_student_card", kwargs={"customer_id": customer.pk}
 | 
						|
                ),
 | 
						|
                {"uid": uid},
 | 
						|
                HTTP_REFERER=reverse(
 | 
						|
                    "counter:click",
 | 
						|
                    kwargs={"counter_id": self.counter.id, "user_id": customer.pk},
 | 
						|
                ),
 | 
						|
            )
 | 
						|
            self.assertContains(response, text="Cet UID est invalide")
 | 
						|
            self.assertContains(response, text=error_msg)
 | 
						|
            customer.refresh_from_db()
 | 
						|
            assert not hasattr(customer, "student_card")
 | 
						|
 | 
						|
    def test_add_student_card_from_counter_unauthorized(self):
 | 
						|
        def send_valid_request(client, counter_id):
 | 
						|
            return client.post(
 | 
						|
                reverse(
 | 
						|
                    "counter:add_student_card", kwargs={"customer_id": self.customer.pk}
 | 
						|
                ),
 | 
						|
                {"uid": "8B90734A802A8F"},
 | 
						|
                HTTP_REFERER=reverse(
 | 
						|
                    "counter:click",
 | 
						|
                    kwargs={"counter_id": counter_id, "user_id": self.customer.pk},
 | 
						|
                ),
 | 
						|
            )
 | 
						|
 | 
						|
        # Send to a counter where you aren't logged in
 | 
						|
        assert send_valid_request(self.client, self.counter.id).status_code == 403
 | 
						|
 | 
						|
        self.login_in_counter()
 | 
						|
        barman = subscriber_user.make()
 | 
						|
        self.counter.sellers.add(barman)
 | 
						|
        # We want to test sending requests from another counter while
 | 
						|
        # we are currently registered to another counter
 | 
						|
        # so we connect to a counter and
 | 
						|
        # we create a new client, in order to check
 | 
						|
        # that using a client not logged to a counter
 | 
						|
        # where another client is logged still isn't authorized.
 | 
						|
        client = Client()
 | 
						|
        # Send to a counter where you aren't logged in
 | 
						|
        assert send_valid_request(client, self.counter.id).status_code == 403
 | 
						|
 | 
						|
        # Send to a non bar counter
 | 
						|
        client.force_login(self.club_admin)
 | 
						|
        assert send_valid_request(client, self.club_counter.id).status_code == 403
 | 
						|
 | 
						|
    def test_delete_student_card_with_owner(self):
 | 
						|
        self.client.force_login(self.customer)
 | 
						|
        self.client.post(
 | 
						|
            reverse(
 | 
						|
                "counter:delete_student_card",
 | 
						|
                kwargs={"customer_id": self.customer.customer.pk},
 | 
						|
            )
 | 
						|
        )
 | 
						|
        self.customer.customer.refresh_from_db()
 | 
						|
        assert not hasattr(self.customer.customer, "student_card")
 | 
						|
 | 
						|
    def test_delete_student_card_with_admin_user(self):
 | 
						|
        """Test that AE board members and root users can delete student cards"""
 | 
						|
        for user in self.board_admin, self.root:
 | 
						|
            self.client.force_login(user)
 | 
						|
            self.client.post(
 | 
						|
                reverse(
 | 
						|
                    "counter:delete_student_card",
 | 
						|
                    kwargs={"customer_id": self.customer.customer.pk},
 | 
						|
                )
 | 
						|
            )
 | 
						|
            self.customer.customer.refresh_from_db()
 | 
						|
            assert not hasattr(self.customer.customer, "student_card")
 | 
						|
 | 
						|
    def test_delete_student_card_from_counter(self):
 | 
						|
        self.login_in_counter()
 | 
						|
        self.client.post(
 | 
						|
            reverse(
 | 
						|
                "counter:delete_student_card",
 | 
						|
                kwargs={"customer_id": self.customer.customer.pk},
 | 
						|
            ),
 | 
						|
            http_referer=reverse(
 | 
						|
                "counter:click",
 | 
						|
                kwargs={
 | 
						|
                    "counter_id": self.counter.id,
 | 
						|
                    "user_id": self.customer.customer.pk,
 | 
						|
                },
 | 
						|
            ),
 | 
						|
        )
 | 
						|
        self.customer.customer.refresh_from_db()
 | 
						|
        assert not hasattr(self.customer.customer, "student_card")
 | 
						|
 | 
						|
    def test_delete_student_card_fail(self):
 | 
						|
        """Test that non-admin users cannot delete student cards"""
 | 
						|
        self.client.force_login(self.subscriber)
 | 
						|
        response = self.client.post(
 | 
						|
            reverse(
 | 
						|
                "counter:delete_student_card",
 | 
						|
                kwargs={"customer_id": self.customer.customer.pk},
 | 
						|
            )
 | 
						|
        )
 | 
						|
        assert response.status_code == 403
 | 
						|
        self.subscriber.customer.refresh_from_db()
 | 
						|
        assert not hasattr(self.subscriber.customer, "student_card")
 | 
						|
 | 
						|
    def test_add_student_card_from_user_preferences(self):
 | 
						|
        users = [self.customer, self.board_admin, self.root]
 | 
						|
        uids = ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"]
 | 
						|
        for user, uid in itertools.product(users, uids):
 | 
						|
            self.customer.customer.student_card.delete()
 | 
						|
            self.client.force_login(user)
 | 
						|
            response = self.client.post(
 | 
						|
                reverse(
 | 
						|
                    "counter:add_student_card",
 | 
						|
                    kwargs={"customer_id": self.customer.customer.pk},
 | 
						|
                ),
 | 
						|
                {"uid": uid},
 | 
						|
            )
 | 
						|
            assert response.status_code == 302
 | 
						|
            response = self.client.get(response.url)
 | 
						|
 | 
						|
            self.customer.customer.refresh_from_db()
 | 
						|
            assert self.customer.customer.student_card.uid == uid
 | 
						|
            self.assertContains(response, text="Carte enregistrée")
 | 
						|
 | 
						|
    def test_add_student_card_from_user_preferences_fail(self):
 | 
						|
        customer = subscriber_user.make()
 | 
						|
        self.client.force_login(customer)
 | 
						|
        for uid, error_msg in self.invalid_uids():
 | 
						|
            url = reverse(
 | 
						|
                "counter:add_student_card", kwargs={"customer_id": customer.customer.pk}
 | 
						|
            )
 | 
						|
            response = self.client.post(url, {"uid": uid})
 | 
						|
            self.assertContains(response, text="Cet UID est invalide")
 | 
						|
            self.assertContains(response, text=error_msg)
 | 
						|
            customer.refresh_from_db()
 | 
						|
            assert not hasattr(customer.customer, "student_card")
 | 
						|
 | 
						|
 | 
						|
class TestCustomerAccountId(TestCase):
 | 
						|
    @classmethod
 | 
						|
    def setUpTestData(cls):
 | 
						|
        cls.user_a = User.objects.create(
 | 
						|
            username="a", password="plop", email="a.a@a.fr"
 | 
						|
        )
 | 
						|
        user_b = User.objects.create(username="b", password="plop", email="b.b@b.fr")
 | 
						|
        user_c = User.objects.create(username="c", password="plop", email="c.c@c.fr")
 | 
						|
        Customer.objects.create(user=cls.user_a, amount=10, account_id="1111a")
 | 
						|
        Customer.objects.create(user=user_b, amount=0, account_id="9999z")
 | 
						|
        Customer.objects.create(user=user_c, amount=0, account_id="12345f")
 | 
						|
 | 
						|
    def test_create_customer(self):
 | 
						|
        user_d = User.objects.create(username="d", password="plop")
 | 
						|
        customer, created = Customer.get_or_create(user_d)
 | 
						|
        account_id = customer.account_id
 | 
						|
        number = account_id[:-1]
 | 
						|
        assert created is True
 | 
						|
        assert number == "12346"
 | 
						|
        assert len(account_id) == 6
 | 
						|
        assert account_id[-1] in string.ascii_lowercase
 | 
						|
        assert customer.amount == 0
 | 
						|
 | 
						|
    def test_get_existing_account(self):
 | 
						|
        account, created = Customer.get_or_create(self.user_a)
 | 
						|
        assert created is False
 | 
						|
        assert account.account_id == "1111a"
 | 
						|
        assert account.amount == 10
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.django_db
 | 
						|
def test_update_balance():
 | 
						|
    customers = baker.make(Customer, _quantity=5, _bulk_create=True)
 | 
						|
    refills = [
 | 
						|
        *refill_recipe.prepare(
 | 
						|
            customer=iter(customers),
 | 
						|
            amount=iter([30, 30, 40, 50, 50]),
 | 
						|
            _quantity=len(customers),
 | 
						|
            _save_related=True,
 | 
						|
        ),
 | 
						|
        refill_recipe.prepare(customer=customers[0], amount=30, _save_related=True),
 | 
						|
        refill_recipe.prepare(customer=customers[4], amount=10, _save_related=True),
 | 
						|
    ]
 | 
						|
    Refilling.objects.bulk_create(refills)
 | 
						|
    sales = [
 | 
						|
        *sale_recipe.prepare(
 | 
						|
            customer=iter(customers),
 | 
						|
            _quantity=len(customers),
 | 
						|
            unit_price=10,
 | 
						|
            quantity=1,
 | 
						|
            payment_method="SITH_ACCOUNT",
 | 
						|
            _save_related=True,
 | 
						|
        ),
 | 
						|
        *sale_recipe.prepare(
 | 
						|
            customer=iter(customers[:3]),
 | 
						|
            _quantity=3,
 | 
						|
            unit_price=5,
 | 
						|
            quantity=2,
 | 
						|
            payment_method="SITH_ACCOUNT",
 | 
						|
            _save_related=True,
 | 
						|
        ),
 | 
						|
        sale_recipe.prepare(
 | 
						|
            customer=customers[4],
 | 
						|
            quantity=1,
 | 
						|
            unit_price=50,
 | 
						|
            payment_method="SITH_ACCOUNT",
 | 
						|
            _save_related=True,
 | 
						|
        ),
 | 
						|
        *sale_recipe.prepare(
 | 
						|
            # all customers also bought products without using their AE account.
 | 
						|
            # All purchases made with another mean than the AE account should
 | 
						|
            # be ignored when updating the account balance.
 | 
						|
            customer=iter(customers),
 | 
						|
            _quantity=len(customers),
 | 
						|
            unit_price=50,
 | 
						|
            quantity=1,
 | 
						|
            payment_method="CARD",
 | 
						|
            _save_related=True,
 | 
						|
        ),
 | 
						|
    ]
 | 
						|
    Selling.objects.bulk_create(sales)
 | 
						|
    # customer 0 = 40, customer 1 = 10€, customer 2 = 20€,
 | 
						|
    # customer 3 = 40€, customer 4 = 0€
 | 
						|
    customers_qs = Customer.objects.filter(pk__in={c.pk for c in customers})
 | 
						|
    # put everything at zero to be sure the amounts were wrong beforehand
 | 
						|
    customers_qs.update(amount=0)
 | 
						|
    customers_qs.update_amount()
 | 
						|
    for customer, amount in zip(customers, [40, 10, 20, 40, 0], strict=False):
 | 
						|
        customer.refresh_from_db()
 | 
						|
        assert customer.amount == amount
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.django_db
 | 
						|
def test_update_returnable_balance():
 | 
						|
    ReturnableProduct.objects.all().delete()
 | 
						|
    customer = baker.make(Customer)
 | 
						|
    products = product_recipe.make(selling_price=0, _quantity=4, _bulk_create=True)
 | 
						|
    returnables = [
 | 
						|
        baker.make(
 | 
						|
            ReturnableProduct, product=products[0], returned_product=products[1]
 | 
						|
        ),
 | 
						|
        baker.make(
 | 
						|
            ReturnableProduct, product=products[2], returned_product=products[3]
 | 
						|
        ),
 | 
						|
    ]
 | 
						|
    balance_qs = ReturnableProduct.objects.annotate_balance_for(customer)
 | 
						|
    assert not customer.return_balances.exists()
 | 
						|
    assert list(balance_qs.values_list("balance", flat=True)) == [0, 0]
 | 
						|
 | 
						|
    sale_recipe.make(customer=customer, product=products[0], unit_price=0, quantity=5)
 | 
						|
    sale_recipe.make(customer=customer, product=products[2], unit_price=0, quantity=1)
 | 
						|
    sale_recipe.make(customer=customer, product=products[3], unit_price=0, quantity=3)
 | 
						|
    customer.update_returnable_balance()
 | 
						|
    assert list(customer.return_balances.values("returnable_id", "balance")) == [
 | 
						|
        {"returnable_id": returnables[0].id, "balance": 5},
 | 
						|
        {"returnable_id": returnables[1].id, "balance": -2},
 | 
						|
    ]
 | 
						|
    assert set(balance_qs.values_list("balance", flat=True)) == {-2, 5}
 |