Merge branch 'clubs' into 'master'

Improve Sellings view for clubs

See merge request ae/Sith!262
This commit is contained in:
Antoine Bartuccio 2019-11-29 16:32:18 +01:00
commit 40832bb3bf
4 changed files with 143 additions and 62 deletions

View File

@ -157,7 +157,7 @@ class MailingForm(forms.Form):
return cleaned_data return cleaned_data
class SellingsFormBase(forms.Form): class SellingsForm(forms.Form):
begin_date = forms.DateTimeField( begin_date = forms.DateTimeField(
input_formats=["%Y-%m-%d %H:%M:%S"], input_formats=["%Y-%m-%d %H:%M:%S"],
label=_("Begin date"), label=_("Begin date"),
@ -170,10 +170,24 @@ class SellingsFormBase(forms.Form):
required=False, required=False,
widget=SelectDateTime, widget=SelectDateTime,
) )
counter = forms.ModelChoiceField( counters = forms.ModelMultipleChoiceField(
Counter.objects.order_by("name").all(), label=_("Counter"), required=False 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): class ClubMemberForm(forms.Form):
""" """

View File

@ -1,9 +1,9 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link %} {% from 'core/macros.jinja' import user_profile_link, paginate %}
{% block content %} {% block content %}
<h3>{% trans %}Sellings{% endtrans %}</h3> <h3>{% trans %}Sellings{% endtrans %}</h3>
<form action="" method="get"> <form id="form" action="?page=1" method="post">
{% csrf_token %} {% csrf_token %}
{{ form }} {{ form }}
<p><input type="submit" value="{% trans %}Show{% endtrans %}" /></p> <p><input type="submit" value="{% trans %}Show{% endtrans %}" /></p>
@ -28,7 +28,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for s in result %} {% for s in paginated_result %}
<tr> <tr>
<td>{{ s.date|localtime|date(DATETIME_FORMAT) }} {{ s.date|localtime|time(DATETIME_FORMAT) }}</td> <td>{{ s.date|localtime|date(DATETIME_FORMAT) }} {{ s.date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ s.counter }}</td> <td>{{ s.counter }}</td>
@ -53,6 +53,14 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </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 %} {% endblock %}

View File

