From fbe2e7f59a5ad8d8b8ac0f75306d1f8a7db7c6b7 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 12:43:23 +0200 Subject: [PATCH] Not so atomic, sorry --- .coveragerc | 9 +- {adherents => activity}/__init__.py | 2 +- activity/admin.py | 3 + {adherents => activity}/apps.py | 6 +- activity/locale/fr/LC_MESSAGES/django.po | 54 +++++++ activity/migrations/0001_initial.py | 64 ++++++++ .../migrations/__init__.py | 0 activity/models.py | 77 ++++++++++ {adherents => activity}/tests/__init__.py | 0 adherents/locale/fr/LC_MESSAGES/django.po | 51 ------- adherents/migrations/0001_initial.py | 49 ------ adherents/models.py | 105 ------------- member/__init__.py | 5 + {adherents => member}/admin.py | 0 member/apps.py | 11 ++ {adherents => member}/forms.py | 0 member/locale/fr/LC_MESSAGES/django.po | 119 +++++++++++++++ member/migrations/0001_initial.py | 72 +++++++++ member/migrations/__init__.py | 0 member/models.py | 140 ++++++++++++++++++ member/tests/__init__.py | 0 note/admin.py | 5 +- note/locale/fr/LC_MESSAGES/django.po | 91 ++++++++++++ note/migrations/0001_initial.py | 88 +++++++---- note/models.py | 89 ----------- note/models/__init__.py | 6 + note/models/notes.py | 90 +++++++++++ note/models/transactions.py | 76 ++++++++++ note_kfet/settings.py | 3 +- theme/locale/fr/LC_MESSAGES/django.po | 2 +- 30 files changed, 885 insertions(+), 332 deletions(-) rename {adherents => activity}/__init__.py (71%) create mode 100644 activity/admin.py rename {adherents => activity}/apps.py (71%) create mode 100644 activity/locale/fr/LC_MESSAGES/django.po create mode 100644 activity/migrations/0001_initial.py rename {adherents => activity}/migrations/__init__.py (100%) create mode 100644 activity/models.py rename {adherents => activity}/tests/__init__.py (100%) delete mode 100644 adherents/locale/fr/LC_MESSAGES/django.po delete mode 100644 adherents/migrations/0001_initial.py delete mode 100644 adherents/models.py create mode 100644 member/__init__.py rename {adherents => member}/admin.py (100%) create mode 100644 member/apps.py rename {adherents => member}/forms.py (100%) create mode 100644 member/locale/fr/LC_MESSAGES/django.po create mode 100644 member/migrations/0001_initial.py create mode 100644 member/migrations/__init__.py create mode 100644 member/models.py create mode 100644 member/tests/__init__.py create mode 100644 note/locale/fr/LC_MESSAGES/django.po delete mode 100644 note/models.py create mode 100644 note/models/__init__.py create mode 100644 note/models/notes.py create mode 100644 note/models/transactions.py diff --git a/.coveragerc b/.coveragerc index 61b95053..be61ad50 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,11 +1,14 @@ [run] source = - adherents + activity + member note theme omit = - adherents/tests/*.py - adherents/migrations/*.py + activity/tests/*.py + activity/migrations/*.py + member/tests/*.py + member/migrations/*.py note/tests/*.py note/migrations/*.py theme/tests/*.py \ No newline at end of file diff --git a/adherents/__init__.py b/activity/__init__.py similarity index 71% rename from adherents/__init__.py rename to activity/__init__.py index c1217d13..75df9e1f 100644 --- a/adherents/__init__.py +++ b/activity/__init__.py @@ -2,4 +2,4 @@ # Copyright (C) 2018-2019 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -default_app_config = 'adherents.apps.AdherentsConfig' +default_app_config = 'activity.apps.ActivityConfig' diff --git a/activity/admin.py b/activity/admin.py new file mode 100644 index 00000000..6ac23376 --- /dev/null +++ b/activity/admin.py @@ -0,0 +1,3 @@ +# -*- mode: python; coding: utf-8 -*- +# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later diff --git a/adherents/apps.py b/activity/apps.py similarity index 71% rename from adherents/apps.py rename to activity/apps.py index 05e36034..29990f1b 100644 --- a/adherents/apps.py +++ b/activity/apps.py @@ -6,6 +6,6 @@ from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ -class AdherentsConfig(AppConfig): - name = 'adherents' - verbose_name = _('adherents') +class ActivityConfig(AppConfig): + name = 'activity' + verbose_name = _('activity') diff --git a/activity/locale/fr/LC_MESSAGES/django.po b/activity/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000..8d21c1b6 --- /dev/null +++ b/activity/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,54 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-07-16 12:37+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \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 "activity" +msgstr "activité" + +#: models.py:12 models.py:25 +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:29 +msgid "description" +msgstr "description" + +#: models.py:35 +msgid "type" +msgstr "type" + +#: models.py:41 +msgid "organizer" +msgstr "organisateur" + +#: models.py:47 +msgid "attendees club" +msgstr "" + +#: models.py:50 +msgid "start date" +msgstr "date de début" + +#: models.py:53 +msgid "end date" +msgstr "date de fin" diff --git a/activity/migrations/0001_initial.py b/activity/migrations/0001_initial.py new file mode 100644 index 00000000..ff697212 --- /dev/null +++ b/activity/migrations/0001_initial.py @@ -0,0 +1,64 @@ +# Generated by Django 2.2.3 on 2019-07-16 10:33 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('note', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('member', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Activity', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('description', models.TextField(verbose_name='description')), + ('date_start', models.DateTimeField(verbose_name='start date')), + ('date_end', models.DateTimeField(verbose_name='end date')), + ], + ), + migrations.CreateModel( + name='ActivityType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('can_invite', models.BooleanField(verbose_name='can invite')), + ('guest_entry_fee', models.PositiveIntegerField(verbose_name='guest entry fee')), + ], + ), + migrations.CreateModel( + name='Guest', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('entry', models.DateTimeField(null=True)), + ('activity', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='activity.Activity')), + ('entry_transaction', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='note.Transaction')), + ('inviter', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='activity', + name='activity_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='activity.ActivityType', verbose_name='type'), + ), + migrations.AddField( + model_name='activity', + name='attendees_club', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='attendees club'), + ), + migrations.AddField( + model_name='activity', + name='organizer', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='organizer'), + ), + ] diff --git a/adherents/migrations/__init__.py b/activity/migrations/__init__.py similarity index 100% rename from adherents/migrations/__init__.py rename to activity/migrations/__init__.py diff --git a/activity/models.py b/activity/models.py new file mode 100644 index 00000000..6f36b9e0 --- /dev/null +++ b/activity/models.py @@ -0,0 +1,77 @@ +# -*- 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.translation import gettext_lazy as _ +from django.conf import settings + + +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 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 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, + ) diff --git a/adherents/tests/__init__.py b/activity/tests/__init__.py similarity index 100% rename from adherents/tests/__init__.py rename to activity/tests/__init__.py diff --git a/adherents/locale/fr/LC_MESSAGES/django.po b/adherents/locale/fr/LC_MESSAGES/django.po deleted file mode 100644 index cad278eb..00000000 --- a/adherents/locale/fr/LC_MESSAGES/django.po +++ /dev/null @@ -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 , 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 \n" -"Language-Team: LANGUAGE \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" diff --git a/adherents/migrations/0001_initial.py b/adherents/migrations/0001_initial.py deleted file mode 100644 index 32433430..00000000 --- a/adherents/migrations/0001_initial.py +++ /dev/null @@ -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', - }, - ), - ] diff --git a/adherents/models.py b/adherents/models.py deleted file mode 100644 index 947499bd..00000000 --- a/adherents/models.py +++ /dev/null @@ -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() diff --git a/member/__init__.py b/member/__init__.py new file mode 100644 index 00000000..ec189d6f --- /dev/null +++ b/member/__init__.py @@ -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' diff --git a/adherents/admin.py b/member/admin.py similarity index 100% rename from adherents/admin.py rename to member/admin.py diff --git a/member/apps.py b/member/apps.py new file mode 100644 index 00000000..928c00e4 --- /dev/null +++ b/member/apps.py @@ -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') diff --git a/adherents/forms.py b/member/forms.py similarity index 100% rename from adherents/forms.py rename to member/forms.py diff --git a/member/locale/fr/LC_MESSAGES/django.po b/member/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000..36a4e414 --- /dev/null +++ b/member/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,119 @@ +# 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 , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-07-16 12:37+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \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 "profile picture" +msgstr "image de profil" + +#: models.py:29 +msgid "phone number" +msgstr "numéro de téléphone" + +#: models.py:36 +msgid "section" +msgstr "section" + +#: models.py:37 +msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" +msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" + +#: models.py:41 +msgid "address" +msgstr "adresse" + +#: models.py:47 models.py:61 +msgid "paid" +msgstr "payé" + +#: models.py:52 models.py:53 +msgid "user profile" +msgstr "profil utilisateur" + +#: models.py:65 +msgid "email" +msgstr "courriel" + +#: models.py:70 +msgid "membership fee" +msgstr "cotisation" + +#: models.py:74 +msgid "membership duration" +msgstr "durée de l'adhésion" + +#: models.py:75 +msgid "The longest time a membership can last (NULL = infinite)." +msgstr "La durée maximale d'une adhésion (NULL = infinie)." + +#: models.py:79 +msgid "membership start" +msgstr "début de l'adhésion" + +#: models.py:80 +msgid "How long after January 1st the members can renew their membership." +msgstr "" + +#: models.py:84 +msgid "membership end" +msgstr "fin de l'adhésion" + +#: models.py:85 +msgid "" +"How long the membership can last after January 1st of the next year after " +"members can renew their membership." +msgstr "" + +#: models.py:95 +msgid "name" +msgstr "nom" + +#: models.py:100 +msgid "role" +msgstr "rôle" + +#: models.py:101 +msgid "roles" +msgstr "rôles" + +#: models.py:118 +msgid "membership starts on" +msgstr "l'adhésion commence le" + +#: models.py:121 +#, fuzzy +#| msgid "membership fees" +msgid "membership ends on" +msgstr "l'adhésion finie le" + +#: models.py:125 +msgid "fee" +msgstr "cotisation" + +#: models.py:129 +msgid "membership" +msgstr "adhésion" + +#: models.py:130 +msgid "memberships" +msgstr "adhésions" diff --git a/member/migrations/0001_initial.py b/member/migrations/0001_initial.py new file mode 100644 index 00000000..d090655d --- /dev/null +++ b/member/migrations/0001_initial.py @@ -0,0 +1,72 @@ +# Generated by Django 2.2.3 on 2019-07-16 10:33 + +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='Club', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='paid')), + ('email', models.EmailField(max_length=254, verbose_name='email')), + ('membership_fee', models.PositiveIntegerField(verbose_name='membership fee')), + ('membership_duration', models.DurationField(help_text='The longest time a membership can last (NULL = infinite).', null=True, verbose_name='membership duration')), + ('membership_start', models.DurationField(help_text='How long after January 1st the members can renew their membership.', null=True, verbose_name='membership start')), + ('membership_end', models.DurationField(help_text='How long the membership can last after January 1st of the next year after members can renew their membership.', null=True, verbose_name='membership end')), + ], + ), + migrations.CreateModel( + name='Role', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ], + options={ + 'verbose_name': 'role', + 'verbose_name_plural': 'roles', + }, + ), + migrations.CreateModel( + name='Profile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('profile_picture', 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')), + ('address', models.CharField(blank=True, max_length=255, null=True, verbose_name='address')), + ('paid', models.BooleanField(default=False, verbose_name='paid')), + ('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='Membership', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date_start', models.DateField(verbose_name='membership starts on')), + ('date_end', models.DateField(null=True, verbose_name='membership ends on')), + ('fee', models.PositiveIntegerField(verbose_name='fee')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='member.Club')), + ('roles', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='member.Role')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'membership', + 'verbose_name_plural': 'memberships', + }, + ), + ] diff --git a/member/migrations/__init__.py b/member/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/member/models.py b/member/models.py new file mode 100644 index 00000000..dc3dcd8c --- /dev/null +++ b/member/models.py @@ -0,0 +1,140 @@ +# -*- 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, + ) + profile_picture = models.ImageField( + verbose_name=_('profile picture'), + max_length=255, + blank=True, + ) + phone_number = models.CharField( + verbose_name=_('phone number'), + max_length=50, + blank=True, + null=True, + default='', + ) + section = models.CharField( + verbose_name=_('section'), + help_text=_('e.g. "1A0", "9A♥", "SAPHIRE"'), + max_length=255, + ) + 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=_('paid'), + max_length=255, + ) + 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 Role(models.Model): + """ + Role that an user can have in a club + """ + name = models.CharField( + verbose_name=_('name'), + max_length=255, + ) + + 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() diff --git a/member/tests/__init__.py b/member/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/note/admin.py b/note/admin.py index 122c11e3..b7fcbed4 100644 --- a/note/admin.py +++ b/note/admin.py @@ -1,10 +1,9 @@ from django.contrib import admin -from .models import NoteClub, NoteSpec, NoteUser -from .models import Alias +from .models.notes import NoteClub, NoteUser, NoteSpecial, Alias # Register your models here. admin.site.register(NoteClub) -admin.site.register(NoteSpec) admin.site.register(NoteUser) +admin.site.register(NoteSpecial) admin.site.register(Alias) diff --git a/note/locale/fr/LC_MESSAGES/django.po b/note/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000..1ab6395e --- /dev/null +++ b/note/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,91 @@ +# 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 , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-07-16 12:42+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \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 "note" +msgstr "note" + +#: models/notes.py:19 +msgid "account balance" +msgstr "solde du compte" + +#: models/notes.py:20 +msgid "in centimes, money credited for this instance" +msgstr "en centimes, argent crédité pour cette instance" + +#: models/notes.py:23 +msgid "active" +msgstr "actif" + +#: models/notes.py:26 +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:43 +msgid "one's note" +msgstr "note d'un utilisateur" + +#: models/notes.py:44 +msgid "users note" +msgstr "notes des utilisateurs" + +#: models/notes.py:58 +msgid "club note" +msgstr "note d'un club" + +#: models/notes.py:59 +msgid "clubs notes" +msgstr "notes des clubs" + +#: models/notes.py:72 models/transactions.py:31 models/transactions.py:60 +msgid "type" +msgstr "type" + +#: models/notes.py:83 models/transactions.py:18 +msgid "name" +msgstr "nom" + +#: models/transactions.py:25 models/transactions.py:47 +#: models/transactions.py:50 +msgid "destination" +msgstr "destination" + +#: models/transactions.py:28 models/transactions.py:57 +msgid "amount" +msgstr "montant" + +#: models/transactions.py:41 +msgid "source" +msgstr "source" + +#: models/transactions.py:54 +msgid "quantity" +msgstr "quantité" + +#: models/transactions.py:64 +msgid "description" +msgstr "description" + +#: models/transactions.py:67 +msgid "valid" +msgstr "valide" diff --git a/note/migrations/0001_initial.py b/note/migrations/0001_initial.py index b694bbf9..761e7bf3 100644 --- a/note/migrations/0001_initial.py +++ b/note/migrations/0001_initial.py @@ -1,8 +1,9 @@ -# Generated by Django 2.2.3 on 2019-07-16 07:17 +# Generated by Django 2.2.3 on 2019-07-16 10:33 from django.conf import settings from django.db import migrations, models import django.db.models.deletion +import django.utils.timezone class Migration(migrations.Migration): @@ -10,54 +11,89 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('member', '0001_initial'), ] operations = [ migrations.CreateModel( - name='NoteClub', + name='Note', 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')), + ('balance', models.IntegerField(help_text='in centimes, money credited for this instance', verbose_name='account balance')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this note should be treated as active. Unselect this instead of deleting notes.', verbose_name='active')), ], - options={ - 'abstract': False, - }, ), migrations.CreateModel( - name='NoteSpec', + name='Transaction', 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)), + ('datetime', models.DateTimeField(default=django.utils.timezone.now, verbose_name='destination')), + ('quantity', models.PositiveSmallIntegerField(verbose_name='quantity')), + ('amount', models.PositiveIntegerField(verbose_name='amount')), + ('transaction_type', models.CharField(max_length=31, verbose_name='type')), + ('description', models.TextField(verbose_name='description')), + ('valid', models.NullBooleanField(verbose_name='valid')), + ('destination', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='note.Note', verbose_name='destination')), + ('source', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='note.Note', verbose_name='source')), ], - options={ - 'abstract': False, - }, ), migrations.CreateModel( - name='NoteUser', + name='NoteSpecial', + fields=[ + ('note_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='note.Note')), + ('special_type', models.CharField(max_length=255, unique=True, verbose_name='type')), + ], + bases=('note.note',), + ), + migrations.CreateModel( + name='TransactionTemplate', 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)), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('amount', models.PositiveIntegerField(verbose_name='amount')), + ('template_type', models.CharField(max_length=31, verbose_name='type')), + ('destination', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='note.Note', verbose_name='destination')), ], - 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')), + ('name', models.CharField(max_length=255, unique=True, verbose_name='name')), + ('note', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='note.Note')), ], ), + migrations.CreateModel( + name='NoteUser', + fields=[ + ('note_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='note.Note')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='note', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': "one's note", + 'verbose_name_plural': 'users note', + }, + bases=('note.note',), + ), + migrations.CreateModel( + name='NoteClub', + fields=[ + ('note_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='note.Note')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='note', to='member.Club')), + ], + options={ + 'verbose_name': 'club note', + 'verbose_name_plural': 'clubs notes', + }, + bases=('note.note',), + ), + migrations.CreateModel( + name='MembershipTransaction', + fields=[ + ('transaction_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='note.Transaction')), + ('membership', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='transaction', to='member.Membership')), + ], + bases=('note.transaction',), + ), ] diff --git a/note/models.py b/note/models.py deleted file mode 100644 index 8424355d..00000000 --- a/note/models.py +++ /dev/null @@ -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 diff --git a/note/models/__init__.py b/note/models/__init__.py new file mode 100644 index 00000000..852104c0 --- /dev/null +++ b/note/models/__init__.py @@ -0,0 +1,6 @@ +# -*- 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 * +from .transactions import * diff --git a/note/models/notes.py b/note/models/notes.py new file mode 100644 index 00000000..3596bb85 --- /dev/null +++ b/note/models/notes.py @@ -0,0 +1,90 @@ +# -*- 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 _ + +""" +Defines each note types +""" + + +class Note(models.Model): + """ + An abstract model, use to add transactions capabilities to a user + """ + balance = models.IntegerField( + verbose_name=_('account balance'), + help_text=_('in centimes, money credited for this instance'), + ) + is_active = models.BooleanField( + _('active'), + default=True, + help_text=_( + 'Designates whether this note should be treated as active. ' + 'Unselect this instead of deleting 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 + """ + user = 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 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, + ) diff --git a/note/models/transactions.py b/note/models/transactions.py new file mode 100644 index 00000000..d9dd856b --- /dev/null +++ b/note/models/transactions.py @@ -0,0 +1,76 @@ +# -*- 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 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 MembershipTransaction(Transaction): + membership = models.OneToOneField( + 'member.Membership', + on_delete=models.PROTECT, + related_name='transaction', + ) diff --git a/note_kfet/settings.py b/note_kfet/settings.py index f3d13ccf..1fc0feb3 100644 --- a/note_kfet/settings.py +++ b/note_kfet/settings.py @@ -46,7 +46,8 @@ INSTALLED_APPS = [ 'guardian', # Note apps - 'adherents', + 'activity', + 'member', 'note', ] diff --git a/theme/locale/fr/LC_MESSAGES/django.po b/theme/locale/fr/LC_MESSAGES/django.po index 4163a3da..7b9adc7d 100644 --- a/theme/locale/fr/LC_MESSAGES/django.po +++ b/theme/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n"