Add inline edit panel for claims

This commit is contained in:
Victor Andersson
2025-11-09 21:49:44 +01:00
parent 0d68c75fef
commit caf3df24cf
8 changed files with 359 additions and 108 deletions

View File

@@ -26,7 +26,7 @@ Nyckel-URLer (språkprefixed):
- **Auto-prefill:** Inloggade användare får namn, e-post och senaste kontonummer förifyllt. - **Auto-prefill:** Inloggade användare får namn, e-post och senaste kontonummer förifyllt.
- **Valuta & projekt:** Varje rad har dold valutaväljare (SEK default) och projektreferens. Projekt listas från Django admin > Projekt. - **Valuta & projekt:** Varje rad har dold valutaväljare (SEK default) och projektreferens. Projekt listas från Django admin > Projekt.
- **Kvitton:** Filuppladdningar sparas med slumpat UUID-baserat namn under `receipts/` för säkerhet och unika namn. - **Kvitton:** Filuppladdningar sparas med slumpat UUID-baserat namn under `receipts/` för säkerhet och unika namn.
- **Dashboard:** KPI-kort med totalsiffror, senaste aktivitet, statusfördelning och samma inline-flöde för beslut/utbetalningar (inkl. möjlighet att korrigera projekt vid beslut). - **Dashboard:** KPI-kort med totalsiffror, senaste aktivitet, statusfördelning och samma inline-flöde för beslut/utbetalningar. Attestanter kan öppna en redigeringspanel för att justera namn, belopp, valuta, kontonummer och projekt innan beslut.
- **Betalspårning:** När intern betalning är på får godkända claims en "Betala"-knapp. När ett claim markeras som betalt låses status/kommentar tills reset görs. - **Betalspårning:** När intern betalning är på får godkända claims en "Betala"-knapp. När ett claim markeras som betalt låses status/kommentar tills reset görs.
- **Mina utlägg:** Inloggade ser sina egna claims i samma Tailwind-layout med kvitto-länk och logg. - **Mina utlägg:** Inloggade ser sina egna claims i samma Tailwind-layout med kvitto-länk och logg.
- **Användarhantering:** Tailwind-sida där personal kan skapa konton, tilldela `claims.view_claim`/`claims.change_claim`, markera staff och ta bort användare. - **Användarhantering:** Tailwind-sida där personal kan skapa konton, tilldela `claims.view_claim`/`claims.change_claim`, markera staff och ta bort användare.

View File

@@ -73,14 +73,15 @@ class ClaimDecisionForm(forms.Form):
label=_("Evenemang/Projekt"), label=_("Evenemang/Projekt"),
) )
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["project"].queryset = Project.objects.filter(is_active=True).order_by("name")
decision_note = forms.CharField( decision_note = forms.CharField(
required=False, required=False,
widget=forms.Textarea(attrs={"rows": 2, "placeholder": _("Kommentar")}), widget=forms.Textarea(attrs={"rows": 2, "placeholder": _("Kommentar")}),
) )
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["project"].queryset = Project.objects.filter(is_active=True).order_by("name")
def clean(self): def clean(self):
cleaned = super().clean() cleaned = super().clean()
action = cleaned.get("action") action = cleaned.get("action")
@@ -90,6 +91,29 @@ class ClaimDecisionForm(forms.Form):
return cleaned return cleaned
class ClaimEditForm(forms.ModelForm):
class Meta:
model = Claim
fields = [
"full_name",
"email",
"account_number",
"amount",
"currency",
"project",
"description",
]
labels = {
"full_name": _("Namn"),
"email": _("E-post"),
"account_number": _("Kontonummer"),
"amount": _("Belopp"),
"currency": _("Valuta"),
"project": _("Evenemang/Projekt"),
"description": _("Beskrivning"),
}
class UserManagementForm(forms.Form): class UserManagementForm(forms.Form):
username = forms.CharField(max_length=150, label=_("Användarnamn")) username = forms.CharField(max_length=150, label=_("Användarnamn"))
email = forms.EmailField(required=False, label=_("E-post")) email = forms.EmailField(required=False, label=_("E-post"))

View File

@@ -123,6 +123,7 @@ class ClaimLog(models.Model):
STATUS_CHANGED = "status_changed", _("Status changed") STATUS_CHANGED = "status_changed", _("Status changed")
MARKED_PAID = "marked_paid", _("Marked as paid") MARKED_PAID = "marked_paid", _("Marked as paid")
PROJECT_CHANGED = "project_changed", _("Project changed") PROJECT_CHANGED = "project_changed", _("Project changed")
DETAILS_EDITED = "details_edited", _("Details edited")
claim = models.ForeignKey(Claim, related_name="logs", on_delete=models.CASCADE) claim = models.ForeignKey(Claim, related_name="logs", on_delete=models.CASCADE)
action = models.CharField(max_length=32, choices=Action.choices) action = models.CharField(max_length=32, choices=Action.choices)

View File

@@ -118,25 +118,32 @@
</p> </p>
</div> </div>
</div> </div>
<div class="flex flex-col items-start gap-2 text-sm lg:items-end"> <div class="flex flex-col items-start gap-2 text-sm lg:items-end">
<span class="rounded-full px-4 py-2 text-sm font-semibold {% if claim.status == 'approved' %}bg-green-50 text-green-700 border border-green-200{% elif claim.status == 'rejected' %}bg-rose-50 text-rose-700 border border-rose-200{% else %}bg-amber-50 text-amber-800 border border-amber-200{% endif %}"> <span class="rounded-full px-4 py-2 text-sm font-semibold {% if claim.status == 'approved' %}bg-green-50 text-green-700 border border-green-200{% elif claim.status == 'rejected' %}bg-rose-50 text-rose-700 border border-rose-200{% else %}bg-amber-50 text-amber-800 border border-amber-200{% endif %}">
{{ claim.get_status_display }} {{ claim.get_status_display }}
</span> </span>
{% if claim.decision_note %} {% if claim.decision_note %}
<p class="text-xs text-gray-500">{% trans "Kommentar" %}: {{ claim.decision_note }}</p> <p class="text-xs text-gray-500">{% trans "Kommentar" %}: {{ claim.decision_note }}</p>
{% endif %} {% endif %}
{% if payments_enabled and claim.status == 'approved' %} {% if payments_enabled and claim.status == 'approved' %}
{% if claim.is_paid %} {% if claim.is_paid %}
<span class="rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-800"> <span class="rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-800">
{% trans "Betald" %} {{ claim.paid_at|date:"Y-m-d H:i" }} {% trans "Betald" %} {{ claim.paid_at|date:"Y-m-d H:i" }}
{% if claim.paid_by %}{% trans "av" %} {{ claim.paid_by.get_username }}{% endif %} {% if claim.paid_by %}{% trans "av" %} {{ claim.paid_by.get_username }}{% endif %}
</span> </span>
{% else %} {% else %}
<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 %}
</div> {% if can_change %}
</div> <button type="button"
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">
{% trans "Redigera" %}
</button>
{% endif %}
</div>
</div>
{% if claim.status == 'approved' %} {% if claim.status == 'approved' %}
<div class="mx-6 mt-4 grid gap-4 rounded-3xl border border-green-100 bg-green-50 px-6 py-4 text-sm text-green-900 {% if payments_enabled and claim.is_paid %}md:grid-cols-1{% else %}md:grid-cols-[2fr,1fr]{% endif %}"> <div class="mx-6 mt-4 grid gap-4 rounded-3xl border border-green-100 bg-green-50 px-6 py-4 text-sm text-green-900 {% if payments_enabled and claim.is_paid %}md:grid-cols-1{% else %}md:grid-cols-[2fr,1fr]{% endif %}">
@@ -294,6 +301,78 @@
<p class="mt-2 text-sm">{% trans "Välj en annan status för att se fler poster." %}</p> <p class="mt-2 text-sm">{% trans "Välj en annan status för att se fler poster." %}</p>
</div> </div>
{% endif %} {% endif %}
{% if can_change %}
<div class="fixed inset-0 z-40 hidden items-center justify-center bg-black/60 p-4" data-edit-panel="{{ claim.id }}">
<div class="w-full max-w-2xl rounded-3xl bg-white p-6 text-left shadow-2xl">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-semibold uppercase tracking-wide text-gray-500">{% trans "Redigera utlägg" %}</p>
<h3 class="text-xl font-semibold text-gray-900">{{ claim.full_name }}</h3>
</div>
<button type="button" data-close-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_type" value="edit">
<input type="hidden" name="edit_claim_id" value="{{ claim.id }}">
<div class="grid gap-4 md:grid-cols-2">
<label class="text-sm font-medium text-gray-700">
{% trans "Namn" %}
<input type="text" name="full_name" value="{{ claim.full_name }}" class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600" required>
</label>
<label class="text-sm font-medium text-gray-700">
{% trans "E-post" %}
<input type="email" name="email" value="{{ claim.email }}" class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600" required>
</label>
<label class="text-sm font-medium text-gray-700">
{% trans "Kontonummer" %}
<input type="text" name="account_number" value="{{ claim.account_number }}" class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600" required>
</label>
<label class="text-sm font-medium text-gray-700">
{% trans "Belopp" %}
<input type="number" step="0.01" name="amount" value="{{ claim.amount }}" class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600" required>
</label>
<label class="text-sm font-medium text-gray-700">
{% trans "Valuta" %}
<select name="currency" class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600">
{% for value, label in currency_choices %}
<option value="{{ value }}"{% if claim.currency == value %} selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</label>
<label class="text-sm font-medium text-gray-700">
{% trans "Evenemang/Projekt" %}
<select name="project" class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600">
<option value="">{% trans "Ingen" %}</option>
{% for project in project_options %}
<option value="{{ project.id }}"{% if claim.project and project.id == claim.project.id %} selected{% endif %}>{{ project }}</option>
{% endfor %}
</select>
</label>
</div>
<div>
<label class="text-sm font-medium text-gray-700" for="edit-description-{{ claim.id }}">{% trans "Beskrivning" %}</label>
<textarea id="edit-description-{{ claim.id }}" name="description" rows="4" class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600">{{ claim.description }}</textarea>
</div>
<div class="flex items-center justify-end gap-3">
<button type="button" data-close-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 ändringar" %}
</button>
</div>
</form>
<noscript>
<p class="mt-4 rounded-2xl bg-amber-50 px-4 py-3 text-xs text-amber-800">
{% trans "Aktivera JavaScript för att kunna redigera uppgifter direkt här." %}
</p>
</noscript>
</div>
</div>
{% endif %}
</div> </div>
<aside class="space-y-6"> <aside class="space-y-6">
@@ -352,6 +431,37 @@ document.addEventListener("DOMContentLoaded", () => {
const filterButtons = Array.from(document.querySelectorAll("[data-filter-button]")); const filterButtons = Array.from(document.querySelectorAll("[data-filter-button]"));
const cards = Array.from(document.querySelectorAll("[data-claim-card]")); const cards = Array.from(document.querySelectorAll("[data-claim-card]"));
const emptyState = document.querySelector("[data-claim-empty]"); const emptyState = document.querySelector("[data-claim-empty]");
const editButtons = Array.from(document.querySelectorAll("[data-open-edit]"));
const panels = Array.from(document.querySelectorAll("[data-edit-panel]"));
if (!filterButtons.length || !cards.length) {
// still set up edit panels if filters missing
}
if (editButtons.length) {
editButtons.forEach((button) => {
button.addEventListener("click", () => {
const target = document.querySelector(`[data-edit-panel="${button.dataset.openEdit}"]`);
if (target) {
target.classList.remove("hidden");
target.classList.add("flex");
}
});
});
}
panels.forEach((panel) => {
panel.addEventListener("click", (event) => {
if (event.target === panel) {
panel.classList.add("hidden");
panel.classList.remove("flex");
}
});
panel.querySelectorAll("[data-close-edit]").forEach((btn) => {
btn.addEventListener("click", () => {
panel.classList.add("hidden");
panel.classList.remove("flex");
});
});
});
if (!filterButtons.length || !cards.length) { if (!filterButtons.length || !cards.length) {
return; return;
} }

View File

@@ -144,6 +144,33 @@ class DashboardViewTests(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
claim.refresh_from_db() claim.refresh_from_db()
self.assertEqual(claim.project, project_new) self.assertEqual(claim.project, project_new)
self.assertTrue( self.assertTrue(claim.logs.filter(action=ClaimLog.Action.PROJECT_CHANGED).exists())
claim.logs.filter(action=ClaimLog.Action.PROJECT_CHANGED, note__icontains="Project updated").exists()
def test_attester_can_edit_details(self):
project = Project.objects.create(name="Event", is_active=True)
claim = self._create_claim(project=project, amount=100)
response = self.client.post(
reverse("claims:admin-list"),
{
"action_type": "edit",
"edit_claim_id": claim.id,
"full_name": "Changed Name",
"email": "changed@example.com",
"account_number": "789-000",
"amount": "555.55",
"currency": Claim.Currency.EUR,
"project": "",
"description": "Updated description",
},
follow=True,
) )
self.assertEqual(response.status_code, 200)
claim.refresh_from_db()
self.assertEqual(claim.full_name, "Changed Name")
self.assertEqual(claim.email, "changed@example.com")
self.assertEqual(claim.currency, Claim.Currency.EUR)
self.assertIsNone(claim.project)
edit_log = claim.logs.filter(action=ClaimLog.Action.DETAILS_EDITED).first()
self.assertIsNotNone(edit_log)
self.assertIn("Namn", edit_log.note)

View File

@@ -17,6 +17,7 @@ from django.views.generic import ListView, TemplateView
from .forms import ( from .forms import (
ClaimDecisionForm, ClaimDecisionForm,
ClaimEditForm,
ClaimLineForm, ClaimLineForm,
ClaimantForm, ClaimantForm,
DeleteUserForm, DeleteUserForm,
@@ -162,6 +163,7 @@ class ClaimDashboardView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
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")
context["currency_choices"] = Claim.Currency.choices
context["has_any_claims"] = context["summary"]["total_claims"] > 0 context["has_any_claims"] = context["summary"]["total_claims"] > 0
context["has_filtered_claims"] = self._has_filtered_claims(context["status_filter"], context["summary"]) context["has_filtered_claims"] = self._has_filtered_claims(context["status_filter"], context["summary"])
context["recent_claims"] = ( context["recent_claims"] = (
@@ -175,6 +177,8 @@ class ClaimDashboardView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
action_type = request.POST.get("action_type", "decision") action_type = request.POST.get("action_type", "decision")
if action_type == "payment": if action_type == "payment":
return self._handle_payment(request) return self._handle_payment(request)
if action_type == "edit":
return self._handle_edit(request)
return self._handle_decision(request) return self._handle_decision(request)
def _handle_decision(self, request): def _handle_decision(self, request):
@@ -256,6 +260,39 @@ class ClaimDashboardView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
messages.success(request, _("%(claim)s markerades som betald.") % {"claim": claim}) messages.success(request, _("%(claim)s markerades som betald.") % {"claim": claim})
return redirect(request.get_full_path()) return redirect(request.get_full_path())
def _handle_edit(self, request):
if not request.user.has_perm("claims.change_claim"):
messages.error(request, _("Du har inte behörighet att uppdatera utlägg."))
return redirect(request.get_full_path())
claim = get_object_or_404(Claim, pk=request.POST.get("edit_claim_id"))
form = ClaimEditForm(request.POST, instance=claim)
if not form.is_valid():
for error in form.errors.get("__all__", []):
messages.error(request, error)
for field, field_errors in form.errors.items():
if field == "__all__":
continue
for error in field_errors:
messages.error(request, f"{form.fields[field].label}: {error}")
return redirect(request.get_full_path())
updated_claim = form.save()
changed_fields = []
for field in form.changed_data:
label = form.fields[field].label or field
changed_fields.append(str(label))
if changed_fields:
note = _("Fields updated: %(fields)s") % {"fields": ", ".join(changed_fields)}
claim.add_log(
action=ClaimLog.Action.DETAILS_EDITED,
performed_by=request.user,
note=note,
)
messages.success(request, _("Informationen uppdaterades."))
else:
messages.info(request, _("Inga förändringar att spara."))
return redirect(request.get_full_path())
def _build_summary(self): def _build_summary(self):
now = timezone.now() now = timezone.now()
last_week = now - timedelta(days=7) last_week = now - timedelta(days=7)

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-09 14:00+0100\n" "POT-Creation-Date: 2025-11-09 21:45+0100\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"
@@ -12,34 +12,43 @@ msgstr ""
"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:19 claims/forms.py:107
msgid "Namn" msgid "Namn"
msgstr "Name" msgstr "Name"
#: claims/forms.py:23 claims/forms.py:95 #: claims/forms.py:23 claims/forms.py:108 claims/forms.py:119
#: claims/templates/claims/dashboard.html:169 #: claims/templates/claims/dashboard.html:176
#: claims/templates/claims/dashboard.html:326
msgid "E-post" msgid "E-post"
msgstr "Email" msgstr "Email"
#: claims/forms.py:28 claims/templates/claims/dashboard.html:157 #: claims/forms.py:28 claims/forms.py:109
#: claims/templates/claims/dashboard.html:164
#: claims/templates/claims/dashboard.html:330
msgid "Kontonummer" msgid "Kontonummer"
msgstr "Account number" msgstr "Account number"
#: claims/forms.py:49 claims/templates/claims/dashboard.html:204 #: claims/forms.py:49 claims/forms.py:113
#: claims/templates/claims/dashboard.html:211
#: claims/templates/claims/dashboard.html:356
msgid "Beskrivning" msgid "Beskrivning"
msgstr "Description" msgstr "Description"
#: claims/forms.py:50 claims/templates/claims/dashboard.html:153 #: claims/forms.py:50 claims/forms.py:110
#: claims/templates/claims/dashboard.html:160
#: claims/templates/claims/dashboard.html:334
#: 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:51 claims/forms.py:111
#: claims/templates/claims/dashboard.html:338
msgid "Valuta" msgid "Valuta"
msgstr "Currency" msgstr "Currency"
#: claims/forms.py:52 claims/forms.py:73 #: claims/forms.py:52 claims/forms.py:73 claims/forms.py:112
#: claims/templates/claims/dashboard.html:264 #: claims/templates/claims/dashboard.html:271
#: claims/templates/claims/dashboard.html:346
msgid "Evenemang/Projekt" msgid "Evenemang/Projekt"
msgstr "Project" msgstr "Project"
@@ -55,76 +64,76 @@ msgstr "Approve"
msgid "Neka" msgid "Neka"
msgstr "Reject" msgstr "Reject"
#: claims/forms.py:81 claims/templates/claims/dashboard.html:126 #: claims/forms.py:78 claims/templates/claims/dashboard.html:126
#: claims/templates/claims/dashboard.html:261 #: claims/templates/claims/dashboard.html:268
msgid "Kommentar" msgid "Kommentar"
msgstr "Comment" msgstr "Comment"
#: claims/forms.py:89 #: claims/forms.py:90
msgid "Kommentar krävs när du nekar ett utlägg." msgid "Kommentar krävs när du nekar ett utlägg."
msgstr "A comment is required when you reject an expense." msgstr "A comment is required when you reject an expense."
#: claims/forms.py:94 #: claims/forms.py:118
msgid "Användarnamn" msgid "Användarnamn"
msgstr "Username" msgstr "Username"
#: claims/forms.py:96 #: claims/forms.py:120
msgid "Förnamn" msgid "Förnamn"
msgstr "First name" msgstr "First name"
#: claims/forms.py:97 #: claims/forms.py:121
msgid "Efternamn" msgid "Efternamn"
msgstr "Last name" msgstr "Last name"
#: claims/forms.py:98 #: claims/forms.py:122
msgid "Lösenord" msgid "Lösenord"
msgstr "Password" msgstr "Password"
#: claims/forms.py:99 #: claims/forms.py:123
msgid "Bekräfta lösenord" msgid "Bekräfta lösenord"
msgstr "Confirm password" msgstr "Confirm password"
#: claims/forms.py:100 #: claims/forms.py:124
msgid "Administratör (staff)" msgid "Administratör (staff)"
msgstr "Administrator (staff)" msgstr "Administrator (staff)"
#: claims/forms.py:101 #: claims/forms.py:125
msgid "Ge behörighet att se utlägg" msgid "Ge behörighet att se utlägg"
msgstr "Allow viewing claims" msgstr "Allow viewing claims"
#: claims/forms.py:102 #: claims/forms.py:126
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:107 #: claims/forms.py:131
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:113 #: claims/forms.py:137
msgid "Lösenorden matchar inte." msgid "Lösenorden matchar inte."
msgstr "Passwords do not match." msgstr "Passwords do not match."
#: claims/forms.py:131 claims/templates/claims/user_management.html:116 #: claims/forms.py:155 claims/templates/claims/user_management.html:116
msgid "Admin/staff" msgid "Admin/staff"
msgstr "Admin/staff" msgstr "Admin/staff"
#: claims/forms.py:132 claims/templates/claims/user_management.html:120 #: claims/forms.py:156 claims/templates/claims/user_management.html:120
msgid "Får se utlägg" msgid "Får se utlägg"
msgstr "May view claims" msgstr "May view claims"
#: claims/forms.py:133 claims/templates/claims/user_management.html:124 #: claims/forms.py:157 claims/templates/claims/user_management.html:124
msgid "Får besluta utlägg" msgid "Får besluta utlägg"
msgstr "May decide claims" msgstr "May decide claims"
#: claims/models.py:29 claims/templates/claims/dashboard.html:334 #: claims/models.py:29 claims/templates/claims/dashboard.html:413
msgid "Pending" msgid "Pending"
msgstr "Pending" msgstr "Pending"
#: claims/models.py:30 claims/templates/claims/dashboard.html:338 #: claims/models.py:30 claims/templates/claims/dashboard.html:417
msgid "Approved" msgid "Approved"
msgstr "Approved" msgstr "Approved"
#: claims/models.py:31 claims/templates/claims/dashboard.html:342 #: claims/models.py:31 claims/templates/claims/dashboard.html:421
msgid "Rejected" msgid "Rejected"
msgstr "Rejected" msgstr "Rejected"
@@ -164,6 +173,10 @@ msgstr "Marked as paid"
msgid "Project changed" msgid "Project changed"
msgstr "Project changed" msgstr "Project changed"
#: claims/models.py:126
msgid "Details edited"
msgstr ""
#: claims/templates/claims/base.html:8 #: claims/templates/claims/base.html:8
msgid "Claims" msgid "Claims"
msgstr "Claims" msgstr "Claims"
@@ -323,7 +336,7 @@ msgid "Alla"
msgstr "All" msgstr "All"
#: claims/templates/claims/dashboard.html:106 #: claims/templates/claims/dashboard.html:106
#: claims/templates/claims/dashboard.html:165 #: claims/templates/claims/dashboard.html:172
msgid "Skapad" msgid "Skapad"
msgstr "Created" msgstr "Created"
@@ -356,20 +369,24 @@ msgstr "by"
msgid "Ej markerad som betald" msgid "Ej markerad som betald"
msgstr "Not marked as paid" msgstr "Not marked as paid"
#: claims/templates/claims/dashboard.html:145 #: claims/templates/claims/dashboard.html:142
msgid "Redigera"
msgstr "Edit"
#: claims/templates/claims/dashboard.html:152
msgid "Utbetalningsdetaljer" msgid "Utbetalningsdetaljer"
msgstr "Payout details" msgstr "Payout details"
#: claims/templates/claims/dashboard.html:161 #: claims/templates/claims/dashboard.html:168
msgid "Referens (Claim ID)" msgid "Referens (Claim ID)"
msgstr "Reference (Claim ID)" msgstr "Reference (Claim ID)"
#: claims/templates/claims/dashboard.html:173 #: claims/templates/claims/dashboard.html:180
#: claims/templates/claims/my_claims.html:24 #: claims/templates/claims/my_claims.html:24
msgid "Projekt" msgid "Projekt"
msgstr "Project" msgstr "Project"
#: claims/templates/claims/dashboard.html:177 #: claims/templates/claims/dashboard.html:184
msgid "" msgid ""
"Använd referensen och beloppet när du lägger upp betalningen hjälper att " "Använd referensen och beloppet när du lägger upp betalningen hjälper att "
"undvika dubbletter." "undvika dubbletter."
@@ -377,7 +394,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:182 #: claims/templates/claims/dashboard.html:189
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."
@@ -385,17 +402,15 @@ 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:187 #: claims/templates/claims/dashboard.html:194
#, fuzzy
#| msgid "Markerad som betald"
msgid "Markera som betald" msgid "Markera som betald"
msgstr "Marked as paid" msgstr "Mark as paid"
#: claims/templates/claims/dashboard.html:190 #: claims/templates/claims/dashboard.html:197
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:195 #: claims/templates/claims/dashboard.html:202
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."
@@ -403,91 +418,115 @@ 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:212 #: claims/templates/claims/dashboard.html:219
msgid "Visa kvitto" msgid "Visa kvitto"
msgstr "View receipt" msgstr "View receipt"
#: claims/templates/claims/dashboard.html:215 #: claims/templates/claims/dashboard.html:222
msgid "Inget kvitto bifogat" msgid "Inget kvitto bifogat"
msgstr "No receipt attached" msgstr "No receipt attached"
#: claims/templates/claims/dashboard.html:217 #: claims/templates/claims/dashboard.html:224
msgid "Senast uppdaterad" msgid "Senast uppdaterad"
msgstr "Last updated" msgstr "Last updated"
#: claims/templates/claims/dashboard.html:222 #: claims/templates/claims/dashboard.html:229
msgid "Logg" msgid "Logg"
msgstr "Log" msgstr "Log"
#: claims/templates/claims/dashboard.html:229 #: claims/templates/claims/dashboard.html:236
#: 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:235 #: claims/templates/claims/dashboard.html:242
msgid "Av" msgid "Av"
msgstr "By" msgstr "By"
#: claims/templates/claims/dashboard.html:239 #: claims/templates/claims/dashboard.html:246
#: 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:247 #: claims/templates/claims/dashboard.html:254
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:254 #: claims/templates/claims/dashboard.html:261
msgid "Åtgärd" msgid "Åtgärd"
msgstr "Action" msgstr "Action"
#: claims/templates/claims/dashboard.html:266 #: claims/templates/claims/dashboard.html:273
msgid "Behåll nuvarande" msgid "Behåll nuvarande"
msgstr "Keep current" msgstr "Keep current"
#: claims/templates/claims/dashboard.html:271 #: claims/templates/claims/dashboard.html:278
msgid "Justera projekt om underlaget skickats in mot fel evenemang." msgid "Justera projekt om underlaget skickats in mot fel evenemang."
msgstr "Adjust the project if the submission was sent against the wrong event." msgstr "Adjust the project if the submission was sent against the wrong event."
#: claims/templates/claims/dashboard.html:275 #: claims/templates/claims/dashboard.html:282
msgid "Uppdatera beslut" msgid "Uppdatera beslut"
msgstr "Update decision" msgstr "Update decision"
#: claims/templates/claims/dashboard.html:286 #: claims/templates/claims/dashboard.html:293
#: 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:287 #: claims/templates/claims/dashboard.html:294
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:293 #: claims/templates/claims/dashboard.html:300
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:294 #: claims/templates/claims/dashboard.html:301
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:302 #: claims/templates/claims/dashboard.html:309
msgid "Redigera utlägg"
msgstr "Edit claim"
#: claims/templates/claims/dashboard.html:313
msgid "Stäng"
msgstr "Close"
#: claims/templates/claims/dashboard.html:348
msgid "Ingen"
msgstr "None"
#: claims/templates/claims/dashboard.html:361
msgid "Avbryt"
msgstr "Cancel"
#: claims/templates/claims/dashboard.html:364
msgid "Spara ändringar"
msgstr "Save changes"
#: claims/templates/claims/dashboard.html:370
msgid "Aktivera JavaScript för att kunna redigera uppgifter direkt här."
msgstr "Enable JavaScript to edit the information directly here."
#: claims/templates/claims/dashboard.html:381
msgid "Senaste inskick" msgid "Senaste inskick"
msgstr "Latest submissions" msgstr "Latest submissions"
#: claims/templates/claims/dashboard.html:303 #: claims/templates/claims/dashboard.html:382
msgid "Aktivitet" msgid "Aktivitet"
msgstr "Activity" msgstr "Activity"
#: claims/templates/claims/dashboard.html:321 #: claims/templates/claims/dashboard.html:400
msgid "Inga aktiviteter än." msgid "Inga aktiviteter än."
msgstr "No activity yet." msgstr "No activity yet."
#: claims/templates/claims/dashboard.html:329 #: claims/templates/claims/dashboard.html:408
msgid "Statusfördelning" msgid "Statusfördelning"
msgstr "Status breakdown" msgstr "Status breakdown"
#: claims/templates/claims/dashboard.html:330 #: claims/templates/claims/dashboard.html:409
msgid "Snabbstatistik" msgid "Snabbstatistik"
msgstr "Quick stats" msgstr "Quick stats"
@@ -831,102 +870,115 @@ 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:126 #: claims/views.py:127
#, python-brace-format #, python-brace-format
msgid "{} utlägg skickade in." msgid "{} utlägg skickade in."
msgstr "" msgstr ""
#: claims/views.py:129 #: claims/views.py:130
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:131 #: claims/views.py:132
msgid "Kunde inte spara utläggen. Kontrollera formuläret." msgid "Kunde inte spara utläggen. Kontrollera formuläret."
msgstr "" msgstr ""
#: claims/views.py:182 claims/views.py:237 #: claims/views.py:186 claims/views.py:241 claims/views.py:265
#, fuzzy #, fuzzy
#| msgid "Ge behörighet att besluta utlägg" #| 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 "Allow deciding claims"
#: claims/views.py:196 #: claims/views.py:200
#, fuzzy #, fuzzy
#| 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."
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 "The claim is marked as paid. Decision/comments are locked." msgstr "The claim is marked as paid. Decision/comments are locked."
#: claims/views.py:208 #: claims/views.py:212
#, 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:215
#, 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:228 #: claims/views.py:232
msgid "Project updated during decision." msgid "Project updated during decision."
msgstr "Project updated during decision." msgstr "Project updated during decision."
#: claims/views.py:234 #: 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:242 #: 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:245 #: 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:256 #: 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:322 #: claims/views.py:287
msgid "Du saknar behörighet för åtgärden." #, python-format
msgstr "" msgid "Fields updated: %(fields)s"
msgstr "Fields updated: %(fields)s"
#: claims/views.py:369 #: claims/views.py:293
msgid "Informationen uppdaterades."
msgstr "Information updated."
#: claims/views.py:295
msgid "Inga förändringar att spara."
msgstr "No changes to save."
#: claims/views.py:361
msgid "Du saknar behörighet för åtgärden."
msgstr "You do not have permission to perform this action."
#: claims/views.py:408
#, python-format #, python-format
msgid "Användaren %(user)s skapades." msgid "Användaren %(user)s skapades."
msgstr "" msgstr ""
#: claims/views.py:380 #: claims/views.py:419
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:386 #: claims/views.py:425
#, 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:388 #: claims/views.py:427
#, 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:398 #: claims/views.py:437
msgid "Du kan inte ta bort ditt eget konto." msgid "Du kan inte ta bort ditt eget konto."
msgstr "" msgstr ""
#: claims/views.py:400 #: claims/views.py:439
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:403 #: claims/views.py:442
#, 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:406 #: claims/views.py:445
msgid "Okänd åtgärd." msgid "Okänd åtgärd."
msgstr "" msgstr ""