Merge branch 'feat-distribution-place' into 'develop'

Draft: feature: Add placekey relationship for distribution spots

See merge request la-chariotte/la-chariotte!129
This commit is contained in:
selfhoster1312 ACAB 2025-03-02 19:15:45 +00:00
commit 8d7ba5e616
24 changed files with 499 additions and 58 deletions

View file

View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class LieuConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "la_chariotte.lieu"

View file

@ -0,0 +1,35 @@
import datetime
from django import forms
from django.contrib.auth import get_user_model
from django.forms.utils import to_current_timezone
from django.utils import timezone
from la_chariotte.lieu.models import Lieu
class LieuForm(forms.ModelForm):
class Meta:
model = Lieu
fields = [
"name",
"description",
"url",
]
widgets = {
"name": forms.TextInput(
attrs={"placeholder": "ex : Centre social Kropotkine"}
),
"description": forms.Textarea(
attrs={"placeholder": "Plus d'infos sur le lieu ? (facultatif)"}
),
"url": forms.TextInput(attrs={"placeholder": "raccourci"}),
}
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user")
super().__init__(*args, **kwargs)
def save(self, commit=True):
self.instance.orga = get_user_model().objects.get(id=self.user.pk)
return super().save(commit=commit)

View file

@ -0,0 +1,28 @@
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Lieu",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=100, verbose_name="Nom du lieu de distribution")),
("url", models.CharField(max_length=20, verbose_name='Portion du lien pour le lieu', unique=True)),
("orga", models.ForeignKey(on_delete=models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Organisateur·ice')),
("description", models.TextField(blank=True, null=True, verbose_name="Description")),
],
),
]

View file

View file

@ -0,0 +1,27 @@
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.order.models import GroupedOrder
from la_chariotte.settings import AUTH_USER_MODEL
class Lieu(models.Model):
name = models.CharField(max_length=100, verbose_name="Nom du lieu de distribution")
orga = models.ForeignKey(
AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name="Organisateur·ice"
)
url = models.CharField(
max_length=20, verbose_name="Portion du lien pour le lieu", unique=True
)
description = models.TextField("Description", null=True, blank=True)
def __str__(self): # pragma: no cover
return self.name
def get_absolute_url(self):
return reverse("lieu:lieu_update", kwargs={"url": self.url})

View file

@ -0,0 +1,62 @@
{% extends 'base.html' %}
{% block title %}Mes lieux de livraison{% endblock %}
{% block content %}
<p class="desktop-hidden mobile-content-title">
{% block content_title %}Lieux de distribution que vous organisez{% endblock %}
</p>
<div class="buttons is-pulled-right">
<a class="button is-primary" href="{% url 'lieu:create_lieu' %}">
<i class="fa fa-plus-circle mr-3" aria-hidden="true"></i>
Créer un nouveau lieu de distribution</a>
</div>
{% if lieu_context.lieu_liste %}
<table class="table">
<thead>
<tr>
<th>Lieu</th>
<th>Commandes</th>
<th>Description</th>
<th>URL</th>
<th> </th>
</tr>
</thead>
<tbody>
{% for lieu in lieu_context.lieu_liste %}
<tr>
<td>
<a title="Détail du lieu de distribution" href="{% url 'lieu:lieu_update' lieu.url %}">{{ lieu }}</a>
</td>
<td>
{% if lieu.url in lieu_context.commandes.keys %}
{% for lieu_url, lieu_commandes in lieu_context.commandes.items %}
{% if lieu_url == lieu.url %}
{% for commande in lieu_commandes %}
{% url 'order:grouped_order_detail' code=commande.code as order_url %}
{% if order_url %}
<a href="{{ order_url }}">{{commande.name}}</a><br>
{% else %}
{{ commande.name }}<br>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% else %}
Aucune
{% endif %}
</td>
<td>
{{ lieu.description }}
</td>
<td>
{{ lieu.url }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>Pas de lieux de distribution pour l'instant</p>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,26 @@
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}Nouveau lieu de distribution{% endblock %}
{% block content %}
<p class="desktop-hidden mobile-content-title">
{% block content_title %}Créer un lieu de distribution{% endblock %}
</p>
<div class="box">
<p class="title">Nouvelle lieu</p>
<div class="columns">
<div class="column is-8">
<form method="post" onsubmit="deadlinePassedCheck(event)">
{% csrf_token %}
{{ form | crispy }}
<div class="buttons">
<a class="button is-light" href="{% url 'lieu:index' %}">Annuler</a>
<input class="button is-primary" type="submit" value="Suivant">
</div>
</form>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,22 @@
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}{{ lieu }}{% endblock %}
{% block content %}
<p class="desktop-hidden mobile-content-title">
{% block content_title %}Modifier le lieu de livraison{% endblock %}
</p>
<div class="box">
<p class="title">{{ lieu.name }} - modifier</p>
<form method="post">{% csrf_token %}
{{ form | crispy }}
<div class="buttons">
<a class="button is-light" href="{% url 'lieu:index' %}">Annuler</a>
<input class="button is-primary" type="submit" value="Suivant">
</div>
</form>
</div>
{% endblock %}

