forked from DGNum/gestioCOF
Merge branch 'Aufinal/can-delete-stuff' into 'master'
Délétions d'objets K-Fêt See merge request klub-dev-ens/gestioCOF!359
This commit is contained in:
commit
7f1adf7c4e
14 changed files with 590 additions and 114 deletions
|
@ -1,3 +1,4 @@
|
|||
- On peut supprimer des comptes et des articles K-Fêt
|
||||
- Passage à Django2
|
||||
- Dev : on peut désactiver la barre de debug avec une variable shell
|
||||
- Remplace les CSS de Google par des polices de proximité
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
default_app_config = "kfet.apps.KFetConfig"
|
||||
KFET_DELETED_TRIGRAMME = "☠☠☠"
|
||||
KFET_DELETED_USERNAME = "kfet_deleted_user"
|
||||
|
|
97
kfet/migrations/0066_on_delete_actions.py
Normal file
97
kfet/migrations/0066_on_delete_actions.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
# Generated by Django 2.2 on 2019-05-23 13:20
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("kfet", "0065_choices_promo")]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(model_name="checkouttransfer", name="from_checkout"),
|
||||
migrations.RemoveField(model_name="checkouttransfer", name="to_checkout"),
|
||||
migrations.AlterField(
|
||||
model_name="accountnegative",
|
||||
name="account",
|
||||
field=models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="negative",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="checkoutstatement",
|
||||
name="checkout",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="statements",
|
||||
to="kfet.Checkout",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="inventoryarticle",
|
||||
name="article",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="kfet.Article"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="inventoryarticle",
|
||||
name="inventory",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="kfet.Inventory"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="operation",
|
||||
name="article",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="operations",
|
||||
to="kfet.Article",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="order",
|
||||
name="supplier",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="orders",
|
||||
to="kfet.Supplier",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="orderarticle",
|
||||
name="article",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="kfet.Article"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="orderarticle",
|
||||
name="order",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="kfet.Order"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="supplierarticle",
|
||||
name="article",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="kfet.Article"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="supplierarticle",
|
||||
name="supplier",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="kfet.Supplier"
|
||||
),
|
||||
),
|
||||
migrations.DeleteModel(name="ArticleRule"),
|
||||
migrations.DeleteModel(name="CheckoutTransfer"),
|
||||
]
|
32
kfet/migrations/0067_deleted_account.py
Normal file
32
kfet/migrations/0067_deleted_account.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 2.2 on 2019-05-23 13:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
from kfet import KFET_DELETED_TRIGRAMME, KFET_DELETED_USERNAME
|
||||
|
||||
|
||||
def setup_kfet_deleted_user(apps, schema_editor):
|
||||
"""
|
||||
Setup models instances for the kfet deleted account.
|
||||
|
||||
Username and trigramme are retrieved from kfet.__init__ module.
|
||||
Other data are registered here.
|
||||
"""
|
||||
User = apps.get_model("auth", "User")
|
||||
CofProfile = apps.get_model("gestioncof", "CofProfile")
|
||||
Account = apps.get_model("kfet", "Account")
|
||||
|
||||
user, _ = User.objects.update_or_create(
|
||||
username=KFET_DELETED_USERNAME, defaults={"first_name": "Compte K-Fêt supprimé"}
|
||||
)
|
||||
profile, _ = CofProfile.objects.update_or_create(user=user)
|
||||
account, _ = Account.objects.update_or_create(
|
||||
cofprofile=profile, defaults={"trigramme": KFET_DELETED_TRIGRAMME}
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("kfet", "0066_on_delete_actions")]
|
||||
|
||||
operations = [migrations.RunPython(setup_kfet_deleted_user)]
|
127
kfet/migrations/0068_on_delete_account.py
Normal file
127
kfet/migrations/0068_on_delete_account.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
# Generated by Django 2.2 on 2019-05-23 16:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
import kfet.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [("kfet", "0067_deleted_account")]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="checkout",
|
||||
name="created_by",
|
||||
field=models.ForeignKey(
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="+",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="checkoutstatement",
|
||||
name="by",
|
||||
field=models.ForeignKey(
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="+",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="inventory",
|
||||
name="by",
|
||||
field=models.ForeignKey(
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="+",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="operation",
|
||||
name="addcost_for",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="addcosts",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="operation",
|
||||
name="canceled_by",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="+",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="operationgroup",
|
||||
name="on_acc",
|
||||
field=models.ForeignKey(
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="opesgroup",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="operationgroup",
|
||||
name="valid_by",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="+",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="transfer",
|
||||
name="canceled_by",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="+",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="transfer",
|
||||
name="from_acc",
|
||||
field=models.ForeignKey(
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="transfers_from",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="transfer",
|
||||
name="to_acc",
|
||||
field=models.ForeignKey(
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="transfers_to",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="transfergroup",
|
||||
name="valid_by",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=models.SET(kfet.models.get_deleted_account),
|
||||
related_name="+",
|
||||
to="kfet.Account",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -12,6 +12,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from gestioncof.models import CofProfile
|
||||
|
||||
from . import KFET_DELETED_TRIGRAMME
|
||||
from .auth import KFET_GENERIC_TRIGRAMME
|
||||
from .auth.models import GenericTeamToken # noqa
|
||||
from .config import kfet_config
|
||||
|
@ -151,7 +152,7 @@ class Account(models.Model):
|
|||
|
||||
@property
|
||||
def readable(self):
|
||||
return self.trigramme != "GNR"
|
||||
return self.trigramme not in [KFET_DELETED_TRIGRAMME, KFET_GENERIC_TRIGRAMME]
|
||||
|
||||
@property
|
||||
def is_team(self):
|
||||
|
@ -267,12 +268,6 @@ class Account(models.Model):
|
|||
|
||||
self.password = hash_password(clear_password)
|
||||
|
||||
# Surcharge de delete
|
||||
# Pas de suppression possible
|
||||
# Cas à régler plus tard
|
||||
def delete(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def update_negative(self):
|
||||
if self.real_balance < 0:
|
||||
if hasattr(self, "negative") and not self.negative.start:
|
||||
|
@ -299,6 +294,10 @@ class Account(models.Model):
|
|||
self.trigramme = trigramme
|
||||
|
||||
|
||||
def get_deleted_account():
|
||||
return Account.objects.get(trigramme=KFET_DELETED_TRIGRAMME)
|
||||
|
||||
|
||||
class AccountNegativeManager(models.Manager):
|
||||
"""Manager for AccountNegative model."""
|
||||
|
||||
|
@ -310,7 +309,7 @@ class AccountNegative(models.Model):
|
|||
objects = AccountNegativeManager()
|
||||
|
||||
account = models.OneToOneField(
|
||||
Account, on_delete=models.PROTECT, related_name="negative"
|
||||
Account, on_delete=models.CASCADE, related_name="negative"
|
||||
)
|
||||
start = models.DateTimeField(blank=True, null=True, default=None)
|
||||
balance_offset = models.DecimalField(
|
||||
|
@ -350,7 +349,9 @@ class CheckoutQuerySet(models.QuerySet):
|
|||
|
||||
|
||||
class Checkout(models.Model):
|
||||
created_by = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="+")
|
||||
created_by = models.ForeignKey(
|
||||
Account, on_delete=models.SET(get_deleted_account), related_name="+"
|
||||
)
|
||||
name = models.CharField(max_length=45)
|
||||
valid_from = models.DateTimeField()
|
||||
valid_to = models.DateTimeField()
|
||||
|
@ -384,20 +385,12 @@ class Checkout(models.Model):
|
|||
return ret
|
||||
|
||||
|
||||
class CheckoutTransfer(models.Model):
|
||||
from_checkout = models.ForeignKey(
|
||||
Checkout, on_delete=models.PROTECT, related_name="transfers_from"
|
||||
)
|
||||
to_checkout = models.ForeignKey(
|
||||
Checkout, on_delete=models.PROTECT, related_name="transfers_to"
|
||||
)
|
||||
amount = models.DecimalField(max_digits=6, decimal_places=2)
|
||||
|
||||
|
||||
class CheckoutStatement(models.Model):
|
||||
by = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="+")
|
||||
by = models.ForeignKey(
|
||||
Account, on_delete=models.SET(get_deleted_account), related_name="+"
|
||||
)
|
||||
checkout = models.ForeignKey(
|
||||
Checkout, on_delete=models.PROTECT, related_name="statements"
|
||||
Checkout, on_delete=models.CASCADE, related_name="statements"
|
||||
)
|
||||
balance_old = models.DecimalField(
|
||||
"ancienne balance", max_digits=6, decimal_places=2
|
||||
|
@ -526,21 +519,13 @@ class Article(models.Model):
|
|||
return to_ukf(self.price)
|
||||
|
||||
|
||||
class ArticleRule(models.Model):
|
||||
article_on = models.OneToOneField(
|
||||
Article, on_delete=models.PROTECT, related_name="rule_on"
|
||||
)
|
||||
article_to = models.OneToOneField(
|
||||
Article, on_delete=models.PROTECT, related_name="rule_to"
|
||||
)
|
||||
ratio = models.PositiveSmallIntegerField()
|
||||
|
||||
|
||||
class Inventory(models.Model):
|
||||
articles = models.ManyToManyField(
|
||||
Article, through="InventoryArticle", related_name="inventories"
|
||||
)
|
||||
by = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="+")
|
||||
by = models.ForeignKey(
|
||||
Account, on_delete=models.SET(get_deleted_account), related_name="+"
|
||||
)
|
||||
at = models.DateTimeField(auto_now_add=True)
|
||||
# Optional
|
||||
order = models.OneToOneField(
|
||||
|
@ -560,8 +545,8 @@ class Inventory(models.Model):
|
|||
|
||||
|
||||
class InventoryArticle(models.Model):
|
||||
inventory = models.ForeignKey(Inventory, on_delete=models.PROTECT)
|
||||
article = models.ForeignKey(Article, on_delete=models.PROTECT)
|
||||
inventory = models.ForeignKey(Inventory, on_delete=models.CASCADE)
|
||||
article = models.ForeignKey(Article, on_delete=models.CASCADE)
|
||||
stock_old = models.IntegerField()
|
||||
stock_new = models.IntegerField()
|
||||
stock_error = models.IntegerField(default=0)
|
||||
|
@ -592,8 +577,8 @@ class Supplier(models.Model):
|
|||
|
||||
|
||||
class SupplierArticle(models.Model):
|
||||
supplier = models.ForeignKey(Supplier, on_delete=models.PROTECT)
|
||||
article = models.ForeignKey(Article, on_delete=models.PROTECT)
|
||||
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
|
||||
article = models.ForeignKey(Article, on_delete=models.CASCADE)
|
||||
at = models.DateTimeField(auto_now_add=True)
|
||||
price_HT = models.DecimalField(
|
||||
max_digits=7, decimal_places=4, blank=True, null=True, default=None
|
||||
|
@ -608,7 +593,7 @@ class SupplierArticle(models.Model):
|
|||
|
||||
class Order(models.Model):
|
||||
supplier = models.ForeignKey(
|
||||
Supplier, on_delete=models.PROTECT, related_name="orders"
|
||||
Supplier, on_delete=models.CASCADE, related_name="orders"
|
||||
)
|
||||
articles = models.ManyToManyField(
|
||||
Article, through="OrderArticle", related_name="orders"
|
||||
|
@ -621,8 +606,8 @@ class Order(models.Model):
|
|||
|
||||
|
||||
class OrderArticle(models.Model):
|
||||
order = models.ForeignKey(Order, on_delete=models.PROTECT)
|
||||
article = models.ForeignKey(Article, on_delete=models.PROTECT)
|
||||
order = models.ForeignKey(Order, on_delete=models.CASCADE)
|
||||
article = models.ForeignKey(Article, on_delete=models.CASCADE)
|
||||
quantity_ordered = models.IntegerField()
|
||||
quantity_received = models.IntegerField(default=0)
|
||||
|
||||
|
@ -633,7 +618,7 @@ class TransferGroup(models.Model):
|
|||
comment = models.CharField(max_length=255, blank=True, default="")
|
||||
valid_by = models.ForeignKey(
|
||||
Account,
|
||||
on_delete=models.PROTECT,
|
||||
on_delete=models.SET(get_deleted_account),
|
||||
related_name="+",
|
||||
blank=True,
|
||||
null=True,
|
||||
|
@ -646,16 +631,18 @@ class Transfer(models.Model):
|
|||
TransferGroup, on_delete=models.PROTECT, related_name="transfers"
|
||||
)
|
||||
from_acc = models.ForeignKey(
|
||||
Account, on_delete=models.PROTECT, related_name="transfers_from"
|
||||
Account,
|
||||
on_delete=models.SET(get_deleted_account),
|
||||
related_name="transfers_from",
|
||||
)
|
||||
to_acc = models.ForeignKey(
|
||||
Account, on_delete=models.PROTECT, related_name="transfers_to"
|
||||
Account, on_delete=models.SET(get_deleted_account), related_name="transfers_to"
|
||||
)
|
||||
amount = models.DecimalField(max_digits=6, decimal_places=2)
|
||||
# Optional
|
||||
canceled_by = models.ForeignKey(
|
||||
Account,
|
||||
on_delete=models.PROTECT,
|
||||
on_delete=models.SET(get_deleted_account),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=None,
|
||||
|
@ -669,7 +656,7 @@ class Transfer(models.Model):
|
|||
|
||||
class OperationGroup(models.Model):
|
||||
on_acc = models.ForeignKey(
|
||||
Account, on_delete=models.PROTECT, related_name="opesgroup"
|
||||
Account, on_delete=models.SET(get_deleted_account), related_name="opesgroup"
|
||||
)
|
||||
checkout = models.ForeignKey(
|
||||
Checkout, on_delete=models.PROTECT, related_name="opesgroup"
|
||||
|
@ -681,7 +668,7 @@ class OperationGroup(models.Model):
|
|||
comment = models.CharField(max_length=255, blank=True, default="")
|
||||
valid_by = models.ForeignKey(
|
||||
Account,
|
||||
on_delete=models.PROTECT,
|
||||
on_delete=models.SET(get_deleted_account),
|
||||
related_name="+",
|
||||
blank=True,
|
||||
null=True,
|
||||
|
@ -717,7 +704,7 @@ class Operation(models.Model):
|
|||
# Optional
|
||||
article = models.ForeignKey(
|
||||
Article,
|
||||
on_delete=models.PROTECT,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="operations",
|
||||
blank=True,
|
||||
null=True,
|
||||
|
@ -726,7 +713,7 @@ class Operation(models.Model):
|
|||
article_nb = models.PositiveSmallIntegerField(blank=True, null=True, default=None)
|
||||
canceled_by = models.ForeignKey(
|
||||
Account,
|
||||
on_delete=models.PROTECT,
|
||||
on_delete=models.SET(get_deleted_account),
|
||||
related_name="+",
|
||||
blank=True,
|
||||
null=True,
|
||||
|
@ -735,7 +722,7 @@ class Operation(models.Model):
|
|||
canceled_at = models.DateTimeField(blank=True, null=True, default=None)
|
||||
addcost_for = models.ForeignKey(
|
||||
Account,
|
||||
on_delete=models.PROTECT,
|
||||
on_delete=models.SET(get_deleted_account),
|
||||
related_name="addcosts",
|
||||
blank=True,
|
||||
null=True,
|
||||
|
|
|
@ -57,12 +57,20 @@ aside .heading .sub {
|
|||
aside .buttons {
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
aside .buttons > * {
|
||||
flex: 0 1 auto !important;
|
||||
}
|
||||
|
||||
aside .buttons > hr {
|
||||
flex-basis: 100%;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Aside - Text */
|
||||
|
||||
|
|
|
@ -23,11 +23,19 @@
|
|||
}
|
||||
|
||||
.jconfirm .jconfirm-box .content-pane {
|
||||
margin:0 !important;
|
||||
border-bottom:1px solid #ddd;
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
.jconfirm .jconfirm-box .content {
|
||||
border-bottom:1px solid #ddd;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.jconfirm .jconfirm-box .content div.warning {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
|
||||
.jconfirm .jconfirm-box input {
|
||||
|
@ -43,7 +51,7 @@
|
|||
}
|
||||
|
||||
.jconfirm .jconfirm-box .buttons {
|
||||
margin-top:-5px; /* j'arrive pas à voir pk y'a un espace au dessus sinon... */
|
||||
margin-top:-6px; /* j'arrive pas à voir pk y'a un espace au dessus sinon... */
|
||||
padding:0;
|
||||
height:40px;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ function KHistory(options={}) {
|
|||
|
||||
if (ope['type'] == 'purchase') {
|
||||
infos1 = ope['article_nb'];
|
||||
infos2 = ope['article__name'];
|
||||
infos2 = ope['article__name'] ? ope['article__name'] : 'Article supprimé';
|
||||
} else {
|
||||
infos1 = parsed_amount.toFixed(2) + '€';
|
||||
switch (ope['type']) {
|
||||
|
|
|
@ -20,12 +20,21 @@
|
|||
<a class="btn btn-default" href="{% url 'kfet.article.update' article.pk %}">
|
||||
<span class="glyphicon glyphicon-cog"></span><span>Éditer</span>
|
||||
</a>
|
||||
{% if perms.kfet.delete_account %}
|
||||
<button class="btn btn-default" id="button-delete">
|
||||
<span class="glyphicon glyphicon-remove"></span><span>Supprimer</span>
|
||||
</button>
|
||||
<form method="post" action="{% url 'kfet.article.delete' article.pk %}" id="article-delete-form">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="text">
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<b>Prix:</b> <span>{{ article.price }}€</span>
|
||||
<span data-toggle="tooltip" data-placement="right" class="glyphicon glyphicon-question-sign" title="Hors réduction COF"></span>
|
||||
<span data-toggle="tooltip" data-placement="right" class="glyphicon glyphicon-question-sign"
|
||||
title="Hors réduction COF"></span>
|
||||
</li>
|
||||
<li><b>Stock:</b> {{ article.stock }}</li>
|
||||
<li><b>En vente:</b> {{ article.is_sold|yesno|title }}</li>
|
||||
|
@ -129,6 +138,25 @@ $(document).ready(function() {
|
|||
tabs_buttons.removeClass('focus');
|
||||
$(this).addClass('focus');
|
||||
});
|
||||
|
||||
// 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>
|
||||
</div>
|
||||
`,
|
||||
backgroundDismiss: true,
|
||||
animation: 'top',
|
||||
closeAnimation: 'bottom',
|
||||
keyboardEnabled: true,
|
||||
confirm: function () {
|
||||
$('#article-delete-form').submit();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -18,6 +18,15 @@
|
|||
<a class="btn btn-default" disabled>
|
||||
<span class="glyphicon glyphicon-credit-card"></span><span>Créditer</span>
|
||||
</a>
|
||||
{% if perms.kfet.delete_account %}
|
||||
<hr>
|
||||
<button class="btn btn-default" id="button-delete">
|
||||
<span class="glyphicon glyphicon-remove"></span><span>Supprimer</span>
|
||||
</button>
|
||||
<form method="post" action="{% url 'kfet.account.delete' account.trigramme %}" id="account-delete-form">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="text">
|
||||
|
@ -92,5 +101,24 @@ $( function() {
|
|||
$(this).addClass('focus');
|
||||
});
|
||||
|
||||
// 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>
|
||||
</div>
|
||||
<span>Toutes les données associées à ce compte seront anonymisées.</span>
|
||||
`,
|
||||
backgroundDismiss: true,
|
||||
animation: 'top',
|
||||
closeAnimation: 'bottom',
|
||||
keyboardEnabled: true,
|
||||
confirm: function() {
|
||||
$('#account-delete-form').submit();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -8,6 +8,8 @@ from django.test import Client, TestCase
|
|||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from .. import KFET_DELETED_TRIGRAMME
|
||||
from ..auth import KFET_GENERIC_TRIGRAMME
|
||||
from ..config import kfet_config
|
||||
from ..models import (
|
||||
Account,
|
||||
|
@ -340,6 +342,61 @@ class AccountUpdateViewTests(ViewTestCaseMixin, TestCase):
|
|||
self.assertForbiddenKfet(r)
|
||||
|
||||
|
||||
class AccountDeleteViewTests(ViewTestCaseMixin, TestCase):
|
||||
url_name = "kfet.account.delete"
|
||||
url_kwargs = {"trigramme": "001"}
|
||||
url_expected = "/k-fet/accounts/001/delete"
|
||||
|
||||
auth_user = "team1"
|
||||
auth_forbidden = [None, "user", "team"]
|
||||
http_methods = ["GET", "POST"]
|
||||
with_liq = True
|
||||
|
||||
def get_users_extra(self):
|
||||
return {
|
||||
"user1": create_user("user1", "001"),
|
||||
"team1": create_team("team1", "101", perms=["kfet.delete_account"]),
|
||||
"trez": create_user("trez", "#13"),
|
||||
}
|
||||
|
||||
def test_get_405(self):
|
||||
r = self.client.get(self.url)
|
||||
self.assertEqual(r.status_code, 405)
|
||||
|
||||
def test_post_ok(self):
|
||||
r = self.client.post(self.url, {})
|
||||
self.assertRedirects(r, reverse("kfet.account"))
|
||||
|
||||
with self.assertRaises(Account.DoesNotExist):
|
||||
self.accounts["user1"].refresh_from_db()
|
||||
|
||||
def test_protected_accounts(self):
|
||||
for trigramme in ["LIQ", "#13", KFET_GENERIC_TRIGRAMME, KFET_DELETED_TRIGRAMME]:
|
||||
if Account.objects.get(trigramme=trigramme).readable:
|
||||
expected_code = 200
|
||||
else:
|
||||
expected_code = 403
|
||||
r = self.client.post(
|
||||
reverse(self.url_name, kwargs={"trigramme": trigramme}), {}
|
||||
)
|
||||
self.assertRedirects(
|
||||
r,
|
||||
reverse("kfet.account.read", kwargs={"trigramme": trigramme}),
|
||||
target_status_code=expected_code,
|
||||
)
|
||||
# Devrait être redondant avec le précédent, mais on sait jamais
|
||||
self.assertTrue(Account.objects.filter(trigramme=trigramme).exists())
|
||||
|
||||
def test_nonempty_accounts(self):
|
||||
self.accounts["user1"].balance = 1
|
||||
self.accounts["user1"].save()
|
||||
|
||||
r = self.client.post(self.url, {})
|
||||
self.assertRedirects(r, reverse("kfet.account.read", kwargs=self.url_kwargs))
|
||||
# Shouldn't throw an error
|
||||
self.accounts["user1"].refresh_from_db()
|
||||
|
||||
|
||||
class AccountGroupListViewTests(ViewTestCaseMixin, TestCase):
|
||||
url_name = "kfet.account.group"
|
||||
url_expected = "/k-fet/accounts/groups"
|
||||
|
@ -1288,6 +1345,42 @@ class ArticleUpdateViewTests(ViewTestCaseMixin, TestCase):
|
|||
self.assertForbiddenKfet(r)
|
||||
|
||||
|
||||
class ArticleDeleteViewTests(ViewTestCaseMixin, TestCase):
|
||||
url_name = "kfet.article.delete"
|
||||
|
||||
auth_user = "team1"
|
||||
auth_forbidden = [None, "user", "team"]
|
||||
|
||||
@property
|
||||
def url_kwargs(self):
|
||||
return {"pk": self.article.pk}
|
||||
|
||||
@property
|
||||
def url_expected(self):
|
||||
return "/k-fet/articles/{}/delete".format(self.article.pk)
|
||||
|
||||
def get_users_extra(self):
|
||||
return {"team1": create_team("team1", "101", perms=["kfet.delete_article"])}
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.category = ArticleCategory.objects.create(name="Category")
|
||||
self.article = Article.objects.create(
|
||||
name="Article", category=self.category, stock=5, price=Decimal("2.5")
|
||||
)
|
||||
|
||||
def test_get_redirects(self):
|
||||
r = self.client.get(self.url)
|
||||
self.assertRedirects(r, reverse("kfet.article.read", kwargs=self.url_kwargs))
|
||||
|
||||
def test_post_ok(self):
|
||||
r = self.client.post(self.url, {})
|
||||
self.assertRedirects(r, reverse("kfet.article"))
|
||||
|
||||
with self.assertRaises(Article.DoesNotExist):
|
||||
self.article.refresh_from_db()
|
||||
|
||||
|
||||
class ArticleStatSalesListViewTests(ViewTestCaseMixin, TestCase):
|
||||
url_name = "kfet.article.stat.sales.list"
|
||||
|
||||
|
|
12
kfet/urls.py
12
kfet/urls.py
|
@ -57,6 +57,12 @@ urlpatterns = [
|
|||
views.account_update,
|
||||
name="kfet.account.update",
|
||||
),
|
||||
# Account - Delete
|
||||
path(
|
||||
"accounts/<trigramme:trigramme>/delete",
|
||||
views.AccountDelete.as_view(),
|
||||
name="kfet.account.delete",
|
||||
),
|
||||
# Account - Groups
|
||||
path("accounts/groups", views.account_group, name="kfet.account.group"),
|
||||
path(
|
||||
|
@ -180,6 +186,12 @@ urlpatterns = [
|
|||
teamkfet_required(views.ArticleUpdate.as_view()),
|
||||
name="kfet.article.update",
|
||||
),
|
||||
# Article - Delete
|
||||
path(
|
||||
"articles/<int:pk>/delete",
|
||||
views.ArticleDelete.as_view(),
|
||||
name="kfet.article.delete",
|
||||
),
|
||||
# Article - Statistics
|
||||
path(
|
||||
"articles/<int:pk>/stat/sales/list",
|
||||
|
|
|
@ -7,6 +7,7 @@ from urllib.parse import urlencode
|
|||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
@ -20,10 +21,10 @@ from django.utils import timezone
|
|||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import DetailView, FormView, ListView, TemplateView
|
||||
from django.views.generic.detail import BaseDetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
|
||||
from gestioncof.models import CofProfile
|
||||
from kfet import consumers
|
||||
from kfet import KFET_DELETED_TRIGRAMME, consumers
|
||||
from kfet.config import kfet_config
|
||||
from kfet.decorators import teamkfet_required
|
||||
from kfet.forms import (
|
||||
|
@ -78,6 +79,7 @@ from kfet.models import (
|
|||
)
|
||||
from kfet.statistic import ScaleMixin, WeekScale, last_stats_manifest
|
||||
|
||||
from .auth import KFET_GENERIC_TRIGRAMME
|
||||
from .auth.views import ( # noqa
|
||||
AccountGroupCreate,
|
||||
AccountGroupUpdate,
|
||||
|
@ -467,6 +469,43 @@ def account_update(request, trigramme):
|
|||
},
|
||||
)
|
||||
|
||||
# Account - Delete
|
||||
|
||||
|
||||
class AccountDelete(PermissionRequiredMixin, DeleteView):
|
||||
model = Account
|
||||
slug_field = "trigramme"
|
||||
slug_url_kwarg = "trigramme"
|
||||
success_url = reverse_lazy("kfet.account")
|
||||
success_message = "Compte supprimé avec succès !"
|
||||
permission_required = "kfet.delete_account"
|
||||
|
||||
http_method_names = ["post"]
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
if self.object.balance >= 0.01:
|
||||
messages.error(
|
||||
request,
|
||||
"Impossible de supprimer un compte "
|
||||
"avec une balance strictement positive !",
|
||||
)
|
||||
return redirect("kfet.account.read", self.object.trigramme)
|
||||
|
||||
if self.object.trigramme in [
|
||||
"LIQ",
|
||||
KFET_GENERIC_TRIGRAMME,
|
||||
KFET_DELETED_TRIGRAMME,
|
||||
"#13",
|
||||
]:
|
||||
messages.error(request, "Impossible de supprimer un trigramme protégé !")
|
||||
return redirect("kfet.account.read", self.object.trigramme)
|
||||
|
||||
# SuccessMessageMixin does not work with DeleteView, see :
|
||||
# https://code.djangoproject.com/ticket/21926
|
||||
messages.success(request, self.success_message)
|
||||
return super().delete(request, *args, **kwargs)
|
||||
|
||||
|
||||
class AccountNegativeList(ListView):
|
||||
queryset = AccountNegative.objects.select_related(
|
||||
|
@ -837,6 +876,20 @@ class ArticleUpdate(SuccessMessageMixin, UpdateView):
|
|||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ArticleDelete(PermissionRequiredMixin, DeleteView):
|
||||
model = Article
|
||||
success_url = reverse_lazy("kfet.article")
|
||||
success_message = "Article supprimé avec succès !"
|
||||
permission_required = "kfet.delete_article"
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return redirect("kfet.article.read", self.kwargs.get(self.pk_url_kwarg))
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(request, self.success_message)
|
||||
return super().delete(request, *args, **kwargs)
|
||||
|
||||
|
||||
# -----
|
||||
# K-Psul
|
||||
# -----
|
||||
|
|
Loading…
Reference in a new issue