From a4c64399819e812ed0b9a43065fa2bd299ab148c Mon Sep 17 00:00:00 2001 From: Sli Date: Fri, 10 Jan 2025 16:35:42 +0100 Subject: [PATCH] Fix selling ordering bug that created "not enough money" errors * Add tests * Add tests for cons/dcons --- core/management/commands/populate.py | 2 + counter/tests/test_counter.py | 89 +++++++++++++++++++++++++++- counter/views/click.py | 6 +- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/core/management/commands/populate.py b/core/management/commands/populate.py index e3d6d8e4..918c7dd7 100644 --- a/core/management/commands/populate.py +++ b/core/management/commands/populate.py @@ -460,6 +460,7 @@ Welcome to the wiki page! limit_age=18, ) cons = Product.objects.create( + id=settings.SITH_ECOCUP_CONS, name="Consigne Eco-cup", code="CONS", product_type=verre, @@ -469,6 +470,7 @@ Welcome to the wiki page! club=main_club, ) dcons = Product.objects.create( + id=settings.SITH_ECOCUP_DECO, name="Déconsigne Eco-cup", code="DECO", product_type=verre, diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py index bd9ee56a..fb64759c 100644 --- a/counter/tests/test_counter.py +++ b/counter/tests/test_counter.py @@ -236,6 +236,10 @@ class TestCounterClick(TestFullClickBase): BanGroup.objects.get(pk=settings.SITH_GROUP_BANNED_COUNTER_ID) ) + cls.gift = product_recipe.make( + selling_price="-1.5", + special_selling_price="-1.5", + ) cls.beer = product_recipe.make( limit_age=18, selling_price="1.5", special_selling_price="1" ) @@ -253,7 +257,12 @@ class TestCounterClick(TestFullClickBase): limit_age=0, selling_price="1.5", special_selling_price="1" ) - cls.counter.products.add(cls.beer, cls.beer_tap, cls.snack) + cls.cons = Product.objects.get(id=settings.SITH_ECOCUP_CONS) + cls.dcons = Product.objects.get(id=settings.SITH_ECOCUP_DECO) + + cls.counter.products.add( + cls.gift, cls.beer, cls.beer_tap, cls.snack, cls.cons, cls.dcons + ) cls.other_counter.products.add(cls.snack) @@ -594,6 +603,84 @@ class TestCounterClick(TestFullClickBase): else: assert not counter.has_annotated_barman + def test_selling_ordering(self): + # Cheaper items should be processed with a higher priority + self.login_in_bar(self.barmen) + + assert ( + self.submit_basket( + self.customer, + [ + BasketItem(self.beer.id, 1), + BasketItem(self.gift.id, 1), + ], + ).status_code + == 302 + ) + + assert self.updated_amount(self.customer) == 0 + + def test_recordings(self): + self.refill_user(self.customer, self.cons.selling_price * 3) + self.login_in_bar(self.barmen) + assert ( + self.submit_basket( + self.customer, + [BasketItem(self.cons.id, 3)], + ).status_code + == 302 + ) + assert self.updated_amount(self.customer) == 0 + + assert ( + self.submit_basket( + self.customer, + [BasketItem(self.dcons.id, 3)], + ).status_code + == 302 + ) + + assert self.updated_amount(self.customer) == self.dcons.selling_price * -3 + + assert ( + self.submit_basket( + self.customer, + [BasketItem(self.dcons.id, settings.SITH_ECOCUP_LIMIT)], + ).status_code + == 302 + ) + + assert self.updated_amount(self.customer) == self.dcons.selling_price * ( + -3 - settings.SITH_ECOCUP_LIMIT + ) + + assert ( + self.submit_basket( + self.customer, + [BasketItem(self.dcons.id, 1)], + ).status_code + == 200 + ) + + assert self.updated_amount(self.customer) == self.dcons.selling_price * ( + -3 - settings.SITH_ECOCUP_LIMIT + ) + + assert ( + self.submit_basket( + self.customer, + [ + BasketItem(self.cons.id, 1), + BasketItem(self.dcons.id, 1), + ], + ).status_code + == 302 + ) + + assert self.updated_amount(self.customer) == self.dcons.selling_price * ( + -3 - settings.SITH_ECOCUP_LIMIT + ) + class TestCounterStats(TestCase): @classmethod diff --git a/counter/views/click.py b/counter/views/click.py index f324a9fc..c1815dec 100644 --- a/counter/views/click.py +++ b/counter/views/click.py @@ -194,7 +194,11 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView): with transaction.atomic(): self.request.session["last_basket"] = [] - for form in formset: + # We sort items from cheap to expensive + # This is important because some items have a negative price + # Negative priced items gives money to the customer and should + # be processed first so that we don't throw a not enough money error + for form in sorted(formset, key=lambda form: form.product.price): self.request.session["last_basket"].append( f"{form.cleaned_data['quantity']} x {form.product.name}" )