mirror of
https://framagit.org/la-chariotte/la-chariotte.git
synced 2025-05-01 19:32:26 +02:00
186 lines
7 KiB
Python
186 lines
7 KiB
Python
import random
|
|
|
|
import base36
|
|
from django.core.exceptions import ValidationError
|
|
from django.db import models
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from la_chariotte.settings import AUTH_USER_MODEL
|
|
|
|
CODE_LENGTH = 6
|
|
|
|
|
|
class GroupedOrder(models.Model):
|
|
name = models.CharField(
|
|
max_length=100, null=True, verbose_name="Titre de la commande"
|
|
)
|
|
orga = models.ForeignKey(
|
|
AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name="Organisateur·ice"
|
|
)
|
|
delivery_date = models.DateField("Date de livraison")
|
|
deadline = models.DateTimeField("Date limite de commande")
|
|
place = models.CharField(
|
|
max_length=100, null=True, blank=True, verbose_name="Lieu de livraison"
|
|
)
|
|
description = models.TextField("Description", null=True, blank=True)
|
|
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
|
code = models.CharField(auto_created=True)
|
|
|
|
def create_code_from_pk(self):
|
|
"""When a grouped order is created, we compute a unique code that will be used in url path
|
|
How we generate this code :
|
|
1. The instance pk, written in base36 (max 5 digits for now - we assume that there will not be more than 60466175 grouped orders)
|
|
2. A random int written in base36 (5 digits long)
|
|
3. Only the 6 first digits of this string
|
|
The use of pk in the beginning of the string guarantees the uniqueness, and the random part makes that we cannot guess the url path.
|
|
"""
|
|
base_36_pk = base36.dumps(self.pk)
|
|
random_string = base36.dumps(
|
|
random.randint(pow(36, CODE_LENGTH - 2), pow(36, CODE_LENGTH - 1) - 1)
|
|
) # generates a 5 digits long string
|
|
self.code = f"{base_36_pk}{random_string}"[:CODE_LENGTH]
|
|
|
|
def compute_total_price(self):
|
|
price = 0
|
|
for order in self.order_set.all():
|
|
price += order.price
|
|
self.total_price = price
|
|
self.save()
|
|
|
|
def compute_items_ordered_nb(self):
|
|
"""Updates the ordered_nb of all items fo the grouped_order"""
|
|
for item in self.item_set.all():
|
|
item.compute_ordered_nb()
|
|
|
|
def is_ongoing(self):
|
|
"""Returns True if the grouped order is open for new Orders - False if it's too late"""
|
|
return self.deadline >= timezone.now()
|
|
|
|
def is_to_be_delivered(self):
|
|
"""Returns True if the grouped order has not been delivered yet - False if it's old"""
|
|
return self.delivery_date >= timezone.now().date()
|
|
|
|
def get_absolute_url(self):
|
|
return reverse("order:manage_items", kwargs={"pk": self.pk})
|
|
|
|
def clean_fields(self, exclude=None):
|
|
super().clean_fields(exclude=exclude)
|
|
if self.delivery_date < timezone.now().date() and not self.pk:
|
|
# if the grouped order is being created (not updated), it cannot be in the past
|
|
# if we are updating, it's ok
|
|
raise ValidationError("La date de livraison ne doit pas être dans le passé")
|
|
|
|
if self.delivery_date < self.deadline.date():
|
|
raise ValidationError(
|
|
"La date limite de commande doit être avant la date de livraison"
|
|
)
|
|
|
|
def __str__(self): # pragma: no cover
|
|
return (
|
|
self.name
|
|
if self.name
|
|
else f"Commande groupée {self.pk} du {self.date} organisée par {self.orga}"
|
|
)
|
|
|
|
|
|
class OrderAuthor(models.Model):
|
|
"""Created when a user orders without having an account - or when a user creates an account"""
|
|
|
|
# TODO faire le lien avec CustomUser (CustomUser hérite de OrderAuthor), pour ensuite préremplir quand on est connecté·e
|
|
first_name = models.CharField(verbose_name="Prénom")
|
|
last_name = models.CharField(verbose_name="Nom")
|
|
phone = models.CharField(
|
|
verbose_name="Numéro de téléphone",
|
|
help_text="Pour que l'organisateur·ice vous contacte en cas de besoin",
|
|
)
|
|
email = models.CharField(
|
|
verbose_name="Adresse mail",
|
|
help_text="Pour que l'organisateur·ice vous contacte en cas de besoin",
|
|
)
|
|
|
|
def __str__(self): # pragma: no cover
|
|
return f"{self.first_name} {self.last_name}"
|
|
|
|
|
|
class Order(models.Model):
|
|
grouped_order = models.ForeignKey(
|
|
GroupedOrder, on_delete=models.CASCADE, related_name="order_set"
|
|
)
|
|
author = models.ForeignKey(OrderAuthor, on_delete=models.CASCADE)
|
|
articles_nb = models.PositiveIntegerField(default=0)
|
|
price = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
|
created_date = models.DateTimeField("Date de la commande", auto_now_add=True)
|
|
note = models.TextField(max_length=200, null=True, blank=True)
|
|
|
|
def compute_order_articles_nb(self):
|
|
"""Computes the number of articles in this order"""
|
|
articles_nb = 0
|
|
for ord_item in self.ordered_items.all():
|
|
articles_nb += ord_item.nb
|
|
self.articles_nb = articles_nb
|
|
self.save()
|
|
|
|
def compute_order_price(self):
|
|
"""Computes the total price of the order"""
|
|
price = 0
|
|
for ord_item in self.ordered_items.all():
|
|
price += ord_item.get_price()
|
|
self.price = price
|
|
self.save()
|
|
|
|
def __str__(self): # pragma: no cover
|
|
return f"Commande de {self.author} pour la commande groupée {self.grouped_order.pk}"
|
|
|
|
|
|
class Item(models.Model):
|
|
name = models.CharField(max_length=100)
|
|
grouped_order = models.ForeignKey(GroupedOrder, on_delete=models.CASCADE)
|
|
price = models.DecimalField(max_digits=10, decimal_places=2)
|
|
max_limit = models.PositiveSmallIntegerField(null=True, blank=True)
|
|
|
|
ordered_nb = models.IntegerField(default=0)
|
|
|
|
def compute_ordered_nb(self):
|
|
"""Computes the number of ordered articles for this item (in this grouped order)"""
|
|
ordered_nb = 0
|
|
for order in self.orders.all():
|
|
ordered_nb += order.nb
|
|
self.ordered_nb = ordered_nb
|
|
self.save()
|
|
|
|
def get_total_price(self):
|
|
"""Returns the price of all orders on this item"""
|
|
return self.price * self.ordered_nb
|
|
|
|
def get_remaining_nb(self):
|
|
"""Returns the number of remaining articles for this item in this grouped order"""
|
|
if self.max_limit is not None:
|
|
return self.max_limit - self.ordered_nb
|
|
else:
|
|
return None
|
|
|
|
def get_absolute_url(self):
|
|
return reverse("order:manage_items", kwargs={"pk": self.grouped_order.pk})
|
|
|
|
def __str__(self): # pragma: no cover
|
|
return f"{self.name} ({self.price} €)"
|
|
|
|
class Meta:
|
|
ordering = ["name"]
|
|
|
|
|
|
class OrderedItem(models.Model):
|
|
"""Item in one specific Order, and the number of articles"""
|
|
|
|
nb = models.PositiveSmallIntegerField(default=0) # works up to 32767
|
|
order = models.ForeignKey(
|
|
Order, on_delete=models.CASCADE, related_name="ordered_items"
|
|
)
|
|
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="orders")
|
|
|
|
def get_price(self):
|
|
return self.nb * self.item.price
|
|
|
|
def __str__(self): # pragma: no cover
|
|
return f"{self.nb} {self.item}, dans la commande {self.order.pk}"
|