diff --git a/kfet/auth/migrations/0001_initial.py b/kfet/auth/migrations/0001_initial.py
index 036a30c6..eb7ef049 100644
--- a/kfet/auth/migrations/0001_initial.py
+++ b/kfet/auth/migrations/0001_initial.py
@@ -20,6 +20,9 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)),
('token', models.CharField(unique=True, max_length=50)),
],
+ options={
+ 'default_permissions': (),
+ },
),
migrations.CreateModel(
name='Group',
@@ -27,6 +30,7 @@ class Migration(migrations.Migration):
('group_ptr', models.OneToOneField(parent_link=True, serialize=False, primary_key=True, auto_created=True, to='auth.Group')),
],
options={
+ 'default_permissions': ('view', 'add', 'change'),
'verbose_name': 'Groupe',
'verbose_name_plural': 'Groupes',
},
diff --git a/kfet/auth/migrations/0002_existing_groups.py b/kfet/auth/migrations/0002_existing_groups.py
index f816429e..7d0144f7 100644
--- a/kfet/auth/migrations/0002_existing_groups.py
+++ b/kfet/auth/migrations/0002_existing_groups.py
@@ -23,6 +23,7 @@ class Migration(migrations.Migration):
dependencies = [
('kfetauth', '0001_initial'),
+ ('auth', '0006_require_contenttypes_0002'),
]
operations = [
diff --git a/kfet/auth/migrations/0003_update_permissions.py b/kfet/auth/migrations/0003_update_permissions.py
new file mode 100644
index 00000000..168e21e7
--- /dev/null
+++ b/kfet/auth/migrations/0003_update_permissions.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+from django.db import migrations
+from django.db.models import Q
+
+
+def convert_manage_perms(apps, schema_editor):
+ """
+ Use the permissions of `kfet.auth.models.Group` model, instead of using the
+ `manage_perms` permission, which is deleted, of `kfet.models.Account`.
+
+ `Group` which have this last permission will get the permissions:
+ `kfetauth.view_group`, `kfetauth.add_group` and `kfetauth.change_group`.
+ """
+ Group = apps.get_model('auth', 'Group')
+ Permission = apps.get_model('auth', 'Permission')
+ ContentType = apps.get_model('contenttypes', 'ContentType')
+
+ try:
+ old_p = Permission.objects.get(
+ content_type__app_label='kfet',
+ content_type__model='account',
+ codename='manage_perms',
+ )
+ except Permission.DoesNotExist:
+ return
+
+ groups = old_p.group_set.all()
+
+ ct_group, _ = ContentType.objects.get_or_create(
+ app_label='kfetauth',
+ model='group',
+ )
+
+ view_p, _ = Permission.objects.get_or_create(
+ content_type=ct_group, codename='view_group',
+ defaults={'name': 'Can view Groupe'})
+ add_p, _ = Permission.objects.get_or_create(
+ content_type=ct_group, codename='add_group',
+ defaults={'name': 'Can add Groupe'})
+ change_p, _ = Permission.objects.get_or_create(
+ content_type=ct_group, codename='change_group',
+ defaults={'name': 'Can change Group'})
+
+ GroupPermission = Group.permissions.through
+
+ GroupPermission.objects.bulk_create([
+ GroupPermission(permission=p, group=g)
+ for g in groups
+ for p in (view_p, add_p, change_p)
+ ])
+
+ old_p.delete()
+
+
+def delete_unused_permissions(apps, schema_editor):
+ Permission = apps.get_model('auth', 'Permission')
+
+ to_delete_q = Q(content_type__model='genericteamtoken') | Q(
+ content_type__model='group',
+ codename='delete_group',
+ )
+
+ to_delete_q &= Q(content_type__app_label='kfetauth')
+
+ Permission.objects.filter(to_delete_q).delete()
+
+
+class Migration(migrations.Migration):
+ """
+ Data migration about permissions.
+ """
+ dependencies = [
+ ('kfetauth', '0002_existing_groups'),
+ ('auth', '0006_require_contenttypes_0002'),
+ ('contenttypes', '0002_remove_content_type_name'),
+ ]
+
+ operations = [
+ migrations.RunPython(convert_manage_perms),
+ migrations.RunPython(delete_unused_permissions),
+ ]
diff --git a/kfet/auth/models.py b/kfet/auth/models.py
index 0dfd26fd..c8648eda 100644
--- a/kfet/auth/models.py
+++ b/kfet/auth/models.py
@@ -20,6 +20,9 @@ class GenericTeamToken(models.Model):
objects = GenericTeamTokenManager()
+ class Meta:
+ default_permissions = ()
+
class Group(DjangoGroup):
@@ -35,6 +38,7 @@ class Group(DjangoGroup):
class Meta:
verbose_name = _("Groupe")
verbose_name_plural = _("Groupes")
+ default_permissions = ('view', 'add', 'change')
KFET_CORE_APP_LABELS = ['kfet', 'kfetauth']
diff --git a/kfet/auth/views.py b/kfet/auth/views.py
index 073c558f..7d7b48c5 100644
--- a/kfet/auth/views.py
+++ b/kfet/auth/views.py
@@ -104,7 +104,7 @@ class GenericLoginView(View):
login_generic = GenericLoginView.as_view()
-@permission_required('kfet.manage_perms')
+@permission_required('kfetauth.view_group')
def account_group(request):
user_pre = Prefetch(
'user_set',
diff --git a/kfet/migrations/0060_change_models_opts.py b/kfet/migrations/0060_change_models_opts.py
new file mode 100644
index 00000000..65bba33c
--- /dev/null
+++ b/kfet/migrations/0060_change_models_opts.py
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('kfet', '0059_create_generic'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='account',
+ options={
+ 'permissions': (
+ ('is_team', "Membre de l'équipe"),
+ ('manage_addcosts', 'Gérer les majorations'),
+ (
+ 'edit_balance_account',
+ "Modifier la balance d'un compte"
+ ),
+ (
+ 'change_account_password',
+ "Modifier le mot de passe d'une personne de l'équipe"
+ ),
+ (
+ 'special_add_account',
+ 'Créer un compte avec une balance initiale'
+ ),
+ ('can_force_close', 'Fermer manuellement la K-Fêt')
+ ),
+ 'default_permissions': ('add', 'change'),
+ 'verbose_name_plural': 'Comptes',
+ 'verbose_name': 'Compte',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='accountnegative',
+ options={
+ 'default_permissions': ('view', 'change',),
+ 'verbose_name_plural': 'Comptes en négatif',
+ 'verbose_name': 'Compte en négatif',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='article',
+ options={
+ 'default_permissions': ('add', 'change'),
+ 'verbose_name_plural': 'Articles',
+ 'verbose_name': 'Article',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='articlecategory',
+ options={
+ 'default_permissions': ('change',),
+ 'verbose_name_plural': "Catégories d'articles",
+ 'verbose_name': "Catégorie d'articles",
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='articlerule',
+ options={
+ 'default_permissions': (),
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='checkout',
+ options={
+ 'default_permissions': ('add', 'change'),
+ 'ordering': ['-valid_to'],
+ 'verbose_name_plural': 'Caisses',
+ 'verbose_name': 'Caisse',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='checkoutstatement',
+ options={
+ 'default_permissions': ('add', 'change'),
+ 'verbose_name_plural': 'Relevés de caisses',
+ 'verbose_name': 'Relevé de caisse',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='checkouttransfer',
+ options={
+ 'default_permissions': (),
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='inventory',
+ options={
+ 'permissions': (
+ (
+ 'order_to_inventory',
+ "Générer un inventaire à partir d'une commande"
+ ),
+ ),
+ 'ordering': ['-at'],
+ 'verbose_name_plural': 'Inventaires',
+ 'default_permissions': ('add',),
+ 'verbose_name': 'Inventaire',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='inventoryarticle',
+ options={
+ 'default_permissions': (),
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='operation',
+ options={
+ 'permissions': (
+ ('perform_deposit', 'Effectuer une charge'),
+ (
+ 'perform_negative_operations',
+ 'Enregistrer des commandes en négatif'
+ ),
+ (
+ 'override_frozen_protection',
+ "Forcer le gel d'un compte"
+ ),
+ (
+ 'cancel_old_operations',
+ 'Annuler des commandes non récentes'
+ ),
+ (
+ 'perform_commented_operations',
+ 'Enregistrer des commandes avec commentaires'
+ ),
+ ),
+ 'default_permissions': (),
+ 'verbose_name_plural': 'Opérations',
+ 'verbose_name': 'Opération',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='operationgroup',
+ options={
+ 'default_permissions': (),
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='order',
+ options={
+ 'default_permissions': ('add',),
+ 'ordering': ['-at'],
+ 'verbose_name_plural': 'Commandes',
+ 'verbose_name': 'Commande',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='orderarticle',
+ options={
+ 'default_permissions': (),
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='supplier',
+ options={
+ 'default_permissions': ('change',),
+ 'verbose_name_plural': 'Fournisseurs',
+ 'verbose_name': 'Fournisseur',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='supplierarticle',
+ options={
+ 'default_permissions': (),
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='transfer',
+ options={
+ 'default_permissions': ('add',),
+ 'verbose_name_plural': 'Transferts',
+ 'verbose_name': 'Transfert',
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='transfergroup',
+ options={
+ 'default_permissions': (),
+ },
+ ),
+ ]
diff --git a/kfet/migrations/0061_update_permissions.py b/kfet/migrations/0061_update_permissions.py
new file mode 100644
index 00000000..2e30700e
--- /dev/null
+++ b/kfet/migrations/0061_update_permissions.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations
+from django.db.models import Q
+
+
+def update_permissions(apps, schema_editor):
+ Permission = apps.get_model('auth', 'Permission')
+ ContentType = apps.get_model('contenttypes', 'ContentType')
+ Account = apps.get_model('kfet', 'Account')
+
+ # If `kfet.is_team` permission exists, rename it.
+
+ Permission.objects.filter(
+ content_type__app_label='kfet',
+ content_type__model='account',
+ codename='is_team',
+ ).update(name="Membre de l'équipe")
+
+ # If `kfet.view_negs` permission exists, move it as
+ # `kfet.view_accountnegative`.
+
+ try:
+ view_negs_p = Permission.objects.get(
+ content_type__app_label='kfet',
+ content_type__model='accountnegative',
+ codename='view_negs',
+ )
+ except Permission.DoesNotExist:
+ pass
+ else:
+ # Avoid failure due to unicity constraint if migrations were partially
+ # applied.
+ # Because `view_negs` still exists here, it should be safe to consider
+ # that nothing uses `view_accountnegative` so that it can be deleted.
+ Permission.objects.filter(
+ content_type__app_label='kfet',
+ content_type__model='accountnegative',
+ codename='view_accountnegative',
+ ).delete()
+
+ view_negs_p.codename = 'view_accountnegative'
+ view_negs_p.name = 'Can view Compte en négatif'
+ view_negs_p.save()
+
+ # Delete unused permissions.
+
+ to_delete = {
+ 'account': ['delete_account'],
+ 'accountnegative': [
+ 'add_accountnegative', 'delete_accountnegative', 'view_negs'],
+ 'article': ['delete_article'],
+ 'articlecategory': ['add_articlecategory', 'delete_articlecategory'],
+ 'articlerule': '__all__',
+ 'checkout': ['delete_checkout'],
+ 'checkoutstatement': ['delete_checkoutstatement'],
+ 'checkouttransfer': '__all__',
+ 'inventory': ['change_inventory', 'delete_inventory'],
+ 'inventoryarticle': '__all__',
+ 'operation': ['add_operation', 'change_operation', 'delete_operation'],
+ 'operationgroup': '__all__',
+ 'order': ['change_order', 'delete_order'],
+ 'orderarticle': '__all__',
+ 'supplier': ['add_supplier', 'delete_supplier'],
+ 'supplierarticle': '__all__',
+ 'transfer': ['change_transfer', 'delete_transfer'],
+ 'transfergroup': '__all__',
+ }
+
+ to_delete_q = Q()
+
+ for model_name, codenames in to_delete.items():
+ if codenames == '__all__':
+ to_delete_q |= Q(content_type__model=model_name)
+ else:
+ to_delete_q |= Q(
+ content_type__model=model_name,
+ codename__in=codenames,
+ )
+
+ to_delete_q &= Q(content_type__app_label='kfet')
+
+ Permission.objects.filter(to_delete_q).delete()
+
+
+class Migration(migrations.Migration):
+ """
+ Data migration which performs permissions cleaning.
+ """
+ dependencies = [
+ ('kfet', '0060_change_models_opts'),
+ ('auth', '0006_require_contenttypes_0002'),
+ ('contenttypes', '0002_remove_content_type_name'),
+ ]
+
+ operations = [
+ migrations.RunPython(update_permissions),
+ ]
diff --git a/kfet/models.py b/kfet/models.py
index 62fd3ed6..82d769a5 100644
--- a/kfet/models.py
+++ b/kfet/models.py
@@ -8,6 +8,7 @@ from gestioncof.models import CofProfile
from django.utils.six.moves import reduce
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
+from django.utils.translation import ugettext_lazy as _
from django.db import transaction
from django.db.models import F
from datetime import date
@@ -83,9 +84,11 @@ class Account(models.Model):
blank = True, null = True, default = None)
class Meta:
+ verbose_name = _("Compte")
+ verbose_name_plural = _("Comptes")
+ default_permissions = ('add', 'change')
permissions = (
- ('is_team', 'Is part of the team'),
- ('manage_perms', 'Gérer les permissions K-Fêt'),
+ ('is_team', "Membre de l'équipe"),
('manage_addcosts', 'Gérer les majorations'),
('edit_balance_account', "Modifier la balance d'un compte"),
('change_account_password',
@@ -329,9 +332,9 @@ class AccountNegative(models.Model):
comment = models.CharField("commentaire", max_length=255, blank=True)
class Meta:
- permissions = (
- ('view_negs', 'Voir la liste des négatifs'),
- )
+ verbose_name = _("Compte en négatif")
+ verbose_name_plural = _("Comptes en négatif")
+ default_permissions = ('view', 'change')
@property
def until_default(self):
@@ -355,7 +358,10 @@ class Checkout(models.Model):
return reverse('kfet.checkout.read', kwargs={'pk': self.pk})
class Meta:
+ verbose_name = _("Caisse")
+ verbose_name_plural = _("Caisses")
ordering = ['-valid_to']
+ default_permissions = ('add', 'change')
def __str__(self):
return self.name
@@ -370,6 +376,10 @@ class CheckoutTransfer(models.Model):
amount = models.DecimalField(
max_digits = 6, decimal_places = 2)
+ class Meta:
+ default_permissions = ()
+
+
@python_2_unicode_compatible
class CheckoutStatement(models.Model):
by = models.ForeignKey(
@@ -408,6 +418,11 @@ class CheckoutStatement(models.Model):
"montant des chèques",
default=0, max_digits=6, decimal_places=2)
+ class Meta:
+ verbose_name = _("Relevé de caisse")
+ verbose_name_plural = _("Relevés de caisses")
+ default_permissions = ('add', 'change')
+
def __str__(self):
return '%s %s' % (self.checkout, self.at)
@@ -448,6 +463,11 @@ class ArticleCategory(models.Model):
"appliquée aux articles de "
"cette catégorie.")
+ class Meta:
+ verbose_name = _("Catégorie d'articles")
+ verbose_name_plural = _("Catégories d'articles")
+ default_permissions = ('change',)
+
def __str__(self):
return self.name
@@ -484,6 +504,11 @@ class Article(models.Model):
"capacité du contenant",
blank = True, null = True, default = None)
+ class Meta:
+ verbose_name = _("Article")
+ verbose_name_plural = _("Articles")
+ default_permissions = ('add', 'change')
+
def __str__(self):
return '%s - %s' % (self.category.name, self.name)
@@ -503,6 +528,10 @@ class ArticleRule(models.Model):
related_name = "rule_to")
ratio = models.PositiveSmallIntegerField()
+ class Meta:
+ default_permissions = ()
+
+
class Inventory(models.Model):
articles = models.ManyToManyField(
Article,
@@ -519,7 +548,10 @@ class Inventory(models.Model):
blank = True, null = True, default = None)
class Meta:
+ verbose_name = _("Inventaire")
+ verbose_name_plural = _("Inventaires")
ordering = ['-at']
+ default_permissions = ('add',)
permissions = (
('order_to_inventory', "Générer un inventaire à partir d'une commande"),
)
@@ -534,6 +566,9 @@ class InventoryArticle(models.Model):
stock_new = models.IntegerField()
stock_error = models.IntegerField(default = 0)
+ class Meta:
+ default_permissions = ()
+
def save(self, *args, **kwargs):
# S'il s'agit d'un inventaire provenant d'une livraison, il n'y a pas
# d'erreur
@@ -553,6 +588,11 @@ class Supplier(models.Model):
phone = models.CharField("téléphone", max_length = 10)
comment = models.TextField("commentaire")
+ class Meta:
+ verbose_name = _("Fournisseur")
+ verbose_name_plural = _("Fournisseurs")
+ default_permissions = ('change',)
+
def __str__(self):
return self.name
@@ -572,6 +612,9 @@ class SupplierArticle(models.Model):
max_digits = 7, decimal_places = 4,
blank = True, null = True, default = None)
+ class Meta:
+ default_permissions = ()
+
class Order(models.Model):
supplier = models.ForeignKey(
Supplier, on_delete = models.PROTECT,
@@ -585,7 +628,10 @@ class Order(models.Model):
max_digits = 6, decimal_places = 2, default = 0)
class Meta:
+ verbose_name = _("Commande")
+ verbose_name_plural = _("Commandes")
ordering = ['-at']
+ default_permissions = ('add',)
class OrderArticle(models.Model):
order = models.ForeignKey(
@@ -595,6 +641,9 @@ class OrderArticle(models.Model):
quantity_ordered = models.IntegerField()
quantity_received = models.IntegerField(default = 0)
+ class Meta:
+ default_permissions = ()
+
class TransferGroup(models.Model):
at = models.DateTimeField(default=timezone.now)
# Optional
@@ -606,6 +655,9 @@ class TransferGroup(models.Model):
related_name = "+",
blank = True, null = True, default = None)
+ class Meta:
+ default_permissions = ()
+
class Transfer(models.Model):
group = models.ForeignKey(
@@ -626,6 +678,11 @@ class Transfer(models.Model):
canceled_at = models.DateTimeField(
null=True, blank=True, default=None)
+ class Meta:
+ verbose_name = _("Transfert")
+ verbose_name_plural = _("Transferts")
+ default_permissions = ('add',)
+
def __str__(self):
return '{} -> {}: {}€'.format(self.from_acc, self.to_acc, self.amount)
@@ -651,6 +708,9 @@ class OperationGroup(models.Model):
related_name = "+",
blank = True, null = True, default = None)
+ class Meta:
+ default_permissions = ()
+
def __str__(self):
return ', '.join(map(str, self.opes.all()))
@@ -701,6 +761,9 @@ class Operation(models.Model):
blank=True, null=True, default=None)
class Meta:
+ verbose_name = _("Opération")
+ verbose_name_plural = _("Opérations")
+ default_permissions = ()
permissions = (
('perform_deposit', 'Effectuer une charge'),
('perform_negative_operations',
diff --git a/kfet/templates/kfet/account.html b/kfet/templates/kfet/account.html
index c4147b14..da98f521 100644
--- a/kfet/templates/kfet/account.html
+++ b/kfet/templates/kfet/account.html
@@ -22,11 +22,11 @@
- {% if perms.kfet.manage_perms %}
+ {% if perms.kfetauth.view_group %}
Permissions
{% endif %}
- {% if perms.kfet.view_negs %}
+ {% if perms.kfet.view_accountnegative %}
Négatifs
{% endif %}
diff --git a/kfet/urls.py b/kfet/urls.py
index eb4f8311..192fe24c 100644
--- a/kfet/urls.py
+++ b/kfet/urls.py
@@ -54,16 +54,17 @@ urlpatterns = [
url(r'^accounts/groups$', views.account_group,
name='kfet.account.group'),
url(r'^accounts/groups/new$',
- permission_required('kfet.manage_perms')
+ permission_required('kfetauth.add_group')
(views.AccountGroupCreate.as_view()),
name='kfet.account.group.create'),
url(r'^accounts/groups/(?P\d+)/edit$',
- permission_required('kfet.manage_perms')
+ permission_required('kfetauth.change_group')
(views.AccountGroupUpdate.as_view()),
name='kfet.account.group.update'),
+
url(r'^accounts/negatives$',
- permission_required('kfet.view_negs')
+ permission_required('kfet.view_accountnegative')
(views.AccountNegativeList.as_view()),
name='kfet.account.negative'),