From fbe2e7f59a5ad8d8b8ac0f75306d1f8a7db7c6b7 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 12:43:23 +0200 Subject: [PATCH 01/12] 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" From 2313ebcdb4368f05095f65a0a050cde6d7bb6090 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 12:59:11 +0200 Subject: [PATCH 02/12] Flake8 complient --- activity/models.py | 2 +- member/forms.py | 1 + member/models.py | 11 +++++++---- note/admin.py | 8 ++++---- note/models/__init__.py | 12 ++++++++++-- tox.ini | 5 +++-- 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/activity/models.py b/activity/models.py index 6f36b9e0..f5169e5e 100644 --- a/activity/models.py +++ b/activity/models.py @@ -2,9 +2,9 @@ # 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 _ -from django.conf import settings class ActivityType(models.Model): diff --git a/member/forms.py b/member/forms.py index a387d73c..6cc9ca4e 100644 --- a/member/forms.py +++ b/member/forms.py @@ -10,6 +10,7 @@ class CustomUserChangeForm(UserChangeForm): Make first name, last name and email required in the default Django Auth User model """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['first_name'].required = True diff --git a/member/models.py b/member/models.py index dc3dcd8c..4910bc43 100644 --- a/member/models.py +++ b/member/models.py @@ -72,18 +72,21 @@ class Club(models.Model): membership_duration = models.DurationField( null=True, verbose_name=_('membership duration'), - help_text=_('The longest time a membership can last (NULL = infinite).'), + 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.'), + 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.'), + help_text=_('How long the membership can last after January 1st ' + 'of the next year after members can renew their ' + 'membership.'), ) diff --git a/note/admin.py b/note/admin.py index b7fcbed4..7d82658d 100644 --- a/note/admin.py +++ b/note/admin.py @@ -1,9 +1,9 @@ from django.contrib import admin -from .models.notes import NoteClub, NoteUser, NoteSpecial, Alias +from .models.notes import Alias, NoteClub, NoteSpecial, NoteUser # Register your models here. -admin.site.register(NoteClub) -admin.site.register(NoteUser) -admin.site.register(NoteSpecial) admin.site.register(Alias) +admin.site.register(NoteClub) +admin.site.register(NoteSpecial) +admin.site.register(NoteUser) diff --git a/note/models/__init__.py b/note/models/__init__.py index 852104c0..b00572ce 100644 --- a/note/models/__init__.py +++ b/note/models/__init__.py @@ -2,5 +2,13 @@ # Copyright (C) 2018-2019 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from .notes import * -from .transactions import * +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', +] diff --git a/tox.ini b/tox.ini index 615a2701..41474f6c 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ deps = pyflakes pylint commands = - flake8 note_* + flake8 activity member note pylint . [flake8] @@ -41,7 +41,8 @@ exclude = *.pyc, *.egg-info, .cache, - .eggs + .eggs, + *migrations* max-complexity = 10 import-order-style = google application-import-names = flake8 From 7043ab5e45276f9d63ff4e6d6a82f540345e1fbc Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 13:50:05 +0200 Subject: [PATCH 03/12] Translate models --- activity/locale/fr/LC_MESSAGES/django.po | 38 ++++++++--- activity/migrations/0001_initial.py | 14 +++- activity/models.py | 12 ++++ member/locale/fr/LC_MESSAGES/django.po | 36 ++++++----- member/migrations/0001_initial.py | 6 +- member/models.py | 4 ++ note/locale/fr/LC_MESSAGES/django.po | 82 ++++++++++++++++++------ note/migrations/0001_initial.py | 28 +++++++- note/models/notes.py | 16 ++++- note/models/transactions.py | 12 ++++ 10 files changed, 200 insertions(+), 48 deletions(-) diff --git a/activity/locale/fr/LC_MESSAGES/django.po b/activity/locale/fr/LC_MESSAGES/django.po index 8d21c1b6..dc8f2db2 100644 --- a/activity/locale/fr/LC_MESSAGES/django.po +++ b/activity/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-16 12:37+0200\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 \n" "Language-Team: LANGUAGE \n" @@ -13,11 +13,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: apps.py:11 +#: apps.py:11 models.py:61 msgid "activity" msgstr "activité" -#: models.py:12 models.py:25 +#: models.py:12 models.py:29 msgid "name" msgstr "nom" @@ -29,26 +29,46 @@ msgstr "peut inviter" msgid "guest entry fee" msgstr "cotisation de l'entrée invité" -#: models.py:29 +#: 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:35 +#: models.py:39 msgid "type" msgstr "type" -#: models.py:41 +#: models.py:45 msgid "organizer" msgstr "organisateur" -#: models.py:47 +#: models.py:51 msgid "attendees club" msgstr "" -#: models.py:50 +#: models.py:54 msgid "start date" msgstr "date de début" -#: models.py:53 +#: 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" diff --git a/activity/migrations/0001_initial.py b/activity/migrations/0001_initial.py index ff697212..accbc91e 100644 --- a/activity/migrations/0001_initial.py +++ b/activity/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.3 on 2019-07-16 10:33 +# Generated by Django 2.2.3 on 2019-07-16 11:44 from django.conf import settings from django.db import migrations, models @@ -25,6 +25,10 @@ class Migration(migrations.Migration): ('date_start', models.DateTimeField(verbose_name='start date')), ('date_end', models.DateTimeField(verbose_name='end date')), ], + options={ + 'verbose_name': 'activity', + 'verbose_name_plural': 'activities', + }, ), migrations.CreateModel( name='ActivityType', @@ -34,6 +38,10 @@ class Migration(migrations.Migration): ('can_invite', models.BooleanField(verbose_name='can invite')), ('guest_entry_fee', models.PositiveIntegerField(verbose_name='guest entry fee')), ], + options={ + 'verbose_name': 'activity type', + 'verbose_name_plural': 'activity types', + }, ), migrations.CreateModel( name='Guest', @@ -45,6 +53,10 @@ class Migration(migrations.Migration): ('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)), ], + options={ + 'verbose_name': 'guest', + 'verbose_name_plural': 'guests', + }, ), migrations.AddField( model_name='activity', diff --git a/activity/models.py b/activity/models.py index f5169e5e..ee411d76 100644 --- a/activity/models.py +++ b/activity/models.py @@ -19,6 +19,10 @@ class ActivityType(models.Model): verbose_name=_('guest entry fee'), ) + class Meta: + verbose_name = _("activity type") + verbose_name_plural = _("activity types") + class Activity(models.Model): name = models.CharField( @@ -53,6 +57,10 @@ class Activity(models.Model): verbose_name=_('end date'), ) + class Meta: + verbose_name = _("activity") + verbose_name_plural = _("activities") + class Guest(models.Model): activity = models.ForeignKey( @@ -75,3 +83,7 @@ class Guest(models.Model): 'note.Transaction', on_delete=models.PROTECT, ) + + class Meta: + verbose_name = _("guest") + verbose_name_plural = _("guests") diff --git a/member/locale/fr/LC_MESSAGES/django.po b/member/locale/fr/LC_MESSAGES/django.po index 36a4e414..e3795f30 100644 --- a/member/locale/fr/LC_MESSAGES/django.po +++ b/member/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-16 12:37+0200\n" +"POT-Creation-Date: 2019-07-16 13:46+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -66,54 +66,60 @@ msgstr "durée de l'adhésion" msgid "The longest time a membership can last (NULL = infinite)." msgstr "La durée maximale d'une adhésion (NULL = infinie)." -#: models.py:79 +#: models.py:80 msgid "membership start" msgstr "début de l'adhésion" -#: models.py:80 +#: models.py:81 msgid "How long after January 1st the members can renew their membership." msgstr "" -#: models.py:84 +#: models.py:86 msgid "membership end" msgstr "fin de l'adhésion" -#: models.py:85 +#: models.py:87 msgid "" "How long the membership can last after January 1st of the next year after " "members can renew their membership." msgstr "" -#: models.py:95 +#: models.py:93 +msgid "club" +msgstr "club" + +#: models.py:94 +msgid "clubs" +msgstr "clubs" + +#: models.py:102 msgid "name" msgstr "nom" -#: models.py:100 +#: models.py:107 msgid "role" msgstr "rôle" -#: models.py:101 +#: models.py:108 msgid "roles" msgstr "rôles" -#: models.py:118 +#: models.py:125 msgid "membership starts on" msgstr "l'adhésion commence le" -#: models.py:121 -#, fuzzy -#| msgid "membership fees" +#: models.py:128 msgid "membership ends on" msgstr "l'adhésion finie le" -#: models.py:125 +#: models.py:132 msgid "fee" msgstr "cotisation" -#: models.py:129 +#: models.py:136 msgid "membership" msgstr "adhésion" -#: models.py:130 +#: models.py:137 msgid "memberships" msgstr "adhésions" diff --git a/member/migrations/0001_initial.py b/member/migrations/0001_initial.py index d090655d..02d19288 100644 --- a/member/migrations/0001_initial.py +++ b/member/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.3 on 2019-07-16 10:33 +# Generated by Django 2.2.3 on 2019-07-16 11:44 from django.conf import settings from django.db import migrations, models @@ -25,6 +25,10 @@ class Migration(migrations.Migration): ('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')), ], + options={ + 'verbose_name': 'club', + 'verbose_name_plural': 'clubs', + }, ), migrations.CreateModel( name='Role', diff --git a/member/models.py b/member/models.py index 4910bc43..dc80d87d 100644 --- a/member/models.py +++ b/member/models.py @@ -89,6 +89,10 @@ class Club(models.Model): 'membership.'), ) + class Meta: + verbose_name = _("club") + verbose_name_plural = _("clubs") + class Role(models.Model): """ diff --git a/note/locale/fr/LC_MESSAGES/django.po b/note/locale/fr/LC_MESSAGES/django.po index 1ab6395e..a4c4bfb6 100644 --- a/note/locale/fr/LC_MESSAGES/django.po +++ b/note/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-16 12:42+0200\n" +"POT-Creation-Date: 2019-07-16 13:47+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,74 +18,118 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: apps.py:11 +#: apps.py:11 models/notes.py:34 msgid "note" msgstr "note" -#: models/notes.py:19 +#: models/notes.py:21 msgid "account balance" msgstr "solde du compte" -#: models/notes.py:20 +#: models/notes.py:22 msgid "in centimes, money credited for this instance" msgstr "en centimes, argent crédité pour cette instance" -#: models/notes.py:23 +#: models/notes.py:25 msgid "active" msgstr "actif" -#: models/notes.py:26 +#: 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:43 +#: models/notes.py:35 +msgid "notes" +msgstr "notes" + +#: models/notes.py:49 msgid "one's note" msgstr "note d'un utilisateur" -#: models/notes.py:44 +#: models/notes.py:50 msgid "users note" msgstr "notes des utilisateurs" -#: models/notes.py:58 +#: models/notes.py:64 msgid "club note" msgstr "note d'un club" -#: models/notes.py:59 +#: models/notes.py:65 msgid "clubs notes" msgstr "notes des clubs" -#: models/notes.py:72 models/transactions.py:31 models/transactions.py:60 +#: models/notes.py:78 models/transactions.py:31 models/transactions.py:64 msgid "type" msgstr "type" -#: models/notes.py:83 models/transactions.py:18 +#: models/notes.py:84 +msgid "special note" +msgstr "note spéciale" + +#: models/notes.py:85 +msgid "special notes" +msgstr "notes spéciale" + +#: models/notes.py:93 models/transactions.py:18 msgid "name" msgstr "nom" -#: models/transactions.py:25 models/transactions.py:47 -#: models/transactions.py:50 +#: models/notes.py:103 +msgid "alias" +msgstr "alias" + +#: models/notes.py:104 +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:57 +#: models/transactions.py:28 models/transactions.py:61 msgid "amount" msgstr "montant" -#: models/transactions.py:41 +#: 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:54 +#: models/transactions.py:58 msgid "quantity" msgstr "quantité" -#: models/transactions.py:64 +#: models/transactions.py:68 msgid "description" msgstr "description" -#: models/transactions.py:67 +#: 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" diff --git a/note/migrations/0001_initial.py b/note/migrations/0001_initial.py index 761e7bf3..031d9f51 100644 --- a/note/migrations/0001_initial.py +++ b/note/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.3 on 2019-07-16 10:33 +# Generated by Django 2.2.3 on 2019-07-16 11:44 from django.conf import settings from django.db import migrations, models @@ -11,8 +11,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('member', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ @@ -23,6 +23,10 @@ class Migration(migrations.Migration): ('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={ + 'verbose_name': 'note', + 'verbose_name_plural': 'notes', + }, ), migrations.CreateModel( name='Transaction', @@ -37,6 +41,10 @@ class Migration(migrations.Migration): ('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={ + 'verbose_name': 'transaction', + 'verbose_name_plural': 'transactions', + }, ), migrations.CreateModel( name='NoteSpecial', @@ -44,6 +52,10 @@ class Migration(migrations.Migration): ('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')), ], + options={ + 'verbose_name': 'special note', + 'verbose_name_plural': 'special notes', + }, bases=('note.note',), ), migrations.CreateModel( @@ -55,6 +67,10 @@ class Migration(migrations.Migration): ('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': 'transaction template', + 'verbose_name_plural': 'transaction templates', + }, ), migrations.CreateModel( name='Alias', @@ -63,6 +79,10 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=255, unique=True, verbose_name='name')), ('note', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='note.Note')), ], + options={ + 'verbose_name': 'alias', + 'verbose_name_plural': 'aliases', + }, ), migrations.CreateModel( name='NoteUser', @@ -94,6 +114,10 @@ class Migration(migrations.Migration): ('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')), ], + options={ + 'verbose_name': 'membership transaction', + 'verbose_name_plural': 'membership transactions', + }, bases=('note.transaction',), ), ] diff --git a/note/models/notes.py b/note/models/notes.py index 3596bb85..2c91d1c5 100644 --- a/note/models/notes.py +++ b/note/models/notes.py @@ -13,7 +13,9 @@ Defines each note types class Note(models.Model): """ - An abstract model, use to add transactions capabilities to a user + 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'), @@ -28,6 +30,10 @@ class Note(models.Model): ), ) + class Meta: + verbose_name = _("note") + verbose_name_plural = _("notes") + class NoteUser(Note): """ @@ -74,6 +80,10 @@ class NoteSpecial(Note): unique=True, ) + class Meta: + verbose_name = _("special note") + verbose_name_plural = _("special notes") + class Alias(models.Model): """ @@ -88,3 +98,7 @@ class Alias(models.Model): Note, on_delete=models.PROTECT, ) + + class Meta: + verbose_name = _("alias") + verbose_name_plural = _("aliases") diff --git a/note/models/transactions.py b/note/models/transactions.py index d9dd856b..753f4925 100644 --- a/note/models/transactions.py +++ b/note/models/transactions.py @@ -32,6 +32,10 @@ class TransactionTemplate(models.Model): max_length=31 ) + class Meta: + verbose_name = _("transaction template") + verbose_name_plural = _("transaction templates") + class Transaction(models.Model): source = models.ForeignKey( @@ -67,6 +71,10 @@ class Transaction(models.Model): verbose_name=_('valid'), ) + class Meta: + verbose_name = _("transaction") + verbose_name_plural = _("transactions") + class MembershipTransaction(Transaction): membership = models.OneToOneField( @@ -74,3 +82,7 @@ class MembershipTransaction(Transaction): on_delete=models.PROTECT, related_name='transaction', ) + + class Meta: + verbose_name = _("membership transaction") + verbose_name_plural = _("membership transactions") From 8a44f81d2c6fa7a58c19ca7280b7581ed1754105 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 13:54:32 +0200 Subject: [PATCH 04/12] Make section optinal for an user --- activity/migrations/0001_initial.py | 4 ++-- member/migrations/0001_initial.py | 6 +++--- member/models.py | 3 ++- note/migrations/0001_initial.py | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/activity/migrations/0001_initial.py b/activity/migrations/0001_initial.py index accbc91e..5fb6579c 100644 --- a/activity/migrations/0001_initial.py +++ b/activity/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.3 on 2019-07-16 11:44 +# Generated by Django 2.2.3 on 2019-07-16 11:53 from django.conf import settings from django.db import migrations, models @@ -10,8 +10,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('note', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('note', '0001_initial'), ('member', '0001_initial'), ] diff --git a/member/migrations/0001_initial.py b/member/migrations/0001_initial.py index 02d19288..c3d7172d 100644 --- a/member/migrations/0001_initial.py +++ b/member/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.3 on 2019-07-16 11:44 +# Generated by Django 2.2.3 on 2019-07-16 11:53 from django.conf import settings from django.db import migrations, models @@ -46,8 +46,8 @@ class Migration(migrations.Migration): 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')), + ('phone_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='phone number')), + ('section', models.CharField(blank=True, help_text='e.g. "1A0", "9A♥", "SAPHIRE"', max_length=255, null=True, 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)), diff --git a/member/models.py b/member/models.py index dc80d87d..db52cdd9 100644 --- a/member/models.py +++ b/member/models.py @@ -30,12 +30,13 @@ class Profile(models.Model): max_length=50, blank=True, null=True, - default='', ) 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'), diff --git a/note/migrations/0001_initial.py b/note/migrations/0001_initial.py index 031d9f51..dd014d82 100644 --- a/note/migrations/0001_initial.py +++ b/note/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.3 on 2019-07-16 11:44 +# Generated by Django 2.2.3 on 2019-07-16 11:53 from django.conf import settings from django.db import migrations, models @@ -11,8 +11,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('member', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('member', '0001_initial'), ] operations = [ From 5172f748454d15c66cf97c8e9986a98d67365930 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 14:38:52 +0200 Subject: [PATCH 05/12] Move aliases to inline admin --- note/admin.py | 42 +++++++++++++++++++--- note/locale/fr/LC_MESSAGES/django.po | 2 +- note/migrations/0002_auto_20190716_1406.py | 18 ++++++++++ note/models/notes.py | 2 +- 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 note/migrations/0002_auto_20190716_1406.py diff --git a/note/admin.py b/note/admin.py index 7d82658d..8b786666 100644 --- a/note/admin.py +++ b/note/admin.py @@ -2,8 +2,42 @@ from django.contrib import admin from .models.notes import Alias, NoteClub, NoteSpecial, NoteUser + +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') + + +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') + + # Organize note by registration date + date_hierarchy = 'user__date_joined' + + # Register your models here. -admin.site.register(Alias) -admin.site.register(NoteClub) -admin.site.register(NoteSpecial) -admin.site.register(NoteUser) +admin.site.register(NoteClub, NoteClubAdmin) +admin.site.register(NoteSpecial, NoteSpecialAdmin) +admin.site.register(NoteUser, NoteUserAdmin) diff --git a/note/locale/fr/LC_MESSAGES/django.po b/note/locale/fr/LC_MESSAGES/django.po index a4c4bfb6..5616c6db 100644 --- a/note/locale/fr/LC_MESSAGES/django.po +++ b/note/locale/fr/LC_MESSAGES/django.po @@ -71,7 +71,7 @@ msgstr "note spéciale" #: models/notes.py:85 msgid "special notes" -msgstr "notes spéciale" +msgstr "notes spéciales" #: models/notes.py:93 models/transactions.py:18 msgid "name" diff --git a/note/migrations/0002_auto_20190716_1406.py b/note/migrations/0002_auto_20190716_1406.py new file mode 100644 index 00000000..399205aa --- /dev/null +++ b/note/migrations/0002_auto_20190716_1406.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.3 on 2019-07-16 12:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('note', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='noteclub', + old_name='user', + new_name='club', + ), + ] diff --git a/note/models/notes.py b/note/models/notes.py index 2c91d1c5..ce2d3ec1 100644 --- a/note/models/notes.py +++ b/note/models/notes.py @@ -54,7 +54,7 @@ class NoteClub(Note): """ A Note associated to a Club """ - user = models.OneToOneField( + club = models.OneToOneField( 'member.Club', on_delete=models.PROTECT, related_name='note', From 89cedd18f1d7965ad3793fb7857c4f65c6f7ec87 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 15:17:57 +0200 Subject: [PATCH 06/12] Admin for club --- member/admin.py | 3 ++- member/locale/fr/LC_MESSAGES/django.po | 2 +- member/models.py | 7 ++++++- note/admin.py | 5 +++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/member/admin.py b/member/admin.py index a6918d19..4ac79ec0 100644 --- a/member/admin.py +++ b/member/admin.py @@ -7,7 +7,7 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from .forms import CustomUserChangeForm -from .models import Profile +from .models import Profile, Club class ProfileInline(admin.StackedInline): @@ -35,3 +35,4 @@ class CustomUserAdmin(UserAdmin): admin.site.unregister(User) admin.site.register(User, CustomUserAdmin) +admin.site.register(Club) diff --git a/member/locale/fr/LC_MESSAGES/django.po b/member/locale/fr/LC_MESSAGES/django.po index e3795f30..75e11d6d 100644 --- a/member/locale/fr/LC_MESSAGES/django.po +++ b/member/locale/fr/LC_MESSAGES/django.po @@ -56,7 +56,7 @@ msgstr "courriel" #: models.py:70 msgid "membership fee" -msgstr "cotisation" +msgstr "cotisation pour adhérer" #: models.py:74 msgid "membership duration" diff --git a/member/models.py b/member/models.py index db52cdd9..eca2351c 100644 --- a/member/models.py +++ b/member/models.py @@ -59,8 +59,9 @@ class Club(models.Model): A student club """ name = models.CharField( - verbose_name=_('paid'), + verbose_name=_('name'), max_length=255, + unique=True, ) email = models.EmailField( verbose_name=_('email'), @@ -94,6 +95,9 @@ class Club(models.Model): verbose_name = _("club") verbose_name_plural = _("clubs") + def __str__(self): + return self.name + class Role(models.Model): """ @@ -102,6 +106,7 @@ class Role(models.Model): name = models.CharField( verbose_name=_('name'), max_length=255, + unique=True, ) class Meta: diff --git a/note/admin.py b/note/admin.py index 8b786666..9ec51a77 100644 --- a/note/admin.py +++ b/note/admin.py @@ -17,6 +17,8 @@ class NoteClubAdmin(admin.ModelAdmin): """ inlines = (AliasInlines,) list_display = ('club', 'balance', 'is_active') + list_filter = ('is_active',) + search_fields = ['club__name'] class NoteSpecialAdmin(admin.ModelAdmin): @@ -32,9 +34,12 @@ class NoteUserAdmin(admin.ModelAdmin): """ 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'] # Register your models here. From 9a35b8f9e0e321831736c1fd5a714605ba14ee9d Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 15:22:38 +0200 Subject: [PATCH 07/12] Move display image to Note --- note/locale/fr/LC_MESSAGES/django.po | 35 ++++++++++++++-------------- note/models/notes.py | 5 ++++ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/note/locale/fr/LC_MESSAGES/django.po b/note/locale/fr/LC_MESSAGES/django.po index 5616c6db..97d5f9c7 100644 --- a/note/locale/fr/LC_MESSAGES/django.po +++ b/note/locale/fr/LC_MESSAGES/django.po @@ -1,14 +1,9 @@ -# 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 13:47+0200\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 \n" "Language-Team: LANGUAGE \n" @@ -18,7 +13,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: apps.py:11 models/notes.py:34 +#: apps.py:11 models/notes.py:39 msgid "note" msgstr "note" @@ -41,47 +36,51 @@ msgid "" msgstr "" "Indique si la note est active. Désactiver cela plutôt que supprimer la note." -#: models/notes.py:35 +#: models/notes.py:33 +msgid "display image" +msgstr "image affichée" + +#: models/notes.py:40 msgid "notes" msgstr "notes" -#: models/notes.py:49 +#: models/notes.py:54 msgid "one's note" msgstr "note d'un utilisateur" -#: models/notes.py:50 +#: models/notes.py:55 msgid "users note" msgstr "notes des utilisateurs" -#: models/notes.py:64 +#: models/notes.py:69 msgid "club note" msgstr "note d'un club" -#: models/notes.py:65 +#: models/notes.py:70 msgid "clubs notes" msgstr "notes des clubs" -#: models/notes.py:78 models/transactions.py:31 models/transactions.py:64 +#: models/notes.py:83 models/transactions.py:31 models/transactions.py:64 msgid "type" msgstr "type" -#: models/notes.py:84 +#: models/notes.py:89 msgid "special note" msgstr "note spéciale" -#: models/notes.py:85 +#: models/notes.py:90 msgid "special notes" msgstr "notes spéciales" -#: models/notes.py:93 models/transactions.py:18 +#: models/notes.py:98 models/transactions.py:18 msgid "name" msgstr "nom" -#: models/notes.py:103 +#: models/notes.py:108 msgid "alias" msgstr "alias" -#: models/notes.py:104 +#: models/notes.py:109 msgid "aliases" msgstr "alias" diff --git a/note/models/notes.py b/note/models/notes.py index ce2d3ec1..f7c8c539 100644 --- a/note/models/notes.py +++ b/note/models/notes.py @@ -29,6 +29,11 @@ class Note(models.Model): 'Unselect this instead of deleting notes.' ), ) + display_image = models.ImageField( + verbose_name=_('display image'), + max_length=255, + blank=True, + ) class Meta: verbose_name = _("note") From b63aaf41503e55b8f4153985aa5c7faf64cb2614 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 15:24:05 +0200 Subject: [PATCH 08/12] Move display image to Note (2) --- .gitignore | 3 ++ member/locale/fr/LC_MESSAGES/django.po | 63 +++++++++++--------------- member/models.py | 5 -- tox.ini | 1 + 4 files changed, 31 insertions(+), 41 deletions(-) diff --git a/.gitignore b/.gitignore index 3bc22e7b..5d8adc55 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ settings_local.py env/ venv/ db.sqlite3 + +# Ignore migrations during first phase dev +migrations/ diff --git a/member/locale/fr/LC_MESSAGES/django.po b/member/locale/fr/LC_MESSAGES/django.po index 75e11d6d..226234a9 100644 --- a/member/locale/fr/LC_MESSAGES/django.po +++ b/member/locale/fr/LC_MESSAGES/django.po @@ -1,14 +1,9 @@ -# 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 13:46+0200\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 \n" "Language-Team: LANGUAGE \n" @@ -23,103 +18,99 @@ 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 +#: models.py:30 msgid "section" msgstr "section" -#: models.py:37 +#: models.py:31 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" -#: models.py:41 +#: models.py:37 msgid "address" msgstr "adresse" -#: models.py:47 models.py:61 +#: models.py:43 msgid "paid" msgstr "payé" -#: models.py:52 models.py:53 +#: models.py:48 models.py:49 msgid "user profile" msgstr "profil utilisateur" -#: models.py:65 +#: models.py:57 models.py:102 +msgid "name" +msgstr "nom" + +#: models.py:62 msgid "email" msgstr "courriel" -#: models.py:70 +#: models.py:67 msgid "membership fee" msgstr "cotisation pour adhérer" -#: models.py:74 +#: models.py:71 msgid "membership duration" msgstr "durée de l'adhésion" -#: models.py:75 +#: 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:80 +#: models.py:77 msgid "membership start" msgstr "début de l'adhésion" -#: models.py:81 +#: models.py:78 msgid "How long after January 1st the members can renew their membership." msgstr "" -#: models.py:86 +#: models.py:83 msgid "membership end" msgstr "fin de l'adhésion" -#: models.py:87 +#: 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:93 +#: models.py:90 msgid "club" msgstr "club" -#: models.py:94 +#: models.py:91 msgid "clubs" msgstr "clubs" -#: models.py:102 -msgid "name" -msgstr "nom" - -#: models.py:107 +#: models.py:108 msgid "role" msgstr "rôle" -#: models.py:108 +#: models.py:109 msgid "roles" msgstr "rôles" -#: models.py:125 +#: models.py:126 msgid "membership starts on" msgstr "l'adhésion commence le" -#: models.py:128 +#: models.py:129 msgid "membership ends on" msgstr "l'adhésion finie le" -#: models.py:132 +#: models.py:133 msgid "fee" msgstr "cotisation" -#: models.py:136 +#: models.py:137 msgid "membership" msgstr "adhésion" -#: models.py:137 +#: models.py:138 msgid "memberships" msgstr "adhésions" diff --git a/member/models.py b/member/models.py index eca2351c..dbe0ad07 100644 --- a/member/models.py +++ b/member/models.py @@ -20,11 +20,6 @@ class Profile(models.Model): 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, diff --git a/tox.ini b/tox.ini index 41474f6c..dd2f3efe 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,7 @@ deps = -r{toxinidir}/requirements.txt coverage commands = + ./manage.py makemigrations coverage run ./manage.py test {posargs} coverage report -m From 740b468547f097b5a42bc8855e5470a75cda23ae Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 15:42:31 +0200 Subject: [PATCH 09/12] More admin --- activity/admin.py | 30 ++++++++++++++++++++++++++++++ activity/models.py | 3 +++ member/admin.py | 2 +- note/admin.py | 17 +++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/activity/admin.py b/activity/admin.py index 6ac23376..1efe272c 100644 --- a/activity/admin.py +++ b/activity/admin.py @@ -1,3 +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) diff --git a/activity/models.py b/activity/models.py index ee411d76..2cc81732 100644 --- a/activity/models.py +++ b/activity/models.py @@ -23,6 +23,9 @@ class ActivityType(models.Model): verbose_name = _("activity type") verbose_name_plural = _("activity types") + def __str__(self): + return self.name + class Activity(models.Model): name = models.CharField( diff --git a/member/admin.py b/member/admin.py index 4ac79ec0..d055d268 100644 --- a/member/admin.py +++ b/member/admin.py @@ -7,7 +7,7 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from .forms import CustomUserChangeForm -from .models import Profile, Club +from .models import Club, Profile class ProfileInline(admin.StackedInline): diff --git a/note/admin.py b/note/admin.py index 9ec51a77..78936da9 100644 --- a/note/admin.py +++ b/note/admin.py @@ -1,6 +1,12 @@ +# -*- 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.notes import Alias, NoteClub, NoteSpecial, NoteUser +from .models.transactions import MembershipTransaction, Transaction, \ + TransactionTemplate class AliasInlines(admin.TabularInline): @@ -42,7 +48,18 @@ class NoteUserAdmin(admin.ModelAdmin): ordering = ['-user__date_joined'] +class TransactionTemplateAdmin(admin.ModelAdmin): + """ + Admin customisation for TransactionTemplate + """ + list_display = ('name', 'destination', 'amount', 'template_type') + list_filter = ('destination', 'template_type',) + + # Register your models here. admin.site.register(NoteClub, NoteClubAdmin) admin.site.register(NoteSpecial, NoteSpecialAdmin) admin.site.register(NoteUser, NoteUserAdmin) +admin.site.register(MembershipTransaction) +admin.site.register(Transaction) +admin.site.register(TransactionTemplate, TransactionTemplateAdmin) From ba0a4519e3b69d60d0c015e681515f3c11a10a79 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Tue, 16 Jul 2019 15:47:19 +0200 Subject: [PATCH 10/12] Add Django Revision in deps --- note_kfet/settings.py | 1 + requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/note_kfet/settings.py b/note_kfet/settings.py index 1fc0feb3..a4f837e9 100644 --- a/note_kfet/settings.py +++ b/note_kfet/settings.py @@ -44,6 +44,7 @@ INSTALLED_APPS = [ # External apps 'guardian', + 'reversion', # Note apps 'activity', diff --git a/requirements.txt b/requirements.txt index 5f3c52ab..6201c93e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ Pillow==6.1.0 pytz==2019.1 six==1.12.0 sqlparse==0.3.0 +django-reversion==3.0.3 \ No newline at end of file From eed5f4b5425cc80056ef9d165210749da27008cb Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 17 Jul 2019 09:37:11 +0200 Subject: [PATCH 11/12] Create user and club notes automatically --- activity/migrations/0001_initial.py | 76 ------------- member/migrations/0001_initial.py | 76 ------------- note/migrations/0001_initial.py | 123 --------------------- note/migrations/0002_auto_20190716_1406.py | 18 --- note/models/notes.py | 23 ++++ 5 files changed, 23 insertions(+), 293 deletions(-) delete mode 100644 activity/migrations/0001_initial.py delete mode 100644 member/migrations/0001_initial.py delete mode 100644 note/migrations/0001_initial.py delete mode 100644 note/migrations/0002_auto_20190716_1406.py diff --git a/activity/migrations/0001_initial.py b/activity/migrations/0001_initial.py deleted file mode 100644 index 5fb6579c..00000000 --- a/activity/migrations/0001_initial.py +++ /dev/null @@ -1,76 +0,0 @@ -# Generated by Django 2.2.3 on 2019-07-16 11:53 - -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), - ('note', '0001_initial'), - ('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')), - ], - options={ - 'verbose_name': 'activity', - 'verbose_name_plural': 'activities', - }, - ), - 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')), - ], - options={ - 'verbose_name': 'activity type', - 'verbose_name_plural': 'activity types', - }, - ), - 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)), - ], - options={ - 'verbose_name': 'guest', - 'verbose_name_plural': 'guests', - }, - ), - 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/member/migrations/0001_initial.py b/member/migrations/0001_initial.py deleted file mode 100644 index c3d7172d..00000000 --- a/member/migrations/0001_initial.py +++ /dev/null @@ -1,76 +0,0 @@ -# Generated by Django 2.2.3 on 2019-07-16 11:53 - -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')), - ], - options={ - 'verbose_name': 'club', - 'verbose_name_plural': 'clubs', - }, - ), - 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, max_length=50, null=True, verbose_name='phone number')), - ('section', models.CharField(blank=True, help_text='e.g. "1A0", "9A♥", "SAPHIRE"', max_length=255, null=True, 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/note/migrations/0001_initial.py b/note/migrations/0001_initial.py deleted file mode 100644 index dd014d82..00000000 --- a/note/migrations/0001_initial.py +++ /dev/null @@ -1,123 +0,0 @@ -# Generated by Django 2.2.3 on 2019-07-16 11:53 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('member', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Note', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('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={ - 'verbose_name': 'note', - 'verbose_name_plural': 'notes', - }, - ), - migrations.CreateModel( - name='Transaction', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('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={ - 'verbose_name': 'transaction', - 'verbose_name_plural': 'transactions', - }, - ), - migrations.CreateModel( - 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')), - ], - options={ - 'verbose_name': 'special note', - 'verbose_name_plural': 'special notes', - }, - bases=('note.note',), - ), - migrations.CreateModel( - name='TransactionTemplate', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('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': 'transaction template', - 'verbose_name_plural': 'transaction templates', - }, - ), - migrations.CreateModel( - name='Alias', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, unique=True, verbose_name='name')), - ('note', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='note.Note')), - ], - options={ - 'verbose_name': 'alias', - 'verbose_name_plural': 'aliases', - }, - ), - 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')), - ], - options={ - 'verbose_name': 'membership transaction', - 'verbose_name_plural': 'membership transactions', - }, - bases=('note.transaction',), - ), - ] diff --git a/note/migrations/0002_auto_20190716_1406.py b/note/migrations/0002_auto_20190716_1406.py deleted file mode 100644 index 399205aa..00000000 --- a/note/migrations/0002_auto_20190716_1406.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.3 on 2019-07-16 12:06 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('note', '0001_initial'), - ] - - operations = [ - migrations.RenameField( - model_name='noteclub', - old_name='user', - new_name='club', - ), - ] diff --git a/note/models/notes.py b/note/models/notes.py index f7c8c539..499678d9 100644 --- a/note/models/notes.py +++ b/note/models/notes.py @@ -4,6 +4,8 @@ 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 _ """ @@ -20,6 +22,7 @@ class Note(models.Model): balance = models.IntegerField( verbose_name=_('account balance'), help_text=_('in centimes, money credited for this instance'), + default=0, ) is_active = models.BooleanField( _('active'), @@ -107,3 +110,23 @@ class Alias(models.Model): 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() From 90ffaae2f222a5a9f587d1a12a7eb166cdf85572 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 17 Jul 2019 09:49:59 +0200 Subject: [PATCH 12/12] More models in admin --- member/admin.py | 7 ++++++- note/admin.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/member/admin.py b/member/admin.py index d055d268..dc595d7e 100644 --- a/member/admin.py +++ b/member/admin.py @@ -7,7 +7,7 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from .forms import CustomUserChangeForm -from .models import Club, Profile +from .models import Club, Membership, Profile, Role class ProfileInline(admin.StackedInline): @@ -33,6 +33,11 @@ class CustomUserAdmin(UserAdmin): return super().get_inline_instances(request, obj) +# Update Django User with profile admin.site.unregister(User) admin.site.register(User, CustomUserAdmin) + +# Add other models admin.site.register(Club) +admin.site.register(Membership) +admin.site.register(Role) diff --git a/note/admin.py b/note/admin.py index 78936da9..e634e607 100644 --- a/note/admin.py +++ b/note/admin.py @@ -26,6 +26,21 @@ class NoteClubAdmin(admin.ModelAdmin): 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): """ @@ -47,6 +62,21 @@ class NoteUserAdmin(admin.ModelAdmin): 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): """