mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-22 22:23:23 +00:00
Merge branch 'clubs' into 'master'
Improve Sellings view for clubs See merge request ae/Sith!262
This commit is contained in:
commit
40832bb3bf
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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 %}
|
||||||
|
|
||||||
|
|
||||||
|
164
club/views.py
164
club/views.py
@ -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
|
||||||
|
|
||||||
|
@ -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 %}
|
||||||
|
Loading…
Reference in New Issue
Block a user