Commit 40832bb3 authored by Sli's avatar Sli

Merge branch 'clubs' into 'master'

Improve Sellings view for clubs

See merge request !262
parents ad8bcc72 4a78157f
Pipeline #2164 passed with stage
in 34 minutes and 52 seconds
...@@ -157,7 +157,7 @@ class MailingForm(forms.Form): ...@@ -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): ...@@ -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):
""" """
......
{% 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 @@ ...@@ -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 @@ ...@@ -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 %}
......
...@@ -23,6 +23,7 @@ ...@@ -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 ...@@ -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 ( ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -371,16 +411,46 @@ class ClubSellingCSVView(ClubSellingView):
Generate sellings in csv for a given period Generate sellings in csv for a given period
""" """
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): def get(self, request, *args, **kwargs):
import csv
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): ...@@ -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
......
...@@ -113,10 +113,11 @@ ...@@ -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 @@ ...@@ -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 %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment