On utilise Scale

This commit is contained in:
Tom Hubrecht 2021-06-26 13:34:14 +02:00
parent cecbb583c6
commit ad8c3143b6
2 changed files with 49 additions and 72 deletions

View file

@ -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
# ----- # -----

View file

@ -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
# ----------------------- # -----------------------