View file

@ -0,0 +1,22 @@
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}Modifier le lieu de livraison {% endblock %}
{% block content %}
<p class="desktop-hidden mobile-content-title">
{% block content_title %}Modifier le lieu de livraison{% endblock %}
</p>
<div class="box">
<p class="title">{{ lieu.name }} - modifier</p>
<form method="post">{% csrf_token %}
{{ form | crispy }}
<div class="buttons">
<a class="button is-light" href="{% url 'lieu:index' %}">Annuler</a>
<input class="button is-primary" type="submit" value="Suivant">
</div>
</form>
</div>
{% endblock %}

14
la_chariotte/lieu/urls.py Normal file
View file

@ -0,0 +1,14 @@
from django.urls import path
from . import views
app_name = "lieu"
urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
path(
"<str:url>/",
views.LieuUpdateView.as_view(),
name="lieu_update",
),
path("creer", views.LieuCreateView.as_view(), name="create_lieu"),
]

View file

@ -0,0 +1,6 @@
from .lieu import (
IndexView,
LieuCreateView,
LieuDetailView,
LieuUpdateView,
)

View file

@ -0,0 +1,87 @@
import csv
import json
from django import http
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.core.serializers.json import DjangoJSONEncoder
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse, reverse_lazy
from django.utils import timezone
from django.views import generic
from django_weasyprint import WeasyTemplateResponseMixin
from icalendar import Calendar, Event, vCalAddress, vText
from la_chariotte.order.models import GroupedOrder
from la_chariotte.order.views.mixins import UserIsOrgaMixin
from ..forms import LieuForm
from ..models import Lieu
class IndexView(LoginRequiredMixin, generic.ListView):
"""View showing all the grouped orders managed by the authenticated user"""
template_name = "lieu/index.html"
context_object_name = "lieu_context"
def get_queryset(self):
lieux = Lieu.objects.filter(orga=self.request.user)
commandes_par_lieu = dict()
for lieu in lieux:
orders = GroupedOrder.objects.all().filter(placekey=lieu)
if orders:
commandes_par_lieu[lieu.url] = orders
return {
"lieu_liste": lieux,
"commandes": commandes_par_lieu,
}
class LieuDetailView(generic.DetailView):
model = Lieu
template_name = "lieu/lieu_detail.html"
context_object_name = "lieu_context"
form_class = LieuForm
def get_object(self, queryset=None):
return get_object_or_404(Lieu, url=self.kwargs.get("url"))
def get_context_data(self, **kwargs):
lieu = self.get_object()
context = super().get_context_data(**kwargs)
return context
class LieuUpdateView(UserIsOrgaMixin, generic.UpdateView):
model = Lieu
template_name = "lieu/lieu_update.html"
context_object_name = "lieu"
form_class = LieuForm
# Can't change URL after creation
form_class.base_fields["url"].disabled = True
def get_object(self, queryset=None):
return get_object_or_404(Lieu, url=self.kwargs.get("url"))
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["user"] = self.request.user
return kwargs
class LieuCreateView(LoginRequiredMixin, generic.CreateView):
model = Lieu
form_class = LieuForm
template_name = "lieu/lieu_create.html"
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["user"] = self.request.user
return kwargs
def form_valid(self, form):
self.object = form.save()
return super().form_valid(form)

