svthalia/concrexit

View on GitHub
website/sales/tests/test_models.py

Summary

Maintainability
F
6 days
Test Coverage
from unittest.mock import MagicMock

from django.core.exceptions import ValidationError
from django.test import TestCase
from django.utils import timezone

from freezegun import freeze_time

from activemembers.models import Committee, MemberGroupMembership
from members.models import Member
from payments.exceptions import PaymentError
from payments.models import Payment
from payments.services import create_payment
from sales.models.order import Order, OrderItem
from sales.models.product import Product, ProductList
from sales.models.shift import Shift
from sales.services import is_manager


class ProductTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.beer = Product.objects.create(name="beer", age_restricted=True)
        cls.wine = Product.objects.create(name="wine", age_restricted=True)
        cls.soda = Product.objects.create(name="soda", age_restricted=False)

    def test_str(self):
        self.assertEqual("beer", str(self.beer))
        self.assertEqual("wine", str(self.wine))
        self.assertEqual("soda", str(self.soda))


class ProductListTest(TestCase):
    fixtures = ["members.json", "products.json"]

    @classmethod
    def setUpTestData(cls):
        cls.member = Member.objects.filter(last_name="Wiggers").first()

        cls.beer = Product.objects.get(name="beer")
        cls.wine = Product.objects.get(name="wine")
        cls.soda = Product.objects.get(name="soda")

        cls.normal = ProductList.objects.get(
            name="normal",
        )
        cls.free = ProductList.objects.get(
            name="free",
        )

    def test_str(self):
        self.assertEqual("normal", str(self.normal))
        self.assertEqual("free", str(self.free))


