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;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
function KHistory(options={}) {
|
||||
function KHistory(options = {}) {
|
||||
$.extend(this, KHistory.default_options, options);
|
||||
|
||||
this.$container = $(this.container);
|
||||
|
||||
this.reset = function() {
|
||||
this.reset = function () {
|
||||
this.$container.html('');
|
||||
};
|
||||
|
||||
this.addOpeGroup = function(opegroup) {
|
||||
this.addOpeGroup = function (opegroup) {
|
||||
var $day = this._getOrCreateDay(opegroup['at']);
|
||||
var $opegroup = this._opeGroupHtml(opegroup);
|
||||
|
||||
$day.after($opegroup);
|
||||
|
||||
var trigramme = opegroup['on_acc_trigramme'];
|
||||
var is_cof = opegroup['is_cof'];
|
||||
for (var i=0; i<opegroup['opes'].length; i++) {
|
||||
var is_cof = opegroup['is_cof'];
|
||||
for (var i = 0; i < opegroup['opes'].length; i++) {
|
||||
var $ope = this._opeHtml(opegroup['opes'][i], is_cof, trigramme);
|
||||
$ope.data('opegroup', opegroup['id']);
|
||||
$opegroup.after($ope);
|
||||
}
|
||||
}
|
||||
|
||||
this._opeHtml = function(ope, is_cof, trigramme) {
|
||||
this._opeHtml = function (ope, is_cof, trigramme) {
|
||||
var $ope_html = $(this.template_ope);
|
||||
var parsed_amount = parseFloat(ope['amount']);
|
||||
var amount = amountDisplay(parsed_amount, is_cof, trigramme);
|
||||
|
@ -30,9 +30,9 @@ 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)+'€';
|
||||
infos1 = parsed_amount.toFixed(2) + '€';
|
||||
switch (ope['type']) {
|
||||
case 'initial':
|
||||
infos2 = 'Initial';
|
||||
|
@ -58,7 +58,7 @@ function KHistory(options={}) {
|
|||
var addcost_for = ope['addcost_for__trigramme'];
|
||||
if (addcost_for) {
|
||||
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'])
|
||||
|
@ -67,26 +67,26 @@ function KHistory(options={}) {
|
|||
return $ope_html;
|
||||
}
|
||||
|
||||
this.cancelOpe = function(ope, $ope = null) {
|
||||
this.cancelOpe = function (ope, $ope = null) {
|
||||
if (!$ope)
|
||||
$ope = this.findOpe(ope['id']);
|
||||
|
||||
var cancel = 'Annulé';
|
||||
var canceled_at = dateUTCToParis(ope['canceled_at']);
|
||||
if (ope['canceled_by__trigramme'])
|
||||
cancel += ' par '+ope['canceled_by__trigramme'];
|
||||
cancel += ' le '+canceled_at.format('DD/MM/YY à HH:mm:ss');
|
||||
cancel += ' par ' + ope['canceled_by__trigramme'];
|
||||
cancel += ' le ' + canceled_at.format('DD/MM/YY à HH:mm:ss');
|
||||
|
||||
$ope.addClass('canceled').find('.canceled').text(cancel);
|
||||
}
|
||||
|
||||
this._opeGroupHtml = function(opegroup) {
|
||||
this._opeGroupHtml = function (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 amount = amountDisplay(
|
||||
parseFloat(opegroup['amount']), opegroup['is_cof'], trigramme);
|
||||
var amount = amountDisplay(
|
||||
parseFloat(opegroup['amount']), opegroup['is_cof'], trigramme);
|
||||
var comment = opegroup['comment'] || '';
|
||||
|
||||
$opegroup_html
|
||||
|
@ -100,15 +100,15 @@ function KHistory(options={}) {
|
|||
$opegroup_html.find('.trigramme').remove();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
this._getOrCreateDay = function(date) {
|
||||
this._getOrCreateDay = function (date) {
|
||||
var at = dateUTCToParis(date);
|
||||
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
|
||||
});
|
||||
if ($day.length == 1)
|
||||
|
@ -117,23 +117,23 @@ function KHistory(options={}) {
|
|||
return $day.data('date', at_ser).text(at.format('D MMMM'));
|
||||
}
|
||||
|
||||
this.findOpeGroup = function(id) {
|
||||
return this.$container.find('.opegroup').filter(function() {
|
||||
this.findOpeGroup = function (id) {
|
||||
return this.$container.find('.opegroup').filter(function () {
|
||||
return $(this).data('opegroup') == id
|
||||
});
|
||||
}
|
||||
|
||||
this.findOpe = function(id) {
|
||||
return this.$container.find('.ope').filter(function() {
|
||||
this.findOpe = function (id) {
|
||||
return this.$container.find('.ope').filter(function () {
|
||||
return $(this).data('ope') == id
|
||||
});
|
||||
}
|
||||
|
||||
this.cancelOpeGroup = function(opegroup) {
|
||||
this.cancelOpeGroup = function (opegroup) {
|
||||
var $opegroup = this.findOpeGroup(opegroup['id']);
|
||||
var trigramme = $opegroup.find('.trigramme').text();
|
||||
var amount = amountDisplay(
|
||||
parseFloat(opegroup['amount'], opegroup['is_cof'], trigramme));
|
||||
parseFloat(opegroup['amount'], opegroup['is_cof'], trigramme));
|
||||
$opegroup.find('.amount').text(amount);
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
@ -63,35 +72,35 @@
|
|||
|
||||
<div id="tab_summary" class="tab-pane fade in active">
|
||||
|
||||
<section>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<section>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
|
||||
<h3>Inventaires récents</h3>
|
||||
<div class="table-responsive">
|
||||
{% include "kfet/article_inventories_snippet.html" with inventoryarts=inventoryarts|slice:5 %}
|
||||
</div>
|
||||
<h3>Inventaires récents</h3>
|
||||
<div class="table-responsive">
|
||||
{% include "kfet/article_inventories_snippet.html" with inventoryarts=inventoryarts|slice:5 %}
|
||||
</div>
|
||||
|
||||
</div><!-- col -->
|
||||
<div class="col-lg-6">
|
||||
</div><!-- col -->
|
||||
<div class="col-lg-6">
|
||||
|
||||
<h3>Derniers prix fournisseurs</h3>
|
||||
<div class="table-responsive">
|
||||
{% include "kfet/article_suppliers_snippet.html" with supplierarts=supplierarts|slice:5 %}
|
||||
</div>
|
||||
<h3>Derniers prix fournisseurs</h3>
|
||||
<div class="table-responsive">
|
||||
{% include "kfet/article_suppliers_snippet.html" with supplierarts=supplierarts|slice:5 %}
|
||||
</div>
|
||||
|
||||
</div><!-- col -->
|
||||
</div><!-- row -->
|
||||
</div>
|
||||
</section>
|
||||
</div><!-- col -->
|
||||
</div><!-- row -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div>
|
||||
<h3>Ventes</h3>
|
||||
<div id="stat_last"></div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div>
|
||||
<h3>Ventes</h3>
|
||||
<div id="stat_last"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div><!-- summary tab -->
|
||||
|
||||
|
@ -110,26 +119,45 @@
|
|||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function () {
|
||||
var stat_last = new StatsGroup(
|
||||
"{% url 'kfet.article.stat.sales.list' article.id %}",
|
||||
$("#stat_last")
|
||||
"{% url 'kfet.article.stat.sales.list' article.id %}",
|
||||
$("#stat_last")
|
||||
);
|
||||
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
$("table .more").click( function() {
|
||||
$(this).hide();
|
||||
$(this).siblings(".hidden").removeClass("hidden");
|
||||
$("table .more").click(function () {
|
||||
$(this).hide();
|
||||
$(this).siblings(".hidden").removeClass("hidden");
|
||||
});
|
||||
|
||||
let tabs_buttons = $('.tabs-buttons a');
|
||||
tabs_buttons.click( function() {
|
||||
tabs_buttons.removeClass('focus');
|
||||
$(this).addClass('focus');
|
||||
tabs_buttons.click(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>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
|
@ -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