View file

@ -1,10 +1,12 @@
import datetime import datetime
from django import forms from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.forms.utils import to_current_timezone from django.forms.utils import to_current_timezone
from django.utils import timezone from django.utils import timezone
from la_chariotte.lieu.models import Lieu
from la_chariotte.order.models import GroupedOrder, Item from la_chariotte.order.models import GroupedOrder, Item
@ -22,6 +24,12 @@ class GroupedOrderForm(forms.ModelForm):
label="Numéro de téléphone obligatoire pour les participants", label="Numéro de téléphone obligatoire pour les participants",
required=False, required=False,
) )
placekey = forms.ModelMultipleChoiceField(
label="Lieux de distribution",
queryset=Lieu.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
class Meta: class Meta:
model = GroupedOrder model = GroupedOrder
@ -34,6 +42,7 @@ class GroupedOrderForm(forms.ModelForm):
"place", "place",
"description", "description",
"is_phone_mandatory", "is_phone_mandatory",
"placekey",
] ]
widgets = { widgets = {
"name": forms.TextInput( "name": forms.TextInput(

View file

@ -0,0 +1,23 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("order", "0029_set_phone_mandatory_for_existing_orders"),
("lieu", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="groupedorder",
name="placekey",
field=models.ManyToManyField(
verbose_name="Lieu de distribution",
to="lieu.lieu",
),
),
migrations.AddField(
model_name="order",
name="placekey",
field=models.ForeignKey(blank=True, null=True, on_delete=models.deletion.CASCADE, to='lieu.lieu'),
),
]

View file

@ -21,9 +21,17 @@ class GroupedOrder(models.Model):
max_length=50, null=True, blank=True, verbose_name="Créneau de distribution" max_length=50, null=True, blank=True, verbose_name="Créneau de distribution"
) )
deadline = models.DateTimeField("Date limite de commande") deadline = models.DateTimeField("Date limite de commande")
# Try to associate with a saved distribution place in DB
placekey = models.ManyToManyField(
"lieu.Lieu",
verbose_name="Lieu de distribution",
)
# Alternatively, propose a free-text distribution place field
place = models.CharField( place = models.CharField(
max_length=100, null=True, blank=True, verbose_name="Lieu de livraison" max_length=100, null=True, blank=True, verbose_name="Lieu de livraison"
) )
description = models.TextField("Description", null=True, blank=True) description = models.TextField("Description", null=True, blank=True)
code = models.CharField(auto_created=True) code = models.CharField(auto_created=True)
is_phone_mandatory = models.BooleanField( is_phone_mandatory = models.BooleanField(
@ -123,6 +131,9 @@ class Order(models.Model):
author = models.ForeignKey(OrderAuthor, on_delete=models.CASCADE) author = models.ForeignKey(OrderAuthor, on_delete=models.CASCADE)
created_date = models.DateTimeField("Date et heure de commande", auto_now_add=True) created_date = models.DateTimeField("Date et heure de commande", auto_now_add=True)
note = models.TextField(max_length=200, null=True, blank=True) note = models.TextField(max_length=200, null=True, blank=True)
placekey = models.ForeignKey(
"lieu.lieu", on_delete=models.CASCADE, null=True, blank=True
)
@property @property
def articles_nb(self): def articles_nb(self):

View file

@ -171,6 +171,17 @@
<p><label for="note">Note à l'organisateur·ice<em> (facultatif)</em> :</label> <p><label for="note">Note à l'organisateur·ice<em> (facultatif)</em> :</label>
<textarea id="note" rows=3 name="note">{{ note }}</textarea></p> <textarea id="note" rows=3 name="note">{{ note }}</textarea></p>
{% if placekey %}
<label for="placekey">Point de distribution:</label>
<select name="placekey" id="placekey-select">
{% for place in placekey %}
<option value="{{ place.url }}">{{ place.name }}</option>
{% endfor %}
</select>
{% endif %}
<hr>
<div class="buttons"> <div class="buttons">
<button id="submit" type="submit" value="Order" class="button is-primary"> <button id="submit" type="submit" value="Order" class="button is-primary">
<i class="fa fa-shopping-basket mr-3" aria-hidden="true"></i>Commander <i class="fa fa-shopping-basket mr-3" aria-hidden="true"></i>Commander

View file

@ -184,6 +184,7 @@
<header class="modal-card-head has-background-info"> <header class="modal-card-head has-background-info">
<div class="modal-card-title-container"> <div class="modal-card-title-container">
<p class="modal-card-title mb-2">Commande de {{ order.author }}</p> <p class="modal-card-title mb-2">Commande de {{ order.author }}</p>
{% if order.placekey %}<p class="has-text-grey-dark">Lieu: {{ order.placekey }}</p>{% endif %}
<p class="has-text-grey-dark">Le {{ order.created_date|date:'d M Y' }} à {{ order.created_date|date:'H:i' }}</p> <p class="has-text-grey-dark">Le {{ order.created_date|date:'d M Y' }} à {{ order.created_date|date:'H:i' }}</p>
</div> </div>
<button class="delete" aria-label="close"></button> <button class="delete" aria-label="close"></button>
@ -370,4 +371,4 @@
}); });
{% endblock %} {% endblock %}
</script> </script>

