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
|
- Passage à Django2
|
||||||
- Dev : on peut désactiver la barre de debug avec une variable shell
|
- Dev : on peut désactiver la barre de debug avec une variable shell
|
||||||
- Remplace les CSS de Google par des polices de proximité
|
- Remplace les CSS de Google par des polices de proximité
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
default_app_config = "kfet.apps.KFetConfig"
|
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 gestioncof.models import CofProfile
|
||||||
|
|
||||||
|
from . import KFET_DELETED_TRIGRAMME
|
||||||
from .auth import KFET_GENERIC_TRIGRAMME
|
from .auth import KFET_GENERIC_TRIGRAMME
|
||||||
from .auth.models import GenericTeamToken # noqa
|
from .auth.models import GenericTeamToken # noqa
|
||||||
from .config import kfet_config
|
from .config import kfet_config
|
||||||
|
@ -151,7 +152,7 @@ class Account(models.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def readable(self):
|
def readable(self):
|
||||||
return self.trigramme != "GNR"
|
return self.trigramme not in [KFET_DELETED_TRIGRAMME, KFET_GENERIC_TRIGRAMME]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_team(self):
|
def is_team(self):
|
||||||
|
@ -267,12 +268,6 @@ class Account(models.Model):
|
||||||
|
|
||||||
self.password = hash_password(clear_password)
|
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):
|
def update_negative(self):
|
||||||
if self.real_balance < 0:
|
if self.real_balance < 0:
|
||||||
if hasattr(self, "negative") and not self.negative.start:
|
if hasattr(self, "negative") and not self.negative.start:
|
||||||
|
@ -299,6 +294,10 @@ class Account(models.Model):
|
||||||
self.trigramme = trigramme
|
self.trigramme = trigramme
|
||||||
|
|
||||||
|
|
||||||
|
def get_deleted_account():
|
||||||
|
return Account.objects.get(trigramme=KFET_DELETED_TRIGRAMME)
|
||||||
|
|
||||||
|
|
||||||
class AccountNegativeManager(models.Manager):
|
class AccountNegativeManager(models.Manager):
|
||||||
"""Manager for AccountNegative model."""
|
"""Manager for AccountNegative model."""
|
||||||
|
|
||||||
|
@ -310,7 +309,7 @@ class AccountNegative(models.Model):
|
||||||
objects = AccountNegativeManager()
|
objects = AccountNegativeManager()
|
||||||
|
|
||||||
account = models.OneToOneField(
|
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)
|
start = models.DateTimeField(blank=True, null=True, default=None)
|
||||||
balance_offset = models.DecimalField(
|
balance_offset = models.DecimalField(
|
||||||
|
@ -350,7 +349,9 @@ class CheckoutQuerySet(models.QuerySet):
|
||||||
|
|
||||||
|
|
||||||
class Checkout(models.Model):
|
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)
|
name = models.CharField(max_length=45)
|
||||||
valid_from = models.DateTimeField()
|
valid_from = models.DateTimeField()
|
||||||
valid_to = models.DateTimeField()
|
valid_to = models.DateTimeField()
|
||||||
|
@ -384,20 +385,12 @@ class Checkout(models.Model):
|
||||||
return ret
|
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):
|
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 = models.ForeignKey(
|
||||||
Checkout, on_delete=models.PROTECT, related_name="statements"
|
Checkout, on_delete=models.CASCADE, related_name="statements"
|
||||||
)
|
)
|
||||||
balance_old = models.DecimalField(
|
balance_old = models.DecimalField(
|
||||||
"ancienne balance", max_digits=6, decimal_places=2
|
"ancienne balance", max_digits=6, decimal_places=2
|
||||||
|
@ -526,21 +519,13 @@ class Article(models.Model):
|
||||||
return to_ukf(self.price)
|
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):
|
class Inventory(models.Model):
|
||||||
articles = models.ManyToManyField(
|
articles = models.ManyToManyField(
|
||||||
Article, through="InventoryArticle", related_name="inventories"
|
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)
|
at = models.DateTimeField(auto_now_add=True)
|
||||||
# Optional
|
# Optional
|
||||||
order = models.OneToOneField(
|
order = models.OneToOneField(
|
||||||
|
@ -560,8 +545,8 @@ class Inventory(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class InventoryArticle(models.Model):
|
class InventoryArticle(models.Model):
|
||||||
inventory = models.ForeignKey(Inventory, on_delete=models.PROTECT)
|
inventory = models.ForeignKey(Inventory, on_delete=models.CASCADE)
|
||||||
article = models.ForeignKey(Article, on_delete=models.PROTECT)
|
article = models.ForeignKey(Article, on_delete=models.CASCADE)
|
||||||
stock_old = models.IntegerField()
|
stock_old = models.IntegerField()
|
||||||
stock_new = models.IntegerField()
|
stock_new = models.IntegerField()
|
||||||
stock_error = models.IntegerField(default=0)
|
stock_error = models.IntegerField(default=0)
|
||||||
|
@ -592,8 +577,8 @@ class Supplier(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class SupplierArticle(models.Model):
|
class SupplierArticle(models.Model):
|
||||||
supplier = models.ForeignKey(Supplier, on_delete=models.PROTECT)
|
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
|
||||||
article = models.ForeignKey(Article, on_delete=models.PROTECT)
|
article = models.ForeignKey(Article, on_delete=models.CASCADE)
|
||||||
at = models.DateTimeField(auto_now_add=True)
|
at = models.DateTimeField(auto_now_add=True)
|
||||||
price_HT = models.DecimalField(
|
price_HT = models.DecimalField(
|
||||||
max_digits=7, decimal_places=4, blank=True, null=True, default=None
|
max_digits=7, decimal_places=4, blank=True, null=True, default=None
|
||||||
|
@ -608,7 +593,7 @@ class SupplierArticle(models.Model):
|
||||||
|
|
||||||
class Order(models.Model):
|
class Order(models.Model):
|
||||||
supplier = models.ForeignKey(
|
supplier = models.ForeignKey(
|
||||||
Supplier, on_delete=models.PROTECT, related_name="orders"
|
Supplier, on_delete=models.CASCADE, related_name="orders"
|
||||||
)
|
)
|
||||||
articles = models.ManyToManyField(
|
articles = models.ManyToManyField(
|
||||||
Article, through="OrderArticle", related_name="orders"
|
Article, through="OrderArticle", related_name="orders"
|
||||||
|
@ -621,8 +606,8 @@ class Order(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class OrderArticle(models.Model):
|
class OrderArticle(models.Model):
|
||||||
order = models.ForeignKey(Order, on_delete=models.PROTECT)
|
order = models.ForeignKey(Order, on_delete=models.CASCADE)
|
||||||
article = models.ForeignKey(Article, on_delete=models.PROTECT)
|
article = models.ForeignKey(Article, on_delete=models.CASCADE)
|
||||||
quantity_ordered = models.IntegerField()
|
quantity_ordered = models.IntegerField()
|
||||||
quantity_received = models.IntegerField(default=0)
|
quantity_received = models.IntegerField(default=0)
|
||||||
|
|
||||||
|
@ -633,7 +618,7 @@ class TransferGroup(models.Model):
|
||||||
comment = models.CharField(max_length=255, blank=True, default="")
|
comment = models.CharField(max_length=255, blank=True, default="")
|
||||||
valid_by = models.ForeignKey(
|
valid_by = models.ForeignKey(
|
||||||
Account,
|
Account,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.SET(get_deleted_account),
|
||||||
related_name="+",
|
related_name="+",
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -646,16 +631,18 @@ class Transfer(models.Model):
|
||||||
TransferGroup, on_delete=models.PROTECT, related_name="transfers"
|
TransferGroup, on_delete=models.PROTECT, related_name="transfers"
|
||||||
)
|
)
|
||||||
from_acc = models.ForeignKey(
|
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(
|
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)
|
amount = models.DecimalField(max_digits=6, decimal_places=2)
|
||||||
# Optional
|
# Optional
|
||||||
canceled_by = models.ForeignKey(
|
canceled_by = models.ForeignKey(
|
||||||
Account,
|
Account,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.SET(get_deleted_account),
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
default=None,
|
default=None,
|
||||||
|
@ -669,7 +656,7 @@ class Transfer(models.Model):
|
||||||
|
|
||||||
class OperationGroup(models.Model):
|
class OperationGroup(models.Model):
|
||||||
on_acc = models.ForeignKey(
|
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 = models.ForeignKey(
|
||||||
Checkout, on_delete=models.PROTECT, related_name="opesgroup"
|
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="")
|
comment = models.CharField(max_length=255, blank=True, default="")
|
||||||
valid_by = models.ForeignKey(
|
valid_by = models.ForeignKey(
|
||||||
Account,
|
Account,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.SET(get_deleted_account),
|
||||||
related_name="+",
|
related_name="+",
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -717,7 +704,7 @@ class Operation(models.Model):
|
||||||
# Optional
|
# Optional
|
||||||
article = models.ForeignKey(
|
article = models.ForeignKey(
|
||||||
Article,
|
Article,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.SET_NULL,
|
||||||
related_name="operations",
|
related_name="operations",
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -726,7 +713,7 @@ class Operation(models.Model):
|
||||||
article_nb = models.PositiveSmallIntegerField(blank=True, null=True, default=None)
|
article_nb = models.PositiveSmallIntegerField(blank=True, null=True, default=None)
|
||||||
canceled_by = models.ForeignKey(
|
canceled_by = models.ForeignKey(
|
||||||
Account,
|
Account,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.SET(get_deleted_account),
|
||||||
related_name="+",
|
related_name="+",
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -735,7 +722,7 @@ class Operation(models.Model):
|
||||||
canceled_at = models.DateTimeField(blank=True, null=True, default=None)
|
canceled_at = models.DateTimeField(blank=True, null=True, default=None)
|
||||||
addcost_for = models.ForeignKey(
|
addcost_for = models.ForeignKey(
|
||||||
Account,
|
Account,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.SET(get_deleted_account),
|
||||||
related_name="addcosts",
|
related_name="addcosts",
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
|
|
@ -57,12 +57,20 @@ aside .heading .sub {
|
||||||
aside .buttons {
|
aside .buttons {
|
||||||
margin-left: -15px;
|
margin-left: -15px;
|
||||||
margin-right: -15px;
|
margin-right: -15px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside .buttons > * {
|
aside .buttons > * {
|
||||||
flex: 0 1 auto !important;
|
flex: 0 1 auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aside .buttons > hr {
|
||||||
|
flex-basis: 100%;
|
||||||
|
height: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Aside - Text */
|
/* Aside - Text */
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.jconfirm .jconfirm-box .content-pane {
|
.jconfirm .jconfirm-box .content-pane {
|
||||||
margin:0 !important;
|
border-bottom:1px solid #ddd;
|
||||||
|
margin: 0px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jconfirm .jconfirm-box .content {
|
.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 {
|
.jconfirm .jconfirm-box input {
|
||||||
|
@ -43,7 +51,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.jconfirm .jconfirm-box .buttons {
|
.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;
|
padding:0;
|
||||||
height:40px;
|
height:40px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
function KHistory(options={}) {
|
function KHistory(options = {}) {
|
||||||
$.extend(this, KHistory.default_options, options);
|
$.extend(this, KHistory.default_options, options);
|
||||||
|
|
||||||
this.$container = $(this.container);
|
this.$container = $(this.container);
|
||||||
|
|
||||||
this.reset = function() {
|
this.reset = function () {
|
||||||
this.$container.html('');
|
this.$container.html('');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addOpeGroup = function(opegroup) {
|
this.addOpeGroup = function (opegroup) {
|
||||||
var $day = this._getOrCreateDay(opegroup['at']);
|
var $day = this._getOrCreateDay(opegroup['at']);
|
||||||
var $opegroup = this._opeGroupHtml(opegroup);
|
var $opegroup = this._opeGroupHtml(opegroup);
|
||||||
|
|
||||||
$day.after($opegroup);
|
$day.after($opegroup);
|
||||||
|
|
||||||
var trigramme = opegroup['on_acc_trigramme'];
|
var trigramme = opegroup['on_acc_trigramme'];
|
||||||
var is_cof = opegroup['is_cof'];
|
var is_cof = opegroup['is_cof'];
|
||||||
for (var i=0; i<opegroup['opes'].length; i++) {
|
for (var i = 0; i < opegroup['opes'].length; i++) {
|
||||||
var $ope = this._opeHtml(opegroup['opes'][i], is_cof, trigramme);
|
var $ope = this._opeHtml(opegroup['opes'][i], is_cof, trigramme);
|
||||||
$ope.data('opegroup', opegroup['id']);
|
$ope.data('opegroup', opegroup['id']);
|
||||||
$opegroup.after($ope);
|
$opegroup.after($ope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._opeHtml = function(ope, is_cof, trigramme) {
|
this._opeHtml = function (ope, is_cof, trigramme) {
|
||||||
var $ope_html = $(this.template_ope);
|
var $ope_html = $(this.template_ope);
|
||||||
var parsed_amount = parseFloat(ope['amount']);
|
var parsed_amount = parseFloat(ope['amount']);
|
||||||
var amount = amountDisplay(parsed_amount, is_cof, trigramme);
|
var amount = amountDisplay(parsed_amount, is_cof, trigramme);
|
||||||
|
@ -30,9 +30,9 @@ function KHistory(options={}) {
|
||||||
|
|
||||||
if (ope['type'] == 'purchase') {
|
if (ope['type'] == 'purchase') {
|
||||||
infos1 = ope['article_nb'];
|
infos1 = ope['article_nb'];
|
||||||
infos2 = ope['article__name'];
|
infos2 = ope['article__name'] ? ope['article__name'] : 'Article supprimé';
|
||||||
} else {
|
} else {
|
||||||
infos1 = parsed_amount.toFixed(2)+'€';
|
infos1 = parsed_amount.toFixed(2) + '€';
|
||||||
switch (ope['type']) {
|
switch (ope['type']) {
|
||||||
case 'initial':
|
case 'initial':
|
||||||
infos2 = 'Initial';
|
infos2 = 'Initial';
|
||||||
|
@ -58,7 +58,7 @@ function KHistory(options={}) {
|
||||||
var addcost_for = ope['addcost_for__trigramme'];
|
var addcost_for = ope['addcost_for__trigramme'];
|
||||||
if (addcost_for) {
|
if (addcost_for) {
|
||||||
var addcost_amount = parseFloat(ope['addcost_amount']);
|
var addcost_amount = parseFloat(ope['addcost_amount']);
|
||||||
$ope_html.find('.addcost').text('('+amountDisplay(addcost_amount, is_cof)+'UKF pour '+addcost_for+')');
|
$ope_html.find('.addcost').text('(' + amountDisplay(addcost_amount, is_cof) + 'UKF pour ' + addcost_for + ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ope['canceled_at'])
|
if (ope['canceled_at'])
|
||||||
|
@ -67,26 +67,26 @@ function KHistory(options={}) {
|
||||||
return $ope_html;
|
return $ope_html;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cancelOpe = function(ope, $ope = null) {
|
this.cancelOpe = function (ope, $ope = null) {
|
||||||
if (!$ope)
|
if (!$ope)
|
||||||
$ope = this.findOpe(ope['id']);
|
$ope = this.findOpe(ope['id']);
|
||||||
|
|
||||||
var cancel = 'Annulé';
|
var cancel = 'Annulé';
|
||||||
var canceled_at = dateUTCToParis(ope['canceled_at']);
|
var canceled_at = dateUTCToParis(ope['canceled_at']);
|
||||||
if (ope['canceled_by__trigramme'])
|
if (ope['canceled_by__trigramme'])
|
||||||
cancel += ' par '+ope['canceled_by__trigramme'];
|
cancel += ' par ' + ope['canceled_by__trigramme'];
|
||||||
cancel += ' le '+canceled_at.format('DD/MM/YY à HH:mm:ss');
|
cancel += ' le ' + canceled_at.format('DD/MM/YY à HH:mm:ss');
|
||||||
|
|
||||||
$ope.addClass('canceled').find('.canceled').text(cancel);
|
$ope.addClass('canceled').find('.canceled').text(cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._opeGroupHtml = function(opegroup) {
|
this._opeGroupHtml = function (opegroup) {
|
||||||
var $opegroup_html = $(this.template_opegroup);
|
var $opegroup_html = $(this.template_opegroup);
|
||||||
|
|
||||||
var at = dateUTCToParis(opegroup['at']).format('HH:mm:ss');
|
var at = dateUTCToParis(opegroup['at']).format('HH:mm:ss');
|
||||||
var trigramme = opegroup['on_acc__trigramme'];
|
var trigramme = opegroup['on_acc__trigramme'];
|
||||||
var amount = amountDisplay(
|
var amount = amountDisplay(
|
||||||
parseFloat(opegroup['amount']), opegroup['is_cof'], trigramme);
|
parseFloat(opegroup['amount']), opegroup['is_cof'], trigramme);
|
||||||
var comment = opegroup['comment'] || '';
|
var comment = opegroup['comment'] || '';
|
||||||
|
|
||||||
$opegroup_html
|
$opegroup_html
|
||||||
|
@ -100,15 +100,15 @@ function KHistory(options={}) {
|
||||||
$opegroup_html.find('.trigramme').remove();
|
$opegroup_html.find('.trigramme').remove();
|
||||||
|
|
||||||
if (opegroup['valid_by__trigramme'])
|
if (opegroup['valid_by__trigramme'])
|
||||||
$opegroup_html.find('.valid_by').text('Par '+opegroup['valid_by__trigramme']);
|
$opegroup_html.find('.valid_by').text('Par ' + opegroup['valid_by__trigramme']);
|
||||||
|
|
||||||
return $opegroup_html;
|
return $opegroup_html;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._getOrCreateDay = function(date) {
|
this._getOrCreateDay = function (date) {
|
||||||
var at = dateUTCToParis(date);
|
var at = dateUTCToParis(date);
|
||||||
var at_ser = at.format('YYYY-MM-DD');
|
var at_ser = at.format('YYYY-MM-DD');
|
||||||
var $day = this.$container.find('.day').filter(function() {
|
var $day = this.$container.find('.day').filter(function () {
|
||||||
return $(this).data('date') == at_ser
|
return $(this).data('date') == at_ser
|
||||||
});
|
});
|
||||||
if ($day.length == 1)
|
if ($day.length == 1)
|
||||||
|
@ -117,23 +117,23 @@ function KHistory(options={}) {
|
||||||
return $day.data('date', at_ser).text(at.format('D MMMM'));
|
return $day.data('date', at_ser).text(at.format('D MMMM'));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.findOpeGroup = function(id) {
|
this.findOpeGroup = function (id) {
|
||||||
return this.$container.find('.opegroup').filter(function() {
|
return this.$container.find('.opegroup').filter(function () {
|
||||||
return $(this).data('opegroup') == id
|
return $(this).data('opegroup') == id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.findOpe = function(id) {
|
this.findOpe = function (id) {
|
||||||
return this.$container.find('.ope').filter(function() {
|
return this.$container.find('.ope').filter(function () {
|
||||||
return $(this).data('ope') == id
|
return $(this).data('ope') == id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cancelOpeGroup = function(opegroup) {
|
this.cancelOpeGroup = function (opegroup) {
|
||||||
var $opegroup = this.findOpeGroup(opegroup['id']);
|
var $opegroup = this.findOpeGroup(opegroup['id']);
|
||||||
var trigramme = $opegroup.find('.trigramme').text();
|
var trigramme = $opegroup.find('.trigramme').text();
|
||||||
var amount = amountDisplay(
|
var amount = amountDisplay(
|
||||||
parseFloat(opegroup['amount'], opegroup['is_cof'], trigramme));
|
parseFloat(opegroup['amount'], opegroup['is_cof'], trigramme));
|
||||||
$opegroup.find('.amount').text(amount);
|
$opegroup.find('.amount').text(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,21 @@
|
||||||
<a class="btn btn-default" href="{% url 'kfet.article.update' article.pk %}">
|
<a class="btn btn-default" href="{% url 'kfet.article.update' article.pk %}">
|
||||||
<span class="glyphicon glyphicon-cog"></span><span>Éditer</span>
|
<span class="glyphicon glyphicon-cog"></span><span>Éditer</span>
|
||||||
</a>
|
</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>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li>
|
<li>
|
||||||
<b>Prix:</b> <span>{{ article.price }}€</span>
|
<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>
|
||||||
<li><b>Stock:</b> {{ article.stock }}</li>
|
<li><b>Stock:</b> {{ article.stock }}</li>
|
||||||
<li><b>En vente:</b> {{ article.is_sold|yesno|title }}</li>
|
<li><b>En vente:</b> {{ article.is_sold|yesno|title }}</li>
|
||||||
|
@ -63,35 +72,35 @@
|
||||||
|
|
||||||
<div id="tab_summary" class="tab-pane fade in active">
|
<div id="tab_summary" class="tab-pane fade in active">
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div>
|
<div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
|
|
||||||
<h3>Inventaires récents</h3>
|
<h3>Inventaires récents</h3>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
{% include "kfet/article_inventories_snippet.html" with inventoryarts=inventoryarts|slice:5 %}
|
{% include "kfet/article_inventories_snippet.html" with inventoryarts=inventoryarts|slice:5 %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div><!-- col -->
|
</div><!-- col -->
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
|
|
||||||
<h3>Derniers prix fournisseurs</h3>
|
<h3>Derniers prix fournisseurs</h3>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
{% include "kfet/article_suppliers_snippet.html" with supplierarts=supplierarts|slice:5 %}
|
{% include "kfet/article_suppliers_snippet.html" with supplierarts=supplierarts|slice:5 %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div><!-- col -->
|
</div><!-- col -->
|
||||||
</div><!-- row -->
|
</div><!-- row -->
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div>
|
<div>
|
||||||
<h3>Ventes</h3>
|
<h3>Ventes</h3>
|
||||||
<div id="stat_last"></div>
|
<div id="stat_last"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</div><!-- summary tab -->
|
</div><!-- summary tab -->
|
||||||
|
|
||||||
|
@ -110,26 +119,45 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
var stat_last = new StatsGroup(
|
var stat_last = new StatsGroup(
|
||||||
"{% url 'kfet.article.stat.sales.list' article.id %}",
|
"{% url 'kfet.article.stat.sales.list' article.id %}",
|
||||||
$("#stat_last")
|
$("#stat_last")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
|
|
||||||
$("table .more").click( function() {
|
$("table .more").click(function () {
|
||||||
$(this).hide();
|
$(this).hide();
|
||||||
$(this).siblings(".hidden").removeClass("hidden");
|
$(this).siblings(".hidden").removeClass("hidden");
|
||||||
});
|
});
|
||||||
|
|
||||||
let tabs_buttons = $('.tabs-buttons a');
|
let tabs_buttons = $('.tabs-buttons a');
|
||||||
tabs_buttons.click( function() {
|
tabs_buttons.click(function () {
|
||||||
tabs_buttons.removeClass('focus');
|
tabs_buttons.removeClass('focus');
|
||||||
$(this).addClass('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>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -18,6 +18,15 @@
|
||||||
<a class="btn btn-default" disabled>
|
<a class="btn btn-default" disabled>
|
||||||
<span class="glyphicon glyphicon-credit-card"></span><span>Créditer</span>
|
<span class="glyphicon glyphicon-credit-card"></span><span>Créditer</span>
|
||||||
</a>
|
</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>
|
||||||
|
|
||||||
<div class="text">
|
<div class="text">
|
||||||
|
@ -92,5 +101,24 @@ $( function() {
|
||||||
$(this).addClass('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>
|
||||||
|
<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>
|
</script>
|
||||||
|
|
|
@ -8,6 +8,8 @@ from django.test import Client, TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from .. import KFET_DELETED_TRIGRAMME
|
||||||
|
from ..auth import KFET_GENERIC_TRIGRAMME
|
||||||
from ..config import kfet_config
|
from ..config import kfet_config
|
||||||
from ..models import (
|
from ..models import (
|
||||||
Account,
|
Account,
|
||||||
|
@ -340,6 +342,61 @@ class AccountUpdateViewTests(ViewTestCaseMixin, TestCase):
|
||||||
self.assertForbiddenKfet(r)
|
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):
|
class AccountGroupListViewTests(ViewTestCaseMixin, TestCase):
|
||||||
url_name = "kfet.account.group"
|
url_name = "kfet.account.group"
|
||||||
url_expected = "/k-fet/accounts/groups"
|
url_expected = "/k-fet/accounts/groups"
|
||||||
|
@ -1288,6 +1345,42 @@ class ArticleUpdateViewTests(ViewTestCaseMixin, TestCase):
|
||||||
self.assertForbiddenKfet(r)
|
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):
|
class ArticleStatSalesListViewTests(ViewTestCaseMixin, TestCase):
|
||||||
url_name = "kfet.article.stat.sales.list"
|
url_name = "kfet.article.stat.sales.list"
|
||||||
|
|
||||||
|
|
12
kfet/urls.py
12
kfet/urls.py
|
@ -57,6 +57,12 @@ urlpatterns = [
|
||||||
views.account_update,
|
views.account_update,
|
||||||
name="kfet.account.update",
|
name="kfet.account.update",
|
||||||
),
|
),
|
||||||
|
# Account - Delete
|
||||||
|
path(
|
||||||
|
"accounts/<trigramme:trigramme>/delete",
|
||||||
|
views.AccountDelete.as_view(),
|
||||||
|
name="kfet.account.delete",
|
||||||
|
),
|
||||||
# Account - Groups
|
# Account - Groups
|
||||||
path("accounts/groups", views.account_group, name="kfet.account.group"),
|
path("accounts/groups", views.account_group, name="kfet.account.group"),
|
||||||
path(
|
path(
|
||||||
|
@ -180,6 +186,12 @@ urlpatterns = [
|
||||||
teamkfet_required(views.ArticleUpdate.as_view()),
|
teamkfet_required(views.ArticleUpdate.as_view()),
|
||||||
name="kfet.article.update",
|
name="kfet.article.update",
|
||||||
),
|
),
|
||||||
|
# Article - Delete
|
||||||
|
path(
|
||||||
|
"articles/<int:pk>/delete",
|
||||||
|
views.ArticleDelete.as_view(),
|
||||||
|
name="kfet.article.delete",
|
||||||
|
),
|
||||||
# Article - Statistics
|
# Article - Statistics
|
||||||
path(
|
path(
|
||||||
"articles/<int:pk>/stat/sales/list",
|
"articles/<int:pk>/stat/sales/list",
|
||||||
|
|
|
@ -7,6 +7,7 @@ from urllib.parse import urlencode
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
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.auth.models import Permission, User
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
@ -20,10 +21,10 @@ from django.utils import timezone
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.generic import DetailView, FormView, ListView, TemplateView
|
from django.views.generic import DetailView, FormView, ListView, TemplateView
|
||||||
from django.views.generic.detail import BaseDetailView
|
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 gestioncof.models import CofProfile
|
||||||
from kfet import consumers
|
from kfet import KFET_DELETED_TRIGRAMME, consumers
|
||||||
from kfet.config import kfet_config
|
from kfet.config import kfet_config
|
||||||
from kfet.decorators import teamkfet_required
|
from kfet.decorators import teamkfet_required
|
||||||
from kfet.forms import (
|
from kfet.forms import (
|
||||||
|
@ -78,6 +79,7 @@ from kfet.models import (
|
||||||
)
|
)
|
||||||
from kfet.statistic import ScaleMixin, WeekScale, last_stats_manifest
|
from kfet.statistic import ScaleMixin, WeekScale, last_stats_manifest
|
||||||
|
|
||||||
|
from .auth import KFET_GENERIC_TRIGRAMME
|
||||||
from .auth.views import ( # noqa
|
from .auth.views import ( # noqa
|
||||||
AccountGroupCreate,
|
AccountGroupCreate,
|
||||||
AccountGroupUpdate,
|
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):
|
class AccountNegativeList(ListView):
|
||||||
queryset = AccountNegative.objects.select_related(
|
queryset = AccountNegative.objects.select_related(
|
||||||
|
@ -837,6 +876,20 @@ class ArticleUpdate(SuccessMessageMixin, UpdateView):
|
||||||
return super().form_valid(form)
|
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
|
# K-Psul
|
||||||
# -----
|
# -----
|
||||||
|
|
Loading…
Reference in a new issue