diff --git a/kfet/templates/kfet/base_nav.html b/kfet/templates/kfet/base_nav.html
index 1cded20b..d5624197 100644
--- a/kfet/templates/kfet/base_nav.html
+++ b/kfet/templates/kfet/base_nav.html
@@ -88,6 +88,7 @@
Articles
Inventaires
Commandes
+ Export des ventes
{% if user.username != 'kfet_genericteam' %}
diff --git a/kfet/urls.py b/kfet/urls.py
index 7a9498ed..0982a46e 100644
--- a/kfet/urls.py
+++ b/kfet/urls.py
@@ -236,6 +236,15 @@ urlpatterns = [
"k-psul/get_settings", views.kpsul_get_settings, name="kfet.kpsul.get_settings"
),
# -----
+ # Sales history urls
+ # -----
+ path("purchases", views.purchases_csv, name="kfet.purchases"),
+ path(
+ "purchases/-/-",
+ views.purchases_csv,
+ name="kfet.purchases",
+ ),
+ # -----
# JSON urls
# -----
path("history.json", views.history_json, name="kfet.history.json"),
diff --git a/kfet/views.py b/kfet/views.py
index 0d9f9544..78de56cc 100644
--- a/kfet/views.py
+++ b/kfet/views.py
@@ -1,7 +1,8 @@
+import csv
import heapq
import statistics
from collections import defaultdict
-from datetime import datetime, timedelta
+from datetime import date, datetime, timedelta
from decimal import Decimal
from typing import List
from urllib.parse import urlencode
@@ -2451,6 +2452,85 @@ class ScaleMixin(object):
return {"labels": self.scale.get_labels()}
+# ------------------------
+# Export des ventes en CSV
+# ------------------------
+
+
+@teamkfet_required
+def purchases_csv(request, st_y=None, st_m=None, en_y=None, en_m=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 (st_y and st_m and en_y and en_m) 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)
+ st_y = start.year
+ st_m = start.month
+ en_y = end.year
+ en_m = end.month
+
+ # On calcule la liste des premiers jours des mois dont on veut avoir les
+ # ventes
+ dates = []
+
+ for year in range(st_y, en_y + 1):
+ m_st = st_m if year == st_y else 1
+ m_en = en_m if year == en_y else 12
+ for month in range(m_st, m_en + 1):
+ dates.append(date(year, month, 1))
+
+ # On rajoute le mois suivant
+ dates.append((dates[-1] + timedelta(days=31)).replace(day=1))
+
+ # On récupère les données voulues
+ articles_list = list(Article.objects.order_by("name"))
+ nb_articles = len(articles_list)
+ articles = {}
+ for i in range(nb_articles):
+ articles[articles_list[i].name] = i + 1
+
+ ventes = (
+ 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
+ response = HttpResponse(content_type="text/csv")
+ response["Content-Disposition"] = 'attachment; filename="historique_ventes.csv"'
+ writer = csv.writer(response)
+
+ # On met un article par colonne
+ header = ["Mois"] + [a for a in articles]
+ writer.writerow(header)
+
+ # On calcule la consommation par mois sur la période donnée
+ 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
+ last_row[articles[v.article.name]] += v.article_nb
+
+ writer.writerow(row)
+ writer.writerow(last_row)
+
+ return response
+
+
# -----------------------
# Evolution Balance perso
# -----------------------