Club model implementation, various other changes

This commit is contained in:
Skia 2016-01-29 15:20:00 +01:00
parent 7f4955d15c
commit 4322318c31
26 changed files with 267 additions and 204 deletions

View File

@ -9,64 +9,64 @@ import accounting.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('core', '0005_auto_20160128_0842'), ('core', '0001_initial'),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Customer', name='Customer',
fields=[ fields=[
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, primary_key=True, serialize=False)), ('user', models.OneToOneField(primary_key=True, to=settings.AUTH_USER_MODEL, serialize=False)),
('account_id', models.CharField(unique=True, verbose_name='account id', max_length=10)), ('account_id', models.CharField(max_length=10, unique=True, verbose_name='account id')),
], ],
options={ options={
'verbose_name': 'customer',
'verbose_name_plural': 'customers', 'verbose_name_plural': 'customers',
'verbose_name': 'customer',
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='GeneralJournal', name='GeneralJournal',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start_date', models.DateField(verbose_name='start date')), ('start_date', models.DateField(verbose_name='start date')),
('end_date', models.DateField(null=True, verbose_name='end date', blank=True, default=None)), ('end_date', models.DateField(blank=True, default=None, null=True, verbose_name='end date')),
('name', models.CharField(verbose_name='name', max_length=30)), ('name', models.CharField(max_length=30, verbose_name='name')),
('closed', models.BooleanField(default=False, verbose_name='is closed')), ('closed', models.BooleanField(default=False, verbose_name='is closed')),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='GenericInvoice', name='GenericInvoice',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(verbose_name='name', max_length=100)), ('name', models.CharField(max_length=100, verbose_name='name')),
('journal', models.ForeignKey(to='accounting.GeneralJournal', related_name='invoices')), ('journal', models.ForeignKey(related_name='invoices', to='accounting.GeneralJournal')),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Product', name='Product',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(verbose_name='name', max_length=30)), ('name', models.CharField(max_length=30, verbose_name='name')),
('description', models.TextField(verbose_name='description')), ('description', models.TextField(blank=True, verbose_name='description')),
('code', models.CharField(verbose_name='code', max_length=10)), ('code', models.CharField(max_length=10, verbose_name='code')),
('purchase_price', accounting.models.CurrencyField(max_digits=12, decimal_places=2, verbose_name='purchase price')), ('purchase_price', accounting.models.CurrencyField(decimal_places=2, max_digits=12, verbose_name='purchase price')),
('selling_price', accounting.models.CurrencyField(max_digits=12, decimal_places=2, verbose_name='selling price')), ('selling_price', accounting.models.CurrencyField(decimal_places=2, max_digits=12, verbose_name='selling price')),
('special_selling_price', accounting.models.CurrencyField(max_digits=12, decimal_places=2, verbose_name='special selling price')), ('special_selling_price', accounting.models.CurrencyField(decimal_places=2, max_digits=12, verbose_name='special selling price')),
('icon', models.ImageField(null=True, upload_to='products')), ('icon', models.ImageField(blank=True, upload_to='products', null=True)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='ProductType', name='ProductType',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(verbose_name='name', max_length=30)), ('name', models.CharField(max_length=30, verbose_name='name')),
('description', models.TextField(verbose_name='description')), ('description', models.TextField(blank=True, null=True, verbose_name='description')),
('icon', models.ImageField(null=True, upload_to='products')), ('icon', models.ImageField(blank=True, upload_to='products', null=True)),
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='product', model_name='product',
name='product_type', name='product_type',
field=models.ForeignKey(null=True, to='accounting.ProductType', related_name='products'), field=models.ForeignKey(related_name='products', blank=True, to='accounting.ProductType', null=True),
), ),
] ]

View File