@ -23,6 +23,7 @@
# #
# #
import csv
from django.conf import settings from django.conf import settings
from django import forms 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.edit import DeleteView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import UpdateView, CreateView 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.urls import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext as _t from django.utils.translation import ugettext as _t
from django.core.exceptions import PermissionDenied, ValidationError, NON_FIELD_ERRORS 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.shortcuts import get_object_or_404, redirect
from django.db.models import Sum
from core.views import ( from core.views import (
CanCreateMixin, CanCreateMixin,
@ -60,7 +69,7 @@ from com.views import (
) )
from club.models import Club, Membership, Mailing, MailingSubscription 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): class ClubTabsMixin(TabedViewMixin):
@ -319,7 +328,7 @@ class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
current_tab = "elderlies" current_tab = "elderlies"
class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView): class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailFormView):
""" """
Sellings of a club Sellings of a club
""" """
@ -328,21 +337,35 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
pk_url_kwarg = "club_id" pk_url_kwarg = "club_id"
template_name = "club/club_sellings.jinja" template_name = "club/club_sellings.jinja"
current_tab = "sellings" current_tab = "sellings"
form_class = SellingsForm
paginate_by = 70
def get_form_class(self): def dispatch(self, request, *args, **kwargs):
kwargs = { try:
"product": forms.ModelChoiceField( self.asked_page = int(request.GET.get("page", 1))
self.object.products.order_by("name").all(), except ValueError:
label=_("Product"), raise Http404
required=False, return super(ClubSellingView, self).dispatch(request, *args, **kwargs)
)
} def get_form_kwargs(self):
return type("SellingsForm", (SellingsFormBase,), kwargs) 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): def get_context_data(self, **kwargs):
kwargs = super(ClubSellingView, self).get_context_data(**kwargs) kwargs = super(ClubSellingView, self).get_context_data(**kwargs)
form = self.get_form_class()(self.request.GET)
qs = Selling.objects.filter(club=self.object) 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 form.is_valid():
if not len([v for v in form.cleaned_data.values() if v is not None]): if not len([v for v in form.cleaned_data.values() if v is not None]):
qs = Selling.objects.filter(id=-1) 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"]) qs = qs.filter(date__gte=form.cleaned_data["begin_date"])
if form.cleaned_data["end_date"]: if form.cleaned_data["end_date"]:
qs = qs.filter(date__lte=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["counters"]:
if form.cleaned_data["product"]: qs = qs.filter(counter__in=form.cleaned_data["counters"])
qs = qs.filter(product__id=form.cleaned_data["product"].id)
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["result"] = qs.all().order_by("-id")
kwargs["total"] = sum([s.quantity * s.unit_price for s in qs.all()]) kwargs["total"] = sum([s.quantity * s.unit_price for s in kwargs["result"]])
kwargs["total_quantity"] = sum([s.quantity for s in qs.all()]) total_quantity = qs.all().aggregate(Sum("quantity"))
kwargs["benefit"] = kwargs["total"] - sum( if total_quantity["quantity__sum"]:
[s.product.purchase_price for s in qs.exclude(product=None)] kwargs["total_quantity"] = total_quantity["quantity__sum"]
benefit = (
qs.exclude(product=None).all().aggregate(Sum("product__purchase_price"))
) )
else: if benefit["product__purchase_price__sum"]:
kwargs["result"] = qs[:0] kwargs["benefit"] = benefit["product__purchase_price__sum"]
kwargs["form"] = form
kwargs["paginator"] = Paginator(kwargs["result"], self.paginate_by)
try:
kwargs["paginated_result"] = kwargs["paginator"].page(self.asked_page)
except InvalidPage:
raise Http404
return kwargs return kwargs
@ -371,16 +411,46 @@ class ClubSellingCSVView(ClubSellingView):
Generate sellings in csv for a given period Generate sellings in csv for a given period
""" """
def get(self, request, *args, **kwargs): class StreamWriter:
import csv """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() self.object = self.get_object()
name = _("Sellings") + "_" + self.object.name + ".csv"
response["Content-Disposition"] = "filename=" + name
kwargs = self.get_context_data(**kwargs) kwargs = self.get_context_data(**kwargs)
# Use the StreamWriter class instead of request for streaming
pseudo_buffer = self.StreamWriter()
writer = csv.writer( 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"]]) writer.writerow([_t("Quantity"), kwargs["total_quantity"]])
@ -401,29 +471,17 @@ class ClubSellingCSVView(ClubSellingView):
_t("Benefit"), _t("Benefit"),
] ]
) )
for o in kwargs["result"]:
row = [o.date, o.counter] # Stream response
if o.seller: response = StreamingHttpResponse(
row.append(o.seller.get_display_name()) (
else: writer.writerow(self.write_selling(selling))
row.append("") for selling in kwargs["result"]
if o.customer: ),
row.append(o.customer.user.get_display_name()) content_type="text/csv",
else: )
row.append("") name = _("Sellings") + "_" + self.object.name + ".csv"
row = row + [ response["Content-Disposition"] = "filename=" + name
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)
return response return response

View File

@ -113,10 +113,11 @@
{% endif %} {% endif %}
{% endmacro %} {% 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() or page_obj.has_next() %}
{% if page_obj.has_previous() %} {% 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 %} {% else %}
<span class="disabled">{% trans %}Previous{% endtrans %}</span> <span class="disabled">{% trans %}Previous{% endtrans %}</span>
{% endif %} {% endif %}
@ -124,11 +125,11 @@
{% if page_obj.number == i %} {% if page_obj.number == i %}
<span class="active">{{ i }} <span class="sr-only">({% trans %}current{% endtrans %})</span></span> <span class="active">{{ i }} <span class="sr-only">({% trans %}current{% endtrans %})</span></span>
{% else %} {% else %}
<a href="?page={{ i }}">{{ i }}</a> <a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ i }}">{{ i }}</a>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if page_obj.has_next() %} {% 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 %} {% else %}
<span class="disabled">{% trans %}Next{% endtrans %}</span> <span class="disabled">{% trans %}Next{% endtrans %}</span>
{% endif %} {% endif %}