mirror of
https://framagit.org/la-chariotte/la-chariotte.git
synced 2025-05-03 04:11:52 +02:00
generate a 6 digit long unique code for each grouped order
This commit is contained in:
parent
9e52ded095
commit
2083142d47
7 changed files with 91 additions and 2 deletions
45
la_chariotte/order/migrations/0025_groupedorder_code.py
Normal file
45
la_chariotte/order/migrations/0025_groupedorder_code.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Generated by Django 4.2.1 on 2023-08-08 09:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import base36
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
def create_code_from_pk(pk):
|
||||||
|
"""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(pk)
|
||||||
|
random_string = base36.dumps(random.randint(1727605,60466175)) # generates a 5 digits long string
|
||||||
|
return f"{base_36_pk}{random_string}"
|
||||||
|
|
||||||
|
def set_code_default(apps, schema_editor):
|
||||||
|
"""Provides a default code to existing grouped orders during migration"""
|
||||||
|
GroupedOrder = apps.get_model("order","GroupedOrder")
|
||||||
|
for grouped_order in GroupedOrder.objects.all().iterator():
|
||||||
|
grouped_order.code = create_code_from_pk(grouped_order.pk)
|
||||||
|
grouped_order.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("order", "0024_alter_item_options"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="groupedorder",
|
||||||
|
name="code",
|
||||||
|
field=models.CharField(auto_created=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.RunPython(set_code_default),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="groupedorder",
|
||||||
|
name="code",
|
||||||
|
field=models.CharField(auto_created=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,3 +1,6 @@
|
||||||
|
import random
|
||||||
|
|
||||||
|
import base36
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -5,6 +8,8 @@ from django.utils import timezone
|
||||||
|
|
||||||
from la_chariotte.settings import AUTH_USER_MODEL
|
from la_chariotte.settings import AUTH_USER_MODEL
|
||||||
|
|
||||||
|
CODE_LENGTH = 6
|
||||||
|
|
||||||
|
|
||||||
class GroupedOrder(models.Model):
|
class GroupedOrder(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
|
@ -20,6 +25,21 @@ class GroupedOrder(models.Model):
|
||||||
)
|
)
|
||||||
description = models.TextField("Description", null=True, blank=True)
|
description = models.TextField("Description", null=True, blank=True)
|
||||||
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
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):
|
def compute_total_price(self):
|
||||||
price = 0
|
price = 0
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from la_chariotte.order.models import GroupedOrder, Item
|
from la_chariotte.order.models import CODE_LENGTH, GroupedOrder, Item
|
||||||
|
from la_chariotte.order.tests.utils import create_grouped_order
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
class TestGroupedOrderModel:
|
class TestGroupedOrderModel:
|
||||||
|
@ -124,6 +128,15 @@ class TestGroupedOrderModel:
|
||||||
"La date de livraison ne doit pas être dans le passé" in response.content.decode()
|
"La date de livraison ne doit pas être dans le passé" in response.content.decode()
|
||||||
assert GroupedOrder.objects.count() == 0
|
assert GroupedOrder.objects.count() == 0
|
||||||
|
|
||||||
|
def test_create_unique_code_from_pk(self, client_log):
|
||||||
|
grouped_order = create_grouped_order(
|
||||||
|
days_before_delivery_date=5,
|
||||||
|
days_before_deadline=2,
|
||||||
|
name="future",
|
||||||
|
orga_user=auth.get_user(client_log),
|
||||||
|
)
|
||||||
|
assert len(grouped_order.code) == CODE_LENGTH
|
||||||
|
|
||||||
|
|
||||||
class TestItemModel:
|
class TestItemModel:
|
||||||
"""Tests for Item model"""
|
"""Tests for Item model"""
|
||||||
|
|
|
@ -791,6 +791,7 @@ class TestGroupedOrderCreateView:
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
assert response.url.endswith("gerer-produits")
|
assert response.url.endswith("gerer-produits")
|
||||||
assert models.GroupedOrder.objects.count() == 1
|
assert models.GroupedOrder.objects.count() == 1
|
||||||
|
assert models.GroupedOrder.objects.first().code != ""
|
||||||
|
|
||||||
def test_create_grouped_order__anonymous_user(self, client):
|
def test_create_grouped_order__anonymous_user(self, client):
|
||||||
create_gorder_url = reverse("order:create_grouped_order")
|
create_gorder_url = reverse("order:create_grouped_order")
|
||||||
|
|
|
@ -18,9 +18,11 @@ def create_grouped_order(
|
||||||
"""
|
"""
|
||||||
date = timezone.now().date() + datetime.timedelta(days=days_before_delivery_date)
|
date = timezone.now().date() + datetime.timedelta(days=days_before_delivery_date)
|
||||||
deadline = timezone.now() + datetime.timedelta(days=days_before_deadline)
|
deadline = timezone.now() + datetime.timedelta(days=days_before_deadline)
|
||||||
return models.GroupedOrder.objects.create(
|
grouped_order = models.GroupedOrder.objects.create(
|
||||||
name=name, orga=orga_user, delivery_date=date, deadline=deadline
|
name=name, orga=orga_user, delivery_date=date, deadline=deadline
|
||||||
)
|
)
|
||||||
|
grouped_order.create_code_from_pk()
|
||||||
|
return grouped_order
|
||||||
|
|
||||||
|
|
||||||
def order_items_in_grouped_order(grouped_order):
|
def order_items_in_grouped_order(grouped_order):
|
||||||
|
|
|
@ -122,6 +122,12 @@ class GroupedOrderCreateView(LoginRequiredMixin, generic.CreateView):
|
||||||
kwargs["user"] = self.request.user
|
kwargs["user"] = self.request.user
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
"""If the form is valid, generate a unique code and save"""
|
||||||
|
self.object = form.save()
|
||||||
|
self.object.create_code_from_pk()
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class GroupedOrderUpdateView(UserPassesTestMixin, generic.UpdateView):
|
class GroupedOrderUpdateView(UserPassesTestMixin, generic.UpdateView):
|
||||||
"""View for updating a grouped order"""
|
"""View for updating a grouped order"""
|
||||||
|
|
|
@ -13,6 +13,8 @@ asn1crypto==1.5.1
|
||||||
# oscrypto
|
# oscrypto
|
||||||
# pyhanko
|
# pyhanko
|
||||||
# pyhanko-certvalidator
|
# pyhanko-certvalidator
|
||||||
|
base36==0.1.1
|
||||||
|
# à la main
|
||||||
certifi==2023.5.7
|
certifi==2023.5.7
|
||||||
# via
|
# via
|
||||||
# requests
|
# requests
|
||||||
|
|
Loading…
Reference in a new issue