from django import http from django.contrib.auth.mixins import UserPassesTestMixin from django.core.exceptions import SuspiciousOperation from django.shortcuts import get_object_or_404, render from django.urls import reverse, reverse_lazy from django.utils import timezone from django.views import generic from la_chariotte.mail.utils import send_order_confirmation_mail from ..models import GroupedOrder, Order, OrderAuthor, OrderedItem, Place def place_order(request, code): # Creates an AnonymousUser and an Order with related OrderedItems grouped_order = get_object_or_404(GroupedOrder, code=code) # Handle permissions user_is_orga = request.user == grouped_order.orga is_to_be_delivered = grouped_order.is_to_be_delivered() is_open = grouped_order.is_open() access_allowed = is_open or (user_is_orga and is_to_be_delivered) if not access_allowed: return http.HttpResponseForbidden() # get a dict with quantity_{{item_id}}:[ {{ quantity on phone }},{{ quantity on desktop }} ] orders_dict = { key: request.POST.getlist(key) for key, value in request.POST.items() if key.startswith("quantity") } # transform it into quantity_{{item_id}}: {{ greatest of the two quantities }} for i in orders_dict: value = ( orders_dict[i][0] if orders_dict[i][0] >= orders_dict[i][1] else orders_dict[i][1] ) orders_dict[i] = value # create an order first_name = request.POST["first_name"] last_name = request.POST["last_name"] phone = request.POST["phone"] email = request.POST["email"] note = request.POST["note"] error_message = None author = OrderAuthor.objects.create( first_name=first_name, last_name=last_name, email=email, phone=phone ) # Make sure requested place is valid in this group order # Query grouped order places only once to avoid many DB roundtrips grouped_order_places = grouped_order.places.all() place = None if len(grouped_order_places) > 0: # Places are enabled for this grouped order, make sure one was supplied if "place" not in request.POST or request.POST["place"] == "": error_message = ( "Aucun lieu n'est spécifié alors que la commande groupée en exige un" ) else: place = request.POST["place"] try: # QuerySet is already queried from DB so this is a cheap operation. # Could fail if no entry is found, or multiple entries are found place = grouped_order_places.get(code=place) except Exception as e: # The requested place, whether it exists or not, # is not enabled for this GroupedOrder error_message = ( "Le lieu demandé n'est pas valide pour cette commande: %s" % place ) if error_message: author.delete() return render( request, "order/grouped_order_detail.html", { "grouped_order": grouped_order, "error_message": error_message, "note": note, "author": author, "place": place, }, ) order = Order.objects.create( author=author, grouped_order=grouped_order, note=note, created_date=timezone.now(), place=place, ) # add items to the order error_message = None for key, quantity in orders_dict.items(): quantity = int(quantity) item = grouped_order.item_set.get(pk=key.split("_")[1]) # check if too many items are ordered error_message = validate_item_ordered_nb(item, quantity) if error_message: break # stop creating items if there is an error if quantity > 0: OrderedItem.objects.create(nb=quantity, order=order, item=item) # Redisplay the form with error messages if there is an error if error_message: order.delete() author.delete() return render( request, "order/grouped_order_detail.html", { "grouped_order": grouped_order, "error_message": error_message, "note": order.note, "author": author, "place": place, }, ) # check if the order contains articles error_message = validate_articles_ordered_nb(order) # Redisplay the form with error messages if there is an error if error_message: order.delete() author.delete() return render( request, "order/grouped_order_detail.html", { "grouped_order": grouped_order, "error_message": error_message, "note": order.note, "author": author, "place": place, }, ) # Send confirmation mail and redirect to confirmation page send_order_confirmation_mail(order) # Redirect to prevent data from being posted twice when the user hits the Back # button. return http.HttpResponseRedirect( reverse("order:order_confirm", args=(grouped_order.code, order.pk)) ) def validate_item_ordered_nb(item, ordered_nb): """Returns an error message if the ordered items are not available""" if item.get_remaining_nb() is not None and item.get_remaining_nb() - ordered_nb < 0: return f"Trop de {item.name} commandés pour la quantité disponible" return None def validate_articles_ordered_nb(order): """Return an error if no items are ordered""" if order.articles_nb == 0: return "Veuillez commander au moins un produit" return None class OrderDetailView(generic.DetailView): model = Order class OrderDeleteView(UserPassesTestMixin, generic.DeleteView): model = Order def get_success_url(self): return reverse_lazy( "order:grouped_order_overview", args=[self.object.grouped_order.code] ) def test_func(self): # Restrict access to the manager or a superuser return ( self.get_object().grouped_order.orga == self.request.user or self.request.user.is_superuser )