mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 09:03:06 +00:00 
			
		
		
		
	Merge branch 'clubs' into 'master'
Improve Sellings view for clubs See merge request ae/Sith!262
This commit is contained in:
		| @@ -157,7 +157,7 @@ class MailingForm(forms.Form): | ||||
|         return cleaned_data | ||||
|  | ||||
|  | ||||
| class SellingsFormBase(forms.Form): | ||||
| class SellingsForm(forms.Form): | ||||
|     begin_date = forms.DateTimeField( | ||||
|         input_formats=["%Y-%m-%d %H:%M:%S"], | ||||
|         label=_("Begin date"), | ||||
| @@ -170,10 +170,24 @@ class SellingsFormBase(forms.Form): | ||||
|         required=False, | ||||
|         widget=SelectDateTime, | ||||
|     ) | ||||
|     counter = forms.ModelChoiceField( | ||||
|     counters = forms.ModelMultipleChoiceField( | ||||
|         Counter.objects.order_by("name").all(), label=_("Counter"), required=False | ||||
|     ) | ||||
|  | ||||
|     def __init__(self, club, *args, **kwargs): | ||||
|  | ||||
|         super(SellingsForm, self).__init__(*args, **kwargs) | ||||
|         self.fields["products"] = forms.ModelMultipleChoiceField( | ||||
|             club.products.order_by("name").filter(archived=False).all(), | ||||
|             label=_("Products"), | ||||
|             required=False, | ||||
|         ) | ||||
|         self.fields["archived_products"] = forms.ModelMultipleChoiceField( | ||||
|             club.products.order_by("name").filter(archived=True).all(), | ||||
|             label=_("Archived products"), | ||||
|             required=False, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class ClubMemberForm(forms.Form): | ||||
|     """ | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| {% extends "core/base.jinja" %} | ||||
| {% from 'core/macros.jinja' import user_profile_link %} | ||||
| {% from 'core/macros.jinja' import user_profile_link, paginate %} | ||||
|  | ||||
| {% block content %} | ||||
| <h3>{% trans %}Sellings{% endtrans %}</h3> | ||||
| <form action="" method="get"> | ||||
| <form id="form" action="?page=1" method="post"> | ||||
|     {% csrf_token %} | ||||
|     {{ form }} | ||||
|     <p><input type="submit" value="{% trans %}Show{% endtrans %}" /></p> | ||||
| @@ -28,7 +28,7 @@ | ||||
|         </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for s in result %} | ||||
|     {% for s in paginated_result %} | ||||
|         <tr> | ||||
|             <td>{{ s.date|localtime|date(DATETIME_FORMAT) }} {{ s.date|localtime|time(DATETIME_FORMAT) }}</td> | ||||
|             <td>{{ s.counter }}</td> | ||||
| @@ -53,6 +53,14 @@ | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
| </table> | ||||
| <script type="text/javascript"> | ||||
|     function formPagination(link){ | ||||
|         $("form").attr("action", link.href); | ||||
|         link.href = "javascript:void(0)"; // block link action | ||||
|         $("form").submit(); | ||||
|     } | ||||
| </script> | ||||
| {{ paginate(paginated_result, paginator, "formPagination(this)") }} | ||||
| {% endblock %} | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										164
									
								
								club/views.py
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								club/views.py
									
									
									
									
									
								
							| @@ -23,6 +23,7 @@ | ||||
| # | ||||
| # | ||||
|  | ||||
| import csv | ||||
|  | ||||
| from django.conf import settings | ||||
| from django import forms | ||||
| @@ -30,13 +31,21 @@ from django.views.generic import ListView, DetailView, TemplateView, View | ||||
| from django.views.generic.edit import DeleteView | ||||
| from django.views.generic.detail import SingleObjectMixin | ||||
| from django.views.generic.edit import UpdateView, CreateView | ||||
| from django.http import HttpResponseRedirect, HttpResponse, Http404 | ||||
| from django.http import ( | ||||
|     HttpResponseRedirect, | ||||
|     HttpResponse, | ||||
|     Http404, | ||||
|     StreamingHttpResponse, | ||||
| ) | ||||
| from django.urls import reverse, reverse_lazy | ||||
| from django.utils import timezone | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
| from django.utils.translation import ugettext as _t | ||||
| from django.core.exceptions import PermissionDenied, ValidationError, NON_FIELD_ERRORS | ||||
| from django.core.paginator import Paginator, InvalidPage | ||||
| from django.shortcuts import get_object_or_404, redirect | ||||
| from django.db.models import Sum | ||||
|  | ||||
|  | ||||
| from core.views import ( | ||||
|     CanCreateMixin, | ||||
| @@ -60,7 +69,7 @@ from com.views import ( | ||||
| ) | ||||
|  | ||||
| from club.models import Club, Membership, Mailing, MailingSubscription | ||||
| from club.forms import MailingForm, ClubEditForm, ClubMemberForm, SellingsFormBase | ||||
| from club.forms import MailingForm, ClubEditForm, ClubMemberForm, SellingsForm | ||||
|  | ||||
|  | ||||
| class ClubTabsMixin(TabedViewMixin): | ||||
| @@ -319,7 +328,7 @@ class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView): | ||||
|     current_tab = "elderlies" | ||||
|  | ||||
|  | ||||
| class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView): | ||||
| class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailFormView): | ||||
|     """ | ||||
|     Sellings of a club | ||||
|     """ | ||||
| @@ -328,21 +337,35 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView): | ||||
|     pk_url_kwarg = "club_id" | ||||
|     template_name = "club/club_sellings.jinja" | ||||
|     current_tab = "sellings" | ||||
|     form_class = SellingsForm | ||||
|     paginate_by = 70 | ||||
|  | ||||
|     def get_form_class(self): | ||||
|         kwargs = { | ||||
|             "product": forms.ModelChoiceField( | ||||
|                 self.object.products.order_by("name").all(), | ||||
|                 label=_("Product"), | ||||
|                 required=False, | ||||
|             ) | ||||
|         } | ||||
|         return type("SellingsForm", (SellingsFormBase,), kwargs) | ||||
|     def dispatch(self, request, *args, **kwargs): | ||||
|         try: | ||||
|             self.asked_page = int(request.GET.get("page", 1)) | ||||
|         except ValueError: | ||||
|             raise Http404 | ||||
|         return super(ClubSellingView, self).dispatch(request, *args, **kwargs) | ||||
|  | ||||
|     def get_form_kwargs(self): | ||||
|         kwargs = super(ClubSellingView, self).get_form_kwargs() | ||||
|         kwargs["club"] = self.object | ||||
|         return kwargs | ||||
|  | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         return self.get(request, *args, **kwargs) | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|         kwargs = super(ClubSellingView, self).get_context_data(**kwargs) | ||||
|         form = self.get_form_class()(self.request.GET) | ||||
|         qs = Selling.objects.filter(club=self.object) | ||||
|  | ||||
|         kwargs["result"] = qs[:0] | ||||
|         kwargs["paginated_result"] = kwargs["result"] | ||||
|         kwargs["total"] = 0 | ||||
|         kwargs["total_quantity"] = 0 | ||||
|         kwargs["benefit"] = 0 | ||||
|  | ||||
|         form = self.get_form() | ||||
|         if form.is_valid(): | ||||
|             if not len([v for v in form.cleaned_data.values() if v is not None]): | ||||
|                 qs = Selling.objects.filter(id=-1) | ||||
| @@ -350,19 +373,36 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView): | ||||
|                 qs = qs.filter(date__gte=form.cleaned_data["begin_date"]) | ||||
|             if form.cleaned_data["end_date"]: | ||||
|                 qs = qs.filter(date__lte=form.cleaned_data["end_date"]) | ||||
|             if form.cleaned_data["counter"]: | ||||
|                 qs = qs.filter(counter=form.cleaned_data["counter"]) | ||||
|             if form.cleaned_data["product"]: | ||||
|                 qs = qs.filter(product__id=form.cleaned_data["product"].id) | ||||
|  | ||||
|             if form.cleaned_data["counters"]: | ||||
|                 qs = qs.filter(counter__in=form.cleaned_data["counters"]) | ||||
|  | ||||
|             selected_products = [] | ||||
|             if form.cleaned_data["products"]: | ||||
|                 selected_products.extend(form.cleaned_data["products"]) | ||||
|             if form.cleaned_data["archived_products"]: | ||||
|                 selected_products.extend(form.cleaned_data["archived_products"]) | ||||
|  | ||||
|             if len(selected_products) > 0: | ||||
|                 qs = qs.filter(product__in=selected_products) | ||||
|  | ||||
|             kwargs["result"] = qs.all().order_by("-id") | ||||
|             kwargs["total"] = sum([s.quantity * s.unit_price for s in qs.all()]) | ||||
|             kwargs["total_quantity"] = sum([s.quantity for s in qs.all()]) | ||||
|             kwargs["benefit"] = kwargs["total"] - sum( | ||||
|                 [s.product.purchase_price for s in qs.exclude(product=None)] | ||||
|             kwargs["total"] = sum([s.quantity * s.unit_price for s in kwargs["result"]]) | ||||
|             total_quantity = qs.all().aggregate(Sum("quantity")) | ||||
|             if total_quantity["quantity__sum"]: | ||||
|                 kwargs["total_quantity"] = total_quantity["quantity__sum"] | ||||
|             benefit = ( | ||||
|                 qs.exclude(product=None).all().aggregate(Sum("product__purchase_price")) | ||||
|             ) | ||||
|         else: | ||||
|             kwargs["result"] = qs[:0] | ||||
|         kwargs["form"] = form | ||||
|             if benefit["product__purchase_price__sum"]: | ||||
|                 kwargs["benefit"] = benefit["product__purchase_price__sum"] | ||||
|  | ||||
|         kwargs["paginator"] = Paginator(kwargs["result"], self.paginate_by) | ||||
|         try: | ||||
|             kwargs["paginated_result"] = kwargs["paginator"].page(self.asked_page) | ||||
|         except InvalidPage: | ||||
|             raise Http404 | ||||
|  | ||||
|         return kwargs | ||||
|  | ||||
|  | ||||
| @@ -371,16 +411,46 @@ class ClubSellingCSVView(ClubSellingView): | ||||
|     Generate sellings in csv for a given period | ||||
|     """ | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         import csv | ||||
|     class StreamWriter: | ||||
|         """Implements a file-like interface for streaming the CSV""" | ||||
|  | ||||
|         def write(self, value): | ||||
|             """Write the value by returning it, instead of storing in a buffer.""" | ||||
|             return value | ||||
|  | ||||
|     def write_selling(self, selling): | ||||
|         row = [selling.date, selling.counter] | ||||
|         if selling.seller: | ||||
|             row.append(selling.seller.get_display_name()) | ||||
|         else: | ||||
|             row.append("") | ||||
|         if selling.customer: | ||||
|             row.append(selling.customer.user.get_display_name()) | ||||
|         else: | ||||
|             row.append("") | ||||
|         row = row + [ | ||||
|             selling.label, | ||||
|             selling.quantity, | ||||
|             selling.quantity * selling.unit_price, | ||||
|             selling.get_payment_method_display(), | ||||
|         ] | ||||
|         if selling.product: | ||||
|             row.append(selling.product.selling_price) | ||||
|             row.append(selling.product.purchase_price) | ||||
|             row.append(selling.product.selling_price - selling.product.purchase_price) | ||||
|         else: | ||||
|             row = row + ["", "", ""] | ||||
|         return row | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|  | ||||
|         response = HttpResponse(content_type="text/csv") | ||||
|         self.object = self.get_object() | ||||
|         name = _("Sellings") + "_" + self.object.name + ".csv" | ||||
|         response["Content-Disposition"] = "filename=" + name | ||||
|         kwargs = self.get_context_data(**kwargs) | ||||
|  | ||||
|         # Use the StreamWriter class instead of request for streaming | ||||
|         pseudo_buffer = self.StreamWriter() | ||||
|         writer = csv.writer( | ||||
|             response, delimiter=";", lineterminator="\n", quoting=csv.QUOTE_ALL | ||||
|             pseudo_buffer, delimiter=";", lineterminator="\n", quoting=csv.QUOTE_ALL | ||||
|         ) | ||||
|  | ||||
|         writer.writerow([_t("Quantity"), kwargs["total_quantity"]]) | ||||
| @@ -401,29 +471,17 @@ class ClubSellingCSVView(ClubSellingView): | ||||
|                 _t("Benefit"), | ||||
|             ] | ||||
|         ) | ||||
|         for o in kwargs["result"]: | ||||
|             row = [o.date, o.counter] | ||||
|             if o.seller: | ||||
|                 row.append(o.seller.get_display_name()) | ||||
|             else: | ||||
|                 row.append("") | ||||
|             if o.customer: | ||||
|                 row.append(o.customer.user.get_display_name()) | ||||
|             else: | ||||
|                 row.append("") | ||||
|             row = row + [ | ||||
|                 o.label, | ||||
|                 o.quantity, | ||||
|                 o.quantity * o.unit_price, | ||||
|                 o.get_payment_method_display(), | ||||
|             ] | ||||
|             if o.product: | ||||
|                 row.append(o.product.selling_price) | ||||
|                 row.append(o.product.purchase_price) | ||||
|                 row.append(o.product.selling_price - o.product.purchase_price) | ||||
|             else: | ||||
|                 row = row + ["", "", ""] | ||||
|             writer.writerow(row) | ||||
|  | ||||
|         # Stream response | ||||
|         response = StreamingHttpResponse( | ||||
|             ( | ||||
|                 writer.writerow(self.write_selling(selling)) | ||||
|                 for selling in kwargs["result"] | ||||
|             ), | ||||
|             content_type="text/csv", | ||||
|         ) | ||||
|         name = _("Sellings") + "_" + self.object.name + ".csv" | ||||
|         response["Content-Disposition"] = "filename=" + name | ||||
|  | ||||
|         return response | ||||
|  | ||||
|   | ||||
| @@ -113,10 +113,11 @@ | ||||
|     {% endif %} | ||||
| {% endmacro %} | ||||
|  | ||||
| {% macro paginate(page_obj, paginator) %} | ||||
| {% macro paginate(page_obj, paginator, js_action) %} | ||||
|     {% set js = js_action|default('') %} | ||||
|     {% if page_obj.has_previous() or page_obj.has_next() %} | ||||
|         {% if page_obj.has_previous() %} | ||||
|             <a href="?page={{ page_obj.previous_page_number() }}">{% trans %}Previous{% endtrans %}</a> | ||||
|             <a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ page_obj.previous_page_number() }}">{% trans %}Previous{% endtrans %}</a> | ||||
|         {% else %} | ||||
|             <span class="disabled">{% trans %}Previous{% endtrans %}</span> | ||||
|         {% endif %} | ||||
| @@ -124,11 +125,11 @@ | ||||
|             {% if page_obj.number == i %} | ||||
|                 <span class="active">{{ i }} <span class="sr-only">({% trans %}current{% endtrans %})</span></span> | ||||
|             {% else %} | ||||
|                 <a href="?page={{ i }}">{{ i }}</a> | ||||
|                 <a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ i }}">{{ i }}</a> | ||||
|             {% endif %} | ||||
|         {% endfor %} | ||||
|         {% if page_obj.has_next() %} | ||||
|             <a href="?page={{ page_obj.next_page_number() }}">{% trans %}Next{% endtrans %}</a> | ||||
|             <a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ page_obj.next_page_number() }}">{% trans %}Next{% endtrans %}</a> | ||||
|         {% else %} | ||||
|             <span class="disabled">{% trans %}Next{% endtrans %}</span> | ||||
|         {% endif %} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user