On utilise Scale
This commit is contained in:
parent
cecbb583c6
commit
ad8c3143b6
2 changed files with 49 additions and 72 deletions
|
@ -238,12 +238,7 @@ urlpatterns = [
|
||||||
# -----
|
# -----
|
||||||
# Sales history urls
|
# Sales history urls
|
||||||
# -----
|
# -----
|
||||||
path("purchases", views.purchases_csv, name="kfet.purchases"),
|
path("purchases", views.SalesStatList.as_view(), name="kfet.purchases"),
|
||||||
path(
|
|
||||||
"purchases/<int:start_year>-<int:start_month>/<int:end_year>-<int:end_month>",
|
|
||||||
views.purchases_csv,
|
|
||||||
name="kfet.purchases",
|
|
||||||
),
|
|
||||||
# -----
|
# -----
|
||||||
# JSON urls
|
# JSON urls
|
||||||
# -----
|
# -----
|
||||||
|
|
114
kfet/views.py
114
kfet/views.py
|
@ -2,7 +2,7 @@ import csv
|
||||||
import heapq
|
import heapq
|
||||||
import statistics
|
import statistics
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import List
|
from typing import List
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
@ -13,12 +13,13 @@ from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
from django.contrib.auth.models import Permission, User
|
from django.contrib.auth.models import Permission, User
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.core.exceptions import SuspiciousOperation, ValidationError
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Count, F, Max, OuterRef, Prefetch, Q, Subquery, Sum
|
from django.db.models import Count, F, Max, OuterRef, Prefetch, Q, Subquery, Sum
|
||||||
from django.forms import ValidationError, formset_factory
|
from django.forms import ValidationError, formset_factory
|
||||||
from django.http import (
|
from django.http import (
|
||||||
Http404,
|
Http404,
|
||||||
|
HttpResponse,
|
||||||
HttpResponseBadRequest,
|
HttpResponseBadRequest,
|
||||||
HttpResponseForbidden,
|
HttpResponseForbidden,
|
||||||
JsonResponse,
|
JsonResponse,
|
||||||
|
@ -30,6 +31,7 @@ from django.utils.decorators import method_decorator
|
||||||
from django.views.generic import DetailView, FormView, ListView, TemplateView
|
from django.views.generic import DetailView, FormView, ListView, TemplateView
|
||||||
from django.views.generic.detail import BaseDetailView
|
from django.views.generic.detail import BaseDetailView
|
||||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||||
|
from django.views.generic.list import BaseListView
|
||||||
|
|
||||||
from gestioncof.models import CofProfile
|
from gestioncof.models import CofProfile
|
||||||
from kfet import KFET_DELETED_TRIGRAMME, consumers
|
from kfet import KFET_DELETED_TRIGRAMME, consumers
|
||||||
|
@ -2456,81 +2458,61 @@ class ScaleMixin(object):
|
||||||
# Export des ventes en CSV
|
# Export des ventes en CSV
|
||||||
# ------------------------
|
# ------------------------
|
||||||
|
|
||||||
|
MONTHS = (
|
||||||
|
"_Janvier_Février_Mars_Avril_Mai_Juin_Juillet_Août_Septembre_Octobre"
|
||||||
|
"_Novembre_Décembre"
|
||||||
|
).split("_")
|
||||||
|
|
||||||
@teamkfet_required
|
|
||||||
def purchases_csv(
|
|
||||||
request, start_year=None, start_month=None, end_year=None, end_month=None
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Renvoie un historique des achats en K-Fêt, par mois et sur une période donnée
|
|
||||||
(Par défault les 12 mois précédents)
|
|
||||||
"""
|
|
||||||
# Si on ne spécifie pas tout l'intervalle, on prend les 12 derniers mois
|
|
||||||
if (start_year and start_month and end_year and end_month) is None:
|
|
||||||
# On utilise replace pour gérer le cas des années bissextiles sans
|
|
||||||
# changer de mois
|
|
||||||
end = date.today().replace(day=1)
|
|
||||||
start = end - timedelta(days=365)
|
|
||||||
start_year = start.year
|
|
||||||
start_month = start.month
|
|
||||||
end_year = end.year
|
|
||||||
end_month = end.month
|
|
||||||
|
|
||||||
# On calcule la liste des premiers jours des mois dont on veut avoir les
|
@method_decorator(teamkfet_required, name="dispatch")
|
||||||
# ventes
|
class SalesStatList(BaseListView):
|
||||||
dates = []
|
model = Operation
|
||||||
|
|
||||||
for year in range(start_year, end_year + 1):
|
def get_queryset(self):
|
||||||
month_start = start_month if year == start_year else 1
|
return super().get_queryset().filter(type=Operation.PURCHASE, canceled_at=None)
|
||||||
month_end = end_month if year == end_year else 12
|
|
||||||
for month in range(month_start, month_end + 1):
|
|
||||||
dates.append(date(year, month, 1))
|
|
||||||
|
|
||||||
# On rajoute le mois suivant
|
def get_context_data(self, **kwargs):
|
||||||
dates.append((dates[-1] + timedelta(days=31)).replace(day=1))
|
ctx = super().get_context_data(**kwargs)
|
||||||
|
scale = MonthScale(n_steps=12, last=True)
|
||||||
# On récupère les données voulues
|
qs = self.get_queryset()
|
||||||
articles_list = list(Article.objects.order_by("name"))
|
articles = list(Article.objects.order_by("name"))
|
||||||
nb_articles = len(articles_list)
|
indexes = {a.name: index for index, a in enumerate(articles)}
|
||||||
articles = {}
|
ventes = scale.chunkify_qs(
|
||||||
for i in range(nb_articles):
|
qs.values_list(
|
||||||
articles[articles_list[i].name] = i + 1
|
"article__name", "group__at__month", "group__at__year"
|
||||||
|
).annotate(Sum("article_nb")),
|
||||||
ventes = (
|
field="group__at",
|
||||||
Operation.objects.filter(
|
|
||||||
type="purchase", canceled_by=None, group__at__range=[dates[0], dates[-1]]
|
|
||||||
)
|
)
|
||||||
.exclude(article=None)
|
|
||||||
.order_by("group__at")
|
|
||||||
.select_related("group", "article")
|
|
||||||
)
|
|
||||||
|
|
||||||
# On crée le fichier CSV
|
ctx["header"] = ["Mois"] + [a.name for a in articles]
|
||||||
response = HttpResponse(content_type="text/csv")
|
ctx["ventes"] = []
|
||||||
response["Content-Disposition"] = 'attachment; filename="historique_ventes.csv"'
|
ctx["total"] = [0] * len(articles)
|
||||||
writer = csv.writer(response)
|
for v in ventes:
|
||||||
|
m_ventes = [0] * len(articles)
|
||||||
|
for (a_name, month, year, nb_ventes) in v:
|
||||||
|
index = indexes[a_name]
|
||||||
|
m_ventes[index] = nb_ventes
|
||||||
|
ctx["total"][index] += nb_ventes
|
||||||
|
|
||||||
# On met un article par colonne
|
ctx["ventes"].append(["{} {}".format(MONTHS[month], year)] + m_ventes)
|
||||||
header = ["Mois"] + [a for a in articles]
|
|
||||||
writer.writerow(header)
|
|
||||||
|
|
||||||
# On calcule la consommation par mois sur la période donnée
|
return ctx
|
||||||
i_m = 0
|
|
||||||
row = [f"{dates[i_m].month:02d}/{dates[i_m].year}"] + [0] * nb_articles
|
|
||||||
last_row = ["Total"] + [0] * nb_articles
|
|
||||||
for v in ventes:
|
|
||||||
while v.group.at.date() > dates[i_m + 1]:
|
|
||||||
writer.writerow(row)
|
|
||||||
i_m += 1
|
|
||||||
row = [f"{dates[i_m].month:02d}/{dates[i_m].year}"] + [0] * nb_articles
|
|
||||||
|
|
||||||
row[articles[v.article.name]] += v.article_nb
|
def render_to_response(self, context):
|
||||||
last_row[articles[v.article.name]] += v.article_nb
|
response = HttpResponse(content_type="text/csv")
|
||||||
|
response["Content-Disposition"] = 'attachment; filename="historique_ventes.csv"'
|
||||||
|
|
||||||
writer.writerow(row)
|
writer = csv.writer(response)
|
||||||
writer.writerow(last_row)
|
writer.writerow(context["header"])
|
||||||
|
|
||||||
return response
|
# On écrit chaque mois
|
||||||
|
for line in context["ventes"]:
|
||||||
|
writer.writerow(line)
|
||||||
|
|
||||||
|
writer.writerow([])
|
||||||
|
writer.writerow(["Total"] + context["total"])
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
# -----------------------
|
# -----------------------
|
||||||
|
|
Loading…
Reference in a new issue