Make changes to members module

parent 0300c1af
......@@ -26,13 +26,12 @@ class ProfileInline(admin.StackedInline):
'address_street2', 'address_postal_code', 'address_city',
'address_country', 'student_number', 'phone_number',
'receive_optin', 'receive_newsletter', 'birthday',
'show_birthday', 'direct_debit_authorized', 'bank_account',
'initials', 'nickname', 'display_name_preference',
'profile_description', 'website', 'photo', 'emergency_contact',
'show_birthday', 'auto_renew', 'initials',
'nickname', 'display_name_preference', 'profile_description',
'website', 'photo', 'emergency_contact',
'emergency_contact_phone_number', 'language',
'event_permissions')
model = models.Profile
form = forms.ProfileForm
can_delete = False
......@@ -101,7 +100,8 @@ class UserAdmin(BaseUserAdmin):
'is_superuser',
AgeListFilter,
'profile__event_permissions',
'profile__starting_year')
'profile__starting_year',
'profile__auto_renew')
add_fieldsets = (
(None, {
......
......@@ -94,8 +94,7 @@
"display_name_preference": "full",
"language": "nl",
"receive_optin": true,
"direct_debit_authorized": false,
"bank_account": ""
"auto_renew": false
}
},
{
......@@ -121,8 +120,7 @@
"display_name_preference": "full",
"language": "nl",
"receive_optin": true,
"direct_debit_authorized": false,
"bank_account": ""
"auto_renew": false
}
},
{
......@@ -148,8 +146,7 @@
"display_name_preference": "full",
"language": "nl",
"receive_optin": true,
"direct_debit_authorized": false,
"bank_account": ""
"auto_renew": false
}
},
{
......@@ -175,8 +172,7 @@
"display_name_preference": "full",
"language": "nl",
"receive_optin": true,
"direct_debit_authorized": false,
"bank_account": ""
"auto_renew": false
}
},
{
......
......@@ -15,22 +15,11 @@ class ProfileForm(forms.ModelForm):
'phone_number', 'emergency_contact',
'emergency_contact_phone_number',
'show_birthday', 'website',
'profile_description', 'nickname',
'profile_description', 'nickname', 'initials',
'display_name_preference', 'photo', 'language',
'receive_optin', 'receive_newsletter']
model = Profile
def clean(self):
super().clean()
errors = {}
direct_debit_authorized = self.cleaned_data\
.get('direct_debit_authorized')
bank_account = self.cleaned_data.get('bank_account')
if direct_debit_authorized and not bank_account:
errors.update({'bank_account': _('Please enter a bank account')})
raise forms.ValidationError(errors)
class UserCreationForm(BaseUserCreationForm):
# Don't forget to edit the formset in admin.py!
......
......@@ -45,6 +45,20 @@ class Command(BaseCommand):
code = current_relations.pop(member.pk, None)
profile = member.profile
if member.bank_accounts.exists():
account = member.bank_accounts.last()
bank_account = {
'name': account.name,
'bic': account.bic or '',
'iban': account.iban,
}
else:
bank_account = {
'name': '',
'bic': '',
'iban': '',
}
fields = {
'website_id': member.pk,
'voornaam': member.first_name,
......@@ -57,11 +71,7 @@ class Command(BaseCommand):
'postcode': profile.address_postal_code,
'plaats': profile.address_city,
'land': profile.get_address_country_display(),
'bankrekeningnummer': {
'name': f'{profile.initials} {member.last_name}',
'bic': '',
'iban': profile.bank_account,
},
'bankrekeningnummer': bank_account,
}
replace_commands.append(ApiCommand(
......
from django.db import migrations
def forwards_func(apps, schema_editor):
Membership = apps.get_model('members', 'membership')
db_alias = schema_editor.connection.alias
Membership.objects.using(db_alias).filter(
type='supporter').update(type='benefactor')
def reverse_func(apps, schema_editor):
Membership = apps.get_model('members', 'membership')
db_alias = schema_editor.connection.alias
Membership.objects.using(db_alias).filter(
type='benefactor').update(type='supporter')
class Migration(migrations.Migration):
dependencies = [
('members', '0029_profile_address_country'),
]
operations = [
migrations.RunPython(forwards_func, reverse_func),
]
from django.db import migrations
from django.utils import timezone
def forwards_func(apps, schema_editor):
Member = apps.get_model('members', 'profile')
BankAccount = apps.get_model('payments', 'BankAccount')
db_alias = schema_editor.connection.alias
for profile in Member.objects.using(db_alias).exclude(
bank_account=None).all():
BankAccount.objects.using(db_alias).create(
owner=profile.user,
initials=profile.initials or profile.user.first_name[0],
last_name=profile.user.last_name,
valid_from=timezone.now(),
mandate_no=f'{profile.user.pk}-1',
signature="data:image/png;base64,",
iban=profile.bank_account
)
def reverse_func(apps, schema_editor):
BankAccount = apps.get_model('payments', 'BankAccount')
db_alias = schema_editor.connection.alias
for account in BankAccount.objects.using(db_alias).all():
account.owner.profile.bank_account = account.iban
account.owner.profile.save()
account.delete()
class Migration(migrations.Migration):
dependencies = [
('members', '0031_benefactor_model_value'),
('payments', '0004_bankaccount'),
]
operations = [
migrations.RunPython(forwards_func, reverse_func),
]
......@@ -15,8 +15,6 @@ from django.db.models import Q
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import pgettext_lazy, gettext_lazy as _
from localflavor.generic.countries.sepa import IBAN_SEPA_COUNTRIES
from localflavor.generic.models import IBANField
from activemembers.models import MemberGroup, MemberGroupMembership
from utils import countries
......@@ -404,27 +402,15 @@ class Profile(models.Model):
default=True,
)
# --- Direct debit information ----
# --- Membership preference ----
direct_debit_authorized = models.BooleanField(
choices=((True, _('Yes, I want Thalia to take the membership fees '
'from my bank account through direct debit for '
'each year.')),
(False, _('No, I will pay the contribution myself'))),
verbose_name=_('Direct debit'),
help_text=_('Each year, have Thalia take the membership fees from my '
'bank account'),
auto_renew = models.BooleanField(
choices=((True, _('Yes, enable auto renewal.')),
(False, _('No, manual renewal required.'))),
verbose_name=_('Automatically renew membership'),
default=False,
)
bank_account = IBANField(
verbose_name=_('Bank account'),
help_text=_('Bank account for direct debit'),
include_countries=IBAN_SEPA_COUNTRIES,
blank=True,
null=True,
)
def display_name(self):
pref = self.display_name_preference
if pref == 'nickname' and self.nickname is not None:
......
......@@ -199,7 +199,7 @@ def execute_data_minimisation(dry_run=False, members=None):
profile.emergency_contact_phone_number = None
profile.emergency_contact = None
profile.website = None
profile.bank_account = None
member.bank_accounts.all().delete()
if not dry_run:
profile.save()
......
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "members"|capfirst %} — {{ block.super }}{% endblock %}
{% block opengraph_title %}{% trans "members"|capfirst %} — {{ block.super }}{% endblock %}
{% block body %}
<section class="page-section">
<div class="container">
<h1 class="text-center section-title">{% trans 'Account' %}</h1>
<div class="row">
<div class="col-lg-6 offset-lg-3">
<p class="text-center">
{% blocktrans trimmed with user=request.member.username %}
You’re currently logged in as <strong>{{ user }}</strong>
{% endblocktrans %}.
</p>
<hr>
<div>
<a href="{% url 'members:profile' request.member.pk %}">{% trans "show public profile"|capfirst %}</a>
<p>{% blocktrans %}Take a look at your own profile.{% endblocktrans %}</p>
</div>
<hr>
<div>
<a href="{% url 'registrations:renew' %}">{% trans "manage membership"|capfirst %}</a>
<p>{% blocktrans %}Get information about your membership or renew it.{% endblocktrans %}</p>
</div>
<hr>
<div>
<a href="{% url 'members:edit-profile' %}">{% trans "edit profile"|capfirst %}</a>
<p>{% blocktrans %}Edit your profile and avatar.{% endblocktrans %}</p>
</div>
<hr>
<div>
<a href="{% url 'password_change' %}">{% trans "change password"|capfirst %}</a>
<p>{% blocktrans %}Change your accounts' password.{% endblocktrans %}</p>
</div>
<hr>
<div>
<a href="{% url 'logout' %}">{% trans "logout"|capfirst %}</a>
<p>{% blocktrans %}Leave the restricted area of the website.{% endblocktrans %}</p>
</div>
</div>
</div>
</div>
</section>
{% endblock %}}
from django.conf.urls import url
from django.urls import path
from django.urls import path, include
from . import views
app_name = "members"
urlpatterns = [
url('^profile/(?P<pk>[0-9]*)$', views.profile, name='profile'),
url('^profile/edit/$', views.edit_profile, name='edit-profile'),
url('^members/iban-export/$', views.iban_export, name='iban-export'),
path('profile/change-email/', views.EmailChangeFormView.as_view(),
name='email-change'),
path('profile/change-email/verify/<uuid:key>/',
views.EmailChangeVerifyView.as_view(), name='email-change-verify'),
path('profile/change-email/confirm/<uuid:key>/',
views.EmailChangeConfirmView.as_view(), name='email-change-confirm'),
url('^$', views.index, name='index'),
path('iban-export/', views.iban_export,
name='iban-export'),
path('members/', include([
path('', views.index,
name='index'),
path('statistics/', views.statistics,
name='statistics'),
path('profile/<int:pk>', views.profile,
name='profile'),
])),
path('user/', include([
path('', views.user,
name='user'),
path('edit-profile/', views.edit_profile,
name='edit-profile'),
path('change-email/', views.EmailChangeFormView.as_view(),
name='email-change'),
path('change-email/verify/<uuid:key>/',
views.EmailChangeVerifyView.as_view(),
name='email-change-verify'),
path('change-email/confirm/<uuid:key>/',
views.EmailChangeConfirmView.as_view(),
name='email-change-confirm'),
])),
]
......@@ -168,8 +168,8 @@ def profile(request, pk=None):
@login_required
def account(request):
return render(request, 'members/account.html')
def user(request):
return render(request, 'members/user.html')
@login_required
......@@ -191,18 +191,21 @@ def edit_profile(request):
@permission_required('auth.change_user')
def iban_export(request):
header_fields = ['name', 'username', 'iban']
header_fields = ['name', 'username', 'iban', 'bic']
rows = []
members = models.Member.current_members.filter(
profile__direct_debit_authorized=True)
profile__auto_renew=True)
for member in members:
if member.current_membership.type != 'honorary':
if (member.current_membership.type != 'honorary' and
member.bank_accounts.exists()):
bank_account = member.bank_accounts.last()
rows.append({
'name': member.get_full_name(),
'name': bank_account.name,
'username': member.username,
'iban': member.profile.bank_account
'iban': bank_account.iban,
'bic': bank_account.bic
})
response = HttpResponse(content_type='text/csv')
......
......@@ -214,17 +214,11 @@ class BankAccount(models.Model):
@property
def valid(self):
if self.valid_from and self.valid_until:
return self.valid_from < timezone.now().date() < self.valid_until
return self.valid_from and self.valid_from < timezone.now().date()
return self.valid_from <= timezone.now().date() < self.valid_until
return self.valid_from and self.valid_from <= timezone.now().date()
def __str__(self):
return f'{self.iban} - {self.owner.get_full_name()}'
class Meta:
ordering = ('created_at',)
......@@ -47,10 +47,11 @@ class BankAccountCreateView(SuccessMessageMixin, CreateView):
return super().post(request, *args, **kwargs)
def form_valid(self, form):
BankAccount.objects.filter(mandate_no=None).delete()
BankAccount.objects.exclude(mandate_no=None).update(
valid_until=timezone.now()
)
BankAccount.objects.filter(
owner=self.request.member, mandate_no=None).delete()
BankAccount.objects.filter(
owner=self.request.member
).exclude(mandate_no=None).update(valid_until=timezone.now())
return super().form_valid(form)
......@@ -63,7 +64,7 @@ class BankAccountRevokeView(SuccessMessageMixin, UpdateView):
def get_queryset(self):
return super().get_queryset().filter(owner=self.request.member)
def get(self, **kwargs):
raise Http404
......
"""Widgets provided by the payments package"""
import base64
from django.forms import Widget
......
......@@ -36,7 +36,7 @@ MAIN_MENU = [
'submenu': [
{'title': _('Member list'), 'name': 'members:index'},
{'title': _('Photos'), 'name': 'photos:index'},
{'title': _('Statistics'), 'name': 'statistics'},
{'title': _('Statistics'), 'name': 'members:statistics'},
{'title': _('Styleguide'), 'name': 'styleguide'},
{'title': _('Become Active'), 'name': 'become-active'},
{'title': _('Nextcloud'), 'url': 'https://cloud.thalia.nu/',
......
......@@ -89,7 +89,7 @@
<a href="{% url 'login' %}" class="btn btn-primary"><i
class="fas fa-user"></i></a>
{% else %}
<a href="{% url 'account' %}" class="btn btn-primary"><i
<a href="{% url 'members:user' %}" class="btn btn-primary"><i
class="fas fa-user"></i></a>
<button type="button"
class="btn btn-primary dropdown-toggle dropdown-toggle-split"
......
......@@ -36,11 +36,9 @@ from django.contrib import admin
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import LoginView
from django.contrib.sitemaps.views import sitemap
from django.urls import path
from django.views.generic import TemplateView
from django.views.i18n import JavaScriptCatalog
import members
from activemembers.sitemaps import sitemap as activemembers_sitemap
from documents.sitemaps import sitemap as documents_sitemap
from events.sitemaps import sitemap as events_sitemap
......@@ -74,12 +72,7 @@ urlpatterns = [ # pylint: disable=invalid-name
url(r'^event-registration-terms/', TemplateView.as_view(template_name='singlepages/event_registration_terms.html'), name='event-registration-terms'),
url(r'^admin/', admin.site.urls),
url(r'^alumni/$', AlumniEventsView.as_view(), name='alumni'),
url(r'^members/', include('members.urls')),
url(r'^registration/', include('registrations.urls')),
url(r'^account/', include([
url(r'^$', members.views.account, name='account'),
url(r'^finance/', include('payments.urls'))
])),
url(r'^events/', include('events.urls')),
url(r'^pizzas/', include('pizzas.urls')),
url(r'^newsletters/', include('newsletters.urls')),
......@@ -94,7 +87,6 @@ urlpatterns = [ # pylint: disable=invalid-name
url(r'^', include([ # 'for members' menu
url(r'^become-active/', login_required(TemplateView.as_view(template_name='singlepages/become_active.html')), name='become-active'),
url(r'^photos/', include('photos.urls')),
url(r'^statistics/$', members.views.statistics, name='statistics'),
url(r'^styleguide/$', views.styleguide, name='styleguide'),
url(r'^styleguide/file/(?P<filename>[\w\-_\.]+)$', views.styleguide_file, name='styleguide-file'),
])),
......@@ -133,6 +125,8 @@ urlpatterns = [ # pylint: disable=invalid-name
url(r'crash/$', views.crash),
# Custom media paths
url(r'^media/generate-thumbnail/(?P<request_path>.*)', generate_thumbnail, name='generate-thumbnail'),
url(r'^media/private/(?P<request_path>.*)$', private_media, name='private-media')
url(r'^media/private/(?P<request_path>.*)$', private_media, name='private-media'),
url('', include('members.urls')),
url('', include('payments.urls')),
] + static(settings.MEDIA_URL + 'public/',
document_root=os.path.join(settings.MEDIA_ROOT, 'public'))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment