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
|
Uniquement un modèle simple de clubs avec des respos. Aucune gestion des
|
||||||
adhérents ni des cotisations.
|
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
|
## Version 0.8 - 03/12/2020
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,17 @@
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</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>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
@ -64,4 +75,29 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</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 %}
|
{% endblock %}
|
||||||
|
|
|
@ -4579,6 +4579,87 @@ class InventoryReadViewTests(ViewTestCaseMixin, TestCase):
|
||||||
self.assertEqual(r.status_code, 200)
|
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):
|
class OrderListViewTests(ViewTestCaseMixin, TestCase):
|
||||||
url_name = "kfet.order"
|
url_name = "kfet.order"
|
||||||
url_expected = "/k-fet/orders/"
|
url_expected = "/k-fet/orders/"
|
||||||
|
|
|
@ -270,6 +270,11 @@ urlpatterns = [
|
||||||
teamkfet_required(views.InventoryRead.as_view()),
|
teamkfet_required(views.InventoryRead.as_view()),
|
||||||
name="kfet.inventory.read",
|
name="kfet.inventory.read",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"inventaires/<int:pk>/delete",
|
||||||
|
views.InventoryDelete.as_view(),
|
||||||
|
name="kfet.inventory.delete",
|
||||||
|
),
|
||||||
# -----
|
# -----
|
||||||
# Order urls
|
# Order urls
|
||||||
# -----
|
# -----
|
||||||
|
|
|
@ -13,7 +13,7 @@ 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
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.db import transaction
|
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.forms import formset_factory
|
||||||
from django.http import Http404, HttpResponseBadRequest, JsonResponse
|
from django.http import Http404, HttpResponseBadRequest, JsonResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
@ -1909,6 +1909,32 @@ class InventoryRead(DetailView):
|
||||||
return context
|
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
|
# Order views
|
||||||
# -----
|
# -----
|
||||||
|
|
Loading…
Reference in a new issue