diff --git a/CHANGELOG.md b/CHANGELOG.md index b35d8e73..8a87eccc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,9 +21,12 @@ Liste des changements notables dans GestioCOF depuis la version 0.1 (septembre Uniquement un modèle simple de clubs avec des respos. Aucune gestion des adhérents ni des cotisations. -## Version ??? - le futur +## Version ??? - dans un futur proche -... +### K-Fêt + +- On peut supprimer un inventaire. Seuls les articles dont c'est le dernier + inventaire sont affectés. ## Version 0.8 - 03/12/2020 diff --git a/kfet/templates/kfet/inventory_read.html b/kfet/templates/kfet/inventory_read.html index 964e81b0..9a9275f8 100644 --- a/kfet/templates/kfet/inventory_read.html +++ b/kfet/templates/kfet/inventory_read.html @@ -19,6 +19,17 @@ {% endif %} + {% if perms.kfet.delete_inventory %} +
+
+ +
+ {% csrf_token %} +
+ {% endif %} +
@@ -64,4 +75,29 @@ + + {% endblock %} diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index 47382aa1..ecd7131e 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -4579,6 +4579,87 @@ class InventoryReadViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) +class InventoryDeleteViewTests(ViewTestCaseMixin, TestCase): + url_name = "kfet.inventory.delete" + + auth_user = "team1" + auth_forbidden = [None, "user", "team"] + + def get_users_extra(self): + return { + "user1": create_user("user1", "001"), + "team1": create_team("team1", "101", perms=["kfet.delete_inventory"]), + } + + @property + def url_kwargs(self): + return {"pk": self.inventory1.pk} + + @property + def url_expected(self): + return "/k-fet/inventaires/{}/delete".format(self.inventory1.pk) + + def setUp(self): + super().setUp() + # Deux inventaires : un avec article 1 + 2, l'autre avec 1 + 3 + self.inventory1 = Inventory.objects.create( + by=self.accounts["team"], at=self.now + ) + self.inventory2 = Inventory.objects.create( + by=self.accounts["team"], at=self.now + timedelta(days=1) + ) + category = ArticleCategory.objects.create(name="Category") + # Le stock des articles correspond à leur dernier inventaire + self.article1 = Article.objects.create( + name="Article1", category=category, stock=51 + ) + self.article2 = Article.objects.create( + name="Article2", category=category, stock=42 + ) + self.article3 = Article.objects.create( + name="Article3", category=category, stock=42 + ) + + InventoryArticle.objects.create( + inventory=self.inventory1, + article=self.article1, + stock_old=23, + stock_new=42, + ) + InventoryArticle.objects.create( + inventory=self.inventory1, + article=self.article2, + stock_old=23, + stock_new=42, + ) + InventoryArticle.objects.create( + inventory=self.inventory2, + article=self.article1, + stock_old=42, + stock_new=51, + ) + InventoryArticle.objects.create( + inventory=self.inventory2, + article=self.article3, + stock_old=23, + stock_new=42, + ) + + def test_ok(self): + r = self.client.post(self.url) + self.assertRedirects(r, reverse("kfet.inventory")) + + # On vérifie que l'inventaire n'existe plus + self.assertFalse(Inventory.objects.filter(pk=self.inventory1.pk).exists()) + # On check les stocks + self.article1.refresh_from_db() + self.article2.refresh_from_db() + self.article3.refresh_from_db() + self.assertEqual(self.article1.stock, 51) + self.assertEqual(self.article2.stock, 23) + self.assertEqual(self.article3.stock, 42) + + class OrderListViewTests(ViewTestCaseMixin, TestCase): url_name = "kfet.order" url_expected = "/k-fet/orders/" diff --git a/kfet/urls.py b/kfet/urls.py index 2548e77e..7a9498ed 100644 --- a/kfet/urls.py +++ b/kfet/urls.py @@ -270,6 +270,11 @@ urlpatterns = [ teamkfet_required(views.InventoryRead.as_view()), name="kfet.inventory.read", ), + path( + "inventaires//delete", + views.InventoryDelete.as_view(), + name="kfet.inventory.delete", + ), # ----- # Order urls # ----- diff --git a/kfet/views.py b/kfet/views.py index a9dd1945..d42c6338 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -13,7 +13,7 @@ from django.contrib.auth.models import Permission, User from django.contrib.messages.views import SuccessMessageMixin from django.core.exceptions import SuspiciousOperation from django.db import transaction -from django.db.models import Count, F, Prefetch, Q, Sum +from django.db.models import Count, F, Max, OuterRef, Prefetch, Q, Subquery, Sum from django.forms import formset_factory from django.http import Http404, HttpResponseBadRequest, JsonResponse from django.shortcuts import get_object_or_404, redirect, render @@ -1909,6 +1909,32 @@ class InventoryRead(DetailView): return context +class InventoryDelete(PermissionRequiredMixin, DeleteView): + model = Inventory + success_url = reverse_lazy("kfet.inventory") + success_message = "Inventaire annulé avec succès !" + permission_required = "kfet.delete_inventory" + + def get(self, request, *args, **kwargs): + return redirect("kfet.inventory.read", self.kwargs.get(self.pk_url_kwarg)) + + def delete(self, request, *args, **kwargs): + inv = self.get_object() + # On met à jour les articles dont c'est le dernier inventaire + # .get() ne marche pas avec OuterRef, donc on utilise .filter() avec [:1] + update_subquery = InventoryArticle.objects.filter( + inventory=inv, article=OuterRef("pk") + ).values("stock_old")[:1] + + Article.objects.annotate(last_env=Max("inventories__at")).filter( + last_env=inv.at + ).update(stock=Subquery(update_subquery)) + + # On a tout mis à jour, on peut delete (avec un message) + messages.success(request, self.success_message) + return super().delete(request, *args, **kwargs) + + # ----- # Order views # -----