@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='product',
name='description',
field=models.TextField(verbose_name='description', blank=True),
),
migrations.AlterField(
model_name='product',
name='icon',
field=models.ImageField(null=True, upload_to='products', blank=True),
),
migrations.AlterField(
model_name='product',
name='product_type',
field=models.ForeignKey(to='accounting.ProductType', null=True, blank=True, related_name='products'),
),
migrations.AlterField(
model_name='producttype',
name='description',
field=models.TextField(verbose_name='description', null=True, blank=True),
),
migrations.AlterField(
model_name='producttype',
name='icon',
field=models.ImageField(null=True, upload_to='products', blank=True),
),
]

0
club/__init__.py Normal file
View File

8
club/admin.py Normal file
View File

@ -0,0 +1,8 @@
from django.contrib import admin
from club.models import Club, Membership
admin.site.register(Club)
admin.site.register(Membership)

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.core.validators
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Club',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)),
('name', models.CharField(max_length=30, verbose_name='name')),
('unix_name', models.CharField(unique=True, error_messages={'unique': 'A club with that unix name already exists.'}, validators=[django.core.validators.RegexValidator('^[a-z0-9][a-z0-9._-]*[a-z0-9]$', 'Enter a valid unix name. This value may contain only letters, numbers ./-/_ characters.')], verbose_name='unix name', max_length=30)),
('address', models.CharField(max_length=254, verbose_name='address')),
('parent', models.ForeignKey(related_name='children', blank=True, to='club.Club', null=True)),
],
),
migrations.CreateModel(
name='Membership',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)),
('start_date', models.DateField(auto_now=True, verbose_name='start date')),
('end_date', models.DateField(null=True, verbose_name='end date', blank=True)),
('role', models.IntegerField(verbose_name='role', choices=[(0, 'Membre'), (1, 'Membre actif'), (2, 'Membre du bureau'), (3, 'Responsable info'), (4, 'Secrétaire'), (5, 'Responsable com'), (7, 'Trésorier'), (8, 'Vice-Président'), (9, 'Vice-Président'), (10, 'Président')], default=0)),
('description', models.CharField(max_length=30, verbose_name='description', blank=True)),
('club', models.ForeignKey(related_name='members', to='club.Club')),
('user', models.ForeignKey(related_name='membership', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

77
club/models.py Normal file
View File

@ -0,0 +1,77 @@
from django.db import models
from django.core import validators
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from core.models import User
# Create your models here.
class Club(models.Model):
"""
The Club class, made as a tree to allow nice tidy organization
"""
name = models.CharField(_('name'), max_length=30)
parent = models.ForeignKey('Club', related_name='children', null=True, blank=True)
unix_name = models.CharField(_('unix name'), max_length=30, unique=True,
validators=[
validators.RegexValidator(
r'^[a-z0-9][a-z0-9._-]*[a-z0-9]$',
_('Enter a valid unix name. This value may contain only '
'letters, numbers ./-/_ characters.')
),
],
error_messages={
'unique': _("A club with that unix name already exists."),
},
)
address = models.CharField(_('address'), max_length=254)
# email = models.EmailField(_('email address'), unique=True) # This should, and will be generated automatically
def check_loop(self):
"""Raise a validation error when a loop is found within the parent list"""
objs = []
cur = self
while cur.parent is not None:
if cur in objs:
raise ValidationError(_('You can not make loops in clubs'))
objs.append(cur)
cur = cur.parent
def clean(self):
self.check_loop()
def __str__(self):
return self.name
class Membership(models.Model):
"""
The Membership class makes the connection between User and Clubs
Both Users and Clubs can have many Membership objects:
- a user can be a member of many clubs at a time
- a club can have many members at a time too
A User is currently member of all the Clubs where its Membership has an end_date set to null/None.
Otherwise, it's a past membership kept because it can be very useful to see who was in which Club in the past.
"""
user = models.ForeignKey(User, related_name="membership", null=False, blank=False)
club = models.ForeignKey(Club, related_name="members", null=False, blank=False)
start_date = models.DateField(_('start date'), auto_now=True)
end_date = models.DateField(_('end date'), null=True, blank=True)
role = models.IntegerField(_('role'), choices=sorted(settings.CLUB_ROLES.items()),
default=sorted(settings.CLUB_ROLES.items())[0][0])
description = models.CharField(_('description'), max_length=30, null=False, blank=True)
def clean(self):
if Membership.objects.filter(user=self.user).filter(club=self.club).filter(end_date=None).exclude(pk=self.pk).exists():
raise ValidationError(_('User is already member of that club'))
def __str__(self):
return self.club.name+' - '+self.user.username+' - '+settings.CLUB_ROLES[self.role]+str(
" - "+str(_('past member')) if self.end_date is not None else ""
)

3
club/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
club/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

@ -6,6 +6,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 from accounting.models import Customer, GeneralJournal, ProductType, Product
from club.models import Club
class Command(BaseCommand): class Command(BaseCommand):
help = "Set up a new instance of the Sith AE" help = "Set up a new instance of the Sith AE"
@ -28,6 +29,10 @@ class Command(BaseCommand):
u.save() u.save()
for g in settings.AE_GROUPS.values(): for g in settings.AE_GROUPS.values():
Group(id=g['id'], name=g['name']).save() Group(id=g['id'], name=g['name']).save()
Club(name=settings.AE_MAIN_CLUB['name'], unix_name=settings.AE_MAIN_CLUB['unix_name'],
address=settings.AE_MAIN_CLUB['address']).save()
# 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']:
print("Dev mode, adding some test data") print("Dev mode, adding some test data")
s = User(username='skia', last_name="Kia", first_name="S'", s = User(username='skia', last_name="Kia", first_name="S'",

View File

@ -2,10 +2,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.utils.timezone
import django.core.validators import django.core.validators
import django.db.models.deletion
from django.conf import settings from django.conf import settings
import django.db.models.deletion
import django.contrib.auth.models import django.contrib.auth.models
@ -19,19 +18,19 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='User', name='User',
fields=[ fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('password', models.CharField(verbose_name='password', max_length=128)), ('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(null=True, blank=True, verbose_name='last login')), ('last_login', models.DateTimeField(blank=True, verbose_name='last login', null=True)),
('is_superuser', models.BooleanField(help_text='Designates that this user has all permissions without explicitly assigning them.', default=False, verbose_name='superuser status')), ('is_superuser', models.BooleanField(help_text='Designates that this user has all permissions without explicitly assigning them.', default=False, verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 254 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=254, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], unique=True, verbose_name='username')), ('username', models.CharField(max_length=254, verbose_name='username', unique=True, help_text='Required. 254 characters or fewer. Letters, digits and @/./+/-/_ only.', validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], error_messages={'unique': 'A user with that username already exists.'})),
('first_name', models.CharField(verbose_name='first name', max_length=30)), ('first_name', models.CharField(max_length=30, verbose_name='first name')),
('last_name', models.CharField(verbose_name='last name', max_length=30)), ('last_name', models.CharField(max_length=30, verbose_name='last name')),
('email', models.EmailField(unique=True, verbose_name='email address', max_length=254)), ('email', models.EmailField(max_length=254, verbose_name='email address', unique=True)),
('date_of_birth', models.DateTimeField(verbose_name='date of birth')), ('date_of_birth', models.DateField(verbose_name='date of birth')),
('nick_name', models.CharField(blank=True, max_length=30)), ('nick_name', models.CharField(max_length=30, blank=True)),
('is_staff', models.BooleanField(help_text='Designates whether the user can log into this admin site.', default=False, verbose_name='staff status')), ('is_staff', models.BooleanField(help_text='Designates whether the user can log into this admin site.', default=False, verbose_name='staff status')),
('is_active', models.BooleanField(help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', default=True, verbose_name='active')), ('is_active', models.BooleanField(help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', default=True, verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), ('date_joined', models.DateField(auto_now_add=True, verbose_name='date joined')),
], ],
options={ options={
'verbose_name_plural': 'users', 'verbose_name_plural': 'users',
@ -45,20 +44,20 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Group', name='Group',
fields=[ fields=[
('group_ptr', models.OneToOneField(primary_key=True, to='auth.Group', parent_link=True, serialize=False, auto_created=True)), ('group_ptr', models.OneToOneField(serialize=False, primary_key=True, parent_link=True, to='auth.Group', auto_created=True)),
], ],
bases=('auth.group',), bases=('auth.group',),
), ),
migrations.CreateModel( migrations.CreateModel(
name='Page', name='Page',
fields=[ fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('name', models.CharField(verbose_name='page name', max_length=30)), ('name', models.CharField(max_length=30, verbose_name='page name')),
('full_name', models.CharField(blank=True, verbose_name='page name', max_length=255)), ('_full_name', models.CharField(max_length=255, verbose_name='page name', blank=True)),
('edit_group', models.ManyToManyField(blank=True, to='core.Group', related_name='editable_page')), ('edit_group', models.ManyToManyField(to='core.Group', related_name='editable_page', blank=True)),
('owner_group', models.ForeignKey(to='core.Group', default=1, related_name='owned_page')), ('owner_group', models.ForeignKey(to='core.Group', related_name='owned_page', default=1)),
('parent', models.ForeignKey(null=True, to='core.Page', on_delete=django.db.models.deletion.SET_NULL, blank=True, related_name='children')), ('parent', models.ForeignKey(to='core.Page', on_delete=django.db.models.deletion.SET_NULL, null=True, related_name='children', blank=True)),
('view_group', models.ManyToManyField(blank=True, to='core.Group', related_name='viewable_page')), ('view_group', models.ManyToManyField(to='core.Group', related_name='viewable_page', blank=True)),
], ],
options={ options={
'permissions': (('change_prop_page', "Can change the page's properties (groups, ...)"), ('view_page', 'Can view the page')), 'permissions': (('change_prop_page', "Can change the page's properties (groups, ...)"), ('view_page', 'Can view the page')),
@ -67,10 +66,10 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='PageRev', name='PageRev',
fields=[ fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('title', models.CharField(blank=True, verbose_name='page title', max_length=255)), ('title', models.CharField(max_length=255, verbose_name='page title', blank=True)),
('content', models.TextField(blank=True, verbose_name='page content')), ('content', models.TextField(verbose_name='page content', blank=True)),
('date', models.DateTimeField(verbose_name='date', auto_now=True)), ('date', models.DateTimeField(auto_now=True, verbose_name='date')),
('author', models.ForeignKey(related_name='page_rev', to=settings.AUTH_USER_MODEL)), ('author', models.ForeignKey(related_name='page_rev', to=settings.AUTH_USER_MODEL)),
('page', models.ForeignKey(related_name='revisions', to='core.Page')), ('page', models.ForeignKey(related_name='revisions', to='core.Page')),
], ],
@ -81,27 +80,27 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='user', model_name='user',
name='edit_group', name='edit_group',
field=models.ManyToManyField(blank=True, to='core.Group', related_name='editable_user'), field=models.ManyToManyField(to='core.Group', related_name='editable_user', blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='user', model_name='user',
name='groups', name='groups',
field=models.ManyToManyField(help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', to='auth.Group', blank=True, verbose_name='groups', related_query_name='user', related_name='user_set'), field=models.ManyToManyField(to='auth.Group', related_query_name='user', verbose_name='groups', related_name='user_set', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='user', model_name='user',
name='owner_group', name='owner_group',
field=models.ForeignKey(to='core.Group', default=1, related_name='owned_user'), field=models.ForeignKey(to='core.Group', related_name='owned_user', default=1),
), ),
migrations.AddField( migrations.AddField(
model_name='user', model_name='user',
name='user_permissions', name='user_permissions',
field=models.ManyToManyField(help_text='Specific permissions for this user.', to='auth.Permission', blank=True, verbose_name='user permissions', related_query_name='user', related_name='user_set'), field=models.ManyToManyField(to='auth.Permission', related_query_name='user', verbose_name='user permissions', related_name='user_set', help_text='Specific permissions for this user.', blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='user', model_name='user',
name='view_group', name='view_group',
field=models.ManyToManyField(blank=True, to='core.Group', related_name='viewable_user'), field=models.ManyToManyField(to='core.Group', related_name='viewable_user', blank=True),
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='page', name='page',

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='date_joined',
field=models.DateTimeField(auto_now_add=True, verbose_name='date joined'),
),
]

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0002_auto_20151215_0827'),
]
operations = [
migrations.RenameField(
model_name='page',
old_name='full_name',
new_name='_full_name',
),
]

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_auto_20160111_0900'),
]
operations = [
migrations.AlterField(
model_name='user',
name='date_of_birth',
field=models.DateField(verbose_name='date of birth'),
),
]

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_auto_20160128_0835'),
]
operations = [
migrations.AlterField(
model_name='user',
name='date_joined',
field=models.DateField(auto_now_add=True, verbose_name='date joined'),
),
]

View File

@ -160,13 +160,12 @@ class User(AbstractBaseUser, PermissionsMixin):
""" """
Determine if the object can be edited by the user Determine if the object can be edited by the user
""" """
if not hasattr(obj, "edit_group"):
return False
if self.is_owner(obj): if self.is_owner(obj):
return True return True
for g in obj.edit_group.all(): if hasattr(obj, "edit_group"):
if self.groups.filter(name=g.name).exists(): for g in obj.edit_group.all():
return True if self.groups.filter(name=g.name).exists():
return True
if isinstance(obj, User) and obj == self: if isinstance(obj, User) and obj == self:
return True return True
if self.has_perm(obj.__class__.__module__.split('.')[0]+".change_"+obj.__class__.__name__.lower()): if self.has_perm(obj.__class__.__module__.split('.')[0]+".change_"+obj.__class__.__name__.lower()):
@ -177,13 +176,12 @@ class User(AbstractBaseUser, PermissionsMixin):
""" """
Determine if the object can be viewed by the user Determine if the object can be viewed by the user
""" """
if not hasattr(obj, "view_group"):
return False
if self.can_edit(obj): if self.can_edit(obj):
return True return True
for g in obj.view_group.all(): if hasattr(obj, "view_group"):
if self.groups.filter(name=g.name).exists(): for g in obj.view_group.all():
return True if self.groups.filter(name=g.name).exists():
return True
if self.has_perm(obj.__class__.__module__.split('.')[0]+".view_"+obj.__class__.__name__.lower()): if self.has_perm(obj.__class__.__module__.split('.')[0]+".view_"+obj.__class__.__name__.lower()):
return True return True
return False return False

View File

@ -42,6 +42,7 @@ class CanEditMixin(View):
try: # Always unlock when 403 try: # Always unlock when 403
self.object.unset_lock() self.object.unset_lock()
except: pass except: pass
print("CanEditMixin 403")
raise PermissionDenied raise PermissionDenied
class CanViewMixin(View): class CanViewMixin(View):
@ -56,6 +57,7 @@ class CanViewMixin(View):
try: # Always unlock when 403 try: # Always unlock when 403
self.object.unset_lock() self.object.unset_lock()
except: pass except: pass
print("CanViewMixin 403")
raise PermissionDenied raise PermissionDenied
from .user import * from .user import *

View File

@ -3,15 +3,16 @@ from django.views.generic import ListView
from core.models import Group from core.models import Group
from core.views.forms import GroupEditForm from core.views.forms import GroupEditForm
from core.views import CanEditMixin
class GroupListView(ListView): class GroupListView(CanEditMixin, ListView):
""" """
Displays the group list Displays the group list
""" """
model = Group model = Group
template_name = "core/group_list.html" template_name = "core/group_list.html"
class GroupEditView(UpdateView): class GroupEditView(CanEditMixin, UpdateView):
model = Group model = Group
pk_url_kwarg = "group_id" pk_url_kwarg = "group_id"
template_name = "core/group_edit.html" template_name = "core/group_edit.html"

View File

@ -1,2 +1,3 @@
# Django 1.8 LTS is required, version 1.9 is not supported # Django 1.8 LTS is required, version 1.9 is not supported
Django >=1.8,<1.9 Django >=1.8,<1.9
Pillow

View File

@ -38,6 +38,7 @@ INSTALLED_APPS = (
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'core', 'core',
'club',
'subscription', 'subscription',
'accounting', 'accounting',
) )
@ -122,6 +123,11 @@ EMAIL_HOST="localhost"
EMAIL_PORT=25 EMAIL_PORT=25
# AE configuration # AE configuration
AE_MAIN_CLUB = {
'name': "AE",
'unix_name': "ae",
'address': "6 Boulevard Anatole France, 90000 Belfort"
}
# Define the date in the year serving as reference for the subscriptions calendar # Define the date in the year serving as reference for the subscriptions calendar
# (month, day) # (month, day)
AE_START_DATE = (8, 15) # 15th August AE_START_DATE = (8, 15) # 15th August
@ -173,3 +179,16 @@ AE_SUBSCRIPTIONS = {
}, },
# To be completed.... # To be completed....
} }
CLUB_ROLES = {
10: 'Président',
9: 'Vice-Président',
8: 'Vice-Président',
7: 'Trésorier',
5: 'Responsable com',
4: 'Secrétaire',
3: 'Responsable info',
2: 'Membre du bureau',
1: 'Membre actif',
0: 'Membre',
}