@freeze_time("2021-01-01")
class OrderTest(TestCase):
    fixtures = ["members.json", "bank_accounts.json", "products.json"]

    @classmethod
    def setUpTestData(cls):
        cls.member = Member.objects.filter(last_name="Wiggers").first()

        cls.beer = Product.objects.get(name="beer")
        cls.wine = Product.objects.get(name="wine")
        cls.soda = Product.objects.get(name="soda")

        cls.normal = ProductList.objects.get(
            name="normal",
        )
        cls.free = ProductList.objects.get(
            name="free",
        )

        cls.shift = Shift.objects.create(
            start=timezone.now(),
            end=timezone.now() + timezone.timedelta(hours=1),
            product_list=cls.normal,
        )

    def test_age_restricted(self):
        order = Order.objects.create(shift=self.shift)
        self.assertFalse(order.age_restricted)
        i1 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.soda),
            amount=1,
        )
        self.assertFalse(order.age_restricted)
        i2 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=1,
        )
        self.assertTrue(order.age_restricted)
        i2.delete()
        self.assertFalse(order.age_restricted)

    def test_subtotal(self):
        order = Order.objects.create(shift=self.shift)
        self.assertEqual(order.subtotal, 0)
        i1 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.soda),
            amount=2,
        )
        self.assertEqual(order.subtotal, 0)
        i2 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        self.assertEqual(order.subtotal, 1)
        i3 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.wine),
            amount=3,
        )
        self.assertEqual(order.subtotal, 2.5)
        i2.delete()
        self.assertEqual(order.subtotal, 1.5)
        i3.total = 4
        i3.save()
        self.assertEqual(order.subtotal, 4)

    def test_total_amount(self):
        order = Order.objects.create(shift=self.shift)
        self.assertEqual(order.total_amount, 0)
        i1 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.soda),
            amount=2,
        )
        self.assertEqual(order.total_amount, 0)
        i2 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        self.assertEqual(order.total_amount, 1)
        i3 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.wine),
            amount=3,
        )
        self.assertEqual(order.total_amount, 2.5)
        i2.delete()
        self.assertEqual(order.total_amount, 1.5)
        i3.total = 4
        i3.save()
        self.assertEqual(order.total_amount, 4)
        order.discount = 2
        order.save()
        self.assertEqual(order.total_amount, 2)

    def test_num_items(self):
        order = Order.objects.create(shift=self.shift)
        self.assertEqual(order.num_items, 0)
        i1 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.soda),
            amount=2,
        )
        self.assertEqual(order.num_items, 2)
        i2 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        self.assertEqual(order.num_items, 4)
        i3 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.wine),
            amount=3,
        )
        self.assertEqual(order.num_items, 7)
        i2.delete()
        self.assertEqual(order.num_items, 5)

    def test_create_order_shift_locked(self):
        self.shift.locked = True
        self.shift.save()

        with self.assertRaises(ValueError):
            Order.objects.create(shift=self.shift)

    def test_create_order_shift_not_started(self):
        self.shift.start = self.shift.start + timezone.timedelta(days=2)
        self.shift.end = self.shift.end + timezone.timedelta(days=2)
        self.shift.save()

        with self.assertRaises(ValueError):
            Order.objects.create(shift=self.shift)

    def test_update_order_shift_locked(self):
        order = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )

        self.shift.locked = True
        self.shift.save()

        order.discount = 0.5

        with self.assertRaises(Order.DoesNotExist):
            order.refresh_from_db()

    def test_update_order_paid(self):
        order = Order.objects.create(shift=self.shift)
        i1 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        self.assertEqual(order.total_amount, 1)
        self.assertEqual(i1.total, 1)
        order.payment = create_payment(
            order, processed_by=self.member, pay_type=Payment.CASH
        )
        order.save()

        order.refresh_from_db()
        self.assertIsNotNone(order.payment)

        order.discount = 0.5

        with self.assertRaises(PaymentError):
            order.save()

        with self.assertRaises(ValueError):
            OrderItem.objects.create(
                order=order,
                product=self.shift.product_list.product_items.get(product=self.wine),
                amount=1,
            )

        i1.amount = 3
        with self.assertRaises(ValueError):
            i1.save()

        i1.refresh_from_db()
        self.assertEqual(i1.amount, 2)

    def test_discount_amount(self):
        order = Order.objects.create(shift=self.shift)
        i1 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        order.discount = 2
        with self.assertRaises(ValidationError):
            order.clean()

        order.discount = 1
        order.clean()
        order.save()
        self.assertEqual(order.total_amount, 0)

    def test_order_item_total(self):
        order = Order.objects.create(shift=self.shift)
        i1 = OrderItem.objects.create(
            order=order,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        i1.refresh_from_db()
        self.assertEqual(i1.total, 1)

        i1.total = None
        i1.save()
        i1.refresh_from_db()
        self.assertEqual(i1.total, 1)

        i1.total = 2
        i1.save()
        i1.refresh_from_db()
        self.assertEqual(i1.total, 2)

    def test_nonexistent_product(self):
        order = Order.objects.create(shift=self.shift)
        i1 = OrderItem.objects.create(
            order=order,
            product=self.free.product_items.get(product=self.beer),
            amount=2,
        )
        with self.assertRaises(ValidationError):
            i1.clean()


@freeze_time("2021-01-01")
class ShiftTest(TestCase):
    fixtures = [
        "members.json",
        "bank_accounts.json",
        "member_groups.json",
        "products.json",
    ]

    @classmethod
    def setUpTestData(cls):
        cls.member = Member.objects.filter(last_name="Wiggers").first()

        cls.beer = Product.objects.get(name="beer")
        cls.wine = Product.objects.get(name="wine")
        cls.soda = Product.objects.get(name="soda")

        cls.normal = ProductList.objects.get(
            name="normal",
        )
        cls.free = ProductList.objects.get(
            name="free",
        )

        cls.shift = Shift.objects.create(
            start=timezone.now(),
            end=timezone.now() + timezone.timedelta(hours=1),
            product_list=cls.normal,
        )

    def test_time(self):
        self.assertTrue(self.shift.active)
        self.shift.start = self.shift.end
        with self.assertRaises(ValidationError):
            self.shift.clean()

    def test_remove_orders_on_locked(self):
        order1 = Order.objects.create(shift=self.shift)
        order1.save()

        order2 = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=order2,
            product=self.shift.product_list.product_items.get(product=self.soda),
            amount=2,
        )
        order2.save()

        order3 = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=order3,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        order3.save()

        order4 = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=order4,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        order4.save()
        order4.payment = create_payment(
            order4, processed_by=self.member, pay_type=Payment.CASH
        )
        order4.save()

        self.shift.locked = True
        self.shift.save()

        with self.assertRaises(Order.DoesNotExist):
            order1.refresh_from_db()

        order2.refresh_from_db()

        with self.assertRaises(Order.DoesNotExist):
            order3.refresh_from_db()

        order4.refresh_from_db()

    def test_active(self):
        self.assertTrue(self.shift.active)
        self.shift.start = self.shift.start + timezone.timedelta(minutes=10)
        self.assertFalse(self.shift.active)

    def test_shift_statistics(self):
        self.assertEqual(self.shift.total_revenue, 0)
        self.assertEqual(self.shift.total_revenue_paid, 0)

        self.assertEqual(self.shift.num_orders, 0)
        self.assertEqual(self.shift.num_orders_paid, 0)

        self.assertDictEqual(self.shift.product_sales, {})

        o1 = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=o1,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        o2 = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=o2,
            product=self.shift.product_list.product_items.get(product=self.soda),
            amount=2,
        )
        o3 = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=o3,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        OrderItem.objects.create(
            order=o3,
            product=self.shift.product_list.product_items.get(product=self.wine),
            amount=2,
        )
        o4 = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=o4,
            product=self.shift.product_list.product_items.get(product=self.wine),
            amount=2,
        )
        o5 = Order.objects.create(shift=self.shift)
        OrderItem.objects.create(
            order=o5,
            product=self.shift.product_list.product_items.get(product=self.beer),
            amount=2,
        )
        OrderItem.objects.create(
            order=o5,
            product=self.shift.product_list.product_items.get(product=self.wine),
            amount=2,
        )
        o4.payment = create_payment(o4, processed_by=self.member, pay_type=Payment.CASH)
        o4.save()
        o5.payment = create_payment(o5, processed_by=self.member, pay_type=Payment.CASH)
        o5.save()

        # The test was accidentally passing because orders had the same created at,
        # resulting in the distinct seeing them as the same order
        o2.created_at += timezone.timedelta(minutes=1)
        o3.created_at += timezone.timedelta(minutes=2)
        o4.created_at += timezone.timedelta(minutes=3)
        o5.created_at += timezone.timedelta(minutes=4)
        o2.save()
        o3.save()
        o4.save()
        o5.save()

        self.assertEqual(self.shift.total_revenue, 6)
        self.assertEqual(self.shift.total_revenue_paid, 3)

        self.assertEqual(self.shift.num_orders, 5)
        self.assertEqual(self.shift.num_orders_paid, 3)

        self.assertDictEqual(
            self.shift.product_sales,
            {self.beer.name: 6, self.soda.name: 2, self.wine.name: 6},
        )

    def test_is_manager(self):
        # @todo Move this test to test_services
        self.member.is_superuser = False
        self.assertFalse(is_manager(self.member, self.shift))

        cie = Committee.objects.get(pk=1)
        MemberGroupMembership.objects.create(group=cie, member=self.member)
        self.shift.managers.add(cie)
        self.assertTrue(is_manager(self.member, self.shift))

        self.shift.managers.remove(cie)
        self.assertFalse(is_manager(self.member, self.shift))

        self.member.is_superuser = True
        self.assertTrue(is_manager(self.member, self.shift))
        self.member.is_superuser = False
        self.member.has_perm = MagicMock()
        self.member.has_perm.return_value = True
        self.assertTrue(is_manager(self.member, self.shift))