diff --git a/AGENTS.md b/AGENTS.md index 739f783..92b10b2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,6 +22,8 @@ Bygg ett webbaserat system för hantering av utlägg (”claims”) åt en organ 10. Claims ska kopplas till ett projekt/evenemang; projekten hanteras via Django admin. 11. Offentliga sidor ska använda Tailwind-baserade komponenter (CDN är okej) med minimalistisk layout. Claim-formuläret ska erbjuda klient-side kontroll för antal rader (plus/minus) utan sidladdning och återanvända formsetets tomma form som mall. 12. Adminvyn för claims ska spegla samma designprinciper (kort per claim, statuschippar, loggtimeline och inlinebeslut). +13. Hantering av utbetalningar i UI är bakom flaggan `CLAIMS_ENABLE_INTERNAL_PAYMENTS` och kan även togglas via Django admin (modell `SystemSetting`). När den är på ska godkända claims få en summeringssektion med tydlig info (namn, belopp, kontonr) och en "Betala"-knapp som markerar posten som betald (med logg och markerad betalstatus). När flaggan är av saknas knappen och admins instrueras att hantera betalning externt. +14. När ett utlägg markerats som betalt ska beslut/status vara låst (ingen uppdatering av kommentar eller status i UI eller Django admin). ## Säkerhet och drift - Skydda admin-flöden bakom inloggning. diff --git a/README.md b/README.md index 89202ac..db95da9 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,10 @@ uv run python manage.py runserver - Välj även vilket projekt/evenemang utlägget hör till (valen hämtas från Django admin > Projekt). - Adminlista (kräver `claims.view_claim`, uppdateringar kräver `claims.change_claim`): `http://localhost:8000/claims/admin/` - Adminlistan visar kvittolänk, vem som skickade in (och om det var en inloggad användare) samt en logg över alla statusändringar. +- När ett utlägg markerats som betalat låses beslutskommentar/status i hela systemet (både listvyn och Django admin). - Export-meny (placeholder för framtida integrationer): `http://localhost:8000/claims/export/` - Inloggade användare kan följa sina egna claim via `http://localhost:8000/claims/mine/`. - Behörighets- och kontohantering (visa kräver `auth.view_user`, skapa/uppdatera/ta bort kräver respektive `auth.add_user`/`auth.change_user`/`auth.delete_user`): `http://localhost:8000/claims/users/` - Django auth-vyer (login/logout) exponeras under `/accounts/`. - Använd Django admin (`/admin/`) för att skapa konton, lägga användare i grupper, lägga upp projekt/evenemang samt tilldela behörigheterna `claims.view_claim` och `claims.change_claim`. Superusers har full kontroll per default. +- Intern betalningshantering styrs av miljövariabeln `CLAIMS_ENABLE_INTERNAL_PAYMENTS` (default `true`) och kan dessutom togglas i Django admin under **Systeminställningar**. När funktionen är på får godkända claims en "Betala"-knapp som loggar vem som markerade posten som betald. diff --git a/claims/admin.py b/claims/admin.py index 175404b..c65ac3b 100644 --- a/claims/admin.py +++ b/claims/admin.py @@ -1,6 +1,11 @@ +from django.conf import settings from django.contrib import admin +from django.shortcuts import redirect +from django.urls import path, reverse +from django.utils import timezone +from django.utils.html import format_html -from .models import Claim, ClaimLog, Project +from .models import Claim, ClaimLog, Project, SystemSetting class ClaimLogInline(admin.TabularInline): @@ -12,11 +17,113 @@ class ClaimLogInline(admin.TabularInline): @admin.register(Claim) class ClaimAdmin(admin.ModelAdmin): - list_display = ("full_name", "amount", "currency", "project", "status", "created_at", "submitted_by") - list_filter = ("status", "created_at", "project") + list_display = ("full_name", "amount", "currency", "project", "status", "paid", "created_at", "submitted_by") + list_filter = ("status", "created_at", "project", "paid_at") search_fields = ("full_name", "email", "description") - readonly_fields = ("created_at", "updated_at") + base_readonly = ("created_at", "updated_at", "paid_at", "paid_by", "internal_payments_enabled", "reset_paid_button") + readonly_fields = base_readonly inlines = [ClaimLogInline] + actions = ("mark_as_paid", "mark_as_unpaid") + + @admin.display(boolean=True, description="Betald") + def paid(self, obj): + return obj.is_paid + + @admin.display(description="Intern betalningshantering på?") + def internal_payments_enabled(self, obj): + return SystemSetting.internal_payments_active() + + @admin.display(description="Återställ betalning") + def reset_paid_button(self, obj): + if not obj.is_paid: + return "Ej betald" + url = reverse("admin:claims_claim_reset_payment", args=[obj.pk]) + return format_html( + 'Resetta', + url, + ) + + @admin.action(description="Markera valda som betalda") + def mark_as_paid(self, request, queryset): + count = 0 + for claim in queryset.filter(status=Claim.Status.APPROVED, paid_at__isnull=True): + claim.paid_at = timezone.now() + claim.paid_by = request.user + claim.save(update_fields=["paid_at", "paid_by"]) + claim.add_log( + action=ClaimLog.Action.MARKED_PAID, + performed_by=request.user, + note="Markerad som betald via Django admin.", + ) + count += 1 + if count: + self.message_user(request, f"{count} utlägg markerades som betalda.") + else: + self.message_user(request, "Inga utlägg markerades – kontrollera status/betalning.", level="warning") + + @admin.action(description="Återställ betalningsstatus (markera som obetalda)") + def mark_as_unpaid(self, request, queryset): + count = 0 + for claim in queryset.filter(paid_at__isnull=False): + claim.paid_at = None + claim.paid_by = None + claim.save(update_fields=["paid_at", "paid_by"]) + claim.add_log( + action=ClaimLog.Action.MARKED_PAID, + performed_by=request.user, + note="Betalningsstatus återställd via Django admin.", + ) + count += 1 + if count: + self.message_user(request, f"{count} utlägg markerades som obetalda.") + else: + self.message_user(request, "Inga utlägg behövde återställas.", level="warning") + + def save_model(self, request, obj, form, change): + super().save_model(request, obj, form, change) + + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path( + "/reset-payment/", + self.admin_site.admin_view(self.reset_payment_view), + name="claims_claim_reset_payment", + ), + ] + return custom_urls + urls + + def reset_payment_view(self, request, claim_id): + claim = Claim.objects.filter(pk=claim_id).first() + if not claim: + self.message_user(request, "Utlägget hittades inte.", level="error") + return redirect("admin:claims_claim_changelist") + claim.paid_at = None + claim.paid_by = None + claim.save(update_fields=["paid_at", "paid_by"]) + claim.add_log( + action=ClaimLog.Action.MARKED_PAID, + performed_by=request.user, + note="Betalningsstatus återställd via reset-knapp i admin.", + ) + self.message_user(request, f"{claim} markerades som obetald.") + return redirect("admin:claims_claim_change", claim_id) + + def get_readonly_fields(self, request, obj=None): + if obj and obj.is_paid: + return self.base_readonly + ("status", "decision_note") + return self.base_readonly + + +@admin.register(SystemSetting) +class SystemSettingAdmin(admin.ModelAdmin): + list_display = ("internal_payments_enabled", "updated_at") + list_display_links = ("updated_at",) + list_editable = ("internal_payments_enabled",) + actions = None + + def has_add_permission(self, request): + return not SystemSetting.objects.exists() @admin.register(ClaimLog) diff --git a/claims/migrations/0005_claim_paid_at_claim_paid_by_alter_claimlog_action.py b/claims/migrations/0005_claim_paid_at_claim_paid_by_alter_claimlog_action.py new file mode 100644 index 0000000..cbb7318 --- /dev/null +++ b/claims/migrations/0005_claim_paid_at_claim_paid_by_alter_claimlog_action.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.8 on 2025-11-08 17:35 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('claims', '0004_project_claim_project'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='claim', + name='paid_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='claim', + name='paid_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='claims_marked_paid', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='claimlog', + name='action', + field=models.CharField(choices=[('created', 'Submitted'), ('status_changed', 'Status changed'), ('marked_paid', 'Marked as paid')], max_length=32), + ), + ] diff --git a/claims/migrations/0006_systemsetting.py b/claims/migrations/0006_systemsetting.py new file mode 100644 index 0000000..23bc27c --- /dev/null +++ b/claims/migrations/0006_systemsetting.py @@ -0,0 +1,25 @@ +# Generated by Django 5.2.8 on 2025-11-08 17:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('claims', '0005_claim_paid_at_claim_paid_by_alter_claimlog_action'), + ] + + operations = [ + migrations.CreateModel( + name='SystemSetting', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('internal_payments_enabled', models.BooleanField(default=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Systeminställning', + 'verbose_name_plural': 'Systeminställningar', + }, + ), + ] diff --git a/claims/models.py b/claims/models.py index fed983e..d1f855f 100644 --- a/claims/models.py +++ b/claims/models.py @@ -58,6 +58,14 @@ class Claim(models.Model): ) status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING) decision_note = models.TextField(blank=True) + paid_at = models.DateTimeField(null=True, blank=True) + paid_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="claims_marked_paid", + ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -68,6 +76,10 @@ class Claim(models.Model): project = f" [{self.project}]" if self.project else "" return f"{self.full_name} – {self.amount} {self.currency}{project} ({self.get_status_display()})" + @property + def is_paid(self): + return self.paid_at is not None + def add_log(self, *, action, performed_by=None, from_status=None, to_status=None, note=""): return ClaimLog.objects.create( claim=self, @@ -83,6 +95,7 @@ class ClaimLog(models.Model): class Action(models.TextChoices): CREATED = "created", _("Submitted") STATUS_CHANGED = "status_changed", _("Status changed") + MARKED_PAID = "marked_paid", _("Marked as paid") claim = models.ForeignKey(Claim, related_name="logs", on_delete=models.CASCADE) action = models.CharField(max_length=32, choices=Action.choices) @@ -108,3 +121,24 @@ class ClaimLog(models.Model): def __str__(self): return f"{self.get_action_display()} ({self.created_at:%Y-%m-%d %H:%M})" + + +class SystemSetting(models.Model): + internal_payments_enabled = models.BooleanField(default=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + verbose_name = "Systeminställning" + verbose_name_plural = "Systeminställningar" + + def __str__(self): + return "Systeminställningar" + + @classmethod + def get_solo(cls): + obj, _ = cls.objects.get_or_create(pk=1) + return obj + + @classmethod + def internal_payments_active(cls): + return cls.get_solo().internal_payments_enabled diff --git a/claims/templates/claims/admin_list.html b/claims/templates/claims/admin_list.html index 3beb0b1..9b819c5 100644 --- a/claims/templates/claims/admin_list.html +++ b/claims/templates/claims/admin_list.html @@ -34,8 +34,8 @@
{% for claim in claims %}
-
-
+
+
{{ claim.amount }} {{ claim.currency }} @@ -47,25 +47,67 @@ {% endif %} Skapad {{ claim.created_at|date:"Y-m-d H:i" }}
-