View File

@ -23,6 +23,6 @@ handler404 = "core.views.not_found"
urlpatterns = [ urlpatterns = [
url(r'^', include('core.urls', namespace="core", app_name="core")), url(r'^', include('core.urls', namespace="core", app_name="core")),
url(r'^subscription/', include('subscription.urls', namespace="asso", app_name="subscription")), url(r'^subscription/', include('subscription.urls', namespace="subscription", app_name="subscription")),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # TODO: remove me for production!!! ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # TODO: remove me for production!!!

View File

@ -1,8 +1,8 @@
from django.contrib import admin from django.contrib import admin
from subscription.models import Member, Subscription from subscription.models import Subscriber, Subscription
admin.site.register(Member) admin.site.register(Subscriber)
admin.site.register(Subscription) admin.site.register(Subscription)

View File

@ -2,34 +2,44 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
from django.conf import settings import django.contrib.auth.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('core', '0005_auto_20160128_0842'), ('core', '0001_initial'),
] ]
operations = [ operations = [
migrations.CreateModel(
name='Member',
fields=[
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, primary_key=True, serialize=False)),
],
),
migrations.CreateModel( migrations.CreateModel(
name='Subscription', name='Subscription',
fields=[ fields=[
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)), ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('subscription_type', models.CharField(choices=[('cursus-branche', 'Cursus Branche'), ('cursus-tronc-commun', 'Cursus Tronc Commun'), ('deux-semestres', 'Deux semestres'), ('un-semestre', 'Un semestre')], verbose_name='subscription type', max_length=255)), ('subscription_type', models.CharField(choices=[('cursus-branche', 'Cursus Branche'), ('cursus-tronc-commun', 'Cursus Tronc Commun'), ('deux-semestres', 'Deux semestres'), ('un-semestre', 'Un semestre')], max_length=255, verbose_name='subscription type')),
('subscription_start', models.DateField(verbose_name='subscription start')), ('subscription_start', models.DateField(verbose_name='subscription start')),
('subscription_end', models.DateField(verbose_name='subscription end')), ('subscription_end', models.DateField(verbose_name='subscription end')),
('payment_method', models.CharField(choices=[('cheque', 'Chèque'), ('cash', 'Espèce'), ('other', 'Autre')], verbose_name='payment method', max_length=255)), ('payment_method', models.CharField(choices=[('cheque', 'Chèque'), ('cash', 'Espèce'), ('other', 'Autre')], max_length=255, verbose_name='payment method')),
('member', models.ForeignKey(to='subscription.Member', related_name='subscriptions')),
], ],
options={ options={
'ordering': ['subscription_start'], 'ordering': ['subscription_start'],
}, },
), ),
migrations.CreateModel(
name='Subscriber',
fields=[
],
options={
'proxy': True,
},
bases=('core.user',),
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.AddField(
model_name='subscription',
name='member',
field=models.ForeignKey(related_name='subscriptions', to='subscription.Subscriber'),
),
] ]

