forked from DGNum/gestioCOF
Merge branch 'Aufinal/inventory_delete' into 'master'
Permet l'annulation d'un inventaire Closes #251 See merge request klub-dev-ens/gestioCOF!457
This commit is contained in:
commit
72cd55716b
5 changed files with 154 additions and 3 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -19,6 +19,17 @@
|
|||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% if perms.kfet.delete_inventory %}
|
||||
<hr>
|
||||
<div class="buttons">
|
||||
<button class="btn btn-default" id="button-delete">
|
||||
<span class="glyphicon glyphicon-remove"></span><span>Annuler l'inventaire</span>
|
||||
</button>
|
||||
<form method="post" action="{% url 'kfet.inventory.delete' inventory.pk %}" id="inventory-delete-form">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
@ -64,4 +75,29 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$( function() {
|
||||
// Delete button
|
||||
$('#button-delete').click(function() {
|
||||
$.confirm({
|
||||
title: 'Confirmer la suppression',
|
||||
content: `
|
||||
<div class="warning">
|
||||
<span class='glyphicon glyphicon-warning-sign'></span><span>Cette opération est irréversible !</span>
|
||||
<br>
|
||||
<span>N.B. : seuls les articles dont c'est le <b>dernier</b> inventaire en date seront affectés.</span>
|
||||
</div>
|
||||
`,
|
||||
backgroundDismiss: true,
|
||||
animation: 'top',
|
||||
closeAnimation: 'bottom',
|
||||
keyboardEnabled: true,
|
||||
confirm: function() {
|
||||
$('#inventory-delete-form').submit();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -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/"
|
||||
|
|
|
@ -270,6 +270,11 @@ urlpatterns = [
|
|||
teamkfet_required(views.InventoryRead.as_view()),
|
||||
name="kfet.inventory.read",
|
||||
),
|
||||
path(
|
||||
"inventaires/<int:pk>/delete",
|
||||
views.InventoryDelete.as_view(),
|
||||
name="kfet.inventory.delete",
|
||||
),
|
||||
# -----
|
||||
# Order urls
|
||||
# -----
|
||||
|
|
|
@ -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
|
||||
# -----
|
||||
|
|
Loading…
Reference in a new issue