{{ claim.full_name }}

-

- {{ claim.email }} · Konto: {{ claim.account_number }}
- {% if claim.submitted_by %} - Inloggad användare: {{ claim.submitted_by.get_username }} - {% else %} - Inskickad av gäst - {% endif %} -

+
+

Person

+

{{ claim.full_name }}

+

+ {{ claim.email }} · Konto: {{ claim.account_number }}
+ {% if claim.submitted_by %} + Inloggad användare: {{ claim.submitted_by.get_username }} + {% else %} + Inskickad av gäst + {% endif %} +

+
-
+
{{ claim.get_status_display }} {% if claim.decision_note %}

Kommentar: {{ claim.decision_note }}

{% endif %} + {% if payments_enabled and claim.status == 'approved' %} + {% if claim.is_paid %} + + Betald {{ claim.paid_at|date:"Y-m-d H:i" }} + {% if claim.paid_by %}av {{ claim.paid_by.get_username }}{% endif %} + + {% else %} + Ej markerad som betald + {% endif %} + {% endif %}
+ + {% if claim.status == 'approved' %} +
+
+
+

Sammanfattning

+

{{ claim.full_name }}

+

Belopp: {{ claim.amount }} {{ claim.currency }} · Konto: {{ claim.account_number }}

+
+ {% if payments_enabled %} + {% if claim.is_paid %} +

