mirror of
https://github.com/ae-utbm/sith.git
synced 2026-06-13 03:29:19 +00:00
apply review comments
This commit is contained in:
+33
-12
@@ -3,6 +3,7 @@ import math
|
||||
import uuid
|
||||
from collections import defaultdict
|
||||
from datetime import date, datetime, timezone
|
||||
from typing import ClassVar
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django import forms
|
||||
@@ -11,6 +12,7 @@ from django.core.exceptions import ValidationError
|
||||
from django.db.models import Exists, OuterRef, Q
|
||||
from django.forms import BaseModelFormSet
|
||||
from django.http import HttpRequest
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_celery_beat.models import ClockedSchedule
|
||||
@@ -600,6 +602,10 @@ class BasketItemForm(forms.Form):
|
||||
|
||||
|
||||
class BaseBasketForm(forms.BaseFormSet):
|
||||
# Minimum amount of money there must be on the account after the transaction
|
||||
# If None, the min balance check is skipped
|
||||
min_result_balance: ClassVar[int | None] = 0
|
||||
|
||||
def __init__(self, *args, customer: Customer, counter: Counter, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.customer = customer
|
||||
@@ -614,8 +620,7 @@ class BaseBasketForm(forms.BaseFormSet):
|
||||
self._check_forms_have_errors()
|
||||
self._check_product_are_unique()
|
||||
self._check_recorded_products()
|
||||
self._check_enough_money()
|
||||
self._check_refills()
|
||||
self._check_account_balance()
|
||||
|
||||
def _check_forms_have_errors(self):
|
||||
if any(len(form.errors) > 0 for form in self):
|
||||
@@ -626,10 +631,33 @@ class BaseBasketForm(forms.BaseFormSet):
|
||||
if len(price_ids) != len(self.forms):
|
||||
raise forms.ValidationError(_("Duplicated product entries."))
|
||||
|
||||
def _check_enough_money(self):
|
||||
self.total_price = sum([data["total_price"] for data in self.cleaned_data])
|
||||
if self.total_price > self.customer.amount:
|
||||
@cached_property
|
||||
def total_price(self):
|
||||
refill = settings.SITH_COUNTER_PRODUCTTYPE_REFILLING
|
||||
total_other = sum(
|
||||
form.cleaned_data["total_price"]
|
||||
for form in self.forms
|
||||
if form.price.product.product_type_id != refill
|
||||
)
|
||||
total_refill = sum(
|
||||
form.cleaned_data["total_price"]
|
||||
for form in self.forms
|
||||
if form.price.product.product_type_id == refill
|
||||
)
|
||||
return total_other - total_refill
|
||||
|
||||
def _check_account_balance(self):
|
||||
result_balance = self.customer.amount - self.total_price
|
||||
if (
|
||||
self.min_result_balance is not None
|
||||
and self.min_result_balance > result_balance
|
||||
):
|
||||
raise forms.ValidationError(_("Not enough money"))
|
||||
if result_balance > settings.SITH_ACCOUNT_MAX_MONEY:
|
||||
raise ValidationError(
|
||||
_("There cannot be more than %(money)d€ on an AE account")
|
||||
% {"money": settings.SITH_ACCOUNT_MAX_MONEY}
|
||||
)
|
||||
|
||||
def _check_recorded_products(self):
|
||||
"""Check for, among other things, ecocups and pitchers"""
|
||||
@@ -659,13 +687,6 @@ class BaseBasketForm(forms.BaseFormSet):
|
||||
% ", ".join([str(p) for p in limit_reached])
|
||||
)
|
||||
|
||||
def _check_refills(self):
|
||||
refill_type_id = settings.SITH_COUNTER_PRODUCTTYPE_REFILLING
|
||||
if any(f.price.product.product_type_id == refill_type_id for f in self.forms):
|
||||
raise ValidationError(
|
||||
_("Refill bonds cannot be purchased outside of the eboutic")
|
||||
)
|
||||
|
||||
|
||||
BasketForm = forms.formset_factory(
|
||||
BasketItemForm, formset=BaseBasketForm, absolute_max=None, min_num=1
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ class Customer(models.Model):
|
||||
|
||||
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
|
||||
account_id = models.CharField(_("account id"), max_length=10, unique=True)
|
||||
amount = CurrencyField(
|
||||
amount: CurrencyField = CurrencyField(
|
||||
_("amount"), max_value=settings.SITH_ACCOUNT_MAX_MONEY, default=0
|
||||
)
|
||||
|
||||
|
||||
@@ -535,6 +535,19 @@ class TestCounterClick(TestFullClickBase):
|
||||
|
||||
assert self.updated_amount(self.customer) == Decimal(10)
|
||||
|
||||
def test_unrecord_above_limit_fails(self):
|
||||
"""Test that it's forbidden to give back a recorded product
|
||||
if it puts the account balance above the limit.
|
||||
"""
|
||||
self.login_in_bar()
|
||||
limit = settings.SITH_ACCOUNT_MAX_MONEY
|
||||
# put the account balance just at the limit
|
||||
baker.make(Refilling, customer=self.customer.customer, amount=limit)
|
||||
response = self.submit_basket(self.customer, [BasketItem(self.dcons.id, 1)])
|
||||
assert response.status_code == 200 # no redirect = failure
|
||||
self.customer.customer.refresh_from_db()
|
||||
assert self.updated_amount(self.customer) == limit
|
||||
|
||||
def test_annotate_has_barman_queryset(self):
|
||||
"""Test if the custom queryset method `annotate_has_barman` works as intended."""
|
||||
counters = Counter.objects.annotate_has_barman(self.barmen)
|
||||
|
||||
@@ -65,11 +65,15 @@ document.addEventListener("alpine:init", () => {
|
||||
);
|
||||
},
|
||||
|
||||
getTotalRefill() {
|
||||
/**
|
||||
* Get the total of money that would be added to the AE account on basket purchase.
|
||||
*/
|
||||
getTotalAdded() {
|
||||
return this.basket
|
||||
.filter((item) => item.isRefill)
|
||||
.filter((item) => item.isRefill || item.unitPrice < 0)
|
||||
.reduce(
|
||||
(acc: number, item: BasketItem) => acc + item.quantity * item.unitPrice,
|
||||
(acc: number, item: BasketItem) =>
|
||||
acc + Math.abs(item.quantity * item.unitPrice),
|
||||
0,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<template x-if="(getTotalRefill() + {{ customer_amount }}) > {{ settings.SITH_ACCOUNT_MAX_MONEY }}">
|
||||
<template x-if="(getTotalAdded() + {{ customer_amount }}) > {{ settings.SITH_ACCOUNT_MAX_MONEY }}">
|
||||
<div class="alert alert-red">
|
||||
<div class="alert-main">
|
||||
{% trans trimmed limit=settings.SITH_ACCOUNT_MAX_MONEY %}
|
||||
@@ -122,7 +122,7 @@
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-blue"
|
||||
:disabled="(getTotalRefill() + {{ customer_amount }}) > {{ settings.SITH_ACCOUNT_MAX_MONEY }}"
|
||||
:disabled="(getTotalAdded() + {{ customer_amount }}) > {{ settings.SITH_ACCOUNT_MAX_MONEY }}"
|
||||
>
|
||||
<i class="fa fa-check"></i>
|
||||
{% trans %}Validate{% endtrans %}
|
||||
|
||||
+1
-23
@@ -66,29 +66,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class BaseEbouticBasketForm(BaseBasketForm):
|
||||
def _check_enough_money(self, *args, **kwargs):
|
||||
# Disable money check
|
||||
...
|
||||
|
||||
def _check_refills(self):
|
||||
"""Check that this basket won't put customer balance above the limit."""
|
||||
refill_type_id = settings.SITH_COUNTER_PRODUCTTYPE_REFILLING
|
||||
total_refill = sum(
|
||||
f.price.amount * f.cleaned_data["quantity"]
|
||||
for f in self.forms
|
||||
if f.price.product.product_type_id == refill_type_id
|
||||
)
|
||||
total_other = sum(
|
||||
f.price.amount * f.cleaned_data["quantity"]
|
||||
for f in self.forms
|
||||
if f.price.product.product_type_id != refill_type_id
|
||||
)
|
||||
limit = settings.SITH_ACCOUNT_MAX_MONEY
|
||||
if (total_refill - total_other + self.customer.amount) > limit:
|
||||
raise ValidationError(
|
||||
_("There cannot be more than %(money)d€ on an AE account")
|
||||
% {"money": limit}
|
||||
)
|
||||
min_result_balance = None # user can pay by card, so no minimum enforced
|
||||
|
||||
|
||||
EbouticBasketForm = forms.formset_factory(
|
||||
|
||||
@@ -3306,6 +3306,11 @@ msgstr "Saisie de produit dupliquée"
|
||||
msgid "Not enough money"
|
||||
msgstr "Solde insuffisant"
|
||||
|
||||
#: counter/forms.py counter/models.py
|
||||
#, python-format
|
||||
msgid "There cannot be more than %(money)d€ on an AE account"
|
||||
msgstr "Il ne peut pas y avoir plus de %(money)d€ sur un compte AE"
|
||||
|
||||
#: counter/forms.py
|
||||
#, python-format
|
||||
msgid ""
|
||||
@@ -3314,11 +3319,6 @@ msgstr ""
|
||||
"Cet utilisateur a atteint sa limite de déconsigne pour les produits "
|
||||
"suivants : %s"
|
||||
|
||||
#: counter/forms.py
|
||||
msgid "Refill bonds cannot be purchased outside of the eboutic"
|
||||
msgstr ""
|
||||
"Les bons de rechargement ne peuvent pas être achetés en dehors de l'eboutic"
|
||||
|
||||
#: counter/management/commands/dump_accounts.py
|
||||
msgid "Your AE account has been emptied"
|
||||
msgstr "Votre compte AE a été vidé"
|
||||
@@ -3524,11 +3524,6 @@ msgstr "méthode de paiement"
|
||||
msgid "refilling"
|
||||
msgstr "rechargement"
|
||||
|
||||
#: counter/models.py eboutic/views.py
|
||||
#, python-format
|
||||
msgid "There cannot be more than %(money)d€ on an AE account"
|
||||
msgstr "Il ne peut pas y avoir plus de %(money)d€ sur un compte AE"
|
||||
|
||||
#: counter/models.py
|
||||
msgid "Sith account"
|
||||
msgstr "Compte utilisateur"
|
||||
|
||||
@@ -504,6 +504,10 @@ SITH_ACCOUNT_DUMP_DELTA = timedelta(days=30)
|
||||
"""timedelta between the warning mail and the actual account dump"""
|
||||
|
||||
SITH_ACCOUNT_MAX_MONEY = 250 # €
|
||||
"""Maximum amount of money a sith account can hold.
|
||||
|
||||
This amount is defined by the AE's Terms and Conditions of Sale.
|
||||
"""
|
||||
|
||||
# Defines which product type is the refilling type,
|
||||
# and thus increases the account amount
|
||||
|
||||
Reference in New Issue
Block a user