la-chariotte/la_chariotte/order/views/grouped_order.py
2023-09-17 13:46:40 +02:00

392 lines
14 KiB
Python

import csv
import json
from datetime import timedelta
from io import BytesIO
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.template.loader import get_template
from django.urls import reverse, reverse_lazy
from django.utils import timezone
from django.views import generic
from django_weasyprint import WeasyTemplateResponseMixin
from django_weasyprint.views import WeasyTemplateResponse, WeasyTemplateView
from icalendar import Calendar, Event, vCalAddress, vText
from xhtml2pdf import pisa
from ..forms import GroupedOrderForm, Item, JoinGroupedOrderForm
from ..models import GroupedOrder, OrderAuthor
class IndexView(LoginRequiredMixin, generic.ListView):
"""View showing all the grouped orders managed by the authenticated user"""
template_name = "order/index.html"
context_object_name = "grouped_order_list"
def get_queryset(self):
grouped_orders = GroupedOrder.objects.filter(orga=self.request.user)
now = timezone.now()
today = now.date()
# Get the 5 most recent old grouped orders
old = grouped_orders.filter(delivery_date__lt=today).order_by("-delivery_date")[
:5
]
# Get grouped orders that have crossed their ordering deadline
# but the delivery date is still to come.
crossed_deadline = (
grouped_orders.filter(delivery_date__gte=today)
.filter(deadline__lt=now)
.order_by("-delivery_date")
)
# Get all incoming grouped orders.
incoming = grouped_orders.filter(deadline__gte=now).order_by("deadline")
return {
"old_grouped_orders": old,
"crossed_deadline_grouped_orders": crossed_deadline,
"incoming_grouped_orders": incoming,
}
class JoinGroupedOrderView(generic.FormView, generic.RedirectView, LoginRequiredMixin):
form_class = JoinGroupedOrderForm
template_name = "dashboard.html"
def form_valid(self, form):
return redirect(
reverse_lazy(
"order:grouped_order_detail", kwargs={"code": form.cleaned_data["code"]}
)
)
class GroupedOrderEventView(generic.DetailView):
model = GroupedOrder
slug_field = "code"
slug_url_kwarg = "code"
content_type = "text/calendar"
response_class = http.HttpResponse
def get_content(self):
event = Event()
event.add("summary", self.object.name)
event.add("dtstart", self.object.delivery_date)
event.add("dtend", self.object.delivery_date + timedelta(days=1))
event.add("location", vText(self.object.place))
event.add("description", vText(self.object.description))
organizer = vCalAddress("MAILTO:" + self.object.orga.email)
organizer.params["cn"] = vText(
self.object.orga.first_name + " " + self.object.orga.last_name
)
event.add("organizer", organizer)
cal = Calendar()
cal.add_component(event)
return cal.to_ical()
def render_to_response(self, context, **response_kwargs):
response_kwargs.setdefault("content_type", self.content_type)
response = self.response_class(content=self.get_content(), **response_kwargs)
response["Content-Disposition"] = f"attachment; filename={self.object.name}.ics"
return response
class GroupedOrderDetailView(generic.DetailView):
model = GroupedOrder
template_name = "order/grouped_order_detail.html"
context_object_name = "grouped_order"
def get_object(self, queryset=None):
return get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
def get_context_data(self, **kwargs):
items = self.get_object().item_set.all()
remaining_qty = {item.id: item.get_remaining_nb() for item in items}
prices_dict = {item.id: item.price for item in items}
context = super().get_context_data(**kwargs)
context.update(
{
# Used for the js display of total price of an order
"prices_dict": json.dumps(prices_dict, cls=DjangoJSONEncoder),
"remaining_qty": remaining_qty,
}
)
return context
class GroupedOrderOverview(UserPassesTestMixin, generic.DetailView):
model = GroupedOrder
template_name = "order/grouped_order_overview.html"
context_object_name = "grouped_order"
def get_object(self, queryset=None):
return get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
def test_func(self):
# Restrict access to the manager
return self.get_object().orga == self.request.user
def get(self, request, *args, **kwargs):
# Compute grouped order total price before display
self.get_object().compute_total_price()
self.get_object().compute_items_ordered_nb()
return super().get(self, request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(GroupedOrderOverview, self).get_context_data(**kwargs)
# Add share link to context
context["share_link"] = self.request.build_absolute_uri(
reverse("order:grouped_order_detail", args=(self.get_object().code,))
)
return context
class GroupedOrderCreateView(LoginRequiredMixin, generic.CreateView):
model = GroupedOrder
form_class = GroupedOrderForm
template_name = "order/grouped_order_create.html"
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["user"] = self.request.user
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):
model = GroupedOrder
template_name = "order/grouped_order_update.html"
context_object_name = "grouped_order"
form_class = GroupedOrderForm
def get_object(self, queryset=None):
return get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
def test_func(self):
# Restrict access to the manager
return self.get_object().orga == self.request.user
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["user"] = self.request.user
return kwargs
class GroupedOrderDuplicateView(UserPassesTestMixin, generic.RedirectView):
def get_object(self, queryset=None):
return get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
def test_func(self):
# Restrict access to the manager
return self.get_object().orga == self.request.user
def get(self, request, *args, **kwargs):
# overwrite the ``get`` function to copy the initial grouped order before
# redirecting to the update view of the new grouped order
initial_grouped_order = get_object_or_404(
GroupedOrder, code=self.kwargs.get("code")
)
new_grouped_order = GroupedOrder.objects.create(
name=f"{initial_grouped_order.name} - copie",
orga=self.request.user,
delivery_date=initial_grouped_order.delivery_date,
deadline=initial_grouped_order.deadline,
place=initial_grouped_order.place,
description=initial_grouped_order.description,
)
# create a unique code for the new grouped order
new_grouped_order.create_code_from_pk()
new_grouped_order.save()
# duplicate each item and add it to new_grouped_order
for item in initial_grouped_order.item_set.all():
item.pk = None
item.ordered_nb = 0
item.save()
new_grouped_order.item_set.add(item)
self.kwargs["new_go_code"] = new_grouped_order.code
return super().get(request, *args, **kwargs)
def get_redirect_url(self, *args, **kwargs):
return reverse_lazy(
"order:update_grouped_order", kwargs={"code": self.kwargs["new_go_code"]}
)
class GroupedOrderDeleteView(UserPassesTestMixin, generic.DeleteView):
model = GroupedOrder
template_name = "order/grouped_order_confirm_delete.html"
context_object_name = "grouped_order"
def get_object(self, queryset=None):
return get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
def get_success_url(self):
return reverse_lazy("order:index")
def test_func(self):
# Restrict access to the manager
return self.get_object().orga == self.request.user
def form_valid(self, form):
# Delete related OrderAuthors
grouped_order = self.get_object()
for order in grouped_order.order_set.all():
order.author.delete()
return super().form_valid(form)
class GroupedOrderAddItemsView(UserPassesTestMixin, generic.ListView):
model = Item
template_name = "order/grouped_order_add_items.html"
queryset = Item.objects.all()
def get_queryset(self):
items = super().get_queryset()
items = items.filter(grouped_order__code=self.kwargs.get("code"))
return items
def get_context_data(self, **kwargs):
context = super(GroupedOrderAddItemsView, self).get_context_data(**kwargs)
context["grouped_order"] = GroupedOrder.objects.get(
code=self.kwargs.get("code")
)
return context
def test_func(self):
# Restrict access to the manager
grouped_order = get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
return grouped_order.orga == self.request.user
class GroupedOrderExportView(UserPassesTestMixin, generic.DetailView):
model = GroupedOrder
template_name = "order/grouped_order_sheet.html"
context_object_name = "grouped_order"
def get_object(self, queryset=None):
return get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
def test_func(self):
# Restrict access to the manager
return self.get_object().orga == self.request.user
def get_context_data(self, **kwargs):
context = super(GroupedOrderExportView, self).get_context_data(**kwargs)
grouped_order = self.get_object()
items = grouped_order.item_set.filter(ordered_nb__gt=0).order_by("name")
orders = grouped_order.order_set.all().order_by(
"author__last_name", "author__first_name"
)
orders_dict = {}
for order in orders:
ordered_values = []
for item in items:
match = order.ordered_items.filter(item=item).first()
value = match.nb if match else 0
ordered_values.append(value)
orders_dict[order] = ordered_values
context["items"] = items
context["orders_dict"] = orders_dict
return context
class DownloadGroupedOrderSheetView(WeasyTemplateResponseMixin, GroupedOrderExportView):
def get_pdf_filename(self):
# This is the name of the file that will be generated to download.
return f"{self.get_object().delivery_date} - {self.get_object().name}"
class ExportGroupedOrderEmailAddressesToCSVView(UserPassesTestMixin, generic.View):
def test_func(self):
# Restrict access to the manager
origin = get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
return origin.orga == self.request.user
def get(self, request, *args, **kwargs):
grouped_order = get_object_or_404(GroupedOrder, code=self.kwargs.get("code"))
participants = OrderAuthor.objects.filter(
order__in=grouped_order.order_set.all()
)
response = http.HttpResponse(
content_type="text/csv",
headers={
"Content-Disposition": f'attachment; filename="{ grouped_order.name }-mails.csv"'
},
)
writer = csv.writer(response)
row = [participant.email for participant in participants]
writer.writerow(row)
return response
class ExportGroupedOrderToCSVView(GroupedOrderExportView):
def get(self, request, *args, **kwargs):
super(ExportGroupedOrderToCSVView, self).get(self, request, *args, **kwargs)
context = self.get_context_data()
response = http.HttpResponse(
content_type="text/csv",
headers={
"Content-Disposition": f'attachment; filename="{ context["object"].name }-commandes"'
},
)
writer = csv.writer(response)
# write headers rows
row = ["", ""]
for item in context["items"]:
row.append(item.name)
row.append("Prix de la commande")
row.append("Mail")
row.append("Téléphone")
writer.writerow(row)
row = ["", "Prix unitaire TTC (€)"]
for item in context["items"]:
row.append(str(item.price).replace(".", ","))
writer.writerow(row)
row = ["Nom", "Prénom"]
writer.writerow(row)
# write ordered values rows
for order, ordered_items in context["orders_dict"].items():
row = [order.author.last_name]
row.append(order.author.first_name)
for ordered_nb in ordered_items:
row.append(ordered_nb)
row.append(str(order.price).replace(".", ","))
row.append(order.author.email)
row.append(f"'{order.author.phone}")
writer.writerow(row)
# write total row
row = ["", "TOTAL"]
for item in context["items"]:
row.append(item.ordered_nb)
row.append(str(context["object"].total_price).replace(".", ","))
writer.writerow(row)
return response