Markerad som betald

+ {% else %} +
+ {% csrf_token %} + + + +
+ {% endif %} + {% else %} +

Intern betalningshantering är av – markera betalning i ekonomisystemet.

+ {% endif %} +
+
+ {% endif %} +

Beskrivning

@@ -110,24 +152,31 @@ {% if can_change %} -
- {% csrf_token %} - + {% if claim.is_paid %} +

+ Utlägget är markerat som betalt. Ändringar av beslut/kommentar är låsta. +

+ {% else %} + + {% csrf_token %} + - - + + - - + + - -
+ + + + {% endif %} {% endif %}
diff --git a/claims/templates/claims/base.html b/claims/templates/claims/base.html index ddb74d6..00da0ad 100644 --- a/claims/templates/claims/base.html +++ b/claims/templates/claims/base.html @@ -24,21 +24,27 @@
-
- claims-system -
-
{% else %} -

Du har inte skickat in några utlägg ännu eller så gjordes de utan inloggning.

+
+

Inga utlägg ännu

+

När du skickar in utlägg medan du är inloggad dyker de upp här.

+
{% endif %} + {% endblock %} diff --git a/claims/templates/claims/submit_success.html b/claims/templates/claims/submit_success.html new file mode 100644 index 0000000..b7af359 --- /dev/null +++ b/claims/templates/claims/submit_success.html @@ -0,0 +1,26 @@ +{% extends "claims/base.html" %} + +{% block title %}Tack för ditt utlägg{% endblock %} + +{% block content %} +
+
+