View File

@ -3,6 +3,8 @@ from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from core.models import User from core.models import User
@ -14,17 +16,15 @@ def validate_payment(value):
if value not in settings.AE_PAYMENT_METHOD: if value not in settings.AE_PAYMENT_METHOD:
raise ValidationError(_('Bad payment method')) raise ValidationError(_('Bad payment method'))
class Member(models.Model): class Subscriber(User):
user = models.OneToOneField(User, primary_key=True) class Meta:
proxy = True
def is_subscribed(self): def is_subscribed(self):
return self.subscriptions.last().is_valid_now() return self.subscriptions.last().is_valid_now()
def __str__(self):
return self.user.username
class Subscription(models.Model): class Subscription(models.Model):
member = models.ForeignKey(Member, related_name='subscriptions') member = models.ForeignKey(Subscriber, related_name='subscriptions')
subscription_type = models.CharField(_('subscription type'), subscription_type = models.CharField(_('subscription type'),
max_length=255, max_length=255,
choices=((k, v['name']) for k,v in sorted(settings.AE_SUBSCRIPTIONS.items()))) choices=((k, v['name']) for k,v in sorted(settings.AE_SUBSCRIPTIONS.items())))
@ -32,6 +32,17 @@ class Subscription(models.Model):
subscription_end = models.DateField(_('subscription end')) subscription_end = models.DateField(_('subscription end'))
payment_method = models.CharField(_('payment method'), max_length=255, choices=settings.AE_PAYMENT_METHOD) payment_method = models.CharField(_('payment method'), max_length=255, choices=settings.AE_PAYMENT_METHOD)
class Meta:
permissions = (
('change_subscription', 'Can make someone become a subscriber'),
('view_subscription', 'Can view who is a subscriber'),
)
def clean(self):
for s in Subscription.objects.filter(member=self.member).exclude(pk=self.pk).all():
if s.is_valid_now():
raise ValidationError(_('You can not subscribe many time for the same period'))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
""" """
This makes the Subscription to be updated with right dates with respect to date.today() each time you save the This makes the Subscription to be updated with right dates with respect to date.today() each time you save the
@ -50,8 +61,11 @@ class Subscription(models.Model):
class Meta: class Meta:
ordering = ['subscription_start',] ordering = ['subscription_start',]
def get_absolute_url(self):
return reverse('core:user_profile', kwargs={'user_id': self.member.pk})
def __str__(self): def __str__(self):
return self.member.user.username+' - '+str(self.pk) return self.member.username+' - '+str(self.pk)
@staticmethod @staticmethod
def compute_start(d=date.today()): def compute_start(d=date.today()):

View File

@ -4,7 +4,7 @@ from subscription.views import *
urlpatterns = [ urlpatterns = [
# Subscription views # Subscription views
url(r'^subscription/(?P<user_id>[0-9]+)/$', NewSubscription.as_view(), name='subscription'), url(r'^$', NewSubscription.as_view(), name='subscription'),
] ]

View File

@ -4,13 +4,13 @@ from django import forms
from django.forms import Select from django.forms import Select
from django.conf import settings from django.conf import settings
from subscription.models import Member, Subscription from subscription.models import Subscriber, Subscription
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
class SubscriptionForm(forms.ModelForm): class SubscriptionForm(forms.ModelForm):
class Meta: class Meta:
model = Subscription model = Subscription
fields = ['subscription_type', 'payment_method'] fields = ['member', 'subscription_type', 'payment_method']
#widgets = { #widgets = {
# 'subscription_type': Select(choices={(k.lower(), k+" - "+str(v['price'])+"€"+str(Subscription.compute_end(2))) for k,v in settings.AE_SUBSCRIPTIONS.items()}), # 'subscription_type': Select(choices={(k.lower(), k+" - "+str(v['price'])+"€"+str(Subscription.compute_end(2))) for k,v in settings.AE_SUBSCRIPTIONS.items()}),
#} #}