View file

@ -75,64 +75,13 @@
<h2 style="text-align: center"> <h2 style="text-align: center">
{{ grouped_order.name }} - {{ grouped_order.delivery_date }} {{ grouped_order.name }} - {{ grouped_order.delivery_date }}
</h2> </h2>
{% if items %} {% if by_lieu %}
<table> {% for lieu_name, ordered_dict in lieux.items %}
<thead> <h3>{{ lieu_name }}</h3>
<tr> {% include 'order/grouped_order_sheet_list.html' with orders_dict=lieu_orders items=items grouped_order=grouped_order %}
<th style="font-size: 0.5em; width: 2em">OK</th> {% endfor %}
<th style="text-align: center; width: 20%">Nom</th>
{% for item in items %}
<th class="item_name" style="font-weight: normal;">
<div>{{ item.name }}</div>
</th>
{% endfor %}
<th style="width: 2cm">Prix</th>
</tr>
</thead>
<tbody>
<tr style="background-color: #bababa">
<td></td>
<td style="text-align: left">Prix unitaire</td>
{% for item in items %}
<td>
{{ item.price }} €
</td>
{% endfor %}
<td></td>
</tr>
<tr style="background-color: #bababa">
<th></th>
<th style="text-align: left">TOTAL</th>
{% for item in items %}
<th>
{{ item.ordered_nb }}
</th>
{% endfor %}
<th>{{ grouped_order.total_price }} €</th>
</tr>
{% for order, ordered_items in orders_dict.items %}
<tr>
<td></td>
<td>
{{ order.author.last_name|upper }} {{ order.author.first_name }}
</td>
{% for ordered_item in ordered_items %}
<td>
{% if ordered_item > 0 %}
{{ ordered_item }}
{% endif %}
</td>
{% endfor %}
<td>
{{ order.price }} €
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %} {% else %}
Aucun produit n'a été commandé {% include 'order/grouped_order_sheet_list.html' with orders_dict=orders_dict items=items grouped_order=grouped_order %}
{% endif %} {% endif %}
</body> </body>
</html> </html>

View file

@ -0,0 +1,58 @@
{% if items %}
<table>
<thead>
<tr>
<th style="font-size: 0.5em; width: 2em">OK</th>
<th style="text-align: center; width: 20%">Nom</th>
{% for item in items %}
<th class="item_name" style="font-weight: normal;">
<div>{{ item.name }}</div>
</th>
{% endfor %}
<th style="width: 2cm">Prix</th>
</tr>
</thead>
<tbody>
<tr style="background-color: #bababa">
<td></td>
<td style="text-align: left">Prix unitaire</td>
{% for item in items %}
<td>
{{ item.price }} €
</td>
{% endfor %}
<td></td>
</tr>
<tr style="background-color: #bababa">
<th></th>
<th style="text-align: left">TOTAL</th>
{% for item in items %}
<th>
{{ item.ordered_nb }}
</th>
{% endfor %}
<th>{{ grouped_order.total_price }} €</th>
</tr>
{% for order, ordered_items in orders_dict.items %}
<tr>
<td></td>
<td>
{{ order.author.last_name|upper }} {{ order.author.first_name }}
</td>
{% for ordered_item in ordered_items %}
<td>
{% if ordered_item > 0 %}
{{ ordered_item }}
{% endif %}
</td>
{% endfor %}
<td>
{{ order.price }} €
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
Aucun produit n'a été commandé
{% endif %}

View file

@ -11,6 +11,8 @@ from django.views import generic
from django_weasyprint import WeasyTemplateResponseMixin from django_weasyprint import WeasyTemplateResponseMixin
from icalendar import Calendar, Event, vCalAddress, vText from icalendar import Calendar, Event, vCalAddress, vText
from la_chariotte.lieu.models import Lieu
from ..forms import GroupedOrderForm, Item, JoinGroupedOrderForm from ..forms import GroupedOrderForm, Item, JoinGroupedOrderForm
from ..models import GroupedOrder, OrderAuthor from ..models import GroupedOrder, OrderAuthor
from .mixins import UserIsOrgaMixin from .mixins import UserIsOrgaMixin
@ -136,6 +138,7 @@ class GroupedOrderDetailView(generic.DetailView):
"order_author": order_author, "order_author": order_author,
# Used to set if the phone is required in the form # Used to set if the phone is required in the form
"is_phone_mandatory": grouped_order.is_phone_mandatory, "is_phone_mandatory": grouped_order.is_phone_mandatory,
"placekey": grouped_order.placekey.all(),
} }
) )
return context return context
@ -198,6 +201,11 @@ class GroupedOrderUpdateView(UserIsOrgaMixin, generic.UpdateView):
kwargs["user"] = self.request.user kwargs["user"] = self.request.user
return kwargs return kwargs
def get_context_data(self, **kwargs):
context = super(GroupedOrderUpdateView, self).get_context_data(**kwargs)
context["places"] = Lieu.objects.filter(orga=self.request.user)
return context
class GroupedOrderDuplicateView(UserIsOrgaMixin, generic.RedirectView): class GroupedOrderDuplicateView(UserIsOrgaMixin, generic.RedirectView):
def get_object(self, queryset=None): def get_object(self, queryset=None):
@ -296,6 +304,8 @@ class GroupedOrderExportView(UserIsOrgaMixin, generic.DetailView):
context = super(GroupedOrderExportView, self).get_context_data(**kwargs) context = super(GroupedOrderExportView, self).get_context_data(**kwargs)
grouped_order = self.get_object() grouped_order = self.get_object()
by_lieu = len(grouped_order.placekey.all()) > 0
items = [ items = [
item item
for item in grouped_order.item_set.all().order_by("name") for item in grouped_order.item_set.all().order_by("name")
@ -316,6 +326,14 @@ class GroupedOrderExportView(UserIsOrgaMixin, generic.DetailView):
context["items"] = items context["items"] = items
context["orders_dict"] = orders_dict context["orders_dict"] = orders_dict
context["by_lieu"] = by_lieu
if by_lieu:
lieux = dict()
for order, order_items in orders_dict.items():
if order.placekey.name not in lieux:
lieux[order.placekey.name] = dict()
lieux[order.placekey.name][order] = order_items
context["lieux"] = lieux
return context return context
@ -355,6 +373,8 @@ class ExportGroupOrderEmailAdressesToDownloadView(UserPassesTestMixin, generic.V
class ExportGroupedOrderToCSVView(GroupedOrderExportView): class ExportGroupedOrderToCSVView(GroupedOrderExportView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
grouped_order = self.get_object()
enableLieu = len(grouped_order.placekey.all()) > 0
super(ExportGroupedOrderToCSVView, self).get(self, request, *args, **kwargs) super(ExportGroupedOrderToCSVView, self).get(self, request, *args, **kwargs)
context = self.get_context_data() context = self.get_context_data()
@ -376,6 +396,8 @@ class ExportGroupedOrderToCSVView(GroupedOrderExportView):
row.append("Note") row.append("Note")
row.append("Date") row.append("Date")
row.append("Heure") row.append("Heure")
if enableLieu:
row.append("Lieu")
writer.writerow(row) writer.writerow(row)
row = ["", "Prix unitaire TTC (€)"] row = ["", "Prix unitaire TTC (€)"]
@ -398,6 +420,8 @@ class ExportGroupedOrderToCSVView(GroupedOrderExportView):
row.append(order.note) row.append(order.note)
row.append(order.created_date.strftime("%d/%m/%Y")) row.append(order.created_date.strftime("%d/%m/%Y"))
row.append(order.created_date.strftime("%H:%M")) row.append(order.created_date.strftime("%H:%M"))
if enableLieu:
row.append(order.placekey.name)
writer.writerow(row) writer.writerow(row)
# write total row # write total row

View file

@ -5,6 +5,7 @@ from django.urls import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.views import generic from django.views import generic
from la_chariotte.lieu.models import Lieu
from la_chariotte.mail.utils import send_order_confirmation_mail from la_chariotte.mail.utils import send_order_confirmation_mail
from ..models import GroupedOrder, Order, OrderAuthor, OrderedItem from ..models import GroupedOrder, Order, OrderAuthor, OrderedItem
@ -13,6 +14,7 @@ from ..models import GroupedOrder, Order, OrderAuthor, OrderedItem
def place_order(request, code): def place_order(request, code):
# Creates an AnonymousUser and an Order with related OrderedItems # Creates an AnonymousUser and an Order with related OrderedItems
grouped_order = get_object_or_404(GroupedOrder, code=code) grouped_order = get_object_or_404(GroupedOrder, code=code)
places = grouped_order.placekey.all()
# Handle permissions # Handle permissions
user_is_orga = request.user == grouped_order.orga user_is_orga = request.user == grouped_order.orga
@ -46,6 +48,19 @@ def place_order(request, code):
phone = request.POST["phone"] phone = request.POST["phone"]
email = request.POST["email"] email = request.POST["email"]
note = request.POST["note"] note = request.POST["note"]
# Make sure requested location is valid in this group order (and exists at all)
# If no placekey is enabled for this group order, placekey is always null
if len(places) == 0:
placekey = None
else:
placekey = get_object_or_404(Lieu, url=request.POST["placekey"])
if placekey not in places:
raise http.Http404(
"Le lieu demandé n'est pas valide pour cette commande: %s"
% requested_placekey
)
author = OrderAuthor.objects.create( author = OrderAuthor.objects.create(
first_name=first_name, last_name=last_name, email=email, phone=phone first_name=first_name, last_name=last_name, email=email, phone=phone
) )
@ -54,6 +69,7 @@ def place_order(request, code):
grouped_order=grouped_order, grouped_order=grouped_order,
note=note, note=note,
created_date=timezone.now(), created_date=timezone.now(),
placekey=placekey,
) )
# add items to the order # add items to the order
@ -80,6 +96,7 @@ def place_order(request, code):
"error_message": error_message, "error_message": error_message,
"note": order.note, "note": order.note,
"author": author, "author": author,
"placekey": placekey,
}, },
) )
@ -98,6 +115,7 @@ def place_order(request, code):
"error_message": error_message, "error_message": error_message,
"note": order.note, "note": order.note,
"author": author, "author": author,
"placekey": placekey,
}, },
) )

View file

@ -27,6 +27,7 @@ if os.getenv("ALLOWED_HOSTS"):
# Applications & middlewares # Applications & middlewares
INSTALLED_APPS = [ INSTALLED_APPS = [
"la_chariotte.lieu",
"la_chariotte.order", "la_chariotte.order",
"la_chariotte.accounts", "la_chariotte.accounts",
"la_chariotte.mail", "la_chariotte.mail",

View file

@ -30,6 +30,7 @@ from la_chariotte.order.views.stats import stats
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("commande/", include("la_chariotte.order.urls")), path("commande/", include("la_chariotte.order.urls")),
path("lieu/", include("la_chariotte.lieu.urls")),
path("comptes/", include("la_chariotte.accounts.urls")), path("comptes/", include("la_chariotte.accounts.urls")),
# Some paths for accounts are easier to leave here # Some paths for accounts are easier to leave here
# - PasswordResetView sends the mail # - PasswordResetView sends the mail