Tack!

+

Utlägget är skickat

+

+ Vi har tagit emot underlaget. Om du har fler kvitton kan du fylla i ett nytt formulär direkt, + annars kan du logga in för att följa statusen. +

+ +
+
+{% endblock %} diff --git a/claims/templates/claims/user_management.html b/claims/templates/claims/user_management.html index 72272f0..784f968 100644 --- a/claims/templates/claims/user_management.html +++ b/claims/templates/claims/user_management.html @@ -3,76 +3,175 @@ {% block title %}Användarhantering{% endblock %} {% block content %} -

Användare & behörigheter

-

Skapa nya konton, underhåll behörigheter och ta bort användare kopplat till utläggssystemet.

+
+
+

Konton & behörigheter

+

Hantera användare

+

+ Skapa nya konton, justera rättigheter för claim-flödet och ta bort användare som inte längre ska ha åtkomst. +

+
+ Notis: denna sida styr direkta behörigheter. Rättigheter via grupper eller superuser-status gäller även om kryssrutorna avmarkeras. +
+
-

Notis: sidan hanterar direkta behörigheter. Behörigheter via grupper eller superuser-status gäller även om kryssrutorna avmarkeras.

- -
-

Skapa ny användare

- - {% csrf_token %} - - {{ create_form.as_p }} - - -
- -
- -
-

Befintliga användare

- - - - - - - - - - - - {% for row in user_rows %} - {% with user=row.user %} - - - - - - - + {% if field.help_text %} +

{{ field.help_text }}

+ {% endif %} + {% for error in field.errors %} +

{{ error }}

+ {% endfor %} + + {% endfor %} + + + + +
+

Tips för kontohantering

+
    +
  • + + Lägg användare i grupper via Django admin om flera personer ska dela samma roll. +
  • +
  • + + + Behörigheterna claims.view_claim + och claims.change_claim + styr åtkomst till adminvyn respektive beslutsflödet. + +
  • +
  • + + En markerad Admin/staff-användare kan nå Django admin och skapa projekt, exportflöden m.m. +
  • +
  • + + Ta bara bort konton du är säker på – historik försvinner inte, men personen tappar all åtkomst. +
  • +
+
+ + +
+
+

