5 Commits

Author SHA1 Message Date
Victor Andersson
65b249f2f8 fix: show edit button and translations 2025-11-11 21:09:59 +01:00
Victor Andersson
3d8e5ed410 feat: allow editing user profile info via modal 2025-11-11 21:04:33 +01:00
Victor Andersson
73ff0a9d45 fix: use precomputed permission flags in template 2025-11-11 20:48:12 +01:00
Victor Andersson
9fe70ac13b feat: add permission edit modal 2025-11-11 20:44:56 +01:00
Victor Andersson
cbada0794f feat: add granular permissions for editing and payments 2025-11-11 20:38:53 +01:00
12 changed files with 867 additions and 283 deletions

View File

@@ -117,6 +117,16 @@ class UserManagementForm(forms.Form):
is_staff = forms.BooleanField(required=False, initial=True, label=_("Administratör (staff)")) is_staff = forms.BooleanField(required=False, initial=True, label=_("Administratör (staff)"))
grant_view = forms.BooleanField(required=False, initial=True, label=_("Ge behörighet att se utlägg")) grant_view = forms.BooleanField(required=False, initial=True, label=_("Ge behörighet att se utlägg"))
grant_change = forms.BooleanField(required=False, initial=True, label=_("Ge behörighet att besluta utlägg")) grant_change = forms.BooleanField(required=False, initial=True, label=_("Ge behörighet att besluta utlägg"))
grant_edit = forms.BooleanField(
required=False,
initial=False,
label=_("Ge behörighet att redigera utläggsdetaljer"),
)
grant_pay = forms.BooleanField(
required=False,
initial=False,
label=_("Ge behörighet att markera betalningar"),
)
def clean_username(self): def clean_username(self):
username = self.cleaned_data["username"] username = self.cleaned_data["username"]
@@ -148,6 +158,44 @@ class UserPermissionForm(forms.Form):
is_staff = forms.BooleanField(required=False, label=_("Admin/staff")) is_staff = forms.BooleanField(required=False, label=_("Admin/staff"))
grant_view = forms.BooleanField(required=False, label=_("Får se utlägg")) grant_view = forms.BooleanField(required=False, label=_("Får se utlägg"))
grant_change = forms.BooleanField(required=False, label=_("Får besluta utlägg")) grant_change = forms.BooleanField(required=False, label=_("Får besluta utlägg"))
grant_edit = forms.BooleanField(required=False, label=_("Får redigera utlägg"))
grant_pay = forms.BooleanField(required=False, label=_("Får markera betalningar"))
first_name = forms.CharField(
max_length=150,
required=False,
label=_("Förnamn"),
widget=forms.TextInput(attrs={"class": INPUT_CLASSES}),
)
last_name = forms.CharField(
max_length=150,
required=False,
label=_("Efternamn"),
widget=forms.TextInput(attrs={"class": INPUT_CLASSES}),
)
email = forms.EmailField(
required=False,
label=_("E-post"),
widget=forms.EmailInput(attrs={"class": INPUT_CLASSES}),
)
new_password1 = forms.CharField(
required=False,
label=_("Nytt lösenord"),
widget=forms.PasswordInput(attrs={"class": INPUT_CLASSES}),
)
new_password2 = forms.CharField(
required=False,
label=_("Bekräfta nytt lösenord"),
widget=forms.PasswordInput(attrs={"class": INPUT_CLASSES}),
)
def clean(self):
cleaned = super().clean()
pwd1 = cleaned.get("new_password1")
pwd2 = cleaned.get("new_password2")
if pwd1 or pwd2:
if pwd1 != pwd2:
self.add_error("new_password2", _("Lösenorden matchar inte."))
return cleaned
class DeleteUserForm(forms.Form): class DeleteUserForm(forms.Form):

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.2.8 on 2025-11-11 19:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('claims', '0007_delete_systemsetting_alter_claim_receipt'),
]
operations = [
migrations.AlterModelOptions(
name='claim',
options={'ordering': ['-created_at'], 'permissions': [('mark_claim_paid', 'Can mark claims as paid'), ('edit_claim_details', 'Can edit claim details')]},
),
migrations.AlterField(
model_name='claimlog',
name='action',
field=models.CharField(choices=[('created', 'Submitted'), ('status_changed', 'Status changed'), ('marked_paid', 'Marked as paid'), ('project_changed', 'Project changed'), ('details_edited', 'Details edited')], max_length=32),
),
]

View File

@@ -81,6 +81,10 @@ class Claim(models.Model):
class Meta: class Meta:
ordering = ["-created_at"] ordering = ["-created_at"]
permissions = [
("mark_claim_paid", _("Can mark claims as paid")),
("edit_claim_details", _("Can edit claim details")),
]
def __str__(self): def __str__(self):
project = f" [{self.project}]" if self.project else "" project = f" [{self.project}]" if self.project else ""

View File

@@ -92,5 +92,6 @@
{% block content %}{% endblock %} {% block content %}{% endblock %}
</main> </main>
{% block modals %}{% endblock %} {% block modals %}{% endblock %}
{% block extra_js %}{% endblock %}
</body> </body>
</html> </html>

View File

@@ -135,7 +135,7 @@
<span class="text-xs text-gray-500">{% trans "Ej markerad som betald" %}</span> <span class="text-xs text-gray-500">{% trans "Ej markerad som betald" %}</span>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if can_change and claim.status == 'pending' %} {% if can_edit_claim and claim.status == 'pending' %}
<button type="button" <button type="button"
data-open-edit="{{ claim.id }}" data-open-edit="{{ claim.id }}"
class="rounded-full border border-gray-300 px-3 py-1 text-xs font-semibold text-gray-700 transition hover:bg-gray-100"> class="rounded-full border border-gray-300 px-3 py-1 text-xs font-semibold text-gray-700 transition hover:bg-gray-100">
@@ -186,15 +186,21 @@
</details> </details>
{% if payments_enabled and not claim.is_paid %} {% if payments_enabled and not claim.is_paid %}
<div class="flex flex-col items-start gap-3 md:items-end"> <div class="flex flex-col items-start gap-3 md:items-end">
<form method="post" class="w-full max-w-xs" onsubmit="return confirm('{% trans "Är du säker att du har lagt upp betalningen? Markera endast som betald om beloppet skickas till banken." %}');"> {% if can_mark_paid %}
{% csrf_token %} <form method="post" class="w-full max-w-xs" onsubmit="return confirm('{% trans "Är du säker att du har lagt upp betalningen? Markera endast som betald om beloppet skickas till banken." %}');">
<input type="hidden" name="action_type" value="payment"> {% csrf_token %}
<input type="hidden" name="payment_claim_id" value="{{ claim.id }}"> <input type="hidden" name="action_type" value="payment">
<button type="submit" class="flex w-full items-center justify-center gap-2 rounded-2xl bg-emerald-600 px-4 py-3 text-xs font-semibold uppercase tracking-wide text-white transition hover:bg-emerald-700"> <input type="hidden" name="payment_claim_id" value="{{ claim.id }}">
{% trans "Markera som betald" %} <button type="submit" class="flex w-full items-center justify-center gap-2 rounded-2xl bg-emerald-600 px-4 py-3 text-xs font-semibold uppercase tracking-wide text-white transition hover:bg-emerald-700">
</button> {% trans "Markera som betald" %}
</form> </button>
<p class="text-[11px] text-green-700">{% trans "Dubbelkolla belopp och kontonummer i panelen innan du bekräftar." %}</p> </form>
<p class="text-[11px] text-green-700">{% trans "Dubbelkolla belopp och kontonummer i panelen innan du bekräftar." %}</p>
{% else %}
<p class="rounded-2xl bg-white/80 px-4 py-3 text-xs text-green-800">
{% trans "Du saknar behörighet att markera betalningar. Kontakta en administratör." %}
</p>
{% endif %}
</div> </div>
{% elif not payments_enabled %} {% elif not payments_enabled %}
<div class="flex flex-col items-start gap-3 md:items-end"> <div class="flex flex-col items-start gap-3 md:items-end">
@@ -486,7 +492,7 @@
{% block modals %} {% block modals %}
{{ block.super }} {{ block.super }}
{% if can_change %} {% if can_edit_claim %}
{% for claim in claims %} {% for claim in claims %}
{% if claim.status == 'pending' %} {% if claim.status == 'pending' %}
<div class="fixed inset-0 z-40 hidden items-center justify-center bg-slate-900/80 p-4" <div class="fixed inset-0 z-40 hidden items-center justify-center bg-slate-900/80 p-4"

View File

@@ -65,9 +65,11 @@
<li class="flex items-start gap-3"> <li class="flex items-start gap-3">
<span class="mt-1 h-2 w-2 rounded-full bg-brand-400"></span> <span class="mt-1 h-2 w-2 rounded-full bg-brand-400"></span>
<span class="min-w-0"> <span class="min-w-0">
{% blocktrans %}Behörigheterna <code class="break-normal rounded bg-slate-800 px-2 py-1 text-xs">claims.view_claim</code> {% blocktrans %}Behörigheterna <code class="break-normal rounded bg-slate-800 px-2 py-1 text-xs">claims.view_claim</code>,
och <code class="break-normal rounded bg-slate-800 px-2 py-1 text-xs">claims.change_claim</code> <code class="break-normal rounded bg-slate-800 px-2 py-1 text-xs">claims.change_claim</code>,
styr åtkomst till adminvyn respektive beslutsflödet.{% endblocktrans %} <code class="break-normal rounded bg-slate-800 px-2 py-1 text-xs">claims.edit_claim_details</code>
och <code class="break-normal rounded bg-slate-800 px-2 py-1 text-xs">claims.mark_claim_paid</code>
styr åtkomst till adminvyn, beslutsflödet, redigering samt betalningspanelen.{% endblocktrans %}
</span> </span>
</li> </li>
<li class="flex items-start gap-3"> <li class="flex items-start gap-3">
@@ -103,31 +105,48 @@
{{ user.get_full_name|default:_("Saknar namn") }} · {{ user.email|default:_("Ingen e-post") }} {{ user.get_full_name|default:_("Saknar namn") }} · {{ user.email|default:_("Ingen e-post") }}
</p> </p>
</div> </div>
<p class="text-xs uppercase tracking-wide text-gray-400">ID: {{ user.id }}</p> <div class="flex items-center gap-3">
<p class="text-xs uppercase tracking-wide text-gray-400">ID: {{ user.id }}</p>
{% if can_change_users %}
<button type="button"
data-open-permission-edit="{{ user.id }}"
class="inline-flex items-center gap-2 rounded-full border border-gray-200 px-3 py-1 text-xs font-semibold text-gray-700 transition hover:bg-gray-100">
{% trans "Redigera användare" %}
</button>
{% endif %}
</div>
</div> </div>
<div class="mt-4 grid gap-6 lg:grid-cols-[2fr,1fr]"> <div class="mt-4 grid gap-6 lg:grid-cols-[2fr,1fr]">
<form method="post" class="space-y-4 rounded-2xl bg-slate-50 p-4"> <div class="rounded-2xl bg-slate-50 p-4">
{% csrf_token %} <p class="text-sm font-semibold text-gray-700">{% trans "Behörigheter" %}</p>
<input type="hidden" name="action" value="update"> <div class="mt-3 flex flex-wrap gap-2 text-xs">
{{ form.user_id }} {% if row.permission_flags.is_staff %}
<div class="space-y-3 text-sm text-gray-700"> <span class="rounded-full px-3 py-1 bg-emerald-100 text-emerald-800">{% trans "Admin/staff" %}</span>
<label class="flex items-center gap-2" for="{{ form.is_staff.id_for_label }}"> {% endif %}
{{ form.is_staff }} {% if row.permission_flags.view %}
<span>{% trans "Admin/staff" %}</span> <span class="rounded-full px-3 py-1 bg-blue-100 text-blue-800">{% trans "Får se utlägg" %}</span>
</label> {% endif %}
<label class="flex items-center gap-2" for="{{ form.grant_view.id_for_label }}"> {% if row.permission_flags.change %}
{{ form.grant_view }} <span class="rounded-full px-3 py-1 bg-indigo-100 text-indigo-800">{% trans "Får besluta utlägg" %}</span>
<span>{% trans "Får se utlägg" %}</span> {% endif %}
</label> {% if row.permission_flags.edit %}
<label class="flex items-center gap-2" for="{{ form.grant_change.id_for_label }}"> <span class="rounded-full px-3 py-1 bg-purple-100 text-purple-800">{% trans "Får redigera utlägg" %}</span>
{{ form.grant_change }} {% endif %}
<span>{% trans "Får besluta utlägg" %}</span> {% if row.permission_flags.pay %}
</label> <span class="rounded-full px-3 py-1 bg-amber-100 text-amber-800">{% trans "Får markera betalningar" %}</span>
{% endif %}
{% if not row.permission_flags.is_staff and not row.permission_flags.view and not row.permission_flags.change and not row.permission_flags.edit and not row.permission_flags.pay %}
<span class="rounded-full bg-slate-200 px-3 py-1 text-slate-600">{% trans "Inga behörigheter tilldelade" %}</span>
{% endif %}
</div> </div>
<button type="submit" class="w-full rounded-2xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-brand-700"> {% if can_change_users %}
{% trans "Spara behörigheter" %} <button type="button"
</button> data-open-permission-edit="{{ user.id }}"
</form> class="mt-4 inline-flex items-center gap-2 rounded-full bg-brand-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-brand-700">
{% trans "Redigera behörigheter" %}
</button>
{% endif %}
</div>
<div class="rounded-2xl border border-red-100 bg-red-50 p-4 text-sm text-red-800"> <div class="rounded-2xl border border-red-100 bg-red-50 p-4 text-sm text-red-800">
<p class="font-semibold">{% trans "Ta bort konto" %}</p> <p class="font-semibold">{% trans "Ta bort konto" %}</p>
{% if delete_form %} {% if delete_form %}
@@ -157,3 +176,191 @@
</section> </section>
</section> </section>
{% endblock %} {% endblock %}
{% block modals %}
{{ block.super }}
{% if can_change_users %}
{% for row in user_rows %}
{% with user=row.user form=row.permission_form %}
<div class="fixed inset-0 z-40 hidden items-center justify-center bg-slate-900/80 p-4"
data-permission-modal="{{ user.id }}"
aria-hidden="true"
role="dialog"
aria-modal="true">
<div class="w-full max-w-xl rounded-3xl bg-white p-6 shadow-2xl">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-semibold uppercase tracking-wide text-gray-500">{% trans "Redigera behörigheter" %}</p>
<h3 class="text-xl font-semibold text-gray-900">{{ user.username }}</h3>
</div>
<button type="button"
data-close-permission-edit
class="rounded-full bg-gray-100 px-3 py-1 text-xs font-semibold text-gray-600 transition hover:bg-gray-200">
{% trans "Stäng" %}
</button>
</div>
<form method="post" class="mt-4 space-y-4">
{% csrf_token %}
<input type="hidden" name="action" value="update">
{{ form.user_id }}
<div class="space-y-5 text-sm text-gray-800">
<div>
<p class="text-xs font-semibold uppercase tracking-wide text-gray-500">{% trans "Kontaktuppgifter" %}</p>
<div class="mt-3 grid gap-3 md:grid-cols-2">
<div>
<label class="text-xs font-semibold text-gray-600" for="{{ form.first_name.id_for_label }}">{{ form.first_name.label }}</label>
{{ form.first_name }}
{% for error in form.first_name.errors %}
<p class="text-xs text-rose-600">{{ error }}</p>
{% endfor %}
</div>
<div>
<label class="text-xs font-semibold text-gray-600" for="{{ form.last_name.id_for_label }}">{{ form.last_name.label }}</label>
{{ form.last_name }}
{% for error in form.last_name.errors %}
<p class="text-xs text-rose-600">{{ error }}</p>
{% endfor %}
</div>
<div class="md:col-span-2">
<label class="text-xs font-semibold text-gray-600" for="{{ form.email.id_for_label }}">{{ form.email.label }}</label>
{{ form.email }}
{% for error in form.email.errors %}
<p class="text-xs text-rose-600">{{ error }}</p>
{% endfor %}
</div>
</div>
</div>
<div>
<p class="text-xs font-semibold uppercase tracking-wide text-gray-500">{% trans "Lösenord" %}</p>
<div class="mt-3 grid gap-3 md:grid-cols-2">
<div>
<label class="text-xs font-semibold text-gray-600" for="{{ form.new_password1.id_for_label }}">{{ form.new_password1.label }}</label>
{{ form.new_password1 }}
{% for error in form.new_password1.errors %}
<p class="text-xs text-rose-600">{{ error }}</p>
{% endfor %}
</div>
<div>
<label class="text-xs font-semibold text-gray-600" for="{{ form.new_password2.id_for_label }}">{{ form.new_password2.label }}</label>
{{ form.new_password2 }}
{% for error in form.new_password2.errors %}
<p class="text-xs text-rose-600">{{ error }}</p>
{% endfor %}
</div>
</div>
<p class="mt-1 text-xs text-gray-500">{% trans "Lämna fälten tomma för att behålla nuvarande lösenord." %}</p>
</div>
<div class="space-y-3">
<p class="text-xs font-semibold uppercase tracking-wide text-gray-500">{% trans "Behörigheter" %}</p>
<label class="flex items-center gap-3" for="{{ form.is_staff.id_for_label }}">
{{ form.is_staff }}
<span>{% trans "Admin/staff" %}</span>
</label>
<label class="flex items-center gap-3" for="{{ form.grant_view.id_for_label }}">
{{ form.grant_view }}
<span>{% trans "Får se utlägg" %}</span>
</label>
<label class="flex items-center gap-3" for="{{ form.grant_change.id_for_label }}">
{{ form.grant_change }}
<span>{% trans "Får besluta utlägg" %}</span>
</label>
<label class="flex items-center gap-3" for="{{ form.grant_edit.id_for_label }}">
{{ form.grant_edit }}
<span>{% trans "Får redigera utlägg" %}</span>
</label>
<label class="flex items-center gap-3" for="{{ form.grant_pay.id_for_label }}">
{{ form.grant_pay }}
<span>{% trans "Får markera betalningar" %}</span>
</label>
</div>
</div>
<div class="flex items-center justify-end gap-3">
<button type="button"
data-close-permission-edit
class="rounded-full border border-gray-300 px-4 py-2 text-sm font-semibold text-gray-600 transition hover:bg-gray-100">
{% trans "Avbryt" %}
</button>
<button type="submit" class="rounded-full bg-brand-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-brand-700">
{% trans "Spara behörigheter" %}
</button>
</div>
</form>
</div>
</div>
{% endwith %}
{% endfor %}
{% endif %}
{% endblock %}
{% block extra_js %}
{{ block.super }}
{% if can_change_users %}
<script>
(function () {
function lockScroll() {
document.body.classList.add("overflow-hidden");
}
function unlockScrollIfNeeded() {
const anyOpen = Array.from(document.querySelectorAll("[data-permission-modal]")).some(
(modal) => !modal.classList.contains("hidden")
);
if (!anyOpen) {
document.body.classList.remove("overflow-hidden");
}
}
function openModal(id) {
const modal = document.querySelector(`[data-permission-modal="${id}"]`);
if (!modal) return;
modal.classList.remove("hidden");
modal.classList.add("flex");
modal.setAttribute("aria-hidden", "false");
lockScroll();
}
function closeModal(modal) {
modal.classList.add("hidden");
modal.classList.remove("flex");
modal.setAttribute("aria-hidden", "true");
unlockScrollIfNeeded();
}
document.addEventListener("click", (event) => {
const backdrop = event.target.closest("[data-permission-modal]");
if (backdrop && event.target === backdrop) {
closeModal(backdrop);
}
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
document.querySelectorAll("[data-permission-modal]").forEach((modal) => {
if (!modal.classList.contains("hidden")) {
closeModal(modal);
}
});
}
});
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("[data-open-permission-edit]").forEach((button) => {
button.addEventListener("click", (event) => {
event.preventDefault();
openModal(button.dataset.openPermissionEdit);
});
});
document.querySelectorAll("[data-close-permission-edit]").forEach((button) => {
button.addEventListener("click", (event) => {
event.preventDefault();
const modal = button.closest("[data-permission-modal]");
if (modal) {
closeModal(modal);
}
});
});
});
})();
</script>
{% endif %}
{% endblock %}

View File

@@ -81,7 +81,9 @@ class DashboardViewTests(TestCase):
self.user = User.objects.create_user(username="admin", password="test123", email="admin@example.com") self.user = User.objects.create_user(username="admin", password="test123", email="admin@example.com")
view_perm = Permission.objects.get(codename="view_claim") view_perm = Permission.objects.get(codename="view_claim")
change_perm = Permission.objects.get(codename="change_claim") change_perm = Permission.objects.get(codename="change_claim")
self.user.user_permissions.add(view_perm, change_perm) edit_perm = Permission.objects.get(codename="edit_claim_details")
pay_perm = Permission.objects.get(codename="mark_claim_paid")
self.user.user_permissions.add(view_perm, change_perm, edit_perm, pay_perm)
self.client.force_login(self.user) self.client.force_login(self.user)
def _create_claim(self, **kwargs): def _create_claim(self, **kwargs):
@@ -197,3 +199,80 @@ class DashboardViewTests(TestCase):
claim.refresh_from_db() claim.refresh_from_db()
self.assertNotEqual(claim.full_name, "Blocked") self.assertNotEqual(claim.full_name, "Blocked")
self.assertFalse(claim.logs.filter(action=ClaimLog.Action.DETAILS_EDITED).exists()) self.assertFalse(claim.logs.filter(action=ClaimLog.Action.DETAILS_EDITED).exists())
def test_edit_requires_permission(self):
self.user.user_permissions.remove(Permission.objects.get(codename="edit_claim_details"))
claim = self._create_claim()
response = self.client.post(
reverse("claims:admin-list"),
{
"action_type": "edit",
"edit_claim_id": claim.id,
"full_name": "Nope",
"email": "nope@example.com",
"account_number": "456",
"amount": "200",
"currency": Claim.Currency.SEK,
"project": "",
"description": "Should fail",
},
follow=True,
)
self.assertEqual(response.status_code, 200)
claim.refresh_from_db()
self.assertNotEqual(claim.full_name, "Nope")
self.assertFalse(claim.logs.filter(action=ClaimLog.Action.DETAILS_EDITED).exists())
@override_settings(CLAIMS_ENABLE_INTERNAL_PAYMENTS=True)
def test_mark_paid_requires_permission(self):
claim = self._create_claim(status=Claim.Status.APPROVED)
self.user.user_permissions.remove(Permission.objects.get(codename="mark_claim_paid"))
response = self.client.post(
reverse("claims:admin-list"),
{
"action_type": "payment",
"payment_claim_id": claim.id,
},
follow=True,
)
self.assertEqual(response.status_code, 200)
claim.refresh_from_db()
self.assertIsNone(claim.paid_at)
self.assertFalse(claim.logs.filter(action=ClaimLog.Action.MARKED_PAID).exists())
class UserManagementViewTests(TestCase):
def setUp(self):
User = get_user_model()
self.admin = User.objects.create_user(username="manager", password="test123", email="manager@example.com")
perms = Permission.objects.filter(codename__in=["view_user", "change_user"])
self.admin.user_permissions.add(*perms)
self.client.force_login(self.admin)
self.target = User.objects.create_user(username="editor", password="oldpass123", email="old@example.com")
def test_admin_can_update_profile_and_password(self):
response = self.client.post(
reverse("claims:user-manage"),
{
"action": "update",
"user_id": self.target.id,
"is_staff": "on",
"grant_view": "on",
"grant_change": "",
"grant_edit": "on",
"grant_pay": "on",
"first_name": "New",
"last_name": "Name",
"email": "new@example.com",
"new_password1": "StrongPass123!",
"new_password2": "StrongPass123!",
},
follow=True,
)
self.assertEqual(response.status_code, 200)
target = get_user_model().objects.get(pk=self.target.pk)
self.assertEqual(target.first_name, "New")
self.assertEqual(target.last_name, "Name")
self.assertEqual(target.email, "new@example.com")
self.assertTrue(target.is_staff)
self.assertTrue(target.check_password("StrongPass123!"))

View File

