mirror of https://gitlab.crans.org/bde/nk20
Merge branch 'models' into 'master'
Not so atomic, sorry See merge request bde/nk20!2
This commit is contained in:
commit
14282427af
|
@ -1,11 +1,14 @@
|
||||||
[run]
|
[run]
|
||||||
source =
|
source =
|
||||||
adherents
|
activity
|
||||||
|
member
|
||||||
note
|
note
|
||||||
theme
|
theme
|
||||||
omit =
|
omit =
|
||||||
adherents/tests/*.py
|
activity/tests/*.py
|
||||||
adherents/migrations/*.py
|
activity/migrations/*.py
|
||||||
|
member/tests/*.py
|
||||||
|
member/migrations/*.py
|
||||||
note/tests/*.py
|
note/tests/*.py
|
||||||
note/migrations/*.py
|
note/migrations/*.py
|
||||||
theme/tests/*.py
|
theme/tests/*.py
|
|
@ -36,3 +36,6 @@ settings_local.py
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
|
|
||||||
|
# Ignore migrations during first phase dev
|
||||||
|
migrations/
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
default_app_config = 'adherents.apps.AdherentsConfig'
|
default_app_config = 'activity.apps.ActivityConfig'
|
|
@ -0,0 +1,33 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import Activity, ActivityType, Guest
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin customisation for Activity
|
||||||
|
"""
|
||||||
|
list_display = ('name', 'activity_type', 'organizer')
|
||||||
|
list_filter = ('activity_type',)
|
||||||
|
search_fields = ['name', 'organizer__name']
|
||||||
|
|
||||||
|
# Organize activities by start date
|
||||||
|
date_hierarchy = 'date_start'
|
||||||
|
ordering = ['-date_start']
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityTypeAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin customisation for ActivityType
|
||||||
|
"""
|
||||||
|
list_display = ('name', 'can_invite', 'guest_entry_fee')
|
||||||
|
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
|
admin.site.register(Activity, ActivityAdmin)
|
||||||
|
admin.site.register(ActivityType, ActivityTypeAdmin)
|
||||||
|
admin.site.register(Guest)
|
|
@ -6,6 +6,6 @@ from django.apps import AppConfig
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class AdherentsConfig(AppConfig):
|
class ActivityConfig(AppConfig):
|
||||||
name = 'adherents'
|
name = 'activity'
|
||||||
verbose_name = _('adherents')
|
verbose_name = _('activity')
|
|
@ -0,0 +1,74 @@
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2019-07-16 13:45+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
|
||||||
|
#: apps.py:11 models.py:61
|
||||||
|
msgid "activity"
|
||||||
|
msgstr "activité"
|
||||||
|
|
||||||
|
#: models.py:12 models.py:29
|
||||||
|
msgid "name"
|
||||||
|
msgstr "nom"
|
||||||
|
|
||||||
|
#: models.py:16
|
||||||
|
msgid "can invite"
|
||||||
|
msgstr "peut inviter"
|
||||||
|
|
||||||
|
#: models.py:19
|
||||||
|
msgid "guest entry fee"
|
||||||
|
msgstr "cotisation de l'entrée invité"
|
||||||
|
|
||||||
|
#: models.py:23
|
||||||
|
msgid "activity type"
|
||||||
|
msgstr "type d'activité"
|
||||||
|
|
||||||
|
#: models.py:24
|
||||||
|
msgid "activity types"
|
||||||
|
msgstr "types d'activité"
|
||||||
|
|
||||||
|
#: models.py:33
|
||||||
|
msgid "description"
|
||||||
|
msgstr "description"
|
||||||
|
|
||||||
|
#: models.py:39
|
||||||
|
msgid "type"
|
||||||
|
msgstr "type"
|
||||||
|
|
||||||
|
#: models.py:45
|
||||||
|
msgid "organizer"
|
||||||
|
msgstr "organisateur"
|
||||||
|
|
||||||
|
#: models.py:51
|
||||||
|
msgid "attendees club"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:54
|
||||||
|
msgid "start date"
|
||||||
|
msgstr "date de début"
|
||||||
|
|
||||||
|
#: models.py:57
|
||||||
|
msgid "end date"
|
||||||
|
msgstr "date de fin"
|
||||||
|
|
||||||
|
#: models.py:62
|
||||||
|
msgid "activities"
|
||||||
|
msgstr "activités"
|
||||||
|
|
||||||
|
#: models.py:88
|
||||||
|
msgid "guest"
|
||||||
|
msgstr "invité"
|
||||||
|
|
||||||
|
#: models.py:89
|
||||||
|
msgid "guests"
|
||||||
|
msgstr "invités"
|
|
@ -0,0 +1,92 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityType(models.Model):
|
||||||
|
name = models.CharField(
|
||||||
|
verbose_name=_('name'),
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
can_invite = models.BooleanField(
|
||||||
|
verbose_name=_('can invite'),
|
||||||
|
)
|
||||||
|
guest_entry_fee = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('guest entry fee'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("activity type")
|
||||||
|
verbose_name_plural = _("activity types")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Activity(models.Model):
|
||||||
|
name = models.CharField(
|
||||||
|
verbose_name=_('name'),
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
description = models.TextField(
|
||||||
|
verbose_name=_('description'),
|
||||||
|
)
|
||||||
|
activity_type = models.ForeignKey(
|
||||||
|
ActivityType,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='+',
|
||||||
|
verbose_name=_('type'),
|
||||||
|
)
|
||||||
|
organizer = models.ForeignKey(
|
||||||
|
'member.Club',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='+',
|
||||||
|
verbose_name=_('organizer'),
|
||||||
|
)
|
||||||
|
attendees_club = models.ForeignKey(
|
||||||
|
'member.Club',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='+',
|
||||||
|
verbose_name=_('attendees club'),
|
||||||
|
)
|
||||||
|
date_start = models.DateTimeField(
|
||||||
|
verbose_name=_('start date'),
|
||||||
|
)
|
||||||
|
date_end = models.DateTimeField(
|
||||||
|
verbose_name=_('end date'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("activity")
|
||||||
|
verbose_name_plural = _("activities")
|
||||||
|
|
||||||
|
|
||||||
|
class Guest(models.Model):
|
||||||
|
activity = models.ForeignKey(
|
||||||
|
Activity,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='+',
|
||||||
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
inviter = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='+',
|
||||||
|
)
|
||||||
|
entry = models.DateTimeField(
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
entry_transaction = models.ForeignKey(
|
||||||
|
'note.Transaction',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("guest")
|
||||||
|
verbose_name_plural = _("guests")
|
|
@ -1,51 +0,0 @@
|
||||||
# SOME DESCRIPTIVE TITLE.
|
|
||||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
||||||
# This file is distributed under the same license as the PACKAGE package.
|
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
||||||
#
|
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2019-07-08 13:45+0200\n"
|
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
||||||
"Language: \n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
|
||||||
|
|
||||||
#: models.py:26
|
|
||||||
msgid "phone number"
|
|
||||||
msgstr "numéro de téléphone"
|
|
||||||
|
|
||||||
#: models.py:30
|
|
||||||
msgid "section"
|
|
||||||
msgstr "section"
|
|
||||||
|
|
||||||
#: models.py:31
|
|
||||||
msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
|
|
||||||
msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
|
|
||||||
|
|
||||||
#: models.py:35 models.py:36
|
|
||||||
msgid "user profile"
|
|
||||||
msgstr "profil utilisateur"
|
|
||||||
|
|
||||||
#: models.py:52
|
|
||||||
msgid "date"
|
|
||||||
msgstr "date"
|
|
||||||
|
|
||||||
#: models.py:57
|
|
||||||
msgid "amount"
|
|
||||||
msgstr "montant"
|
|
||||||
|
|
||||||
#: models.py:61
|
|
||||||
msgid "membership fee"
|
|
||||||
msgstr "cotisation"
|
|
||||||
|
|
||||||
#: models.py:62
|
|
||||||
msgid "membership fees"
|
|
||||||
msgstr "cotisations"
|
|
|
@ -1,49 +0,0 @@
|
||||||
# Generated by Django 2.2.3 on 2019-07-16 07:17
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Profile',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('avatar', models.ImageField(blank=True, max_length=255, upload_to='', verbose_name='profile picture')),
|
|
||||||
('phone_number', models.CharField(blank=True, default='', max_length=50, null=True, verbose_name='phone number')),
|
|
||||||
('section', models.CharField(help_text='e.g. "1A0", "9A♥", "SAPHIRE"', max_length=255, verbose_name='section')),
|
|
||||||
('genre', models.CharField(blank=True, choices=[(None, 'ND'), ('M', 'M'), ('F', 'F')], max_length=1, null=True)),
|
|
||||||
('address', models.TextField(blank=True, null=True)),
|
|
||||||
('paid', models.BooleanField(default=False, verbose_name='paid')),
|
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='is active')),
|
|
||||||
('is_deleted', models.BooleanField(default=False, verbose_name='is deleted')),
|
|
||||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'user profile',
|
|
||||||
'verbose_name_plural': 'user profile',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='MembershipFee',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('date', models.DateField(max_length=255, verbose_name='date')),
|
|
||||||
('amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='amount')),
|
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'membership fee',
|
|
||||||
'verbose_name_plural': 'membership fees',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,105 +0,0 @@
|
||||||
# -*- mode: python; coding: utf-8 -*-
|
|
||||||
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.db import models
|
|
||||||
from django.db.models.signals import post_save
|
|
||||||
from django.dispatch import receiver
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
|
|
||||||
class Profile(models.Model):
|
|
||||||
"""
|
|
||||||
An user profile
|
|
||||||
|
|
||||||
We do not want to patch the Django Contrib Auth User class
|
|
||||||
so this model add an user profile with additional information.
|
|
||||||
"""
|
|
||||||
GENRES = [
|
|
||||||
(None, "ND"),
|
|
||||||
("M", "M"),
|
|
||||||
("F", "F"),
|
|
||||||
]
|
|
||||||
|
|
||||||
user = models.OneToOneField(
|
|
||||||
settings.AUTH_USER_MODEL,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
avatar = models.ImageField(
|
|
||||||
max_length=255,
|
|
||||||
blank=True,
|
|
||||||
verbose_name=_('profile picture'),
|
|
||||||
)
|
|
||||||
phone_number = models.CharField(
|
|
||||||
max_length=50,
|
|
||||||
blank=True,
|
|
||||||
null=True,
|
|
||||||
default='',
|
|
||||||
verbose_name=_('phone number'),
|
|
||||||
)
|
|
||||||
section = models.CharField(
|
|
||||||
max_length=255,
|
|
||||||
verbose_name=_('section'),
|
|
||||||
help_text=_('e.g. "1A0", "9A♥", "SAPHIRE"'),
|
|
||||||
)
|
|
||||||
genre = models.CharField(
|
|
||||||
max_length=1,
|
|
||||||
blank=True,
|
|
||||||
null=True,
|
|
||||||
choices=GENRES,
|
|
||||||
)
|
|
||||||
address = models.TextField(
|
|
||||||
blank=True,
|
|
||||||
null=True,
|
|
||||||
)
|
|
||||||
paid = models.BooleanField(
|
|
||||||
verbose_name=_("paid"),
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
is_active = models.BooleanField(
|
|
||||||
verbose_name=_("is active"),
|
|
||||||
default=True,
|
|
||||||
)
|
|
||||||
is_deleted = models.BooleanField(
|
|
||||||
verbose_name=_("is deleted"),
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('user profile')
|
|
||||||
verbose_name_plural = _('user profile')
|
|
||||||
|
|
||||||
|
|
||||||
class MembershipFee(models.Model):
|
|
||||||
"""
|
|
||||||
User can become member by paying a membership fee
|
|
||||||
"""
|
|
||||||
user = models.ForeignKey(
|
|
||||||
settings.AUTH_USER_MODEL,
|
|
||||||
on_delete=models.PROTECT,
|
|
||||||
)
|
|
||||||
date = models.DateField(
|
|
||||||
max_length=255,
|
|
||||||
verbose_name=_('date'),
|
|
||||||
)
|
|
||||||
amount = models.DecimalField(
|
|
||||||
max_digits=5, # Max 999.99 €
|
|
||||||
decimal_places=2,
|
|
||||||
verbose_name=_('amount'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('membership fee')
|
|
||||||
verbose_name_plural = _('membership fees')
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
|
||||||
def save_user_profile(instance, created, **_kwargs):
|
|
||||||
"""
|
|
||||||
Hook to save an user profile when an user is updated
|
|
||||||
"""
|
|
||||||
if created:
|
|
||||||
Profile.objects.create(user=instance)
|
|
||||||
instance.profile.save()
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
default_app_config = 'member.apps.MemberConfig'
|
|
@ -7,7 +7,7 @@ from django.contrib.auth.admin import UserAdmin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from .forms import CustomUserChangeForm
|
from .forms import CustomUserChangeForm
|
||||||
from .models import Profile
|
from .models import Club, Membership, Profile, Role
|
||||||
|
|
||||||
|
|
||||||
class ProfileInline(admin.StackedInline):
|
class ProfileInline(admin.StackedInline):
|
||||||
|
@ -33,5 +33,11 @@ class CustomUserAdmin(UserAdmin):
|
||||||
return super().get_inline_instances(request, obj)
|
return super().get_inline_instances(request, obj)
|
||||||
|
|
||||||
|
|
||||||
|
# Update Django User with profile
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User, CustomUserAdmin)
|
admin.site.register(User, CustomUserAdmin)
|
||||||
|
|
||||||
|
# Add other models
|
||||||
|
admin.site.register(Club)
|
||||||
|
admin.site.register(Membership)
|
||||||
|
admin.site.register(Role)
|
|
@ -0,0 +1,11 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class MemberConfig(AppConfig):
|
||||||
|
name = 'member'
|
||||||
|
verbose_name = _('member')
|
|
@ -10,6 +10,7 @@ class CustomUserChangeForm(UserChangeForm):
|
||||||
Make first name, last name and email required
|
Make first name, last name and email required
|
||||||
in the default Django Auth User model
|
in the default Django Auth User model
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['first_name'].required = True
|
self.fields['first_name'].required = True
|
|
@ -0,0 +1,116 @@
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2019-07-16 15:21+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
|
||||||
|
#: apps.py:11
|
||||||
|
msgid "member"
|
||||||
|
msgstr "adhérent"
|
||||||
|
|
||||||
|
#: models.py:24
|
||||||
|
msgid "phone number"
|
||||||
|
msgstr "numéro de téléphone"
|
||||||
|
|
||||||
|
#: models.py:30
|
||||||
|
msgid "section"
|
||||||
|
msgstr "section"
|
||||||
|
|
||||||
|
#: models.py:31
|
||||||
|
msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
|
||||||
|
msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
|
||||||
|
|
||||||
|
#: models.py:37
|
||||||
|
msgid "address"
|
||||||
|
msgstr "adresse"
|
||||||
|
|
||||||
|
#: models.py:43
|
||||||
|
msgid "paid"
|
||||||
|
msgstr "payé"
|
||||||
|
|
||||||
|
#: models.py:48 models.py:49
|
||||||
|
msgid "user profile"
|
||||||
|
msgstr "profil utilisateur"
|
||||||
|
|
||||||
|
#: models.py:57 models.py:102
|
||||||
|
msgid "name"
|
||||||
|
msgstr "nom"
|
||||||
|
|
||||||
|
#: models.py:62
|
||||||
|
msgid "email"
|
||||||
|
msgstr "courriel"
|
||||||
|
|
||||||
|
#: models.py:67
|
||||||
|
msgid "membership fee"
|
||||||
|
msgstr "cotisation pour adhérer"
|
||||||
|
|
||||||
|
#: models.py:71
|
||||||
|
msgid "membership duration"
|
||||||
|
msgstr "durée de l'adhésion"
|
||||||
|
|
||||||
|
#: models.py:72
|
||||||
|
msgid "The longest time a membership can last (NULL = infinite)."
|
||||||
|
msgstr "La durée maximale d'une adhésion (NULL = infinie)."
|
||||||
|
|
||||||
|
#: models.py:77
|
||||||
|
msgid "membership start"
|
||||||
|
msgstr "début de l'adhésion"
|
||||||
|
|
||||||
|
#: models.py:78
|
||||||
|
msgid "How long after January 1st the members can renew their membership."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:83
|
||||||
|
msgid "membership end"
|
||||||
|
msgstr "fin de l'adhésion"
|
||||||
|
|
||||||
|
#: models.py:84
|
||||||
|
msgid ""
|
||||||
|
"How long the membership can last after January 1st of the next year after "
|
||||||
|
"members can renew their membership."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:90
|
||||||
|
msgid "club"
|
||||||
|
msgstr "club"
|
||||||
|
|
||||||
|
#: models.py:91
|
||||||
|
msgid "clubs"
|
||||||
|
msgstr "clubs"
|
||||||
|
|
||||||
|
#: models.py:108
|
||||||
|
msgid "role"
|
||||||
|
msgstr "rôle"
|
||||||
|
|
||||||
|
#: models.py:109
|
||||||
|
msgid "roles"
|
||||||
|
msgstr "rôles"
|
||||||
|
|
||||||
|
#: models.py:126
|
||||||
|
msgid "membership starts on"
|
||||||
|
msgstr "l'adhésion commence le"
|
||||||
|
|
||||||
|
#: models.py:129
|
||||||
|
msgid "membership ends on"
|
||||||
|
msgstr "l'adhésion finie le"
|
||||||
|
|
||||||
|
#: models.py:133
|
||||||
|
msgid "fee"
|
||||||
|
msgstr "cotisation"
|
||||||
|
|
||||||
|
#: models.py:137
|
||||||
|
msgid "membership"
|
||||||
|
msgstr "adhésion"
|
||||||
|
|
||||||
|
#: models.py:138
|
||||||
|
msgid "memberships"
|
||||||
|
msgstr "adhésions"
|
|
@ -0,0 +1,148 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class Profile(models.Model):
|
||||||
|
"""
|
||||||
|
An user profile
|
||||||
|
|
||||||
|
We do not want to patch the Django Contrib Auth User class
|
||||||
|
so this model add an user profile with additional information.
|
||||||
|
"""
|
||||||
|
user = models.OneToOneField(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
phone_number = models.CharField(
|
||||||
|
verbose_name=_('phone number'),
|
||||||
|
max_length=50,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
section = models.CharField(
|
||||||
|
verbose_name=_('section'),
|
||||||
|
help_text=_('e.g. "1A0", "9A♥", "SAPHIRE"'),
|
||||||
|
max_length=255,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
address = models.CharField(
|
||||||
|
verbose_name=_('address'),
|
||||||
|
max_length=255,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
paid = models.BooleanField(
|
||||||
|
verbose_name=_("paid"),
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('user profile')
|
||||||
|
verbose_name_plural = _('user profile')
|
||||||
|
|
||||||
|
|
||||||
|
class Club(models.Model):
|
||||||
|
"""
|
||||||
|
A student club
|
||||||
|
"""
|
||||||
|
name = models.CharField(
|
||||||
|
verbose_name=_('name'),
|
||||||
|
max_length=255,
|
||||||
|
unique=True,
|
||||||
|
)
|
||||||
|
email = models.EmailField(
|
||||||
|
verbose_name=_('email'),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Memberships
|
||||||
|
membership_fee = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('membership fee'),
|
||||||
|
)
|
||||||
|
membership_duration = models.DurationField(
|
||||||
|
null=True,
|
||||||
|
verbose_name=_('membership duration'),
|
||||||
|
help_text=_('The longest time a membership can last '
|
||||||
|
'(NULL = infinite).'),
|
||||||
|
)
|
||||||
|
membership_start = models.DurationField(
|
||||||
|
null=True,
|
||||||
|
verbose_name=_('membership start'),
|
||||||
|
help_text=_('How long after January 1st the members can renew '
|
||||||
|
'their membership.'),
|
||||||
|
)
|
||||||
|
membership_end = models.DurationField(
|
||||||
|
null=True,
|
||||||
|
verbose_name=_('membership end'),
|
||||||
|
help_text=_('How long the membership can last after January 1st '
|
||||||
|
'of the next year after members can renew their '
|
||||||
|
'membership.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("club")
|
||||||
|
verbose_name_plural = _("clubs")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Role(models.Model):
|
||||||
|
"""
|
||||||
|
Role that an user can have in a club
|
||||||
|
"""
|
||||||
|
name = models.CharField(
|
||||||
|
verbose_name=_('name'),
|
||||||
|
max_length=255,
|
||||||
|
unique=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('role')
|
||||||
|
verbose_name_plural = _('roles')
|
||||||
|
|
||||||
|
|
||||||
|
class Membership(models.Model):
|
||||||
|
user = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.PROTECT
|
||||||
|
)
|
||||||
|
club = models.ForeignKey(
|
||||||
|
Club,
|
||||||
|
on_delete=models.PROTECT
|
||||||
|
)
|
||||||
|
roles = models.ForeignKey(
|
||||||
|
Role,
|
||||||
|
on_delete=models.PROTECT
|
||||||
|
)
|
||||||
|
date_start = models.DateField(
|
||||||
|
verbose_name=_('membership starts on'),
|
||||||
|
)
|
||||||
|
date_end = models.DateField(
|
||||||
|
verbose_name=_('membership ends on'),
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
fee = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('fee'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('membership')
|
||||||
|
verbose_name_plural = _('memberships')
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
||||||
|
def save_user_profile(instance, created, **_kwargs):
|
||||||
|
"""
|
||||||
|
Hook to save an user profile when an user is updated
|
||||||
|
"""
|
||||||
|
if created:
|
||||||
|
Profile.objects.create(user=instance)
|
||||||
|
instance.profile.save()
|
|
@ -1,10 +1,95 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import NoteClub, NoteSpec, NoteUser
|
from .models.notes import Alias, NoteClub, NoteSpecial, NoteUser
|
||||||
from .models import Alias
|
from .models.transactions import MembershipTransaction, Transaction, \
|
||||||
|
TransactionTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class AliasInlines(admin.TabularInline):
|
||||||
|
"""
|
||||||
|
Define user and club aliases when editing their note
|
||||||
|
"""
|
||||||
|
extra = 0
|
||||||
|
model = Alias
|
||||||
|
|
||||||
|
|
||||||
|
class NoteClubAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin customisation for NoteClub
|
||||||
|
"""
|
||||||
|
inlines = (AliasInlines,)
|
||||||
|
list_display = ('club', 'balance', 'is_active')
|
||||||
|
list_filter = ('is_active',)
|
||||||
|
search_fields = ['club__name']
|
||||||
|
|
||||||
|
# We can't change club after creation
|
||||||
|
readonly_fields = ('club',)
|
||||||
|
|
||||||
|
def has_add_permission(self, request):
|
||||||
|
"""
|
||||||
|
A club note should not be manually added
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
A club note should not be manually removed
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class NoteSpecialAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin customisation for NoteSpecial
|
||||||
|
"""
|
||||||
|
list_display = ('special_type', 'balance', 'is_active')
|
||||||
|
|
||||||
|
|
||||||
|
class NoteUserAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin customisation for NoteUser
|
||||||
|
"""
|
||||||
|
inlines = (AliasInlines,)
|
||||||
|
list_display = ('user', 'balance', 'is_active')
|
||||||
|
list_filter = ('is_active',)
|
||||||
|
search_fields = ['user__username']
|
||||||
|
|
||||||
|
# Organize note by registration date
|
||||||
|
date_hierarchy = 'user__date_joined'
|
||||||
|
ordering = ['-user__date_joined']
|
||||||
|
|
||||||
|
# We can't change user after creation
|
||||||
|
readonly_fields = ('user',)
|
||||||
|
|
||||||
|
def has_add_permission(self, request):
|
||||||
|
"""
|
||||||
|
An user note should not be manually added
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
An user note should not be manually removed
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionTemplateAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Admin customisation for TransactionTemplate
|
||||||
|
"""
|
||||||
|
list_display = ('name', 'destination', 'amount', 'template_type')
|
||||||
|
list_filter = ('destination', 'template_type',)
|
||||||
|
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
admin.site.register(NoteClub)
|
admin.site.register(NoteClub, NoteClubAdmin)
|
||||||
admin.site.register(NoteSpec)
|
admin.site.register(NoteSpecial, NoteSpecialAdmin)
|
||||||
admin.site.register(NoteUser)
|
admin.site.register(NoteUser, NoteUserAdmin)
|
||||||
admin.site.register(Alias)
|
admin.site.register(MembershipTransaction)
|
||||||
|
admin.site.register(Transaction)
|
||||||
|
admin.site.register(TransactionTemplate, TransactionTemplateAdmin)
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2019-07-16 15:21+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
|
||||||
|
#: apps.py:11 models/notes.py:39
|
||||||
|
msgid "note"
|
||||||
|
msgstr "note"
|
||||||
|
|
||||||
|
#: models/notes.py:21
|
||||||
|
msgid "account balance"
|
||||||
|
msgstr "solde du compte"
|
||||||
|
|
||||||
|
#: models/notes.py:22
|
||||||
|
msgid "in centimes, money credited for this instance"
|
||||||
|
msgstr "en centimes, argent crédité pour cette instance"
|
||||||
|
|
||||||
|
#: models/notes.py:25
|
||||||
|
msgid "active"
|
||||||
|
msgstr "actif"
|
||||||
|
|
||||||
|
#: models/notes.py:28
|
||||||
|
msgid ""
|
||||||
|
"Designates whether this note should be treated as active. Unselect this "
|
||||||
|
"instead of deleting notes."
|
||||||
|
msgstr ""
|
||||||
|
"Indique si la note est active. Désactiver cela plutôt que supprimer la note."
|
||||||
|
|
||||||
|
#: models/notes.py:33
|
||||||
|
msgid "display image"
|
||||||
|
msgstr "image affichée"
|
||||||
|
|
||||||
|
#: models/notes.py:40
|
||||||
|
msgid "notes"
|
||||||
|
msgstr "notes"
|
||||||
|
|
||||||
|
#: models/notes.py:54
|
||||||
|
msgid "one's note"
|
||||||
|
msgstr "note d'un utilisateur"
|
||||||
|
|
||||||
|
#: models/notes.py:55
|
||||||
|
msgid "users note"
|
||||||
|
msgstr "notes des utilisateurs"
|
||||||
|
|
||||||
|
#: models/notes.py:69
|
||||||
|
msgid "club note"
|
||||||
|
msgstr "note d'un club"
|
||||||
|
|
||||||
|
#: models/notes.py:70
|
||||||
|
msgid "clubs notes"
|
||||||
|
msgstr "notes des clubs"
|
||||||
|
|
||||||
|
#: models/notes.py:83 models/transactions.py:31 models/transactions.py:64
|
||||||
|
msgid "type"
|
||||||
|
msgstr "type"
|
||||||
|
|
||||||
|
#: models/notes.py:89
|
||||||
|
msgid "special note"
|
||||||
|
msgstr "note spéciale"
|
||||||
|
|
||||||
|
#: models/notes.py:90
|
||||||
|
msgid "special notes"
|
||||||
|
msgstr "notes spéciales"
|
||||||
|
|
||||||
|
#: models/notes.py:98 models/transactions.py:18
|
||||||
|
msgid "name"
|
||||||
|
msgstr "nom"
|
||||||
|
|
||||||
|
#: models/notes.py:108
|
||||||
|
msgid "alias"
|
||||||
|
msgstr "alias"
|
||||||
|
|
||||||
|
#: models/notes.py:109
|
||||||
|
msgid "aliases"
|
||||||
|
msgstr "alias"
|
||||||
|
|
||||||
|
#: models/transactions.py:25 models/transactions.py:51
|
||||||
|
#: models/transactions.py:54
|
||||||
|
msgid "destination"
|
||||||
|
msgstr "destination"
|
||||||
|
|
||||||
|
#: models/transactions.py:28 models/transactions.py:61
|
||||||
|
msgid "amount"
|
||||||
|
msgstr "montant"
|
||||||
|
|
||||||
|
#: models/transactions.py:36
|
||||||
|
msgid "transaction template"
|
||||||
|
msgstr "modèle de transaction"
|
||||||
|
|
||||||
|
#: models/transactions.py:37
|
||||||
|
msgid "transaction templates"
|
||||||
|
msgstr "modèles de transaction"
|
||||||
|
|
||||||
|
#: models/transactions.py:45
|
||||||
|
msgid "source"
|
||||||
|
msgstr "source"
|
||||||
|
|
||||||
|
#: models/transactions.py:58
|
||||||
|
msgid "quantity"
|
||||||
|
msgstr "quantité"
|
||||||
|
|
||||||
|
#: models/transactions.py:68
|
||||||
|
msgid "description"
|
||||||
|
msgstr "description"
|
||||||
|
|
||||||
|
#: models/transactions.py:71
|
||||||
|
msgid "valid"
|
||||||
|
msgstr "valide"
|
||||||
|
|
||||||
|
#: models/transactions.py:75
|
||||||
|
msgid "transaction"
|
||||||
|
msgstr "transaction"
|
||||||
|
|
||||||
|
#: models/transactions.py:76
|
||||||
|
msgid "transactions"
|
||||||
|
msgstr "transactions"
|
||||||
|
|
||||||
|
#: models/transactions.py:87
|
||||||
|
msgid "membership transaction"
|
||||||
|
msgstr "transaction d'adhésion"
|
||||||
|
|
||||||
|
#: models/transactions.py:88
|
||||||
|
msgid "membership transactions"
|
||||||
|
msgstr "transactions d'adhésion"
|
|
@ -1,63 +0,0 @@
|
||||||
# Generated by Django 2.2.3 on 2019-07-16 07:17
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('contenttypes', '0002_remove_content_type_name'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='NoteClub',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('balance', models.DecimalField(decimal_places=2, default=0, help_text='money credited for this instance', max_digits=8, verbose_name='account balance')),
|
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='is active')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='NoteSpec',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('balance', models.DecimalField(decimal_places=2, default=0, help_text='money credited for this instance', max_digits=8, verbose_name='account balance')),
|
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='is active')),
|
|
||||||
('account_type', models.CharField(choices=[('CH', 'bank check'), ('CB', 'credit card'), ('VB', 'bank transfer'), ('CA', 'cash'), ('RB', 'refund')], max_length=2, unique=True)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='NoteUser',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('balance', models.DecimalField(decimal_places=2, default=0, help_text='money credited for this instance', max_digits=8, verbose_name='account balance')),
|
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='is active')),
|
|
||||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': "one's note",
|
|
||||||
'verbose_name_plural': 'users note',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Alias',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('alias', models.TextField(unique=True, verbose_name='alias')),
|
|
||||||
('owner_id', models.PositiveIntegerField()),
|
|
||||||
('owner_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(('app_label', 'note'), ('model', 'NoteUser')), models.Q(('app_label', 'note'), ('model', 'NoteClub')), _connector='OR'), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,89 +0,0 @@
|
||||||
# -*- mode: python; coding: utf-8 -*-
|
|
||||||
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
||||||
|
|
||||||
|
|
||||||
class Alias(models.Model):
|
|
||||||
"""
|
|
||||||
A alias labels a Note instance, only for user and clubs
|
|
||||||
"""
|
|
||||||
alias = models.TextField(
|
|
||||||
"alias",
|
|
||||||
unique=True,
|
|
||||||
blank=False,
|
|
||||||
null=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Owner can be linked to an user note or a club note
|
|
||||||
limit = models.Q(app_label="note", model="NoteUser") | models.Q(app_label="note", model="NoteClub")
|
|
||||||
owner_id = models.PositiveIntegerField()
|
|
||||||
owner_type = models.ForeignKey(
|
|
||||||
ContentType,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
limit_choices_to=limit
|
|
||||||
)
|
|
||||||
owner = GenericForeignKey('owner_type', 'owner_id')
|
|
||||||
|
|
||||||
|
|
||||||
class Note(models.Model):
|
|
||||||
"""
|
|
||||||
An abstract model, use to add transactions capabilities to a user
|
|
||||||
"""
|
|
||||||
balance = models.DecimalField(
|
|
||||||
verbose_name=_('account balance'),
|
|
||||||
help_text=_("money credited for this instance"),
|
|
||||||
decimal_places=2, # Limit to centimes
|
|
||||||
max_digits=8, # Limit to 999999,99€
|
|
||||||
default=0,
|
|
||||||
)
|
|
||||||
is_active = models.BooleanField(
|
|
||||||
default=True,
|
|
||||||
verbose_name=_('is active')
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class NoteUser(Note):
|
|
||||||
"""
|
|
||||||
A Note associated to an User
|
|
||||||
"""
|
|
||||||
user = models.OneToOneField(
|
|
||||||
settings.AUTH_USER_MODEL,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("one's note")
|
|
||||||
verbose_name_plural = _("users note")
|
|
||||||
|
|
||||||
|
|
||||||
class NoteSpec(Note):
|
|
||||||
"""
|
|
||||||
A Note for special account, where real money enter or leave the system
|
|
||||||
"""
|
|
||||||
account_type = models.CharField(
|
|
||||||
max_length=2,
|
|
||||||
choices=(
|
|
||||||
("CH", _("bank check")),
|
|
||||||
("CB", _("credit card")),
|
|
||||||
("VB", _("bank transfer")),
|
|
||||||
("CA", _("cash")),
|
|
||||||
("RB", _("refund")),
|
|
||||||
),
|
|
||||||
unique=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NoteClub(Note):
|
|
||||||
# to be added
|
|
||||||
pass
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser
|
||||||
|
from .transactions import MembershipTransaction, Transaction, \
|
||||||
|
TransactionTemplate
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
# Notes
|
||||||
|
'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser',
|
||||||
|
# Transactions
|
||||||
|
'MembershipTransaction', 'Transaction', 'TransactionTemplate',
|
||||||
|
]
|
|
@ -0,0 +1,132 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
"""
|
||||||
|
Defines each note types
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Note(models.Model):
|
||||||
|
"""
|
||||||
|
An model, use to add transactions capabilities
|
||||||
|
|
||||||
|
We do not use an abstract model to simplify the transfer between two notes.
|
||||||
|
"""
|
||||||
|
balance = models.IntegerField(
|
||||||
|
verbose_name=_('account balance'),
|
||||||
|
help_text=_('in centimes, money credited for this instance'),
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
is_active = models.BooleanField(
|
||||||
|
_('active'),
|
||||||
|
default=True,
|
||||||
|
help_text=_(
|
||||||
|
'Designates whether this note should be treated as active. '
|
||||||
|
'Unselect this instead of deleting notes.'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
display_image = models.ImageField(
|
||||||
|
verbose_name=_('display image'),
|
||||||
|
max_length=255,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("note")
|
||||||
|
verbose_name_plural = _("notes")
|
||||||
|
|
||||||
|
|
||||||
|
class NoteUser(Note):
|
||||||
|
"""
|
||||||
|
A Note associated to an User
|
||||||
|
"""
|
||||||
|
user = models.OneToOneField(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='note',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("one's note")
|
||||||
|
verbose_name_plural = _("users note")
|
||||||
|
|
||||||
|
|
||||||
|
class NoteClub(Note):
|
||||||
|
"""
|
||||||
|
A Note associated to a Club
|
||||||
|
"""
|
||||||
|
club = models.OneToOneField(
|
||||||
|
'member.Club',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='note',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("club note")
|
||||||
|
verbose_name_plural = _("clubs notes")
|
||||||
|
|
||||||
|
|
||||||
|
class NoteSpecial(Note):
|
||||||
|
"""
|
||||||
|
A Note for special account, where real money enter or leave the system
|
||||||
|
- bank check
|
||||||
|
- credit card
|
||||||
|
- bank transfer
|
||||||
|
- cash
|
||||||
|
- refund
|
||||||
|
"""
|
||||||
|
special_type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
|
max_length=255,
|
||||||
|
unique=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("special note")
|
||||||
|
verbose_name_plural = _("special notes")
|
||||||
|
|
||||||
|
|
||||||
|
class Alias(models.Model):
|
||||||
|
"""
|
||||||
|
An alias labels a Note instance, only for user and clubs
|
||||||
|
"""
|
||||||
|
name = models.CharField(
|
||||||
|
verbose_name=_('name'),
|
||||||
|
max_length=255,
|
||||||
|
unique=True,
|
||||||
|
)
|
||||||
|
note = models.ForeignKey(
|
||||||
|
Note,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("alias")
|
||||||
|
verbose_name_plural = _("aliases")
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
||||||
|
def save_user_note(instance, created, **_kwargs):
|
||||||
|
"""
|
||||||
|
Hook to create and save a note when an user is updated
|
||||||
|
"""
|
||||||
|
if created:
|
||||||
|
NoteUser.objects.create(user=instance)
|
||||||
|
instance.note.save()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender='member.Club')
|
||||||
|
def save_club_note(instance, created, **_kwargs):
|
||||||
|
"""
|
||||||
|
Hook to create and save a note when a club is updated
|
||||||
|
"""
|
||||||
|
if created:
|
||||||
|
NoteClub.objects.create(club=instance)
|
||||||
|
instance.note.save()
|
|
@ -0,0 +1,88 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from .notes import Note
|
||||||
|
|
||||||
|
"""
|
||||||
|
Defines transactions
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionTemplate(models.Model):
|
||||||
|
name = models.CharField(
|
||||||
|
verbose_name=_('name'),
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
destination = models.ForeignKey(
|
||||||
|
Note,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='+', # no reverse
|
||||||
|
verbose_name=_('destination'),
|
||||||
|
)
|
||||||
|
amount = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('amount'),
|
||||||
|
)
|
||||||
|
template_type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
|
max_length=31
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("transaction template")
|
||||||
|
verbose_name_plural = _("transaction templates")
|
||||||
|
|
||||||
|
|
||||||
|
class Transaction(models.Model):
|
||||||
|
source = models.ForeignKey(
|
||||||
|
Note,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='+',
|
||||||
|
verbose_name=_('source'),
|
||||||
|
)
|
||||||
|
destination = models.ForeignKey(
|
||||||
|
Note,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='+',
|
||||||
|
verbose_name=_('destination'),
|
||||||
|
)
|
||||||
|
datetime = models.DateTimeField(
|
||||||
|
verbose_name=_('destination'),
|
||||||
|
default=timezone.now,
|
||||||
|
)
|
||||||
|
quantity = models.PositiveSmallIntegerField(
|
||||||
|
verbose_name=_('quantity'),
|
||||||
|
)
|
||||||
|
amount = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('amount'),
|
||||||
|
)
|
||||||
|
transaction_type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
|
max_length=31,
|
||||||
|
)
|
||||||
|
description = models.TextField(
|
||||||
|
verbose_name=_('description'),
|
||||||
|
)
|
||||||
|
valid = models.NullBooleanField(
|
||||||
|
verbose_name=_('valid'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("transaction")
|
||||||
|
verbose_name_plural = _("transactions")
|
||||||
|
|
||||||
|
|
||||||
|
class MembershipTransaction(Transaction):
|
||||||
|
membership = models.OneToOneField(
|
||||||
|
'member.Membership',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='transaction',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("membership transaction")
|
||||||
|
verbose_name_plural = _("membership transactions")
|
|
@ -44,9 +44,11 @@ INSTALLED_APPS = [
|
||||||
|
|
||||||
# External apps
|
# External apps
|
||||||
'guardian',
|
'guardian',
|
||||||
|
'reversion',
|
||||||
|
|
||||||
# Note apps
|
# Note apps
|
||||||
'adherents',
|
'activity',
|
||||||
|
'member',
|
||||||
'note',
|
'note',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,4 @@ Pillow==6.1.0
|
||||||
pytz==2019.1
|
pytz==2019.1
|
||||||
six==1.12.0
|
six==1.12.0
|
||||||
sqlparse==0.3.0
|
sqlparse==0.3.0
|
||||||
|
django-reversion==3.0.3
|
|
@ -3,7 +3,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2019-07-08 13:45+0200\n"
|
"POT-Creation-Date: 2019-07-16 12:36+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
6
tox.ini
6
tox.ini
|
@ -8,6 +8,7 @@ deps =
|
||||||
-r{toxinidir}/requirements.txt
|
-r{toxinidir}/requirements.txt
|
||||||
coverage
|
coverage
|
||||||
commands =
|
commands =
|
||||||
|
./manage.py makemigrations
|
||||||
coverage run ./manage.py test {posargs}
|
coverage run ./manage.py test {posargs}
|
||||||
coverage report -m
|
coverage report -m
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ deps =
|
||||||
pyflakes
|
pyflakes
|
||||||
pylint
|
pylint
|
||||||
commands =
|
commands =
|
||||||
flake8 note_*
|
flake8 activity member note
|
||||||
pylint .
|
pylint .
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
|
@ -41,7 +42,8 @@ exclude =
|
||||||
*.pyc,
|
*.pyc,
|
||||||
*.egg-info,
|
*.egg-info,
|
||||||
.cache,
|
.cache,
|
||||||
.eggs
|
.eggs,
|
||||||
|
*migrations*
|
||||||
max-complexity = 10
|
max-complexity = 10
|
||||||
import-order-style = google
|
import-order-style = google
|
||||||
application-import-names = flake8
|
application-import-names = flake8
|
||||||
|
|
Loading…
Reference in New Issue