Befintliga användare

+

Justera behörigheter

+
+
+ {% for row in user_rows %} + {% with user=row.user form=row.permission_form delete_form=row.delete_form %} +
+
+
+
+

{{ user.username }}

+ {% if user.is_superuser %} + Superuser + {% endif %} +
+

+ {{ user.get_full_name|default:"Saknar namn" }} · {{ user.email|default:"Ingen e-post" }} +

+
+

+ ID: {{ user.id }} +

+
+
+
+ {% csrf_token %} + + {{ form.user_id }} +
+ + + +
+ + +
+

Ta bort konto

+ {% if delete_form %} +

Åtgärden går inte att ångra. Användaren förlorar omedelbart åtkomst.

+
+ {% csrf_token %} + + {{ delete_form.user_id }} + + + {% else %} +

Kan inte tas bort (antingen du själv eller superuser).

+ {% endif %} +
+
+
{% endwith %} {% empty %} -
+
+

Inga användare ännu

+

Skapa det första kontot via formuläret ovan.

+
{% endfor %} - -
AnvändareNamnE-postBehörigheterTa bort
- {{ user.username }} - {% if user.is_superuser %}{% endif %} - {{ user.get_full_name|default:"-" }}{{ user.email|default:"-" }} - {% with form=row.permission_form %} -
- {% csrf_token %} - - {{ form.user_id }} -
-
-
- -
- {% endwith %} -
- {% if row.delete_form %} -
- {% csrf_token %} - - {{ row.delete_form.user_id }} - -
+
+
+
+

Nytt konto

+

Skapa användare

+

Lösenordet valideras mot Djangos standardregler.

+
+
+ {% csrf_token %} + + {% for field in create_form %} +
+ + {% if field.is_hidden %} + {{ field }} {% else %} - + {% endif %} -
Inga användare upplagda.
+
+ {% endblock %} diff --git a/claims/urls.py b/claims/urls.py index 5dfa971..4f06a76 100644 --- a/claims/urls.py +++ b/claims/urls.py @@ -6,12 +6,14 @@ from .views import ( MyClaimsView, SubmitClaimView, UserManagementView, + SubmitClaimSuccessView, ) app_name = "claims" urlpatterns = [ path("new/", SubmitClaimView.as_view(), name="submit"), + path("submitted/", SubmitClaimSuccessView.as_view(), name="submit-success"), path("admin/", ClaimAdminListView.as_view(), name="admin-list"), path("export/", ClaimExportMenuView.as_view(), name="export"), path("mine/", MyClaimsView.as_view(), name="my-claims"), diff --git a/claims/views.py b/claims/views.py index 3e0716c..9406a76 100644 --- a/claims/views.py +++ b/claims/views.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission @@ -5,6 +6,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix from django.forms import formset_factory from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse +from django.utils import timezone from django.views import View from django.views.generic import ListView, TemplateView @@ -16,7 +18,7 @@ from .forms import ( UserManagementForm, UserPermissionForm, ) -from .models import Claim, ClaimLog +from .models import Claim, ClaimLog, SystemSetting User = get_user_model() @@ -110,7 +112,7 @@ class SubmitClaimView(View): if created: messages.success(request, f"{created} utlägg skickade in.") - return redirect(reverse("claims:admin-list")) + return redirect(reverse("claims:submit-success")) messages.error(request, "Inga utlägg kunde sparas. Fyll i minst en rad.") else: @@ -133,7 +135,7 @@ class ClaimAdminListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): def get_queryset(self): queryset = ( - Claim.objects.select_related("submitted_by", "project") + Claim.objects.select_related("submitted_by", "project", "paid_by") .prefetch_related("logs__performed_by") .all() ) @@ -148,9 +150,16 @@ class ClaimAdminListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): context["status_choices"] = Claim.Status.choices context["decision_choices"] = ClaimDecisionForm().fields["action"].choices context["can_change"] = self.request.user.has_perm("claims.change_claim") + context["payments_enabled"] = SystemSetting.internal_payments_active() return context def post(self, request, *args, **kwargs): + action_type = request.POST.get("action_type", "decision") + if action_type == "payment": + return self._handle_payment(request) + return self._handle_decision(request) + + def _handle_decision(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()) @@ -165,6 +174,9 @@ class ClaimAdminListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): claim = get_object_or_404(Claim, pk=form.cleaned_data["claim_id"]) action = form.cleaned_data["action"] decision_note = form.cleaned_data.get("decision_note", "") + if claim.is_paid: + messages.error(request, "Utlägget är redan markerat som betalt och kan inte ändras.") + return redirect(request.get_full_path()) previous_status = claim.status claim.decision_note = decision_note @@ -185,6 +197,33 @@ class ClaimAdminListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): ) return redirect(request.get_full_path()) + def _handle_payment(self, request): + if not SystemSetting.internal_payments_active(): + messages.error(request, "Betalningshantering är inte aktiverad.") + return redirect(request.get_full_path()) + 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("payment_claim_id")) + if claim.status != Claim.Status.APPROVED: + messages.error(request, "Endast godkända utlägg kan markeras som betalda.") + return redirect(request.get_full_path()) + if claim.is_paid: + messages.info(request, "Detta utlägg är redan markerat som betalt.") + return redirect(request.get_full_path()) + + claim.paid_by = request.user + claim.paid_at = timezone.now() + claim.save(update_fields=["paid_by", "paid_at"]) + claim.add_log( + action=ClaimLog.Action.MARKED_PAID, + performed_by=request.user, + note="Markerad som betald via systemet.", + ) + messages.success(request, f"{claim} markerades som betald.") + return redirect(request.get_full_path()) + class ClaimExportMenuView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView): template_name = "claims/export_placeholder.html" @@ -311,3 +350,7 @@ class UserManagementView(LoginRequiredMixin, PermissionRequiredMixin, TemplateVi user.user_permissions.add(perm) else: user.user_permissions.remove(perm) + + +class SubmitClaimSuccessView(TemplateView): + template_name = "claims/submit_success.html" diff --git a/claims_system/settings.py b/claims_system/settings.py index 96c7151..31ef487 100644 --- a/claims_system/settings.py +++ b/claims_system/settings.py @@ -10,6 +10,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.2/ref/settings/ """ +import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -120,6 +121,10 @@ MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media' LOGIN_REDIRECT_URL = '/claims/admin/' +LOGOUT_REDIRECT_URL = '/accounts/login/' + +os.environ.setdefault("CLAIMS_ENABLE_INTERNAL_PAYMENTS", "true") +CLAIMS_ENABLE_INTERNAL_PAYMENTS = os.getenv("CLAIMS_ENABLE_INTERNAL_PAYMENTS", "true").lower() in {"1", "true", "yes"} # Default primary key field type # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field diff --git a/templates/registration/logged_out.html b/templates/registration/logged_out.html new file mode 100644 index 0000000..bc48f7a --- /dev/null +++ b/templates/registration/logged_out.html @@ -0,0 +1,19 @@ +{% extends "claims/base.html" %} + +{% block title %}Utloggad{% endblock %} + +{% block content %} +
+
+

Du är utloggad

+

Vi ses snart igen

+

+ Din session är avslutad. Du kan när som helst logga in igen för att hantera utlägg eller administrera systemet. +

+ + Till inloggningen + +
+
+{% endblock %} diff --git a/templates/registration/login.html b/templates/registration/login.html index dc5bda4..cb4e0d6 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -3,11 +3,46 @@ {% block title %}Logga in{% endblock %} {% block content %} -

Logga in

-
- {% csrf_token %} - {{ form.as_p }} - - -
+
+
+
+

Välkommen tillbaka

+

Logga in

+

Använd dina administratörsuppgifter för att hantera utlägg.

+
+
+ {% csrf_token %} + + {% for field in form %} + {% if field.is_hidden %} + {{ field }} + {% else %} +
+ + + {% if field.help_text %} +

{{ field.help_text }}

+ {% endif %} + {% for error in field.errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} + {% endfor %} + +
+

Behöver du ett konto? Kontakta en superuser i organisationen.

+
+
{% endblock %}