@@ -3,7 +3,7 @@ from decimal import Decimal
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model, password_validation, update_session_auth_hash
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.db.models import Sum from django.db.models import Sum
@@ -14,6 +14,7 @@ from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views import View from django.views import View
from django.views.generic import ListView, TemplateView from django.views.generic import ListView, TemplateView
from django.core.exceptions import ValidationError
from .forms import ( from .forms import (
ClaimDecisionForm, ClaimDecisionForm,
@@ -160,6 +161,8 @@ class ClaimDashboardView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
context["status_choices"] = Claim.Status.choices context["status_choices"] = Claim.Status.choices
context["decision_choices"] = ClaimDecisionForm().fields["action"].choices context["decision_choices"] = ClaimDecisionForm().fields["action"].choices
context["can_change"] = self.request.user.has_perm("claims.change_claim") context["can_change"] = self.request.user.has_perm("claims.change_claim")
context["can_edit_claim"] = self.request.user.has_perm("claims.edit_claim_details")
context["can_mark_paid"] = self.request.user.has_perm("claims.mark_claim_paid")
context["payments_enabled"] = getattr(settings, "CLAIMS_ENABLE_INTERNAL_PAYMENTS", False) context["payments_enabled"] = getattr(settings, "CLAIMS_ENABLE_INTERNAL_PAYMENTS", False)
context["summary"] = self._build_summary() context["summary"] = self._build_summary()
context["project_options"] = Project.objects.filter(is_active=True).order_by("name") context["project_options"] = Project.objects.filter(is_active=True).order_by("name")
@@ -234,8 +237,8 @@ class ClaimDashboardView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
if not getattr(settings, "CLAIMS_ENABLE_INTERNAL_PAYMENTS", False): if not getattr(settings, "CLAIMS_ENABLE_INTERNAL_PAYMENTS", False):
messages.error(request, _("Betalningshantering är inte aktiverad.")) messages.error(request, _("Betalningshantering är inte aktiverad."))
return redirect(request.get_full_path()) return redirect(request.get_full_path())
if not request.user.has_perm("claims.change_claim"): if not request.user.has_perm("claims.mark_claim_paid"):
messages.error(request, _("Du har inte behörighet att uppdatera utlägg.")) messages.error(request, _("Du har inte behörighet att markera betalningar i systemet."))
return redirect(request.get_full_path()) return redirect(request.get_full_path())
claim = get_object_or_404(Claim, pk=request.POST.get("payment_claim_id")) claim = get_object_or_404(Claim, pk=request.POST.get("payment_claim_id"))
@@ -258,8 +261,8 @@ class ClaimDashboardView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
return redirect(request.get_full_path()) return redirect(request.get_full_path())
def _handle_edit(self, request): def _handle_edit(self, request):
if not request.user.has_perm("claims.change_claim"): if not request.user.has_perm("claims.edit_claim_details"):
messages.error(request, _("Du har inte behörighet att uppdatera utlägg.")) messages.error(request, _("Du har inte behörighet att redigera utlägg."))
return redirect(request.get_full_path()) return redirect(request.get_full_path())
claim = get_object_or_404(Claim, pk=request.POST.get("edit_claim_id")) claim = get_object_or_404(Claim, pk=request.POST.get("edit_claim_id"))
if claim.status != Claim.Status.PENDING: if claim.status != Claim.Status.PENDING:
@@ -375,17 +378,30 @@ class UserManagementView(LoginRequiredMixin, PermissionRequiredMixin, TemplateVi
users = User.objects.order_by("username") users = User.objects.order_by("username")
rows = [] rows = []
for user in users: for user in users:
perms = {
"is_staff": user.is_staff,
"view": user.has_perm("claims.view_claim"),
"change": user.has_perm("claims.change_claim"),
"edit": user.has_perm("claims.edit_claim_details"),
"pay": user.has_perm("claims.mark_claim_paid"),
}
rows.append( rows.append(
{ {
"user": user, "user": user,
"permission_form": UserPermissionForm( "permission_form": UserPermissionForm(
initial={ initial={
"user_id": user.id, "user_id": user.id,
"is_staff": user.is_staff, "is_staff": perms["is_staff"],
"grant_view": user.has_perm("claims.view_claim"), "grant_view": perms["view"],
"grant_change": user.has_perm("claims.change_claim"), "grant_change": perms["change"],
"grant_edit": perms["edit"],
"grant_pay": perms["pay"],
"first_name": user.first_name,
"last_name": user.last_name,
"email": user.email,
} }
), ),
"permission_flags": perms,
"delete_form": None "delete_form": None
if user == self.request.user or user.is_superuser if user == self.request.user or user.is_superuser
else DeleteUserForm(initial={"user_id": user.id}), else DeleteUserForm(initial={"user_id": user.id}),
@@ -393,6 +409,7 @@ class UserManagementView(LoginRequiredMixin, PermissionRequiredMixin, TemplateVi
) )
context["user_rows"] = rows context["user_rows"] = rows
context["create_form"] = kwargs.get("create_form") or UserManagementForm() context["create_form"] = kwargs.get("create_form") or UserManagementForm()
context["can_change_users"] = self.request.user.has_perm("auth.change_user")
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
@@ -413,6 +430,8 @@ class UserManagementView(LoginRequiredMixin, PermissionRequiredMixin, TemplateVi
) )
self._set_perm(user, "claims.view_claim", form.cleaned_data.get("grant_view", False)) self._set_perm(user, "claims.view_claim", form.cleaned_data.get("grant_view", False))
self._set_perm(user, "claims.change_claim", form.cleaned_data.get("grant_change", False)) self._set_perm(user, "claims.change_claim", form.cleaned_data.get("grant_change", False))
self._set_perm(user, "claims.edit_claim_details", form.cleaned_data.get("grant_edit", False))
self._set_perm(user, "claims.mark_claim_paid", form.cleaned_data.get("grant_pay", False))
messages.success(request, _("Användaren %(user)s skapades.") % {"user": user.username}) messages.success(request, _("Användaren %(user)s skapades.") % {"user": user.username})
return redirect(request.path) return redirect(request.path)
return self.render_to_response(self.get_context_data(create_form=form)) return self.render_to_response(self.get_context_data(create_form=form))
@@ -423,13 +442,39 @@ class UserManagementView(LoginRequiredMixin, PermissionRequiredMixin, TemplateVi
form = UserPermissionForm(request.POST) form = UserPermissionForm(request.POST)
if form.is_valid(): if form.is_valid():
user = get_object_or_404(User, pk=form.cleaned_data["user_id"]) user = get_object_or_404(User, pk=form.cleaned_data["user_id"])
if user == request.user and not form.cleaned_data["is_staff"]: new_is_staff = form.cleaned_data["is_staff"]
if user == request.user and not new_is_staff:
messages.error(request, _("Du kan inte ta bort din egen staff-status.")) messages.error(request, _("Du kan inte ta bort din egen staff-status."))
return redirect(request.path) return redirect(request.path)
user.is_staff = form.cleaned_data["is_staff"] update_fields = set()
user.save(update_fields=["is_staff"]) if user.is_staff != new_is_staff:
user.is_staff = new_is_staff
update_fields.add("is_staff")
for attr in ("first_name", "last_name", "email"):
new_value = form.cleaned_data.get(attr)
if new_value is None:
continue
if getattr(user, attr) != new_value:
setattr(user, attr, new_value)
update_fields.add(attr)
new_password = form.cleaned_data.get("new_password1")
if new_password:
try:
password_validation.validate_password(new_password, user)
except ValidationError as exc:
for error in exc:
messages.error(request, error)
return redirect(request.path)
user.set_password(new_password)
update_fields.add("password")
if update_fields:
user.save(update_fields=list(update_fields))
if new_password and user == request.user:
update_session_auth_hash(request, user)
self._set_perm(user, "claims.view_claim", form.cleaned_data["grant_view"]) self._set_perm(user, "claims.view_claim", form.cleaned_data["grant_view"])
self._set_perm(user, "claims.change_claim", form.cleaned_data["grant_change"]) self._set_perm(user, "claims.change_claim", form.cleaned_data["grant_change"])
self._set_perm(user, "claims.edit_claim_details", form.cleaned_data["grant_edit"])
self._set_perm(user, "claims.mark_claim_paid", form.cleaned_data["grant_pay"])
messages.success(request, _("Behörigheter uppdaterades för %(user)s.") % {"user": user.username}) messages.success(request, _("Behörigheter uppdaterades för %(user)s.") % {"user": user.username})
else: else:
messages.error(request, _("Kunde inte uppdatera behörigheter.")) messages.error(request, _("Kunde inte uppdatera behörigheter."))

Binary file not shown.

View File

@@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: claims-system 0.1\n" "Project-Id-Version: claims-system 0.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-11 19:06+0000\n" "POT-Creation-Date: 2025-11-11 20:06+0000\n"
"PO-Revision-Date: 2025-11-08 23:40+0100\n" "PO-Revision-Date: 2025-11-08 23:40+0100\n"
"Last-Translator: ChatGPT <noreply@example.com>\n" "Last-Translator: ChatGPT <noreply@example.com>\n"
"Language-Team: English\n" "Language-Team: English\n"
@@ -13,42 +13,42 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: claims/forms.py:19 claims/forms.py:100 #: claims/forms.py:19 claims/forms.py:100
#: claims/templates/claims/dashboard.html:516 #: claims/templates/claims/dashboard.html:522
msgid "Namn" msgid "Namn"
msgstr "Name" msgstr "Name"
#: claims/forms.py:23 claims/forms.py:101 claims/forms.py:112 #: claims/forms.py:23 claims/forms.py:101 claims/forms.py:112
#: claims/templates/claims/dashboard.html:176 #: claims/forms.py:177 claims/templates/claims/dashboard.html:176
#: claims/templates/claims/dashboard.html:520 #: claims/templates/claims/dashboard.html:526
msgid "E-post" msgid "E-post"
msgstr "Email" msgstr "Email"
#: claims/forms.py:28 claims/forms.py:102 #: claims/forms.py:28 claims/forms.py:102
#: claims/templates/claims/dashboard.html:164 #: claims/templates/claims/dashboard.html:164
#: claims/templates/claims/dashboard.html:524 #: claims/templates/claims/dashboard.html:530
msgid "Kontonummer" msgid "Kontonummer"
msgstr "Account number" msgstr "Account number"
#: claims/forms.py:49 claims/forms.py:106 #: claims/forms.py:49 claims/forms.py:106
#: claims/templates/claims/dashboard.html:211 #: claims/templates/claims/dashboard.html:217
#: claims/templates/claims/dashboard.html:550 #: claims/templates/claims/dashboard.html:556
msgid "Beskrivning" msgid "Beskrivning"
msgstr "Description" msgstr "Description"
#: claims/forms.py:50 claims/forms.py:103 #: claims/forms.py:50 claims/forms.py:103
#: claims/templates/claims/dashboard.html:160 #: claims/templates/claims/dashboard.html:160
#: claims/templates/claims/dashboard.html:528 #: claims/templates/claims/dashboard.html:534
#: claims/templates/claims/my_claims.html:23 #: claims/templates/claims/my_claims.html:23
msgid "Belopp" msgid "Belopp"
msgstr "Amount" msgstr "Amount"
#: claims/forms.py:51 claims/forms.py:104 #: claims/forms.py:51 claims/forms.py:104
#: claims/templates/claims/dashboard.html:532 #: claims/templates/claims/dashboard.html:538
msgid "Valuta" msgid "Valuta"
msgstr "Currency" msgstr "Currency"
#: claims/forms.py:52 claims/forms.py:105 #: claims/forms.py:52 claims/forms.py:105
#: claims/templates/claims/dashboard.html:540 #: claims/templates/claims/dashboard.html:546
msgid "Evenemang/Projekt" msgid "Evenemang/Projekt"
msgstr "Project" msgstr "Project"
@@ -65,12 +65,12 @@ msgid "Neka"
msgstr "Reject" msgstr "Reject"
#: claims/forms.py:67 claims/models.py:29 #: claims/forms.py:67 claims/models.py:29
#: claims/templates/claims/dashboard.html:332 #: claims/templates/claims/dashboard.html:338
msgid "Pending" msgid "Pending"
msgstr "Pending" msgstr "Pending"
#: claims/forms.py:75 claims/templates/claims/dashboard.html:126 #: claims/forms.py:75 claims/templates/claims/dashboard.html:126
#: claims/templates/claims/dashboard.html:268 #: claims/templates/claims/dashboard.html:274
msgid "Kommentar" msgid "Kommentar"
msgstr "Comment" msgstr "Comment"
@@ -82,15 +82,15 @@ msgstr "A comment is required when you reject an expense."
msgid "Användarnamn" msgid "Användarnamn"
msgstr "Username" msgstr "Username"
#: claims/forms.py:113 #: claims/forms.py:113 claims/forms.py:166
msgid "Förnamn" msgid "Förnamn"
msgstr "First name" msgstr "First name"
#: claims/forms.py:114 #: claims/forms.py:114 claims/forms.py:172
msgid "Efternamn" msgid "Efternamn"
msgstr "Last name" msgstr "Last name"
#: claims/forms.py:115 #: claims/forms.py:115 claims/templates/claims/user_management.html:234
msgid "Lösenord" msgid "Lösenord"
msgstr "Password" msgstr "Password"
@@ -110,31 +110,64 @@ msgstr "Allow viewing claims"
msgid "Ge behörighet att besluta utlägg" msgid "Ge behörighet att besluta utlägg"
msgstr "Allow deciding claims" msgstr "Allow deciding claims"
#: claims/forms.py:124 #: claims/forms.py:123
msgid "Ge behörighet att redigera utläggsdetaljer"
msgstr "Allow editing claim details"
#: claims/forms.py:128
msgid "Ge behörighet att markera betalningar"
msgstr "Allow marking payments"
#: claims/forms.py:134
msgid "Användarnamnet är upptaget." msgid "Användarnamnet är upptaget."
msgstr "That username is already taken." msgstr "That username is already taken."
#: claims/forms.py:130 #: claims/forms.py:140 claims/forms.py:197
msgid "Lösenorden matchar inte." msgid "Lösenorden matchar inte."
msgstr "Passwords do not match." msgstr "Passwords do not match."
#: claims/forms.py:148 claims/templates/claims/user_management.html:116 #: claims/forms.py:158 claims/templates/claims/user_management.html:124
#: claims/templates/claims/user_management.html:257
msgid "Admin/staff" msgid "Admin/staff"
msgstr "Admin/staff" msgstr "Admin/staff"
#: claims/forms.py:149 claims/templates/claims/user_management.html:120 #: claims/forms.py:159 claims/templates/claims/user_management.html:127
#: claims/templates/claims/user_management.html:261
msgid "Får se utlägg" msgid "Får se utlägg"
msgstr "May view claims" msgstr "May view claims"
#: claims/forms.py:150 claims/templates/claims/user_management.html:124 #: claims/forms.py:160 claims/templates/claims/user_management.html:130
#: claims/templates/claims/user_management.html:265
msgid "Får besluta utlägg" msgid "Får besluta utlägg"
msgstr "May decide claims" msgstr "May decide claims"
#: claims/models.py:30 claims/templates/claims/dashboard.html:336 #: claims/forms.py:161 claims/templates/claims/user_management.html:133
#: claims/templates/claims/user_management.html:269
msgid "Får redigera utlägg"
msgstr "May edit claims"
#: claims/forms.py:162 claims/templates/claims/user_management.html:136
#: claims/templates/claims/user_management.html:273
msgid "Får markera betalningar"
msgstr "May mark payments"
#: claims/forms.py:182
#, fuzzy
#| msgid "Lösenord"
msgid "Nytt lösenord"
msgstr "Password"
#: claims/forms.py:187
#, fuzzy
#| msgid "Bekräfta lösenord"
msgid "Bekräfta nytt lösenord"
msgstr "Confirm password"
#: claims/models.py:30 claims/templates/claims/dashboard.html:342
msgid "Approved" msgid "Approved"
msgstr "Approved" msgstr "Approved"
#: claims/models.py:31 claims/templates/claims/dashboard.html:340 #: claims/models.py:31 claims/templates/claims/dashboard.html:346
msgid "Rejected" msgid "Rejected"
msgstr "Rejected" msgstr "Rejected"
@@ -158,23 +191,31 @@ msgstr "British pound (GBP)"
msgid "Describe what the reimbursement is for" msgid "Describe what the reimbursement is for"
msgstr "Describe what the reimbursement is for" msgstr "Describe what the reimbursement is for"
#: claims/models.py:122 #: claims/models.py:85
msgid "Can mark claims as paid"
msgstr ""
#: claims/models.py:86
msgid "Can edit claim details"
msgstr ""
#: claims/models.py:126
msgid "Submitted" msgid "Submitted"
msgstr "Submitted" msgstr "Submitted"
#: claims/models.py:123 #: claims/models.py:127
msgid "Status changed" msgid "Status changed"
msgstr "Status changed" msgstr "Status changed"
#: claims/models.py:124 #: claims/models.py:128
msgid "Marked as paid" msgid "Marked as paid"
msgstr "Marked as paid" msgstr "Marked as paid"
#: claims/models.py:125 #: claims/models.py:129
msgid "Project changed" msgid "Project changed"
msgstr "Project changed" msgstr "Project changed"
#: claims/models.py:126 #: claims/models.py:130
msgid "Details edited" msgid "Details edited"
msgstr "" msgstr ""
@@ -395,7 +436,7 @@ msgstr ""
"Use the reference and amount when entering the payment it helps avoid " "Use the reference and amount when entering the payment it helps avoid "
"duplicates." "duplicates."
#: claims/templates/claims/dashboard.html:189 #: claims/templates/claims/dashboard.html:190
msgid "" msgid ""
"Är du säker på att du har lagt upp betalningen? Markera endast som betald om " "Är du säker på att du har lagt upp betalningen? Markera endast som betald om "
"beloppet skickas till banken." "beloppet skickas till banken."
@@ -403,15 +444,20 @@ msgstr ""
"Are you sure the payment has been scheduled? Only mark as paid if the amount " "Are you sure the payment has been scheduled? Only mark as paid if the amount "
"has been sent to the bank." "has been sent to the bank."
#: claims/templates/claims/dashboard.html:194 #: claims/templates/claims/dashboard.html:195
msgid "Markera som betald" msgid "Markera som betald"
msgstr "Mark as paid" msgstr "Mark as paid"
#: claims/templates/claims/dashboard.html:197 #: claims/templates/claims/dashboard.html:198
msgid "Dubbelkolla belopp och kontonummer i panelen innan du bekräftar." msgid "Dubbelkolla belopp och kontonummer i panelen innan du bekräftar."
msgstr "Double-check the amount and account number before confirming." msgstr "Double-check the amount and account number before confirming."
#: claims/templates/claims/dashboard.html:202 #: claims/templates/claims/dashboard.html:201
msgid ""
"Du saknar behörighet att markera betalningar. Kontakta en administratör."
msgstr "You do not have permission to mark payments. Contact an administrator."
#: claims/templates/claims/dashboard.html:208
msgid "" msgid ""
"Intern betalningshantering är av markera betalning i ekonomisystemet och " "Intern betalningshantering är av markera betalning i ekonomisystemet och "
"resetta status vid behov." "resetta status vid behov."
@@ -419,103 +465,105 @@ msgstr ""
"Internal payment handling is off register the payment in the finance " "Internal payment handling is off register the payment in the finance "
"system and reset the status if needed." "system and reset the status if needed."
#: claims/templates/claims/dashboard.html:219 #: claims/templates/claims/dashboard.html:225
msgid "Visa kvitto" msgid "Visa kvitto"
msgstr "View receipt" msgstr "View receipt"
#: claims/templates/claims/dashboard.html:222 #: claims/templates/claims/dashboard.html:228
msgid "Inget kvitto bifogat" msgid "Inget kvitto bifogat"
msgstr "No receipt attached" msgstr "No receipt attached"
#: claims/templates/claims/dashboard.html:224 #: claims/templates/claims/dashboard.html:230
msgid "Senast uppdaterad" msgid "Senast uppdaterad"
msgstr "Last updated" msgstr "Last updated"
#: claims/templates/claims/dashboard.html:229 #: claims/templates/claims/dashboard.html:235
msgid "Logg" msgid "Logg"
msgstr "Log" msgstr "Log"
#: claims/templates/claims/dashboard.html:236 #: claims/templates/claims/dashboard.html:242
#: claims/templates/claims/my_claims.html:62 #: claims/templates/claims/my_claims.html:62
msgid "Status" msgid "Status"
msgstr "Status" msgstr "Status"
#: claims/templates/claims/dashboard.html:242 #: claims/templates/claims/dashboard.html:248
msgid "Av" msgid "Av"
msgstr "By" msgstr "By"
#: claims/templates/claims/dashboard.html:246 #: claims/templates/claims/dashboard.html:252
#: claims/templates/claims/my_claims.html:69 #: claims/templates/claims/my_claims.html:69
msgid "Ingen logg än." msgid "Ingen logg än."
msgstr "No log entries yet." msgstr "No log entries yet."
#: claims/templates/claims/dashboard.html:254 #: claims/templates/claims/dashboard.html:260
msgid "" msgid ""
"Utlägget är markerat som betalt. Ändringar av beslut/kommentar är låsta." "Utlägget är markerat som betalt. Ändringar av beslut/kommentar är låsta."
msgstr "The claim is marked as paid. Decision/comments are locked." msgstr "The claim is marked as paid. Decision/comments are locked."
#: claims/templates/claims/dashboard.html:261 #: claims/templates/claims/dashboard.html:267
msgid "Åtgärd" msgid "Åtgärd"
msgstr "Action" msgstr "Action"
#: claims/templates/claims/dashboard.html:273 #: claims/templates/claims/dashboard.html:279
msgid "Uppdatera beslut" msgid "Uppdatera beslut"
msgstr "Update decision" msgstr "Update decision"
#: claims/templates/claims/dashboard.html:284 #: claims/templates/claims/dashboard.html:290
#: claims/templates/claims/my_claims.html:78 #: claims/templates/claims/my_claims.html:78
msgid "Inga utlägg ännu" msgid "Inga utlägg ännu"
msgstr "No claims yet" msgstr "No claims yet"
#: claims/templates/claims/dashboard.html:285 #: claims/templates/claims/dashboard.html:291
msgid "När formuläret tas emot visas posterna automatiskt här." msgid "När formuläret tas emot visas posterna automatiskt här."
msgstr "As soon as submissions arrive they will appear here." msgstr "As soon as submissions arrive they will appear here."
#: claims/templates/claims/dashboard.html:291 #: claims/templates/claims/dashboard.html:297
msgid "Inga utlägg matchar filtret" msgid "Inga utlägg matchar filtret"
msgstr "No claims match the filter" msgstr "No claims match the filter"
#: claims/templates/claims/dashboard.html:292 #: claims/templates/claims/dashboard.html:298
msgid "Välj en annan status för att se fler poster." msgid "Välj en annan status för att se fler poster."
msgstr "Choose another status to see more entries." msgstr "Choose another status to see more entries."
#: claims/templates/claims/dashboard.html:300 #: claims/templates/claims/dashboard.html:306
msgid "Senaste inskick" msgid "Senaste inskick"
msgstr "Latest submissions" msgstr "Latest submissions"
#: claims/templates/claims/dashboard.html:301 #: claims/templates/claims/dashboard.html:307
msgid "Aktivitet" msgid "Aktivitet"
msgstr "Activity" msgstr "Activity"
#: claims/templates/claims/dashboard.html:319 #: claims/templates/claims/dashboard.html:325
msgid "Inga aktiviteter än." msgid "Inga aktiviteter än."
msgstr "No activity yet." msgstr "No activity yet."
#: claims/templates/claims/dashboard.html:327 #: claims/templates/claims/dashboard.html:333
msgid "Statusfördelning" msgid "Statusfördelning"
msgstr "Status breakdown" msgstr "Status breakdown"
#: claims/templates/claims/dashboard.html:328 #: claims/templates/claims/dashboard.html:334
msgid "Snabbstatistik" msgid "Snabbstatistik"
msgstr "Quick stats" msgstr "Quick stats"
#: claims/templates/claims/dashboard.html:501 #: claims/templates/claims/dashboard.html:507
msgid "Redigera utlägg" msgid "Redigera utlägg"
msgstr "Edit claim" msgstr "Edit claim"
#: claims/templates/claims/dashboard.html:507 #: claims/templates/claims/dashboard.html:513
#: claims/templates/claims/user_management.html:199
msgid "Stäng" msgid "Stäng"
msgstr "Close" msgstr "Close"
#: claims/templates/claims/dashboard.html:542 #: claims/templates/claims/dashboard.html:548
msgid "Ingen" msgid "Ingen"
msgstr "None" msgstr "None"
#: claims/templates/claims/dashboard.html:557 #: claims/templates/claims/dashboard.html:563
#: claims/templates/claims/user_management.html:281
msgid "Avbryt" msgid "Avbryt"
msgstr "Cancel" msgstr "Cancel"
#: claims/templates/claims/dashboard.html:560 #: claims/templates/claims/dashboard.html:566
msgid "Spara ändringar" msgid "Spara ändringar"
msgstr "Save changes" msgstr "Save changes"
@@ -760,17 +808,26 @@ msgstr "Use Django admin groups when multiple people share a role."
#: claims/templates/claims/user_management.html:68 #: claims/templates/claims/user_management.html:68
msgid "" msgid ""
"Behörigheterna <code class=\"break-normal rounded bg-slate-800 px-2 py-1 " "Behörigheterna <code class=\"break-normal rounded bg-slate-800 px-2 py-1 "
"text-xs\">claims.view_claim</code>\n" "text-xs\">claims.view_claim</code>,\n"
" <code class=\"break-normal rounded bg-slate-800 px-2 "
"py-1 text-xs\">claims.change_claim</code>,\n"
" <code class=\"break-normal rounded bg-slate-800 px-2 "
"py-1 text-xs\">claims.edit_claim_details</code>\n"
" och <code class=\"break-normal rounded bg-slate-800 " " och <code class=\"break-normal rounded bg-slate-800 "
"px-2 py-1 text-xs\">claims.change_claim</code>\n" "px-2 py-1 text-xs\">claims.mark_claim_paid</code>\n"
" styr åtkomst till adminvyn respektive beslutsflödet." " styr åtkomst till adminvyn, beslutsflödet, "
"redigering samt betalningspanelen."
msgstr "" msgstr ""
"The permissions <code class=\"break-normal rounded bg-slate-800 px-2 py-1 " "The permissions <code class=\"break-normal rounded bg-slate-800 px-2 py-1 "
"text-xs\">claims.view_claim</code> and <code class=\"break-normal rounded bg-" "text-xs\">claims.view_claim</code>, <code class=\"break-normal rounded bg-"
"slate-800 px-2 py-1 text-xs\">claims.change_claim</code> control access to " "slate-800 px-2 py-1 text-xs\">claims.change_claim</code>, <code "
"the list and decision flows." "class=\"break-normal rounded bg-slate-800 px-2 py-1 text-"
"xs\">claims.edit_claim_details</code>, and <code class=\"break-normal "
"rounded bg-slate-800 px-2 py-1 text-xs\">claims.mark_claim_paid</code> "
"control access to the dashboard, decision flow, edit dialog, and payment "
"panel."
#: claims/templates/claims/user_management.html:75 #: claims/templates/claims/user_management.html:77
msgid "" msgid ""
"En markerad Admin/staff-användare kan nå Django admin och skapa projekt, " "En markerad Admin/staff-användare kan nå Django admin och skapa projekt, "
"exportflöden m.m." "exportflöden m.m."
@@ -778,7 +835,7 @@ msgstr ""
"Users flagged as Admin/staff may access Django admin to create projects, " "Users flagged as Admin/staff may access Django admin to create projects, "
"exports, etc." "exports, etc."
#: claims/templates/claims/user_management.html:79 #: claims/templates/claims/user_management.html:81
msgid "" msgid ""
"Ta bara bort konton du är säker på historik försvinner inte, men personen " "Ta bara bort konton du är säker på historik försvinner inte, men personen "
"tappar all åtkomst." "tappar all åtkomst."
@@ -786,60 +843,85 @@ msgstr ""
"Only delete accounts you are sure about history stays, but the person " "Only delete accounts you are sure about history stays, but the person "
"loses access." "loses access."
#: claims/templates/claims/user_management.html:87 #: claims/templates/claims/user_management.html:89
msgid "Befintliga användare" msgid "Befintliga användare"
msgstr "Existing users" msgstr "Existing users"
#: claims/templates/claims/user_management.html:88 #: claims/templates/claims/user_management.html:90
msgid "Justera behörigheter" msgid "Justera behörigheter"
msgstr "Adjust permissions" msgstr "Adjust permissions"
#: claims/templates/claims/user_management.html:99 #: claims/templates/claims/user_management.html:101
msgid "Superuser" msgid "Superuser"
msgstr "Superuser" msgstr "Superuser"
#: claims/templates/claims/user_management.html:103 #: claims/templates/claims/user_management.html:105
msgid "Saknar namn" msgid "Saknar namn"
msgstr "No name" msgstr "No name"
#: claims/templates/claims/user_management.html:103 #: claims/templates/claims/user_management.html:105
msgid "Ingen e-post" msgid "Ingen e-post"
msgstr "No email" msgstr "No email"
#: claims/templates/claims/user_management.html:128 #: claims/templates/claims/user_management.html:114
msgid "Spara behörigheter" msgid "Redigera användare"
msgstr "Save permissions" msgstr "Edit user"
#: claims/templates/claims/user_management.html:132 #: claims/templates/claims/user_management.html:121
#: claims/templates/claims/user_management.html:254
msgid "Behörigheter"
msgstr "Permissions"
#: claims/templates/claims/user_management.html:139
msgid "Inga behörigheter tilldelade"
msgstr "No permissions assigned"
#: claims/templates/claims/user_management.html:146
#: claims/templates/claims/user_management.html:193
msgid "Redigera behörigheter"
msgstr "Edit permissions"
#: claims/templates/claims/user_management.html:151
msgid "Ta bort konto" msgid "Ta bort konto"
msgstr "Remove account" msgstr "Remove account"
#: claims/templates/claims/user_management.html:134 #: claims/templates/claims/user_management.html:153
msgid "Åtgärden går inte att ångra. Användaren förlorar omedelbart åtkomst." msgid "Åtgärden går inte att ångra. Användaren förlorar omedelbart åtkomst."
msgstr "This action cannot be undone. The user loses access immediately." msgstr "This action cannot be undone. The user loses access immediately."
#: claims/templates/claims/user_management.html:135 #: claims/templates/claims/user_management.html:154
#, fuzzy, python-format #, python-format
#| msgid "Ta bort {{ user.username }}?"
msgid "Ta bort %(user.username)s?" msgid "Ta bort %(user.username)s?"
msgstr "Remove {{ user.username }}?" msgstr "Delete %(user.username)s?"
#: claims/templates/claims/user_management.html:140 #: claims/templates/claims/user_management.html:159
msgid "Ta bort användare" msgid "Ta bort användare"
msgstr "Delete user" msgstr "Delete user"
#: claims/templates/claims/user_management.html:144 #: claims/templates/claims/user_management.html:163
msgid "Kan inte tas bort (antingen du själv eller superuser)." msgid "Kan inte tas bort (antingen du själv eller superuser)."
msgstr "Cannot be removed (either yourself or a superuser)." msgstr "Cannot be removed (either yourself or a superuser)."
#: claims/templates/claims/user_management.html:152 #: claims/templates/claims/user_management.html:171
msgid "Inga användare upplagda." msgid "Inga användare upplagda."
msgstr "No users yet." msgstr "No users yet."
#: claims/templates/claims/user_management.html:153 #: claims/templates/claims/user_management.html:172
msgid "Skapa det första kontot via formuläret ovan." msgid "Skapa det första kontot via formuläret ovan."
msgstr "Create the first account using the form above." msgstr "Create the first account using the form above."
#: claims/templates/claims/user_management.html:208
msgid "Kontaktuppgifter"
msgstr "Contact details"
#: claims/templates/claims/user_management.html:251
msgid "Lämna fälten tomma för att behålla nuvarande lösenord."
msgstr "Leave the fields blank to keep the current password."
#: claims/templates/claims/user_management.html:284
msgid "Spara behörigheter"
msgstr "Save permissions"
#: claims/validators.py:87 #: claims/validators.py:87
#, python-format #, python-format
msgid "Kvitton får vara max %(size)s MB." msgid "Kvitton får vara max %(size)s MB."
@@ -859,117 +941,123 @@ msgstr ""
msgid "Filens innehåll matchar inte förväntat format." msgid "Filens innehåll matchar inte förväntat format."
msgstr "" msgstr ""
#: claims/views.py:127 #: claims/views.py:128
#, python-brace-format #, python-brace-format
msgid "{} utlägg skickade in." msgid "{} utlägg skickade in."
msgstr "" msgstr ""
#: claims/views.py:130 #: claims/views.py:131
msgid "Inga utlägg kunde sparas. Fyll i minst en rad." msgid "Inga utlägg kunde sparas. Fyll i minst en rad."
msgstr "" msgstr ""
#: claims/views.py:132 #: claims/views.py:133
msgid "Kunde inte spara utläggen. Kontrollera formuläret." msgid "Kunde inte spara utläggen. Kontrollera formuläret."
msgstr "" msgstr ""
#: claims/views.py:186 claims/views.py:238 claims/views.py:262 #: claims/views.py:189
#, fuzzy
#| msgid "Ge behörighet att besluta utlägg"
msgid "Du har inte behörighet att uppdatera utlägg." msgid "Du har inte behörighet att uppdatera utlägg."
msgstr "Allow deciding claims" msgstr "You do not have permission to update claims."
#: claims/views.py:200 #: claims/views.py:203
msgid "Utlägget är redan markerat som betalt och kan inte ändras." msgid "Utlägget är redan markerat som betalt och kan inte ändras."
msgstr "This claim is already marked as paid and cannot be changed." msgstr "This claim is already marked as paid and cannot be changed."
#: claims/views.py:207 #: claims/views.py:210
#, python-format #, python-format
msgid "%(claim)s markerades som godkänd." msgid "%(claim)s markerades som godkänd."
msgstr "%(claim)s was marked as approved." msgstr "%(claim)s was marked as approved."
#: claims/views.py:211 #: claims/views.py:214
#, python-format #, python-format
msgid "%(claim)s markerades som nekad." msgid "%(claim)s markerades som nekad."
msgstr "%(claim)s was marked as rejected." msgstr "%(claim)s was marked as rejected."
#: claims/views.py:215 #: claims/views.py:218
#, python-format #, python-format
msgid "%(claim)s återställdes till väntande status." msgid "%(claim)s återställdes till väntande status."
msgstr "%(claim)s was reset to pending status." msgstr "%(claim)s was reset to pending status."
#: claims/views.py:235 #: claims/views.py:238
msgid "Betalningshantering är inte aktiverad." msgid "Betalningshantering är inte aktiverad."
msgstr "Payment handling is not enabled." msgstr "Payment handling is not enabled."
#: claims/views.py:243 #: claims/views.py:241
msgid "Du har inte behörighet att markera betalningar i systemet."
msgstr "You do not have permission to mark payments in the system."
#: claims/views.py:246
msgid "Endast godkända utlägg kan markeras som betalda." msgid "Endast godkända utlägg kan markeras som betalda."
msgstr "Only approved claims can be marked as paid." msgstr "Only approved claims can be marked as paid."
#: claims/views.py:246 #: claims/views.py:249
msgid "Detta utlägg är redan markerat som betalt." msgid "Detta utlägg är redan markerat som betalt."
msgstr "This claim is already marked as paid." msgstr "This claim is already marked as paid."
#: claims/views.py:257 #: claims/views.py:260
#, python-format #, python-format
msgid "%(claim)s markerades som betald." msgid "%(claim)s markerades som betald."
msgstr "%(claim)s was marked as paid." msgstr "%(claim)s was marked as paid."
#: claims/views.py:266 #: claims/views.py:265
msgid "Du har inte behörighet att redigera utlägg."
msgstr "You do not have permission to edit claims."
#: claims/views.py:269
msgid "Endast väntande utlägg kan redigeras via panelen." msgid "Endast väntande utlägg kan redigeras via panelen."
msgstr "Only pending claims can be edited via the panel." msgstr "Only pending claims can be edited via the panel."
#: claims/views.py:295 #: claims/views.py:298
#, python-format #, python-format
msgid "Följande fält uppdaterades: %(fields)s" msgid "Följande fält uppdaterades: %(fields)s"
msgstr "The following fields were updated: %(fields)s" msgstr "The following fields were updated: %(fields)s"
#: claims/views.py:301 #: claims/views.py:304
msgid "Informationen uppdaterades." msgid "Informationen uppdaterades."
msgstr "Information updated." msgstr "Information updated."
#: claims/views.py:303 #: claims/views.py:306
msgid "Inga förändringar att spara." msgid "Inga förändringar att spara."
msgstr "No changes to save." msgstr "No changes to save."
#: claims/views.py:369 #: claims/views.py:372
msgid "Du saknar behörighet för åtgärden." msgid "Du saknar behörighet för åtgärden."
msgstr "You do not have permission to perform this action." msgstr "You do not have permission to perform this action."
#: claims/views.py:416 #: claims/views.py:435
#, python-format #, python-format
msgid "Användaren %(user)s skapades." msgid "Användaren %(user)s skapades."
msgstr "" msgstr ""
#: claims/views.py:427 #: claims/views.py:447
msgid "Du kan inte ta bort din egen staff-status." msgid "Du kan inte ta bort din egen staff-status."
msgstr "" msgstr ""
#: claims/views.py:433 #: claims/views.py:478
#, python-format #, python-format
msgid "Behörigheter uppdaterades för %(user)s." msgid "Behörigheter uppdaterades för %(user)s."
msgstr "" msgstr ""
#: claims/views.py:435 #: claims/views.py:480
#, fuzzy #, fuzzy
#| msgid "Justera behörigheter" #| msgid "Justera behörigheter"
msgid "Kunde inte uppdatera behörigheter." msgid "Kunde inte uppdatera behörigheter."
msgstr "Adjust permissions" msgstr "Adjust permissions"
#: claims/views.py:445 #: claims/views.py:490
msgid "Du kan inte ta bort ditt eget konto." msgid "Du kan inte ta bort ditt eget konto."
msgstr "" msgstr ""
#: claims/views.py:447 #: claims/views.py:492
msgid "Du kan inte ta bort en superuser via detta gränssnitt." msgid "Du kan inte ta bort en superuser via detta gränssnitt."
msgstr "" msgstr ""
#: claims/views.py:450 #: claims/views.py:495
#, fuzzy #, fuzzy
#| msgid "Användare" #| msgid "Användare"
msgid "Användaren togs bort." msgid "Användaren togs bort."
msgstr "Users" msgstr "Users"
#: claims/views.py:453 #: claims/views.py:498
msgid "Okänd åtgärd." msgid "Okänd åtgärd."
msgstr "" msgstr ""

Binary file not shown.

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-11 19:06+0000\n" "POT-Creation-Date: 2025-11-11 20:06+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,43 +17,44 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: claims/forms.py:19 claims/forms.py:100 #: claims/forms.py:19 claims/forms.py:100
#: claims/templates/claims/dashboard.html:516 #: claims/templates/claims/dashboard.html:522
msgid "Namn" msgid "Namn"
msgstr "" msgstr ""
#: claims/forms.py:23 claims/forms.py:101 claims/forms.py:112 #: claims/forms.py:23 claims/forms.py:101 claims/forms.py:112
#: claims/templates/claims/dashboard.html:176 #: claims/forms.py:177 claims/templates/claims/dashboard.html:176
#: claims/templates/claims/dashboard.html:520 #: claims/templates/claims/dashboard.html:526
msgid "E-post" msgid "E-post"
msgstr "" msgstr ""
#: claims/forms.py:28 claims/forms.py:102 #: claims/forms.py:28 claims/forms.py:102
#: claims/templates/claims/dashboard.html:164 #: claims/templates/claims/dashboard.html:164
#: claims/templates/claims/dashboard.html:524 #: claims/templates/claims/dashboard.html:530
msgid "Kontonummer" msgid "Kontonummer"
msgstr "" msgstr ""
#: claims/forms.py:49 claims/forms.py:106 #: claims/forms.py:49 claims/forms.py:106
#: claims/templates/claims/dashboard.html:211 #: claims/templates/claims/dashboard.html:217
#: claims/templates/claims/dashboard.html:550 #: claims/templates/claims/dashboard.html:556
msgid "Beskrivning" msgid "Beskrivning"
msgstr "" msgstr ""
#: claims/forms.py:50 claims/forms.py:103 #: claims/forms.py:50 claims/forms.py:103
#: claims/templates/claims/dashboard.html:160 #: claims/templates/claims/dashboard.html:160
#: claims/templates/claims/dashboard.html:528 #: claims/templates/claims/dashboard.html:534
#: claims/templates/claims/my_claims.html:23 #: claims/templates/claims/my_claims.html:23
msgid "Belopp" msgid "Belopp"
msgstr "" msgstr ""
#: claims/forms.py:51 claims/forms.py:104 #: claims/forms.py:51 claims/forms.py:104
#: claims/templates/claims/dashboard.html:532 #: claims/templates/claims/dashboard.html:538
msgid "Valuta" msgid "Valuta"
msgstr "" msgstr ""
#: claims/forms.py:52 claims/forms.py:105 #: claims/forms.py:52 claims/forms.py:105
#: claims/templates/claims/dashboard.html:540 #: claims/templates/claims/dashboard.html:546
msgid "Evenemang/Projekt" msgid "Evenemang/Projekt"
msgstr "" msgstr ""
@@ -70,12 +71,12 @@ msgid "Neka"
msgstr "Neka" msgstr "Neka"
#: claims/forms.py:67 claims/models.py:29 #: claims/forms.py:67 claims/models.py:29
#: claims/templates/claims/dashboard.html:332 #: claims/templates/claims/dashboard.html:338
msgid "Pending" msgid "Pending"
msgstr "Väntande" msgstr "Väntande"
#: claims/forms.py:75 claims/templates/claims/dashboard.html:126 #: claims/forms.py:75 claims/templates/claims/dashboard.html:126
#: claims/templates/claims/dashboard.html:268 #: claims/templates/claims/dashboard.html:274
msgid "Kommentar" msgid "Kommentar"
msgstr "" msgstr ""
@@ -87,15 +88,15 @@ msgstr ""
msgid "Användarnamn" msgid "Användarnamn"
msgstr "" msgstr ""
#: claims/forms.py:113 #: claims/forms.py:113 claims/forms.py:166
msgid "Förnamn" msgid "Förnamn"
msgstr "" msgstr ""
#: claims/forms.py:114 #: claims/forms.py:114 claims/forms.py:172
msgid "Efternamn" msgid "Efternamn"
msgstr "" msgstr ""
#: claims/forms.py:115 #: claims/forms.py:115 claims/templates/claims/user_management.html:234
msgid "Lösenord" msgid "Lösenord"
msgstr "" msgstr ""
@@ -115,31 +116,60 @@ msgstr ""
msgid "Ge behörighet att besluta utlägg" msgid "Ge behörighet att besluta utlägg"
msgstr "" msgstr ""
#: claims/forms.py:124 #: claims/forms.py:123
msgid "Ge behörighet att redigera utläggsdetaljer"
msgstr ""
#: claims/forms.py:128
msgid "Ge behörighet att markera betalningar"
msgstr ""
#: claims/forms.py:134
msgid "Användarnamnet är upptaget." msgid "Användarnamnet är upptaget."
msgstr "" msgstr ""
#: claims/forms.py:130 #: claims/forms.py:140 claims/forms.py:197
msgid "Lösenorden matchar inte." msgid "Lösenorden matchar inte."
msgstr "" msgstr ""
#: claims/forms.py:148 claims/templates/claims/user_management.html:116 #: claims/forms.py:158 claims/templates/claims/user_management.html:124
#: claims/templates/claims/user_management.html:257
msgid "Admin/staff" msgid "Admin/staff"
msgstr "" msgstr ""
#: claims/forms.py:149 claims/templates/claims/user_management.html:120 #: claims/forms.py:159 claims/templates/claims/user_management.html:127
#: claims/templates/claims/user_management.html:261
msgid "Får se utlägg" msgid "Får se utlägg"
msgstr "" msgstr ""
#: claims/forms.py:150 claims/templates/claims/user_management.html:124 #: claims/forms.py:160 claims/templates/claims/user_management.html:130
#: claims/templates/claims/user_management.html:265
msgid "Får besluta utlägg" msgid "Får besluta utlägg"
msgstr "" msgstr ""
#: claims/models.py:30 claims/templates/claims/dashboard.html:336 #: claims/forms.py:161 claims/templates/claims/user_management.html:133
#: claims/templates/claims/user_management.html:269
msgid "Får redigera utlägg"
msgstr ""
#: claims/forms.py:162 claims/templates/claims/user_management.html:136
#: claims/templates/claims/user_management.html:273
msgid "Får markera betalningar"
msgstr ""
#: claims/forms.py:182
msgid "Nytt lösenord"
msgstr ""
#: claims/forms.py:187
msgid "Bekräfta nytt lösenord"
msgstr ""
#: claims/models.py:30 claims/templates/claims/dashboard.html:342
msgid "Approved" msgid "Approved"
msgstr "Godkänd" msgstr "Godkänd"
#: claims/models.py:31 claims/templates/claims/dashboard.html:340 #: claims/models.py:31 claims/templates/claims/dashboard.html:346
msgid "Rejected" msgid "Rejected"
msgstr "Nekad" msgstr "Nekad"
@@ -163,23 +193,31 @@ msgstr ""
msgid "Describe what the reimbursement is for" msgid "Describe what the reimbursement is for"
msgstr "" msgstr ""
#: claims/models.py:122 #: claims/models.py:85
msgid "Submitted" msgid "Can mark claims as paid"
msgstr "" msgstr ""
#: claims/models.py:123 #: claims/models.py:86
msgid "Status changed" msgid "Can edit claim details"
msgstr ""
#: claims/models.py:124
msgid "Marked as paid"
msgstr ""
#: claims/models.py:125
msgid "Project changed"
msgstr "" msgstr ""
#: claims/models.py:126 #: claims/models.py:126
msgid "Submitted"
msgstr ""
#: claims/models.py:127
msgid "Status changed"
msgstr ""
#: claims/models.py:128
msgid "Marked as paid"
msgstr ""
#: claims/models.py:129
msgid "Project changed"
msgstr ""
#: claims/models.py:130
msgid "Details edited" msgid "Details edited"
msgstr "" msgstr ""
@@ -396,123 +434,130 @@ msgid ""
"undvika dubbletter." "undvika dubbletter."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:189 #: claims/templates/claims/dashboard.html:190
msgid "" msgid ""
"Är du säker på att du har lagt upp betalningen? Markera endast som betald om " "Är du säker på att du har lagt upp betalningen? Markera endast som betald om "
"beloppet skickas till banken." "beloppet skickas till banken."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:194 #: claims/templates/claims/dashboard.html:195
msgid "Markera som betald" msgid "Markera som betald"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:197 #: claims/templates/claims/dashboard.html:198
msgid "Dubbelkolla belopp och kontonummer i panelen innan du bekräftar." msgid "Dubbelkolla belopp och kontonummer i panelen innan du bekräftar."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:202 #: claims/templates/claims/dashboard.html:201
msgid ""
"Du saknar behörighet att markera betalningar. Kontakta en administratör."
msgstr ""
#: claims/templates/claims/dashboard.html:208
msgid "" msgid ""
"Intern betalningshantering är av markera betalning i ekonomisystemet och " "Intern betalningshantering är av markera betalning i ekonomisystemet och "
"resetta status vid behov." "resetta status vid behov."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:219 #: claims/templates/claims/dashboard.html:225
msgid "Visa kvitto" msgid "Visa kvitto"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:222 #: claims/templates/claims/dashboard.html:228
msgid "Inget kvitto bifogat" msgid "Inget kvitto bifogat"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:224 #: claims/templates/claims/dashboard.html:230
msgid "Senast uppdaterad" msgid "Senast uppdaterad"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:229 #: claims/templates/claims/dashboard.html:235
msgid "Logg" msgid "Logg"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:236 #: claims/templates/claims/dashboard.html:242
#: claims/templates/claims/my_claims.html:62 #: claims/templates/claims/my_claims.html:62
msgid "Status" msgid "Status"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:242 #: claims/templates/claims/dashboard.html:248
msgid "Av" msgid "Av"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:246 #: claims/templates/claims/dashboard.html:252
#: claims/templates/claims/my_claims.html:69 #: claims/templates/claims/my_claims.html:69
msgid "Ingen logg än." msgid "Ingen logg än."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:254 #: claims/templates/claims/dashboard.html:260
msgid "" msgid ""
"Utlägget är markerat som betalt. Ändringar av beslut/kommentar är låsta." "Utlägget är markerat som betalt. Ändringar av beslut/kommentar är låsta."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:261 #: claims/templates/claims/dashboard.html:267
msgid "Åtgärd" msgid "Åtgärd"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:273 #: claims/templates/claims/dashboard.html:279
msgid "Uppdatera beslut" msgid "Uppdatera beslut"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:284 #: claims/templates/claims/dashboard.html:290
#: claims/templates/claims/my_claims.html:78 #: claims/templates/claims/my_claims.html:78
msgid "Inga utlägg ännu" msgid "Inga utlägg ännu"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:285 #: claims/templates/claims/dashboard.html:291
msgid "När formuläret tas emot visas posterna automatiskt här." msgid "När formuläret tas emot visas posterna automatiskt här."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:291 #: claims/templates/claims/dashboard.html:297
msgid "Inga utlägg matchar filtret" msgid "Inga utlägg matchar filtret"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:292 #: claims/templates/claims/dashboard.html:298
msgid "Välj en annan status för att se fler poster." msgid "Välj en annan status för att se fler poster."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:300 #: claims/templates/claims/dashboard.html:306
msgid "Senaste inskick" msgid "Senaste inskick"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:301 #: claims/templates/claims/dashboard.html:307
msgid "Aktivitet" msgid "Aktivitet"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:319 #: claims/templates/claims/dashboard.html:325
msgid "Inga aktiviteter än." msgid "Inga aktiviteter än."
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:327 #: claims/templates/claims/dashboard.html:333
msgid "Statusfördelning" msgid "Statusfördelning"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:328 #: claims/templates/claims/dashboard.html:334
msgid "Snabbstatistik" msgid "Snabbstatistik"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:501 #: claims/templates/claims/dashboard.html:507
msgid "Redigera utlägg" msgid "Redigera utlägg"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:507 #: claims/templates/claims/dashboard.html:513
#: claims/templates/claims/user_management.html:199
msgid "Stäng" msgid "Stäng"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:542 #: claims/templates/claims/dashboard.html:548
msgid "Ingen" msgid "Ingen"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:557 #: claims/templates/claims/dashboard.html:563
#: claims/templates/claims/user_management.html:281
msgid "Avbryt" msgid "Avbryt"
msgstr "" msgstr ""
#: claims/templates/claims/dashboard.html:560 #: claims/templates/claims/dashboard.html:566
msgid "Spara ändringar" msgid "Spara ändringar"
msgstr "" msgstr ""
@@ -743,76 +788,107 @@ msgstr ""
#: claims/templates/claims/user_management.html:68 #: claims/templates/claims/user_management.html:68
msgid "" msgid ""
"Behörigheterna <code class=\"break-normal rounded bg-slate-800 px-2 py-1 " "Behörigheterna <code class=\"break-normal rounded bg-slate-800 px-2 py-1 "
"text-xs\">claims.view_claim</code>\n" "text-xs\">claims.view_claim</code>,\n"
" <code class=\"break-normal rounded bg-slate-800 px-2 "
"py-1 text-xs\">claims.change_claim</code>,\n"
" <code class=\"break-normal rounded bg-slate-800 px-2 "
"py-1 text-xs\">claims.edit_claim_details</code>\n"
" och <code class=\"break-normal rounded bg-slate-800 " " och <code class=\"break-normal rounded bg-slate-800 "
"px-2 py-1 text-xs\">claims.change_claim</code>\n" "px-2 py-1 text-xs\">claims.mark_claim_paid</code>\n"
" styr åtkomst till adminvyn respektive beslutsflödet." " styr åtkomst till adminvyn, beslutsflödet, "
"redigering samt betalningspanelen."
msgstr "" msgstr ""
#: claims/templates/claims/user_management.html:75 #: claims/templates/claims/user_management.html:77
msgid "" msgid ""
"En markerad Admin/staff-användare kan nå Django admin och skapa projekt, " "En markerad Admin/staff-användare kan nå Django admin och skapa projekt, "
"exportflöden m.m." "exportflöden m.m."
msgstr "" msgstr ""
#: claims/templates/claims/user_management.html:79 #: claims/templates/claims/user_management.html:81
msgid "" msgid ""
"Ta bara bort konton du är säker på historik försvinner inte, men personen " "Ta bara bort konton du är säker på historik försvinner inte, men personen "
"tappar all åtkomst." "tappar all åtkomst."
msgstr "" msgstr ""
#: claims/templates/claims/user_management.html:87 #: claims/templates/claims/user_management.html:89
msgid "Befintliga användare" msgid "Befintliga användare"
msgstr "" msgstr ""
#: claims/templates/claims/user_management.html:88 #: claims/templates/claims/user_management.html:90
msgid "Justera behörigheter" msgid "Justera behörigheter"
msgstr "" msgstr ""
#: claims/templates/claims/user_management.html:99 #: claims/templates/claims/user_management.html:101
msgid "Superuser" msgid "Superuser"
msgstr "" msgstr ""
#: claims/templates/claims/user_management.html:103 #: claims/templates/claims/user_management.html:105
msgid "Saknar namn" msgid "Saknar namn"
msgstr "" msgstr ""
#: claims/templates/claims/user_management.html:103 #: claims/templates/claims/user_management.html:105
msgid "Ingen e-post" msgid "Ingen e-post"
msgstr "" msgstr "Ingen e-post"
#: claims/templates/claims/user_management.html:128 #: claims/templates/claims/user_management.html:114
msgid "Spara behörigheter" msgid "Redigera användare"
msgstr "" msgstr "Redigera användare"
#: claims/templates/claims/user_management.html:132 #: claims/templates/claims/user_management.html:121
#: claims/templates/claims/user_management.html:254
msgid "Behörigheter"
msgstr "Behörigheter"
#: claims/templates/claims/user_management.html:139
msgid "Inga behörigheter tilldelade"
msgstr "Inga behörigheter tilldelade"
#: claims/templates/claims/user_management.html:146
#: claims/templates/claims/user_management.html:193
msgid "Redigera behörigheter"
msgstr "Redigera behörigheter"
#: claims/templates/claims/user_management.html:151
msgid "Ta bort konto" msgid "Ta bort konto"
msgstr "" msgstr "Ta bort konto"
#: claims/templates/claims/user_management.html:134
msgid "Åtgärden går inte att ångra. Användaren förlorar omedelbart åtkomst."
msgstr ""
#: claims/templates/claims/user_management.html:135
#, python-format
msgid "Ta bort %(user.username)s?"
msgstr ""
#: claims/templates/claims/user_management.html:140
msgid "Ta bort användare"
msgstr ""
#: claims/templates/claims/user_management.html:144
msgid "Kan inte tas bort (antingen du själv eller superuser)."
msgstr ""
#: claims/templates/claims/user_management.html:152
msgid "Inga användare upplagda."
msgstr ""
#: claims/templates/claims/user_management.html:153 #: claims/templates/claims/user_management.html:153
msgid "Åtgärden går inte att ångra. Användaren förlorar omedelbart åtkomst."
msgstr "Åtgärden går inte att ångra. Användaren förlorar omedelbart åtkomst."
#: claims/templates/claims/user_management.html:154
#, python-format
msgid "Ta bort %(user.username)s?"
msgstr "Ta bort %(user.username)s?"
#: claims/templates/claims/user_management.html:159
msgid "Ta bort användare"
msgstr "Ta bort användare"
#: claims/templates/claims/user_management.html:163
msgid "Kan inte tas bort (antingen du själv eller superuser)."
msgstr "Kan inte tas bort (antingen du själv eller superuser)."
#: claims/templates/claims/user_management.html:171
msgid "Inga användare upplagda."
msgstr "Inga användare upplagda."
#: claims/templates/claims/user_management.html:172
msgid "Skapa det första kontot via formuläret ovan." msgid "Skapa det första kontot via formuläret ovan."
msgstr "" msgstr "Skapa det första kontot via formuläret ovan."
#: claims/templates/claims/user_management.html:208
msgid "Kontaktuppgifter"
msgstr "Kontaktuppgifter"
#: claims/templates/claims/user_management.html:251
msgid "Lämna fälten tomma för att behålla nuvarande lösenord."
msgstr "Lämna fälten tomma för att behålla nuvarande lösenord."
#: claims/templates/claims/user_management.html:284
msgid "Spara behörigheter"
msgstr "Spara behörigheter"
#: claims/validators.py:87 #: claims/validators.py:87
#, python-format #, python-format
@@ -833,111 +909,119 @@ msgstr ""
msgid "Filens innehåll matchar inte förväntat format." msgid "Filens innehåll matchar inte förväntat format."
msgstr "" msgstr ""
#: claims/views.py:127 #: claims/views.py:128
#, python-brace-format #, python-brace-format
msgid "{} utlägg skickade in." msgid "{} utlägg skickade in."
msgstr "" msgstr ""
#: claims/views.py:130 #: claims/views.py:131
msgid "Inga utlägg kunde sparas. Fyll i minst en rad." msgid "Inga utlägg kunde sparas. Fyll i minst en rad."
msgstr "" msgstr ""
#: claims/views.py:132 #: claims/views.py:133
msgid "Kunde inte spara utläggen. Kontrollera formuläret." msgid "Kunde inte spara utläggen. Kontrollera formuläret."
msgstr "" msgstr ""
#: claims/views.py:186 claims/views.py:238 claims/views.py:262 #: claims/views.py:189
msgid "Du har inte behörighet att uppdatera utlägg." msgid "Du har inte behörighet att uppdatera utlägg."
msgstr "" msgstr "Du har inte behörighet att uppdatera utlägg."
#: claims/views.py:200 #: claims/views.py:203
msgid "Utlägget är redan markerat som betalt och kan inte ändras." msgid "Utlägget är redan markerat som betalt och kan inte ändras."
msgstr "" msgstr ""
#: claims/views.py:207 #: claims/views.py:210
#, python-format #, python-format
msgid "%(claim)s markerades som godkänd." msgid "%(claim)s markerades som godkänd."
msgstr "" msgstr ""
#: claims/views.py:211 #: claims/views.py:214
#, python-format #, python-format
msgid "%(claim)s markerades som nekad." msgid "%(claim)s markerades som nekad."
msgstr "" msgstr ""
#: claims/views.py:215 #: claims/views.py:218
#, python-format #, python-format
msgid "%(claim)s återställdes till väntande status." msgid "%(claim)s återställdes till väntande status."
msgstr "" msgstr ""
#: claims/views.py:235 #: claims/views.py:238
msgid "Betalningshantering är inte aktiverad." msgid "Betalningshantering är inte aktiverad."
msgstr "" msgstr ""
#: claims/views.py:243 #: claims/views.py:241
msgid "Endast godkända utlägg kan markeras som betalda." msgid "Du har inte behörighet att markera betalningar i systemet."
msgstr "" msgstr ""
#: claims/views.py:246 #: claims/views.py:246
msgid "Endast godkända utlägg kan markeras som betalda."
msgstr ""
#: claims/views.py:249
msgid "Detta utlägg är redan markerat som betalt." msgid "Detta utlägg är redan markerat som betalt."
msgstr "Detta utlägg är redan markerat som betalt." msgstr "Detta utlägg är redan markerat som betalt."
#: claims/views.py:257 #: claims/views.py:260
#, python-format #, python-format
msgid "%(claim)s markerades som betald." msgid "%(claim)s markerades som betald."
msgstr "%(claim)s markerades som betald." msgstr "%(claim)s markerades som betald."
#: claims/views.py:266 #: claims/views.py:265
msgid "Du har inte behörighet att redigera utlägg."
msgstr ""
#: claims/views.py:269
msgid "Endast väntande utlägg kan redigeras via panelen." msgid "Endast väntande utlägg kan redigeras via panelen."
msgstr "Endast väntande utlägg kan redigeras via panelen." msgstr "Endast väntande utlägg kan redigeras via panelen."
#: claims/views.py:295 #: claims/views.py:298
#, python-format #, python-format
msgid "Följande fält uppdaterades: %(fields)s" msgid "Följande fält uppdaterades: %(fields)s"
msgstr "Följande fält uppdaterades: %(fields)s" msgstr "Följande fält uppdaterades: %(fields)s"
#: claims/views.py:301 #: claims/views.py:304
msgid "Informationen uppdaterades." msgid "Informationen uppdaterades."
msgstr "Informationen uppdaterades." msgstr "Informationen uppdaterades."
#: claims/views.py:303 #: claims/views.py:306
msgid "Inga förändringar att spara." msgid "Inga förändringar att spara."
msgstr "Inga förändringar att spara." msgstr "Inga förändringar att spara."
#: claims/views.py:369 #: claims/views.py:372
msgid "Du saknar behörighet för åtgärden." msgid "Du saknar behörighet för åtgärden."
msgstr "Du saknar behörighet för åtgärden." msgstr "Du saknar behörighet för åtgärden."
#: claims/views.py:416 #: claims/views.py:435
#, python-format #, python-format
msgid "Användaren %(user)s skapades." msgid "Användaren %(user)s skapades."
msgstr "" msgstr ""
#: claims/views.py:427 #: claims/views.py:447
msgid "Du kan inte ta bort din egen staff-status." msgid "Du kan inte ta bort din egen staff-status."
msgstr "" msgstr ""
#: claims/views.py:433 #: claims/views.py:478
#, python-format #, python-format
msgid "Behörigheter uppdaterades för %(user)s." msgid "Behörigheter uppdaterades för %(user)s."
msgstr "" msgstr ""
#: claims/views.py:435 #: claims/views.py:480
msgid "Kunde inte uppdatera behörigheter." msgid "Kunde inte uppdatera behörigheter."
msgstr "" msgstr ""
#: claims/views.py:445 #: claims/views.py:490
msgid "Du kan inte ta bort ditt eget konto." msgid "Du kan inte ta bort ditt eget konto."
msgstr "" msgstr ""
#: claims/views.py:447 #: claims/views.py:492
msgid "Du kan inte ta bort en superuser via detta gränssnitt." msgid "Du kan inte ta bort en superuser via detta gränssnitt."
msgstr "" msgstr ""
#: claims/views.py:450 #: claims/views.py:495
msgid "Användaren togs bort." msgid "Användaren togs bort."
msgstr "" msgstr ""
#: claims/views.py:453 #: claims/views.py:498
msgid "Okänd åtgärd." msgid "Okänd åtgärd."
msgstr "" msgstr ""