Improve right handling for accounting

This commit is contained in:
Skia 2016-05-09 11:49:01 +02:00
parent 39661b8de7
commit 27805640a1
11 changed files with 240 additions and 60 deletions

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('club', '0001_initial'),
('accounting', '0002_auto_20160502_0952'),
]
operations = [
migrations.AddField(
model_name='bankaccount',
name='club',
field=models.OneToOneField(to='club.Club', related_name='bank_accounts', default=1),
preserve_default=False,
),
migrations.AddField(
model_name='product',
name='club',
field=models.OneToOneField(to='club.Club', related_name='products', default=1),
preserve_default=False,
),
migrations.AlterField(
model_name='clubaccount',
name='club',
field=models.OneToOneField(related_name='club_account', to='club.Club'),
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0003_auto_20160509_0712'),
]
operations = [
migrations.AlterField(
model_name='product',
name='club',
field=models.ForeignKey(related_name='products', to='club.Club'),
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0004_auto_20160509_0715'),
]
operations = [
migrations.AlterField(
model_name='bankaccount',
name='club',
field=models.ForeignKey(related_name='bank_accounts', to='club.Club'),
),
]

View File

@ -39,19 +39,7 @@ class Customer(models.Model):
def __str__(self): def __str__(self):
return self.user.username return self.user.username
class AccountingMixin(): class ProductType(models.Model):
"""
Mixin providing the rights managment for all accounting classes
"""
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
return True
return False
class ProductType(models.Model, AccountingMixin):
""" """
This describes a product type This describes a product type
Useful only for categorizing, changes are made at the product level for now Useful only for categorizing, changes are made at the product level for now
@ -60,10 +48,18 @@ class ProductType(models.Model, AccountingMixin):
description = models.TextField(_('description'), null=True, blank=True) description = models.TextField(_('description'), null=True, blank=True)
icon = models.ImageField(upload_to='products', null=True, blank=True) icon = models.ImageField(upload_to='products', null=True, blank=True)
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
return True
return False
def __str__(self): def __str__(self):
return self.name return self.name
class Product(models.Model, AccountingMixin): class Product(models.Model):
""" """
This describes a product, with all its related informations This describes a product, with all its related informations
""" """
@ -75,14 +71,35 @@ class Product(models.Model, AccountingMixin):
selling_price = CurrencyField(_('selling price')) selling_price = CurrencyField(_('selling price'))
special_selling_price = CurrencyField(_('special selling price')) special_selling_price = CurrencyField(_('special selling price'))
icon = models.ImageField(upload_to='products', null=True, blank=True) icon = models.ImageField(upload_to='products', null=True, blank=True)
club = models.ForeignKey(Club, related_name="products")
def is_owned_by(self, user): # TODO do this for all models
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
return True
return False
def __str__(self): def __str__(self):
return self.name return self.name
class BankAccount(models.Model, AccountingMixin): class BankAccount(models.Model):
name = models.CharField(_('name'), max_length=30) name = models.CharField(_('name'), max_length=30)
rib = models.CharField(_('rib'), max_length=255, blank=True) rib = models.CharField(_('rib'), max_length=255, blank=True)
number = models.CharField(_('account number'), max_length=255, blank=True) number = models.CharField(_('account number'), max_length=255, blank=True)
club = models.ForeignKey(Club, related_name="bank_accounts")
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
return True
m = self.club.get_membership_for(user)
if m is not None and m.role >= 7:
return True
return False
def get_absolute_url(self): def get_absolute_url(self):
return reverse('accounting:bank_details', kwargs={'b_account_id': self.id}) return reverse('accounting:bank_details', kwargs={'b_account_id': self.id})
@ -90,18 +107,35 @@ class BankAccount(models.Model, AccountingMixin):
def __str__(self): def __str__(self):
return self.name return self.name
class ClubAccount(models.Model, AccountingMixin): class ClubAccount(models.Model):
name = models.CharField(_('name'), max_length=30) name = models.CharField(_('name'), max_length=30)
club = models.OneToOneField(Club, related_name="club_accounts") club = models.OneToOneField(Club, related_name="club_account")
bank_account = models.ForeignKey(BankAccount, related_name="club_accounts") bank_account = models.ForeignKey(BankAccount, related_name="club_accounts")
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
return True
return False
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
m = self.club.get_membership_for(user)
if m is not None and m.role >= 7:
return True
return False
def get_absolute_url(self): def get_absolute_url(self):
return reverse('accounting:club_details', kwargs={'c_account_id': self.id}) return reverse('accounting:club_details', kwargs={'c_account_id': self.id})
def __str__(self): def __str__(self):
return self.name return self.name
class GeneralJournal(models.Model, AccountingMixin): class GeneralJournal(models.Model):
""" """
Class storing all the operations for a period of time Class storing all the operations for a period of time
""" """
@ -111,13 +145,29 @@ class GeneralJournal(models.Model, AccountingMixin):
closed = models.BooleanField(_('is closed'), default=False) closed = models.BooleanField(_('is closed'), default=False)
club_account = models.ForeignKey(ClubAccount, related_name="journals", null=False) club_account = models.ForeignKey(ClubAccount, related_name="journals", null=False)
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
return True
return False
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if self.club_account.can_be_edited_by(user):
return True
return False
def get_absolute_url(self): def get_absolute_url(self):
return reverse('accounting:journal_details', kwargs={'j_id': self.id}) return reverse('accounting:journal_details', kwargs={'j_id': self.id})
def __str__(self): def __str__(self):
return self.name return self.name
class AccountingType(models.Model, AccountingMixin): class AccountingType(models.Model):
""" """
Class describing the accounting types. Class describing the accounting types.
@ -127,13 +177,21 @@ class AccountingType(models.Model, AccountingMixin):
label = models.CharField(_('label'), max_length=60) label = models.CharField(_('label'), max_length=60)
movement_type = models.CharField(_('movement type'), choices=[('credit', 'Credit'), ('debit', 'Debit'), ('neutral', 'Neutral')], max_length=12) movement_type = models.CharField(_('movement type'), choices=[('credit', 'Credit'), ('debit', 'Debit'), ('neutral', 'Neutral')], max_length=12)
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
return True
return False
def get_absolute_url(self): def get_absolute_url(self):
return reverse('accounting:type_list') return reverse('accounting:type_list')
def __str__(self): def __str__(self):
return self.movement_type+" - "+self.code+" - "+self.label return self.movement_type+" - "+self.code+" - "+self.label
class Operation(models.Model, AccountingMixin): class Operation(models.Model):
""" """
An operation is a line in the journal, a debit or a credit An operation is a line in the journal, a debit or a credit
""" """
@ -146,6 +204,26 @@ class Operation(models.Model, AccountingMixin):
done = models.BooleanField(_('is done'), default=False) done = models.BooleanField(_('is done'), default=False)
type = models.ForeignKey(AccountingType, related_name="operations") type = models.ForeignKey(AccountingType, related_name="operations")
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']):
return True
m = self.journal.club_account.get_membership_for(user)
if m is not None and m.role >= 7:
return True
return False
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if self.journal.can_be_edited_by(user):
return True
return False
def get_absolute_url(self): def get_absolute_url(self):
return reverse('accounting:journal_details', kwargs={'j_id': self.journal.id}) return reverse('accounting:journal_details', kwargs={'j_id': self.journal.id})

View File

@ -24,7 +24,7 @@ class AccountingTypeEditView(CanViewMixin, UpdateView):
fields = ['code', 'label', 'movement_type'] fields = ['code', 'label', 'movement_type']
template_name = 'accounting/account_edit.jinja' template_name = 'accounting/account_edit.jinja'
class AccountingTypeCreateView(CanEditMixin, CreateView): class AccountingTypeCreateView(CanEditPropMixin, CreateView):
""" """
Create an accounting type (for the admins) Create an accounting type (for the admins)
""" """
@ -58,7 +58,7 @@ class BankAccountDetailView(CanViewMixin, DetailView):
pk_url_kwarg = "b_account_id" pk_url_kwarg = "b_account_id"
template_name = 'accounting/bank_account_details.jinja' template_name = 'accounting/bank_account_details.jinja'
class BankAccountCreateView(CanEditMixin, CreateView): class BankAccountCreateView(CanEditPropMixin, CreateView):
""" """
Create a bank account (for the admins) Create a bank account (for the admins)
""" """
@ -66,7 +66,7 @@ class BankAccountCreateView(CanEditMixin, CreateView):
fields = ['name', 'rib', 'number'] fields = ['name', 'rib', 'number']
template_name = 'accounting/account_edit.jinja' template_name = 'accounting/account_edit.jinja'
class BankAccountDeleteView(CanEditMixin, DeleteView): # TODO change Delete to Close class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
""" """
Delete a bank account (for the admins) Delete a bank account (for the admins)
""" """
@ -94,7 +94,7 @@ class ClubAccountDetailView(CanViewMixin, DetailView):
pk_url_kwarg = "c_account_id" pk_url_kwarg = "c_account_id"
template_name = 'accounting/club_account_details.jinja' template_name = 'accounting/club_account_details.jinja'
class ClubAccountCreateView(CanEditMixin, CreateView): class ClubAccountCreateView(CanEditPropMixin, CreateView):
""" """
Create a club account (for the admins) Create a club account (for the admins)
""" """
@ -102,7 +102,7 @@ class ClubAccountCreateView(CanEditMixin, CreateView):
fields = ['name', 'club', 'bank_account'] fields = ['name', 'club', 'bank_account']
template_name = 'accounting/account_edit.jinja' template_name = 'accounting/account_edit.jinja'
class ClubAccountDeleteView(CanEditMixin, DeleteView): # TODO change Delete to Close class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
""" """
Delete a club account (for the admins) Delete a club account (for the admins)
""" """

View File

@ -0,0 +1,12 @@
{% extends "core/base.jinja" %}
{% block content %}
<h3>Club tools</h3>
<p><a href="{{ url('club:club_view', club_id=object.id) }}">Back to club</a></p>
<ul>
<li><a href="{{ url('accounting:club_details', c_account_id=object.club_account.id) }}">{{ object }}</a></li>
</ul>
{% endblock %}

View File

@ -9,9 +9,6 @@ urlpatterns = [
url(r'^(?P<club_id>[0-9]+)/edit$', ClubEditView.as_view(), name='club_edit'), url(r'^(?P<club_id>[0-9]+)/edit$', ClubEditView.as_view(), name='club_edit'),
url(r'^(?P<club_id>[0-9]+)/members$', ClubMembersView.as_view(), name='club_members'), url(r'^(?P<club_id>[0-9]+)/members$', ClubMembersView.as_view(), name='club_members'),
url(r'^(?P<club_id>[0-9]+)/prop$', ClubEditPropView.as_view(), name='club_prop'), url(r'^(?P<club_id>[0-9]+)/prop$', ClubEditPropView.as_view(), name='club_prop'),
#url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='club_tools'), url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
## API
#url(r'^api/markdown$', render_markdown, name='api_markdown'),
] ]

View File

@ -25,6 +25,14 @@ class ClubView(CanViewMixin, DetailView):
pk_url_kwarg = "club_id" pk_url_kwarg = "club_id"
template_name = 'club/club_detail.jinja' template_name = 'club/club_detail.jinja'
class ClubToolsView(CanViewMixin, DetailView):
"""
Tools page of a Club
"""
model = Club
pk_url_kwarg = "club_id"
template_name = 'club/club_tools.jinja'
class ClubMemberForm(forms.ModelForm): class ClubMemberForm(forms.ModelForm):
""" """
Form handling the members of a club Form handling the members of a club

View File

@ -5,7 +5,7 @@ from django.conf import settings
from core.models import Group, User, Page, PageRev from core.models import Group, User, Page, PageRev
from accounting.models import Customer, GeneralJournal, ProductType, Product, BankAccount, ClubAccount, Operation from accounting.models import Customer, GeneralJournal, ProductType, Product, BankAccount, ClubAccount, Operation, AccountingType
from club.models import Club, Membership from club.models import Club, Membership
from subscription.models import Subscription, Subscriber from subscription.models import Subscription, Subscriber
@ -41,13 +41,13 @@ Welcome to the wiki page!
# Here we add a lot of test datas, that are not necessary for the Sith, but that provide a basic development environment # Here we add a lot of test datas, that are not necessary for the Sith, but that provide a basic development environment
if not options['prod']: if not options['prod']:
# Adding user Skia # Adding user Skia
s = User(username='skia', last_name="Kia", first_name="S'", skia = User(username='skia', last_name="Kia", first_name="S'",
email="skia@git.an", email="skia@git.an",
date_of_birth="1942-06-12") date_of_birth="1942-06-12")
s.set_password("plop") skia.set_password("plop")
s.save() skia.save()
s.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id] skia.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
s.save() skia.save()
# Adding user public # Adding user public
public = User(username='public', last_name="Not subscribed", first_name="Public", public = User(username='public', last_name="Not subscribed", first_name="Public",
email="public@git.an", email="public@git.an",
@ -66,6 +66,16 @@ Welcome to the wiki page!
subscriber.save() subscriber.save()
subscriber.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id] subscriber.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
subscriber.save() subscriber.save()
# Adding user Comptable
comptable = User(username='comptable', last_name="Able", first_name="Compte",
email="compta@git.an",
date_of_birth="1942-06-12",
is_superuser=False, is_staff=False)
comptable.set_password("plop")
comptable.save()
comptable.view_groups=[Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id]
comptable.groups=[Group.objects.filter(name=settings.SITH_GROUPS['accounting-admin']['name']).first().id]
comptable.save()
# Adding user Guy # Adding user Guy
u = User(username='guy', last_name="Carlier", first_name="Guy", u = User(username='guy', last_name="Carlier", first_name="Guy",
email="guy@git.an", email="guy@git.an",
@ -86,21 +96,24 @@ Welcome to the wiki page!
# Adding syntax help page # Adding syntax help page
p = Page(name='Aide_sur_la_syntaxe') p = Page(name='Aide_sur_la_syntaxe')
p.save() p.save()
PageRev(page=p, title="Aide sur la syntaxe", author=s, content=""" PageRev(page=p, title="Aide sur la syntaxe", author=skia, content="""
Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site. Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site.
""").save() """).save()
# Adding README # Adding README
p = Page(name='README') p = Page(name='README')
p.save() p.save()
p.view_groups=[settings.SITH_GROUPS['public']['id']] p.view_groups=[settings.SITH_GROUPS['public']['id']]
p.set_lock(s) p.set_lock(skia)
p.save() p.save()
with open(os.path.join(root_path)+'/README.md', 'r') as rm: with open(os.path.join(root_path)+'/README.md', 'r') as rm:
PageRev(page=p, title="REAMDE", author=s, content=rm.read()).save() PageRev(page=p, title="REAMDE", author=skia, content=rm.read()).save()
# Subscription # Subscription
## Skia ## Skia
Subscription(member=Subscriber.objects.filter(pk=s.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0], Subscription(member=Subscriber.objects.filter(pk=skia.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0]).save()
## Comptable
Subscription(member=Subscriber.objects.filter(pk=comptable.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0]).save() payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0]).save()
## Richard ## Richard
Subscription(member=Subscriber.objects.filter(pk=r.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0], Subscription(member=Subscriber.objects.filter(pk=r.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
@ -119,28 +132,29 @@ Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site.
address="Woenzel", parent=guyut).save() address="Woenzel", parent=guyut).save()
Club(name="BdF", unix_name="bdf", Club(name="BdF", unix_name="bdf",
address="Guyéuéyuéyuyé").save() address="Guyéuéyuéyuyé").save()
Membership(user=s, club=ae, role=3, description="").save() Membership(user=skia, club=ae, role=3, description="").save()
troll = Club(name="Troll Penché", unix_name="troll", troll = Club(name="Troll Penché", unix_name="troll",
address="Terre Du Milieu", parent=ae) address="Terre Du Milieu", parent=ae)
troll.save() troll.save()
# Accounting test values: # Accounting test values:
Customer(user=s, account_id="6568j").save() Customer(user=skia, account_id="6568j").save()
p = ProductType(name="Bières bouteilles") p = ProductType(name="Bières bouteilles")
p.save() p.save()
Product(name="Barbar", code="BARB", product_type=p, purchase_price="1.50", selling_price="1.7", Product(name="Barbar", code="BARB", product_type=p, purchase_price="1.50", selling_price="1.7",
special_selling_price="1.6").save() special_selling_price="1.6", club=ae).save()
Product(name="Chimay", code="CBLE", product_type=p, purchase_price="1.50", selling_price="1.7", Product(name="Chimay", code="CBLE", product_type=p, purchase_price="1.50", selling_price="1.7",
special_selling_price="1.6").save() special_selling_price="1.6", club=ae).save()
Product(name="Corsendonk", code="CORS", product_type=p, purchase_price="1.50", selling_price="1.7", Product(name="Corsendonk", code="CORS", product_type=p, purchase_price="1.50", selling_price="1.7",
special_selling_price="1.6").save() special_selling_price="1.6", club=ae).save()
Product(name="Carolus", code="CARO", product_type=p, purchase_price="1.50", selling_price="1.7", Product(name="Carolus", code="CARO", product_type=p, purchase_price="1.50", selling_price="1.7",
special_selling_price="1.6").save() special_selling_price="1.6", club=ae).save()
BankAccount(name="AE TG").save() BankAccount(name="AE TG", club=ae).save()
BankAccount(name="Carte AE").save() BankAccount(name="Carte AE", club=ae).save()
ba = BankAccount(name="AE TI") ba = BankAccount(name="AE TI", club=ae)
ba.save() ba.save()
ca = ClubAccount(name="Troll Penché", bank_account=ba, club=troll) ca = ClubAccount(name="Troll Penché", bank_account=ba, club=troll)
ca.save() ca.save()
AccountingType(code=666, label="Guy credit", movement_type='credit').save()
AccountingType(code=4000, label="Guy debit", movement_type='debit').save()

View File

@ -207,14 +207,12 @@ class User(AbstractBaseUser, PermissionsMixin):
""" """
Determine if the object is owned by the user Determine if the object is owned by the user
""" """
if not hasattr(obj, "owner_group"):
return False
if (self.is_superuser or self.is_in_group(obj.owner_group.name) or
self.has_perm(obj.__class__.__module__.split('.')[0]+".change_prop_"+obj.__class__.__name__.lower()) or
self.groups.filter(id=settings.SITH_GROUPS['root']['id']).exists()):
return True
if hasattr(obj, "is_owned_by") and obj.is_owned_by(self): if hasattr(obj, "is_owned_by") and obj.is_owned_by(self):
return True return True
if hasattr(obj, "owner_group") and self.is_in_group(obj.owner_group.name):
return False
if self.is_superuser or self.is_in_group(settings.SITH_GROUPS['root']['name']):
return True
return False return False
def can_edit(self, obj): def can_edit(self, obj):
@ -231,8 +229,6 @@ class User(AbstractBaseUser, PermissionsMixin):
return True return True
if hasattr(obj, "can_be_edited_by") and obj.can_be_edited_by(self): if hasattr(obj, "can_be_edited_by") and obj.can_be_edited_by(self):
return True return True
if self.has_perm(obj.__class__.__module__.split('.')[0]+".change_"+obj.__class__.__name__.lower()):
return True
return False return False
def can_view(self, obj): def can_view(self, obj):
@ -247,8 +243,6 @@ class User(AbstractBaseUser, PermissionsMixin):
return True return True
if hasattr(obj, "can_be_viewed_by") and obj.can_be_viewed_by(self): if hasattr(obj, "can_be_viewed_by") and obj.can_be_viewed_by(self):
return True return True
if self.has_perm(obj.__class__.__module__.split('.')[0]+".view_"+obj.__class__.__name__.lower()):
return True
return False return False
def can_be_edited_by(self, user): def can_be_edited_by(self, user):

View File

@ -8,6 +8,7 @@
<h3>User Tools</h3> <h3>User Tools</h3>
<p><a href="{{ url('core:user_profile', user_id=request.user.id) }}">Back to profile</a></p> <p><a href="{{ url('core:user_profile', user_id=request.user.id) }}">Back to profile</a></p>
<h4>Sith management</h4>
<ul> <ul>
{% if user.is_in_group(settings.SITH_GROUPS['root']['name']) %} {% if user.is_in_group(settings.SITH_GROUPS['root']['name']) %}
<li><a href="{{ url('core:group_list') }}">Groups</a></li> <li><a href="{{ url('core:group_list') }}">Groups</a></li>
@ -15,11 +16,17 @@
{% if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']) %} {% if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']) %}
<li><a href="{{ url('accounting:bank_list') }}">Accounting</a></li> <li><a href="{{ url('accounting:bank_list') }}">Accounting</a></li>
{% endif %} {% endif %}
{% if user.is_in_group(settings.SITH_GROUPS['root']['name']) or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) %} {% if user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_in_group(settings.SITH_GROUPS['root']['name']) %}
<li><a href="{{ url('subscription:subscription') }}">Subscriptions</a></li> <li><a href="{{ url('subscription:subscription') }}">Subscriptions</a></li>
<li><a href="{{ url('counter:admin_list') }}">Counters management</a></li> <li><a href="{{ url('counter:admin_list') }}">Counters management</a></li>
{% endif %} {% endif %}
</ul> </ul>
<h4>Clubs</h4>
<ul>
{% for m in user.membership.filter(end_date=None).all() %}
<li><a href="{{ url('club:tools', club_id=m.club.id) }}">{{ m.club }}</a></li>
{% endfor %}
</ul>
{% endblock %} {% endblock %}