feat(finance/admin): unified statement view (#6)
This PR replaces the separate admin views for unsubmitted, submitted and confirmed statements by one common view. To distinguish the state, we now display a colored badge in the changelist. The default permissions for the `Standard` group are changed so that normal users can continue to view statements they are related to when these are submitted or confirmed.main
parent
3b8964fbb0
commit
5d4f29be89
@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.2.20 on 2025-10-12 19:16
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('finance', '0011_remove_statement_confirmed_and_submitted'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='StatementOnExcursionProxy',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Statement',
|
||||
'verbose_name_plural': 'Statements',
|
||||
'abstract': False,
|
||||
'proxy': True,
|
||||
'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'),
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('finance.statement',),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,121 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import migrations
|
||||
from django.contrib.auth.management import create_permissions
|
||||
|
||||
STANDARD_PERMS = [
|
||||
('members', 'view_member'),
|
||||
('members', 'view_freizeit'),
|
||||
('members', 'add_global_freizeit'),
|
||||
('members', 'view_memberwaitinglist'),
|
||||
('members', 'view_memberunconfirmedproxy'),
|
||||
('mailer', 'view_message'),
|
||||
('mailer', 'add_global_message'),
|
||||
('finance', 'view_statement'),
|
||||
('finance', 'add_global_statement'),
|
||||
]
|
||||
|
||||
FINANCE_PERMS = [
|
||||
('finance', 'view_bill'),
|
||||
('finance', 'view_ledger'),
|
||||
('finance', 'add_ledger'),
|
||||
('finance', 'change_ledger'),
|
||||
('finance', 'delete_ledger'),
|
||||
('finance', 'view_global_statement'),
|
||||
('finance', 'change_global_statement'),
|
||||
('finance', 'process_statementsubmitted'),
|
||||
('finance', 'may_manage_confirmed_statements'),
|
||||
('finance', 'view_transaction'),
|
||||
('finance', 'change_transaction'),
|
||||
('finance', 'add_transaction'),
|
||||
('finance', 'delete_transaction'),
|
||||
('members', 'list_global_freizeit'),
|
||||
('members', 'view_global_freizeit'),
|
||||
]
|
||||
|
||||
WAITINGLIST_PERMS = [
|
||||
('members', 'view_global_memberwaitinglist'),
|
||||
('members', 'list_global_memberwaitinglist'),
|
||||
('members', 'change_global_memberwaitinglist'),
|
||||
('members', 'delete_global_memberwaitinglist'),
|
||||
]
|
||||
|
||||
TRAINING_PERMS = [
|
||||
('members', 'change_global_member'),
|
||||
('members', 'list_global_member'),
|
||||
('members', 'view_global_member'),
|
||||
('members', 'add_global_membertraining'),
|
||||
('members', 'change_global_membertraining'),
|
||||
('members', 'list_global_membertraining'),
|
||||
('members', 'view_global_membertraining'),
|
||||
('members', 'view_trainingcategory'),
|
||||
('members', 'add_trainingcategory'),
|
||||
('members', 'change_trainingcategory'),
|
||||
('members', 'delete_trainingcategory'),
|
||||
]
|
||||
|
||||
REGISTRATION_PERMS = [
|
||||
('members', 'may_manage_all_registrations'),
|
||||
('members', 'change_memberunconfirmedproxy'),
|
||||
('members', 'delete_memberunconfirmedproxy'),
|
||||
]
|
||||
|
||||
MATERIAL_PERMS = [
|
||||
('members', 'list_global_member'),
|
||||
('material', 'view_materialpart'),
|
||||
('material', 'change_materialpart'),
|
||||
('material', 'add_materialpart'),
|
||||
('material', 'delete_materialpart'),
|
||||
('material', 'view_materialcategory'),
|
||||
('material', 'change_materialcategory'),
|
||||
('material', 'add_materialcategory'),
|
||||
('material', 'delete_materialcategory'),
|
||||
('material', 'view_ownership'),
|
||||
('material', 'change_ownership'),
|
||||
('material', 'add_ownership'),
|
||||
('material', 'delete_ownership'),
|
||||
]
|
||||
|
||||
|
||||
def ensure_group_perms(apps, schema_editor, name, perm_names):
|
||||
"""
|
||||
Ensure the group `name` has the permissions `perm_names`. If the group does not
|
||||
exist, create it with the given permissions, otherwise add the missing ones.
|
||||
|
||||
This only adds permissions, already existing ones that are not listed here are not
|
||||
removed.
|
||||
"""
|
||||
db_alias = schema_editor.connection.alias
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
perms = [ Permission.objects.get(codename=codename, content_type__app_label=app_label) for app_label, codename in perm_names ]
|
||||
try:
|
||||
g = Group.objects.using(db_alias).get(name=name)
|
||||
for perm in perms:
|
||||
g.permissions.add(perm)
|
||||
g.save()
|
||||
# This case is only executed if users have manually removed one of the standard groups.
|
||||
except Group.DoesNotExist: # pragma: no cover
|
||||
g = Group.objects.using(db_alias).create(name=name)
|
||||
g.permissions.set(perms)
|
||||
g.save()
|
||||
|
||||
|
||||
def update_default_permission_groups(apps, schema_editor):
|
||||
ensure_group_perms(apps, schema_editor, "Standard", STANDARD_PERMS)
|
||||
ensure_group_perms(apps, schema_editor, "Finance", FINANCE_PERMS)
|
||||
ensure_group_perms(apps, schema_editor, "Waitinglist", WAITINGLIST_PERMS)
|
||||
ensure_group_perms(apps, schema_editor, "Trainings", TRAINING_PERMS)
|
||||
ensure_group_perms(apps, schema_editor, "Registrations", REGISTRATION_PERMS)
|
||||
ensure_group_perms(apps, schema_editor, "Material", MATERIAL_PERMS)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('finance', '0012_statementonexcursionproxy'),
|
||||
('members', '0044_membertraining_activity_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(update_default_permission_groups, migrations.RunPython.noop),
|
||||
]
|
||||
@ -0,0 +1,25 @@
|
||||
span.statement-unsubmitted, span.statement-submitted, span.statement-confirmed {
|
||||
color: black;
|
||||
padding: 4px;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
border-radius: 10px;
|
||||
width: 20px;
|
||||
min-width: 20px;
|
||||
max-width: 20px;
|
||||
}
|
||||
|
||||
span.statement-submitted {
|
||||
background-color: #e8e8bd;
|
||||
color: black;
|
||||
}
|
||||
|
||||
span.statement-unsubmitted {
|
||||
background-color: #f0dada;
|
||||
color: black;
|
||||
}
|
||||
|
||||
span.statement-confirmed {
|
||||
background-color: #e0eec5;
|
||||
color: black;
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
{% extends "admin/change_form_object_tools.html" %}
|
||||
{% load i18n admin_urls rules %}
|
||||
|
||||
{% block object-tools-items %}
|
||||
|
||||
{% if original.confirmed and perms.finance.may_manage_confirmed_statements %}
|
||||
<li>
|
||||
{% url opts|admin_urlname:'unconfirm' original.pk|admin_urlquote as invite_url %}
|
||||
<a class="historylink" href="{% add_preserved_filters invite_url %}">{% trans 'Unconfirm' %}</a>
|
||||
</li>
|
||||
<li>
|
||||
{% url opts|admin_urlname:'summary' original.pk|admin_urlquote as invite_url %}
|
||||
<a class="historylink" target="_blank" href="{% add_preserved_filters invite_url %}">{% trans 'Download summary' %}</a>
|
||||
</li>
|
||||
{% elif original.submitted and perms.finance.process_statementsubmitted %}
|
||||
<script>
|
||||
function requestWithCurrentURL(path) {
|
||||
var xpath = path + "?redirectTo=" + window.location.href;
|
||||
location.href = xpath;
|
||||
}
|
||||
</script>
|
||||
|
||||
<li>
|
||||
{% url opts|admin_urlname:'reduce_transactions' original.pk|admin_urlquote as invite_url %}
|
||||
<a value="hi" onclick='requestWithCurrentURL("{% add_preserved_filters invite_url %}")'>
|
||||
{% trans 'Reduce transactions' %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
{% url opts|admin_urlname:'overview' original.pk|admin_urlquote as invite_url %}
|
||||
<a class="historylink" href="{% add_preserved_filters invite_url %}">{% trans 'Overview' %}</a>
|
||||
</li>
|
||||
{% elif not original.submitted %}
|
||||
<li>
|
||||
{% url opts|admin_urlname:'submit' original.pk|admin_urlquote as invite_url %}
|
||||
<a class="historylink" href="{% add_preserved_filters invite_url %}">{% trans 'Submit' %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{{block.super}}
|
||||
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue