mirror of
https://github.com/ae-utbm/sith.git
synced 2025-07-10 03:49:24 +00:00
use google convention for docstrings
This commit is contained in:
@ -30,9 +30,8 @@ class BillingInfoForm(forms.ModelForm):
|
||||
|
||||
|
||||
class StudentCardForm(forms.ModelForm):
|
||||
"""
|
||||
Form for adding student cards
|
||||
Only used for user profile since CounterClick is to complicated
|
||||
"""Form for adding student cards
|
||||
Only used for user profile since CounterClick is to complicated.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
@ -48,8 +47,7 @@ class StudentCardForm(forms.ModelForm):
|
||||
|
||||
|
||||
class GetUserForm(forms.Form):
|
||||
"""
|
||||
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
|
||||
"""The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
|
||||
reverse function, or any other use.
|
||||
|
||||
The Form implements a nice JS widget allowing the user to type a customer account id, or search the database with
|
||||
|
@ -44,9 +44,10 @@ from subscription.models import Subscription
|
||||
|
||||
|
||||
class Customer(models.Model):
|
||||
"""
|
||||
This class extends a user to make a customer. It adds some basic customers' information, such as the account ID, and
|
||||
is used by other accounting classes as reference to the customer, rather than using User
|
||||
"""Customer data of a User.
|
||||
|
||||
It adds some basic customers' information, such as the account ID, and
|
||||
is used by other accounting classes as reference to the customer, rather than using User.
|
||||
"""
|
||||
|
||||
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
|
||||
@ -63,10 +64,9 @@ class Customer(models.Model):
|
||||
return "%s - %s" % (self.user.username, self.account_id)
|
||||
|
||||
def save(self, *args, allow_negative=False, is_selling=False, **kwargs):
|
||||
"""
|
||||
is_selling : tell if the current action is a selling
|
||||
"""is_selling : tell if the current action is a selling
|
||||
allow_negative : ignored if not a selling. Allow a selling to put the account in negative
|
||||
Those two parameters avoid blocking the save method of a customer if his account is negative
|
||||
Those two parameters avoid blocking the save method of a customer if his account is negative.
|
||||
"""
|
||||
if self.amount < 0 and (is_selling and not allow_negative):
|
||||
raise ValidationError(_("Not enough money"))
|
||||
@ -84,9 +84,8 @@ class Customer(models.Model):
|
||||
|
||||
@property
|
||||
def can_buy(self) -> bool:
|
||||
"""
|
||||
Check if whether this customer has the right to
|
||||
purchase any item.
|
||||
"""Check if whether this customer has the right to purchase any item.
|
||||
|
||||
This must be not confused with the Product.can_be_sold_to(user)
|
||||
method as the present method returns an information
|
||||
about a customer whereas the other tells something
|
||||
@ -100,8 +99,7 @@ class Customer(models.Model):
|
||||
|
||||
@classmethod
|
||||
def get_or_create(cls, user: User) -> Tuple[Customer, bool]:
|
||||
"""
|
||||
Work in pretty much the same way as the usual get_or_create method,
|
||||
"""Work in pretty much the same way as the usual get_or_create method,
|
||||
but with the default field replaced by some under the hood.
|
||||
|
||||
If the user has an account, return it as is.
|
||||
@ -158,9 +156,8 @@ class Customer(models.Model):
|
||||
|
||||
|
||||
class BillingInfo(models.Model):
|
||||
"""
|
||||
Represent the billing information of a user, which are required
|
||||
by the 3D-Secure v2 system used by the etransaction module
|
||||
"""Represent the billing information of a user, which are required
|
||||
by the 3D-Secure v2 system used by the etransaction module.
|
||||
"""
|
||||
|
||||
customer = models.OneToOneField(
|
||||
@ -182,10 +179,9 @@ class BillingInfo(models.Model):
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
|
||||
def to_3dsv2_xml(self) -> str:
|
||||
"""
|
||||
Convert the data from this model into a xml usable
|
||||
"""Convert the data from this model into a xml usable
|
||||
by the online paying service of the Crédit Agricole bank.
|
||||
see : `https://www.ca-moncommerce.com/espace-client-mon-commerce/up2pay-e-transactions/ma-documentation/manuel-dintegration-focus-3ds-v2/principes-generaux/#integration-3dsv2-developpeur-webmaster`
|
||||
see : `https://www.ca-moncommerce.com/espace-client-mon-commerce/up2pay-e-transactions/ma-documentation/manuel-dintegration-focus-3ds-v2/principes-generaux/#integration-3dsv2-developpeur-webmaster`.
|
||||
"""
|
||||
data = {
|
||||
"Address": {
|
||||
@ -204,9 +200,9 @@ class BillingInfo(models.Model):
|
||||
|
||||
|
||||
class ProductType(models.Model):
|
||||
"""
|
||||
This describes a product type
|
||||
Useful only for categorizing, changes are made at the product level for now
|
||||
"""A product type.
|
||||
|
||||
Useful only for categorizing.
|
||||
"""
|
||||
|
||||
name = models.CharField(_("name"), max_length=30)
|
||||
@ -229,9 +225,7 @@ class ProductType(models.Model):
|
||||
return reverse("counter:producttype_list")
|
||||
|
||||
def is_owned_by(self, user):
|
||||
"""
|
||||
Method to see if that object can be edited by the given user
|
||||
"""
|
||||
"""Method to see if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
|
||||
@ -240,9 +234,7 @@ class ProductType(models.Model):
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
"""
|
||||
This describes a product, with all its related informations
|
||||
"""
|
||||
"""A product, with all its related information."""
|
||||
|
||||
name = models.CharField(_("name"), max_length=64)
|
||||
description = models.TextField(_("description"), blank=True)
|
||||
@ -297,9 +289,7 @@ class Product(models.Model):
|
||||
return settings.SITH_ECOCUP_DECO == self.id
|
||||
|
||||
def is_owned_by(self, user):
|
||||
"""
|
||||
Method to see if that object can be edited by the given user
|
||||
"""
|
||||
"""Method to see if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_in_group(
|
||||
@ -309,8 +299,7 @@ class Product(models.Model):
|
||||
return False
|
||||
|
||||
def can_be_sold_to(self, user: User) -> bool:
|
||||
"""
|
||||
Check if whether the user given in parameter has the right to buy
|
||||
"""Check if whether the user given in parameter has the right to buy
|
||||
this product or not.
|
||||
|
||||
This must be not confused with the Customer.can_buy()
|
||||
@ -319,7 +308,8 @@ class Product(models.Model):
|
||||
whereas the other tells something about a Customer
|
||||
(and not a user, they are not the same model).
|
||||
|
||||
:return: True if the user can buy this product else False
|
||||
Returns:
|
||||
True if the user can buy this product else False
|
||||
"""
|
||||
if not self.buying_groups.exists():
|
||||
return True
|
||||
@ -335,15 +325,16 @@ class Product(models.Model):
|
||||
|
||||
class CounterQuerySet(models.QuerySet):
|
||||
def annotate_has_barman(self, user: User) -> CounterQuerySet:
|
||||
"""
|
||||
Annotate the queryset with the `user_is_barman` field.
|
||||
"""Annotate the queryset with the `user_is_barman` field.
|
||||
|
||||
For each counter, this field has value True if the user
|
||||
is a barman of this counter, else False.
|
||||
|
||||
:param user: the user we want to check if he is a barman
|
||||
|
||||
Example::
|
||||
Args:
|
||||
user: the user we want to check if he is a barman
|
||||
|
||||
Examples:
|
||||
```python
|
||||
sli = User.objects.get(username="sli")
|
||||
counters = (
|
||||
Counter.objects
|
||||
@ -353,6 +344,7 @@ class CounterQuerySet(models.QuerySet):
|
||||
print("Sli est barman dans les comptoirs suivants :")
|
||||
for counter in counters:
|
||||
print(f"- {counter.name}")
|
||||
```
|
||||
"""
|
||||
subquery = user.counters.filter(pk=OuterRef("pk"))
|
||||
# noinspection PyTypeChecker
|
||||
@ -417,23 +409,21 @@ class Counter(models.Model):
|
||||
return user.is_board_member or user in self.sellers.all()
|
||||
|
||||
def gen_token(self):
|
||||
"""Generate a new token for this counter"""
|
||||
"""Generate a new token for this counter."""
|
||||
self.token = "".join(
|
||||
random.choice(string.ascii_letters + string.digits) for x in range(30)
|
||||
)
|
||||
self.save()
|
||||
|
||||
def add_barman(self, user):
|
||||
"""
|
||||
Logs a barman in to the given counter
|
||||
A user is stored as a tuple with its login time
|
||||
"""Logs a barman in to the given counter.
|
||||
|
||||
A user is stored as a tuple with its login time.
|
||||
"""
|
||||
Permanency(user=user, counter=self, start=timezone.now(), end=None).save()
|
||||
|
||||
def del_barman(self, user):
|
||||
"""
|
||||
Logs a barman out and store its permanency
|
||||
"""
|
||||
"""Logs a barman out and store its permanency."""
|
||||
perm = Permanency.objects.filter(counter=self, user=user, end=None).all()
|
||||
for p in perm:
|
||||
p.end = p.activity
|
||||
@ -444,8 +434,7 @@ class Counter(models.Model):
|
||||
return self.get_barmen_list()
|
||||
|
||||
def get_barmen_list(self):
|
||||
"""
|
||||
Returns the barman list as list of User
|
||||
"""Returns the barman list as list of User.
|
||||
|
||||
Also handle the timeout of the barmen
|
||||
"""
|
||||
@ -462,16 +451,12 @@ class Counter(models.Model):
|
||||
return bl
|
||||
|
||||
def get_random_barman(self):
|
||||
"""
|
||||
Return a random user being currently a barman
|
||||
"""
|
||||
"""Return a random user being currently a barman."""
|
||||
bl = self.get_barmen_list()
|
||||
return bl[random.randrange(0, len(bl))]
|
||||
|
||||
def update_activity(self):
|
||||
"""
|
||||
Update the barman activity to prevent timeout
|
||||
"""
|
||||
"""Update the barman activity to prevent timeout."""
|
||||
for p in Permanency.objects.filter(counter=self, end=None).all():
|
||||
p.save() # Update activity
|
||||
|
||||
@ -479,25 +464,18 @@ class Counter(models.Model):
|
||||
return len(self.barmen_list) > 0
|
||||
|
||||
def is_inactive(self):
|
||||
"""
|
||||
Returns True if the counter self is inactive from SITH_COUNTER_MINUTE_INACTIVE's value minutes, else False
|
||||
"""
|
||||
"""Returns True if the counter self is inactive from SITH_COUNTER_MINUTE_INACTIVE's value minutes, else False."""
|
||||
return self.is_open() and (
|
||||
(timezone.now() - self.permanencies.order_by("-activity").first().activity)
|
||||
> timedelta(minutes=settings.SITH_COUNTER_MINUTE_INACTIVE)
|
||||
)
|
||||
|
||||
def barman_list(self):
|
||||
"""
|
||||
Returns the barman id list
|
||||
"""
|
||||
"""Returns the barman id list."""
|
||||
return [b.id for b in self.get_barmen_list()]
|
||||
|
||||
def can_refill(self):
|
||||
"""
|
||||
Show if the counter authorize the refilling with physic money
|
||||
"""
|
||||
|
||||
"""Show if the counter authorize the refilling with physic money."""
|
||||
if self.type != "BAR":
|
||||
return False
|
||||
if self.id in SITH_COUNTER_OFFICES:
|
||||
@ -511,8 +489,7 @@ class Counter(models.Model):
|
||||
return is_ae_member
|
||||
|
||||
def get_top_barmen(self) -> QuerySet:
|
||||
"""
|
||||
Return a QuerySet querying the office hours stats of all the barmen of all time
|
||||
"""Return a QuerySet querying the office hours stats of all the barmen of all time
|
||||
of this counter, ordered by descending number of hours.
|
||||
|
||||
Each element of the QuerySet corresponds to a barman and has the following data :
|
||||
@ -535,16 +512,17 @@ class Counter(models.Model):
|
||||
)
|
||||
|
||||
def get_top_customers(self, since: datetime | date | None = None) -> QuerySet:
|
||||
"""
|
||||
Return a QuerySet querying the money spent by customers of this counter
|
||||
"""Return a QuerySet querying the money spent by customers of this counter
|
||||
since the specified date, ordered by descending amount of money spent.
|
||||
|
||||
Each element of the QuerySet corresponds to a customer and has the following data :
|
||||
- the full name (first name + last name) of the customer
|
||||
- the nickname of the customer
|
||||
- the amount of money spent by the customer
|
||||
|
||||
:param since: timestamp from which to perform the calculation
|
||||
- the full name (first name + last name) of the customer
|
||||
- the nickname of the customer
|
||||
- the amount of money spent by the customer
|
||||
|
||||
Args:
|
||||
since: timestamp from which to perform the calculation
|
||||
"""
|
||||
if since is None:
|
||||
since = get_start_of_semester()
|
||||
@ -573,12 +551,15 @@ class Counter(models.Model):
|
||||
)
|
||||
|
||||
def get_total_sales(self, since: datetime | date | None = None) -> CurrencyField:
|
||||
"""
|
||||
Compute and return the total turnover of this counter
|
||||
since the date specified in parameter (by default, since the start of the current
|
||||
semester)
|
||||
:param since: timestamp from which to perform the calculation
|
||||
:return: Total revenue earned at this counter
|
||||
"""Compute and return the total turnover of this counter since the given date.
|
||||
|
||||
By default, the date is the start of the current semester.
|
||||
|
||||
Args:
|
||||
since: timestamp from which to perform the calculation
|
||||
|
||||
Returns:
|
||||
Total revenue earned at this counter.
|
||||
"""
|
||||
if since is None:
|
||||
since = get_start_of_semester()
|
||||
@ -591,9 +572,7 @@ class Counter(models.Model):
|
||||
|
||||
|
||||
class Refilling(models.Model):
|
||||
"""
|
||||
Handle the refilling
|
||||
"""
|
||||
"""Handle the refilling."""
|
||||
|
||||
counter = models.ForeignKey(
|
||||
Counter, related_name="refillings", blank=False, on_delete=models.CASCADE
|
||||
@ -665,9 +644,7 @@ class Refilling(models.Model):
|
||||
|
||||
|
||||
class Selling(models.Model):
|
||||
"""
|
||||
Handle the sellings
|
||||
"""
|
||||
"""Handle the sellings."""
|
||||
|
||||
label = models.CharField(_("label"), max_length=64)
|
||||
product = models.ForeignKey(
|
||||
@ -724,9 +701,7 @@ class Selling(models.Model):
|
||||
)
|
||||
|
||||
def save(self, *args, allow_negative=False, **kwargs):
|
||||
"""
|
||||
allow_negative : Allow this selling to use more money than available for this user
|
||||
"""
|
||||
"""allow_negative : Allow this selling to use more money than available for this user."""
|
||||
if not self.date:
|
||||
self.date = timezone.now()
|
||||
self.full_clean()
|
||||
@ -864,8 +839,10 @@ class Selling(models.Model):
|
||||
|
||||
|
||||
class Permanency(models.Model):
|
||||
"""
|
||||
This class aims at storing a traceability of who was barman where and when
|
||||
"""A permanency of a barman, on a counter.
|
||||
|
||||
This aims at storing a traceability of who was barman where and when.
|
||||
Mainly for ~~dick size contest~~ establishing the top 10 barmen of the semester.
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(
|
||||
@ -971,9 +948,7 @@ class CashRegisterSummary(models.Model):
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
def is_owned_by(self, user):
|
||||
"""
|
||||
Method to see if that object can be edited by the given user
|
||||
"""
|
||||
"""Method to see if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID):
|
||||
@ -1010,9 +985,7 @@ class CashRegisterSummaryItem(models.Model):
|
||||
|
||||
|
||||
class Eticket(models.Model):
|
||||
"""
|
||||
Eticket can be linked to a product an allows PDF generation
|
||||
"""
|
||||
"""Eticket can be linked to a product an allows PDF generation."""
|
||||
|
||||
product = models.OneToOneField(
|
||||
Product,
|
||||
@ -1041,9 +1014,7 @@ class Eticket(models.Model):
|
||||
return reverse("counter:eticket_list")
|
||||
|
||||
def is_owned_by(self, user):
|
||||
"""
|
||||
Method to see if that object can be edited by the given user
|
||||
"""
|
||||
"""Method to see if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
return user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID)
|
||||
@ -1058,11 +1029,11 @@ class Eticket(models.Model):
|
||||
|
||||
|
||||
class StudentCard(models.Model):
|
||||
"""
|
||||
Alternative way to connect a customer into a counter
|
||||
"""Alternative way to connect a customer into a counter.
|
||||
|
||||
We are using Mifare DESFire EV1 specs since it's used for izly cards
|
||||
https://www.nxp.com/docs/en/application-note/AN10927.pdf
|
||||
UID is 7 byte long that means 14 hexa characters
|
||||
UID is 7 byte long that means 14 hexa characters.
|
||||
"""
|
||||
|
||||
UID_SIZE = 14
|
||||
|
@ -140,10 +140,7 @@ class CounterTest(TestCase):
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_annotate_has_barman_queryset(self):
|
||||
"""
|
||||
Test if the custom queryset method ``annotate_has_barman``
|
||||
works as intended
|
||||
"""
|
||||
"""Test if the custom queryset method `annotate_has_barman` works as intended."""
|
||||
self.sli.counters.set([self.foyer, self.mde])
|
||||
counters = Counter.objects.annotate_has_barman(self.sli)
|
||||
for counter in counters:
|
||||
@ -265,15 +262,11 @@ class CounterStatsTest(TestCase):
|
||||
assert response.status_code == 403
|
||||
|
||||
def test_get_total_sales(self):
|
||||
"""
|
||||
Test the result of the Counter.get_total_sales() method
|
||||
"""
|
||||
"""Test the result of the Counter.get_total_sales() method."""
|
||||
assert self.counter.get_total_sales() == 3102
|
||||
|
||||
def test_top_barmen(self):
|
||||
"""
|
||||
Test the result of Counter.get_top_barmen() is correct
|
||||
"""
|
||||
"""Test the result of Counter.get_top_barmen() is correct."""
|
||||
users = [self.skia, self.root, self.sli]
|
||||
perm_times = [
|
||||
timedelta(days=16, hours=2, minutes=35, seconds=54),
|
||||
@ -292,9 +285,7 @@ class CounterStatsTest(TestCase):
|
||||
]
|
||||
|
||||
def test_top_customer(self):
|
||||
"""
|
||||
Test the result of Counter.get_top_customers() is correct
|
||||
"""
|
||||
"""Test the result of Counter.get_top_customers() is correct."""
|
||||
users = [self.sli, self.skia, self.krophil, self.root]
|
||||
sale_amounts = [2000, 1000, 100, 2]
|
||||
assert list(self.counter.get_top_customers()) == [
|
||||
@ -588,9 +579,8 @@ class BarmanConnectionTest(TestCase):
|
||||
|
||||
|
||||
class StudentCardTest(TestCase):
|
||||
"""
|
||||
Tests for adding and deleting Stundent Cards
|
||||
Test that an user can be found with it's student card
|
||||
"""Tests for adding and deleting Stundent Cards
|
||||
Test that an user can be found with it's student card.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
|
197
counter/views.py
197
counter/views.py
@ -75,9 +75,7 @@ from counter.models import (
|
||||
|
||||
|
||||
class CounterAdminMixin(View):
|
||||
"""
|
||||
This view is made to protect counter admin section
|
||||
"""
|
||||
"""Protect counter admin section."""
|
||||
|
||||
edit_group = [settings.SITH_GROUP_COUNTER_ADMIN_ID]
|
||||
edit_club = []
|
||||
@ -105,9 +103,7 @@ class CounterAdminMixin(View):
|
||||
|
||||
|
||||
class StudentCardDeleteView(DeleteView, CanEditMixin):
|
||||
"""
|
||||
View used to delete a card from a user
|
||||
"""
|
||||
"""View used to delete a card from a user."""
|
||||
|
||||
model = StudentCard
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
@ -210,9 +206,7 @@ class CounterTabsMixin(TabedViewMixin):
|
||||
class CounterMain(
|
||||
CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, FormMixin
|
||||
):
|
||||
"""
|
||||
The public (barman) view
|
||||
"""
|
||||
"""The public (barman) view."""
|
||||
|
||||
model = Counter
|
||||
template_name = "counter/counter_main.jinja"
|
||||
@ -239,9 +233,7 @@ class CounterMain(
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
We handle here the login form for the barman
|
||||
"""
|
||||
"""We handle here the login form for the barman."""
|
||||
if self.request.method == "POST":
|
||||
self.object = self.get_object()
|
||||
self.object.update_activity()
|
||||
@ -275,9 +267,7 @@ class CounterMain(
|
||||
return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
We handle here the redirection, passing the user id of the asked customer
|
||||
"""
|
||||
"""We handle here the redirection, passing the user id of the asked customer."""
|
||||
self.kwargs["user_id"] = form.cleaned_data["user_id"]
|
||||
return super().form_valid(form)
|
||||
|
||||
@ -286,10 +276,9 @@ class CounterMain(
|
||||
|
||||
|
||||
class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
The click view
|
||||
"""The click view
|
||||
This is a detail view not to have to worry about loading the counter
|
||||
Everything is made by hand in the post method
|
||||
Everything is made by hand in the post method.
|
||||
"""
|
||||
|
||||
model = Counter
|
||||
@ -347,7 +336,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Simple get view"""
|
||||
"""Simple get view."""
|
||||
if "basket" not in request.session.keys(): # Init the basket session entry
|
||||
request.session["basket"] = {}
|
||||
request.session["basket_total"] = 0
|
||||
@ -364,7 +353,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
return ret
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Handle the many possibilities of the post request"""
|
||||
"""Handle the many possibilities of the post request."""
|
||||
self.object = self.get_object()
|
||||
self.refill_form = None
|
||||
if (self.object.type != "BAR" and not request.user.is_authenticated) or (
|
||||
@ -481,10 +470,9 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
return len(request.POST) == 0 and len(request.body) != 0
|
||||
|
||||
def add_product(self, request, q=1, p=None):
|
||||
"""
|
||||
Add a product to the basket
|
||||
"""Add a product to the basket
|
||||
q is the quantity passed as integer
|
||||
p is the product id, passed as an integer
|
||||
p is the product id, passed as an integer.
|
||||
"""
|
||||
pid = p or parse_qs(request.body.decode())["product_id"][0]
|
||||
pid = str(pid)
|
||||
@ -543,9 +531,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
return True
|
||||
|
||||
def add_student_card(self, request):
|
||||
"""
|
||||
Add a new student card on the customer account
|
||||
"""
|
||||
"""Add a new student card on the customer account."""
|
||||
uid = request.POST["student_card_uid"]
|
||||
uid = str(uid)
|
||||
if not StudentCard.is_valid(uid):
|
||||
@ -564,7 +550,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
return True
|
||||
|
||||
def del_product(self, request):
|
||||
"""Delete a product from the basket"""
|
||||
"""Delete a product from the basket."""
|
||||
pid = parse_qs(request.body.decode())["product_id"][0]
|
||||
product = self.get_product(pid)
|
||||
if pid in request.session["basket"]:
|
||||
@ -581,11 +567,11 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
request.session.modified = True
|
||||
|
||||
def parse_code(self, request):
|
||||
"""
|
||||
Parse the string entered by the barman
|
||||
"""Parse the string entered by the barman.
|
||||
|
||||
This can be of two forms :
|
||||
- <str>, where the string is the code of the product
|
||||
- <int>X<str>, where the integer is the quantity and str the code
|
||||
- `<str>`, where the string is the code of the product
|
||||
- `<int>X<str>`, where the integer is the quantity and str the code.
|
||||
"""
|
||||
string = parse_qs(request.body.decode()).get("code", [""])[0].upper()
|
||||
if string == "FIN":
|
||||
@ -605,7 +591,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
return self.render_to_response(context)
|
||||
|
||||
def finish(self, request):
|
||||
"""Finish the click session, and validate the basket"""
|
||||
"""Finish the click session, and validate the basket."""
|
||||
with transaction.atomic():
|
||||
request.session["last_basket"] = []
|
||||
if self.sum_basket(request) > self.customer.amount:
|
||||
@ -657,7 +643,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
)
|
||||
|
||||
def cancel(self, request):
|
||||
"""Cancel the click session"""
|
||||
"""Cancel the click session."""
|
||||
kwargs = {"counter_id": self.object.id}
|
||||
request.session.pop("basket", None)
|
||||
return HttpResponseRedirect(
|
||||
@ -665,7 +651,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
)
|
||||
|
||||
def refill(self, request):
|
||||
"""Refill the customer's account"""
|
||||
"""Refill the customer's account."""
|
||||
if not self.object.can_refill():
|
||||
raise PermissionDenied
|
||||
form = RefillForm(request.POST)
|
||||
@ -678,7 +664,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
self.refill_form = form
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add customer to the context"""
|
||||
"""Add customer to the context."""
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
products = self.object.products.select_related("product_type")
|
||||
if self.customer_is_barman():
|
||||
@ -701,8 +687,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
|
||||
|
||||
class CounterLogin(RedirectView):
|
||||
"""
|
||||
Handle the login of a barman
|
||||
"""Handle the login of a barman.
|
||||
|
||||
Logged barmen are stored in the Permanency model
|
||||
"""
|
||||
@ -710,9 +695,7 @@ class CounterLogin(RedirectView):
|
||||
permanent = False
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Register the logged user as barman for this counter
|
||||
"""
|
||||
"""Register the logged user as barman for this counter."""
|
||||
self.counter_id = kwargs["counter_id"]
|
||||
self.counter = Counter.objects.filter(id=kwargs["counter_id"]).first()
|
||||
form = LoginForm(request, data=request.POST)
|
||||
@ -745,9 +728,7 @@ class CounterLogout(RedirectView):
|
||||
permanent = False
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Unregister the user from the barman
|
||||
"""
|
||||
"""Unregister the user from the barman."""
|
||||
self.counter = Counter.objects.filter(id=kwargs["counter_id"]).first()
|
||||
user = User.objects.filter(id=request.POST["user_id"]).first()
|
||||
self.counter.del_barman(user)
|
||||
@ -803,9 +784,7 @@ class CounterAdminTabsMixin(TabedViewMixin):
|
||||
|
||||
|
||||
class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
"""
|
||||
"""A list view for the admins."""
|
||||
|
||||
model = Counter
|
||||
template_name = "counter/counter_list.jinja"
|
||||
@ -813,9 +792,7 @@ class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
||||
|
||||
|
||||
class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit a counter's main informations (for the counter's manager)
|
||||
"""
|
||||
"""Edit a counter's main informations (for the counter's manager)."""
|
||||
|
||||
model = Counter
|
||||
form_class = CounterEditForm
|
||||
@ -833,9 +810,7 @@ class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
|
||||
|
||||
class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit a counter's main informations (for the counter's admin)
|
||||
"""
|
||||
"""Edit a counter's main informations (for the counter's admin)."""
|
||||
|
||||
model = Counter
|
||||
form_class = modelform_factory(Counter, fields=["name", "club", "type"])
|
||||
@ -845,9 +820,7 @@ class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
|
||||
|
||||
class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
Create a counter (for the admins)
|
||||
"""
|
||||
"""Create a counter (for the admins)."""
|
||||
|
||||
model = Counter
|
||||
form_class = modelform_factory(
|
||||
@ -860,9 +833,7 @@ class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
|
||||
|
||||
class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
||||
"""
|
||||
Delete a counter (for the admins)
|
||||
"""
|
||||
"""Delete a counter (for the admins)."""
|
||||
|
||||
model = Counter
|
||||
pk_url_kwarg = "counter_id"
|
||||
@ -875,9 +846,7 @@ class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
||||
|
||||
|
||||
class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
"""
|
||||
"""A list view for the admins."""
|
||||
|
||||
model = ProductType
|
||||
template_name = "counter/producttype_list.jinja"
|
||||
@ -885,9 +854,7 @@ class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
|
||||
|
||||
class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
A create view for the admins
|
||||
"""
|
||||
"""A create view for the admins."""
|
||||
|
||||
model = ProductType
|
||||
fields = ["name", "description", "comment", "icon", "priority"]
|
||||
@ -896,9 +863,7 @@ class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView
|
||||
|
||||
|
||||
class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
"""
|
||||
"""An edit view for the admins."""
|
||||
|
||||
model = ProductType
|
||||
template_name = "core/edit.jinja"
|
||||
@ -908,9 +873,7 @@ class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
|
||||
|
||||
class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
"""
|
||||
"""A list view for the admins."""
|
||||
|
||||
model = Product
|
||||
template_name = "counter/product_list.jinja"
|
||||
@ -920,9 +883,7 @@ class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView
|
||||
|
||||
|
||||
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
"""
|
||||
"""A list view for the admins."""
|
||||
|
||||
model = Product
|
||||
template_name = "counter/product_list.jinja"
|
||||
@ -932,9 +893,7 @@ class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
|
||||
|
||||
class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
A create view for the admins
|
||||
"""
|
||||
"""A create view for the admins."""
|
||||
|
||||
model = Product
|
||||
form_class = ProductEditForm
|
||||
@ -943,9 +902,7 @@ class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
|
||||
|
||||
class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
"""
|
||||
"""An edit view for the admins."""
|
||||
|
||||
model = Product
|
||||
form_class = ProductEditForm
|
||||
@ -955,18 +912,14 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
|
||||
|
||||
class RefillingDeleteView(DeleteView):
|
||||
"""
|
||||
Delete a refilling (for the admins)
|
||||
"""
|
||||
"""Delete a refilling (for the admins)."""
|
||||
|
||||
model = Refilling
|
||||
pk_url_kwarg = "refilling_id"
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""
|
||||
We have here a very particular right handling, we can't inherit from CanEditPropMixin
|
||||
"""
|
||||
"""We have here a very particular right handling, we can't inherit from CanEditPropMixin."""
|
||||
self.object = self.get_object()
|
||||
if (
|
||||
timezone.now() - self.object.date
|
||||
@ -990,18 +943,14 @@ class RefillingDeleteView(DeleteView):
|
||||
|
||||
|
||||
class SellingDeleteView(DeleteView):
|
||||
"""
|
||||
Delete a selling (for the admins)
|
||||
"""
|
||||
"""Delete a selling (for the admins)."""
|
||||
|
||||
model = Selling
|
||||
pk_url_kwarg = "selling_id"
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""
|
||||
We have here a very particular right handling, we can't inherit from CanEditPropMixin
|
||||
"""
|
||||
"""We have here a very particular right handling, we can't inherit from CanEditPropMixin."""
|
||||
self.object = self.get_object()
|
||||
if (
|
||||
timezone.now() - self.object.date
|
||||
@ -1028,9 +977,7 @@ class SellingDeleteView(DeleteView):
|
||||
|
||||
|
||||
class CashRegisterSummaryForm(forms.Form):
|
||||
"""
|
||||
Provide the cash summary form
|
||||
"""
|
||||
"""Provide the cash summary form."""
|
||||
|
||||
ten_cents = forms.IntegerField(label=_("10 cents"), required=False, min_value=0)
|
||||
twenty_cents = forms.IntegerField(label=_("20 cents"), required=False, min_value=0)
|
||||
@ -1238,9 +1185,7 @@ class CashRegisterSummaryForm(forms.Form):
|
||||
|
||||
|
||||
class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Provide the last operations to allow barmen to delete them
|
||||
"""
|
||||
"""Provide the last operations to allow barmen to delete them."""
|
||||
|
||||
model = Counter
|
||||
pk_url_kwarg = "counter_id"
|
||||
@ -1248,9 +1193,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
current_tab = "last_ops"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""
|
||||
We have here again a very particular right handling
|
||||
"""
|
||||
"""We have here again a very particular right handling."""
|
||||
self.object = self.get_object()
|
||||
if (
|
||||
self.object.get_barmen_list()
|
||||
@ -1267,7 +1210,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add form to the context"""
|
||||
"""Add form to the context."""
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
threshold = timezone.now() - timedelta(
|
||||
minutes=settings.SITH_LAST_OPERATIONS_LIMIT
|
||||
@ -1282,9 +1225,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
|
||||
|
||||
class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Provide the cash summary form
|
||||
"""
|
||||
"""Provide the cash summary form."""
|
||||
|
||||
model = Counter
|
||||
pk_url_kwarg = "counter_id"
|
||||
@ -1292,9 +1233,7 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
current_tab = "cash_summary"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""
|
||||
We have here again a very particular right handling
|
||||
"""
|
||||
"""We have here again a very particular right handling."""
|
||||
self.object = self.get_object()
|
||||
if (
|
||||
self.object.get_barmen_list()
|
||||
@ -1327,16 +1266,14 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
return reverse_lazy("counter:details", kwargs={"counter_id": self.object.id})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add form to the context"""
|
||||
"""Add form to the context."""
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["form"] = self.form
|
||||
return kwargs
|
||||
|
||||
|
||||
class CounterActivityView(DetailView):
|
||||
"""
|
||||
Show the bar activity
|
||||
"""
|
||||
"""Show the bar activity."""
|
||||
|
||||
model = Counter
|
||||
pk_url_kwarg = "counter_id"
|
||||
@ -1344,16 +1281,14 @@ class CounterActivityView(DetailView):
|
||||
|
||||
|
||||
class CounterStatView(DetailView, CounterAdminMixin):
|
||||
"""
|
||||
Show the bar stats
|
||||
"""
|
||||
"""Show the bar stats."""
|
||||
|
||||
model = Counter
|
||||
pk_url_kwarg = "counter_id"
|
||||
template_name = "counter/stats.jinja"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add stats to the context"""
|
||||
"""Add stats to the context."""
|
||||
counter: Counter = self.object
|
||||
semester_start = get_start_of_semester()
|
||||
office_hours = counter.get_top_barmen()
|
||||
@ -1386,7 +1321,7 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
||||
|
||||
|
||||
class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""Edit cash summaries"""
|
||||
"""Edit cash summaries."""
|
||||
|
||||
model = CashRegisterSummary
|
||||
template_name = "counter/cash_register_summary.jinja"
|
||||
@ -1400,7 +1335,7 @@ class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
|
||||
|
||||
class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""Display a list of cash summaries"""
|
||||
"""Display a list of cash summaries."""
|
||||
|
||||
model = CashRegisterSummary
|
||||
template_name = "counter/cash_summary_list.jinja"
|
||||
@ -1410,7 +1345,7 @@ class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
paginate_by = settings.SITH_COUNTER_CASH_SUMMARY_LENGTH
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add sums to the context"""
|
||||
"""Add sums to the context."""
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
form = CashSummaryFormBase(self.request.GET)
|
||||
kwargs["form"] = form
|
||||
@ -1461,7 +1396,7 @@ class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
||||
current_tab = "invoices_call"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add sums to the context"""
|
||||
"""Add sums to the context."""
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["months"] = Selling.objects.datetimes("date", "month", order="DESC")
|
||||
if "month" in self.request.GET:
|
||||
@ -1522,9 +1457,7 @@ class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
||||
|
||||
|
||||
class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
"""
|
||||
"""A list view for the admins."""
|
||||
|
||||
model = Eticket
|
||||
template_name = "counter/eticket_list.jinja"
|
||||
@ -1533,9 +1466,7 @@ class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
|
||||
|
||||
class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
Create an eticket
|
||||
"""
|
||||
"""Create an eticket."""
|
||||
|
||||
model = Eticket
|
||||
template_name = "core/create.jinja"
|
||||
@ -1544,9 +1475,7 @@ class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
|
||||
|
||||
class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit an eticket
|
||||
"""
|
||||
"""Edit an eticket."""
|
||||
|
||||
model = Eticket
|
||||
template_name = "core/edit.jinja"
|
||||
@ -1556,9 +1485,7 @@ class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
|
||||
|
||||
class EticketPDFView(CanViewMixin, DetailView):
|
||||
"""
|
||||
Display the PDF of an eticket
|
||||
"""
|
||||
"""Display the PDF of an eticket."""
|
||||
|
||||
model = Selling
|
||||
pk_url_kwarg = "selling_id"
|
||||
@ -1647,9 +1574,7 @@ class EticketPDFView(CanViewMixin, DetailView):
|
||||
|
||||
|
||||
class CounterRefillingListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
List of refillings on a counter
|
||||
"""
|
||||
"""List of refillings on a counter."""
|
||||
|
||||
model = Refilling
|
||||
template_name = "counter/refilling_list.jinja"
|
||||
@ -1668,9 +1593,7 @@ class CounterRefillingListView(CounterAdminTabsMixin, CounterAdminMixin, ListVie
|
||||
|
||||
|
||||
class StudentCardFormView(FormView):
|
||||
"""
|
||||
Add a new student card
|
||||
"""
|
||||
"""Add a new student card."""
|
||||
|
||||
form_class = StudentCardForm
|
||||
template_name = "core/create.jinja"
|
||||
|
Reference in New Issue
Block a user